diff --git a/Makefile b/Makefile
index cb5f643..78d9e45 100644
--- a/Makefile
+++ b/Makefile
@@ -32,12 +32,17 @@ OBJS += $(patsubst $(BDKDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(BDKDIR), *.S *.c)))
+# BDK parts the installer never uses (GUI, USB gadget, compression).
+OBJS := $(filter-out $(BUILDDIR)/$(TARGET)/libs/lvgl/% $(BUILDDIR)/$(TARGET)/usb/% $(BUILDDIR)/$(TARGET)/libs/compr/%,$(OBJS))
+OBJS := $(sort $(OBJS))
+
GFX_INC := '"../$(SOURCEDIR)/gfx.h"'
FFCFG_INC := '"../$(SOURCEDIR)/libs/fatfs/ffconf.h"'
################################################################################
CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
+CUSTOMDEFINES += -DBDK_MALLOC_NO_DEFRAG
CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC)
CUSTOMDEFINES += -DVERSION='"$(VERSION)"'
diff --git a/bdk/bdk.h b/bdk/bdk.h
new file mode 100644
index 0000000..02bc4b3
--- /dev/null
+++ b/bdk/bdk.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef BDK_H
+#define BDK_H
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#endif
diff --git a/bdk/display/di.c b/bdk/display/di.c
index 1ca61cc..3c8548c 100644
--- a/bdk/display/di.c
+++ b/bdk/display/di.c
@@ -1,867 +1,1044 @@
-/*
- * Copyright (c) 2018 naehrwert
- * Copyright (c) 2018-2021 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "di.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "di.inl"
-
-extern volatile nyx_storage_t *nyx_str;
-
-static u32 _display_id = 0;
-static bool nx_aula = false;
-
-static void _display_panel_and_hw_end(bool no_panel_deinit);
-
-static void _display_dsi_wait(u32 timeout, u32 off, u32 mask)
-{
- u32 end = get_tmr_us() + timeout;
- while (get_tmr_us() < end && DSI(off) & mask)
- ;
- usleep(5);
-}
-
-static void _display_dsi_send_cmd(u8 cmd, u32 param, u32 wait)
-{
- DSI(_DSIREG(DSI_WR_DATA)) = (param << 8) | cmd;
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
-
- if (wait)
- usleep(wait);
-}
-
-static void _display_dsi_read_rx_fifo(u32 *data)
-{
- u32 fifo_count = DSI(_DSIREG(DSI_STATUS)) & DSI_STATUS_RX_FIFO_SIZE;
- for (u32 i = 0; i < fifo_count; i++)
- {
- // Read or Drain RX FIFO.
- if (data)
- data[i] = DSI(_DSIREG(DSI_RD_DATA));
- else
- (void)DSI(_DSIREG(DSI_RD_DATA));
- }
-}
-
-int display_dsi_read(u8 cmd, u32 len, void *data, bool video_enabled)
-{
- int res = 0;
- u32 host_control = 0;
- u32 cmd_timeout = video_enabled ? 0 : 250000;
- u32 fifo[DSI_STATUS_RX_FIFO_SIZE] = {0};
-
- // Drain RX FIFO.
- _display_dsi_read_rx_fifo(NULL);
-
- // Save host control and enable host cmd packets during video.
- if (video_enabled)
- {
- host_control = DSI(_DSIREG(DSI_HOST_CONTROL));
-
- // Enable vblank interrupt.
- DISPLAY_A(_DIREG(DC_CMD_INT_ENABLE)) = DC_CMD_INT_FRAME_END_INT;
-
- // Use the 4th line to transmit the host cmd packet.
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = DSI_CMD_PKT_VID_ENABLE | DSI_DSI_LINE_TYPE(4);
-
- // Wait for vblank before starting the transfer.
- DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; // Clear interrupt.
- while (!(DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) & DC_CMD_INT_FRAME_END_INT))
- ;
- }
-
- // Set reply size.
- _display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
- _display_dsi_wait(cmd_timeout, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
-
- // Request register read.
- _display_dsi_send_cmd(MIPI_DSI_DCS_READ, cmd, 0);
- _display_dsi_wait(cmd_timeout, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
-
- // Transfer bus control to device for transmitting the reply.
- u32 high_speed = video_enabled ? DSI_HOST_CONTROL_HS : 0;
- DSI(_DSIREG(DSI_HOST_CONTROL)) = DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC | high_speed;
- _display_dsi_wait(150000, _DSIREG(DSI_HOST_CONTROL), DSI_HOST_CONTROL_IMM_BTA);
-
- // Wait a bit for the reply.
- usleep(5000);
-
- // Read RX FIFO.
- _display_dsi_read_rx_fifo(fifo);
-
- // Parse packet and copy over the data.
- if ((fifo[0] & 0xFF) == DSI_ESCAPE_CMD)
- {
- // Act based on reply type.
- switch (fifo[1] & 0xFF)
- {
- case GEN_LONG_RD_RES:
- case DCS_LONG_RD_RES:
- memcpy(data, &fifo[2], MIN((fifo[1] >> 8) & 0xFFFF, len));
- break;
-
- case GEN_1_BYTE_SHORT_RD_RES:
- case DCS_1_BYTE_SHORT_RD_RES:
- memcpy(data, &fifo[2], 1);
- break;
-
- case GEN_2_BYTE_SHORT_RD_RES:
- case DCS_2_BYTE_SHORT_RD_RES:
- memcpy(data, &fifo[2], 2);
- break;
-
- case ACK_ERROR_RES:
- default:
- res = 1;
- break;
- }
- }
- else
- res = 1;
-
- // Disable host cmd packets during video and restore host control.
- if (video_enabled)
- {
- // Wait for vblank before reseting sync points.
- DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; // Clear interrupt.
- while (!(DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) & DC_CMD_INT_FRAME_END_INT))
- ;
-
- // Reset all states of syncpt block.
- DSI(_DSIREG(DSI_INCR_SYNCPT_CNTRL)) = DSI_INCR_SYNCPT_SOFT_RESET;
- usleep(300); // Stabilization delay.
-
- // Clear syncpt block reset.
- DSI(_DSIREG(DSI_INCR_SYNCPT_CNTRL)) = 0;
- usleep(300); // Stabilization delay.
-
- // Restore video mode and host control.
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = 0;
- DSI(_DSIREG(DSI_HOST_CONTROL)) = host_control;
-
- // Disable and clear vblank interrupt.
- DISPLAY_A(_DIREG(DC_CMD_INT_ENABLE)) = 0;
- DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT;
- }
-
- return res;
-}
-
-void display_dsi_write(u8 cmd, u32 len, void *data, bool video_enabled)
-{
- u8 *fifo8;
- u32 *fifo32;
- u32 host_control;
-
- // Enable host cmd packets during video and save host control.
- if (video_enabled)
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = DSI_CMD_PKT_VID_ENABLE;
- host_control = DSI(_DSIREG(DSI_HOST_CONTROL));
-
- // Enable host transfer trigger.
- DSI(_DSIREG(DSI_HOST_CONTROL)) = host_control | DSI_HOST_CONTROL_TX_TRIG_HOST;
-
- switch (len)
- {
- case 0:
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
- break;
-
- case 1:
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd | (*(u8 *)data << 8), 0);
- break;
-
- default:
- fifo32 = calloc(DSI_STATUS_RX_FIFO_SIZE * 8, 4);
- fifo8 = (u8 *)fifo32;
- fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE;
- fifo8[4] = cmd;
- memcpy(&fifo8[5], data, len);
- len += 4 + 1; // Increase length by CMD/length word and DCS CMD.
- for (u32 i = 0; i < (ALIGN(len, 4) / 4); i++)
- DSI(_DSIREG(DSI_WR_DATA)) = fifo32[i];
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
- free(fifo32);
- break;
- }
-
- // Wait for the write to happen.
- _display_dsi_wait(250000, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST);
-
- // Disable host cmd packets during video and restore host control.
- if (video_enabled)
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = 0;
- DSI(_DSIREG(DSI_HOST_CONTROL)) = host_control;
-}
-
-void display_dsi_vblank_write(u8 cmd, u32 len, void *data)
-{
- u8 *fifo8;
- u32 *fifo32;
-
- // Enable vblank interrupt.
- DISPLAY_A(_DIREG(DC_CMD_INT_ENABLE)) = DC_CMD_INT_FRAME_END_INT;
-
- // Use the 4th line to transmit the host cmd packet.
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = DSI_CMD_PKT_VID_ENABLE | DSI_DSI_LINE_TYPE(4);
-
- // Wait for vblank before starting the transfer.
- DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; // Clear interrupt.
- while (!(DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) & DC_CMD_INT_FRAME_END_INT))
- ;
-
- switch (len)
- {
- case 0:
- DSI(_DSIREG(DSI_WR_DATA)) = (cmd << 8) | MIPI_DSI_DCS_SHORT_WRITE;
- break;
-
- case 1:
- DSI(_DSIREG(DSI_WR_DATA)) = ((cmd | (*(u8 *)data << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
- break;
-
- default:
- fifo32 = calloc(DSI_STATUS_RX_FIFO_SIZE * 8, 4);
- fifo8 = (u8 *)fifo32;
- fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE;
- fifo8[4] = cmd;
- memcpy(&fifo8[5], data, len);
- len += 4 + 1; // Increase length by CMD/length word and DCS CMD.
- for (u32 i = 0; i < (ALIGN(len, 4) / 4); i++)
- DSI(_DSIREG(DSI_WR_DATA)) = fifo32[i];
- free(fifo32);
- break;
- }
-
- // Wait for vblank before reseting sync points.
- DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; // Clear interrupt.
- while (!(DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) & DC_CMD_INT_FRAME_END_INT))
- ;
-
- // Reset all states of syncpt block.
- DSI(_DSIREG(DSI_INCR_SYNCPT_CNTRL)) = DSI_INCR_SYNCPT_SOFT_RESET;
- usleep(300); // Stabilization delay.
-
- // Clear syncpt block reset.
- DSI(_DSIREG(DSI_INCR_SYNCPT_CNTRL)) = 0;
- usleep(300); // Stabilization delay.
-
- // Restore video mode and host control.
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = 0;
-
- // Disable and clear vblank interrupt.
- DISPLAY_A(_DIREG(DC_CMD_INT_ENABLE)) = 0;
- DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT;
-}
-
-void display_init()
-{
- // Get Hardware type, as it's used in various DI functions.
- nx_aula = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
-
- // Check if display is already initialized.
- if (CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & BIT(CLK_L_DISP1))
- _display_panel_and_hw_end(true);
-
- // Get Chip ID.
- bool tegra_t210 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210;
-
- // T210B01: Power on SD2 regulator for supplying LDO0.
- if (!tegra_t210)
- {
- // Set SD2 regulator voltage.
- max7762x_regulator_set_voltage(REGULATOR_SD2, 1325000);
-
- // Set slew rate and enable SD2 regulator.
- i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD2_CFG, (1 << MAX77620_SD_SR_SHIFT) | MAX77620_SD_CFG1_FSRADE_SD_ENABLE);
- max7762x_regulator_enable(REGULATOR_SD2, true);
-
- }
-
- // Enable power to display panel controller.
- max7762x_regulator_set_voltage(REGULATOR_LDO0, 1200000);
- max7762x_regulator_enable(REGULATOR_LDO0, true);
-
- if (tegra_t210)
- max77620_config_gpio(7, MAX77620_GPIO_OUTPUT_ENABLE); // T210: LD0 -> GPIO7 -> Display panel.
-
- // Enable Display Interface specific clocks.
- CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_CLR) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
- CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
-
- CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_HOST1X) | BIT(CLK_L_DISP1);
- CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_HOST1X) | BIT(CLK_L_DISP1);
-
- CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_SET) = BIT(CLK_X_UART_FST_MIPI_CAL);
- CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL) = 10; // Set PLLP_OUT3 and div 6 (17MHz).
-
- CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_SET) = BIT(CLK_W_DSIA_LP);
- CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP) = 10; // Set PLLP_OUT and div 6 (68MHz).
-
- // Bring every IO rail out of deep power down.
- PMC(APBDEV_PMC_IO_DPD_REQ) = PMC_IO_DPD_REQ_DPD_OFF;
- PMC(APBDEV_PMC_IO_DPD2_REQ) = PMC_IO_DPD_REQ_DPD_OFF;
-
- // Configure LCD pins.
- PINMUX_AUX(PINMUX_AUX_NFC_EN) &= ~PINMUX_TRISTATE; // PULL_DOWN
- PINMUX_AUX(PINMUX_AUX_NFC_INT) &= ~PINMUX_TRISTATE; // PULL_DOWN
- PINMUX_AUX(PINMUX_AUX_LCD_RST) &= ~PINMUX_TRISTATE; // PULL_DOWN
-
- // Configure Backlight pins.
- PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) &= ~PINMUX_TRISTATE; // PULL_DOWN | 1
- PINMUX_AUX(PINMUX_AUX_LCD_BL_EN) &= ~PINMUX_TRISTATE; // PULL_DOWN
-
- if (nx_aula)
- {
- // Configure LCD RST pin.
- gpio_config(GPIO_PORT_V, GPIO_PIN_2, GPIO_MODE_GPIO);
- gpio_output_enable(GPIO_PORT_V, GPIO_PIN_2, GPIO_OUTPUT_ENABLE);
- }
- else
- {
- // Set LCD +-5V pins mode and direction
- gpio_config(GPIO_PORT_I, GPIO_PIN_0 | GPIO_PIN_1, GPIO_MODE_GPIO);
- gpio_output_enable(GPIO_PORT_I, GPIO_PIN_0 | GPIO_PIN_1, GPIO_OUTPUT_ENABLE);
-
- // Enable LCD power.
- gpio_write(GPIO_PORT_I, GPIO_PIN_0, GPIO_HIGH); // LCD +5V enable.
- usleep(10000);
- gpio_write(GPIO_PORT_I, GPIO_PIN_1, GPIO_HIGH); // LCD -5V enable.
- usleep(10000);
-
- // Configure Backlight PWM/EN and LCD RST pins (BL PWM, BL EN, LCD RST).
- gpio_config(GPIO_PORT_V, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_MODE_GPIO);
- gpio_output_enable(GPIO_PORT_V, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_OUTPUT_ENABLE);
-
- // Enable Backlight power.
- gpio_write(GPIO_PORT_V, GPIO_PIN_1, GPIO_HIGH);
- }
-
- // Power up supply regulator for display interface.
- MIPI_CAL(_DSIREG(MIPI_CAL_MIPI_BIAS_PAD_CFG2)) = 0;
-
- if (!tegra_t210)
- {
- MIPI_CAL(_DSIREG(MIPI_CAL_MIPI_BIAS_PAD_CFG0)) = 0;
- APB_MISC(APB_MISC_GP_DSI_PAD_CONTROL) = 0;
- }
-
- // Set DISP1 clock source and parent clock.
- CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_DISP1) = 0x40000000; // PLLD_OUT.
- u32 plld_div = (3 << 20) | (20 << 11) | 1; // DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 768 MHz, PLLD_OUT0 (DSI): 97.5 MHz (offset).
- CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) = PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | plld_div;
-
- if (tegra_t210)
- {
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC1) = 0x20; // PLLD_SETUP.
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC) = 0x2D0AAA; // PLLD_ENABLE_CLK.
- }
- else
- {
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC1) = 0;
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC) = 0x2DFC00; // PLLD_ENABLE_CLK.
- }
-
- // Setup Display Interface initial window configuration.
- exec_cfg((u32 *)DISPLAY_A_BASE, _display_dc_setup_win_config, 94);
-
- // Setup display communication interfaces.
- exec_cfg((u32 *)DSI_BASE, _display_dsi_init_config_part1, 8);
- if (tegra_t210)
- DSI(_DSIREG(DSI_INIT_SEQ_DATA_15)) = 0;
- else
- DSI(_DSIREG(DSI_INIT_SEQ_DATA_15_B01)) = 0;
- exec_cfg((u32 *)DSI_BASE, _display_dsi_init_config_part2, 14);
- if (!tegra_t210)
- exec_cfg((u32 *)DSI_BASE, _display_dsi_init_config_part3_t210b01, 7);
- exec_cfg((u32 *)DSI_BASE, _display_dsi_init_config_part4, 10);
- DSI(_DSIREG(DSI_PHY_TIMING_0)) = tegra_t210 ? 0x6070601 : 0x6070603;
- exec_cfg((u32 *)DSI_BASE, _display_dsi_init_config_part5, 12);
- DSI(_DSIREG(DSI_PHY_TIMING_0)) = tegra_t210 ? 0x6070601 : 0x6070603;
- exec_cfg((u32 *)DSI_BASE, _display_dsi_init_config_part6, 14);
- usleep(10000);
-
- // Enable LCD Reset.
- gpio_write(GPIO_PORT_V, GPIO_PIN_2, GPIO_HIGH);
- usleep(60000);
-
- // Setup DSI device takeover timeout.
- DSI(_DSIREG(DSI_BTA_TIMING)) = nx_aula ? 0x40103 : 0x50204;
-
- // Get Display ID.
- _display_id = 0xCCCCCC;
- for (u32 i = 0; i < 3; i++)
- {
- if (!display_dsi_read(MIPI_DCS_GET_DISPLAY_ID, 3, &_display_id, DSI_VIDEO_DISABLED))
- break;
-
- usleep(10000);
- }
-
- // Save raw Display ID to Nyx storage.
- nyx_str->info.disp_id = _display_id;
-
- // Decode Display ID.
- _display_id = ((_display_id >> 8) & 0xFF00) | (_display_id & 0xFF);
-
- if ((_display_id & 0xFF) == PANEL_JDI_XXX062M)
- _display_id = PANEL_JDI_XXX062M;
-
- // For Aula ensure that we have a compatible panel id.
- if (nx_aula && _display_id == 0xCCCC)
- _display_id = PANEL_SAM_AMS699VC01;
-
- // Initialize display panel.
- switch (_display_id)
- {
- case PANEL_SAM_AMS699VC01:
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, 0xA0, 0); // Write 0 to 0xA0.
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, MIPI_DCS_SET_CONTROL_DISPLAY | (DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL << 8), 0); // Enable brightness control.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x339; // MIPI_DSI_DCS_LONG_WRITE: 3 bytes.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x000051; // MIPI_DCS_SET_BRIGHTNESS 0000: 0%. FF07: 100%.
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
- usleep(5000);
- break;
-
- case PANEL_JDI_XXX062M:
- exec_cfg((u32 *)DSI_BASE, _display_init_config_jdi, 43);
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
- break;
-
- case PANEL_INL_P062CCA_AZ1:
- case PANEL_AUO_A062TAN01:
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
-
- // Unlock extension cmds.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x439; // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x9483FFB9; // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
- usleep(5000);
-
- // Set Power control.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x739; // MIPI_DSI_DCS_LONG_WRITE: 7 bytes.
- if (_display_id == PANEL_INL_P062CCA_AZ1)
- DSI(_DSIREG(DSI_WR_DATA)) = 0x751548B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT5 / XDK, VRH gamma volt adj 53 / x40).
- else // PANEL_AUO_A062TAN01.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x711148B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
- DSI(_DSIREG(DSI_WR_DATA)) = 0x143209; // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32).
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
- usleep(5000);
- break;
-
- case PANEL_INL_2J055IA_27A:
- case PANEL_AUO_A055TAN01:
- case PANEL_V40_55_UNK:
- default: // Allow spare part displays to work.
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 120000);
- break;
- }
-
- // Unblank display.
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_SET_DISPLAY_ON, 20000);
-
- // Configure PLLD for DISP1.
- plld_div = (1 << 20) | (24 << 11) | 1; // DIVM: 1, DIVN: 24, DIVP: 1. PLLD_OUT: 768 MHz, PLLD_OUT0 (DSI): 234 MHz (offset, it's ddr btw, so normally div2).
- CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) = PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | plld_div;
-
- if (tegra_t210)
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC1) = 0x20; // PLLD_SETUP.
- else
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC1) = 0;
- CLOCK(CLK_RST_CONTROLLER_PLLD_MISC) = 0x2DFC00; // Use new PLLD_SDM_DIN.
-
- // Finalize DSI configuration.
- DSI(_DSIREG(DSI_PAD_CONTROL_1)) = 0;
- DSI(_DSIREG(DSI_PHY_TIMING_0)) = tegra_t210 ? 0x6070601 : 0x6070603;
- exec_cfg((u32 *)DSI_BASE, _display_dsi_packet_config, 19);
- // Set pixel clock dividers: 234 / 3 / 1 = 78 MHz (offset) for 60 Hz.
- DISPLAY_A(_DIREG(DC_DISP_DISP_CLOCK_CONTROL)) = PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4); // 4: div3.
- exec_cfg((u32 *)DSI_BASE, _display_dsi_mode_config, 10);
- usleep(10000);
-
- // Calibrate display communication pads.
- u32 loops = tegra_t210 ? 1 : 2; // Find out why this is done 2 times on Mariko.
- exec_cfg((u32 *)MIPI_CAL_BASE, _display_mipi_pad_cal_config, 4);
- for (u32 i = 0; i < loops; i++)
- {
- // Set MIPI bias pad config.
- MIPI_CAL(_DSIREG(MIPI_CAL_MIPI_BIAS_PAD_CFG2)) = 0x10010;
- MIPI_CAL(_DSIREG(MIPI_CAL_MIPI_BIAS_PAD_CFG1)) = tegra_t210 ? 0x300 : 0;
-
- // Set pad trimmers and set MIPI DSI cal offsets.
- if (tegra_t210)
- {
- exec_cfg((u32 *)DSI_BASE, _display_dsi_pad_cal_config_t210, 4);
- exec_cfg((u32 *)MIPI_CAL_BASE, _display_mipi_dsi_cal_offsets_config_t210, 4);
- }
- else
- {
- exec_cfg((u32 *)DSI_BASE, _display_dsi_pad_cal_config_t210b01, 7);
- exec_cfg((u32 *)MIPI_CAL_BASE, _display_mipi_dsi_cal_offsets_config_t210b01, 4);
- }
-
- // Set the rest of MIPI cal offsets and apply calibration.
- exec_cfg((u32 *)MIPI_CAL_BASE, _display_mipi_apply_dsi_cal_config, 12);
- }
- usleep(10000);
-
- // Enable video display controller.
- exec_cfg((u32 *)DISPLAY_A_BASE, _display_video_disp_controller_enable_config, 113);
-}
-
-void display_backlight_pwm_init()
-{
- if (_display_id == PANEL_SAM_AMS699VC01)
- return;
-
- clock_enable_pwm();
-
- PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN; // Enable PWM and set it to 25KHz PFM. 29.5KHz is stock.
-
- PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) & ~PINMUX_FUNC_MASK) | 1; // Set PWM0 mode.
- gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); // Backlight power mode.
-}
-
-void display_backlight(bool enable)
-{
- gpio_write(GPIO_PORT_V, GPIO_PIN_0, enable ? GPIO_HIGH : GPIO_LOW); // Backlight PWM GPIO.
-}
-
-void display_dsi_backlight_brightness(u32 brightness)
-{
- // Normalize brightness value by 82% and a base of 45 duty.
- if (brightness)
- brightness = (brightness * PANEL_OLED_BL_COEFF / 100) + PANEL_OLED_BL_OFFSET;
-
- u16 bl_ctrl = byte_swap_16((u16)(brightness * 8));
- display_dsi_vblank_write(MIPI_DCS_SET_BRIGHTNESS, 2, &bl_ctrl);
-}
-
-void display_pwm_backlight_brightness(u32 brightness, u32 step_delay)
-{
- u32 old_value = (PWM(PWM_CONTROLLER_PWM_CSR_0) >> 16) & 0xFF;
- if (brightness == old_value)
- return;
-
- if (old_value < brightness)
- {
- for (u32 i = old_value; i < brightness + 1; i++)
- {
- PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN | (i << 16);
- usleep(step_delay);
- }
- }
- else
- {
- for (u32 i = old_value; i > brightness; i--)
- {
- PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN | (i << 16);
- usleep(step_delay);
- }
- }
- if (!brightness)
- PWM(PWM_CONTROLLER_PWM_CSR_0) = 0;
-}
-
-void display_backlight_brightness(u32 brightness, u32 step_delay)
-{
- if (brightness > 255)
- brightness = 255;
-
- if (_display_id != PANEL_SAM_AMS699VC01)
- display_pwm_backlight_brightness(brightness, step_delay);
- else
- display_dsi_backlight_brightness(brightness);
-}
-
-u32 display_get_backlight_brightness()
-{
- return ((PWM(PWM_CONTROLLER_PWM_CSR_0) >> 16) & 0xFF);
-}
-
-static void _display_panel_and_hw_end(bool no_panel_deinit)
-{
- if (no_panel_deinit)
- goto skip_panel_deinit;
-
- display_backlight_brightness(0, 1000);
-
- // Enable host cmd packets during video.
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = DSI_CMD_PKT_VID_ENABLE;
-
- // Blank display.
- DSI(_DSIREG(DSI_WR_DATA)) = (MIPI_DCS_SET_DISPLAY_OFF << 8) | MIPI_DSI_DCS_SHORT_WRITE;
-
- // Propagate changes to all register buffers and disable host cmd packets during video.
- DISPLAY_A(_DIREG(DC_CMD_STATE_ACCESS)) = READ_MUX | WRITE_MUX;
- DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = 0;
-
- // De-initialize video controller.
- exec_cfg((u32 *)DISPLAY_A_BASE, _display_video_disp_controller_disable_config, 17);
- exec_cfg((u32 *)DSI_BASE, _display_dsi_timing_deinit_config, 16);
-
- if (_display_id != PANEL_SAM_AMS699VC01)
- usleep(10000);
-
- // De-initialize display panel.
- switch (_display_id)
- {
- case PANEL_JDI_XXX062M:
- exec_cfg((u32 *)DSI_BASE, _display_deinit_config_jdi, 22);
- break;
-
- case PANEL_AUO_A062TAN01:
- exec_cfg((u32 *)DSI_BASE, _display_deinit_config_auo, 37);
- break;
-
- case PANEL_INL_2J055IA_27A:
- case PANEL_AUO_A055TAN01:
- case PANEL_V40_55_UNK:
- // Unlock extension cmds.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x439; // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x9483FFB9; // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
- usleep(5000);
-
- // Set Power control.
- DSI(_DSIREG(DSI_WR_DATA)) = 0xB39; // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
- if (_display_id == PANEL_INL_2J055IA_27A)
- DSI(_DSIREG(DSI_WR_DATA)) = 0x751548B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT5 / XDK, VRH gamma volt adj 53 / x40).
- else if (_display_id == PANEL_AUO_A055TAN01)
- DSI(_DSIREG(DSI_WR_DATA)) = 0x711148B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
- else // PANEL_V40_55_UNK.
- DSI(_DSIREG(DSI_WR_DATA)) = 0x731348B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT3 / XDK, VRH gamma volt adj 51 / x40).
- if (_display_id == PANEL_INL_2J055IA_27A || _display_id == PANEL_AUO_A055TAN01)
- {
- // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32, Enter standby / PON / VCOMG).
- DSI(_DSIREG(DSI_WR_DATA)) = 0x71143209;
- DSI(_DSIREG(DSI_WR_DATA)) = 0x114D31; // (Unknown).
- }
- else // PANEL_V40_55_UNK.
- {
- // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/48, Enter standby / PON / VCOMG).
- DSI(_DSIREG(DSI_WR_DATA)) = 0x71243209;
- DSI(_DSIREG(DSI_WR_DATA)) = 0x004C31; // (Unknown).
- }
- DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST;
- usleep(5000);
- break;
-
- case PANEL_INL_P062CCA_AZ1:
- default:
- break;
- }
-
- // Blank - powerdown.
- _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_ENTER_SLEEP_MODE,
- (_display_id == PANEL_SAM_AMS699VC01) ? 120000 : 50000);
-
-skip_panel_deinit:
- // Disable LCD power pins.
- gpio_write(GPIO_PORT_V, GPIO_PIN_2, GPIO_LOW); // LCD Reset disable.
-
- if (!nx_aula) // HOS uses panel id.
- {
- usleep(10000);
- gpio_write(GPIO_PORT_I, GPIO_PIN_1, GPIO_LOW); // LCD -5V disable.
- usleep(10000);
- gpio_write(GPIO_PORT_I, GPIO_PIN_0, GPIO_LOW); // LCD +5V disable.
- usleep(10000);
- }
- else
- usleep(30000); // Aula Panel.
-
- // Disable Display Interface specific clocks.
- CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_SET) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
- CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_CLR) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
- CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_HOST1X) | BIT(CLK_L_DISP1);
- CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_HOST1X) | BIT(CLK_L_DISP1);
-
- // Power down pads.
- DSI(_DSIREG(DSI_PAD_CONTROL_0)) = DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) | DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF);
- DSI(_DSIREG(DSI_POWER_CONTROL)) = 0;
-
- // Switch LCD PWM backlight pin to special function mode and enable PWM0 mode.
- if (!nx_aula)
- {
- gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); // Backlight PWM.
- PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) & ~PINMUX_TRISTATE) | PINMUX_TRISTATE;
- PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) & ~PINMUX_FUNC_MASK) | 1; // Set PWM0 mode.
- }
-}
-
-void display_end() { _display_panel_and_hw_end(false); };
-
-u16 display_get_decoded_panel_id()
-{
- return _display_id;
-}
-
-void display_set_decoded_panel_id(u32 id)
-{
- // Get Hardware type, as it's used in various DI functions.
- nx_aula = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
-
- // Decode Display ID.
- _display_id = ((id >> 8) & 0xFF00) | (id & 0xFF);
-
- if ((_display_id & 0xFF) == PANEL_JDI_XXX062M)
- _display_id = PANEL_JDI_XXX062M;
-
- // For Aula ensure that we have a compatible panel id.
- if (nx_aula && _display_id == 0xCCCC)
- _display_id = PANEL_SAM_AMS699VC01;
-}
-
-void display_color_screen(u32 color)
-{
- exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_one_color, 8);
-
- // Configure display to show single color.
- DISPLAY_A(_DIREG(DC_WIN_AD_WIN_OPTIONS)) = 0;
- DISPLAY_A(_DIREG(DC_WIN_BD_WIN_OPTIONS)) = 0;
- DISPLAY_A(_DIREG(DC_WIN_CD_WIN_OPTIONS)) = 0;
- DISPLAY_A(_DIREG(DC_DISP_BLEND_BACKGROUND_COLOR)) = color;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = (DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) & 0xFFFFFFFE) | GENERAL_ACT_REQ;
- usleep(35000); // No need to wait on Aula.
-
- if (_display_id != PANEL_SAM_AMS699VC01)
- display_backlight(true);
- else
- display_backlight_brightness(255, 0);
-}
-
-u32 *display_init_framebuffer_pitch()
-{
- // Sanitize framebuffer area.
- memset((u32 *)IPL_FB_ADDRESS, 0, 0x3C0000);
-
- // This configures the framebuffer @ IPL_FB_ADDRESS with a resolution of 1280x720 (line stride 720).
- exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer_pitch, 32);
- usleep(35000); // No need to wait on Aula.
-
- return (u32 *)IPL_FB_ADDRESS;
-}
-
-u32 *display_init_framebuffer_pitch_inv()
-{
- // This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 1280x720 (line stride 720).
- exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer_pitch_inv, 34);
- usleep(35000); // No need to wait on Aula.
-
- return (u32 *)NYX_FB_ADDRESS;
-}
-
-u32 *display_init_framebuffer_block()
-{
- // This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 1280x720 (line stride 720).
- exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer_block, 34);
- usleep(35000); // No need to wait on Aula.
-
- return (u32 *)NYX_FB_ADDRESS;
-}
-
-u32 *display_init_framebuffer_log()
-{
- // This configures the framebuffer @ LOG_FB_ADDRESS with a resolution of 1280x720 (line stride 720).
- exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer_log, 20);
-
- return (u32 *)LOG_FB_ADDRESS;
-}
-
-void display_activate_console()
-{
- DISPLAY_A(_DIREG(DC_CMD_DISPLAY_WINDOW_HEADER)) = WINDOW_D_SELECT; // Select window D.
- DISPLAY_A(_DIREG(DC_WIN_WIN_OPTIONS)) = WIN_ENABLE; // Enable window DD.
- DISPLAY_A(_DIREG(DC_WIN_POSITION)) = 0xFF80;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | WIN_D_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
-
- for (u32 i = 0xFF80; i < 0x10000; i++)
- {
- DISPLAY_A(_DIREG(DC_WIN_POSITION)) = i & 0xFFFF;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | WIN_D_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
- usleep(1000);
- }
-
- DISPLAY_A(_DIREG(DC_WIN_POSITION)) = 0;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | WIN_D_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
-}
-
-void display_deactivate_console()
-{
- DISPLAY_A(_DIREG(DC_CMD_DISPLAY_WINDOW_HEADER)) = WINDOW_D_SELECT; // Select window D.
-
- for (u32 i = 0xFFFF; i > 0xFF7F; i--)
- {
- DISPLAY_A(_DIREG(DC_WIN_POSITION)) = i & 0xFFFF;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | WIN_D_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
- usleep(500);
- }
-
- DISPLAY_A(_DIREG(DC_WIN_POSITION)) = 0;
- DISPLAY_A(_DIREG(DC_WIN_WIN_OPTIONS)) = 0; // Disable window DD.
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | WIN_D_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
-}
-
-void display_init_cursor(void *crs_fb, u32 size)
-{
- // Setup cursor.
- DISPLAY_A(_DIREG(DC_DISP_CURSOR_START_ADDR)) = CURSOR_CLIPPING(CURSOR_CLIP_WIN_A) | size | ((u32)crs_fb >> 10);
- DISPLAY_A(_DIREG(DC_DISP_BLEND_CURSOR_CONTROL)) =
- CURSOR_BLEND_R8G8B8A8 | CURSOR_BLEND_DST_FACTOR(CURSOR_BLEND_K1) | CURSOR_BLEND_SRC_FACTOR(CURSOR_BLEND_K1) | 0xFF;
-
- DISPLAY_A(_DIREG(DC_DISP_DISP_WIN_OPTIONS)) |= CURSOR_ENABLE;
-
- // Arm and activate changes.
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | CURSOR_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
-}
-
-void display_set_pos_cursor(u32 x, u32 y)
-{
- DISPLAY_A(_DIREG(DC_DISP_CURSOR_POSITION)) = x | (y << 16);
-
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | CURSOR_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
-}
-
-void display_deinit_cursor()
-{
- DISPLAY_A(_DIREG(DC_DISP_BLEND_CURSOR_CONTROL)) = 0;
- DISPLAY_A(_DIREG(DC_DISP_DISP_WIN_OPTIONS)) &= ~CURSOR_ENABLE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_UPDATE | CURSOR_UPDATE;
- DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
-}
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018-2025 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "di.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "di.inl"
+
+static bool _nx_aula = false;
+static u32 _panel_id = 0;
+static u32 _panel_id_raw = 0;
+
+static void _display_panel_and_hw_end(bool no_panel_deinit);
+
+void display_enable_interrupt(u32 intr)
+{
+ DISPLAY_A(DC_CMD_INT_ENABLE) |= intr;
+}
+
+void display_disable_interrupt(u32 intr)
+{
+ DISPLAY_A(DC_CMD_INT_ENABLE) &= ~intr;
+ DISPLAY_A(DC_CMD_INT_STATUS) = intr;
+}
+
+void display_wait_interrupt(u32 intr)
+{
+ DISPLAY_A(DC_CMD_INT_STATUS) = intr;
+
+ // Interrupts are masked. Poll status register for checking if fired.
+ while (!(DISPLAY_A(DC_CMD_INT_STATUS) & intr))
+ ;
+}
+
+static void _display_dsi_wait(u32 timeout, u32 off, u32 mask)
+{
+ u32 end = get_tmr_us() + timeout * 1000;
+ while (get_tmr_us() < end && DSI(off) & mask)
+ ;
+ usleep(5);
+}
+
+static void _display_dsi_soft_reset()
+{
+ // Disable DSI interface.
+ DSI(DSI_POWER_CONTROL) = 0;
+
+ // Settle time.
+ usleep(10);
+}
+
+static void _display_dsi_send_cmd(u8 cmd, u32 param, u32 wait)
+{
+ DSI(DSI_WR_DATA) = (param << 8) | cmd;
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+
+ if (wait)
+ usleep(wait);
+}
+
+static void _display_dsi_wait_vblank(bool enable)
+{
+ if (enable)
+ {
+ // Enable vblank interrupt.
+ display_enable_interrupt(DC_CMD_INT_FRAME_END_INT);
+
+ // Use the 4th line to transmit the host cmd packet.
+ DSI(DSI_VIDEO_MODE_CONTROL) = DSI_CMD_PKT_VID_ENABLE | DSI_DSI_LINE_TYPE(4);
+
+ // Wait for vblank before starting the transfer.
+ display_wait_interrupt(DC_CMD_INT_FRAME_END_INT);
+ }
+ else
+ {
+ // Wait for vblank before resetting sync points.
+ display_wait_interrupt(DC_CMD_INT_FRAME_END_INT);
+ usleep(14);
+
+ // Reset all states of syncpt block.
+ DSI(DSI_INCR_SYNCPT_CNTRL) = DSI_INCR_SYNCPT_SOFT_RESET;
+ usleep(300); // Stabilization delay.
+
+ // Clear syncpt block reset.
+ DSI(DSI_INCR_SYNCPT_CNTRL) = 0;
+ usleep(300); // Stabilization delay.
+
+ // Restore video mode and host control.
+ DSI(DSI_VIDEO_MODE_CONTROL) = 0;
+
+ // Disable and clear vblank interrupt.
+ display_disable_interrupt(DC_CMD_INT_FRAME_END_INT);
+ }
+}
+
+static void _display_dsi_read_rx_fifo(u32 *data)
+{
+ u32 fifo_count = DSI(DSI_STATUS) & DSI_STATUS_RX_FIFO_SIZE;
+ if (fifo_count)
+ DSI(DSI_TRIGGER) = 0;
+
+ for (u32 i = 0; i < fifo_count; i++)
+ {
+ // Read or Drain RX FIFO.
+ if (data)
+ data[i] = DSI(DSI_RD_DATA);
+ else
+ (void)DSI(DSI_RD_DATA);
+ }
+}
+
+int display_dsi_read(u8 cmd, u32 len, void *data)
+{
+ u32 fifo[DSI_STATUS_RX_FIFO_SIZE] = {0};
+
+ // Drain RX FIFO.
+ _display_dsi_read_rx_fifo(NULL);
+
+ // Set reply size.
+ _display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
+ _display_dsi_wait(250, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
+
+ // Request register read.
+ _display_dsi_send_cmd(MIPI_DSI_DCS_READ, cmd, 0);
+ _display_dsi_wait(250, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
+
+ // Transfer bus control to device for transmitting the reply.
+ DSI(DSI_HOST_CONTROL) |= DSI_HOST_CONTROL_IMM_BTA;
+
+ // Wait for reply to complete. DSI_HOST_CONTROL_IMM_BTA bit acts as a DSI host read busy.
+ _display_dsi_wait(150, DSI_HOST_CONTROL, DSI_HOST_CONTROL_IMM_BTA);
+
+ // Wait a bit for the reply.
+ usleep(5000);
+
+ // Read RX FIFO.
+ _display_dsi_read_rx_fifo(fifo);
+
+ // Parse packet and copy over the data.
+ if ((fifo[0] & 0xFF) == DSI_ESCAPE_CMD)
+ {
+ // Act based on reply type.
+ switch (fifo[1] & 0xFF)
+ {
+ case GEN_LONG_RD_RES:
+ case DCS_LONG_RD_RES:
+ memcpy(data, &fifo[2], MIN((fifo[1] >> 8) & 0xFFFF, len));
+ break;
+
+ case GEN_1_BYTE_SHORT_RD_RES:
+ case DCS_1_BYTE_SHORT_RD_RES:
+ memcpy(data, &fifo[2], 1);
+ break;
+
+ case GEN_2_BYTE_SHORT_RD_RES:
+ case DCS_2_BYTE_SHORT_RD_RES:
+ memcpy(data, &fifo[2], 2);
+ break;
+
+ case ACK_ERROR_RES:
+ default:
+ return 1;
+ }
+ }
+ else
+ return 1;
+
+ return 0;
+}
+
+int display_dsi_vblank_read(u8 cmd, u32 len, void *data)
+{
+ int res = 0;
+ u32 host_control = 0;
+ u32 fifo[DSI_STATUS_RX_FIFO_SIZE] = {0};
+
+ // Drain RX FIFO.
+ _display_dsi_read_rx_fifo(NULL);
+
+ // Save host control and enable host cmd packets during video.
+ host_control = DSI(DSI_HOST_CONTROL);
+
+ _display_dsi_wait_vblank(true);
+
+ // Set reply size.
+ _display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
+ _display_dsi_wait(0, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
+
+ // Request register read.
+ _display_dsi_send_cmd(MIPI_DSI_DCS_READ, cmd, 0);
+ _display_dsi_wait(0, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
+
+ _display_dsi_wait_vblank(false);
+
+ // Transfer bus control to device for transmitting the reply.
+ DSI(DSI_HOST_CONTROL) |= DSI_HOST_CONTROL_IMM_BTA;
+
+ // Wait for reply to complete. DSI_HOST_CONTROL_IMM_BTA bit acts as a DSI host read busy.
+ _display_dsi_wait(150, DSI_HOST_CONTROL, DSI_HOST_CONTROL_IMM_BTA);
+
+ // Wait a bit for the reply.
+ usleep(5000);
+
+ // Read RX FIFO.
+ _display_dsi_read_rx_fifo(fifo);
+
+ // Parse packet and copy over the data.
+ if ((fifo[0] & 0xFF) == DSI_ESCAPE_CMD)
+ {
+ // Act based on reply type.
+ switch (fifo[1] & 0xFF)
+ {
+ case GEN_LONG_RD_RES:
+ case DCS_LONG_RD_RES:
+ memcpy(data, &fifo[2], MIN((fifo[1] >> 8) & 0xFFFF, len));
+ break;
+
+ case GEN_1_BYTE_SHORT_RD_RES:
+ case DCS_1_BYTE_SHORT_RD_RES:
+ memcpy(data, &fifo[2], 1);
+ break;
+
+ case GEN_2_BYTE_SHORT_RD_RES:
+ case DCS_2_BYTE_SHORT_RD_RES:
+ memcpy(data, &fifo[2], 2);
+ break;
+
+ case ACK_ERROR_RES:
+ default:
+ res = 1;
+ break;
+ }
+ }
+ else
+ res = 1;
+
+ // Restore host control.
+ DSI(DSI_HOST_CONTROL) = host_control;
+
+ return res;
+}
+
+void display_dsi_write(u8 cmd, u32 len, void *data)
+{
+ u32 host_control;
+ u32 fifo32[DSI_STATUS_TX_FIFO_SIZE] = {0};
+ u8 *fifo8 = (u8 *)fifo32;
+
+ // Prepare data for long write.
+ if (len >= 2)
+ {
+ memcpy(&fifo8[5], data, len);
+ memset(&fifo8[5] + len, 0, len % sizeof(u32));
+ len++; // Increase length by CMD.
+ }
+
+ // Save host control.
+ host_control = DSI(DSI_HOST_CONTROL);
+
+ // Enable host transfer trigger.
+ DSI(DSI_HOST_CONTROL) = (host_control & ~(DSI_HOST_CONTROL_TX_TRIG_MASK)) | DSI_HOST_CONTROL_TX_TRIG_HOST;
+
+ switch (len)
+ {
+ case 0:
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
+ break;
+
+ case 1:
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd | (*(u8 *)data << 8), 0);
+ break;
+
+ default:
+ fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE;
+ fifo8[4] = cmd;
+ len += sizeof(u32); // Increase length by length word and DCS CMD.
+ for (u32 i = 0; i < (ALIGN(len, sizeof(u32)) / sizeof(u32)); i++)
+ DSI(DSI_WR_DATA) = fifo32[i];
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+ break;
+ }
+
+ // Wait for the write to happen.
+ _display_dsi_wait(250, DSI_TRIGGER, DSI_TRIGGER_HOST);
+
+ // Restore host control.
+ DSI(DSI_HOST_CONTROL) = host_control;
+}
+
+void display_dsi_vblank_write(u8 cmd, u32 len, void *data)
+{
+ u32 fifo32[DSI_STATUS_TX_FIFO_SIZE] = {0};
+ u8 *fifo8 = (u8 *)fifo32;
+
+ // Prepare data for long write.
+ if (len >= 2)
+ {
+ memcpy(&fifo8[5], data, len);
+ memset(&fifo8[5] + len, 0, len % sizeof(u32));
+ len++; // Increase length by CMD.
+ }
+
+ _display_dsi_wait_vblank(true);
+
+ switch (len)
+ {
+ case 0:
+ DSI(DSI_WR_DATA) = (cmd << 8) | MIPI_DSI_DCS_SHORT_WRITE;
+ break;
+
+ case 1:
+ DSI(DSI_WR_DATA) = ((cmd | (*(u8 *)data << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+ break;
+
+ default:
+ fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE;
+ fifo8[4] = cmd;
+ len += sizeof(u32); // Increase length by length word and DCS CMD.
+ for (u32 i = 0; i < (ALIGN(len, sizeof(u32)) / sizeof(u32)); i++)
+ DSI(DSI_WR_DATA) = fifo32[i];
+ break;
+ }
+
+ _display_dsi_wait_vblank(false);
+}
+
+void display_init()
+{
+ // Get Hardware type, as it's used in various DI functions.
+ _nx_aula = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
+
+ // Check if display is already initialized.
+ if (CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & BIT(CLK_L_DISP1))
+ _display_panel_and_hw_end(true);
+
+ // Get Chip ID.
+ bool tegra_t210 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210;
+
+ // Enable DSI AVDD.
+ max7762x_regulator_set_voltage(REGULATOR_LDO0, 1200000);
+ max7762x_regulator_enable(REGULATOR_LDO0, true);
+
+ // Enable Display Interface specific clocks.
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_CLR) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_DISP1);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_DISP1);
+
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_SET) = BIT(CLK_X_UART_FST_MIPI_CAL);
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL) = CLK_SRC_DIV(6); // Set PLLP_OUT3 and div 6 (17MHz).
+
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_SET) = BIT(CLK_W_DSIA_LP);
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP) = CLK_SRC_DIV(6); // Set PLLP_OUT and div 6 (68MHz).
+
+ // Bring every IO rail out of deep power down. (Though no rail bit is set.)
+ PMC(APBDEV_PMC_IO_DPD_REQ) = PMC_IO_DPD_REQ_DPD_OFF;
+ PMC(APBDEV_PMC_IO_DPD2_REQ) = PMC_IO_DPD_REQ_DPD_OFF;
+
+ // Configure LCD/WLED driver pins.
+ if (!_nx_aula)
+ {
+ // Configure LCD driver pins.
+ PINMUX_AUX(PINMUX_AUX_NFC_EN) = PINMUX_PULL_DOWN;
+ PINMUX_AUX(PINMUX_AUX_NFC_INT) = PINMUX_PULL_DOWN;
+
+ // Configure WLED driver pins.
+ PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = PINMUX_PULL_DOWN;
+ PINMUX_AUX(PINMUX_AUX_LCD_BL_EN) = PINMUX_PULL_DOWN;
+
+ // Enable LCD driver AVDD channels (+5.4V CH2 EN, -5.4V CH1 EN).
+ gpio_direction_output(GPIO_PORT_I, GPIO_PIN_0 | GPIO_PIN_1, GPIO_HIGH);
+ usleep(10000); // Wait minimum 4.2ms to stabilize.
+
+ // Configure WLED driver PWM/EN pins.
+ gpio_direction_output(GPIO_PORT_V, GPIO_PIN_0 | GPIO_PIN_1, GPIO_LOW);
+
+ // Enable WLED driver.
+ gpio_write(GPIO_PORT_V, GPIO_PIN_1, GPIO_HIGH);
+ }
+ // else
+ // {
+ // // Configure OLED status pin.
+ // PINMUX_AUX(PINMUX_AUX_WIFI_EN) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
+ // gpio_direction_input(GPIO_PORT_H, GPIO_PIN_0);
+ // }
+
+ // Configure Panel Reset pin.
+ PINMUX_AUX(PINMUX_AUX_LCD_RST) = PINMUX_PULL_DOWN;
+ gpio_direction_output(GPIO_PORT_V, GPIO_PIN_2, GPIO_LOW);
+
+ // Power up supply regulator for display interface.
+ MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG2) = 0;
+ MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG0) = 0;
+
+ if (!tegra_t210)
+ APB_MISC(APB_MISC_GP_DSI_PAD_CONTROL) = 0;
+
+ // Set DISP1 clock source, parent clock and DSI/PCLK to command mode.
+ // T210: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 100.0 MHz, PLLD_OUT0 (DSI-BCLK): 50.0 MHz. (PCLK: 16.66 MHz)
+ // T210B01: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 97.8 MHz, PLLD_OUT0 (DSI-BCLK): 48.9 MHz. (PCLK: 16.30 MHz)
+ clock_enable_plld(3, 20, true, tegra_t210);
+
+ // Setup Display Interface initial configuration.
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_dc_init_config, ARRAY_SIZE(_di_dc_init_config));
+
+ // Setup DSI init sequence packets.
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_seq_pkt_reset_config0, ARRAY_SIZE(_di_dsi_seq_pkt_reset_config0));
+ DSI(tegra_t210 ? DSI_INIT_SEQ_DATA_15 : DSI_INIT_SEQ_DATA_15_B01) = 0;
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_seq_pkt_reset_config1, ARRAY_SIZE(_di_dsi_seq_pkt_reset_config1));
+
+ // Reset pad trimmers for T210B01.
+ if (!tegra_t210)
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_init_pads_t210b01, ARRAY_SIZE(_di_dsi_init_pads_t210b01));
+
+ // Setup init seq packet lengths, timings and power on DSI.
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_init_config, ARRAY_SIZE(_di_dsi_init_config));
+ usleep(10);
+
+ // DSI soft reset.
+ _display_dsi_soft_reset();
+
+ // Set DSI LP timings.
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_timing_lp_config, ARRAY_SIZE(_di_dsi_timing_lp_config));
+ usleep(10000);
+
+ // Enable Panel Reset.
+ gpio_write(GPIO_PORT_V, GPIO_PIN_2, GPIO_HIGH);
+ usleep(60000);
+
+ // Setup DSI device takeover timeout.
+ DSI(DSI_BTA_TIMING) = _nx_aula ? 0x40103 : 0x50204;
+
+ // Get Display ID.
+ _panel_id_raw = 0xCCCCCC;
+ for (u32 i = 0; i < 3; i++)
+ {
+ if (!display_dsi_read(MIPI_DCS_GET_DISPLAY_ID, 3, &_panel_id_raw))
+ break;
+
+ usleep(5000);
+ }
+
+ // Decode Display ID.
+ _panel_id = ((_panel_id_raw >> 8) & 0xFF00) | (_panel_id_raw & 0xFF);
+
+ if ((_panel_id & 0xFF) == PANEL_JDI_XXX062M)
+ _panel_id = PANEL_JDI_XXX062M;
+
+ // For Aula ensure that we have a compatible panel id.
+ if (_nx_aula)
+ _panel_id = PANEL_SAM_AMS699VC01;
+
+ // Initialize display panel.
+ switch (_panel_id)
+ {
+ case PANEL_SAM_AMS699VC01:
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
+ // Set color mode to basic (natural). Stock is Saturated (0x00). (POR/Exit value is 0x20/0x00).
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ MIPI_DCS_PRIV_SM_SET_COLOR_MODE | (DCS_SM_COLOR_MODE_BASIC << 8), 0);
+ // Enable backlight and smooth PWM.
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ MIPI_DCS_SET_CONTROL_DISPLAY | ((DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL | DCS_CONTROL_DISPLAY_DIMMING_CTRL) << 8), 0);
+
+ // Unlock Level 2 registers.
+ DSI(DSI_WR_DATA) = 0x539; // MIPI_DSI_DCS_LONG_WRITE: 5 bytes.
+ DSI(DSI_WR_DATA) = 0x5A5A5AE2; // MIPI_DCS_PRIV_SM_SET_REGS_LOCK: Unlock Level 2 registers.
+ DSI(DSI_WR_DATA) = 0x5A;
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+
+ // Set registers offset and set PWM transition to 6 frames (100ms).
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, MIPI_DCS_PRIV_SM_SET_REG_OFFSET | (7 << 8), 0);
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, MIPI_DCS_PRIV_SM_SET_ELVSS | (6 << 8), 0);
+
+ // Relock Level 2 registers.
+ DSI(DSI_WR_DATA) = 0x539; // MIPI_DSI_DCS_LONG_WRITE: 5 bytes.
+ DSI(DSI_WR_DATA) = 0xA55A5AE2; // MIPI_DCS_PRIV_SM_SET_REGS_LOCK: Lock Level 2 registers.
+ DSI(DSI_WR_DATA) = 0xA5;
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+
+ // Set backlight to 0%.
+ DSI(DSI_WR_DATA) = 0x339; // MIPI_DSI_DCS_LONG_WRITE: 3 bytes.
+ DSI(DSI_WR_DATA) = 0x000051; // MIPI_DCS_SET_BRIGHTNESS 0000: 0%. FF07: 100%.
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+ usleep(5000);
+ break;
+
+ case PANEL_JDI_XXX062M:
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_panel_init_config_jdi, ARRAY_SIZE(_di_dsi_panel_init_config_jdi));
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
+ break;
+
+ case PANEL_INL_P062CCA_AZ1:
+ case PANEL_AUO_A062TAN01:
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
+
+ // Unlock extension cmds.
+ DSI(DSI_WR_DATA) = 0x439; // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ DSI(DSI_WR_DATA) = 0x9483FFB9; // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+ usleep(5000);
+
+ // Set Power control.
+ DSI(DSI_WR_DATA) = 0x739; // MIPI_DSI_DCS_LONG_WRITE: 7 bytes.
+ if (_panel_id == PANEL_INL_P062CCA_AZ1)
+ DSI(DSI_WR_DATA) = 0x751548B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT5 / XDK, VRH gamma volt adj 53 / x40).
+ else // PANEL_AUO_A062TAN01.
+ DSI(DSI_WR_DATA) = 0x711148B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
+ DSI(DSI_WR_DATA) = 0x143209; // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32).
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+ usleep(5000);
+ break;
+
+ case PANEL_INL_2J055IA_27A:
+ case PANEL_AUO_A055TAN01:
+ case PANEL_SHP_LQ055T1SW10:
+ default: // Allow spare part displays to work.
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 120000);
+ break;
+ }
+
+ // Unblank display.
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_SET_DISPLAY_ON, 20000);
+
+ // Switch to DSI HS mode.
+ // DIVM: 1, DIVN: 24, DIVP: 1. PLLD_OUT: 468.0 MHz, PLLD_OUT0 (DSI-BCLK): 234.0 MHz. (PCLK: 78 MHz)
+ clock_enable_plld(1, 24, false, tegra_t210);
+
+ // Set HS PHY timing and finalize DSI packet sequence configuration.
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_seq_pkt_video_non_burst_no_eot_config, ARRAY_SIZE(_di_dsi_seq_pkt_video_non_burst_no_eot_config));
+
+ // Set 1-by-1 pixel/clock and pixel clock to 234 / 3 = 78 MHz. For 60 Hz refresh rate.
+ DISPLAY_A(DC_DISP_DISP_CLOCK_CONTROL) = PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4); // div3. Default: div4.
+
+ // Set DSI mode to HOST.
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_host_mode_config, ARRAY_SIZE(_di_dsi_host_mode_config));
+ usleep(10000);
+
+ /*
+ * Calibrate display communication pads.
+ * When switching to the 16ff pad brick, the clock lane termination control
+ * is separated from data lane termination. This change of the mipi cal
+ * brings in a bug that the DSI pad clock termination code can't be loaded
+ * in one time calibration on T210B01. Trigger calibration twice.
+ */
+ reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_pad_cal_config, ARRAY_SIZE(_di_mipi_pad_cal_config));
+ for (u32 i = 0; i < 2; i++)
+ {
+ // Set MIPI bias pad config.
+ MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG2) = 0x10010;
+ MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG1) = tegra_t210 ? 0x300 : 0;
+
+ // Set pad trimmers and set MIPI DSI cal offsets.
+ if (tegra_t210)
+ {
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_pad_cal_config_t210, ARRAY_SIZE(_di_dsi_pad_cal_config_t210));
+ reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_dsi_cal_prod_config_t210, ARRAY_SIZE(_di_mipi_dsi_cal_prod_config_t210));
+ }
+ else
+ {
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_pad_cal_config_t210b01, ARRAY_SIZE(_di_dsi_pad_cal_config_t210b01));
+ reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_dsi_cal_prod_config_t210b01, ARRAY_SIZE(_di_mipi_dsi_cal_prod_config_t210b01));
+ }
+
+ // Reset all unused MIPI cal offsets.
+ reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_dsi_cal_unused_config, ARRAY_SIZE(_di_mipi_dsi_cal_unused_config));
+
+ // Set Prescale/filter and start calibration.
+ MIPI_CAL(MIPI_CAL_MIPI_CAL_CTRL) = 0x2A000001;
+ }
+ usleep(10000);
+
+ // Setup video mode.
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_dc_video_mode_config, ARRAY_SIZE(_di_dc_video_mode_config));
+}
+
+void display_backlight_pwm_init()
+{
+ if (_panel_id == PANEL_SAM_AMS699VC01)
+ return;
+
+ // Enable PWM clock.
+ clock_enable_pwm();
+
+ // Enable PWM and set it to 25KHz PFM. 29.5KHz is stock.
+ PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN;
+
+ PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) & ~PINMUX_FUNC_MASK) | 1; // Set PWM0 mode.
+ usleep(2);
+
+ gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); // Backlight power mode.
+}
+
+void display_backlight(bool enable)
+{
+ if (_panel_id == PANEL_SAM_AMS699VC01)
+ return;
+
+ // Backlight PWM GPIO.
+ gpio_write(GPIO_PORT_V, GPIO_PIN_0, enable ? GPIO_HIGH : GPIO_LOW);
+}
+
+static void _display_dsi_backlight_brightness(u32 duty)
+{
+ if (DISPLAY_A(DC_DCS_BACKLIGHT_LEVEL) == duty)
+ return;
+
+ // Convert duty to candela.
+ u32 candela = duty * PANEL_SM_BL_CANDELA_MAX / 255;
+
+ u16 bl_ctrl = byte_swap_16((u16)candela);
+ display_dsi_vblank_write(MIPI_DCS_SET_BRIGHTNESS, 2, &bl_ctrl);
+
+ // Wait for backlight to completely turn off. 6 frames.
+ if (!duty)
+ usleep(100000);
+
+ DISPLAY_A(DC_DCS_BACKLIGHT_LEVEL) = duty;
+}
+
+static void _display_pwm_backlight_brightness(u32 duty, u32 step_delay)
+{
+ u32 old_value = (PWM(PWM_CONTROLLER_PWM_CSR_0) >> 16) & 0xFF;
+ if (duty == old_value)
+ return;
+
+ if (old_value < duty)
+ {
+ for (u32 i = old_value; i <= duty; i++)
+ {
+ PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN | (i << 16);
+ usleep(step_delay);
+ }
+ }
+ else
+ {
+ for (int i = old_value; i >= (int)duty; i--)
+ {
+ PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN | (i << 16);
+ usleep(step_delay);
+ }
+ }
+}
+
+void display_backlight_brightness(u32 brightness, u32 step_delay)
+{
+ if (brightness > 255)
+ brightness = 255;
+
+ if (_panel_id != PANEL_SAM_AMS699VC01)
+ _display_pwm_backlight_brightness(brightness, step_delay);
+ else
+ _display_dsi_backlight_brightness(brightness);
+}
+
+static void _display_panel_and_hw_end(bool no_panel_deinit)
+{
+ if (no_panel_deinit)
+ goto skip_panel_deinit;
+
+ display_backlight_brightness(0, 1000);
+
+ // Enable host cmd packets during video.
+ DSI(DSI_VIDEO_MODE_CONTROL) = DSI_CMD_PKT_VID_ENABLE;
+
+ // Blank display.
+ DSI(DSI_WR_DATA) = (MIPI_DCS_SET_DISPLAY_OFF << 8) | MIPI_DSI_DCS_SHORT_WRITE;
+
+ // Wait for 5 frames (HOST1X_CH0_SYNC_SYNCPT_9).
+ // Not here. Wait for 1 frame + transmission manually.
+ usleep((_panel_id == PANEL_SAM_AMS699VC01) ? (15933 + 195) : (16666 + 230));
+
+ // Propagate changes to all register buffers and disable host cmd packets during video.
+ DISPLAY_A(DC_CMD_STATE_ACCESS) = READ_MUX_ACTIVE | WRITE_MUX_ACTIVE;
+ DSI(DSI_VIDEO_MODE_CONTROL) = 0;
+
+ // De-initialize video controller.
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_dc_video_disable_config, ARRAY_SIZE(_di_dc_video_disable_config));
+
+ // Set DISP1 clock source, parent clock and DSI/PCLK to command mode.
+ // T210: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 100.0 MHz, PLLD_OUT0 (DSI-BCLK): 50.0 MHz. (PCLK: 16.66 MHz)
+ // T210B01: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 97.8 MHz, PLLD_OUT0 (DSI-BCLK): 48.9 MHz. (PCLK: 16.30 MHz)
+ clock_enable_plld(3, 20, true, hw_get_chip_id() == GP_HIDREV_MAJOR_T210);
+
+ // DSI soft reset.
+ _display_dsi_soft_reset();
+
+ // Set DSI LP timings.
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_timing_lp_config, ARRAY_SIZE(_di_dsi_timing_lp_config));
+
+ if (_panel_id != PANEL_SAM_AMS699VC01)
+ usleep(10000);
+
+ // De-initialize display panel.
+ switch (_panel_id)
+ {
+ case PANEL_JDI_XXX062M:
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_panel_deinit_config_jdi, ARRAY_SIZE(_di_dsi_panel_deinit_config_jdi));
+ break;
+
+ case PANEL_AUO_A062TAN01:
+ reg_write_array((vu32 *)DSI_BASE, _di_dsi_panel_deinit_config_auo, ARRAY_SIZE(_di_dsi_panel_deinit_config_auo));
+ usleep(5000);
+ break;
+
+ case PANEL_INL_2J055IA_27A:
+ case PANEL_AUO_A055TAN01:
+ case PANEL_SHP_LQ055T1SW10:
+ // Unlock extension cmds.
+ DSI(DSI_WR_DATA) = 0x439; // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ DSI(DSI_WR_DATA) = 0x9483FFB9; // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+ usleep(5000);
+
+ // Set Power control.
+ DSI(DSI_WR_DATA) = 0xB39; // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
+ if (_panel_id == PANEL_INL_2J055IA_27A)
+ DSI(DSI_WR_DATA) = 0x751548B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT5 / XDK, VRH gamma volt adj 53 / x40).
+ else if (_panel_id == PANEL_AUO_A055TAN01)
+ DSI(DSI_WR_DATA) = 0x711148B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
+ else // PANEL_SHP_LQ055T1SW10.
+ DSI(DSI_WR_DATA) = 0x731348B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT3 / XDK, VRH gamma volt adj 51 / x40).
+ if (_panel_id == PANEL_INL_2J055IA_27A || _panel_id == PANEL_AUO_A055TAN01)
+ {
+ // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32, Enter standby / PON / VCOMG).
+ DSI(DSI_WR_DATA) = 0x71143209;
+ DSI(DSI_WR_DATA) = 0x114D31; // (Unknown).
+ }
+ else // PANEL_SHP_LQ055T1SW10.
+ {
+ // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/48, Enter standby / PON / VCOMG).
+ DSI(DSI_WR_DATA) = 0x71243209;
+ DSI(DSI_WR_DATA) = 0x004C31; // (Unknown).
+ }
+ DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
+ usleep(5000);
+ break;
+
+ case PANEL_INL_P062CCA_AZ1:
+ case PANEL_SAM_AMS699VC01:
+ default:
+ break;
+ }
+
+ // Blank - powerdown.
+ _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_ENTER_SLEEP_MODE,
+ (_panel_id == PANEL_SAM_AMS699VC01) ? 120000 : 50000);
+
+skip_panel_deinit:
+ // Disable Panel Reset.
+ gpio_write(GPIO_PORT_V, GPIO_PIN_2, GPIO_LOW);
+ usleep(10000);
+
+ if (!_nx_aula) // HOS uses panel id.
+ {
+ // Disable LCD driver AVDD channels.
+ gpio_write(GPIO_PORT_I, GPIO_PIN_0 | GPIO_PIN_1, GPIO_LOW);
+
+ // Make sure LCD driver PWM pin is in PWM0 mode.
+ gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); // Backlight PWM.
+ PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = PINMUX_TRISTATE | PINMUX_PULL_DOWN | 1; // Set PWM0 mode.
+ }
+ usleep(10000);
+
+ // Disable Display Interface specific clocks.
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_SET) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_CLR) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_DISP1);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_DISP1);
+
+ // Power down.
+ DSI(DSI_PAD_CONTROL_0) = DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) |
+ DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF);
+ _display_dsi_soft_reset();
+
+ // Disable DSI AVDD.
+ max7762x_regulator_enable(REGULATOR_LDO0, false);
+}
+
+void display_end() { _display_panel_and_hw_end(false); };
+
+u32 display_get_verbose_panel_id()
+{
+ return _panel_id_raw;
+}
+
+u16 display_get_decoded_panel_id()
+{
+ return _panel_id;
+}
+
+void display_set_decoded_panel_id(u32 id)
+{
+ // Get Hardware type, as it's used in various DI functions.
+ _nx_aula = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
+
+ // Decode Display ID.
+ _panel_id = ((id >> 8) & 0xFF00) | (id & 0xFF);
+
+ if ((_panel_id & 0xFF) == PANEL_JDI_XXX062M)
+ _panel_id = PANEL_JDI_XXX062M;
+
+ // For Aula ensure that we have a compatible panel id.
+ if (_nx_aula && _panel_id == 0xCCCC)
+ _panel_id = PANEL_SAM_AMS699VC01;
+}
+
+void display_color_screen(u32 color)
+{
+ // Disable all windows.
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_win_one_color, ARRAY_SIZE(_di_win_one_color));
+
+ // Configure display to show single color.
+ DISPLAY_A(DC_DISP_BLEND_BACKGROUND_COLOR) = color;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE | WIN_D_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ | WIN_D_ACT_REQ;
+ usleep(35000); // Wait 2 frames. No need on Aula.
+
+ if (_panel_id != PANEL_SAM_AMS699VC01)
+ display_backlight(true);
+ else
+ display_backlight_brightness(150, 0);
+}
+
+u32 *display_init_window_a_pitch()
+{
+ // Sanitize framebuffer area.
+ memset((u32 *)IPL_FB_ADDRESS, 0, IPL_FB_SZ);
+
+ // This configures the framebuffer @ IPL_FB_ADDRESS with a resolution of 720x1280 (line stride 720).
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_pitch, ARRAY_SIZE(_di_winA_pitch));
+ //usleep(35000); // Wait 2 frames. No need on Aula.
+
+ return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
+}
+
+u32 *display_init_window_a_pitch_vic()
+{
+ // This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 720x1280 (line stride 720).
+ if (_panel_id != PANEL_SAM_AMS699VC01)
+ usleep(8000); // Wait half frame for PWM to apply.
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_pitch_vic, ARRAY_SIZE(_di_winA_pitch_vic));
+ if (_panel_id != PANEL_SAM_AMS699VC01)
+ usleep(35000); // Wait 2 frames.
+
+ return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
+}
+
+u32 *display_init_window_a_pitch_inv()
+{
+ // This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 720x1280 (line stride 720).
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_pitch_inv, ARRAY_SIZE(_di_winA_pitch_inv));
+ usleep(35000); // Wait 2 frames. No need on Aula.
+
+ return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
+}
+
+u32 *display_init_window_a_block()
+{
+ // This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 720x1280.
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_block, ARRAY_SIZE(_di_winA_block));
+ usleep(35000); // Wait 2 frames. No need on Aula.
+
+ return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
+}
+
+u32 *display_init_window_d_console()
+{
+ // This configures the framebuffer @ LOG_FB_ADDRESS with a resolution of 1280x720 (line stride 720).
+ reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winD_log, ARRAY_SIZE(_di_winD_log));
+
+ return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
+}
+
+void display_window_disable(u32 window)
+{
+ // Select window C.
+ DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = BIT(WINDOW_SELECT + window);
+
+ // Disable window C.
+ DISPLAY_A(DC_WIN_WIN_OPTIONS) = 0;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | BIT(WIN_UPDATE + window);
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | BIT(WIN_ACT_REQ + window);
+}
+
+void display_set_framebuffer(u32 window, void *fb)
+{
+ // Select window.
+ DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = BIT(WINDOW_SELECT + window);
+
+ // Set new fb address.
+ DISPLAY_A(DC_WINBUF_START_ADDR) = (u32)fb;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | BIT(WIN_UPDATE + window);
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | BIT(WIN_ACT_REQ + window);
+}
+
+void display_move_framebuffer(u32 window, void *fb)
+{
+ // Select window.
+ DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = BIT(WINDOW_SELECT + window);
+
+ // Get current framebuffer address.
+ const void *fb_curr = (void *)DISPLAY_A(DC_WINBUF_START_ADDR);
+ u32 win_size = DISPLAY_A(DC_WIN_PRESCALED_SIZE);
+ win_size = (win_size & 0x7FFF) * ((win_size >> 16) & 0x1FFF);
+
+ // Copy fb over.
+ memcpy(fb, fb_curr, win_size);
+
+ // Set new fb address.
+ DISPLAY_A(DC_WINBUF_START_ADDR) = (u32)fb;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | BIT(WIN_UPDATE + window);
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | BIT(WIN_ACT_REQ + window);
+}
+
+void display_window_d_console_enable()
+{
+ // Only update active registers on vsync.
+ DISPLAY_A(DC_CMD_REG_ACT_CONTROL) = DISPLAY_A(DC_CMD_REG_ACT_CONTROL) & ~WIN_D_ACT_HCNTR_SEL;
+
+ // Select window D.
+ DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = WINDOW_D_SELECT;
+
+ // Enable and setup window D.
+ DISPLAY_A(DC_WIN_WIN_OPTIONS) = WIN_ENABLE;
+ DISPLAY_A(DC_WIN_POSITION) = 0xFF80; // X: -128.
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
+
+ // Pull-down effect.
+ for (u32 i = 0xFF80; i < 0x10000; i++)
+ {
+ // Set window position.
+ DISPLAY_A(DC_WIN_POSITION) = i & 0xFFFF;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
+ usleep(1000);
+ }
+
+ DISPLAY_A(DC_WIN_POSITION) = 0;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
+}
+
+void display_window_d_console_disable()
+{
+ // Select window D.
+ DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = WINDOW_D_SELECT;
+
+ // Pull-up effect.
+ for (u32 i = 0xFFFF; i > 0xFF7F; i--)
+ {
+ // Set window position.
+ DISPLAY_A(DC_WIN_POSITION) = i & 0xFFFF;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
+ usleep(500);
+ }
+
+ // Disable window D.
+ DISPLAY_A(DC_WIN_POSITION) = 0;
+ DISPLAY_A(DC_WIN_WIN_OPTIONS) = 0;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
+}
+
+void display_cursor_init(void *crs_fb, u32 size)
+{
+ // Setup cursor.
+ DISPLAY_A(DC_DISP_CURSOR_START_ADDR) = CURSOR_CLIPPING(CURSOR_CLIP_WIN_A) | size | ((u32)crs_fb >> 10);
+ DISPLAY_A(DC_DISP_BLEND_CURSOR_CONTROL) = CURSOR_BLEND_R8G8B8A8 |
+ CURSOR_BLEND_DST_FACTOR(CURSOR_BLEND_K1) |
+ CURSOR_BLEND_SRC_FACTOR(CURSOR_BLEND_K1) | 0xFF;
+
+ // Enable cursor window.
+ DISPLAY_A(DC_DISP_DISP_WIN_OPTIONS) |= CURSOR_ENABLE;
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | CURSOR_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
+}
+
+void display_cursor_set_pos(u32 x, u32 y)
+{
+ // Set cursor position.
+ DISPLAY_A(DC_DISP_CURSOR_POSITION) = x | (y << 16);
+
+ // Arm and activate changes.
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | CURSOR_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
+}
+
+void display_cursor_deinit()
+{
+ DISPLAY_A(DC_DISP_BLEND_CURSOR_CONTROL) = 0;
+ DISPLAY_A(DC_DISP_DISP_WIN_OPTIONS) &= ~CURSOR_ENABLE;
+
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | CURSOR_UPDATE;
+ DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
+}
diff --git a/bdk/display/di.h b/bdk/display/di.h
index 11085a0..66eaa9a 100644
--- a/bdk/display/di.h
+++ b/bdk/display/di.h
@@ -1,742 +1,913 @@
-/*
- * Copyright (c) 2018 naehrwert
- * Copyright (c) 2018-2021 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef _DI_H_
-#define _DI_H_
-
-#include
-#include
-
-#define DSI_VIDEO_DISABLED 0
-#define DSI_VIDEO_ENABLED 1
-
-/*! Display registers. */
-#define _DIREG(reg) ((reg) * 4)
-
-// Display controller scratch registers.
-#define DC_D_WINBUF_DD_SCRATCH_REGISTER_0 0xED
-#define DC_D_WINBUF_DD_SCRATCH_REGISTER_1 0xEE
-#define DC_T_WINBUF_TD_SCRATCH_REGISTER_0 0x16D
-#define DC_T_WINBUF_TD_SCRATCH_REGISTER_1 0x16E
-#define DC_COM_SCRATCH_REGISTER_A 0x325
-#define DC_COM_SCRATCH_REGISTER_B 0x326
-#define DC_A_WINBUF_AD_SCRATCH_REGISTER_0 0xBED
-#define DC_A_WINBUF_AD_SCRATCH_REGISTER_1 0xBEE
-#define DC_B_WINBUF_BD_SCRATCH_REGISTER_0 0xDED
-#define DC_B_WINBUF_BD_SCRATCH_REGISTER_1 0xDEE
-#define DC_C_WINBUF_CD_SCRATCH_REGISTER_0 0xFED
-#define DC_C_WINBUF_CD_SCRATCH_REGISTER_1 0xFEE
-
-// DC_CMD non-shadowed command/sync registers.
-#define DC_CMD_GENERAL_INCR_SYNCPT 0x00
-
-#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x01
-#define SYNCPT_CNTRL_SOFT_RESET BIT(0)
-#define SYNCPT_CNTRL_NO_STALL BIT(8)
-
-#define DC_CMD_CONT_SYNCPT_VSYNC 0x28
-#define SYNCPT_VSYNC_ENABLE BIT(8)
-
-#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
-
-#define DC_CMD_DISPLAY_COMMAND 0x32
-#define DISP_CTRL_MODE_STOP (0 << 5)
-#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
-#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
-#define DISP_CTRL_MODE_MASK (3 << 5)
-
-#define DC_CMD_DISPLAY_POWER_CONTROL 0x36
-#define PW0_ENABLE BIT(0)
-#define PW1_ENABLE BIT(2)
-#define PW2_ENABLE BIT(4)
-#define PW3_ENABLE BIT(6)
-#define PW4_ENABLE BIT(8)
-#define PM0_ENABLE BIT(16)
-#define PM1_ENABLE BIT(18)
-
-#define DC_CMD_INT_STATUS 0x37
-#define DC_CMD_INT_MASK 0x38
-#define DC_CMD_INT_ENABLE 0x39
-#define DC_CMD_INT_FRAME_END_INT BIT(1)
-
-#define DC_CMD_STATE_ACCESS 0x40
-#define READ_MUX BIT(0)
-#define WRITE_MUX BIT(2)
-
-#define DC_CMD_STATE_CONTROL 0x41
-#define GENERAL_ACT_REQ BIT(0)
-#define WIN_A_ACT_REQ BIT(1)
-#define WIN_B_ACT_REQ BIT(2)
-#define WIN_C_ACT_REQ BIT(3)
-#define WIN_D_ACT_REQ BIT(4)
-#define CURSOR_ACT_REQ BIT(7)
-#define GENERAL_UPDATE BIT(8)
-#define WIN_A_UPDATE BIT(9)
-#define WIN_B_UPDATE BIT(10)
-#define WIN_C_UPDATE BIT(11)
-#define WIN_D_UPDATE BIT(12)
-#define CURSOR_UPDATE BIT(15)
-#define NC_HOST_TRIG BIT(24)
-
-#define DC_CMD_DISPLAY_WINDOW_HEADER 0x42
-#define WINDOW_A_SELECT BIT(4)
-#define WINDOW_B_SELECT BIT(5)
-#define WINDOW_C_SELECT BIT(6)
-#define WINDOW_D_SELECT BIT(7)
-
-#define DC_CMD_REG_ACT_CONTROL 0x043
-
-// DC_D_WIN_DD window D instance of DC_WIN
-#define DC_D_WIN_DD_WIN_OPTIONS 0x80
-#define DC_D_WIN_DD_COLOR_DEPTH 0x83
-#define DC_D_WIN_DD_POSITION 0x84
-#define DC_D_WIN_DD_SIZE 0x85
-#define DC_D_WIN_DD_LINE_STRIDE 0x8A
-#define DC_D_WIN_DD_BLEND_LAYER_CONTROL 0x96
-#define DC_D_WIN_DD_BLEND_MATCH_SELECT 0x97
-#define DC_D_WIN_DD_BLEND_ALPHA_1BIT 0x99
-
-// DC_D_WINBUF_DD window D instance of DC_WINBUF
-#define DC_D_WINBUF_DD_START_ADDR 0xC0
-#define DC_D_WINBUF_DD_ADDR_H_OFFSET 0xC6
-#define DC_D_WINBUF_DD_ADDR_V_OFFSET 0xC8
-#define DC_D_WINBUF_DD_START_ADDR_HI 0xCD
-#define DC_D_WINBUF_DD_MEMFETCH_CONTROL 0xEB
-
-// DC_T_WIN_TD macro for using DD defines.
-#define _DC_T(reg) ((reg) + 0x80)
-
-// DC_COM non-shadowed registers.
-#define DC_COM_CRC_CONTROL 0x300
-#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
-#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
-
-#define DC_COM_DSC_TOP_CTL 0x33E
-
-// DC_DISP shadowed registers.
-#define DC_DISP_DISP_WIN_OPTIONS 0x402
-#define CURSOR_ENABLE BIT(16)
-#define SOR_ENABLE BIT(25)
-#define SOR1_ENABLE BIT(26)
-#define SOR1_TIMING_CYA BIT(27)
-#define DSI_ENABLE BIT(29)
-#define HDMI_ENABLE BIT(30)
-
-
-#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403
-#define DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER 0x404
-#define DC_DISP_DISP_TIMING_OPTIONS 0x405
-#define DC_DISP_REF_TO_SYNC 0x406
-#define DC_DISP_SYNC_WIDTH 0x407
-#define DC_DISP_BACK_PORCH 0x408
-#define DC_DISP_ACTIVE 0x409
-#define DC_DISP_FRONT_PORCH 0x40A
-
-#define DC_DISP_DISP_CLOCK_CONTROL 0x42E
-#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff)
-#define PIXEL_CLK_DIVIDER_PCD1 (0 << 8)
-#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8)
-#define PIXEL_CLK_DIVIDER_PCD2 (2 << 8)
-#define PIXEL_CLK_DIVIDER_PCD3 (3 << 8)
-#define PIXEL_CLK_DIVIDER_PCD4 (4 << 8)
-#define PIXEL_CLK_DIVIDER_PCD6 (5 << 8)
-#define PIXEL_CLK_DIVIDER_PCD8 (6 << 8)
-#define PIXEL_CLK_DIVIDER_PCD9 (7 << 8)
-#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8)
-#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8)
-#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8)
-#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8)
-#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8)
-
-#define DC_DISP_DISP_INTERFACE_CONTROL 0x42F
-#define DISP_DATA_FORMAT_DF1P1C (0 << 0)
-#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0)
-#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0)
-#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0)
-#define DISP_DATA_FORMAT_DF2S (4 << 0)
-#define DISP_DATA_FORMAT_DF3S (5 << 0)
-#define DISP_DATA_FORMAT_DFSPI (6 << 0)
-#define DISP_DATA_FORMAT_DF1P3C24B (7 << 0)
-#define DISP_DATA_FORMAT_DF1P3C18B (8 << 0)
-#define DISP_ALIGNMENT_MSB (0 << 8)
-#define DISP_ALIGNMENT_LSB (1 << 8)
-#define DISP_ORDER_RED_BLUE (0 << 9)
-#define DISP_ORDER_BLUE_RED (1 << 9)
-
-#define DC_DISP_DISP_COLOR_CONTROL 0x430
-#define DITHER_CONTROL_MASK (3 << 8)
-#define DITHER_CONTROL_DISABLE (0 << 8)
-#define DITHER_CONTROL_ORDERED (2 << 8)
-#define DITHER_CONTROL_ERRDIFF (3 << 8)
-#define BASE_COLOR_SIZE_MASK (0xf << 0)
-#define BASE_COLOR_SIZE_666 (0 << 0)
-#define BASE_COLOR_SIZE_111 (1 << 0)
-#define BASE_COLOR_SIZE_222 (2 << 0)
-#define BASE_COLOR_SIZE_333 (3 << 0)
-#define BASE_COLOR_SIZE_444 (4 << 0)
-#define BASE_COLOR_SIZE_555 (5 << 0)
-#define BASE_COLOR_SIZE_565 (6 << 0)
-#define BASE_COLOR_SIZE_332 (7 << 0)
-#define BASE_COLOR_SIZE_888 (8 << 0)
-
-#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
-#define SC0_H_QUALIFIER_NONE BIT(0)
-#define SC1_H_QUALIFIER_NONE BIT(16)
-
-#define DC_DISP_DATA_ENABLE_OPTIONS 0x432
-#define DE_SELECT_ACTIVE_BLANK (0 << 0)
-#define DE_SELECT_ACTIVE (1 << 0)
-#define DE_SELECT_ACTIVE_IS (2 << 0)
-#define DE_CONTROL_ONECLK (0 << 2)
-#define DE_CONTROL_NORMAL (1 << 2)
-#define DE_CONTROL_EARLY_EXT (2 << 2)
-#define DE_CONTROL_EARLY (3 << 2)
-#define DE_CONTROL_ACTIVE_BLANK (4 << 2)
-
-// Cursor configuration registers.
-#define DC_DISP_CURSOR_FOREGROUND 0x43C
-#define DC_DISP_CURSOR_BACKGROUND 0x43D
-#define CURSOR_COLOR(r,g,b) (((r) & 0xFF) | (((g) & 0xFF) << 8) | (((b) & 0xFF) << 16))
-
-#define DC_DISP_CURSOR_START_ADDR 0x43E
-#define CURSOR_CLIPPING(w) ((w) << 28)
-#define CURSOR_CLIP_WIN_A 1
-#define CURSOR_CLIP_WIN_B 2
-#define CURSOR_CLIP_WIN_C 3
-#define CURSOR_SIZE_32 (0 << 24)
-#define CURSOR_SIZE_64 (1 << 24)
-#define CURSOR_SIZE_128 (2 << 24)
-#define CURSOR_SIZE_256 (3 << 24)
-#define DC_DISP_CURSOR_POSITION 0x440
-#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4E4
-#define DC_DISP_CURSOR_START_ADDR_HI 0x4EC
-#define DC_DISP_BLEND_CURSOR_CONTROL 0x4F1
-#define CURSOR_BLEND_2BIT (0 << 24)
-#define CURSOR_BLEND_R8G8B8A8 (1 << 24)
-#define CURSOR_BLEND_SRC_FACTOR(n) ((n) << 8)
-#define CURSOR_BLEND_DST_FACTOR(n) ((n) << 16)
-#define CURSOR_BLEND_ZRO 0
-#define CURSOR_BLEND_K1 1
-#define CURSOR_BLEND_NK1 2
-// End of cursor cfg regs.
-
-#define DC_DISP_DC_MCCIF_FIFOCTRL 0x480
-#define DC_DISP_SD_BL_PARAMETERS 0x4D7
-#define DC_DISP_SD_BL_CONTROL 0x4DC
-#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4E4
-
-#define DC_WIN_CSC_YOF 0x611
-#define DC_WIN_CSC_KYRGB 0x612
-#define DC_WIN_CSC_KUR 0x613
-#define DC_WIN_CSC_KVR 0x614
-#define DC_WIN_CSC_KUG 0x615
-#define DC_WIN_CSC_KVG 0x616
-#define DC_WIN_CSC_KUB 0x617
-#define DC_WIN_CSC_KVB 0x618
-#define DC_WIN_AD_WIN_OPTIONS 0xB80
-#define DC_WIN_BD_WIN_OPTIONS 0xD80
-#define DC_WIN_CD_WIN_OPTIONS 0xF80
-
-// The following registers are A/B/C shadows of the 0xB80/0xD80/0xF80 registers (see DISPLAY_WINDOW_HEADER).
-#define DC_WIN_WIN_OPTIONS 0x700
-#define H_DIRECTION BIT(0)
-#define V_DIRECTION BIT(2)
-#define SCAN_COLUMN BIT(4)
-#define COLOR_EXPAND BIT(6)
-#define CSC_ENABLE BIT(18)
-#define WIN_ENABLE BIT(30)
-
-#define DC_WIN_BUFFER_CONTROL 0x702
-#define BUFFER_CONTROL_HOST 0
-#define BUFFER_CONTROL_VI 1
-#define BUFFER_CONTROL_EPP 2
-#define BUFFER_CONTROL_MPEGE 3
-#define BUFFER_CONTROL_SB2D 4
-
-#define DC_WIN_COLOR_DEPTH 0x703
-#define WIN_COLOR_DEPTH_P1 0x0
-#define WIN_COLOR_DEPTH_P2 0x1
-#define WIN_COLOR_DEPTH_P4 0x2
-#define WIN_COLOR_DEPTH_P8 0x3
-#define WIN_COLOR_DEPTH_B4G4R4A4 0x4
-#define WIN_COLOR_DEPTH_B5G5R5A 0x5
-#define WIN_COLOR_DEPTH_B5G6R5 0x6
-#define WIN_COLOR_DEPTH_AB5G5R5 0x7
-#define WIN_COLOR_DEPTH_B8G8R8A8 0xC
-#define WIN_COLOR_DEPTH_R8G8B8A8 0xD
-#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 0xE
-#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 0xF
-#define WIN_COLOR_DEPTH_YCbCr422 0x10
-#define WIN_COLOR_DEPTH_YUV422 0x11
-#define WIN_COLOR_DEPTH_YCbCr420P 0x12
-#define WIN_COLOR_DEPTH_YUV420P 0x13
-#define WIN_COLOR_DEPTH_YCbCr422P 0x14
-#define WIN_COLOR_DEPTH_YUV422P 0x15
-#define WIN_COLOR_DEPTH_YCbCr422R 0x16
-#define WIN_COLOR_DEPTH_YUV422R 0x17
-#define WIN_COLOR_DEPTH_YCbCr422RA 0x18
-#define WIN_COLOR_DEPTH_YUV422RA 0x19
-
-#define DC_WIN_POSITION 0x704
-#define H_POSITION(x) (((x) & 0xFfff) << 0)
-#define V_POSITION(x) (((x) & 0x1fff) << 16)
-
-#define DC_WIN_SIZE 0x705
-#define H_SIZE(x) (((x) & 0x1fff) << 0)
-#define V_SIZE(x) (((x) & 0x1fff) << 16)
-
-#define DC_WIN_PRESCALED_SIZE 0x706
-#define H_PRESCALED_SIZE(x) (((x) & 0x7fff) << 0)
-#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16)
-
-#define DC_WIN_H_INITIAL_DDA 0x707
-#define DC_WIN_V_INITIAL_DDA 0x708
-
-#define DC_WIN_DDA_INC 0x709
-#define H_DDA_INC(x) (((x) & 0xffff) << 0)
-#define V_DDA_INC(x) (((x) & 0xffff) << 16)
-
-#define DC_WIN_LINE_STRIDE 0x70A
-#define LINE_STRIDE(x) (x)
-#define UV_LINE_STRIDE(x) (((x) & 0xffff) << 16)
-#define DC_WIN_DV_CONTROL 0x70E
-
-#define DC_WINBUF_BLEND_LAYER_CONTROL 0x716
-#define WIN_K1(x) (((x) & 0xff) << 8)
-#define WIN_K2(x) (((x) & 0xff) << 16)
-#define WIN_BLEND_ENABLE (0 << 24)
-#define WIN_BLEND_BYPASS (1 << 24)
-
-#define DC_WINBUF_BLEND_MATCH_SELECT 0x717
-#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_ZERO (0 << 0)
-#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_ONE (1 << 0)
-#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 (2 << 0)
-#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_DST (3 << 0)
-#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_NEG_K1_TIMES_DST (4 << 0)
-#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_SRC (5 << 0)
-
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_ZERO (0 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_ONE (1 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_K1 (2 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_K2 (3 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_K1_TIMES_DST (4 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_DST (5 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC (6 << 4)
-#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1 (7 << 4)
-
-#define WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_ZERO (0 << 8)
-#define WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K1 (1 << 8)
-#define WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 (2 << 8)
-
-#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO (0 << 12)
-#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ONE (1 << 12)
-#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_NEG_K1_TIMES_SRC (2 << 12)
-#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_K2 (3 << 12)
-
-#define DC_WINBUF_BLEND_ALPHA_1BIT 0x719
-#define WIN_ALPHA_1BIT_WEIGHT0(x) (((x) & 0xff) << 0)
-#define WIN_ALPHA_1BIT_WEIGHT1(x) (((x) & 0xff) << 8)
-
-/*! The following registers are A/B/C shadows of the 0xBC0/0xDC0/0xFC0 registers (see DISPLAY_WINDOW_HEADER). */
-#define DC_WINBUF_START_ADDR 0x800
-#define DC_WINBUF_ADDR_H_OFFSET 0x806
-#define DC_WINBUF_ADDR_V_OFFSET 0x808
-#define DC_WINBUF_SURFACE_KIND 0x80B
-#define PITCH (0 << 0)
-#define TILED (1 << 0)
-#define BLOCK (2 << 0)
-#define BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
-
-/*! Display serial interface registers. */
-#define _DSIREG(reg) ((reg) * 4)
-
-#define DSI_INCR_SYNCPT_CNTRL 0x1
-#define DSI_INCR_SYNCPT_SOFT_RESET BIT(0)
-#define DSI_INCR_SYNCPT_NO_STALL BIT(8)
-
-#define DSI_RD_DATA 0x9
-#define DSI_WR_DATA 0xA
-
-#define DSI_POWER_CONTROL 0xB
-#define DSI_POWER_CONTROL_ENABLE 1
-
-#define DSI_INT_ENABLE 0xC
-#define DSI_INT_STATUS 0xD
-#define DSI_INT_MASK 0xE
-
-#define DSI_HOST_CONTROL 0xF
-#define DSI_HOST_CONTROL_ECC BIT(0)
-#define DSI_HOST_CONTROL_CS BIT(1)
-#define DSI_HOST_CONTROL_PKT_BTA BIT(2)
-#define DSI_HOST_CONTROL_IMM_BTA BIT(3)
-#define DSI_HOST_CONTROL_FIFO_SEL BIT(4)
-#define DSI_HOST_CONTROL_HS BIT(5)
-#define DSI_HOST_CONTROL_RAW BIT(6)
-#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12)
-#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12)
-#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12)
-#define DSI_HOST_CONTROL_CRC_RESET BIT(20)
-#define DSI_HOST_CONTROL_FIFO_RESET BIT(21)
-
-#define DSI_CONTROL 0x10
-#define DSI_CONTROL_HOST_ENABLE BIT(0)
-#define DSI_CONTROL_VIDEO_ENABLE BIT(1)
-#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2)
-#define DSI_CONTROL_DCS_ENABLE BIT(3)
-#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4)
-#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8)
-#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12)
-#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16)
-#define DSI_CONTROL_HS_CLK_CTRL BIT(20)
-
-#define DSI_SOL_DELAY 0x11
-#define DSI_MAX_THRESHOLD 0x12
-
-#define DSI_TRIGGER 0x13
-#define DSI_TRIGGER_VIDEO BIT(0)
-#define DSI_TRIGGER_HOST BIT(1)
-
-#define DSI_TX_CRC 0x14
-
-#define DSI_STATUS 0x15
-#define DSI_STATUS_RX_FIFO_SIZE 0x1F
-
-#define DSI_INIT_SEQ_CONTROL 0x1A
-#define DSI_INIT_SEQ_DATA_0 0x1B
-#define DSI_INIT_SEQ_DATA_1 0x1C
-#define DSI_INIT_SEQ_DATA_2 0x1D
-#define DSI_INIT_SEQ_DATA_3 0x1E
-#define DSI_PKT_SEQ_0_LO 0x23
-#define DSI_PKT_SEQ_0_HI 0x24
-#define DSI_PKT_SEQ_1_LO 0x25
-#define DSI_PKT_SEQ_1_HI 0x26
-#define DSI_PKT_SEQ_2_LO 0x27
-#define DSI_PKT_SEQ_2_HI 0x28
-#define DSI_PKT_SEQ_3_LO 0x29
-#define DSI_PKT_SEQ_3_HI 0x2A
-#define DSI_PKT_SEQ_4_LO 0x2B
-#define DSI_PKT_SEQ_4_HI 0x2C
-#define DSI_PKT_SEQ_5_LO 0x2D
-#define DSI_PKT_SEQ_5_HI 0x2E
-#define DSI_DCS_CMDS 0x33
-#define DSI_PKT_LEN_0_1 0x34
-#define DSI_PKT_LEN_2_3 0x35
-#define DSI_PKT_LEN_4_5 0x36
-#define DSI_PKT_LEN_6_7 0x37
-#define DSI_PHY_TIMING_0 0x3C
-#define DSI_PHY_TIMING_1 0x3D
-#define DSI_PHY_TIMING_2 0x3E
-#define DSI_BTA_TIMING 0x3F
-
-#define DSI_TIMEOUT_0 0x44
-#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0)
-#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16)
-
-#define DSI_TIMEOUT_1 0x45
-#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0)
-#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16)
-
-#define DSI_TO_TALLY 0x46
-
-#define DSI_PAD_CONTROL_0 0x4B
-#define DSI_PAD_CONTROL_VS1_PDIO_CLK BIT(8)
-#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
-#define DSI_PAD_CONTROL_VS1_PULLDN_CLK BIT(24)
-#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
-
-#define DSI_PAD_CONTROL_CD 0x4C
-#define DSI_VIDEO_MODE_CONTROL 0x4E
-#define DSI_CMD_PKT_VID_ENABLE 1
-#define DSI_DSI_LINE_TYPE(x) ((x) << 1)
-
-#define DSI_PAD_CONTROL_1 0x4F
-#define DSI_PAD_CONTROL_2 0x50
-
-#define DSI_PAD_CONTROL_3 0x51
-#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0)
-#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4)
-#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8)
-#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12)
-
-#define DSI_PAD_CONTROL_4 0x52
-#define DSI_PAD_CONTROL_5_B01 0x53
-#define DSI_PAD_CONTROL_6_B01 0x54
-#define DSI_PAD_CONTROL_7_B01 0x55
-#define DSI_INIT_SEQ_DATA_15 0x5F
-#define DSI_INIT_SEQ_DATA_15_B01 0x62
-
-/*! DSI packet defines */
-#define DSI_ESCAPE_CMD 0x87
-#define DSI_ACK_NO_ERR 0x84
-
-#define ACK_ERROR_RES 0x02
-#define GEN_LONG_RD_RES 0x1A
-#define DCS_LONG_RD_RES 0x1C
-#define GEN_1_BYTE_SHORT_RD_RES 0x11
-#define DCS_1_BYTE_SHORT_RD_RES 0x21
-#define GEN_2_BYTE_SHORT_RD_RES 0x12
-#define DCS_2_BYTE_SHORT_RD_RES 0x22
-
-/*! MIPI registers. */
-#define MIPI_CAL_MIPI_CAL_CTRL (0x00 / 0x4)
-#define MIPI_CAL_CIL_MIPI_CAL_STATUS (0x08 / 0x4)
-#define MIPI_CAL_CILA_MIPI_CAL_CONFIG (0x14 / 0x4)
-#define MIPI_CAL_CILB_MIPI_CAL_CONFIG (0x18 / 0x4)
-#define MIPI_CAL_CILC_MIPI_CAL_CONFIG (0x1C / 0x4)
-#define MIPI_CAL_CILD_MIPI_CAL_CONFIG (0x20 / 0x4)
-#define MIPI_CAL_CILE_MIPI_CAL_CONFIG (0x24 / 0x4)
-#define MIPI_CAL_CILF_MIPI_CAL_CONFIG (0x28 / 0x4)
-#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG (0x38 / 0x4)
-#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG (0x3C / 0x4)
-#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG (0x40 / 0x4)
-#define MIPI_CAL_DSID_MIPI_CAL_CONFIG (0x44 / 0x4)
-#define MIPI_CAL_MIPI_BIAS_PAD_CFG0 (0x58 / 0x4)
-#define MIPI_CAL_MIPI_BIAS_PAD_CFG1 (0x5C / 0x4)
-#define MIPI_CAL_MIPI_BIAS_PAD_CFG2 (0x60 / 0x4)
-#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2 (0x64 / 0x4)
-#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2 (0x68 / 0x4)
-#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_2 (0x70 / 0x4)
-#define MIPI_CAL_DSID_MIPI_CAL_CONFIG_2 (0x74 / 0x4)
-
-/*! MIPI CMDs. */
-#define MIPI_DSI_V_SYNC_START 0x01
-#define MIPI_DSI_COLOR_MODE_OFF 0x02
-#define MIPI_DSI_END_OF_TRANSMISSION 0x08
-#define MIPI_DSI_NULL_PACKET 0x09
-#define MIPI_DSI_V_SYNC_END 0x11
-#define MIPI_DSI_COLOR_MODE_ON 0x12
-#define MIPI_DSI_BLANKING_PACKET 0x19
-#define MIPI_DSI_H_SYNC_START 0x21
-#define MIPI_DSI_SHUTDOWN_PERIPHERAL 0x22
-#define MIPI_DSI_H_SYNC_END 0x31
-#define MIPI_DSI_TURN_ON_PERIPHERAL 0x32
-#define MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE 0x37
-
-#define MIPI_DSI_DCS_SHORT_WRITE 0x05
-#define MIPI_DSI_DCS_READ 0x06
-#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15
-#define MIPI_DSI_DCS_LONG_WRITE 0x39
-
-#define MIPI_DSI_GENERIC_LONG_WRITE 0x29
-#define MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM 0x03
-#define MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM 0x13
-#define MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM 0x23
-#define MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM 0x04
-#define MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM 0x14
-#define MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM 0x24
-
-/*! MIPI DCS CMDs. */
-#define MIPI_DCS_NOP 0x00
-#define MIPI_DCS_SOFT_RESET 0x01
-#define MIPI_DCS_GET_COMPRESSION_MODE 0x03
-#define MIPI_DCS_GET_DISPLAY_ID 0x04
-#define MIPI_DCS_GET_DISPLAY_ID1 0xDA // GET_DISPLAY_ID Byte0, Module Manufacturer ID.
-#define MIPI_DCS_GET_DISPLAY_ID2 0xDB // GET_DISPLAY_ID Byte1, Module/Driver Version ID.
-#define MIPI_DCS_GET_DISPLAY_ID3 0xDC // GET_DISPLAY_ID Byte2, Module/Driver ID.
-#define MIPI_DCS_GET_NUM_ERRORS 0x05 // 1 byte.
-#define MIPI_DCS_GET_RED_CHANNEL 0x06
-#define MIPI_DCS_GET_GREEN_CHANNEL 0x07
-#define MIPI_DCS_GET_BLUE_CHANNEL 0x08
-#define MIPI_DCS_GET_DISPLAY_STATUS 0x09 // 4 bytes.
-#define MIPI_DCS_GET_POWER_MODE 0x0A // 1 byte. 2: DISON, 3: NORON, 4: SLPOUT, 7: BSTON.
-#define MIPI_DCS_GET_ADDRESS_MODE 0x0B // Display Access Control. 1 byte. 0: GS, 1: SS, 3: BGR.
-#define MIPI_DCS_GET_PIXEL_FORMAT 0x0C // 1 byte. 4-6: DPI.
-#define MIPI_DCS_GET_DISPLAY_MODE 0x0D // 1 byte. 0-2: GCS, 3: ALLPOFF, 4: ALLPON, 5: INVON.
-#define MIPI_DCS_GET_SIGNAL_MODE 0x0E // 1 byte. 0: EODSI, 2: DEON, 3: PCLKON, 4: VSON, 5: HSON, 7: TEON.
-#define MIPI_DCS_GET_DIAGNOSTIC_RESULT 0x0F // 1 byte. 6: FUNDT, 7: REGLD.
-#define MIPI_DCS_ENTER_SLEEP_MODE 0x10
-#define MIPI_DCS_EXIT_SLEEP_MODE 0x11
-#define MIPI_DCS_ENTER_PARTIAL_MODE 0x12
-#define MIPI_DCS_ENTER_NORMAL_MODE 0x13
-#define MIPI_DCS_EXIT_INVERT_MODE 0x20
-#define MIPI_DCS_ENTER_INVERT_MODE 0x21
-#define MIPI_DCS_ALL_PIXELS_OFF 0x22
-#define MIPI_DCS_ALL_PIXELS_ON 0x23
-#define MIPI_DCS_SET_CONTRAST 0x25 // VCON in 40mV steps. 7-bit integer.
-#define MIPI_DCS_SET_GAMMA_CURVE 0x26 // 1 byte. 0-7: GC.
-#define MIPI_DCS_SET_DISPLAY_OFF 0x28
-#define MIPI_DCS_SET_DISPLAY_ON 0x29
-#define MIPI_DCS_SET_COLUMN_ADDRESS 0x2A
-#define MIPI_DCS_SET_PAGE_ADDRESS 0x2B
-#define MIPI_DCS_WRITE_MEMORY_START 0x2C
-#define MIPI_DCS_WRITE_LUT 0x2D // 24-bit: 192 bytes.
-#define MIPI_DCS_READ_MEMORY_START 0x2E
-#define MIPI_DCS_SET_PARTIAL_ROWS 0x30
-#define MIPI_DCS_SET_PARTIAL_COLUMNS 0x31
-#define MIPI_DCS_SET_SCROLL_AREA 0x33
-#define MIPI_DCS_SET_TEAR_OFF 0x34
-#define MIPI_DCS_SET_TEAR_ON 0x35
-#define MIPI_DCS_SET_ADDRESS_MODE 0x36 // Display Access Control. 1 byte. 0: GS, 1: SS, 3: BGR.
-#define MIPI_DCS_SET_SCROLL_START 0x37
-#define MIPI_DCS_EXIT_IDLE_MODE 0x38
-#define MIPI_DCS_ENTER_IDLE_MODE 0x39
-#define MIPI_DCS_SET_PIXEL_FORMAT 0x3A // 1 byte. 4-6: DPI.
-#define MIPI_DCS_WRITE_MEMORY_CONTINUE 0x3C
-#define MIPI_DCS_READ_MEMORY_CONTINUE 0x3E
-#define MIPI_DCS_GET_3D_CONTROL 0x3F
-#define MIPI_DCS_SET_VSYNC_TIMING 0x40
-#define MIPI_DCS_SET_TEAR_SCANLINE 0x44
-#define MIPI_DCS_GET_SCANLINE 0x45
-#define MIPI_DCS_SET_TEAR_SCANLINE_WIDTH 0x46
-#define MIPI_DCS_GET_SCANLINE_WIDTH 0x47
-#define MIPI_DCS_SET_BRIGHTNESS 0x51 // DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL. 1 byte. 0-7: DBV.
-#define MIPI_DCS_GET_BRIGHTNESS 0x52 // 1 byte. 0-7: DBV.
-#define MIPI_DCS_SET_CONTROL_DISPLAY 0x53 // 1 byte. 2: BL, 3: DD, 5: BCTRL.
-#define MIPI_DCS_GET_CONTROL_DISPLAY 0x54 // 1 byte. 2: BL, 3: DD, 5: BCTRL.
-#define MIPI_DCS_SET_CABC_VALUE 0x55 // 1 byte. 0-32: C, 4-7: C.
-#define MIPI_DCS_GET_CABC_VALUE 0x56 // 1 byte. 0-32: C, 4-7: C.
-#define MIPI_DCS_SET_CABC_MIN_BRI 0x5E // 1 byte. 0-7: CMB.
-#define MIPI_DCS_GET_CABC_MIN_BRI 0x5F // 1 byte. 0-7: CMB.
-#define MIPI_DCS_GET_AUTO_BRI_DIAG_RES 0x68 // 1 byte. 6-7: D.
-#define MIPI_DCS_READ_DDB_START 0xA1
-#define MIPI_DCS_READ_DDB_CONTINUE 0xA8 // 0x100 size.
-
-/*! MIPI DCS Panel Private CMDs. */
-#define MIPI_DCS_PRIV_UNK_A0 0xA0
-#define MIPI_DCS_PRIV_SET_POWER_CONTROL 0xB1
-#define MIPI_DCS_PRIV_SET_EXTC 0xB9 // Enable extended commands.
-#define MIPI_DCS_PRIV_UNK_BD 0xBD
-#define MIPI_DCS_PRIV_UNK_D5 0xD5
-#define MIPI_DCS_PRIV_UNK_D6 0xD6
-#define MIPI_DCS_PRIV_UNK_D8 0xD8
-#define MIPI_DCS_PRIV_UNK_D9 0xD9
-#define MIPI_DCS_PRIV_READ_EXTC_CMD_SPI 0xFE // Read EXTC Command In SPI. 1 byte. 0-6: EXT_SPI_CNT, 7:EXT_SP.
-#define MIPI_DCS_PRIV_SET_EXTC_CMD_REG 0xFF // EXTC Command Set enable register. 5 bytes. Pass: FF 98 06 04, PAGE.
-
-/*! MIPI DCS Panel Private CMDs PAGE 1. */
-#define MIPI_DCS_PRIV_GET_DISPLAY_ID4 0x00
-#define MIPI_DCS_PRIV_GET_DISPLAY_ID5 0x01
-#define MIPI_DCS_PRIV_GET_DISPLAY_ID6 0x02
-
-/*! MIPI DCS CMD Defines. */
-#define DCS_POWER_MODE_DISPLAY_ON BIT(2)
-#define DCS_POWER_MODE_NORMAL_MODE BIT(3)
-#define DCS_POWER_MODE_SLEEP_MODE BIT(4)
-#define DCS_POWER_MODE_PARTIAL_MODE BIT(5)
-#define DCS_POWER_MODE_IDLE_MODE BIT(6)
-
-#define DCS_ADDRESS_MODE_V_FLIP BIT(0)
-#define DCS_ADDRESS_MODE_H_FLIP BIT(1)
-#define DCS_ADDRESS_MODE_LATCH_RL BIT(2) // Latch Data Order.
-#define DCS_ADDRESS_MODE_BGR_COLOR BIT(3)
-#define DCS_ADDRESS_MODE_LINE_ORDER BIT(4) // Line Refresh Order.
-#define DCS_ADDRESS_MODE_SWAP_XY BIT(5) // Page/Column Addressing Reverse Order.
-#define DCS_ADDRESS_MODE_MIRROR_X BIT(6) // Column Address Order.
-#define DCS_ADDRESS_MODE_MIRROR_Y BIT(7) // Page Address Order.
-#define DCS_ADDRESS_MODE_ROTATION_MASK (0xF << 4)
-#define DCS_ADDRESS_MODE_ROTATION_90 (DCS_ADDRESS_MODE_SWAP_XY | DCS_ADDRESS_MODE_LINE_ORDER)
-#define DCS_ADDRESS_MODE_ROTATION_180 (DCS_ADDRESS_MODE_MIRROR_X | DCS_ADDRESS_MODE_LINE_ORDER)
-#define DCS_ADDRESS_MODE_ROTATION_270 (DCS_ADDRESS_MODE_SWAP_XY)
-
-#define DCS_GAMMA_CURVE_NONE 0
-#define DCS_GAMMA_CURVE_GC0_1_8 BIT(0)
-#define DCS_GAMMA_CURVE_GC1_2_5 BIT(1)
-#define DCS_GAMMA_CURVE_GC2_1_0 BIT(2)
-#define DCS_GAMMA_CURVE_GC3_1_0 BIT(3) // Are there more?
-
-#define DCS_CONTROL_DISPLAY_BACKLIGHT_CTRL BIT(2)
-#define DCS_CONTROL_DISPLAY_DIMMING_CTRL BIT(3)
-#define DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL BIT(5)
-
-#define PANEL_OLED_BL_COEFF 82 // 82%.
-#define PANEL_OLED_BL_OFFSET 45 // Least legible backlight duty.
-
-/* Switch Panels:
- *
- * 6.2" panels for Icosa and Iowa skus:
- * [10] 81 [26]: JDI LPM062M326A
- * [10] 96 [09]: JDI LAM062M109A
- * [20] 93 [0F]: InnoLux P062CCA-AZ1 (Rev A1)
- * [20] 95 [0F]: InnoLux P062CCA-AZ2 (Rev B1)
- * [20] 96 [0F]: InnoLux P062CCA-AZ3 [UNCONFIRMED MODEL REV]
- * [20] 98 [0F]: InnoLux P062CCA-??? [UNCONFIRMED MODEL REV]
- * [30] 94 [0F]: AUO A062TAN01 (59.06A33.001)
- * [30] 95 [0F]: AUO A062TAN02 (59.06A33.002)
- * [30] XX [0F]: AUO A062TAN03 (59.06A33.003) [UNCONFIRMED ID]
- *
- * 5.5" panels for Hoag skus:
- * [20] 94 [10]: InnoLux 2J055IA-27A (Rev B1)
- * [30] 93 [10]: AUO A055TAN01 (59.05A30.001)
- * [40] XX [10]: Vendor 40 [UNCONFIRMED ID]
- *
- * 7.0" OLED panels for Aula skus:
- * [50] 9B [20]: Samsung AMS699VC01-0 (Rev 2.5)
- */
-
-/* Display ID Decoding:
- *
- * byte0: Vendor
- * byte1: Model
- * byte2: Board
- *
- * Vendors:
- * 10h: Japan Display Inc.
- * 20h: InnoLux Corporation
- * 30h: AU Optronics
- * 40h: Unknown0
- * 50h: Samsung
- *
- * Boards, Panel Size:
- * 0Fh: Icosa/Iowa, 6.2"
- * 10h: Hoag, 5.5"
- * 20h: Aula, 7.0"
- */
-
-enum
-{
- PANEL_JDI_XXX062M = 0x10,
- PANEL_JDI_LAM062M109A = 0x0910,
- PANEL_JDI_LPM062M326A = 0x2610,
- PANEL_INL_P062CCA_AZ1 = 0x0F20,
- PANEL_AUO_A062TAN01 = 0x0F30,
- PANEL_INL_2J055IA_27A = 0x1020,
- PANEL_AUO_A055TAN01 = 0x1030,
- PANEL_V40_55_UNK = 0x1040,
- PANEL_SAM_AMS699VC01 = 0x2050
-};
-
-void display_init();
-void display_backlight_pwm_init();
-void display_end();
-
-/*! Get/Set Display panel ID. */
-u16 display_get_decoded_panel_id();
-void display_set_decoded_panel_id(u32 id);
-
-/*! Show one single color on the display. */
-void display_color_screen(u32 color);
-
-/*! Switches screen backlight ON/OFF. */
-void display_backlight(bool enable);
-void display_backlight_brightness(u32 brightness, u32 step_delay);
-u32 display_get_backlight_brightness();
-
-/*! Init display in full 1280x720 resolution (B8G8R8A8, line stride 768, framebuffer size = 1280*768*4 bytes). */
-u32 *display_init_framebuffer_pitch();
-u32 *display_init_framebuffer_pitch_inv();
-u32 *display_init_framebuffer_block();
-u32 *display_init_framebuffer_log();
-void display_activate_console();
-void display_deactivate_console();
-void display_init_cursor(void *crs_fb, u32 size);
-void display_set_pos_cursor(u32 x, u32 y);
-void display_deinit_cursor();
-
-void display_dsi_write(u8 cmd, u32 len, void *data, bool video_enabled);
-int display_dsi_read(u8 cmd, u32 len, void *data, bool video_enabled);
-
-#endif
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018-2025 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _DI_H_
+#define _DI_H_
+
+#include
+#include
+
+#define DSI_VIDEO_DISABLED 0
+#define DSI_VIDEO_ENABLED 1
+
+#define WINDOW_A 0
+#define WINDOW_B 1
+#define WINDOW_C 2
+#define WINDOW_D 3
+
+/*! Display registers. */
+// All Display/DSI/MIPI register defines and macros are index based (not offset).
+// DC_CMD/DC_COM/WINC Non-shadowed. DC_DISP/DC_WIN/DC_WINBUF Shadowed.
+
+// Display controller scratch registers.
+#define DC_D_WINBUF_DD_SCRATCH_REGISTER_0 0xED
+#define DC_D_WINBUF_DD_SCRATCH_REGISTER_1 0xEE
+#define DC_T_WINBUF_TD_SCRATCH_REGISTER_0 0x16D
+#define DC_T_WINBUF_TD_SCRATCH_REGISTER_1 0x16E
+#define DC_COM_SCRATCH_REGISTER_A 0x325
+#define DC_COM_SCRATCH_REGISTER_B 0x326
+#define DC_A_WINBUF_AD_SCRATCH_REGISTER_0 0xBED
+#define DC_A_WINBUF_AD_SCRATCH_REGISTER_1 0xBEE
+#define DC_B_WINBUF_BD_SCRATCH_REGISTER_0 0xDED
+#define DC_B_WINBUF_BD_SCRATCH_REGISTER_1 0xDEE
+#define DC_C_WINBUF_CD_SCRATCH_REGISTER_0 0xFED
+#define DC_C_WINBUF_CD_SCRATCH_REGISTER_1 0xFEE
+
+// DC_CMD non-shadowed command/sync registers.
+#define DC_CMD_GENERAL_INCR_SYNCPT 0x00
+#define SYNCPT_GENERAL_INDX(x) (((x) & 0xFF) << 0)
+#define SYNCPT_GENERAL_COND(x) (((x) & 0xFF) << 8)
+#define COND_REG_WR_SAFE 3
+
+#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x01
+#define SYNCPT_CNTRL_SOFT_RESET BIT(0)
+#define SYNCPT_CNTRL_NO_STALL BIT(8)
+
+#define DC_CMD_CONT_SYNCPT_VSYNC 0x28
+#define SYNCPT_VSYNC_INDX(x) (((x) & 0xFF) << 0)
+#define SYNCPT_VSYNC_ENABLE BIT(8)
+
+#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
+
+#define DC_CMD_DISPLAY_COMMAND 0x32
+#define DISP_CTRL_MODE_STOP (0 << 5)
+#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
+#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
+#define DISP_CTRL_MODE_MASK (3 << 5)
+
+#define DC_CMD_DISPLAY_POWER_CONTROL 0x36
+#define PW0_ENABLE BIT(0)
+#define PW1_ENABLE BIT(2)
+#define PW2_ENABLE BIT(4)
+#define PW3_ENABLE BIT(6)
+#define PW4_ENABLE BIT(8)
+#define PM0_ENABLE BIT(16)
+#define PM1_ENABLE BIT(18)
+
+#define DC_CMD_INT_STATUS 0x37
+#define DC_CMD_INT_MASK 0x38
+#define DC_CMD_INT_ENABLE 0x39
+#define DC_CMD_INT_FRAME_END_INT BIT(1)
+#define DC_CMD_INT_V_BLANK_INT BIT(2)
+#define DC_CMD_INT_POLARITY 0x3B
+
+#define DC_CMD_STATE_ACCESS 0x40
+#define READ_MUX_ASSEMBLY 0x0
+#define WRITE_MUX_ASSEMBLY 0x0
+#define READ_MUX_ACTIVE BIT(0)
+#define WRITE_MUX_ACTIVE BIT(2)
+
+#define DC_CMD_STATE_CONTROL 0x41
+#define GENERAL_ACT_REQ BIT(0)
+#define WIN_ACT_REQ 1
+#define WIN_A_ACT_REQ BIT(1)
+#define WIN_B_ACT_REQ BIT(2)
+#define WIN_C_ACT_REQ BIT(3)
+#define WIN_D_ACT_REQ BIT(4)
+#define CURSOR_ACT_REQ BIT(7)
+#define GENERAL_UPDATE BIT(8)
+#define WIN_UPDATE 9
+#define WIN_A_UPDATE BIT(9)
+#define WIN_B_UPDATE BIT(10)
+#define WIN_C_UPDATE BIT(11)
+#define WIN_D_UPDATE BIT(12)
+#define CURSOR_UPDATE BIT(15)
+#define NC_HOST_TRIG BIT(24)
+
+#define DC_CMD_DISPLAY_WINDOW_HEADER 0x42
+#define WINDOW_SELECT 4
+#define WINDOW_A_SELECT BIT(4)
+#define WINDOW_B_SELECT BIT(5)
+#define WINDOW_C_SELECT BIT(6)
+#define WINDOW_D_SELECT BIT(7)
+
+#define DC_CMD_REG_ACT_CONTROL 0x43
+#define GENERAL_ACT_HCNTR_SEL BIT(0)
+#define WIN_A_ACT_HCNTR_SEL BIT(2)
+#define WIN_B_ACT_HCNTR_SEL BIT(4)
+#define WIN_C_ACT_HCNTR_SEL BIT(6)
+#define CURSOR_ACT_HCNTR_SEL BIT(7)
+#define WIN_D_ACT_HCNTR_SEL BIT(10)
+
+// DC_D_WIN_DD window D instance of DC_WIN
+#define DC_D_WIN_DD_WIN_OPTIONS 0x80
+#define DC_D_WIN_DD_COLOR_DEPTH 0x83
+#define DC_D_WIN_DD_POSITION 0x84
+#define DC_D_WIN_DD_SIZE 0x85
+#define DC_D_WIN_DD_LINE_STRIDE 0x8A
+#define DC_D_WIN_DD_BLEND_LAYER_CONTROL 0x96
+#define DC_D_WIN_DD_BLEND_MATCH_SELECT 0x97
+#define DC_D_WIN_DD_BLEND_ALPHA_1BIT 0x99
+
+// DC_D_WINBUF_DD window D instance of DC_WINBUF
+#define DC_D_WINBUF_DD_START_ADDR 0xC0
+#define DC_D_WINBUF_DD_ADDR_H_OFFSET 0xC6
+#define DC_D_WINBUF_DD_ADDR_V_OFFSET 0xC8
+#define DC_D_WINBUF_DD_START_ADDR_HI 0xCD
+#define DC_D_WINBUF_DD_MEMFETCH_CONTROL 0xEB
+
+// DC_T_WIN_TD macro for using DD defines.
+#define _DC_T(reg) ((reg) + 0x80)
+
+// DC_COM non-shadowed registers.
+#define DC_COM_CRC_CONTROL 0x300
+#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
+#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
+#define LSC0_OUTPUT_POLARITY_LOW BIT(24)
+
+// CMU registers.
+#define DC_COM_CMU_CSC_KRR 0x32A
+#define DC_COM_CMU_CSC_KGR 0x32B
+#define DC_COM_CMU_CSC_KBR 0x32C
+#define DC_COM_CMU_CSC_KRG 0x32D
+#define DC_COM_CMU_CSC_KGG 0x32E
+#define DC_COM_CMU_CSC_KBG 0x32F
+#define DC_COM_CMU_CSC_KRB 0x330
+#define DC_COM_CMU_CSC_KGB 0x331
+#define DC_COM_CMU_CSC_KBB 0x332
+#define DC_COM_CMU_LUT1 0x336
+#define LUT1_ADDR(x) ((x) & 0xFF)
+#define LUT1_DATA(x) (((x) & 0xFFF) << 16)
+#define LUT1_READ_DATA(x) (((x) >> 16) & 0xFFF)
+#define DC_COM_CMU_LUT2 0x337
+#define LUT2_ADDR(x) ((x) & 0x3FF)
+#define LUT2_DATA(x) (((x) & 0xFF) << 16)
+#define LUT2_READ_DATA(x) (((x) >> 16) & 0xFF)
+#define DC_COM_CMU_LUT1_READ 0x338
+#define LUT1_READ_ADDR(x) (((x) & 0xFF) << 8)
+#define LUT1_READ_EN BIT(0)
+#define DC_COM_CMU_LUT2_READ 0x339
+#define LUT2_READ_ADDR(x) (((x) & 0x3FF) << 8)
+#define LUT2_READ_EN BIT(0)
+
+#define DC_COM_DSC_TOP_CTL 0x33E
+
+// DC_DISP shadowed registers.
+#define DC_DISP_DISP_WIN_OPTIONS 0x402
+#define CURSOR_ENABLE BIT(16)
+#define SOR_ENABLE BIT(25)
+#define SOR1_ENABLE BIT(26)
+#define SOR1_TIMING_CYA BIT(27)
+#define DSI_ENABLE BIT(29)
+#define HDMI_ENABLE BIT(30)
+
+
+#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403
+#define DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER 0x404
+
+#define DC_DISP_DISP_TIMING_OPTIONS 0x405
+#define VSYNC_H_POSITION(x) (((x) & 0x1FFF) << 0)
+
+#define DC_DISP_REF_TO_SYNC 0x406
+#define H_REF_TO_SYNC(x) (((x) & 0x1FFF) << 0) // Min 0 pixel clock.
+#define V_REF_TO_SYNC(x) (((x) & 0x1FFF) << 16) // Min 1 line clock.
+
+#define DC_DISP_SYNC_WIDTH 0x407
+#define H_SYNC_WIDTH(x) (((x) & 0x1FFF) << 0) // Min 1 pixel clock.
+#define V_SYNC_WIDTH(x) (((x) & 0x1FFF) << 16) // Min 1 line clock.
+
+#define DC_DISP_BACK_PORCH 0x408
+#define H_BACK_PORCH(x) (((x) & 0x1FFF) << 0)
+#define V_BACK_PORCH(x) (((x) & 0x1FFF) << 16)
+
+#define DC_DISP_ACTIVE 0x409
+#define H_DISP_ACTIVE(x) (((x) & 0x1FFF) << 0) // Min 16 pixel clock.
+#define V_DISP_ACTIVE(x) (((x) & 0x1FFF) << 16) // Min 16 line clock.
+
+#define DC_DISP_FRONT_PORCH 0x40A
+#define H_FRONT_PORCH(x) (((x) & 0x1FFF) << 0) // Min -=PS_=-H_REF_TO_SYNC + 1
+#define V_FRONT_PORCH(x) (((x) & 0x1FFF) << 16) // Min -=PS_=-V_REF_TO_SYNC + 1
+
+#define DC_DISP_DISP_CLOCK_CONTROL 0x42E
+#define SHIFT_CLK_DIVIDER(x) ((x) & 0xFF)
+#define PIXEL_CLK_DIVIDER_PCD1 (0 << 8)
+#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8)
+#define PIXEL_CLK_DIVIDER_PCD2 (2 << 8)
+#define PIXEL_CLK_DIVIDER_PCD3 (3 << 8)
+#define PIXEL_CLK_DIVIDER_PCD4 (4 << 8)
+#define PIXEL_CLK_DIVIDER_PCD6 (5 << 8)
+#define PIXEL_CLK_DIVIDER_PCD8 (6 << 8)
+#define PIXEL_CLK_DIVIDER_PCD9 (7 << 8)
+#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8)
+#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8)
+#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8)
+#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8)
+#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8)
+
+#define DC_DISP_DISP_INTERFACE_CONTROL 0x42F
+#define DISP_DATA_FORMAT_DF1P1C (0 << 0)
+#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0)
+#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0)
+#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0)
+#define DISP_DATA_FORMAT_DF2S (4 << 0)
+#define DISP_DATA_FORMAT_DF3S (5 << 0)
+#define DISP_DATA_FORMAT_DFSPI (6 << 0)
+#define DISP_DATA_FORMAT_DF1P3C24B (7 << 0)
+#define DISP_DATA_FORMAT_DF1P3C18B (8 << 0)
+#define DISP_ALIGNMENT_MSB (0 << 8)
+#define DISP_ALIGNMENT_LSB (1 << 8)
+#define DISP_ORDER_RED_BLUE (0 << 9)
+#define DISP_ORDER_BLUE_RED (1 << 9)
+
+#define DC_DISP_DISP_COLOR_CONTROL 0x430
+#define BASE_COLOR_SIZE_MASK (0xF << 0)
+#define BASE_COLOR_SIZE_666 (0 << 0)
+#define BASE_COLOR_SIZE_111 (1 << 0)
+#define BASE_COLOR_SIZE_222 (2 << 0)
+#define BASE_COLOR_SIZE_333 (3 << 0)
+#define BASE_COLOR_SIZE_444 (4 << 0)
+#define BASE_COLOR_SIZE_555 (5 << 0)
+#define BASE_COLOR_SIZE_565 (6 << 0)
+#define BASE_COLOR_SIZE_332 (7 << 0)
+#define BASE_COLOR_SIZE_888 (8 << 0)
+#define DITHER_CONTROL_MASK (3 << 8)
+#define DITHER_CONTROL_DISABLE (0 << 8)
+#define DITHER_CONTROL_ORDERED (2 << 8)
+#define DITHER_CONTROL_ERRDIFF (3 << 8)
+#define DISP_COLOR_SWAP BIT(16)
+#define BLANK_COLOR_WHITE BIT(17)
+#define CMU_ENABLE BIT(20)
+
+#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
+#define SC0_H_QUALIFIER_NONE BIT(0)
+#define SC1_H_QUALIFIER_NONE BIT(16)
+
+#define DC_DISP_DATA_ENABLE_OPTIONS 0x432
+#define DE_SELECT_ACTIVE_BLANK (0 << 0)
+#define DE_SELECT_ACTIVE (1 << 0)
+#define DE_SELECT_ACTIVE_IS (2 << 0)
+#define DE_CONTROL_ONECLK (0 << 2)
+#define DE_CONTROL_NORMAL (1 << 2)
+#define DE_CONTROL_EARLY_EXT (2 << 2)
+#define DE_CONTROL_EARLY (3 << 2)
+#define DE_CONTROL_ACTIVE_BLANK (4 << 2)
+
+// Cursor configuration registers.
+#define DC_DISP_CURSOR_FOREGROUND 0x43C
+#define DC_DISP_CURSOR_BACKGROUND 0x43D
+#define CURSOR_COLOR(r,g,b) (((r) & 0xFF) | (((g) & 0xFF) << 8) | (((b) & 0xFF) << 16))
+
+#define DC_DISP_CURSOR_START_ADDR 0x43E
+#define DC_DISP_CURSOR_START_ADDR_NS 0x43F
+#define CURSOR_CLIPPING(w) ((w) << 28)
+#define CURSOR_CLIP_WIN_A 1
+#define CURSOR_CLIP_WIN_B 2
+#define CURSOR_CLIP_WIN_C 3
+#define CURSOR_SIZE_32 (0 << 24)
+#define CURSOR_SIZE_64 (1 << 24)
+#define CURSOR_SIZE_128 (2 << 24)
+#define CURSOR_SIZE_256 (3 << 24)
+#define DC_DISP_CURSOR_POSITION 0x440
+#define DC_DISP_CURSOR_START_ADDR_HI 0x4EC
+#define DC_DISP_CURSOR_START_ADDR_HI_NS 0x4ED
+#define DC_DISP_BLEND_CURSOR_CONTROL 0x4F1
+#define CURSOR_BLEND_2BIT (0 << 24)
+#define CURSOR_BLEND_R8G8B8A8 (1 << 24)
+#define CURSOR_BLEND_SRC_FACTOR(n) ((n) << 8)
+#define CURSOR_BLEND_DST_FACTOR(n) ((n) << 16)
+#define CURSOR_BLEND_ZRO 0
+#define CURSOR_BLEND_K1 1
+#define CURSOR_BLEND_NK1 2
+// End of cursor cfg regs.
+
+#define DC_DISP_DC_MCCIF_FIFOCTRL 0x480
+#define DC_DISP_SD_BL_PARAMETERS 0x4D7
+#define DC_DISP_SD_BL_CONTROL 0x4DC
+#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4E4
+
+#define DC_DISP_DISPLAY_SPARE0 0x4F7 // Used by SW/HW.
+#define DC_DISP_DISPLAY_SPARE1 0x4F8
+
+#define DC_WINC_COLOR_PALETTE 0x500
+#define COLOR_PALETTE_IDX(off) (DC_WINC_COLOR_PALETTE + (off))
+#define COLOR_PALETTE_RGB(rgb) (byte_swap_32(rgb) >> 8)
+#define DC_WINC_PALETTE_COLOR_EXT 0x600
+
+#define DC_WINC_H_FILTER_P(p) (0x601 + (p))
+#define DC_WINC_V_FILTER_P(p) (0x619 + (p))
+#define DC_WINC_H_FILTER_HI_P(p) (0x629 + (p))
+
+#define DC_WINC_CSC_YOF 0x611
+#define DC_WINC_CSC_KYRGB 0x612
+#define DC_WINC_CSC_KUR 0x613
+#define DC_WINC_CSC_KVR 0x614
+#define DC_WINC_CSC_KUG 0x615
+#define DC_WINC_CSC_KVG 0x616
+#define DC_WINC_CSC_KUB 0x617
+#define DC_WINC_CSC_KVB 0x618
+#define DC_WIN_AD_WIN_OPTIONS 0xB80
+#define DC_WIN_BD_WIN_OPTIONS 0xD80
+#define DC_WIN_CD_WIN_OPTIONS 0xF80
+
+// The following registers are A/B/C shadows of the 0xB80/0xD80/0xF80 registers (see DISPLAY_WINDOW_HEADER).
+#define DC_WIN_WIN_OPTIONS 0x700
+#define H_DIRECTION BIT(0)
+#define V_DIRECTION BIT(2)
+#define SCAN_COLUMN BIT(4)
+#define COLOR_EXPAND BIT(6)
+#define H_FILTER_ENABLE BIT(8)
+#define V_FILTER_ENABLE BIT(10)
+#define COLOR_PALETTE_ENABLE BIT(16)
+#define CSC_ENABLE BIT(18)
+#define DV_ENABLE BIT(20)
+#define WIN_ENABLE BIT(30)
+#define H_FILTER_EXPAND BIT(31)
+
+#define DC_WIN_BUFFER_CONTROL 0x702
+#define BUFFER_CONTROL_HOST 0
+#define BUFFER_CONTROL_VI 1
+#define BUFFER_CONTROL_SB2D 4
+
+#define DC_WIN_COLOR_DEPTH 0x703
+#define WIN_COLOR_DEPTH_P1 0x0
+#define WIN_COLOR_DEPTH_P2 0x1
+#define WIN_COLOR_DEPTH_P4 0x2
+#define WIN_COLOR_DEPTH_P8 0x3
+#define WIN_COLOR_DEPTH_B4G4R4A4 0x4
+#define WIN_COLOR_DEPTH_B5G5R5A 0x5
+#define WIN_COLOR_DEPTH_B5G6R5 0x6
+#define WIN_COLOR_DEPTH_AB5G5R5 0x7
+#define WIN_COLOR_DEPTH_B8G8R8A8 0xC
+#define WIN_COLOR_DEPTH_R8G8B8A8 0xD
+#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 0xE
+#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 0xF
+#define WIN_COLOR_DEPTH_YCbCr422 0x10
+#define WIN_COLOR_DEPTH_YUV422 0x11
+#define WIN_COLOR_DEPTH_YCbCr420P 0x12
+#define WIN_COLOR_DEPTH_YUV420P 0x13
+#define WIN_COLOR_DEPTH_YCbCr422P 0x14
+#define WIN_COLOR_DEPTH_YUV422P 0x15
+#define WIN_COLOR_DEPTH_YCbCr422R 0x16
+#define WIN_COLOR_DEPTH_YUV422R 0x17
+#define WIN_COLOR_DEPTH_YCbCr422RA 0x18
+#define WIN_COLOR_DEPTH_YUV422RA 0x19
+#define WIN_COLOR_DEPTH_X1R5G5B5 0x1E
+#define WIN_COLOR_DEPTH_R5G5B5X1 0x1F
+#define WIN_COLOR_DEPTH_X1B5G5R5 0x20
+#define WIN_COLOR_DEPTH_B5G5R5X1 0x21
+#define WIN_COLOR_DEPTH_YCbCr444P 0x29
+#define WIN_COLOR_DEPTH_YCrCb420SP 0x2A
+#define WIN_COLOR_DEPTH_YCbCr420SP 0x2B
+#define WIN_COLOR_DEPTH_YCrCb422SP 0x2C
+#define WIN_COLOR_DEPTH_YCbCr422SP 0x2D
+#define WIN_COLOR_DEPTH_YUV444P 0x34
+#define WIN_COLOR_DEPTH_YVU420SP 0x35
+#define WIN_COLOR_DEPTH_YUV420SP 0x36
+#define WIN_COLOR_DEPTH_YVU422SP 0x37
+#define WIN_COLOR_DEPTH_YUV422SP 0x38
+#define WIN_COLOR_DEPTH_YVU444SP 0x3B
+#define WIN_COLOR_DEPTH_YUV444SP 0x3C
+
+#define DC_WIN_POSITION 0x704
+#define H_POSITION(x) (((x) & 0xFFFF) << 0) // Support negative.
+#define V_POSITION(x) (((x) & 0xFFFF) << 16) // Support negative.
+
+#define DC_WIN_SIZE 0x705
+#define H_SIZE(x) (((x) & 0x1FFF) << 0)
+#define V_SIZE(x) (((x) & 0x1FFF) << 16)
+
+#define DC_WIN_PRESCALED_SIZE 0x706
+#define H_PRESCALED_SIZE(x) (((x) & 0x7FFF) << 0)
+#define V_PRESCALED_SIZE(x) (((x) & 0x1FFF) << 16)
+
+#define DC_WIN_H_INITIAL_DDA 0x707
+#define DC_WIN_V_INITIAL_DDA 0x708
+
+#define DC_WIN_DDA_INC 0x709
+#define H_DDA_INC(x) (((x) & 0xFFFF) << 0)
+#define V_DDA_INC(x) (((x) & 0xFFFF) << 16)
+
+#define DC_WIN_LINE_STRIDE 0x70A
+#define LINE_STRIDE(x) (x)
+#define UV_LINE_STRIDE(x) (((x) & 0xFFFF) << 16)
+
+#define DC_WIN_DV_CONTROL 0x70E
+#define DV_CTRL_R(r) (((r) & 7) << 16)
+#define DV_CTRL_G(g) (((g) & 7) << 8)
+#define DV_CTRL_B(b) (((b) & 7) << 0)
+
+#define DC_WINBUF_BLEND_LAYER_CONTROL 0x716
+#define WIN_BLEND_DEPTH(x) (((x) & 0xFF) << 0)
+#define WIN_K1(x) (((x) & 0xFF) << 8)
+#define WIN_K2(x) (((x) & 0xFF) << 16)
+#define WIN_BLEND_ENABLE (0 << 24)
+#define WIN_BLEND_BYPASS (1 << 24)
+
+#define DC_WINBUF_BLEND_MATCH_SELECT 0x717
+#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_ZERO (0 << 0)
+#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_ONE (1 << 0)
+#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 (2 << 0)
+#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_DST (3 << 0)
+#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_NEG_K1_TIMES_DST (4 << 0)
+#define WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_SRC (5 << 0)
+
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_ZERO (0 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_ONE (1 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_K1 (2 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_K2 (3 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_K1_TIMES_DST (4 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_DST (5 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC (6 << 4)
+#define WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1 (7 << 4)
+
+#define WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_ZERO (0 << 8)
+#define WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K1 (1 << 8)
+#define WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 (2 << 8)
+
+#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO (0 << 12)
+#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ONE (1 << 12)
+#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_NEG_K1_TIMES_SRC (2 << 12)
+#define WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_K2 (3 << 12)
+
+#define DC_WINBUF_BLEND_ALPHA_1BIT 0x719
+#define WIN_ALPHA_1BIT_WEIGHT0(x) (((x) & 0xFF) << 0)
+#define WIN_ALPHA_1BIT_WEIGHT1(x) (((x) & 0xFF) << 8)
+
+/*! The following registers are A/B/C shadows of the 0xBC0/0xDC0/0xFC0 registers (see DISPLAY_WINDOW_HEADER). */
+#define DC_WINBUF_START_ADDR 0x800
+#define DC_WINBUF_ADDR_H_OFFSET 0x806
+#define DC_WINBUF_ADDR_V_OFFSET 0x808
+#define DC_WINBUF_SURFACE_KIND 0x80B
+#define PITCH (0 << 0)
+#define TILED (1 << 0)
+#define BLOCK (2 << 0)
+#define BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
+
+#define DC_WINBUF_MEMFETCH_CONTROL 0x82B
+
+/* Scratch register to store DCS backlight level (custom). */
+#define DC_DCS_BACKLIGHT_LEVEL DC_COM_SCRATCH_REGISTER_B
+
+/*! Display serial interface registers. */
+#define DSI_INCR_SYNCPT_CNTRL 0x1
+#define DSI_INCR_SYNCPT_SOFT_RESET BIT(0)
+#define DSI_INCR_SYNCPT_NO_STALL BIT(8)
+
+#define DSI_RD_DATA 0x9
+#define DSI_WR_DATA 0xA
+
+#define DSI_POWER_CONTROL 0xB
+#define DSI_POWER_CONTROL_ENABLE 1
+
+#define DSI_INT_ENABLE 0xC
+#define DSI_INT_STATUS 0xD
+#define DSI_INT_MASK 0xE
+
+#define DSI_HOST_CONTROL 0xF
+#define DSI_HOST_CONTROL_ECC BIT(0)
+#define DSI_HOST_CONTROL_CS BIT(1)
+#define DSI_HOST_CONTROL_PKT_BTA BIT(2)
+#define DSI_HOST_CONTROL_IMM_BTA BIT(3)
+#define DSI_HOST_CONTROL_FIFO_SEL BIT(4)
+#define DSI_HOST_CONTROL_HS BIT(5)
+#define DSI_HOST_CONTROL_RAW BIT(6)
+#define DSI_HOST_CONTROL_TX_TRIG_MASK (3 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12)
+#define DSI_HOST_CONTROL_CRC_RESET BIT(20)
+#define DSI_HOST_CONTROL_FIFO_RESET BIT(21)
+
+#define DSI_CONTROL 0x10
+#define DSI_CONTROL_HOST_ENABLE BIT(0)
+#define DSI_CONTROL_VIDEO_ENABLE BIT(1)
+#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2)
+#define DSI_CONTROL_DCS_ENABLE BIT(3)
+#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4)
+#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8)
+#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12)
+#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16)
+#define DSI_CONTROL_HS_CLK_CTRL BIT(20)
+
+#define DSI_SOL_DELAY 0x11
+#define DSI_MAX_THRESHOLD 0x12
+
+#define DSI_TRIGGER 0x13
+#define DSI_TRIGGER_VIDEO BIT(0)
+#define DSI_TRIGGER_HOST BIT(1)
+
+#define DSI_TX_CRC 0x14
+
+#define DSI_STATUS 0x15
+#define DSI_STATUS_RX_FIFO_SIZE 0x1F
+#define DSI_STATUS_TX_FIFO_SIZE 0x20 // Actual depth is 64.
+
+#define DSI_INIT_SEQ_CONTROL 0x1A
+#define DSI_INIT_SEQ_DATA_0 0x1B
+#define DSI_INIT_SEQ_DATA_1 0x1C
+#define DSI_INIT_SEQ_DATA_2 0x1D
+#define DSI_INIT_SEQ_DATA_3 0x1E
+#define DSI_PKT_SEQ_0_LO 0x23
+#define DSI_PKT_SEQ_0_HI 0x24
+#define DSI_PKT_SEQ_1_LO 0x25
+#define DSI_PKT_SEQ_1_HI 0x26
+#define DSI_PKT_SEQ_2_LO 0x27
+#define DSI_PKT_SEQ_2_HI 0x28
+#define DSI_PKT_SEQ_3_LO 0x29
+#define DSI_PKT_SEQ_3_HI 0x2A
+#define DSI_PKT_SEQ_4_LO 0x2B
+#define DSI_PKT_SEQ_4_HI 0x2C
+#define DSI_PKT_SEQ_5_LO 0x2D
+#define DSI_PKT_SEQ_5_HI 0x2E
+#define DSI_DCS_CMDS 0x33
+
+#define DSI_PKT_LEN_0_1 0x34
+#define DSI_PKT_LEN_2_3 0x35
+#define DSI_PKT_LEN_4_5 0x36
+#define DSI_PKT_LEN_6_7 0x37
+#define PKT0_LEN(x) (((x) & 0xFFFF) << 0)
+#define PKT1_LEN(x) (((x) & 0xFFFF) << 16)
+
+#define DSI_PHY_TIMING_0 0x3C
+#define DSI_PHY_TIMING_1 0x3D
+#define DSI_PHY_TIMING_2 0x3E
+#define DSI_BTA_TIMING 0x3F
+
+#define DSI_TIMEOUT_0 0x44
+#define DSI_TIMEOUT_HTX(x) (((x) & 0xFFFF) << 0)
+#define DSI_TIMEOUT_LRX(x) (((x) & 0xFFFF) << 16)
+
+#define DSI_TIMEOUT_1 0x45
+#define DSI_TIMEOUT_TA(x) (((x) & 0xFFFF) << 0)
+#define DSI_TIMEOUT_PR(x) (((x) & 0xFFFF) << 16)
+
+#define DSI_TO_TALLY 0x46
+
+#define DSI_PAD_CONTROL_0 0x4B
+#define DSI_PAD_CONTROL_VS1_PDIO_CLK BIT(8)
+#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xF) << 0)
+#define DSI_PAD_CONTROL_VS1_PULLDN_CLK BIT(24)
+#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xF) << 16)
+
+#define DSI_PAD_CONTROL_CD 0x4C
+#define DSI_VIDEO_MODE_CONTROL 0x4E
+#define DSI_CMD_PKT_VID_ENABLE 1
+#define DSI_DSI_LINE_TYPE(x) ((x) << 1)
+
+#define DSI_PAD_CONTROL_1 0x4F
+#define DSI_PAD_CONTROL_2 0x50
+
+#define DSI_PAD_CONTROL_3 0x51
+#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0)
+#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4)
+#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8)
+#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12)
+
+#define DSI_PAD_CONTROL_4 0x52
+#define DSI_PAD_CONTROL_5_B01 0x53
+#define DSI_PAD_CONTROL_6_B01 0x54
+#define DSI_PAD_CONTROL_7_B01 0x55
+#define DSI_INIT_SEQ_DATA_15 0x5F
+#define DSI_INIT_SEQ_DATA_15_B01 0x62
+
+/*! DSI packet defines */
+#define DSI_ESCAPE_CMD 0x87
+#define DSI_ACK_NO_ERR 0x84
+
+#define ACK_ERROR_RES 0x02
+#define GEN_LONG_RD_RES 0x1A
+#define DCS_LONG_RD_RES 0x1C
+#define GEN_1_BYTE_SHORT_RD_RES 0x11
+#define DCS_1_BYTE_SHORT_RD_RES 0x21
+#define GEN_2_BYTE_SHORT_RD_RES 0x12
+#define DCS_2_BYTE_SHORT_RD_RES 0x22
+
+/*! MIPI registers. */
+#define MIPI_CAL_MIPI_CAL_CTRL (0x00 / 0x4)
+#define MIPI_CAL_CIL_MIPI_CAL_STATUS (0x08 / 0x4)
+#define MIPI_CAL_CILA_MIPI_CAL_CONFIG (0x14 / 0x4)
+#define MIPI_CAL_CILB_MIPI_CAL_CONFIG (0x18 / 0x4)
+#define MIPI_CAL_CILC_MIPI_CAL_CONFIG (0x1C / 0x4)
+#define MIPI_CAL_CILD_MIPI_CAL_CONFIG (0x20 / 0x4)
+#define MIPI_CAL_CILE_MIPI_CAL_CONFIG (0x24 / 0x4)
+#define MIPI_CAL_CILF_MIPI_CAL_CONFIG (0x28 / 0x4)
+#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG (0x38 / 0x4)
+#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG (0x3C / 0x4)
+#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG (0x40 / 0x4)
+#define MIPI_CAL_DSID_MIPI_CAL_CONFIG (0x44 / 0x4)
+#define MIPI_CAL_MIPI_BIAS_PAD_CFG0 (0x58 / 0x4)
+#define MIPI_CAL_MIPI_BIAS_PAD_CFG1 (0x5C / 0x4)
+#define MIPI_CAL_MIPI_BIAS_PAD_CFG2 (0x60 / 0x4)
+#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2 (0x64 / 0x4)
+#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2 (0x68 / 0x4)
+#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_2 (0x70 / 0x4)
+#define MIPI_CAL_DSID_MIPI_CAL_CONFIG_2 (0x74 / 0x4)
+
+/*! MIPI CMDs. */
+#define MIPI_DSI_V_SYNC_START 0x01
+#define MIPI_DSI_COLOR_MODE_OFF 0x02
+#define MIPI_DSI_END_OF_TRANSMISSION 0x08
+#define MIPI_DSI_NULL_PACKET 0x09
+#define MIPI_DSI_V_SYNC_END 0x11
+#define MIPI_DSI_COLOR_MODE_ON 0x12
+#define MIPI_DSI_BLANKING_PACKET 0x19
+#define MIPI_DSI_H_SYNC_START 0x21
+#define MIPI_DSI_SHUTDOWN_PERIPHERAL 0x22
+#define MIPI_DSI_H_SYNC_END 0x31
+#define MIPI_DSI_TURN_ON_PERIPHERAL 0x32
+#define MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE 0x37
+
+#define MIPI_DSI_DCS_SHORT_WRITE 0x05
+#define MIPI_DSI_DCS_READ 0x06
+#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15
+#define MIPI_DSI_DCS_LONG_WRITE 0x39
+
+#define MIPI_DSI_GENERIC_LONG_WRITE 0x29
+#define MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM 0x03
+#define MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM 0x13
+#define MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM 0x23
+#define MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM 0x04
+#define MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM 0x14
+#define MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM 0x24
+
+/*! MIPI DCS CMDs. */
+#define MIPI_DCS_NOP 0x00
+#define MIPI_DCS_SOFT_RESET 0x01
+#define MIPI_DCS_GET_COMPRESSION_MODE 0x03
+#define MIPI_DCS_GET_DISPLAY_ID 0x04
+#define MIPI_DCS_GET_DISPLAY_ID1 0xDA // GET_DISPLAY_ID Byte0, Module Manufacturer ID.
+#define MIPI_DCS_GET_DISPLAY_ID2 0xDB // GET_DISPLAY_ID Byte1, Module/Driver Version ID.
+#define MIPI_DCS_GET_DISPLAY_ID3 0xDC // GET_DISPLAY_ID Byte2, Module/Driver ID.
+#define MIPI_DCS_GET_NUM_ERRORS 0x05 // 1 byte.
+#define MIPI_DCS_GET_RED_CHANNEL 0x06
+#define MIPI_DCS_GET_GREEN_CHANNEL 0x07
+#define MIPI_DCS_GET_BLUE_CHANNEL 0x08
+#define MIPI_DCS_GET_DISPLAY_STATUS 0x09 // 4 bytes.
+#define MIPI_DCS_GET_POWER_MODE 0x0A // 1 byte. 2: DISON, 3: NORON, 4: SLPOUT, 7: BSTON.
+#define MIPI_DCS_GET_ADDRESS_MODE 0x0B // Display Access Control. 1 byte. 0: GS, 1: SS, 3: BGR.
+#define MIPI_DCS_GET_PIXEL_FORMAT 0x0C // 1 byte. 4-6: DPI.
+#define MIPI_DCS_GET_DISPLAY_MODE 0x0D // 1 byte. 0-2: GCS, 3: ALLPOFF, 4: ALLPON, 5: INVON.
+#define MIPI_DCS_GET_SIGNAL_MODE 0x0E // 1 byte. 0: EODSI, 2: DEON, 3: PCLKON, 4: VSON, 5: HSON, 7: TEON.
+#define MIPI_DCS_GET_DIAGNOSTIC_RESULT 0x0F // 1 byte. 6: FUNDT, 7: REGLD.
+#define MIPI_DCS_ENTER_SLEEP_MODE 0x10
+#define MIPI_DCS_EXIT_SLEEP_MODE 0x11
+#define MIPI_DCS_ENTER_PARTIAL_MODE 0x12
+#define MIPI_DCS_ENTER_NORMAL_MODE 0x13
+#define MIPI_DCS_EXIT_INVERT_MODE 0x20
+#define MIPI_DCS_ENTER_INVERT_MODE 0x21
+#define MIPI_DCS_ALL_PIXELS_OFF 0x22
+#define MIPI_DCS_ALL_PIXELS_ON 0x23
+#define MIPI_DCS_SET_CONTRAST 0x25 // VCON in 40mV steps. 7-bit integer.
+#define MIPI_DCS_SET_GAMMA_CURVE 0x26 // 1 byte. 0-7: GC.
+#define MIPI_DCS_SET_DISPLAY_OFF 0x28
+#define MIPI_DCS_SET_DISPLAY_ON 0x29
+#define MIPI_DCS_SET_COLUMN_ADDRESS 0x2A
+#define MIPI_DCS_SET_PAGE_ADDRESS 0x2B
+#define MIPI_DCS_WRITE_MEMORY_START 0x2C
+#define MIPI_DCS_WRITE_LUT 0x2D // 24-bit: 192 bytes.
+#define MIPI_DCS_READ_MEMORY_START 0x2E
+#define MIPI_DCS_SET_PARTIAL_ROWS 0x30
+#define MIPI_DCS_SET_PARTIAL_COLUMNS 0x31
+#define MIPI_DCS_SET_SCROLL_AREA 0x33
+#define MIPI_DCS_SET_TEAR_OFF 0x34
+#define MIPI_DCS_SET_TEAR_ON 0x35
+#define MIPI_DCS_SET_ADDRESS_MODE 0x36 // Display Access Control. 1 byte. 0: GS, 1: SS, 3: BGR.
+#define MIPI_DCS_SET_SCROLL_START 0x37
+#define MIPI_DCS_EXIT_IDLE_MODE 0x38
+#define MIPI_DCS_ENTER_IDLE_MODE 0x39
+#define MIPI_DCS_SET_PIXEL_FORMAT 0x3A // 1 byte. 4-6: DPI.
+#define MIPI_DCS_WRITE_MEMORY_CONTINUE 0x3C
+#define MIPI_DCS_READ_MEMORY_CONTINUE 0x3E
+#define MIPI_DCS_GET_3D_CONTROL 0x3F
+#define MIPI_DCS_SET_VSYNC_TIMING 0x40
+#define MIPI_DCS_SET_TEAR_SCANLINE 0x44
+#define MIPI_DCS_GET_SCANLINE 0x45
+#define MIPI_DCS_SET_TEAR_SCANLINE_WIDTH 0x46
+#define MIPI_DCS_GET_SCANLINE_WIDTH 0x47
+#define MIPI_DSI_AREA_COLOR_MODE 0x4C
+#define MIPI_DCS_SET_BRIGHTNESS 0x51 // DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL. 1 byte. 0-7: DBV.
+#define MIPI_DCS_GET_BRIGHTNESS 0x52 // 1 byte. 0-7: DBV.
+#define MIPI_DCS_SET_CONTROL_DISPLAY 0x53 // 1 byte. 2: BL, 3: DD, 5: BCTRL.
+#define MIPI_DCS_GET_CONTROL_DISPLAY 0x54 // 1 byte. 2: BL, 3: DD, 5: BCTRL.
+#define MIPI_DCS_SET_CABC_VALUE 0x55 // 1 byte. 0-32: C, 4-7: C.
+#define MIPI_DCS_GET_CABC_VALUE 0x56 // 1 byte. 0-32: C, 4-7: C.
+#define MIPI_DCS_SET_CABC_MIN_BRI 0x5E // 1 byte. 0-7: CMB.
+#define MIPI_DCS_GET_CABC_MIN_BRI 0x5F // 1 byte. 0-7: CMB.
+#define MIPI_DCS_GET_AUTO_BRI_DIAG_RES 0x68 // 1 byte. 6-7: D.
+#define MIPI_DCS_READ_DDB_START 0xA1
+#define MIPI_DCS_READ_DDB_CONTINUE 0xA8 // 0x100 size.
+
+/*! MIPI DCS Panel Private CMDs. */
+#define MIPI_DCS_PRIV_SM_SET_COLOR_MODE 0xA0 // 43 bytes.
+#define MIPI_DCS_PRIV_SM_SET_REG_OFFSET 0xB0
+#define MIPI_DCS_PRIV_SM_SET_ELVSS 0xB1 // OLED backlight tuning. Byte7: PWM transition time in frames.
+#define MIPI_DCS_PRIV_SET_POWER_CONTROL 0xB1
+#define MIPI_DCS_PRIV_SET_EXTC 0xB9 // Enable extended commands.
+#define MIPI_DCS_PRIV_UNK_BD 0xBD
+#define MIPI_DCS_PRIV_UNK_D5 0xD5
+#define MIPI_DCS_PRIV_UNK_D6 0xD6
+#define MIPI_DCS_PRIV_UNK_D8 0xD8
+#define MIPI_DCS_PRIV_UNK_D9 0xD9
+#define MIPI_DCS_PRIV_SM_DISPLAY_ID 0xDD
+ // LVL1 LVL2 LVL3 UNK0 UNK1
+#define MIPI_DCS_PRIV_SM_SET_REGS_LOCK 0xE2 // Samsung: Lock (default): 5A5A A5A5 A5A5 A500 A500. Lock/Unlock: A5/5A. LVL1 group is normal registers.
+#define MIPI_DCS_PRIV_READ_EXTC_CMD_SPI 0xFE // Read EXTC Command In SPI. 1 byte. 0-6: EXT_SPI_CNT, 7:EXT_SP.
+#define MIPI_DCS_PRIV_SET_EXTC_CMD_REG 0xFF // EXTC Command Set enable register. 5 bytes. Pass: FF 98 06 04, PAGE.
+
+/*! MIPI DCS Panel Private CMDs PAGE 1. */
+#define MIPI_DCS_PRIV_GET_DISPLAY_ID4 0x00
+#define MIPI_DCS_PRIV_GET_DISPLAY_ID5 0x01
+#define MIPI_DCS_PRIV_GET_DISPLAY_ID6 0x02
+
+/*! MIPI DCS CMD Defines. */
+#define DCS_POWER_MODE_DISPLAY_ON BIT(2)
+#define DCS_POWER_MODE_NORMAL_MODE BIT(3)
+#define DCS_POWER_MODE_SLEEP_MODE BIT(4)
+#define DCS_POWER_MODE_PARTIAL_MODE BIT(5)
+#define DCS_POWER_MODE_IDLE_MODE BIT(6)
+
+#define DCS_ADDRESS_MODE_V_FLIP BIT(0)
+#define DCS_ADDRESS_MODE_H_FLIP BIT(1)
+#define DCS_ADDRESS_MODE_LATCH_RL BIT(2) // Latch Data Order.
+#define DCS_ADDRESS_MODE_BGR_COLOR BIT(3)
+#define DCS_ADDRESS_MODE_LINE_ORDER BIT(4) // Line Refresh Order.
+#define DCS_ADDRESS_MODE_SWAP_XY BIT(5) // Page/Column Addressing Reverse Order.
+#define DCS_ADDRESS_MODE_MIRROR_X BIT(6) // Column Address Order.
+#define DCS_ADDRESS_MODE_MIRROR_Y BIT(7) // Page Address Order.
+#define DCS_ADDRESS_MODE_ROTATION_MASK (0xF << 4)
+#define DCS_ADDRESS_MODE_ROTATION_90 (DCS_ADDRESS_MODE_SWAP_XY | DCS_ADDRESS_MODE_LINE_ORDER)
+#define DCS_ADDRESS_MODE_ROTATION_180 (DCS_ADDRESS_MODE_MIRROR_X | DCS_ADDRESS_MODE_LINE_ORDER)
+#define DCS_ADDRESS_MODE_ROTATION_270 (DCS_ADDRESS_MODE_SWAP_XY)
+
+#define DCS_GAMMA_CURVE_NONE 0
+#define DCS_GAMMA_CURVE_GC0_1_8 BIT(0)
+#define DCS_GAMMA_CURVE_GC1_2_5 BIT(1)
+#define DCS_GAMMA_CURVE_GC2_1_0 BIT(2)
+#define DCS_GAMMA_CURVE_GC3_1_0 BIT(3) // Are there more?
+
+#define DCS_CONTROL_DISPLAY_SM_FLASHLIGHT BIT(2)
+#define DCS_CONTROL_DISPLAY_BACKLIGHT_CTRL BIT(2)
+#define DCS_CONTROL_DISPLAY_DIMMING_CTRL BIT(3) // Transition fading.
+#define DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL BIT(5)
+#define DCS_CONTROL_DISPLAY_HBM_CTRL0 BIT(6)
+#define DCS_CONTROL_DISPLAY_HBM_CTRL1 BIT(7)
+
+#define DCS_SM_COLOR_MODE_SATURATED 0x00 // Disabled. Based on Vivid but over-saturated.
+#define DCS_SM_COLOR_MODE_WASHED 0x45
+#define DCS_SM_COLOR_MODE_BASIC 0x03 // Real natural profile.
+#define DCS_SM_COLOR_MODE_POR_RESET 0x20 // Reset value on power on.
+#define DCS_SM_COLOR_MODE_NATURAL 0x23 // Not actually natural.. Extra saturation.
+#define DCS_SM_COLOR_MODE_VIVID 0x65 // Saturated.
+#define DCS_SM_COLOR_MODE_NIGHT0 0x43 // Based on Washed Out.
+#define DCS_SM_COLOR_MODE_NIGHT1 0x15 // Based on Basic.
+#define DCS_SM_COLOR_MODE_NIGHT2 0x35 // Based on Natural.
+#define DCS_SM_COLOR_MODE_NIGHT3 0x75 // Based on Vivid.
+
+#define DCS_SM_COLOR_MODE_ENABLE BIT(0)
+
+#define PANEL_SM_BL_CANDELA_MAX 2047
+
+/* Switch Panels:
+ *
+ * 6.2" panels for Icosa and Iowa SKUs:
+ * [10] 81 [26]: JDI LPM062M326A
+ * [10] 96 [09]: JDI LAM062M109A
+ * [20] 93 [0F]: InnoLux P062CCA-AZ1 (Rev A1)
+ * [20] 95 [0F]: InnoLux P062CCA-AZ2 (Rev B1)
+ * [20] 96 [0F]: InnoLux P062CCA-??? (Rev XX) [UNCONFIRMED MODEL+REV]
+ * [20] 97 [0F]: InnoLux P062CCA-??? (Rev XX) [UNCONFIRMED MODEL+REV]
+ * [20] 98 [0F]: InnoLux P062CCA-??? (Rev XX) [UNCONFIRMED MODEL+REV]
+ * [20] 99 [0F]: InnoLux P062CCA-??? (Rev XX) [UNCONFIRMED MODEL+REV]
+ * [30] 93 [0F]: AUO A062TAN00 (59.06A33.000)
+ * [30] 94 [0F]: AUO A062TAN01 (59.06A33.001)
+ * [30] 95 [0F]: AUO A062TAN02 (59.06A33.002)
+ * [30] 97 [0F]: AUO A062TAN02 (59.06A33.002) [From photo of assumed same panel]
+ * [30] 98 [0F]: AUO A062TAN0? [UNCONFIRMED MODEL]
+ * [30] XX [0F]: AUO A062TAN03 (59.06A33.003) [UNCONFIRMED ID]
+ *
+ *
+ * 5.5" panels for Hoag SKU:
+ * [20] 94 [10]: InnoLux 2J055IA-27A (Rev B1) (6203B001P4000)
+ * [20] 95 [10]: InnoLux 2J055IA-27A (Rev XX) [UNCONFIRMED MODEL+REV]
+ * [20] 96 [10]: InnoLux 2J055IA-27A (Rev XX) [UNCONFIRMED MODEL+REV]
+ * [30] 93 [10]: AUO A055TAN01 (59.05A30.001)
+ * [30] 94 [10]: AUO A055TAN02 (59.05A30.002)
+ * [30] 95 [10]: AUO A055TAN03 (59.05A30.003)
+ * [40] 94 [10]: Sharp LQ055T1SW10 (Rev P)
+ *
+ *
+ * 7.0" OLED panels for Aula SKU:
+ * [50] 9B [20]: Samsung AMS699VC01-0 (Rev 2.5)
+ */
+
+/* Display ID Decoding:
+ *
+ * byte0: Vendor
+ * byte1: Model
+ * byte2: Board
+ *
+ * Vendors:
+ * 10h: Japan Display Inc.
+ * 20h: InnoLux Corporation
+ * 30h: AU Optronics
+ * 40h: Sharp
+ * 50h: Samsung
+ *
+ * Boards, Panel Size:
+ * 0Fh: Icosa/Iowa, 6.2"
+ * 10h: Hoag, 5.5"
+ * 20h: Aula, 7.0"
+ */
+
+enum
+{
+ PANEL_JDI_XXX062M = 0x10,
+ PANEL_JDI_LAM062M109A = 0x0910, // SI.
+ PANEL_JDI_LPM062M326A = 0x2610, // LTPS.
+ PANEL_INL_P062CCA_AZ1 = 0x0F20,
+ PANEL_AUO_A062TAN01 = 0x0F30,
+ PANEL_INL_2J055IA_27A = 0x1020,
+ PANEL_AUO_A055TAN01 = 0x1030,
+ PANEL_SHP_LQ055T1SW10 = 0x1040,
+ PANEL_SAM_AMS699VC01 = 0x2050,
+
+ // Found on 6/2" clones. Unknown markings. Clone of AUO A062TAN01.
+ // Quality seems JDI like. Has bad low backlight scaling. ID: [83] 94 [0F]. Sometimes reports [30] 94 [0F]. Both IDs have correct CRC16.
+ PANEL_OEM_CLONE_6_2 = 0x0F83,
+ // Found on 5.5" clones with AUO A055TAN02 (59.05A30.001) fake markings.
+ PANEL_OEM_CLONE_5_5 = 0x00B3,
+ // Found on 5.5" clones with AUO A055TAN02 (59.05A30.001) fake markings.
+ PANEL_OEM_CLONE = 0x0000
+ //0x0F40 [40] 94 [0F], 5.5" clone
+};
+
+void display_init();
+void display_backlight_pwm_init();
+void display_end();
+
+/*! Interrupt management. */
+void display_enable_interrupt(u32 intr);
+void display_disable_interrupt(u32 intr);
+void display_wait_interrupt(u32 intr);
+
+/*! Get/Set Display panel ID. */
+u32 display_get_verbose_panel_id();
+u16 display_get_decoded_panel_id();
+void display_set_decoded_panel_id(u32 id);
+
+/*! MIPI DCS register management */
+int display_dsi_read(u8 cmd, u32 len, void *data);
+int display_dsi_vblank_read(u8 cmd, u32 len, void *data);
+void display_dsi_write(u8 cmd, u32 len, void *data);
+void display_dsi_vblank_write(u8 cmd, u32 len, void *data);
+
+/*! Show one single color on the display. */
+void display_color_screen(u32 color);
+
+/*! Screen backlight ON/OFF or set via duty and fading. */
+void display_backlight(bool enable);
+void display_backlight_brightness(u32 brightness, u32 step_delay);
+
+u32 *display_init_window_a_pitch();
+u32 *display_init_window_a_pitch_vic();
+u32 *display_init_window_a_pitch_inv();
+u32 *display_init_window_a_block();
+u32 *display_init_window_d_console();
+
+void display_window_disable(u32 window);
+
+void display_set_framebuffer(u32 window, void *fb);
+void display_move_framebuffer(u32 window, void *fb);
+
+void display_window_d_console_enable();
+void display_window_d_console_disable();
+
+void display_cursor_init(void *crs_fb, u32 size);
+void display_cursor_set_pos(u32 x, u32 y);
+void display_cursor_deinit();
+
+#endif
diff --git a/bdk/display/di.inl b/bdk/display/di.inl
index 4c7dcde..2d3ff45 100644
--- a/bdk/display/di.inl
+++ b/bdk/display/di.inl
@@ -1,746 +1,549 @@
-/*
-* Copyright (c) 2018 naehrwert
-* Copyright (c) 2018-2020 CTCaer
-*
-* This program is free software; you can redistribute it and/or modify it
-* under the terms and conditions of the GNU General Public License,
-* version 2, as published by the Free Software Foundation.
-*
-* This program is distributed in the hope it will be useful, but WITHOUT
-* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-* more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program. If not, see .
-*/
-
-//Display A config.
-static const cfg_op_t _display_dc_setup_win_config[94] = {
- {DC_CMD_STATE_ACCESS, 0},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_REG_ACT_CONTROL, 0x54}, // Select H counter for win A/B/C.
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_DISP_DC_MCCIF_FIFOCTRL, 0},
- {DC_DISP_DISP_MEM_HIGH_PRIORITY, 0},
- {DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER, 0},
- {DC_CMD_DISPLAY_POWER_CONTROL, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE},
- {DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL},
- {DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | 0x9}, // 9: SYNCPT
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
- {DC_CMD_STATE_ACCESS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_DV_CONTROL, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- /* Setup default YUV colorspace conversion coefficients */
- {DC_WIN_CSC_YOF, 0xF0},
- {DC_WIN_CSC_KYRGB, 0x12A},
- {DC_WIN_CSC_KUR, 0},
- {DC_WIN_CSC_KVR, 0x198},
- {DC_WIN_CSC_KUG, 0x39B},
- {DC_WIN_CSC_KVG, 0x32F},
- {DC_WIN_CSC_KUB, 0x204},
- {DC_WIN_CSC_KVB, 0},
- /* End of color coefficients */
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_DV_CONTROL, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- /* Setup default YUV colorspace conversion coefficients */
- {DC_WIN_CSC_YOF, 0xF0},
- {DC_WIN_CSC_KYRGB, 0x12A},
- {DC_WIN_CSC_KUR, 0},
- {DC_WIN_CSC_KVR, 0x198},
- {DC_WIN_CSC_KUG, 0x39B},
- {DC_WIN_CSC_KVG, 0x32F},
- {DC_WIN_CSC_KUB, 0x204},
- {DC_WIN_CSC_KVB, 0},
- /* End of color coefficients */
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_DV_CONTROL, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- /* Setup default YUV colorspace conversion coefficients */
- {DC_WIN_CSC_YOF, 0xF0},
- {DC_WIN_CSC_KYRGB, 0x12A},
- {DC_WIN_CSC_KUR, 0},
- {DC_WIN_CSC_KVR, 0x198},
- {DC_WIN_CSC_KUG, 0x39B},
- {DC_WIN_CSC_KVG, 0x32F},
- {DC_WIN_CSC_KUB, 0x204},
- {DC_WIN_CSC_KVB, 0},
- /* End of color coefficients */
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
- {DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
- {DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000},
- {DC_COM_PIN_OUTPUT_POLARITY(3), 0},
- {DC_DISP_BLEND_BACKGROUND_COLOR, 0},
- {DC_COM_CRC_CONTROL, 0},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WINBUF_BLEND_LAYER_CONTROL, 0x10000FF},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WINBUF_BLEND_LAYER_CONTROL, 0x10000FF},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WINBUF_BLEND_LAYER_CONTROL, 0x10000FF},
- {DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_STOP},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}
-};
-
-//DSI Init config.
-static const cfg_op_t _display_dsi_init_config_part1[8] = {
- {DSI_WR_DATA, 0},
- {DSI_INT_ENABLE, 0},
- {DSI_INT_STATUS, 0},
- {DSI_INT_MASK, 0},
- {DSI_INIT_SEQ_DATA_0, 0},
- {DSI_INIT_SEQ_DATA_1, 0},
- {DSI_INIT_SEQ_DATA_2, 0},
- {DSI_INIT_SEQ_DATA_3, 0}
-};
-static const cfg_op_t _display_dsi_init_config_part2[14] = {
- {DSI_DCS_CMDS, 0},
- {DSI_PKT_SEQ_0_LO, 0},
- {DSI_PKT_SEQ_1_LO, 0},
- {DSI_PKT_SEQ_2_LO, 0},
- {DSI_PKT_SEQ_3_LO, 0},
- {DSI_PKT_SEQ_4_LO, 0},
- {DSI_PKT_SEQ_5_LO, 0},
- {DSI_PKT_SEQ_0_HI, 0},
- {DSI_PKT_SEQ_1_HI, 0},
- {DSI_PKT_SEQ_2_HI, 0},
- {DSI_PKT_SEQ_3_HI, 0},
- {DSI_PKT_SEQ_4_HI, 0},
- {DSI_PKT_SEQ_5_HI, 0},
- {DSI_CONTROL, 0}
-};
-static const cfg_op_t _display_dsi_init_config_part3_t210b01[7] = {
- {DSI_PAD_CONTROL_1, 0},
- {DSI_PAD_CONTROL_2, 0},
- {DSI_PAD_CONTROL_3, 0},
- {DSI_PAD_CONTROL_4, 0},
- {DSI_PAD_CONTROL_5_B01, 0},
- {DSI_PAD_CONTROL_6_B01, 0},
- {DSI_PAD_CONTROL_7_B01, 0}
-};
-static const cfg_op_t _display_dsi_init_config_part4[10] = {
- {DSI_PAD_CONTROL_CD, 0},
- {DSI_SOL_DELAY, 0x18},
- {DSI_MAX_THRESHOLD, 0x1E0},
- {DSI_TRIGGER, 0},
- {DSI_INIT_SEQ_CONTROL, 0},
- {DSI_PKT_LEN_0_1, 0},
- {DSI_PKT_LEN_2_3, 0},
- {DSI_PKT_LEN_4_5, 0},
- {DSI_PKT_LEN_6_7, 0},
- {DSI_PAD_CONTROL_1, 0}
-};
-static const cfg_op_t _display_dsi_init_config_part5[12] = {
- {DSI_PHY_TIMING_1, 0x40A0E05},
- {DSI_PHY_TIMING_2, 0x30109},
- {DSI_BTA_TIMING, 0x190A14},
- {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF)},
- {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x765) | DSI_TIMEOUT_TA(0x2000)},
- {DSI_TO_TALLY, 0},
- {DSI_PAD_CONTROL_0, DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0)}, // Enable
- {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
- {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
- {DSI_POWER_CONTROL, 0},
- {DSI_POWER_CONTROL, 0},
- {DSI_PAD_CONTROL_1, 0}
-};
-static const cfg_op_t _display_dsi_init_config_part6[14] = {
- {DSI_PHY_TIMING_1, 0x40A0E05},
- {DSI_PHY_TIMING_2, 0x30118},
- {DSI_BTA_TIMING, 0x190A14},
- {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF)},
- {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x1343) | DSI_TIMEOUT_TA(0x2000)},
- {DSI_TO_TALLY, 0},
- {DSI_HOST_CONTROL, DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
- {DSI_CONTROL, DSI_CONTROL_LANES(3) | DSI_CONTROL_HOST_ENABLE},
- {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
- {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
- {DSI_MAX_THRESHOLD, 0x40},
- {DSI_TRIGGER, 0},
- {DSI_TX_CRC, 0},
- {DSI_INIT_SEQ_CONTROL, 0}
-};
-
-//DSI panel config.
-static const cfg_op_t _display_init_config_jdi[43] = {
- {DSI_WR_DATA, 0x0439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- {DSI_WR_DATA, 0x9483FFB9}, // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0xBD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 0 to 0xBD.
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x1939}, // MIPI_DSI_DCS_LONG_WRITE: 25 bytes.
- {DSI_WR_DATA, 0xAAAAAAD8}, // Register: 0xD8.
- {DSI_WR_DATA, 0xAAAAAAEB},
- {DSI_WR_DATA, 0xAAEBAAAA},
- {DSI_WR_DATA, 0xAAAAAAAA},
- {DSI_WR_DATA, 0xAAAAAAEB},
- {DSI_WR_DATA, 0xAAEBAAAA},
- {DSI_WR_DATA, 0xAA},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x01BD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 1 to 0xBD.
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x2739}, // MIPI_DSI_DCS_LONG_WRITE: 39 bytes.
- {DSI_WR_DATA, 0xFFFFFFD8}, // Register: 0xD8.
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFF},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x02BD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 2 to 0xBD.
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0xF39}, // MIPI_DSI_DCS_LONG_WRITE: 15 bytes.
- {DSI_WR_DATA, 0xFFFFFFD8}, // Register: 0xD8.
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFFFF},
- {DSI_WR_DATA, 0xFFFFFF},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x00BD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 0 to 0xBD.
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x06D915}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 6 to 0xD9.
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- {DSI_WR_DATA, 0x000000B9}, // MIPI_DCS_PRIV_SET_EXTC. Disable.
- {DSI_TRIGGER, DSI_TRIGGER_HOST}
-};
-
-//DSI packet config.
-static const cfg_op_t _display_dsi_packet_config[19] = {
- {DSI_PHY_TIMING_1, 0x40A0E05},
- {DSI_PHY_TIMING_2, 0x30172},
- {DSI_BTA_TIMING, 0x190A14},
- {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xA40)},
- {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x5A2F) | DSI_TIMEOUT_TA(0x2000)},
- {DSI_TO_TALLY, 0},
- {DSI_PKT_SEQ_0_LO, 0x40000208},
- {DSI_PKT_SEQ_2_LO, 0x40000308},
- {DSI_PKT_SEQ_4_LO, 0x40000308},
- {DSI_PKT_SEQ_1_LO, 0x40000308},
- {DSI_PKT_SEQ_3_LO, 0x3F3B2B08},
- {DSI_PKT_SEQ_3_HI, 0x2CC},
- {DSI_PKT_SEQ_5_LO, 0x3F3B2B08},
- {DSI_PKT_SEQ_5_HI, 0x2CC},
- {DSI_PKT_LEN_0_1, 0xCE0000},
- {DSI_PKT_LEN_2_3, 0x87001A2},
- {DSI_PKT_LEN_4_5, 0x190},
- {DSI_PKT_LEN_6_7, 0x190},
- {DSI_HOST_CONTROL, 0}
-};
-
-//DSI mode config.
-static const cfg_op_t _display_dsi_mode_config[10] = {
- {DSI_TRIGGER, 0},
- {DSI_CONTROL, 0},
- {DSI_SOL_DELAY, 6},
- {DSI_MAX_THRESHOLD, 0x1E0},
- {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
- {DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE},
- {DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_FIFO_SEL| DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
- {DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE},
- {DSI_HOST_CONTROL, DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
- {DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}
-};
-
-//MIPI CAL config.
-static const cfg_op_t _display_mipi_pad_cal_config[4] = {
- {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0},
- {MIPI_CAL_CIL_MIPI_CAL_STATUS, 0xF3F10000},
- {MIPI_CAL_MIPI_BIAS_PAD_CFG0, 0},
- {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0}
-};
-
-//DSI config.
-static const cfg_op_t _display_dsi_pad_cal_config_t210[4] = {
- {DSI_PAD_CONTROL_1, 0},
- {DSI_PAD_CONTROL_2, 0},
- {DSI_PAD_CONTROL_3, DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3)},
- {DSI_PAD_CONTROL_4, 0}
-};
-static const cfg_op_t _display_dsi_pad_cal_config_t210b01[7] = {
- {DSI_PAD_CONTROL_1, 0},
- {DSI_PAD_CONTROL_2, 0},
- {DSI_PAD_CONTROL_3, 0},
- {DSI_PAD_CONTROL_4, 0x77777},
- {DSI_PAD_CONTROL_5_B01, 0x77777},
- {DSI_PAD_CONTROL_6_B01, 0x1111},
- {DSI_PAD_CONTROL_7_B01, 0}
-};
-
-//MIPI CAL config.
-static const cfg_op_t _display_mipi_dsi_cal_offsets_config_t210[4] = {
- {MIPI_CAL_DSIA_MIPI_CAL_CONFIG, 0x200200},
- {MIPI_CAL_DSIB_MIPI_CAL_CONFIG, 0x200200},
- {MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2, 0x200002},
- {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0x200002}
-};
-static const cfg_op_t _display_mipi_dsi_cal_offsets_config_t210b01[4] = {
- {MIPI_CAL_DSIA_MIPI_CAL_CONFIG, 0x200006},
- {MIPI_CAL_DSIB_MIPI_CAL_CONFIG, 0x200006},
- {MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2, 0x260000},
- {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0x260000}
-};
-static const cfg_op_t _display_mipi_apply_dsi_cal_config[12] = {
- {MIPI_CAL_CILA_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_CILB_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_CILC_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_CILD_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_CILE_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_CILF_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_DSIC_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_DSID_MIPI_CAL_CONFIG, 0},
- {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0},
- {MIPI_CAL_DSIC_MIPI_CAL_CONFIG_2, 0},
- {MIPI_CAL_DSID_MIPI_CAL_CONFIG_2, 0},
- {MIPI_CAL_MIPI_CAL_CTRL, 0x2A000001}
-};
-
-//Display A config.
-static const cfg_op_t _display_video_disp_controller_enable_config[113] = {
- {DC_CMD_STATE_ACCESS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_DV_CONTROL, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- /* Setup default YUV colorspace conversion coefficients */
- {DC_WIN_CSC_YOF, 0xF0},
- {DC_WIN_CSC_KYRGB, 0x12A},
- {DC_WIN_CSC_KUR, 0},
- {DC_WIN_CSC_KVR, 0x198},
- {DC_WIN_CSC_KUG, 0x39B},
- {DC_WIN_CSC_KVG, 0x32F},
- {DC_WIN_CSC_KUB, 0x204},
- {DC_WIN_CSC_KVB, 0},
- /* End of color coefficients */
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_DV_CONTROL, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- /* Setup default YUV colorspace conversion coefficients */
- {DC_WIN_CSC_YOF, 0xF0},
- {DC_WIN_CSC_KYRGB, 0x12A},
- {DC_WIN_CSC_KUR, 0},
- {DC_WIN_CSC_KVR, 0x198},
- {DC_WIN_CSC_KUG, 0x39B},
- {DC_WIN_CSC_KVG, 0x32F},
- {DC_WIN_CSC_KUB, 0x204},
- {DC_WIN_CSC_KVB, 0},
- /* End of color coefficients */
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_DV_CONTROL, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- /* Setup default YUV colorspace conversion coefficients */
- {DC_WIN_CSC_YOF, 0xF0},
- {DC_WIN_CSC_KYRGB, 0x12A},
- {DC_WIN_CSC_KUR, 0},
- {DC_WIN_CSC_KVR, 0x198},
- {DC_WIN_CSC_KUG, 0x39B},
- {DC_WIN_CSC_KVG, 0x32F},
- {DC_WIN_CSC_KUB, 0x204},
- {DC_WIN_CSC_KVB, 0},
- /* End of color coefficients */
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
- {DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
- {DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000},
- {DC_COM_PIN_OUTPUT_POLARITY(3), 0},
- {DC_DISP_BLEND_BACKGROUND_COLOR, 0},
- {DC_COM_CRC_CONTROL, 0},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WINBUF_BLEND_LAYER_CONTROL, 0x10000FF},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WINBUF_BLEND_LAYER_CONTROL, 0x10000FF},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WINBUF_BLEND_LAYER_CONTROL, 0x10000FF},
- {DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_STOP},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
- {DC_CMD_STATE_ACCESS, 0},
-
- /* Set Display timings
- *
- * DC_DISP_REF_TO_SYNC:
- * V_REF_TO_SYNC - 1
- * H_REF_TO_SYNC - 0
- *
- * DC_DISP_SYNC_WIDTH:
- * V_SYNC_WIDTH - 1
- * H_SYNC_WIDTH - 72
- *
- * DC_DISP_BACK_PORCH:
- * V_BACK_PORCH - 9
- * H_BACK_PORCH - 72
- *
- * DC_DISP_ACTIVE:
- * V_DISP_ACTIVE - 1280
- * H_DISP_ACTIVE - 720
- *
- * DC_DISP_FRONT_PORCH:
- * V_FRONT_PORCH - 10
- * H_FRONT_PORCH - 136
- */
- {DC_DISP_DISP_TIMING_OPTIONS, 0},
- {DC_DISP_REF_TO_SYNC, 0x10000},
- {DC_DISP_SYNC_WIDTH, 0x10048},
- {DC_DISP_BACK_PORCH, 0x90048},
- {DC_DISP_ACTIVE, 0x50002D0},
- {DC_DISP_FRONT_PORCH, 0xA0088}, // Sources say that this should happen before DC_DISP_ACTIVE cmd.
- /* End of Display timings */
-
- {DC_DISP_SHIFT_CLOCK_OPTIONS, SC1_H_QUALIFIER_NONE | SC0_H_QUALIFIER_NONE},
- {DC_COM_PIN_OUTPUT_ENABLE(1), 0},
- {DC_DISP_DATA_ENABLE_OPTIONS, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL},
- {DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
- {DC_DISP_DISP_CLOCK_CONTROL, 0},
- {DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_STATE_ACCESS, READ_MUX | WRITE_MUX},
- {DC_DISP_FRONT_PORCH, 0xA0088},
- {DC_CMD_STATE_ACCESS, 0},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_GENERAL_INCR_SYNCPT, 0x301},
- {DC_CMD_GENERAL_INCR_SYNCPT, 0x301},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_STATE_ACCESS, 0},
- {DC_DISP_DISP_CLOCK_CONTROL, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4)},
- {DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
- {DC_CMD_DISPLAY_COMMAND_OPTION0, 0}
-};
-
-////Display A config.
-static const cfg_op_t _display_video_disp_controller_disable_config[17] = {
- {DC_DISP_FRONT_PORCH, 0xA0088},
- {DC_CMD_INT_MASK, 0},
- {DC_CMD_STATE_ACCESS, 0},
- {DC_CMD_INT_ENABLE, 0},
- {DC_CMD_CONT_SYNCPT_VSYNC, 0},
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_STOP},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_GENERAL_INCR_SYNCPT, 0x301},
- {DC_CMD_GENERAL_INCR_SYNCPT, 0x301},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
- {DC_CMD_DISPLAY_POWER_CONTROL, 0},
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
-};
-
-//DSI config.
-static const cfg_op_t _display_dsi_timing_deinit_config[16] = {
- {DSI_POWER_CONTROL, 0},
- {DSI_PAD_CONTROL_1, 0},
- {DSI_PHY_TIMING_0, 0x6070601},
- {DSI_PHY_TIMING_1, 0x40A0E05},
- {DSI_PHY_TIMING_2, 0x30118},
- {DSI_BTA_TIMING, 0x190A14},
- {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF) },
- {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x1343) | DSI_TIMEOUT_TA(0x2000)},
- {DSI_TO_TALLY, 0},
- {DSI_HOST_CONTROL, DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
- {DSI_CONTROL, DSI_CONTROL_LANES(3) | DSI_CONTROL_HOST_ENABLE},
- {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
- {DSI_MAX_THRESHOLD, 0x40},
- {DSI_TRIGGER, 0},
- {DSI_TX_CRC, 0},
- {DSI_INIT_SEQ_CONTROL, 0}
-};
-
-//DSI config (if ver == 0x10).
-static const cfg_op_t _display_deinit_config_jdi[22] = {
- {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- {DSI_WR_DATA, 0x9483FFB9}, // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x2139}, // MIPI_DSI_DCS_LONG_WRITE: 33 bytes.
- {DSI_WR_DATA, 0x191919D5}, // Register: 0xD5.
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0xB39}, // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
- {DSI_WR_DATA, 0x4F0F41B1}, // MIPI_DCS_PRIV_SET_POWER_CONTROL.
- {DSI_WR_DATA, 0xF179A433},
- {DSI_WR_DATA, 0x002D81},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- {DSI_WR_DATA, 0x000000B9}, // MIPI_DCS_PRIV_SET_EXTC. Disable.
- {DSI_TRIGGER, DSI_TRIGGER_HOST}
-};
-
-static const cfg_op_t _display_deinit_config_auo[37] = {
- {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- {DSI_WR_DATA, 0x9483FFB9}, // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x2C39}, // MIPI_DSI_DCS_LONG_WRITE: 44 bytes.
- {DSI_WR_DATA, 0x191919D5}, // Register: 0xD5.
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x2C39}, // MIPI_DSI_DCS_LONG_WRITE: 44 bytes.
- {DSI_WR_DATA, 0x191919D6}, // Register: 0xD6.
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_WR_DATA, 0x19191919},
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0xB39}, // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
- {DSI_WR_DATA, 0x711148B1}, // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
- // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32, Enter standby / PON / VCOMG).
- {DSI_WR_DATA, 0x71143209},
- {DSI_WR_DATA, 0x114D31}, // (Unknown).
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
- {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
- {DSI_WR_DATA, 0x000000B9}, // MIPI_DCS_PRIV_SET_EXTC. Disable.
- {DSI_TRIGGER, DSI_TRIGGER_HOST}
-};
-
-static const cfg_op_t _display_init_config_invert[3] = {
- {DSI_WR_DATA, 0x239},
- {DSI_WR_DATA, 0x02C1}, // INV_EN.
- {DSI_TRIGGER, DSI_TRIGGER_HOST},
-};
-
-//Display A config.
-static const cfg_op_t cfg_display_one_color[8] = {
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY} // Continuous display.
-};
-
-//Display A config linear pitch.
-static const cfg_op_t cfg_display_framebuffer_pitch[32] = {
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_POSITION, 0}, //(0,0)
- {DC_WIN_H_INITIAL_DDA, 0},
- {DC_WIN_V_INITIAL_DDA, 0},
- {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
- {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
- {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
- {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(720 * 2) | LINE_STRIDE(720 * 4)}, // 720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
- {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
- {DC_WINBUF_SURFACE_KIND, PITCH},
- {DC_WINBUF_START_ADDR, IPL_FB_ADDRESS}, // Framebuffer address.
- {DC_WINBUF_ADDR_H_OFFSET, 0},
- {DC_WINBUF_ADDR_V_OFFSET, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, WIN_ENABLE}, // Enable window AD.
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
-};
-
-//Display A config linear pitch inverse + Win D support.
-static const cfg_op_t cfg_display_framebuffer_pitch_inv[34] = {
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_D_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_POSITION, 0}, //(0,0)
- {DC_WIN_H_INITIAL_DDA, 0},
- {DC_WIN_V_INITIAL_DDA, 0},
- {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
- {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
- {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
- {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(720 * 2) | LINE_STRIDE(720 * 4)}, // 720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
- {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
- {DC_WINBUF_SURFACE_KIND, PITCH},
- {DC_WINBUF_START_ADDR, NYX_FB_ADDRESS}, // Framebuffer address.
- {DC_WINBUF_ADDR_H_OFFSET, 0}, // Linear: 0x383FFC, Block: 0x3813FC.
- {DC_WINBUF_ADDR_V_OFFSET, 1279}, // Linear: 1279, Block: 0.
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, WIN_ENABLE | V_DIRECTION}, // Enable window AD.
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
-};
-
-//Display A config block linear.
-static const cfg_op_t cfg_display_framebuffer_block[34] = {
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_D_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_POSITION, 0}, //(0,0)
- {DC_WIN_H_INITIAL_DDA, 0},
- {DC_WIN_V_INITIAL_DDA, 0},
- {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
- {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
- {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
- {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(1280 * 2) | LINE_STRIDE(1280 * 4)}, //720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
- {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
- {DC_WINBUF_SURFACE_KIND, BLOCK_HEIGHT(4) | BLOCK},
- {DC_WINBUF_START_ADDR, NYX_FB_ADDRESS}, // Framebuffer address.
- {DC_WINBUF_ADDR_H_OFFSET, 0x3813FC}, // Linear: 0x383FFC, Block: 0x3813FC.
- {DC_WINBUF_ADDR_V_OFFSET, 0}, // Linear: 1279, Block: 0.
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
- {DC_WIN_WIN_OPTIONS, WIN_ENABLE | SCAN_COLUMN | H_DIRECTION}, // Enable window AD. | SCAN_COLUMN | H_DIRECTION.
- {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
-};
-
-//Display D config.
-static const cfg_op_t cfg_display_framebuffer_log[20] = {
- {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_D_SELECT},
- {DC_WIN_WIN_OPTIONS, 0},
- {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8},
- {DC_WIN_POSITION, 0}, //(0,0)
- {DC_WIN_H_INITIAL_DDA, 0},
- {DC_WIN_V_INITIAL_DDA, 0},
- {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(656 * 4)},
- {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
- {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(656)},
- {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(656 * 2) | LINE_STRIDE(656 * 4)}, //656*2x656*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
- {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
- {DC_WINBUF_SURFACE_KIND, PITCH},
- {DC_WINBUF_START_ADDR, LOG_FB_ADDRESS}, // Framebuffer address.
- {DC_WINBUF_ADDR_H_OFFSET, 0},
- {DC_WINBUF_ADDR_V_OFFSET, 0},
- {DC_WINBUF_BLEND_LAYER_CONTROL, WIN_BLEND_ENABLE | WIN_K1(200)},
- {DC_WINBUF_BLEND_MATCH_SELECT, WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 | WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1},
- {DC_WIN_WIN_OPTIONS, 0}, // Enable window DD.
- {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_D_UPDATE},
- {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_D_ACT_REQ}
-};
+/*
+* Copyright (c) 2018 naehrwert
+* Copyright (c) 2018-2025 CTCaer
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms and conditions of the GNU General Public License,
+* version 2, as published by the Free Software Foundation.
+*
+* This program is distributed in the hope it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+// Display A config.
+static const reg_cfg_t _di_dc_init_config[] = {
+ /* Display init */
+ {DC_CMD_STATE_ACCESS, READ_MUX_ASSEMBLY | WRITE_MUX_ASSEMBLY},
+ {DC_CMD_REG_ACT_CONTROL, WIN_A_ACT_HCNTR_SEL | WIN_B_ACT_HCNTR_SEL | WIN_C_ACT_HCNTR_SEL | WIN_D_ACT_HCNTR_SEL},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+ {DC_DISP_DC_MCCIF_FIFOCTRL, 0},
+ {DC_DISP_DISP_MEM_HIGH_PRIORITY, 0},
+ {DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER, 0},
+ {DC_CMD_DISPLAY_POWER_CONTROL, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE},
+ {DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL},
+ {DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | SYNCPT_VSYNC_INDX(9)},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE | WIN_D_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ | WIN_D_ACT_REQ},
+
+ /* Disable A/B/C/D Windows */
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT | WINDOW_B_SELECT | WINDOW_C_SELECT | WINDOW_D_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_WIN_DV_CONTROL, 0},
+
+ /* Setup default YUV colorspace conversion coefficients */
+ {DC_WINC_CSC_YOF, 0xF0},
+ {DC_WINC_CSC_KYRGB, 0x12A},
+ {DC_WINC_CSC_KUR, 0},
+ {DC_WINC_CSC_KVR, 0x198},
+ {DC_WINC_CSC_KUG, 0x39B},
+ {DC_WINC_CSC_KVG, 0x32F},
+ {DC_WINC_CSC_KUB, 0x204},
+ {DC_WINC_CSC_KVB, 0},
+
+ /* Init color, format and background */
+ {DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
+ {DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
+ {DC_COM_PIN_OUTPUT_POLARITY(1), LSC0_OUTPUT_POLARITY_LOW},
+ {DC_COM_PIN_OUTPUT_POLARITY(3), 0},
+ {DC_DISP_BLEND_BACKGROUND_COLOR, 0},
+ {DC_COM_CRC_CONTROL, 0},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE | WIN_D_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ | WIN_D_ACT_REQ},
+
+ /* Stop display */
+ {DC_WINBUF_BLEND_LAYER_CONTROL, WIN_BLEND_BYPASS | WIN_BLEND_DEPTH(255)},
+ {DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
+ {DC_DISP_DISP_WIN_OPTIONS, 0},
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_STOP},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE | WIN_D_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ | WIN_D_ACT_REQ}
+};
+
+// DSI Init config.
+static const reg_cfg_t _di_dsi_seq_pkt_reset_config0[] = {
+ {DSI_WR_DATA, 0},
+ {DSI_INT_ENABLE, 0},
+ {DSI_INT_STATUS, 0},
+ {DSI_INT_MASK, 0},
+ {DSI_INIT_SEQ_DATA_0, 0},
+ {DSI_INIT_SEQ_DATA_1, 0},
+ {DSI_INIT_SEQ_DATA_2, 0},
+ {DSI_INIT_SEQ_DATA_3, 0}
+};
+static const reg_cfg_t _di_dsi_seq_pkt_reset_config1[] = {
+ {DSI_DCS_CMDS, 0},
+ {DSI_PKT_SEQ_0_LO, 0},
+ {DSI_PKT_SEQ_1_LO, 0},
+ {DSI_PKT_SEQ_2_LO, 0},
+ {DSI_PKT_SEQ_3_LO, 0},
+ {DSI_PKT_SEQ_4_LO, 0},
+ {DSI_PKT_SEQ_5_LO, 0},
+ {DSI_PKT_SEQ_0_HI, 0},
+ {DSI_PKT_SEQ_1_HI, 0},
+ {DSI_PKT_SEQ_2_HI, 0},
+ {DSI_PKT_SEQ_3_HI, 0},
+ {DSI_PKT_SEQ_4_HI, 0},
+ {DSI_PKT_SEQ_5_HI, 0},
+ {DSI_CONTROL, 0}
+};
+static const reg_cfg_t _di_dsi_init_pads_t210b01[] = {
+ {DSI_PAD_CONTROL_1, 0},
+ {DSI_PAD_CONTROL_2, 0},
+ {DSI_PAD_CONTROL_3, 0},
+ {DSI_PAD_CONTROL_4, 0},
+ {DSI_PAD_CONTROL_5_B01, 0},
+ {DSI_PAD_CONTROL_6_B01, 0},
+ {DSI_PAD_CONTROL_7_B01, 0}
+};
+static const reg_cfg_t _di_dsi_init_config[] = {
+ {DSI_PAD_CONTROL_CD, 0},
+ {DSI_SOL_DELAY, 24},
+ {DSI_MAX_THRESHOLD, 480},
+ {DSI_TRIGGER, 0},
+ {DSI_INIT_SEQ_CONTROL, 0},
+
+ {DSI_PKT_LEN_0_1, 0},
+ {DSI_PKT_LEN_2_3, 0},
+ {DSI_PKT_LEN_4_5, 0},
+ {DSI_PKT_LEN_6_7, 0},
+
+ {DSI_PAD_CONTROL_1, 0},
+
+ /* DSI PHY timings Init - 19.2 MHz */
+ {DSI_PHY_TIMING_0, 0x6070603},
+ {DSI_PHY_TIMING_1, 0x40A0E05},
+ {DSI_PHY_TIMING_2, 0x30109},
+ {DSI_BTA_TIMING, 0x190A14},
+ /* DSI timeout */
+ {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF)},
+ {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x765) | DSI_TIMEOUT_TA(0x2000)},
+ {DSI_TO_TALLY, 0},
+
+ {DSI_PAD_CONTROL_0, DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0)}, // Power up.
+ {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
+};
+
+// DSI LP config.
+static const reg_cfg_t _di_dsi_timing_lp_config[] = {
+ {DSI_PAD_CONTROL_1, 0},
+
+ /* DSI PHY timings LP - 50 MHz */
+ {DSI_PHY_TIMING_0, 0x6070603},
+ {DSI_PHY_TIMING_1, 0x40A0E05},
+ {DSI_PHY_TIMING_2, 0x30118},
+ {DSI_BTA_TIMING, 0x190A14},
+ /* DSI timeout */
+ {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF)},
+ {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x1343) | DSI_TIMEOUT_TA(0x2000)},
+ {DSI_TO_TALLY, 0},
+
+ {DSI_HOST_CONTROL, DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
+ {DSI_CONTROL, DSI_CONTROL_LANES(3) | DSI_CONTROL_HOST_ENABLE},
+ {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
+ {DSI_MAX_THRESHOLD, 64},
+ {DSI_TRIGGER, 0},
+ {DSI_TX_CRC, 0},
+ {DSI_INIT_SEQ_CONTROL, 0}
+};
+
+// DSI panel JDI config.
+static const reg_cfg_t _di_dsi_panel_init_config_jdi[] = {
+ {DSI_WR_DATA, 0x0439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ {DSI_WR_DATA, 0x9483FFB9}, // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0xBD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 0 to 0xBD.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x1939}, // MIPI_DSI_DCS_LONG_WRITE: 25 bytes.
+ {DSI_WR_DATA, 0xAAAAAAD8}, // Register: 0xD8.
+ {DSI_WR_DATA, 0xAAAAAAEB},
+ {DSI_WR_DATA, 0xAAEBAAAA},
+ {DSI_WR_DATA, 0xAAAAAAAA},
+ {DSI_WR_DATA, 0xAAAAAAEB},
+ {DSI_WR_DATA, 0xAAEBAAAA},
+ {DSI_WR_DATA, 0xAA},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x01BD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 1 to 0xBD.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x2739}, // MIPI_DSI_DCS_LONG_WRITE: 39 bytes.
+ {DSI_WR_DATA, 0xFFFFFFD8}, // Register: 0xD8.
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFF},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x02BD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 2 to 0xBD.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0xF39}, // MIPI_DSI_DCS_LONG_WRITE: 15 bytes.
+ {DSI_WR_DATA, 0xFFFFFFD8}, // Register: 0xD8.
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFFFF},
+ {DSI_WR_DATA, 0xFFFFFF},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x00BD15}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 0 to 0xBD.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x06D915}, // MIPI_DSI_DCS_SHORT_WRITE_PARAM: 6 to 0xD9.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ {DSI_WR_DATA, 0x000000B9}, // MIPI_DCS_PRIV_SET_EXTC. Disable.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST}
+};
+
+// DSI HS packet config.
+static const reg_cfg_t _di_dsi_seq_pkt_video_non_burst_no_eot_config[] = {
+ {DSI_PAD_CONTROL_1, 0},
+
+ /* DSI PHY timings HP - 234 MHz */
+ {DSI_PHY_TIMING_0, 0x6070603},
+ {DSI_PHY_TIMING_1, 0x40A0E05},
+ {DSI_PHY_TIMING_2, 0x30172},
+ {DSI_BTA_TIMING, 0x190A14},
+ /* DSI timeout */
+ {DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xA40)},
+ {DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x5A2F) | DSI_TIMEOUT_TA(0x2000)},
+ {DSI_TO_TALLY, 0},
+
+ /* DSI packet sequence */
+ {DSI_PKT_SEQ_0_LO, 0x40000208},
+ {DSI_PKT_SEQ_2_LO, 0x40000308},
+ {DSI_PKT_SEQ_4_LO, 0x40000308},
+ {DSI_PKT_SEQ_1_LO, 0x40000308},
+ {DSI_PKT_SEQ_3_LO, 0x3F3B2B08},
+ {DSI_PKT_SEQ_3_HI, 0x2CC},
+ {DSI_PKT_SEQ_5_LO, 0x3F3B2B08},
+ {DSI_PKT_SEQ_5_HI, 0x2CC},
+ {DSI_PKT_LEN_0_1, PKT1_LEN(206) | PKT0_LEN(0)},
+ {DSI_PKT_LEN_2_3, PKT1_LEN(2160) | PKT0_LEN(418)},
+ {DSI_PKT_LEN_4_5, PKT1_LEN(0) | PKT0_LEN(400)},
+ {DSI_PKT_LEN_6_7, PKT1_LEN(0) | PKT0_LEN(400)},
+ {DSI_HOST_CONTROL, 0}
+};
+
+// DSI mode config.
+static const reg_cfg_t _di_dsi_host_mode_config[] = {
+ {DSI_TRIGGER, 0},
+ {DSI_CONTROL, 0},
+ {DSI_SOL_DELAY, 6},
+ {DSI_MAX_THRESHOLD, 480},
+ {DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
+ {DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE},
+ {DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_FIFO_SEL | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
+ {DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE},
+ {DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_SOL | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
+ {DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_TX_TRIG_SOL | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}
+};
+
+// MIPI CAL config.
+static const reg_cfg_t _di_mipi_pad_cal_config[] = {
+ {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0},
+ {MIPI_CAL_CIL_MIPI_CAL_STATUS, 0xF3F10000},
+ {MIPI_CAL_MIPI_BIAS_PAD_CFG0, 0},
+ {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0}
+};
+
+// DSI pad config.
+static const reg_cfg_t _di_dsi_pad_cal_config_t210[] = {
+ {DSI_PAD_CONTROL_1, 0},
+ {DSI_PAD_CONTROL_2, 0},
+ {DSI_PAD_CONTROL_3, DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3)},
+ {DSI_PAD_CONTROL_4, 0}
+};
+static const reg_cfg_t _di_dsi_pad_cal_config_t210b01[] = {
+ {DSI_PAD_CONTROL_1, 0},
+ {DSI_PAD_CONTROL_2, 0},
+ {DSI_PAD_CONTROL_3, 0},
+ {DSI_PAD_CONTROL_4, 0x77777},
+ {DSI_PAD_CONTROL_5_B01, 0x77777},
+ {DSI_PAD_CONTROL_6_B01, DSI_PAD_PREEMP_PD_CLK(0x1) | DSI_PAD_PREEMP_PU_CLK(0x1) | DSI_PAD_PREEMP_PD(0x01) | DSI_PAD_PREEMP_PU(0x1)},
+ {DSI_PAD_CONTROL_7_B01, 0}
+};
+
+// MIPI CAL config.
+static const reg_cfg_t _di_mipi_dsi_cal_prod_config_t210[] = {
+ {MIPI_CAL_DSIA_MIPI_CAL_CONFIG, 0x200200},
+ {MIPI_CAL_DSIB_MIPI_CAL_CONFIG, 0x200200},
+ {MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2, 0x200002},
+ {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0x200002}
+};
+static const reg_cfg_t _di_mipi_dsi_cal_prod_config_t210b01[] = {
+ {MIPI_CAL_DSIA_MIPI_CAL_CONFIG, 0x200006},
+ {MIPI_CAL_DSIB_MIPI_CAL_CONFIG, 0x200006},
+ {MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2, 0x260000},
+ {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0x260000}
+};
+static const reg_cfg_t _di_mipi_dsi_cal_unused_config[] = {
+ {MIPI_CAL_CILA_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_CILB_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_CILC_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_CILD_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_CILE_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_CILF_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_DSIC_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_DSID_MIPI_CAL_CONFIG, 0},
+ {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0},
+ {MIPI_CAL_DSIC_MIPI_CAL_CONFIG_2, 0},
+ {MIPI_CAL_DSID_MIPI_CAL_CONFIG_2, 0}
+};
+
+// Display A enable config.
+static const reg_cfg_t _di_dc_video_mode_config[] = {
+ /* Set panel timings */
+ {DC_DISP_DISP_TIMING_OPTIONS, VSYNC_H_POSITION(0)},
+ {DC_DISP_REF_TO_SYNC, V_REF_TO_SYNC(1) | H_REF_TO_SYNC(0)},
+ {DC_DISP_SYNC_WIDTH, V_SYNC_WIDTH(1) | H_SYNC_WIDTH(72)},
+ {DC_DISP_BACK_PORCH, V_BACK_PORCH(9) | H_BACK_PORCH(72)},
+ {DC_DISP_FRONT_PORCH, V_FRONT_PORCH(10) | H_FRONT_PORCH(136)},
+ {DC_DISP_ACTIVE, V_DISP_ACTIVE(1280) | H_DISP_ACTIVE(720)},
+ /* End of Display timings */
+
+ {DC_DISP_SHIFT_CLOCK_OPTIONS, SC1_H_QUALIFIER_NONE | SC0_H_QUALIFIER_NONE},
+ {DC_COM_PIN_OUTPUT_ENABLE(1), 0},
+ {DC_DISP_DATA_ENABLE_OPTIONS, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL},
+
+ /* Start continuous display. */
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+ {DC_CMD_GENERAL_INCR_SYNCPT, SYNCPT_GENERAL_COND(COND_REG_WR_SAFE) | SYNCPT_GENERAL_INDX(1)},
+ {DC_CMD_GENERAL_INCR_SYNCPT, SYNCPT_GENERAL_COND(COND_REG_WR_SAFE) | SYNCPT_GENERAL_INDX(1)},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+};
+
+// Display A disable config.
+static const reg_cfg_t _di_dc_video_disable_config[] = {
+ {DC_CMD_INT_MASK, 0},
+ {DC_CMD_STATE_ACCESS, READ_MUX_ASSEMBLY | WRITE_MUX_ASSEMBLY},
+ {DC_CMD_INT_ENABLE, 0},
+ {DC_CMD_CONT_SYNCPT_VSYNC, 0},
+
+ /* Stop display. */
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_STOP},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+ {DC_CMD_GENERAL_INCR_SYNCPT, SYNCPT_GENERAL_COND(COND_REG_WR_SAFE) | SYNCPT_GENERAL_INDX(1)},
+ {DC_CMD_GENERAL_INCR_SYNCPT, SYNCPT_GENERAL_COND(COND_REG_WR_SAFE) | SYNCPT_GENERAL_INDX(1)},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+ // TODO: LCD panels should sleep for 40ms here.
+ {DC_CMD_DISPLAY_POWER_CONTROL, 0},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
+};
+
+// DSI panel JDI deinit config.
+static const reg_cfg_t _di_dsi_panel_deinit_config_jdi[] = {
+ {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ {DSI_WR_DATA, 0x9483FFB9}, // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x2139}, // MIPI_DSI_DCS_LONG_WRITE: 33 bytes.
+ {DSI_WR_DATA, 0x191919D5}, // Register: 0xD5.
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0xB39}, // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
+ {DSI_WR_DATA, 0x4F0F41B1}, // MIPI_DCS_PRIV_SET_POWER_CONTROL.
+ {DSI_WR_DATA, 0xF179A433},
+ {DSI_WR_DATA, 0x002D81},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ {DSI_WR_DATA, 0x000000B9}, // MIPI_DCS_PRIV_SET_EXTC. Disable.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST}
+};
+
+// DSI panel AUO deinit config.
+static const reg_cfg_t _di_dsi_panel_deinit_config_auo[] = {
+ {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ {DSI_WR_DATA, 0x9483FFB9}, // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x2C39}, // MIPI_DSI_DCS_LONG_WRITE: 44 bytes.
+ {DSI_WR_DATA, 0x191919D5}, // Register: 0xD5.
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x2C39}, // MIPI_DSI_DCS_LONG_WRITE: 44 bytes.
+ {DSI_WR_DATA, 0x191919D6}, // Register: 0xD6.
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_WR_DATA, 0x19191919},
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0xB39}, // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
+ {DSI_WR_DATA, 0x711148B1}, // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
+ // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32, Enter standby / PON / VCOMG).
+ {DSI_WR_DATA, 0x71143209},
+ {DSI_WR_DATA, 0x114D31}, // (Unknown).
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+ {DSI_WR_DATA, 0x439}, // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
+ {DSI_WR_DATA, 0x000000B9}, // MIPI_DCS_PRIV_SET_EXTC. Disable.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST}
+};
+
+/*
+static const reg_cfg_t _di_init_config_invert[] = {
+ {DSI_WR_DATA, 0x239},
+ {DSI_WR_DATA, 0x02C1}, // INV_EN.
+ {DSI_TRIGGER, DSI_TRIGGER_HOST},
+};
+*/
+
+// Display A Window A one color config.
+static const reg_cfg_t _di_win_one_color[] = {
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT | WINDOW_B_SELECT | WINDOW_C_SELECT | WINDOW_D_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY} // Continuous display.
+};
+
+// Display A Window A linear pitch config.
+static const reg_cfg_t _di_winA_pitch[] = {
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
+ {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
+ {DC_WIN_POSITION, 0}, //(0,0)
+ {DC_WIN_H_INITIAL_DDA, 0},
+ {DC_WIN_V_INITIAL_DDA, 0},
+ {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
+ {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
+ {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
+ {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(720 * 2) | LINE_STRIDE(720 * 4)}, // 720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
+ {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
+ {DC_WINBUF_SURFACE_KIND, PITCH},
+ {DC_WINBUF_START_ADDR, IPL_FB_ADDRESS}, // Framebuffer address.
+ {DC_WINBUF_ADDR_H_OFFSET, 0},
+ {DC_WINBUF_ADDR_V_OFFSET, 0},
+ {DC_WIN_WIN_OPTIONS, WIN_ENABLE}, // Enable window AD.
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
+};
+
+// Display A Window A linear pitch + Win D support config.
+static const reg_cfg_t _di_winA_pitch_vic[] = {
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
+ {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
+ {DC_WIN_POSITION, 0}, //(0,0)
+ {DC_WIN_H_INITIAL_DDA, 0},
+ {DC_WIN_V_INITIAL_DDA, 0},
+ {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
+ {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
+ {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
+ {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(720 * 2) | LINE_STRIDE(720 * 4)}, // 720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
+ {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
+ {DC_WINBUF_SURFACE_KIND, PITCH},
+ {DC_WINBUF_START_ADDR, NYX_FB_ADDRESS}, // Framebuffer address.
+ {DC_WINBUF_ADDR_H_OFFSET, 0},
+ {DC_WINBUF_ADDR_V_OFFSET, 0},
+ {DC_WIN_WIN_OPTIONS, WIN_ENABLE}, // Enable window AD.
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
+};
+
+// Display A Window A linear pitch inverse + Win D support config.
+static const reg_cfg_t _di_winA_pitch_inv[] = {
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
+ {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
+ {DC_WIN_POSITION, 0}, //(0,0)
+ {DC_WIN_H_INITIAL_DDA, 0},
+ {DC_WIN_V_INITIAL_DDA, 0},
+ {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
+ {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
+ {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
+ {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(720 * 2) | LINE_STRIDE(720 * 4)}, // 720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
+ {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
+ {DC_WINBUF_SURFACE_KIND, PITCH},
+ {DC_WINBUF_START_ADDR, NYX_FB_ADDRESS}, // Framebuffer address.
+ {DC_WINBUF_ADDR_H_OFFSET, 0}, // Linear: 0x383FFC, Block: 0x3813FC.
+ {DC_WINBUF_ADDR_V_OFFSET, 1279}, // Linear: 1279, Block: 0.
+ {DC_WIN_WIN_OPTIONS, WIN_ENABLE | V_DIRECTION}, // Enable window AD.
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
+};
+
+// Display A Window A block linear config.
+static const reg_cfg_t _di_winA_block[] = {
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE},
+ {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, // NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8.
+ {DC_WIN_POSITION, 0}, //(0,0)
+ {DC_WIN_H_INITIAL_DDA, 0},
+ {DC_WIN_V_INITIAL_DDA, 0},
+ {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(720 * 4)},
+ {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
+ {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)},
+ {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(1280 * 2) | LINE_STRIDE(1280 * 4)}, //720*2x720*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
+ {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
+ {DC_WINBUF_SURFACE_KIND, BLOCK_HEIGHT(4) | BLOCK},
+ {DC_WINBUF_START_ADDR, NYX_FB_ADDRESS}, // Framebuffer address.
+ {DC_WINBUF_ADDR_H_OFFSET, 0x3813FC}, // Linear: 0x383FFC, Block: 0x3813FC. Block in HOS: 0x13FF.
+ {DC_WINBUF_ADDR_V_OFFSET, 0}, // Linear: 1279, Block: 0.
+ {DC_WIN_WIN_OPTIONS, WIN_ENABLE | SCAN_COLUMN | H_DIRECTION}, // Enable window AD. | SCAN_COLUMN | H_DIRECTION.
+ {DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, // Continuous display.
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ}
+};
+
+// Display A Window D config.
+static const reg_cfg_t _di_winD_log[] = {
+ {DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_D_SELECT},
+ {DC_WIN_WIN_OPTIONS, 0},
+ {DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8},
+ {DC_WIN_POSITION, 0}, //(0,0)
+ {DC_WIN_H_INITIAL_DDA, 0},
+ {DC_WIN_V_INITIAL_DDA, 0},
+ {DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(656 * 4)},
+ {DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, // 1.0x.
+ {DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(656)},
+ {DC_WIN_LINE_STRIDE, UV_LINE_STRIDE(656 * 2) | LINE_STRIDE(656 * 4)}, //656*2x656*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
+ {DC_WIN_BUFFER_CONTROL, BUFFER_CONTROL_HOST},
+ {DC_WINBUF_SURFACE_KIND, PITCH},
+ {DC_WINBUF_START_ADDR, LOG_FB_ADDRESS}, // Framebuffer address.
+ {DC_WINBUF_ADDR_H_OFFSET, 0},
+ {DC_WINBUF_ADDR_V_OFFSET, 0},
+ {DC_WINBUF_BLEND_LAYER_CONTROL, WIN_BLEND_ENABLE | WIN_K1(200) | WIN_BLEND_DEPTH(0)},
+ {DC_WINBUF_BLEND_MATCH_SELECT, WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 | WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1},
+ {DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_D_UPDATE},
+ {DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_D_ACT_REQ},
+};
diff --git a/bdk/display/vic.c b/bdk/display/vic.c
new file mode 100644
index 0000000..5cd4f1f
--- /dev/null
+++ b/bdk/display/vic.c
@@ -0,0 +1,561 @@
+/*
+ * VIC (4.0) driver for Tegra X1
+ *
+ * Copyright (c) 2018-2024 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "vic.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* VIC Private registers */
+#define PVIC_FALCON_PA_OFFSET 0x1000
+#define PVIC_FALCON_ADDR 0x10AC
+#define PVIC_FALCON_IDLESTATE 0x104C
+
+/* VIC Control and Status registers. */
+/* Fetch Control registers. */
+#define VIC_FC_COMPOSE 0x10000
+#define COMPOSE_START BIT(0)
+
+#define VIC_FC_CFG_STRUCT_SLOT_INDEX 0x10B00
+
+#define VIC_FC_CFG_STRUCT_SLOT_CFG0 0x10B04
+#define SLOT_ENABLE BIT(0)
+#define FIELD_CURRENT_ENABLE BIT(8)
+
+#define VIC_FC_CFG_STRUCT_SLOT_CFG2 0x10B0C
+#define CACHE_WIDTH(n) ((n) << 16)
+#define CACHE_WIDTH_16BX16 0 // Block Linear.
+#define CACHE_WIDTH_32BX8 1 // Block Linear.
+#define CACHE_WIDTH_64BX4 2 // Block Linear, Pitch. Recommended.
+#define CACHE_WIDTH_128BX2 3 // Block Linear, Pitch.
+#define CACHE_WIDTH_256BX1 4 // Pitch.
+#define OUTPUT_FLIP_X BIT(20)
+#define OUTPUT_FLIP_Y BIT(21)
+#define OUTPUT_TRANSPOSE BIT(22)
+
+#define VIC_FC_CFG_STRUCT_SLOT_SFC_SIZE 0x10B10
+#define VIC_FC_CFG_STRUCT_SLOT_LUMA_SIZE 0x10B14
+#define VIC_FC_CFG_STRUCT_SLOT_CHROMA_SIZE 0x10B18
+#define VIC_FC_CFG_STRUCT_SLOT_SRC_RECT_LR 0x10B1C
+#define VIC_FC_CFG_STRUCT_SLOT_SRC_RECT_TB 0x10B20
+#define VIC_FC_CFG_STRUCT_SLOT_DST_RECT_LR 0x10B30
+#define VIC_FC_CFG_STRUCT_SLOT_DST_RECT_TB 0x10B34
+#define VIC_FC_CFG_STRUCT_TGT_RECT_LR 0x10B38
+#define VIC_FC_CFG_STRUCT_TGT_RECT_TB 0x10B3C
+#define VIC_FC_SLOT_MAP 0x10C00
+
+#define VIC_FC_FCE_CTRL 0x11000
+#define START_TRIGGER BIT(0)
+#define HALT_TRIGGER BIT(1)
+#define CLEAR_ERROR BIT(8)
+
+#define VIC_FC_FCE_UCODE_ADDR 0x11200
+#define VIC_FC_FCE_UCODE_INST 0x11300
+
+/* Surface List registers. */
+#define VIC_SL_CFG_STRUCT_SLOT_INDEX 0x12100
+#define VIC_SL_CFG_STRUCT_SLOT_DST_RECT_LR 0x12200
+#define VIC_SL_CFG_STRUCT_SLOT_DST_RECT_TB 0x12300
+#define VIC_SL_CFG_STRUCT_TGT_RECT_LR 0x12400
+#define VIC_SL_CFG_STRUCT_TGT_RECT_TB 0x12500
+#define VIC_SL_CFG_STRUCT_SLOT_CFG0 0x12600
+
+/* Surface Cache registers. */
+#define VIC_SC_PRAMBASE 0x14000
+#define VIC_SC_PRAMSIZE 0x14100
+#define VIC_SC_SFC0_BASE_LUMA(n) (0x14300 + (n) * 0x100)
+
+/* Blending Output registers. */
+#define VIC_BL_TARGET_BASADR 0x22000
+#define VIC_BL_CONFIG 0x22800
+#define SUBPARTITION_MODE BIT(0)
+#define PROCESS_CFG_STRUCT_TRIGGER BIT(2)
+#define SLOTMASK(n) ((n) << 8)
+
+#define VIC_BL_CFG_STRUCT_CFG0 0x22C00
+#define VIC_BL_CFG_STRUCT_SFC_SIZE 0x22C04
+#define VIC_BL_CFG_STRUCT_LUMA_SIZE 0x22C08
+#define VIC_BL_CFG_STRUCT_CHROMA_SIZE 0x22C0C
+#define VIC_BL_CFG_STRUCT_TGT_RECT_LR 0x22C10
+#define VIC_BL_CFG_STRUCT_TGT_RECT_TB 0x22C14
+
+// VIC_FC_CFG_STRUCT_SLOT_CFG2 & VIC_BL_CFG_STRUCT_CFG0.
+#define BLK_KIND(n) ((n) << 8)
+#define BLK_KIND_PITCH 0
+#define BLK_KIND_GENERIC_16BX2 1
+#define BLK_HEIGHT(n) ((n) << 12)
+#define BLK_HEIGHT_ONE_GOB 0
+#define BLK_HEIGHT_SIXTEEN_GOBS 4
+
+// Generic size macros.
+#define SIZE_WIDTH(n) (((n) - 1) << 0)
+#define SIZE_HEIGHT(n) (((n) - 1) << 16)
+#define RECT_LEFT(n) ((n) << 0)
+#define RECT_RIGHT(n) (((n) - 1) << 16)
+#define RECT_TOP(n) ((n) << 0)
+#define RECT_BOTTOM(n) (((n) - 1) << 16)
+
+#define FORMAT_PROGRESSIVE 0
+#define SOFT_CLAMP_MIN 0
+#define SOFT_CLAMP_MAX 0x3FFu
+#define ALPHA_1_0 0x3FFu
+
+typedef struct _OutputConfig {
+ u64 AlphaFillMode:3;
+ u64 AlphaFillSlot:3;
+ u64 BackgroundAlpha:10;
+ u64 BackgroundR:10;
+ u64 BackgroundG:10;
+ u64 BackgroundB:10;
+ u64 RegammaMode:2;
+ u64 OutputFlipX:1;
+ u64 OutputFlipY:1;
+ u64 OutputTranspose:1;
+ u64 rsvd1:1;
+ u64 rsvd2:12;
+ u64 TargetRectLeft:14;
+ u64 rsvd3:2;
+ u64 TargetRectRight:14;
+ u64 rsvd4:2;
+ u64 TargetRectTop:14;
+ u64 rsvd5:2;
+ u64 TargetRectBottom:14;
+ u64 rsvd6:2;
+} OutputConfig;
+
+typedef struct _OutputSurfaceConfig {
+ u64 OutPixelFormat:7;
+ u64 OutChromaLocHoriz:2;
+ u64 OutChromaLocVert:2;
+ u64 OutBlkKind:4;
+ u64 OutBlkHeight:4;
+ u64 rsvd0:3;
+ u64 rsvd1:10;
+ u64 OutSurfaceWidth:14;
+ u64 OutSurfaceHeight:14;
+ u64 rsvd2:4;
+ u64 OutLumaWidth:14;
+ u64 OutLumaHeight:14;
+ u64 rsvd3:4;
+ u64 OutChromaWidth:14;
+ u64 OutChromaHeight:14;
+ u64 rsvd4:4;
+} OutputSurfaceConfig;
+
+typedef struct _SlotConfig {
+ u64 SlotEnable:1;
+ u64 DeNoise:1;
+ u64 AdvancedDenoise:1;
+ u64 CadenceDetect:1;
+ u64 MotionMap:1;
+ u64 MMapCombine:1;
+ u64 IsEven:1;
+ u64 ChromaEven:1;
+ u64 CurrentFieldEnable:1;
+ u64 PrevFieldEnable:1;
+ u64 NextFieldEnable:1;
+ u64 NextNrFieldEnable:1;
+ u64 CurMotionFieldEnable:1;
+ u64 PrevMotionFieldEnable:1;
+ u64 PpMotionFieldEnable:1;
+ u64 CombMotionFieldEnable:1;
+ u64 FrameFormat:4;
+ u64 FilterLengthY:2;
+ u64 FilterLengthX:2;
+ u64 Panoramic:12;
+ u64 rsvd1:22;
+ u64 DetailFltClamp:6;
+ u64 FilterNoise:10;
+ u64 FilterDetail:10;
+ u64 ChromaNoise:10;
+ u64 ChromaDetail:10;
+ u64 DeinterlaceMode:4;
+ u64 MotionAccumWeight:3;
+ u64 NoiseIir:11;
+ u64 LightLevel:4;
+ u64 rsvd4:2;
+ u64 SoftClampLow:10;
+ u64 SoftClampHigh:10;
+ u64 rsvd5:3;
+ u64 rsvd6:9;
+ u64 PlanarAlpha:10;
+ u64 ConstantAlpha:1;
+ u64 StereoInterleave:3;
+ u64 ClipEnabled:1;
+ u64 ClearRectMask:8;
+ u64 DegammaMode:2;
+ u64 rsvd7:1;
+ u64 DecompressEnable:1;
+ u64 rsvd9:5;
+ u64 DecompressCtbCount:8;
+ u64 DecompressZbcColor:32;
+ u64 rsvd12:24;
+ u64 SourceRectLeft:30;
+ u64 rsvd14:2;
+ u64 SourceRectRight:30;
+ u64 rsvd15:2;
+ u64 SourceRectTop:30;
+ u64 rsvd16:2;
+ u64 SourceRectBottom:30;
+ u64 rsvd17:2;
+ u64 DestRectLeft:14;
+ u64 rsvd18:2;
+ u64 DestRectRight:14;
+ u64 rsvd19:2;
+ u64 DestRectTop:14;
+ u64 rsvd20:2;
+ u64 DestRectBottom:14;
+ u64 rsvd21:2;
+ u64 rsvd22:32;
+ u64 rsvd23:32;
+} SlotConfig;
+
+typedef struct _SlotSurfaceConfig {
+ u64 SlotPixelFormat:7;
+ u64 SlotChromaLocHoriz:2;
+ u64 SlotChromaLocVert:2;
+ u64 SlotBlkKind:4;
+ u64 SlotBlkHeight:4;
+ u64 SlotCacheWidth:3;
+ u64 rsvd0:10;
+ u64 SlotSurfaceWidth:14;
+ u64 SlotSurfaceHeight:14;
+ u64 rsvd1:4;
+ u64 SlotLumaWidth:14;
+ u64 SlotLumaHeight:14;
+ u64 rsvd2:4;
+ u64 SlotChromaWidth:14;
+ u64 SlotChromaHeight:14;
+ u64 rsvd3:4;
+} SlotSurfaceConfig;
+
+typedef struct _SlotStruct {
+ SlotConfig slot_cfg;
+ SlotSurfaceConfig slot_sfc_cfg;
+
+ // No need to configure. Reset to zeros.
+ u8 lumaKeyStruct[0x10];
+ u8 colorMatrixStruct[0x20];
+ u8 gamutMatrixStruct[0x20];
+ u8 blendingSlotStruct[0x10];
+} SlotStruct;
+
+typedef struct _vic_config_t {
+ // No need to configure. Reset to zeros.
+ u8 pipeConfig[0x10];
+
+ OutputConfig out_cfg;
+ OutputSurfaceConfig out_sfc_cfg;
+
+ // No need to configure. Reset to zeros.
+ u8 out_color_matrix[0x20];
+ u8 clear_rect[0x10 * 4];
+
+ SlotStruct slots[8];
+} vic_config_t;
+
+// VIC Fetch Control Engine microcode. Dumped from L4T r33.
+u8 vic_fce_ucode[] = {
+ 0x66, 0x00, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x42, 0x40, 0x10, 0x00, 0x4E, 0x01, 0x40, 0x00,
+ 0x6A, 0x07, 0x00, 0x00, 0x6E, 0x23, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x4E, 0x01, 0x04, 0x00,
+ 0x6A, 0x0B, 0x00, 0x00, 0x6E, 0x1F, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x4E, 0x01, 0x10, 0x00,
+ 0x6A, 0x0F, 0x00, 0x00, 0x6E, 0x1F, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x48, 0x80, 0x02, 0x00,
+ 0x0E, 0x11, 0x00, 0x00, 0x6A, 0x14, 0x00, 0x00, 0x6E, 0x08, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00,
+ 0x4E, 0x01, 0x08, 0x00, 0x6A, 0x18, 0x00, 0x00, 0x6E, 0x26, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00,
+ 0x4E, 0x01, 0x20, 0x00, 0x6A, 0x1C, 0x00, 0x00, 0x6E, 0x26, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00,
+ 0x4E, 0x01, 0x02, 0x00, 0x6A, 0x20, 0x00, 0x00, 0x6E, 0x24, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x10, 0x00, 0x56, 0x40, 0x10, 0x00, 0x22, 0x41, 0x01, 0x00, 0x6C, 0x00, 0x00, 0x00,
+ 0x62, 0x80, 0x01, 0x00, 0x60, 0x47, 0x00, 0x00, 0x60, 0x87, 0x00, 0x00, 0x01, 0x4A, 0x00, 0x00,
+ 0x55, 0xC0, 0x20, 0x00, 0x00, 0x59, 0x00, 0x00, 0x60, 0x87, 0x00, 0x00, 0x60, 0xC7, 0x00, 0x00,
+ 0x01, 0x93, 0x00, 0x00, 0x40, 0x82, 0x02, 0x00, 0x4E, 0x02, 0x00, 0x00, 0x6B, 0x34, 0x00, 0x00,
+ 0x43, 0xC1, 0x10, 0x00, 0x42, 0x02, 0x03, 0x00, 0x00, 0x23, 0x01, 0x00, 0x24, 0xD4, 0x00, 0x00,
+ 0x56, 0x40, 0x3D, 0x00, 0x04, 0xEB, 0x00, 0x00, 0x60, 0x07, 0x01, 0x00, 0x60, 0x47, 0x00, 0x00,
+ 0x6A, 0x3E, 0x00, 0x00, 0x55, 0xC0, 0x30, 0x00, 0x48, 0x00, 0x01, 0x00, 0x48, 0x40, 0x01, 0x00,
+ 0x48, 0x80, 0x01, 0x00, 0x6B, 0x28, 0x02, 0x00, 0x56, 0x40, 0x09, 0x00, 0x04, 0x4D, 0x01, 0x00,
+ 0x06, 0x4D, 0x00, 0x00, 0x42, 0xC0, 0x03, 0x00, 0x56, 0x80, 0x09, 0x00, 0x04, 0xFE, 0x01, 0x00,
+ 0x00, 0xF9, 0x01, 0x00, 0x4E, 0x02, 0x00, 0x00, 0x6B, 0x32, 0x02, 0x00, 0x55, 0x40, 0x2F, 0x00,
+ 0x56, 0x80, 0x0D, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x6A, 0x0D, 0x02, 0x00, 0x55, 0x40, 0x31, 0x00,
+ 0x56, 0x80, 0x0B, 0x00, 0x0C, 0x2B, 0x00, 0x00, 0x6A, 0x13, 0x02, 0x00, 0x43, 0x45, 0x03, 0x00,
+ 0x42, 0x86, 0x03, 0x00, 0x4D, 0x06, 0x02, 0x00, 0x6A, 0x0D, 0x02, 0x00, 0x42, 0x86, 0x03, 0x00,
+ 0x22, 0x7E, 0x01, 0x00, 0x4E, 0x04, 0x00, 0x00, 0x6B, 0x32, 0x02, 0x00, 0x55, 0x40, 0x17, 0x00,
+ 0x0D, 0x2C, 0x00, 0x00, 0x56, 0xC0, 0x09, 0x00, 0x6A, 0x1E, 0x02, 0x00, 0x48, 0xC0, 0x01, 0x00,
+ 0x43, 0x04, 0x03, 0x00, 0x6C, 0x20, 0x02, 0x00, 0x55, 0x40, 0x19, 0x00, 0x01, 0x2C, 0x01, 0x00,
+ 0x65, 0x23, 0x01, 0x00, 0x42, 0x42, 0x03, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x24, 0x14, 0x01, 0x00,
+ 0x00, 0x2C, 0x01, 0x00, 0x24, 0x14, 0x01, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x42, 0x04, 0x09, 0x00,
+ 0x42, 0xC3, 0x02, 0x00, 0x65, 0x54, 0x01, 0x00, 0x65, 0x55, 0x01, 0x00, 0x42, 0x45, 0x0D, 0x00,
+ 0x62, 0x03, 0x00, 0x00, 0x62, 0x44, 0x00, 0x00, 0x62, 0x85, 0x00, 0x00, 0x62, 0xC2, 0x00, 0x00,
+ 0x22, 0x48, 0x1F, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x01, 0x00, 0x6C, 0x28, 0x02, 0x00,
+ 0x62, 0x80, 0x01, 0x00, 0x60, 0x07, 0x00, 0x00, 0x60, 0x47, 0x00, 0x00, 0x60, 0x87, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x43, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x01, 0xCA, 0x01, 0x00,
+ 0x60, 0x03, 0x01, 0x00, 0x01, 0xA0, 0x01, 0x00, 0x60, 0x40, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00,
+ 0x55, 0xC0, 0x2E, 0x00, 0x01, 0x18, 0x00, 0x00, 0x43, 0x00, 0x04, 0x00, 0x43, 0x41, 0x06, 0x00,
+ 0x6F, 0x00, 0x00, 0x00, 0x61, 0xC1, 0x00, 0x00, 0x61, 0x42, 0x01, 0x00, 0x65, 0xB5, 0x00, 0x00,
+ 0x65, 0x73, 0x01, 0x00, 0x65, 0x35, 0x01, 0x00, 0x65, 0x34, 0x01, 0x00, 0x42, 0x04, 0x0D, 0x00,
+ 0x01, 0x14, 0x01, 0x00, 0x42, 0x04, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x43, 0x03, 0x05, 0x00,
+ 0x43, 0x85, 0x02, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x48, 0x46, 0x01, 0x00, 0x65, 0xEB, 0x00, 0x00,
+ 0x00, 0x9A, 0x00, 0x00, 0x65, 0xB2, 0x01, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x42, 0x86, 0x0D, 0x00,
+ 0x61, 0x42, 0x01, 0x00, 0x01, 0xAE, 0x01, 0x00, 0x00, 0x71, 0x00, 0x00, 0x42, 0x82, 0x08, 0x00,
+ 0x42, 0xC3, 0x08, 0x00, 0x48, 0x40, 0x01, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x34, 0x02, 0x00,
+ 0x65, 0x79, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x6C, 0x36, 0x04, 0x00, 0x6E, 0x34, 0x02, 0x00,
+ 0x48, 0x7F, 0x01, 0x00, 0x6C, 0x0A, 0x06, 0x00, 0x6E, 0x34, 0x02, 0x00, 0x6E, 0x05, 0x04, 0x00,
+ 0x65, 0x79, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x41, 0x87, 0x03, 0x00, 0x65, 0xBA, 0x00, 0x00,
+ 0x65, 0xB2, 0x00, 0x00, 0x42, 0x82, 0x02, 0x00, 0x00, 0x51, 0x00, 0x00, 0x61, 0xC1, 0x00, 0x00,
+ 0x65, 0xFB, 0x00, 0x00, 0x65, 0xF3, 0x00, 0x00, 0x41, 0x87, 0x05, 0x00, 0x65, 0xF3, 0x00, 0x00,
+ 0x42, 0xC3, 0x08, 0x00, 0x00, 0x59, 0x00, 0x00, 0x60, 0xC7, 0x00, 0x00, 0x60, 0xC7, 0x00, 0x00,
+ 0x56, 0xC0, 0x21, 0x00, 0x04, 0xDF, 0x01, 0x00, 0x43, 0xC7, 0x15, 0x00, 0x00, 0x38, 0x00, 0x00,
+ 0x00, 0x79, 0x00, 0x00, 0x42, 0xC3, 0x20, 0x00, 0x43, 0xC3, 0x04, 0x00, 0x42, 0x00, 0x30, 0x00,
+ 0x42, 0x41, 0x30, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x60, 0xC7, 0x01, 0x00,
+ 0x22, 0x78, 0x01, 0x00, 0x22, 0x79, 0x03, 0x00, 0x22, 0x7F, 0x1F, 0x00, 0x6F, 0x00, 0x00, 0x00,
+ 0x6E, 0x34, 0x02, 0x00, 0x6E, 0x05, 0x04, 0x00, 0x4B, 0x41, 0x00, 0x00, 0x60, 0xC7, 0x01, 0x00,
+ 0x60, 0x87, 0x01, 0x00, 0x43, 0x86, 0x15, 0x00, 0x00, 0x30, 0x00, 0x00, 0x65, 0x39, 0x01, 0x00,
+ 0x42, 0x04, 0x05, 0x00, 0x4E, 0x05, 0x7E, 0x00, 0x6A, 0x1B, 0x06, 0x00, 0x55, 0xC0, 0x3D, 0x00,
+ 0x0A, 0x3C, 0x01, 0x00, 0x60, 0xC7, 0x01, 0x00, 0x22, 0x78, 0x01, 0x00, 0x22, 0x79, 0x03, 0x00,
+ 0x22, 0x7C, 0x09, 0x00, 0x22, 0x7F, 0x1F, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x65, 0x7A, 0x01, 0x00,
+ 0x42, 0x45, 0x05, 0x00, 0x65, 0xBB, 0x01, 0x00, 0x42, 0x86, 0x05, 0x00, 0x55, 0xC0, 0x3D, 0x00,
+ 0x0A, 0x7D, 0x01, 0x00, 0x0A, 0xBE, 0x01, 0x00, 0x07, 0xC7, 0x01, 0x00, 0x0B, 0x7D, 0x01, 0x00,
+ 0x0B, 0xBE, 0x01, 0x00, 0x55, 0xC0, 0x3D, 0x00, 0x0A, 0x3C, 0x01, 0x00, 0x60, 0xC7, 0x01, 0x00,
+ 0x22, 0x78, 0x01, 0x00, 0x22, 0x79, 0x03, 0x00, 0x22, 0x7A, 0x05, 0x00, 0x22, 0x7B, 0x07, 0x00,
+ 0x22, 0x7C, 0x09, 0x00, 0x22, 0x7D, 0x0B, 0x00, 0x22, 0x7E, 0x0D, 0x00, 0x22, 0x7F, 0x1F, 0x00,
+ 0x6F, 0x00, 0x00, 0x00
+};
+
+vic_config_t __attribute__((aligned (0x100))) vic_cfg = {0};
+
+u32 _vic_read_priv(u32 addr)
+{
+ u32 addr_lsb = addr & 0xFF;
+
+ // Set address LSB.
+ if (addr_lsb)
+ VIC(PVIC_FALCON_ADDR) = addr_lsb >> 2;
+
+ // Set address.
+ u32 val = VIC(PVIC_FALCON_PA_OFFSET + (addr >> 6));
+
+ // Unset address LSB.
+ if (addr_lsb)
+ VIC(PVIC_FALCON_ADDR) = 0;
+
+ return val;
+}
+
+static void _vic_write_priv(u32 addr, u32 data)
+{
+ u32 addr_lsb = addr & 0xFF;
+
+ // Set address LSB.
+ if (addr_lsb)
+ VIC(PVIC_FALCON_ADDR) = addr_lsb >> 2;
+
+ // Set address.
+ VIC(PVIC_FALCON_PA_OFFSET + (addr >> 6)) = data;
+
+ // Unset address LSB.
+ if (addr_lsb)
+ VIC(PVIC_FALCON_ADDR) = 0;
+}
+
+int vic_wait_idle()
+{
+ u32 timeout_count = 15000; // 150ms.
+
+ while (VIC(PVIC_FALCON_IDLESTATE))
+ {
+ usleep(10);
+
+ timeout_count--;
+ if (!timeout_count)
+ return 1;
+ };
+
+ return 0;
+}
+
+void vic_set_surface(const vic_surface_t *sfc)
+{
+ u32 flip_x = 0;
+ u32 flip_y = 0;
+ u32 swap_xy = 0;
+ u32 const_alpha = 0;
+
+ u32 width = sfc->width;
+ u32 height = sfc->height;
+ u32 pix_fmt = sfc->pix_fmt;
+ u32 src_buf = sfc->src_buf;
+ u32 dst_buf = sfc->dst_buf;
+
+ // Get format alpha type.
+ switch (sfc->pix_fmt)
+ {
+ case VIC_PIX_FORMAT_L8:
+ case VIC_PIX_FORMAT_X1B5G5R5:
+ case VIC_PIX_FORMAT_B5G5R5X1:
+ case VIC_PIX_FORMAT_X8B8G8R8:
+ case VIC_PIX_FORMAT_X8R8G8B8:
+ case VIC_PIX_FORMAT_B8G8R8X8:
+ case VIC_PIX_FORMAT_R8G8B8X8:
+ const_alpha = 1;
+ break;
+
+ case VIC_PIX_FORMAT_A8B8G8R8:
+ case VIC_PIX_FORMAT_A8R8G8B8:
+ case VIC_PIX_FORMAT_B8G8R8A8:
+ case VIC_PIX_FORMAT_R8G8B8A8:
+ default:
+ break;
+ }
+
+ // Get rotation parameters.
+ switch (sfc->rotation)
+ {
+ case VIC_ROTATION_90:
+ swap_xy = 1;
+ break;
+
+ case VIC_ROTATION_180:
+ flip_x = 1;
+ flip_y = 1;
+ break;
+
+ case VIC_ROTATION_270:
+ flip_x = 1;
+ swap_xy = 1;
+ break;
+
+ case VIC_ROTATION_0:
+ default:
+ break;
+ }
+
+ // Set output surface format.
+ vic_cfg.out_sfc_cfg.OutPixelFormat = pix_fmt;
+ vic_cfg.out_sfc_cfg.OutBlkKind = BLK_KIND_PITCH;
+ vic_cfg.out_sfc_cfg.OutBlkHeight = 0;
+
+ // Set output rotation/flip.
+ vic_cfg.out_cfg.OutputFlipX = flip_x;
+ vic_cfg.out_cfg.OutputFlipY = flip_y;
+ vic_cfg.out_cfg.OutputTranspose = swap_xy;
+
+ // Set output surface resolution.
+ vic_cfg.out_sfc_cfg.OutSurfaceWidth = width - 1;
+ vic_cfg.out_sfc_cfg.OutSurfaceHeight = height - 1;
+ vic_cfg.out_sfc_cfg.OutLumaWidth = width - 1;
+ vic_cfg.out_sfc_cfg.OutLumaHeight = height - 1;
+
+ // Set output destination rectangle. Anything outside will not be touched at output buffer.
+ vic_cfg.out_cfg.TargetRectLeft = 0;
+ vic_cfg.out_cfg.TargetRectRight = width - 1;
+ vic_cfg.out_cfg.TargetRectTop = 0;
+ vic_cfg.out_cfg.TargetRectBottom = height - 1;
+
+ // Initialize slot parameters.
+ vic_cfg.slots[0].slot_cfg.SlotEnable = 1;
+ vic_cfg.slots[0].slot_cfg.SoftClampLow = SOFT_CLAMP_MIN;
+ vic_cfg.slots[0].slot_cfg.SoftClampHigh = SOFT_CLAMP_MAX;
+ vic_cfg.slots[0].slot_cfg.PlanarAlpha = ALPHA_1_0;
+ vic_cfg.slots[0].slot_cfg.ConstantAlpha = const_alpha;
+ vic_cfg.slots[0].slot_cfg.FrameFormat = FORMAT_PROGRESSIVE;
+
+ // Set input source rectangle.
+ vic_cfg.slots[0].slot_cfg.SourceRectLeft = 0;
+ vic_cfg.slots[0].slot_cfg.SourceRectRight = (width - 1) << 16;
+ vic_cfg.slots[0].slot_cfg.SourceRectTop = 0;
+ vic_cfg.slots[0].slot_cfg.SourceRectBottom = (height - 1) << 16;
+
+ // Set input destination rectangle.
+ vic_cfg.slots[0].slot_cfg.DestRectLeft = 0;
+ vic_cfg.slots[0].slot_cfg.DestRectRight = (width - 1);
+ vic_cfg.slots[0].slot_cfg.DestRectTop = 0;
+ vic_cfg.slots[0].slot_cfg.DestRectBottom = (height - 1);
+
+ // Set input surface format.
+ vic_cfg.slots[0].slot_sfc_cfg.SlotPixelFormat = pix_fmt;
+ vic_cfg.slots[0].slot_sfc_cfg.SlotBlkKind = BLK_KIND_PITCH;
+ vic_cfg.slots[0].slot_sfc_cfg.SlotBlkHeight = 0;
+ vic_cfg.slots[0].slot_sfc_cfg.SlotCacheWidth = CACHE_WIDTH_64BX4;
+
+ // Set input surface resolution.
+ vic_cfg.slots[0].slot_sfc_cfg.SlotSurfaceWidth = width - 1;
+ vic_cfg.slots[0].slot_sfc_cfg.SlotSurfaceHeight = height - 1;
+ vic_cfg.slots[0].slot_sfc_cfg.SlotLumaWidth = width - 1;
+ vic_cfg.slots[0].slot_sfc_cfg.SlotLumaHeight = height - 1;
+
+ // Flush data.
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLEAN_WAY, false);
+
+ // Set parameters base and size. Causes a parse by surface cache.
+ _vic_write_priv(VIC_SC_PRAMBASE, (u32)&vic_cfg >> 8);
+ _vic_write_priv(VIC_SC_PRAMSIZE, sizeof(vic_config_t) >> 6);
+
+ // Wait for surface cache to get ready.
+ vic_wait_idle();
+
+ // Set slot mapping.
+ _vic_write_priv(VIC_FC_SLOT_MAP, 0xFFFFFFF0);
+
+ // Set input surface buffer.
+ _vic_write_priv(VIC_SC_SFC0_BASE_LUMA(0), src_buf >> 8);
+
+ // Set output surface buffer.
+ _vic_write_priv(VIC_BL_TARGET_BASADR, dst_buf >> 8);
+
+ // Set blending config and push changes to surface cache.
+ _vic_write_priv(VIC_BL_CONFIG, SLOTMASK(0x1F) | PROCESS_CFG_STRUCT_TRIGGER | SUBPARTITION_MODE);
+
+ // Wait for surface cache to get ready.
+ vic_wait_idle();
+}
+
+int vic_compose()
+{
+ // Wait for surface cache to get ready. Otherwise VIC will hang.
+ int res = vic_wait_idle();
+
+ // Start composition of a single frame.
+ _vic_write_priv(VIC_FC_COMPOSE, COMPOSE_START);
+
+ return res;
+}
+
+int vic_init()
+{
+ clock_enable_vic();
+
+ // Load Fetch Control Engine microcode.
+ for (u32 i = 0; i < sizeof(vic_fce_ucode) / sizeof(u32); i++)
+ {
+ _vic_write_priv(VIC_FC_FCE_UCODE_ADDR, (i * sizeof(u32)));
+ _vic_write_priv(VIC_FC_FCE_UCODE_INST, *(u32 *)&vic_fce_ucode[i * sizeof(u32)]);
+ }
+
+ // Start Fetch Control Engine.
+ _vic_write_priv(VIC_FC_FCE_CTRL, START_TRIGGER);
+
+ return vic_wait_idle();
+}
+
+void vic_end()
+{
+ clock_disable_vic();
+}
diff --git a/bdk/display/vic.h b/bdk/display/vic.h
new file mode 100644
index 0000000..23de631
--- /dev/null
+++ b/bdk/display/vic.h
@@ -0,0 +1,67 @@
+/*
+ * VIC driver for Tegra X1
+ *
+ * Copyright (c) 2018-2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _VIC_H_
+#define _VIC_H_
+
+#include
+
+#define VIC_THI_SLCG_OVERRIDE_LOW_A 0x8C
+
+typedef enum _vic_rotation_t
+{
+ VIC_ROTATION_0 = 0,
+ VIC_ROTATION_90 = 1,
+ VIC_ROTATION_180 = 2,
+ VIC_ROTATION_270 = 3,
+} vic_rotation_t;
+
+typedef enum _vic_pix_format_t
+{
+ VIC_PIX_FORMAT_L8 = 1, // 8-bit LUT.
+ VIC_PIX_FORMAT_X1B5G5R5 = 21, // 16-bit XBGR.
+ VIC_PIX_FORMAT_B5G5R5X1 = 23, // 16-bit BGRX.
+
+ VIC_PIX_FORMAT_A8B8G8R8 = 31, // 32-bit ABGR.
+ VIC_PIX_FORMAT_A8R8G8B8 = 32, // 32-bit ARGB.
+ VIC_PIX_FORMAT_B8G8R8A8 = 33, // 32-bit BGRA.
+ VIC_PIX_FORMAT_R8G8B8A8 = 34, // 32-bit RGBA.
+
+ VIC_PIX_FORMAT_X8B8G8R8 = 35, // 32-bit XBGR.
+ VIC_PIX_FORMAT_X8R8G8B8 = 36, // 32-bit XRGB.
+ VIC_PIX_FORMAT_B8G8R8X8 = 37, // 32-bit BGRX.
+ VIC_PIX_FORMAT_R8G8B8X8 = 38, // 32-bit RGBX.
+} vic_pix_format_t;
+
+typedef struct _vic_surface_t
+{
+ u32 src_buf;
+ u32 dst_buf;
+ u32 width;
+ u32 height;
+ u32 pix_fmt;
+ u32 rotation;
+} vic_surface_t;
+
+void vic_set_surface(const vic_surface_t *sfc);
+int vic_wait_idle();
+int vic_compose();
+int vic_init();
+void vic_end();
+
+#endif
diff --git a/bdk/exception_handlers.S b/bdk/exception_handlers.S
index 6f21f80..50acb91 100644
--- a/bdk/exception_handlers.S
+++ b/bdk/exception_handlers.S
@@ -1,233 +1,239 @@
-/*
- * Copyright (c) 2019 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-/*
- * Armv7tdmi Status register.
- *
- * bit0: Mode 0.
- * bit1: Mode 1.
- * bit2: Mode 2.
- * bit3: Mode 3.
- * bit4: Mode 4.
- * bit5: Thumb state.
- * bit6: FIQ disable.
- * bit7: IRQ disable.
- * bit8-27: Reserved.
- * bit28: Overflow condition.
- * bit29: Carry/Borrow/Extend condition.
- * bit30: Zero condition.
- * bit31: Negative/Less than condition.
- *
- * M[4:0] | Mode | Visible Thumb-state registers | Visible ARM-state registers
- * 10000 | USER | r0–r7, SP, LR, PC, CPSR | r0–r14, PC, CPSR
- * 10001 | FIQ | r0–r7, SP_fiq, LR_fiq, PC, CPSR, SPSR_fiq | r0–r7, r8_fiq–r14_fiq, PC, CPSR, SPSR_fiq
- * 10010 | IRQ | r0–r7, SP_irq, LR_irq, PC, CPSR, SPSR_irq | r0–r12, r13_irq, r14_irq, PC, CPSR, SPSR_irq
- * 10011 | SVC | r0–r7, SP_svc, LR_svc, PC, CPSR, SPSR_svc | r0–r12, r13_svc, r14_svc, PC, CPSR, SPSR_svc
- * 10111 | ABRT | r0–r7, SP_abt, LR_abt, PC, CPSR, SPSR_abt | r0–r12, r13_abt, r14_abt, PC, CPSR, SPSR_abt
- * 11011 | UNDF | r0–r7, SP_und, LR_und, PC, CPSR, SPSR_und | r0–r12, r13_und, r14_und, PC, CPSR, SPSR_und
- * 11111 | SYS | r0–r7, SP, LR, PC, CPSR | r0–r14, PC, CPSR
- */
-
-#define EXCP_EN_ADDR 0x4003FFFC
-#define EXCP_TYPE_ADDR 0x4003FFF8
-#define EXCP_LR_ADDR 0x4003FFF4
-
-#define EXCP_VEC_BASE 0x6000F000
-#define EVP_COP_RESET_VECTOR 0x200
-#define EVP_COP_UNDEF_VECTOR 0x204
-#define EVP_COP_SWI_VECTOR 0x208
-#define EVP_COP_PREFETCH_ABORT_VECTOR 0x20C
-#define EVP_COP_DATA_ABORT_VECTOR 0x210
-#define EVP_COP_RSVD_VECTOR 0x214
-#define EVP_COP_IRQ_VECTOR 0x218
-#define EVP_COP_FIQ_VECTOR 0x21C
-
-#define MODE_USR 0x10
-#define MODE_FIQ 0x11
-#define MODE_IRQ 0x12
-#define MODE_SVC 0x13
-#define MODE_ABT 0x17
-#define MODE_UDF 0x1B
-#define MODE_SYS 0x1F
-#define MODE_MASK 0x1F
-
-#define FIQ 0x40
-#define IRQ 0x80
-
-.section .text._irq_setup
-.arm
-
-.extern ipl_main
-.type ipl_main, %function
-
-.extern svc_handler
-.type svc_handler, %function
-
-.extern irq_handler
-.type irq_handler, %function
-
-.extern fiq_setup
-.type fiq_setup, %function
-
-.extern fiq_handler
-.type fiq_handler, %function
-
-.globl _irq_setup
-.type _irq_setup, %function
-_irq_setup:
- MRS R0, CPSR
- BIC R0, R0, #MODE_MASK /* Clear mode bits */
- ORR R0, R0, #(MODE_SVC | IRQ | FIQ) /* SUPERVISOR mode, IRQ/FIQ disabled */
- MSR CPSR, R0
-
- /* Setup IRQ stack pointer */
- MSR CPSR, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
- LDR SP, =0x40040000
-
- /* Setup SYS stack pointer */
- MSR CPSR, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
- LDR SP, =0x4003FF00 /* Will be changed later to DRAM */
-
- MOV LR, PC
- BL setup_vectors
- /*BL fiq_setup*/
-
- /* Enable interrupts */
- BL irq_enable_cpu_irq_exceptions
-
- B ipl_main
- B .
-
-_reset:
- LDR R0, =EXCP_EN_ADDR
- LDR R1, =0x30505645 /* EVP0 */
- STR R1, [R0] /* EVP0 in EXCP_EN_ADDR */
- LDR R0, =EXCP_LR_ADDR
- MOV R1, LR
- STR R1, [R0] /* Save LR in EXCP_LR_ADDR */
- LDR R0, =__bss_start
- EOR R1, R1, R1
- LDR R2, =__bss_end
- SUB R2, R2, R0
- BL memset
- B _irq_setup
-
-_reset_handler:
- LDR R0, =EXCP_TYPE_ADDR
- LDR R1, =0x545352 /* RST */
- STR R1, [R0] /* RST in EXCP_TYPE_ADDR */
- B _reset
-
-_undefined_handler:
- LDR R0, =EXCP_TYPE_ADDR
- LDR R1, =0x464455 /* UDF */
- STR R1, [R0] /* UDF in EXCP_TYPE_ADDR */
- B _reset
-
-_prefetch_abort_handler:
- LDR R0, =EXCP_TYPE_ADDR
- LDR R1, =0x54424150 /* PABT */
- STR R1, [R0] /* PABT in EXCP_TYPE_ADDR */
- B _reset
-
-_data_abort_handler:
- LDR R0, =EXCP_TYPE_ADDR
- LDR R1, =0x54424144 /* DABT */
- STR R1, [R0] /* DABT in EXCP_TYPE_ADDR */
- B _reset
-
-.globl irq_enable_cpu_irq_exceptions
-.type irq_enable_cpu_irq_exceptions, %function
-irq_enable_cpu_irq_exceptions:
- MRS R12, CPSR
- BIC R12, R12, #(IRQ | FIQ) /* IRQ/FIQ enabled */
- MSR CPSR, R12
- BX LR
-
-.globl irq_disable_cpu_irq_exceptions
-.type irq_disable_cpu_irq_exceptions, %function
-irq_disable_cpu_irq_exceptions:
- MRS R12, CPSR
- ORR R12, R12, #(IRQ | FIQ) /* IRQ/FIQ disabled */
- MSR CPSR, R12
- BX LR
-
-_irq_handler:
- MOV R13, R0 /* Save R0 in R13_IRQ */
- SUB R0, LR, #4 /* Put return address in R0_SYS */
- MOV LR, R1 /* Save R1 in R14_IRQ (LR) */
- MRS R1, SPSR /* Put the SPSR in R1_SYS */
-
- MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
- STMFD SP!, {R0, R1} /* SPSR and PC */
- STMFD SP!, {R2-R3, R12, LR} /* AAPCS-clobbered registers */
- MOV R0, SP /* Make SP_SYS visible to IRQ mode */
- SUB SP, SP, #8 /* Make room for stacking R0 and R1 */
-
- MSR CPSR_c, #(MODE_IRQ | IRQ) /* IRQ mode, IRQ disabled */
- STMFD R0!, {R13, R14} /* Finish saving the context (R0, R1) */
-
- MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
- LDR R12, =irq_handler
- MOV LR, PC /* Copy the return address to link register */
- BX R12 /* Call the C IRQ handler (ARM/THUMB) */
-
- MSR CPSR_c, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
- MOV R0, SP /* Make SP_SYS visible to IRQ mode */
- ADD SP, SP, #32 /* Fake unstacking 8 registers from SP_SYS */
-
- MSR CPSR_c, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
- MOV SP, R0 /* Copy SP_SYS to SP_IRQ */
- LDR R0, [SP, #28] /* Load the saved SPSR from the stack */
- MSR SPSR_cxsf, R0 /* Copy it into SPSR_IRQ */
-
- LDMFD SP, {R0-R3, R12, LR}^ /* Unstack all saved USER/SYSTEM registers */
- NOP /* Cant access barked registers immediately */
- LDR LR, [SP, #24] /* Load return address from the SYS stack */
- MOVS PC, LR /* Return restoring CPSR from SPSR */
-
-_fiq_handler:
- BL fiq_handler
-
-setup_vectors:
- /* Setup vectors */
- LDR R0, =EXCP_VEC_BASE
-
- LDR R1, =_reset_handler
- STR R1, [R0, #EVP_COP_RESET_VECTOR]
-
- LDR R1, =_undefined_handler
- STR R1, [R0, #EVP_COP_UNDEF_VECTOR]
-
- LDR R1, =_reset_handler
- STR R1, [R0, #EVP_COP_SWI_VECTOR]
-
- LDR R1, =_prefetch_abort_handler
- STR R1, [R0, #EVP_COP_PREFETCH_ABORT_VECTOR]
-
- LDR R1, =_data_abort_handler
- STR R1, [R0, #EVP_COP_DATA_ABORT_VECTOR]
-
- LDR R1, =_reset_handler
- STR R1, [R0, #EVP_COP_RSVD_VECTOR]
-
- LDR R1, =_irq_handler
- STR R1, [R0, #EVP_COP_IRQ_VECTOR]
-
- LDR R1, =_fiq_handler
- STR R1, [R0, #EVP_COP_FIQ_VECTOR]
-
- BX LR
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/*
+ * Armv7tdmi Status register.
+ *
+ * bit0: Mode 0.
+ * bit1: Mode 1.
+ * bit2: Mode 2.
+ * bit3: Mode 3.
+ * bit4: Mode 4.
+ * bit5: Thumb state.
+ * bit6: FIQ disable.
+ * bit7: IRQ disable.
+ * bit8-27: Reserved.
+ * bit28: Overflow condition.
+ * bit29: Carry/Borrow/Extend condition.
+ * bit30: Zero condition.
+ * bit31: Negative/Less than condition.
+ *
+ * M[4:0] | Mode | Visible Thumb-state registers | Visible ARM-state registers
+ * 10000 | USER | r0–r7, SP, LR, PC, CPSR | r0–r14, PC, CPSR
+ * 10001 | FIQ | r0–r7, SP_fiq, LR_fiq, PC, CPSR, SPSR_fiq | r0–r7, r8_fiq–r14_fiq, PC, CPSR, SPSR_fiq
+ * 10010 | IRQ | r0–r7, SP_irq, LR_irq, PC, CPSR, SPSR_irq | r0–r12, r13_irq, r14_irq, PC, CPSR, SPSR_irq
+ * 10011 | SVC | r0–r7, SP_svc, LR_svc, PC, CPSR, SPSR_svc | r0–r12, r13_svc, r14_svc, PC, CPSR, SPSR_svc
+ * 10111 | ABRT | r0–r7, SP_abt, LR_abt, PC, CPSR, SPSR_abt | r0–r12, r13_abt, r14_abt, PC, CPSR, SPSR_abt
+ * 11011 | UNDF | r0–r7, SP_und, LR_und, PC, CPSR, SPSR_und | r0–r12, r13_und, r14_und, PC, CPSR, SPSR_und
+ * 11111 | SYS | r0–r7, SP, LR, PC, CPSR | r0–r14, PC, CPSR
+ */
+
+#define EXCP_EN_ADDR 0x4003FF1C
+#define EXCP_TYPE_ADDR 0x4003FF18
+#define EXCP_LR_ADDR 0x4003FF14
+
+#define EXCP_VEC_BASE 0x6000F000
+#define EVP_COP_RESET_VECTOR 0x200
+#define EVP_COP_UNDEF_VECTOR 0x204
+#define EVP_COP_SWI_VECTOR 0x208
+#define EVP_COP_PREFETCH_ABORT_VECTOR 0x20C
+#define EVP_COP_DATA_ABORT_VECTOR 0x210
+#define EVP_COP_RSVD_VECTOR 0x214
+#define EVP_COP_IRQ_VECTOR 0x218
+#define EVP_COP_FIQ_VECTOR 0x21C
+
+#define MODE_USR 0x10
+#define MODE_FIQ 0x11
+#define MODE_IRQ 0x12
+#define MODE_SVC 0x13
+#define MODE_ABT 0x17
+#define MODE_UDF 0x1B
+#define MODE_SYS 0x1F
+#define MODE_MASK 0x1F
+
+#define FIQ 0x40
+#define IRQ 0x80
+
+.section .text._irq_setup
+.arm
+
+.extern ipl_main
+.type ipl_main, %function
+
+.extern svc_handler
+.type svc_handler, %function
+
+.extern irq_handler
+.type irq_handler, %function
+
+.extern fiq_setup
+.type fiq_setup, %function
+
+.extern fiq_handler
+.type fiq_handler, %function
+
+.globl _irq_setup
+.type _irq_setup, %function
+_irq_setup:
+ MRS R0, CPSR
+ BIC R0, R0, #MODE_MASK /* Clear mode bits */
+ ORR R0, R0, #(MODE_SVC | IRQ | FIQ) /* SUPERVISOR mode, IRQ/FIQ disabled */
+ MSR CPSR, R0
+
+ /* Setup IRQ stack pointer */
+ MSR CPSR, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
+ LDR SP, =0x40040000
+
+ /* Setup FIQ stack pointer */
+ MSR CPSR, #(MODE_FIQ | IRQ | FIQ) /* FIQ mode, IRQ/FIQ disabled */
+ LDR SP, =0x40040000
+
+ /* Setup SYS stack pointer */
+ MSR CPSR, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
+ LDR SP, =0x4003FF00 /* Will be changed later to DRAM */
+
+ MOV LR, PC
+ BL setup_vectors
+ /*BL fiq_setup*/
+
+ /* Enable interrupts */
+ BL irq_enable_cpu_irq_exceptions
+
+ B ipl_main
+ B .
+
+.globl excp_reset
+.type excp_reset, %function
+excp_reset:
+ LDR R0, =EXCP_EN_ADDR
+ LDR R1, =0x30505645 /* EVP0 */
+ STR R1, [R0] /* EVP0 in EXCP_EN_ADDR */
+ LDR R0, =EXCP_LR_ADDR
+ MOV R1, LR
+ STR R1, [R0] /* Save LR in EXCP_LR_ADDR */
+ LDR R0, =__bss_start
+ EOR R1, R1, R1
+ LDR R2, =__bss_end
+ SUB R2, R2, R0
+ BL memset
+ B _irq_setup
+
+_reset_handler:
+ LDR R0, =EXCP_TYPE_ADDR
+ LDR R1, =0x545352 /* RST */
+ STR R1, [R0] /* RST in EXCP_TYPE_ADDR */
+ B excp_reset
+
+_undefined_handler:
+ LDR R0, =EXCP_TYPE_ADDR
+ LDR R1, =0x464455 /* UDF */
+ STR R1, [R0] /* UDF in EXCP_TYPE_ADDR */
+ B excp_reset
+
+_prefetch_abort_handler:
+ LDR R0, =EXCP_TYPE_ADDR
+ LDR R1, =0x54424150 /* PABT */
+ STR R1, [R0] /* PABT in EXCP_TYPE_ADDR */
+ B excp_reset
+
+_data_abort_handler:
+ LDR R0, =EXCP_TYPE_ADDR
+ LDR R1, =0x54424144 /* DABT */
+ STR R1, [R0] /* DABT in EXCP_TYPE_ADDR */
+ B excp_reset
+
+.globl irq_enable_cpu_irq_exceptions
+.type irq_enable_cpu_irq_exceptions, %function
+irq_enable_cpu_irq_exceptions:
+ MRS R12, CPSR
+ BIC R12, R12, #(IRQ | FIQ) /* IRQ/FIQ enabled */
+ MSR CPSR, R12
+ BX LR
+
+.globl irq_disable_cpu_irq_exceptions
+.type irq_disable_cpu_irq_exceptions, %function
+irq_disable_cpu_irq_exceptions:
+ MRS R12, CPSR
+ ORR R12, R12, #(IRQ | FIQ) /* IRQ/FIQ disabled */
+ MSR CPSR, R12
+ BX LR
+
+_irq_handler:
+ MOV R13, R0 /* Save R0 in R13_IRQ */
+ SUB R0, LR, #4 /* Put return address in R0_SYS */
+ MOV LR, R1 /* Save R1 in R14_IRQ (LR) */
+ MRS R1, SPSR /* Put the SPSR in R1_SYS */
+
+ MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
+ STMFD SP!, {R0, R1} /* SPSR and PC */
+ STMFD SP!, {R2-R3, R12, LR} /* AAPCS-clobbered registers */
+ MOV R0, SP /* Make SP_SYS visible to IRQ mode */
+ SUB SP, SP, #8 /* Make room for stacking R0 and R1 */
+
+ MSR CPSR_c, #(MODE_IRQ | IRQ) /* IRQ mode, IRQ disabled */
+ STMFD R0!, {R13, R14} /* Finish saving the context (R0, R1) */
+
+ MSR CPSR_c, #(MODE_SYS | IRQ) /* SYSTEM mode, IRQ disabled */
+ LDR R12, =irq_handler
+ MOV LR, PC /* Copy the return address to link register */
+ BX R12 /* Call the C IRQ handler (ARM/THUMB) */
+
+ MSR CPSR_c, #(MODE_SYS | IRQ | FIQ) /* SYSTEM mode, IRQ/FIQ disabled */
+ MOV R0, SP /* Make SP_SYS visible to IRQ mode */
+ ADD SP, SP, #32 /* Fake unstacking 8 registers from SP_SYS */
+
+ MSR CPSR_c, #(MODE_IRQ | IRQ | FIQ) /* IRQ mode, IRQ/FIQ disabled */
+ MOV SP, R0 /* Copy SP_SYS to SP_IRQ */
+ LDR R0, [SP, #28] /* Load the saved SPSR from the stack */
+ MSR SPSR_cxsf, R0 /* Copy it into SPSR_IRQ */
+
+ LDMFD SP, {R0-R3, R12, LR}^ /* Unstack all saved USER/SYSTEM registers */
+ NOP /* Cant access barked registers immediately */
+ LDR LR, [SP, #24] /* Load return address from the SYS stack */
+ MOVS PC, LR /* Return restoring CPSR from SPSR */
+
+_fiq_handler:
+ BL fiq_handler
+
+setup_vectors:
+ /* Setup vectors */
+ LDR R0, =EXCP_VEC_BASE
+
+ LDR R1, =_reset_handler
+ STR R1, [R0, #EVP_COP_RESET_VECTOR]
+
+ LDR R1, =_undefined_handler
+ STR R1, [R0, #EVP_COP_UNDEF_VECTOR]
+
+ LDR R1, =_reset_handler
+ STR R1, [R0, #EVP_COP_SWI_VECTOR]
+
+ LDR R1, =_prefetch_abort_handler
+ STR R1, [R0, #EVP_COP_PREFETCH_ABORT_VECTOR]
+
+ LDR R1, =_data_abort_handler
+ STR R1, [R0, #EVP_COP_DATA_ABORT_VECTOR]
+
+ LDR R1, =_reset_handler
+ STR R1, [R0, #EVP_COP_RSVD_VECTOR]
+
+ LDR R1, =_irq_handler
+ STR R1, [R0, #EVP_COP_IRQ_VECTOR]
+
+ LDR R1, =_fiq_handler
+ STR R1, [R0, #EVP_COP_FIQ_VECTOR]
+
+ BX LR
diff --git a/bdk/fatfs_cfg.h b/bdk/fatfs_cfg.h
index ed9ba5a..77b26dd 100644
--- a/bdk/fatfs_cfg.h
+++ b/bdk/fatfs_cfg.h
@@ -1,24 +1,28 @@
-/*
- * Copyright (c) 2020 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef _FATFS_CFG_H_
-#define _FATFS_CFG_H_
-
-#ifdef FFCFG_INC
-#include FFCFG_INC
-#endif
-
-#endif
+/*
+ * Copyright (c) 2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _FATFS_CFG_H_
+#define _FATFS_CFG_H_
+
+// define FFCFG_INC in a project to use a specific FatFS configuration.
+// Example: FFCFG_INC := '"../$(PROJECT_DIR)/libs/fatfs/ffconf.h"'
+#ifdef FFCFG_INC
+#include FFCFG_INC
+#else
+#include "fatfs_conf.h"
+#endif
+
+#endif
diff --git a/bdk/fatfs_conf.h b/bdk/fatfs_conf.h
new file mode 100644
index 0000000..e87219d
--- /dev/null
+++ b/bdk/fatfs_conf.h
@@ -0,0 +1,305 @@
+/*---------------------------------------------------------------------------/
+/ FatFs Functional Configurations
+/---------------------------------------------------------------------------*/
+
+#define FFCONF_DEF 86604 /* Revision ID */
+
+/*---------------------------------------------------------------------------/
+/ Function Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_READONLY 0
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
+/ Read-only configuration removes writing API functions, f_write(), f_sync(),
+/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
+/ and optional writing functions as well. */
+
+
+#define FF_FS_MINIMIZE 0
+/* This option defines minimization level to remove some basic API functions.
+/
+/ 0: Basic functions are fully enabled.
+/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
+/ are removed.
+/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/ 3: f_lseek() function is removed in addition to 2. */
+
+
+#define FF_USE_STRFUNC 2
+/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
+/
+/ 0: Disable string functions.
+/ 1: Enable without LF-CRLF conversion.
+/ 2: Enable with LF-CRLF conversion. */
+
+
+#define FF_USE_FIND 1
+/* This option switches filtered directory read functions, f_findfirst() and
+/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
+
+
+#define FF_USE_MKFS 0
+/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
+
+#if FF_USE_MKFS
+#define FF_MKFS_LABEL "SWITCH SD "
+#endif
+/* This sets FAT/FAT32 label. Exactly 11 characters, all caps. */
+
+
+#define FF_USE_FASTSEEK 0
+/* This option switches fast seek function. (0:Disable or 1:Enable) */
+
+#define FF_FASTFS 0
+#if FF_FASTFS
+#undef FF_USE_FASTSEEK
+#define FF_USE_FASTSEEK 1
+#endif
+/* This option switches fast access to chained clusters. (0:Disable or 1:Enable) */
+
+
+#define FF_SIMPLE_GPT 1
+/* This option switches support for the first GPT partition. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_EXPAND 0
+/* This option switches f_expand function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_CHMOD 1
+/* This option switches attribute manipulation functions, f_chmod() and f_utime().
+/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
+
+
+#define FF_USE_LABEL 0
+/* This option switches volume label functions, f_getlabel() and f_setlabel().
+/ (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FORWARD 0
+/* This option switches f_forward() function. (0:Disable or 1:Enable) */
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_CODE_PAGE 850
+/* This option specifies the OEM code page to be used on the target system.
+/ Incorrect code page setting can cause a file open failure.
+/
+/ 437 - U.S.
+/ 720 - Arabic
+/ 737 - Greek
+/ 771 - KBL
+/ 775 - Baltic
+/ 850 - Latin 1
+/ 852 - Latin 2
+/ 855 - Cyrillic
+/ 857 - Turkish
+/ 860 - Portuguese
+/ 861 - Icelandic
+/ 862 - Hebrew
+/ 863 - Canadian French
+/ 864 - Arabic
+/ 865 - Nordic
+/ 866 - Russian
+/ 869 - Greek 2
+/ 932 - Japanese (DBCS)
+/ 936 - Simplified Chinese (DBCS)
+/ 949 - Korean (DBCS)
+/ 950 - Traditional Chinese (DBCS)
+/ 0 - Include all code pages above and configured by f_setcp()
+*/
+
+
+#define FF_USE_LFN 3
+#define FF_MAX_LFN 255
+/* The FF_USE_LFN switches the support for LFN (long file name).
+/
+/ 0: Disable LFN. FF_MAX_LFN has no effect.
+/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
+/ 2: Enable LFN with dynamic working buffer on the STACK.
+/ 3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
+/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
+/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
+/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
+/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
+/ specification.
+/ When use stack for the working buffer, take care on stack overflow. When use heap
+/ memory for the working buffer, memory management functions, ff_memalloc() and
+/ ff_memfree() in ffsystem.c, need to be added to the project. */
+
+
+#define FF_LFN_UNICODE 0
+/* This option switches the character encoding on the API when LFN is enabled.
+/
+/ 0: ANSI/OEM in current CP (TCHAR = char)
+/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
+/ 2: Unicode in UTF-8 (TCHAR = char)
+/ 3: Unicode in UTF-32 (TCHAR = DWORD)
+/
+/ Also behavior of string I/O functions will be affected by this option.
+/ When LFN is not enabled, this option has no effect. */
+
+
+#define FF_LFN_BUF 255
+#define FF_SFN_BUF 12
+/* This set of options defines size of file name members in the FILINFO structure
+/ which is used to read out directory items. These values should be suffcient for
+/ the file names to read. The maximum possible length of the read file name depends
+/ on character encoding. When LFN is not enabled, these options have no effect. */
+
+
+#define FF_STRF_ENCODE 0
+/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
+/ f_putc(), f_puts and f_printf() convert the character encoding in it.
+/ This option selects assumption of character encoding ON THE FILE to be
+/ read/written via those functions.
+/
+/ 0: ANSI/OEM in current CP
+/ 1: Unicode in UTF-16LE
+/ 2: Unicode in UTF-16BE
+/ 3: Unicode in UTF-8
+*/
+
+
+#define FF_FS_RPATH 0
+/* This option configures support for relative path.
+/
+/ 0: Disable relative path and remove related functions.
+/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
+/ 2: f_getcwd() function is available in addition to 1.
+*/
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_VOLUMES 1
+/* Number of volumes (logical drives) to be used. (1-10) */
+
+
+#define FF_STR_VOLUME_ID 0
+#define FF_VOLUME_STRS "sd"
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
+/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
+/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
+/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
+/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
+/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
+/ not defined, a user defined volume string table needs to be defined as:
+/
+/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
+/ Order is important. Any change to order, must also be reflected to diskio drive enum.
+*/
+
+
+#define FF_MULTI_PARTITION 0
+/* This option switches support for multiple volumes on the physical drive.
+/ By default (0), each logical drive number is bound to the same physical drive
+/ number and only an FAT volume found on the physical drive will be mounted.
+/ When this function is enabled (1), each logical drive number can be bound to
+/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
+/ funciton will be available. */
+
+
+#define FF_MIN_SS 512
+#define FF_MAX_SS 512
+/* This set of options configures the range of sector size to be supported. (512,
+/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
+/ harddisk. But a larger value may be required for on-board flash memory and some
+/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
+/ for variable sector size mode and disk_ioctl() function needs to implement
+/ GET_SECTOR_SIZE command. */
+
+
+#define FF_USE_TRIM 0
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
+/ To enable Trim function, also CTRL_TRIM command should be implemented to the
+/ disk_ioctl() function. */
+
+
+#define FF_FS_NOFSINFO 1
+/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
+/ option, and f_getfree() function at first time after volume mount will force
+/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
+/
+/ bit0=0: Use free cluster count in the FSINFO if available.
+/ bit0=1: Do not trust free cluster count in the FSINFO.
+/ bit1=0: Use last allocated cluster number in the FSINFO if available.
+/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_TINY 0
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
+/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
+/ Instead of private sector buffer eliminated from the file object, common sector
+/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
+
+
+#define FF_FS_EXFAT 1
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
+/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
+/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
+
+
+#define FF_FS_NORTC 1
+#define FF_NORTC_MON 1
+#define FF_NORTC_MDAY 1
+#define FF_NORTC_YEAR 2022
+/* The option FF_FS_NORTC switches timestamp function. If the system does not have
+/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
+/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
+/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
+/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
+/ added to the project to read current time form real-time clock. FF_NORTC_MON,
+/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
+/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
+
+
+#define FF_FS_LOCK 0
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
+/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
+/ is 1.
+/
+/ 0: Disable file lock function. To avoid volume corruption, application program
+/ should avoid illegal open, remove and rename to the open objects.
+/ >0: Enable file lock function. The value defines how many files/sub-directories
+/ can be opened simultaneously under file lock control. Note that the file
+/ lock control is independent of re-entrancy. */
+
+
+/* #include // O/S definitions */
+#define FF_FS_REENTRANT 0
+#define FF_FS_TIMEOUT 1000
+#define FF_SYNC_t HANDLE
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
+/ module itself. Note that regardless of this option, file access to different
+/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
+/ and f_fdisk() function, are always not re-entrant. Only file/directory access
+/ to the same volume is under control of this function.
+/
+/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
+/ 1: Enable re-entrancy. Also user provided synchronization handlers,
+/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
+/ function, must be added to the project. Samples are available in
+/ option/syscall.c.
+/
+/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
+/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
+/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
+/ included somewhere in the scope of ff.h. */
+
+
+
+/*--- End of configuration options ---*/
diff --git a/bdk/gfx_utils.h b/bdk/gfx_utils.h
index 49f5634..ca81a7f 100644
--- a/bdk/gfx_utils.h
+++ b/bdk/gfx_utils.h
@@ -1,24 +1,24 @@
-/*
- * Copyright (c) 2020 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef _GFX_UTILS_H_
-#define _GFX_UTILS_H_
-
-#ifdef GFX_INC
-#include GFX_INC
-#endif
-
-#endif
+/*
+ * Copyright (c) 2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _GFX_UTILS_H_
+#define _GFX_UTILS_H_
+
+#ifdef GFX_INC
+#include GFX_INC
+#endif
+
+#endif
diff --git a/bdk/ianos/elfload/elf.h b/bdk/ianos/elfload/elf.h
index e2ec8da..2a7111e 100644
--- a/bdk/ianos/elfload/elf.h
+++ b/bdk/ianos/elfload/elf.h
@@ -1,589 +1,590 @@
-/* $OpenBSD: exec_elf.h,v 1.53 2014/01/03 03:00:39 guenther Exp $ */
-/*
- * Copyright (c) 1995, 1996 Erik Theisen. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* imported sys/exec_elf.h from OpenBSD */
-
-#ifndef ELF_H
-#define ELF_H
-#include
-
-typedef uint8_t Elf_Byte;
-
-typedef uint32_t Elf32_Addr; /* Unsigned program address */
-typedef uint32_t Elf32_Off; /* Unsigned file offset */
-typedef int32_t Elf32_Sword; /* Signed large integer */
-typedef uint32_t Elf32_Word; /* Unsigned large integer */
-typedef uint16_t Elf32_Half; /* Unsigned medium integer */
-
-typedef uint64_t Elf64_Addr;
-typedef uint64_t Elf64_Off;
-typedef int32_t Elf64_Shalf;
-
-#ifdef __alpha__
-typedef int64_t Elf64_Sword;
-typedef uint64_t Elf64_Word;
-#else
-typedef int32_t Elf64_Sword;
-typedef uint32_t Elf64_Word;
-#endif
-
-typedef int64_t Elf64_Sxword;
-typedef uint64_t Elf64_Xword;
-
-typedef uint32_t Elf64_Half;
-typedef uint16_t Elf64_Quarter;
-
-/*
- * e_ident[] identification indexes
- * See http://www.sco.com/developers/gabi/latest/ch4.eheader.html
- */
-#define EI_MAG0 0 /* file ID */
-#define EI_MAG1 1 /* file ID */
-#define EI_MAG2 2 /* file ID */
-#define EI_MAG3 3 /* file ID */
-#define EI_CLASS 4 /* file class */
-#define EI_DATA 5 /* data encoding */
-#define EI_VERSION 6 /* ELF header version */
-#define EI_OSABI 7 /* OS/ABI ID */
-#define EI_ABIVERSION 8 /* ABI version */
-#define EI_PAD 9 /* start of pad bytes */
-#define EI_NIDENT 16 /* Size of e_ident[] */
-
-/* e_ident[] magic number */
-#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
-#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
-#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
-#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
-#define ELFMAG "\177ELF" /* magic */
-#define SELFMAG 4 /* size of magic */
-
-/* e_ident[] file class */
-#define ELFCLASSNONE 0 /* invalid */
-#define ELFCLASS32 1 /* 32-bit objs */
-#define ELFCLASS64 2 /* 64-bit objs */
-#define ELFCLASSNUM 3 /* number of classes */
-
-/* e_ident[] data encoding */
-#define ELFDATANONE 0 /* invalid */
-#define ELFDATA2LSB 1 /* Little-Endian */
-#define ELFDATA2MSB 2 /* Big-Endian */
-#define ELFDATANUM 3 /* number of data encode defines */
-
-/* e_ident[] Operating System/ABI */
-#define ELFOSABI_SYSV 0 /* UNIX System V ABI */
-#define ELFOSABI_HPUX 1 /* HP-UX operating system */
-#define ELFOSABI_NETBSD 2 /* NetBSD */
-#define ELFOSABI_LINUX 3 /* GNU/Linux */
-#define ELFOSABI_HURD 4 /* GNU/Hurd */
-#define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */
-#define ELFOSABI_SOLARIS 6 /* Solaris */
-#define ELFOSABI_MONTEREY 7 /* Monterey */
-#define ELFOSABI_IRIX 8 /* IRIX */
-#define ELFOSABI_FREEBSD 9 /* FreeBSD */
-#define ELFOSABI_TRU64 10 /* TRU64 UNIX */
-#define ELFOSABI_MODESTO 11 /* Novell Modesto */
-#define ELFOSABI_OPENBSD 12 /* OpenBSD */
-#define ELFOSABI_ARM 97 /* ARM */
-#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
-
-/* e_ident */
-#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
- (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
- (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
- (ehdr).e_ident[EI_MAG3] == ELFMAG3)
-
-/* ELF Header */
-typedef struct
-{
- unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
- Elf32_Half e_type; /* object file type */
- Elf32_Half e_machine; /* machine */
- Elf32_Word e_version; /* object file version */
- Elf32_Addr e_entry; /* virtual entry point */
- Elf32_Off e_phoff; /* program header table offset */
- Elf32_Off e_shoff; /* section header table offset */
- Elf32_Word e_flags; /* processor-specific flags */
- Elf32_Half e_ehsize; /* ELF header size */
- Elf32_Half e_phentsize; /* program header entry size */
- Elf32_Half e_phnum; /* number of program header entries */
- Elf32_Half e_shentsize; /* section header entry size */
- Elf32_Half e_shnum; /* number of section header entries */
- Elf32_Half e_shstrndx; /* section header table's "section
- header string table" entry offset */
-} Elf32_Ehdr;
-
-typedef struct
-{
- unsigned char e_ident[EI_NIDENT]; /* Id bytes */
- Elf64_Quarter e_type; /* file type */
- Elf64_Quarter e_machine; /* machine type */
- Elf64_Half e_version; /* version number */
- Elf64_Addr e_entry; /* entry point */
- Elf64_Off e_phoff; /* Program hdr offset */
- Elf64_Off e_shoff; /* Section hdr offset */
- Elf64_Half e_flags; /* Processor flags */
- Elf64_Quarter e_ehsize; /* sizeof ehdr */
- Elf64_Quarter e_phentsize; /* Program header entry size */
- Elf64_Quarter e_phnum; /* Number of program headers */
- Elf64_Quarter e_shentsize; /* Section header entry size */
- Elf64_Quarter e_shnum; /* Number of section headers */
- Elf64_Quarter e_shstrndx; /* String table index */
-} Elf64_Ehdr;
-
-/* e_type */
-#define ET_NONE 0 /* No file type */
-#define ET_REL 1 /* relocatable file */
-#define ET_EXEC 2 /* executable file */
-#define ET_DYN 3 /* shared object file */
-#define ET_CORE 4 /* core file */
-#define ET_NUM 5 /* number of types */
-#define ET_LOPROC 0xff00 /* reserved range for processor */
-#define ET_HIPROC 0xffff /* specific e_type */
-
-/* e_machine */
-#define EM_NONE 0 /* No Machine */
-#define EM_M32 1 /* AT&T WE 32100 */
-#define EM_SPARC 2 /* SPARC */
-#define EM_386 3 /* Intel 80386 */
-#define EM_68K 4 /* Motorola 68000 */
-#define EM_88K 5 /* Motorola 88000 */
-#define EM_486 6 /* Intel 80486 - unused? */
-#define EM_860 7 /* Intel 80860 */
-#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */
-/*
- * Don't know if EM_MIPS_RS4_BE,
- * EM_SPARC64, EM_PARISC,
- * or EM_PPC are ABI compliant
- */
-#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */
-#define EM_SPARC64 11 /* SPARC v9 64-bit unofficial */
-#define EM_PARISC 15 /* HPPA */
-#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */
-#define EM_PPC 20 /* PowerPC */
-#define EM_ARM 40 /* ARM AArch32 */
-#define EM_ALPHA 41 /* DEC ALPHA */
-#define EM_SH 42 /* Hitachi/Renesas Super-H */
-#define EM_SPARCV9 43 /* SPARC version 9 */
-#define EM_IA_64 50 /* Intel IA-64 Processor */
-#define EM_AMD64 62 /* AMD64 architecture */
-#define EM_VAX 75 /* DEC VAX */
-#define EM_AARCH64 183 /* ARM AArch64 */
-
-/* Non-standard */
-#define EM_ALPHA_EXP 0x9026 /* DEC ALPHA */
-
-/* Version */
-#define EV_NONE 0 /* Invalid */
-#define EV_CURRENT 1 /* Current */
-#define EV_NUM 2 /* number of versions */
-
-/* Section Header */
-typedef struct
-{
- Elf32_Word sh_name; /* name - index into section header
- * string table section */
- Elf32_Word sh_type; /* type */
- Elf32_Word sh_flags; /* flags */
- Elf32_Addr sh_addr; /* address */
- Elf32_Off sh_offset; /* file offset */
- Elf32_Word sh_size; /* section size */
- Elf32_Word sh_link; /* section header table index link */
- Elf32_Word sh_info; /* extra information */
- Elf32_Word sh_addralign; /* address alignment */
- Elf32_Word sh_entsize; /* section entry size */
-} Elf32_Shdr;
-
-typedef struct
-{
- Elf64_Half sh_name; /* section name */
- Elf64_Half sh_type; /* section type */
- Elf64_Xword sh_flags; /* section flags */
- Elf64_Addr sh_addr; /* virtual address */
- Elf64_Off sh_offset; /* file offset */
- Elf64_Xword sh_size; /* section size */
- Elf64_Half sh_link; /* link to another */
- Elf64_Half sh_info; /* misc info */
- Elf64_Xword sh_addralign; /* memory alignment */
- Elf64_Xword sh_entsize; /* table entry size */
-} Elf64_Shdr;
-
-/* Special Section Indexes */
-#define SHN_UNDEF 0 /* undefined */
-#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */
-#define SHN_LOPROC 0xff00 /* reserved range for processor */
-#define SHN_HIPROC 0xff1f /* specific section indexes */
-#define SHN_ABS 0xfff1 /* absolute value */
-#define SHN_COMMON 0xfff2 /* common symbol */
-#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */
-
-/* sh_type */
-#define SHT_NULL 0 /* inactive */
-#define SHT_PROGBITS 1 /* program defined information */
-#define SHT_SYMTAB 2 /* symbol table section */
-#define SHT_STRTAB 3 /* string table section */
-#define SHT_RELA 4 /* relocation section with addends*/
-#define SHT_HASH 5 /* symbol hash table section */
-#define SHT_DYNAMIC 6 /* dynamic section */
-#define SHT_NOTE 7 /* note section */
-#define SHT_NOBITS 8 /* no space section */
-#define SHT_REL 9 /* relation section without addends */
-#define SHT_SHLIB 10 /* reserved - purpose unknown */
-#define SHT_DYNSYM 11 /* dynamic symbol table section */
-#define SHT_NUM 12 /* number of section types */
-#define SHT_LOPROC 0x70000000 /* reserved range for processor */
-#define SHT_HIPROC 0x7fffffff /* specific section header types */
-#define SHT_LOUSER 0x80000000 /* reserved range for application */
-#define SHT_HIUSER 0xffffffff /* specific indexes */
-
-/* Section names */
-#define ELF_BSS ".bss" /* uninitialized data */
-#define ELF_DATA ".data" /* initialized data */
-#define ELF_DEBUG ".debug" /* debug */
-#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */
-#define ELF_DYNSTR ".dynstr" /* dynamic string table */
-#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */
-#define ELF_FINI ".fini" /* termination code */
-#define ELF_GOT ".got" /* global offset table */
-#define ELF_HASH ".hash" /* symbol hash table */
-#define ELF_INIT ".init" /* initialization code */
-#define ELF_REL_DATA ".rel.data" /* relocation data */
-#define ELF_REL_FINI ".rel.fini" /* relocation termination code */
-#define ELF_REL_INIT ".rel.init" /* relocation initialization code */
-#define ELF_REL_DYN ".rel.dyn" /* relocation dynamic link info */
-#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */
-#define ELF_REL_TEXT ".rel.text" /* relocation code */
-#define ELF_RODATA ".rodata" /* read-only data */
-#define ELF_SHSTRTAB ".shstrtab" /* section header string table */
-#define ELF_STRTAB ".strtab" /* string table */
-#define ELF_SYMTAB ".symtab" /* symbol table */
-#define ELF_TEXT ".text" /* code */
-
-/* Section Attribute Flags - sh_flags */
-#define SHF_WRITE 0x1 /* Writable */
-#define SHF_ALLOC 0x2 /* occupies memory */
-#define SHF_EXECINSTR 0x4 /* executable */
-#define SHF_TLS 0x400 /* thread local storage */
-#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor \
- * specific section attributes */
-
-/* Symbol Table Entry */
-typedef struct elf32_sym
-{
- Elf32_Word st_name; /* name - index into string table */
- Elf32_Addr st_value; /* symbol value */
- Elf32_Word st_size; /* symbol size */
- unsigned char st_info; /* type and binding */
- unsigned char st_other; /* 0 - no defined meaning */
- Elf32_Half st_shndx; /* section header index */
-} Elf32_Sym;
-
-typedef struct
-{
- Elf64_Half st_name; /* Symbol name index in str table */
- Elf_Byte st_info; /* type / binding attrs */
- Elf_Byte st_other; /* unused */
- Elf64_Quarter st_shndx; /* section index of symbol */
- Elf64_Xword st_value; /* value of symbol */
- Elf64_Xword st_size; /* size of symbol */
-} Elf64_Sym;
-
-/* Symbol table index */
-#define STN_UNDEF 0 /* undefined */
-
-/* Extract symbol info - st_info */
-#define ELF32_ST_BIND(x) ((x) >> 4)
-#define ELF32_ST_TYPE(x) (((unsigned int)x) & 0xf)
-#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t)&0xf))
-
-#define ELF64_ST_BIND(x) ((x) >> 4)
-#define ELF64_ST_TYPE(x) (((unsigned int)x) & 0xf)
-#define ELF64_ST_INFO(b, t) (((b) << 4) + ((t)&0xf))
-
-/* Symbol Binding - ELF32_ST_BIND - st_info */
-#define STB_LOCAL 0 /* Local symbol */
-#define STB_GLOBAL 1 /* Global symbol */
-#define STB_WEAK 2 /* like global - lower precedence */
-#define STB_NUM 3 /* number of symbol bindings */
-#define STB_LOPROC 13 /* reserved range for processor */
-#define STB_HIPROC 15 /* specific symbol bindings */
-
-/* Symbol type - ELF32_ST_TYPE - st_info */
-#define STT_NOTYPE 0 /* not specified */
-#define STT_OBJECT 1 /* data object */
-#define STT_FUNC 2 /* function */
-#define STT_SECTION 3 /* section */
-#define STT_FILE 4 /* file */
-#define STT_TLS 6 /* thread local storage */
-#define STT_LOPROC 13 /* reserved range for processor */
-#define STT_HIPROC 15 /* specific symbol types */
-
-/* Relocation entry with implicit addend */
-typedef struct
-{
- Elf32_Addr r_offset; /* offset of relocation */
- Elf32_Word r_info; /* symbol table index and type */
-} Elf32_Rel;
-
-/* Relocation entry with explicit addend */
-typedef struct
-{
- Elf32_Addr r_offset; /* offset of relocation */
- Elf32_Word r_info; /* symbol table index and type */
- Elf32_Sword r_addend;
-} Elf32_Rela;
-
-/* Extract relocation info - r_info */
-#define ELF32_R_SYM(i) ((i) >> 8)
-#define ELF32_R_TYPE(i) ((unsigned char)(i))
-#define ELF32_R_INFO(s, t) (((s) << 8) + (unsigned char)(t))
-
-typedef struct
-{
- Elf64_Xword r_offset; /* where to do it */
- Elf64_Xword r_info; /* index & type of relocation */
-} Elf64_Rel;
-
-typedef struct
-{
- Elf64_Xword r_offset; /* where to do it */
- Elf64_Xword r_info; /* index & type of relocation */
- Elf64_Sxword r_addend; /* adjustment value */
-} Elf64_Rela;
-
-#define ELF64_R_SYM(info) ((info) >> 32)
-#define ELF64_R_TYPE(info) ((info)&0xFFFFFFFF)
-#define ELF64_R_INFO(s, t) (((s) << 32) + (__uint32_t)(t))
-
-#if defined(__mips64__) && defined(__MIPSEL__)
-/*
- * The 64-bit MIPS ELF ABI uses a slightly different relocation format
- * than the regular ELF ABI: the r_info field is split into several
- * pieces (see gnu/usr.bin/binutils/include/elf/mips.h for details).
- */
-#undef ELF64_R_SYM
-#undef ELF64_R_TYPE
-#undef ELF64_R_INFO
-#define ELF64_R_TYPE(info) (swap32((info) >> 32))
-#define ELF64_R_SYM(info) ((info)&0xFFFFFFFF)
-#define ELF64_R_INFO(s, t) (((__uint64_t)swap32(t) << 32) + (__uint32_t)(s))
-#endif /* __mips64__ && __MIPSEL__ */
-
-/* Program Header */
-typedef struct
-{
- Elf32_Word p_type; /* segment type */
- Elf32_Off p_offset; /* segment offset */
- Elf32_Addr p_vaddr; /* virtual address of segment */
- Elf32_Addr p_paddr; /* physical address - ignored? */
- Elf32_Word p_filesz; /* number of bytes in file for seg. */
- Elf32_Word p_memsz; /* number of bytes in mem. for seg. */
- Elf32_Word p_flags; /* flags */
- Elf32_Word p_align; /* memory alignment */
-} Elf32_Phdr;
-
-typedef struct
-{
- Elf64_Half p_type; /* entry type */
- Elf64_Half p_flags; /* flags */
- Elf64_Off p_offset; /* offset */
- Elf64_Addr p_vaddr; /* virtual address */
- Elf64_Addr p_paddr; /* physical address */
- Elf64_Xword p_filesz; /* file size */
- Elf64_Xword p_memsz; /* memory size */
- Elf64_Xword p_align; /* memory & file alignment */
-} Elf64_Phdr;
-
-/* Segment types - p_type */
-#define PT_NULL 0 /* unused */
-#define PT_LOAD 1 /* loadable segment */
-#define PT_DYNAMIC 2 /* dynamic linking section */
-#define PT_INTERP 3 /* the RTLD */
-#define PT_NOTE 4 /* auxiliary information */
-#define PT_SHLIB 5 /* reserved - purpose undefined */
-#define PT_PHDR 6 /* program header */
-#define PT_TLS 7 /* thread local storage */
-#define PT_LOOS 0x60000000 /* reserved range for OS */
-#define PT_HIOS 0x6fffffff /* specific segment types */
-#define PT_LOPROC 0x70000000 /* reserved range for processor */
-#define PT_HIPROC 0x7fffffff /* specific segment types */
-
-#define PT_OPENBSD_RANDOMIZE 0x65a3dbe6 /* fill with random data */
-#define PT_GANDR_KERNEL 0x67646b6c /* gdkl */
-
-/* Segment flags - p_flags */
-#define PF_X 0x1 /* Executable */
-#define PF_W 0x2 /* Writable */
-#define PF_R 0x4 /* Readable */
-#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */
- /* specific segment flags */
-
-/* Dynamic structure */
-typedef struct
-{
- Elf32_Sword d_tag; /* controls meaning of d_val */
- union {
- Elf32_Word d_val; /* Multiple meanings - see d_tag */
- Elf32_Addr d_ptr; /* program virtual address */
- } d_un;
-} Elf32_Dyn;
-
-typedef struct
-{
- Elf64_Xword d_tag; /* controls meaning of d_val */
- union {
- Elf64_Addr d_ptr;
- Elf64_Xword d_val;
- } d_un;
-} Elf64_Dyn;
-
-/* Dynamic Array Tags - d_tag */
-#define DT_NULL 0 /* marks end of _DYNAMIC array */
-#define DT_NEEDED 1 /* string table offset of needed lib */
-#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */
-#define DT_PLTGOT 3 /* address PLT/GOT */
-#define DT_HASH 4 /* address of symbol hash table */
-#define DT_STRTAB 5 /* address of string table */
-#define DT_SYMTAB 6 /* address of symbol table */
-#define DT_RELA 7 /* address of relocation table */
-#define DT_RELASZ 8 /* size of relocation table */
-#define DT_RELAENT 9 /* size of relocation entry */
-#define DT_STRSZ 10 /* size of string table */
-#define DT_SYMENT 11 /* size of symbol table entry */
-#define DT_INIT 12 /* address of initialization func. */
-#define DT_FINI 13 /* address of termination function */
-#define DT_SONAME 14 /* string table offset of shared obj */
-#define DT_RPATH 15 /* string table offset of library \
- * search path */
-#define DT_SYMBOLIC 16 /* start sym search in shared obj. */
-#define DT_REL 17 /* address of rel. tbl. w addends */
-#define DT_RELSZ 18 /* size of DT_REL relocation table */
-#define DT_RELENT 19 /* size of DT_REL relocation entry */
-#define DT_PLTREL 20 /* PLT referenced relocation entry */
-#define DT_DEBUG 21 /* bugger */
-#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */
-#define DT_JMPREL 23 /* add. of PLT's relocation entries */
-#define DT_BIND_NOW 24 /* Bind now regardless of env setting */
-#define DT_LOOS 0x6000000d /* reserved range for OS */
-#define DT_HIOS 0x6ffff000 /* specific dynamic array tags */
-#define DT_LOPROC 0x70000000 /* reserved range for processor */
-#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */
-
-/* some other useful tags */
-#define DT_RELACOUNT 0x6ffffff9 /* if present, number of RELATIVE */
-#define DT_RELCOUNT 0x6ffffffa /* relocs, which must come first */
-#define DT_FLAGS_1 0x6ffffffb
-
-/* Dynamic Flags - DT_FLAGS_1 .dynamic entry */
-#define DF_1_NOW 0x00000001
-#define DF_1_GLOBAL 0x00000002
-#define DF_1_GROUP 0x00000004
-#define DF_1_NODELETE 0x00000008
-#define DF_1_LOADFLTR 0x00000010
-#define DF_1_INITFIRST 0x00000020
-#define DF_1_NOOPEN 0x00000040
-#define DF_1_ORIGIN 0x00000080
-#define DF_1_DIRECT 0x00000100
-#define DF_1_TRANS 0x00000200
-#define DF_1_INTERPOSE 0x00000400
-#define DF_1_NODEFLIB 0x00000800
-#define DF_1_NODUMP 0x00001000
-#define DF_1_CONLFAT 0x00002000
-
-/* ld.so: number of low tags that are used saved internally (0 .. DT_NUM-1) */
-#define DT_NUM (DT_JMPREL + 1)
-
-/*
- * Note Definitions
- */
-typedef struct
-{
- Elf32_Word namesz;
- Elf32_Word descsz;
- Elf32_Word type;
-} Elf32_Note;
-
-typedef struct
-{
- Elf64_Half namesz;
- Elf64_Half descsz;
- Elf64_Half type;
-} Elf64_Note;
-
-#if defined(ELFSIZE) && (ELFSIZE == 32)
-#define Elf_Ehdr Elf32_Ehdr
-#define Elf_Phdr Elf32_Phdr
-#define Elf_Shdr Elf32_Shdr
-#define Elf_Sym Elf32_Sym
-#define Elf_Rel Elf32_Rel
-#define Elf_RelA Elf32_Rela
-#define Elf_Dyn Elf32_Dyn
-#define Elf_Half Elf32_Half
-#define Elf_Word Elf32_Word
-#define Elf_Sword Elf32_Sword
-#define Elf_Addr Elf32_Addr
-#define Elf_Off Elf32_Off
-#define Elf_Nhdr Elf32_Nhdr
-#define Elf_Note Elf32_Note
-
-#define ELF_R_SYM ELF32_R_SYM
-#define ELF_R_TYPE ELF32_R_TYPE
-#define ELF_R_INFO ELF32_R_INFO
-#define ELFCLASS ELFCLASS32
-
-#define ELF_ST_BIND ELF32_ST_BIND
-#define ELF_ST_TYPE ELF32_ST_TYPE
-#define ELF_ST_INFO ELF32_ST_INFO
-
-#elif defined(ELFSIZE) && (ELFSIZE == 64)
-
-#define Elf_Ehdr Elf64_Ehdr
-#define Elf_Phdr Elf64_Phdr
-#define Elf_Shdr Elf64_Shdr
-#define Elf_Sym Elf64_Sym
-#define Elf_Rel Elf64_Rel
-#define Elf_RelA Elf64_Rela
-#define Elf_Dyn Elf64_Dyn
-#define Elf_Half Elf64_Half
-#define Elf_Word Elf64_Word
-#define Elf_Sword Elf64_Sword
-#define Elf_Addr Elf64_Addr
-#define Elf_Off Elf64_Off
-#define Elf_Nhdr Elf64_Nhdr
-#define Elf_Note Elf64_Note
-
-#define ELF_R_SYM ELF64_R_SYM
-#define ELF_R_TYPE ELF64_R_TYPE
-#define ELF_R_INFO ELF64_R_INFO
-#define ELFCLASS ELFCLASS64
-
-#define ELF_ST_BIND ELF64_ST_BIND
-#define ELF_ST_TYPE ELF64_ST_TYPE
-#define ELF_ST_INFO ELF64_ST_INFO
-
-#endif
-
-#endif
+/* $OpenBSD: exec_elf.h,v 1.53 2014/01/03 03:00:39 guenther Exp $ */
+/*
+ * Copyright (c) 1995, 1996 Erik Theisen. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* imported sys/exec_elf.h from OpenBSD */
+
+#ifndef ELF_H
+#define ELF_H
+
+#include
+
+typedef u8 Elf_Byte;
+
+typedef u32 Elf32_Addr; /* Unsigned program address */
+typedef u32 Elf32_Off; /* Unsigned file offset */
+typedef s32 Elf32_Sword; /* Signed large integer */
+typedef u32 Elf32_Word; /* Unsigned large integer */
+typedef u16 Elf32_Half; /* Unsigned medium integer */
+
+typedef u64 Elf64_Addr;
+typedef u64 Elf64_Off;
+typedef s32 Elf64_Shalf;
+
+#ifdef __alpha__
+typedef s64 Elf64_Sword;
+typedef u64 Elf64_Word;
+#else
+typedef s32 Elf64_Sword;
+typedef u32 Elf64_Word;
+#endif
+
+typedef s64 Elf64_Sxword;
+typedef u64 Elf64_Xword;
+
+typedef u32 Elf64_Half;
+typedef u16 Elf64_Quarter;
+
+/*
+ * e_ident[] identification indexes
+ * See http://www.sco.com/developers/gabi/latest/ch4.eheader.html
+ */
+#define EI_MAG0 0 /* file ID */
+#define EI_MAG1 1 /* file ID */
+#define EI_MAG2 2 /* file ID */
+#define EI_MAG3 3 /* file ID */
+#define EI_CLASS 4 /* file class */
+#define EI_DATA 5 /* data encoding */
+#define EI_VERSION 6 /* ELF header version */
+#define EI_OSABI 7 /* OS/ABI ID */
+#define EI_ABIVERSION 8 /* ABI version */
+#define EI_PAD 9 /* start of pad bytes */
+#define EI_NIDENT 16 /* Size of e_ident[] */
+
+/* e_ident[] magic number */
+#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
+#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
+#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
+#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
+#define ELFMAG "\177ELF" /* magic */
+#define SELFMAG 4 /* size of magic */
+
+/* e_ident[] file class */
+#define ELFCLASSNONE 0 /* invalid */
+#define ELFCLASS32 1 /* 32-bit objs */
+#define ELFCLASS64 2 /* 64-bit objs */
+#define ELFCLASSNUM 3 /* number of classes */
+
+/* e_ident[] data encoding */
+#define ELFDATANONE 0 /* invalid */
+#define ELFDATA2LSB 1 /* Little-Endian */
+#define ELFDATA2MSB 2 /* Big-Endian */
+#define ELFDATANUM 3 /* number of data encode defines */
+
+/* e_ident[] Operating System/ABI */
+#define ELFOSABI_SYSV 0 /* UNIX System V ABI */
+#define ELFOSABI_HPUX 1 /* HP-UX operating system */
+#define ELFOSABI_NETBSD 2 /* NetBSD */
+#define ELFOSABI_LINUX 3 /* GNU/Linux */
+#define ELFOSABI_HURD 4 /* GNU/Hurd */
+#define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */
+#define ELFOSABI_SOLARIS 6 /* Solaris */
+#define ELFOSABI_MONTEREY 7 /* Monterey */
+#define ELFOSABI_IRIX 8 /* IRIX */
+#define ELFOSABI_FREEBSD 9 /* FreeBSD */
+#define ELFOSABI_TRU64 10 /* TRU64 UNIX */
+#define ELFOSABI_MODESTO 11 /* Novell Modesto */
+#define ELFOSABI_OPENBSD 12 /* OpenBSD */
+#define ELFOSABI_ARM 97 /* ARM */
+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
+
+/* e_ident */
+#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
+ (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
+ (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
+ (ehdr).e_ident[EI_MAG3] == ELFMAG3)
+
+/* ELF Header */
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
+ Elf32_Half e_type; /* object file type */
+ Elf32_Half e_machine; /* machine */
+ Elf32_Word e_version; /* object file version */
+ Elf32_Addr e_entry; /* virtual entry point */
+ Elf32_Off e_phoff; /* program header table offset */
+ Elf32_Off e_shoff; /* section header table offset */
+ Elf32_Word e_flags; /* processor-specific flags */
+ Elf32_Half e_ehsize; /* ELF header size */
+ Elf32_Half e_phentsize; /* program header entry size */
+ Elf32_Half e_phnum; /* number of program header entries */
+ Elf32_Half e_shentsize; /* section header entry size */
+ Elf32_Half e_shnum; /* number of section header entries */
+ Elf32_Half e_shstrndx; /* section header table's "section
+ header string table" entry offset */
+} Elf32_Ehdr;
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Id bytes */
+ Elf64_Quarter e_type; /* file type */
+ Elf64_Quarter e_machine; /* machine type */
+ Elf64_Half e_version; /* version number */
+ Elf64_Addr e_entry; /* entry point */
+ Elf64_Off e_phoff; /* Program hdr offset */
+ Elf64_Off e_shoff; /* Section hdr offset */
+ Elf64_Half e_flags; /* Processor flags */
+ Elf64_Quarter e_ehsize; /* sizeof ehdr */
+ Elf64_Quarter e_phentsize; /* Program header entry size */
+ Elf64_Quarter e_phnum; /* Number of program headers */
+ Elf64_Quarter e_shentsize; /* Section header entry size */
+ Elf64_Quarter e_shnum; /* Number of section headers */
+ Elf64_Quarter e_shstrndx; /* String table index */
+} Elf64_Ehdr;
+
+/* e_type */
+#define ET_NONE 0 /* No file type */
+#define ET_REL 1 /* relocatable file */
+#define ET_EXEC 2 /* executable file */
+#define ET_DYN 3 /* shared object file */
+#define ET_CORE 4 /* core file */
+#define ET_NUM 5 /* number of types */
+#define ET_LOPROC 0xff00 /* reserved range for processor */
+#define ET_HIPROC 0xffff /* specific e_type */
+
+/* e_machine */
+#define EM_NONE 0 /* No Machine */
+#define EM_M32 1 /* AT&T WE 32100 */
+#define EM_SPARC 2 /* SPARC */
+#define EM_386 3 /* Intel 80386 */
+#define EM_68K 4 /* Motorola 68000 */
+#define EM_88K 5 /* Motorola 88000 */
+#define EM_486 6 /* Intel 80486 - unused? */
+#define EM_860 7 /* Intel 80860 */
+#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */
+/*
+ * Don't know if EM_MIPS_RS4_BE,
+ * EM_SPARC64, EM_PARISC,
+ * or EM_PPC are ABI compliant
+ */
+#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */
+#define EM_SPARC64 11 /* SPARC v9 64-bit unofficial */
+#define EM_PARISC 15 /* HPPA */
+#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */
+#define EM_PPC 20 /* PowerPC */
+#define EM_ARM 40 /* ARM AArch32 */
+#define EM_ALPHA 41 /* DEC ALPHA */
+#define EM_SH 42 /* Hitachi/Renesas Super-H */
+#define EM_SPARCV9 43 /* SPARC version 9 */
+#define EM_IA_64 50 /* Intel IA-64 Processor */
+#define EM_AMD64 62 /* AMD64 architecture */
+#define EM_VAX 75 /* DEC VAX */
+#define EM_AARCH64 183 /* ARM AArch64 */
+
+/* Non-standard */
+#define EM_ALPHA_EXP 0x9026 /* DEC ALPHA */
+
+/* Version */
+#define EV_NONE 0 /* Invalid */
+#define EV_CURRENT 1 /* Current */
+#define EV_NUM 2 /* number of versions */
+
+/* Section Header */
+typedef struct
+{
+ Elf32_Word sh_name; /* name - index into section header
+ * string table section */
+ Elf32_Word sh_type; /* type */
+ Elf32_Word sh_flags; /* flags */
+ Elf32_Addr sh_addr; /* address */
+ Elf32_Off sh_offset; /* file offset */
+ Elf32_Word sh_size; /* section size */
+ Elf32_Word sh_link; /* section header table index link */
+ Elf32_Word sh_info; /* extra information */
+ Elf32_Word sh_addralign; /* address alignment */
+ Elf32_Word sh_entsize; /* section entry size */
+} Elf32_Shdr;
+
+typedef struct
+{
+ Elf64_Half sh_name; /* section name */
+ Elf64_Half sh_type; /* section type */
+ Elf64_Xword sh_flags; /* section flags */
+ Elf64_Addr sh_addr; /* virtual address */
+ Elf64_Off sh_offset; /* file offset */
+ Elf64_Xword sh_size; /* section size */
+ Elf64_Half sh_link; /* link to another */
+ Elf64_Half sh_info; /* misc info */
+ Elf64_Xword sh_addralign; /* memory alignment */
+ Elf64_Xword sh_entsize; /* table entry size */
+} Elf64_Shdr;
+
+/* Special Section Indexes */
+#define SHN_UNDEF 0 /* undefined */
+#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */
+#define SHN_LOPROC 0xff00 /* reserved range for processor */
+#define SHN_HIPROC 0xff1f /* specific section indexes */
+#define SHN_ABS 0xfff1 /* absolute value */
+#define SHN_COMMON 0xfff2 /* common symbol */
+#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */
+
+/* sh_type */
+#define SHT_NULL 0 /* inactive */
+#define SHT_PROGBITS 1 /* program defined information */
+#define SHT_SYMTAB 2 /* symbol table section */
+#define SHT_STRTAB 3 /* string table section */
+#define SHT_RELA 4 /* relocation section with addends*/
+#define SHT_HASH 5 /* symbol hash table section */
+#define SHT_DYNAMIC 6 /* dynamic section */
+#define SHT_NOTE 7 /* note section */
+#define SHT_NOBITS 8 /* no space section */
+#define SHT_REL 9 /* relation section without addends */
+#define SHT_SHLIB 10 /* reserved - purpose unknown */
+#define SHT_DYNSYM 11 /* dynamic symbol table section */
+#define SHT_NUM 12 /* number of section types */
+#define SHT_LOPROC 0x70000000 /* reserved range for processor */
+#define SHT_HIPROC 0x7fffffff /* specific section header types */
+#define SHT_LOUSER 0x80000000 /* reserved range for application */
+#define SHT_HIUSER 0xffffffff /* specific indexes */
+
+/* Section names */
+#define ELF_BSS ".bss" /* uninitialized data */
+#define ELF_DATA ".data" /* initialized data */
+#define ELF_DEBUG ".debug" /* debug */
+#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */
+#define ELF_DYNSTR ".dynstr" /* dynamic string table */
+#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */
+#define ELF_FINI ".fini" /* termination code */
+#define ELF_GOT ".got" /* global offset table */
+#define ELF_HASH ".hash" /* symbol hash table */
+#define ELF_INIT ".init" /* initialization code */
+#define ELF_REL_DATA ".rel.data" /* relocation data */
+#define ELF_REL_FINI ".rel.fini" /* relocation termination code */
+#define ELF_REL_INIT ".rel.init" /* relocation initialization code */
+#define ELF_REL_DYN ".rel.dyn" /* relocation dynamic link info */
+#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */
+#define ELF_REL_TEXT ".rel.text" /* relocation code */
+#define ELF_RODATA ".rodata" /* read-only data */
+#define ELF_SHSTRTAB ".shstrtab" /* section header string table */
+#define ELF_STRTAB ".strtab" /* string table */
+#define ELF_SYMTAB ".symtab" /* symbol table */
+#define ELF_TEXT ".text" /* code */
+
+/* Section Attribute Flags - sh_flags */
+#define SHF_WRITE 0x1 /* Writable */
+#define SHF_ALLOC 0x2 /* occupies memory */
+#define SHF_EXECINSTR 0x4 /* executable */
+#define SHF_TLS 0x400 /* thread local storage */
+#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor \
+ * specific section attributes */
+
+/* Symbol Table Entry */
+typedef struct elf32_sym
+{
+ Elf32_Word st_name; /* name - index into string table */
+ Elf32_Addr st_value; /* symbol value */
+ Elf32_Word st_size; /* symbol size */
+ unsigned char st_info; /* type and binding */
+ unsigned char st_other; /* 0 - no defined meaning */
+ Elf32_Half st_shndx; /* section header index */
+} Elf32_Sym;
+
+typedef struct
+{
+ Elf64_Half st_name; /* Symbol name index in str table */
+ Elf_Byte st_info; /* type / binding attrs */
+ Elf_Byte st_other; /* unused */
+ Elf64_Quarter st_shndx; /* section index of symbol */
+ Elf64_Xword st_value; /* value of symbol */
+ Elf64_Xword st_size; /* size of symbol */
+} Elf64_Sym;
+
+/* Symbol table index */
+#define STN_UNDEF 0 /* undefined */
+
+/* Extract symbol info - st_info */
+#define ELF32_ST_BIND(x) ((x) >> 4)
+#define ELF32_ST_TYPE(x) (((unsigned int)x) & 0xf)
+#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t)&0xf))
+
+#define ELF64_ST_BIND(x) ((x) >> 4)
+#define ELF64_ST_TYPE(x) (((unsigned int)x) & 0xf)
+#define ELF64_ST_INFO(b, t) (((b) << 4) + ((t)&0xf))
+
+/* Symbol Binding - ELF32_ST_BIND - st_info */
+#define STB_LOCAL 0 /* Local symbol */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* like global - lower precedence */
+#define STB_NUM 3 /* number of symbol bindings */
+#define STB_LOPROC 13 /* reserved range for processor */
+#define STB_HIPROC 15 /* specific symbol bindings */
+
+/* Symbol type - ELF32_ST_TYPE - st_info */
+#define STT_NOTYPE 0 /* not specified */
+#define STT_OBJECT 1 /* data object */
+#define STT_FUNC 2 /* function */
+#define STT_SECTION 3 /* section */
+#define STT_FILE 4 /* file */
+#define STT_TLS 6 /* thread local storage */
+#define STT_LOPROC 13 /* reserved range for processor */
+#define STT_HIPROC 15 /* specific symbol types */
+
+/* Relocation entry with implicit addend */
+typedef struct
+{
+ Elf32_Addr r_offset; /* offset of relocation */
+ Elf32_Word r_info; /* symbol table index and type */
+} Elf32_Rel;
+
+/* Relocation entry with explicit addend */
+typedef struct
+{
+ Elf32_Addr r_offset; /* offset of relocation */
+ Elf32_Word r_info; /* symbol table index and type */
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+/* Extract relocation info - r_info */
+#define ELF32_R_SYM(i) ((i) >> 8)
+#define ELF32_R_TYPE(i) ((unsigned char)(i))
+#define ELF32_R_INFO(s, t) (((s) << 8) + (unsigned char)(t))
+
+typedef struct
+{
+ Elf64_Xword r_offset; /* where to do it */
+ Elf64_Xword r_info; /* index & type of relocation */
+} Elf64_Rel;
+
+typedef struct
+{
+ Elf64_Xword r_offset; /* where to do it */
+ Elf64_Xword r_info; /* index & type of relocation */
+ Elf64_Sxword r_addend; /* adjustment value */
+} Elf64_Rela;
+
+#define ELF64_R_SYM(info) ((info) >> 32)
+#define ELF64_R_TYPE(info) ((info)&0xFFFFFFFF)
+#define ELF64_R_INFO(s, t) (((s) << 32) + (u32)(t))
+
+#if defined(__mips64__) && defined(__MIPSEL__)
+/*
+ * The 64-bit MIPS ELF ABI uses a slightly different relocation format
+ * than the regular ELF ABI: the r_info field is split into several
+ * pieces (see gnu/usr.bin/binutils/include/elf/mips.h for details).
+ */
+#undef ELF64_R_SYM
+#undef ELF64_R_TYPE
+#undef ELF64_R_INFO
+#define ELF64_R_TYPE(info) (swap32((info) >> 32))
+#define ELF64_R_SYM(info) ((info)&0xFFFFFFFF)
+#define ELF64_R_INFO(s, t) (((u64)swap32(t) << 32) + (u32)(s))
+#endif /* __mips64__ && __MIPSEL__ */
+
+/* Program Header */
+typedef struct
+{
+ Elf32_Word p_type; /* segment type */
+ Elf32_Off p_offset; /* segment offset */
+ Elf32_Addr p_vaddr; /* virtual address of segment */
+ Elf32_Addr p_paddr; /* physical address - ignored? */
+ Elf32_Word p_filesz; /* number of bytes in file for seg. */
+ Elf32_Word p_memsz; /* number of bytes in mem. for seg. */
+ Elf32_Word p_flags; /* flags */
+ Elf32_Word p_align; /* memory alignment */
+} Elf32_Phdr;
+
+typedef struct
+{
+ Elf64_Half p_type; /* entry type */
+ Elf64_Half p_flags; /* flags */
+ Elf64_Off p_offset; /* offset */
+ Elf64_Addr p_vaddr; /* virtual address */
+ Elf64_Addr p_paddr; /* physical address */
+ Elf64_Xword p_filesz; /* file size */
+ Elf64_Xword p_memsz; /* memory size */
+ Elf64_Xword p_align; /* memory & file alignment */
+} Elf64_Phdr;
+
+/* Segment types - p_type */
+#define PT_NULL 0 /* unused */
+#define PT_LOAD 1 /* loadable segment */
+#define PT_DYNAMIC 2 /* dynamic linking section */
+#define PT_INTERP 3 /* the RTLD */
+#define PT_NOTE 4 /* auxiliary information */
+#define PT_SHLIB 5 /* reserved - purpose undefined */
+#define PT_PHDR 6 /* program header */
+#define PT_TLS 7 /* thread local storage */
+#define PT_LOOS 0x60000000 /* reserved range for OS */
+#define PT_HIOS 0x6fffffff /* specific segment types */
+#define PT_LOPROC 0x70000000 /* reserved range for processor */
+#define PT_HIPROC 0x7fffffff /* specific segment types */
+
+#define PT_OPENBSD_RANDOMIZE 0x65a3dbe6 /* fill with random data */
+#define PT_GANDR_KERNEL 0x67646b6c /* gdkl */
+
+/* Segment flags - p_flags */
+#define PF_X 0x1 /* Executable */
+#define PF_W 0x2 /* Writable */
+#define PF_R 0x4 /* Readable */
+#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */
+ /* specific segment flags */
+
+/* Dynamic structure */
+typedef struct
+{
+ Elf32_Word d_tag; /* controls meaning of d_val */
+ union {
+ Elf32_Word d_val; /* Multiple meanings - see d_tag */
+ Elf32_Addr d_ptr; /* program virtual address */
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct
+{
+ Elf64_Xword d_tag; /* controls meaning of d_val */
+ union {
+ Elf64_Addr d_ptr;
+ Elf64_Xword d_val;
+ } d_un;
+} Elf64_Dyn;
+
+/* Dynamic Array Tags - d_tag */
+#define DT_NULL 0 /* marks end of _DYNAMIC array */
+#define DT_NEEDED 1 /* string table offset of needed lib */
+#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */
+#define DT_PLTGOT 3 /* address PLT/GOT */
+#define DT_HASH 4 /* address of symbol hash table */
+#define DT_STRTAB 5 /* address of string table */
+#define DT_SYMTAB 6 /* address of symbol table */
+#define DT_RELA 7 /* address of relocation table */
+#define DT_RELASZ 8 /* size of relocation table */
+#define DT_RELAENT 9 /* size of relocation entry */
+#define DT_STRSZ 10 /* size of string table */
+#define DT_SYMENT 11 /* size of symbol table entry */
+#define DT_INIT 12 /* address of initialization func. */
+#define DT_FINI 13 /* address of termination function */
+#define DT_SONAME 14 /* string table offset of shared obj */
+#define DT_RPATH 15 /* string table offset of library \
+ * search path */
+#define DT_SYMBOLIC 16 /* start sym search in shared obj. */
+#define DT_REL 17 /* address of rel. tbl. w addends */
+#define DT_RELSZ 18 /* size of DT_REL relocation table */
+#define DT_RELENT 19 /* size of DT_REL relocation entry */
+#define DT_PLTREL 20 /* PLT referenced relocation entry */
+#define DT_DEBUG 21 /* bugger */
+#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */
+#define DT_JMPREL 23 /* add. of PLT's relocation entries */
+#define DT_BIND_NOW 24 /* Bind now regardless of env setting */
+#define DT_LOOS 0x6000000d /* reserved range for OS */
+#define DT_HIOS 0x6ffff000 /* specific dynamic array tags */
+#define DT_LOPROC 0x70000000 /* reserved range for processor */
+#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */
+
+/* some other useful tags */
+#define DT_RELACOUNT 0x6ffffff9 /* if present, number of RELATIVE */
+#define DT_RELCOUNT 0x6ffffffa /* relocs, which must come first */
+#define DT_FLAGS_1 0x6ffffffb
+
+/* Dynamic Flags - DT_FLAGS_1 .dynamic entry */
+#define DF_1_NOW 0x00000001
+#define DF_1_GLOBAL 0x00000002
+#define DF_1_GROUP 0x00000004
+#define DF_1_NODELETE 0x00000008
+#define DF_1_LOADFLTR 0x00000010
+#define DF_1_INITFIRST 0x00000020
+#define DF_1_NOOPEN 0x00000040
+#define DF_1_ORIGIN 0x00000080
+#define DF_1_DIRECT 0x00000100
+#define DF_1_TRANS 0x00000200
+#define DF_1_INTERPOSE 0x00000400
+#define DF_1_NODEFLIB 0x00000800
+#define DF_1_NODUMP 0x00001000
+#define DF_1_CONLFAT 0x00002000
+
+/* ld.so: number of low tags that are used saved internally (0 .. DT_NUM-1) */
+#define DT_NUM (DT_JMPREL + 1)
+
+/*
+ * Note Definitions
+ */
+typedef struct
+{
+ Elf32_Word namesz;
+ Elf32_Word descsz;
+ Elf32_Word type;
+} Elf32_Note;
+
+typedef struct
+{
+ Elf64_Half namesz;
+ Elf64_Half descsz;
+ Elf64_Half type;
+} Elf64_Note;
+
+#if defined(ELFSIZE) && (ELFSIZE == 32)
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Phdr Elf32_Phdr
+#define Elf_Shdr Elf32_Shdr
+#define Elf_Sym Elf32_Sym
+#define Elf_Rel Elf32_Rel
+#define Elf_RelA Elf32_Rela
+#define Elf_Dyn Elf32_Dyn
+#define Elf_Half Elf32_Half
+#define Elf_Word Elf32_Word
+#define Elf_Sword Elf32_Sword
+#define Elf_Addr Elf32_Addr
+#define Elf_Off Elf32_Off
+#define Elf_Nhdr Elf32_Nhdr
+#define Elf_Note Elf32_Note
+
+#define ELF_R_SYM ELF32_R_SYM
+#define ELF_R_TYPE ELF32_R_TYPE
+#define ELF_R_INFO ELF32_R_INFO
+#define ELFCLASS ELFCLASS32
+
+#define ELF_ST_BIND ELF32_ST_BIND
+#define ELF_ST_TYPE ELF32_ST_TYPE
+#define ELF_ST_INFO ELF32_ST_INFO
+
+#elif defined(ELFSIZE) && (ELFSIZE == 64)
+
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Phdr Elf64_Phdr
+#define Elf_Shdr Elf64_Shdr
+#define Elf_Sym Elf64_Sym
+#define Elf_Rel Elf64_Rel
+#define Elf_RelA Elf64_Rela
+#define Elf_Dyn Elf64_Dyn
+#define Elf_Half Elf64_Half
+#define Elf_Word Elf64_Word
+#define Elf_Sword Elf64_Sword
+#define Elf_Addr Elf64_Addr
+#define Elf_Off Elf64_Off
+#define Elf_Nhdr Elf64_Nhdr
+#define Elf_Note Elf64_Note
+
+#define ELF_R_SYM ELF64_R_SYM
+#define ELF_R_TYPE ELF64_R_TYPE
+#define ELF_R_INFO ELF64_R_INFO
+#define ELFCLASS ELFCLASS64
+
+#define ELF_ST_BIND ELF64_ST_BIND
+#define ELF_ST_TYPE ELF64_ST_TYPE
+#define ELF_ST_INFO ELF64_ST_INFO
+
+#endif
+
+#endif
diff --git a/bdk/ianos/elfload/elfarch.h b/bdk/ianos/elfload/elfarch.h
index 81a92a9..f1cc388 100644
--- a/bdk/ianos/elfload/elfarch.h
+++ b/bdk/ianos/elfload/elfarch.h
@@ -1,49 +1,49 @@
-/*
- * Copyright © 2014, Owen Shepherd
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef ELFARCH_H
-#define ELFARCH_H
-
-#if defined(__i386__)
-#define EM_THIS EM_386
-#define EL_ARCH_USES_REL
-#elif defined(__amd64__)
-#define EM_THIS EM_AMD64
-#define EL_ARCH_USES_RELA
-#elif defined(__arm__)
-#define EM_THIS EM_ARM
-#define EL_ARCH_USES_REL
-#elif defined(__aarch64__)
-#define EM_THIS EM_AARCH64
-#define EL_ARCH_USES_RELA
-#define EL_ARCH_USES_REL
-#else
-#error specify your ELF architecture
-#endif
-
-#if defined(__LP64__) || defined(__LLP64__)
-#define ELFSIZE 64
-#else
-#define ELFSIZE 32
-#endif
-
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define ELFDATATHIS ELFDATA2LSB
-#else
-#define ELFDATATHIS ELFDATA2MSB
-#endif
-
-#endif
+/*
+ * Copyright © 2014, Owen Shepherd
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ELFARCH_H
+#define ELFARCH_H
+
+#if defined(__i386__)
+#define EM_THIS EM_386
+#define EL_ARCH_USES_REL
+#elif defined(__amd64__)
+#define EM_THIS EM_AMD64
+#define EL_ARCH_USES_RELA
+#elif defined(__arm__)
+#define EM_THIS EM_ARM
+#define EL_ARCH_USES_REL
+#elif defined(__aarch64__)
+#define EM_THIS EM_AARCH64
+#define EL_ARCH_USES_RELA
+#define EL_ARCH_USES_REL
+#else
+#error specify your ELF architecture
+#endif
+
+#if defined(__LP64__) || defined(__LLP64__)
+#define ELFSIZE 64
+#else
+#define ELFSIZE 32
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define ELFDATATHIS ELFDATA2LSB
+#else
+#define ELFDATATHIS ELFDATA2MSB
+#endif
+
+#endif
diff --git a/bdk/ianos/elfload/elfload.c b/bdk/ianos/elfload/elfload.c
index cae0feb..d56e99b 100644
--- a/bdk/ianos/elfload/elfload.c
+++ b/bdk/ianos/elfload/elfload.c
@@ -1,324 +1,324 @@
-/*
- * Copyright © 2018, M4xw
- * Copyright © 2014, Owen Shepherd
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include
-
-#include "elfload.h"
-
-el_status el_pread(el_ctx *ctx, void *def, size_t nb, size_t offset)
-{
- return ctx->pread(ctx, def, nb, offset) ? EL_OK : EL_EIO;
-}
-
-#define EL_PHOFF(ctx, num) (((ctx)->ehdr.e_phoff + (num) *(ctx)->ehdr.e_phentsize))
-el_status el_findphdr(el_ctx *ctx, Elf_Phdr *phdr, uint32_t type, unsigned *i)
-{
- el_status rv = EL_OK;
- for (; *i < ctx->ehdr.e_phnum; (*i)++)
- {
- if ((rv = el_pread(ctx, phdr, sizeof *phdr, EL_PHOFF(ctx, *i))))
- return rv;
-
- if (phdr->p_type == type)
- {
- return rv;
- }
- }
-
- *i = -1;
- return rv;
-}
-
-#define EL_SHOFF(ctx, num) (((ctx)->ehdr.e_shoff + (num) *(ctx)->ehdr.e_shentsize))
-el_status el_findshdr(el_ctx *ctx, Elf_Shdr *shdr, uint32_t type, unsigned *i)
-{
- el_status rv = EL_OK;
-
- for (; *i < ctx->ehdr.e_shnum; (*i)++)
- {
- if ((rv = el_pread(ctx, shdr, sizeof *shdr, EL_SHOFF(ctx, *i))))
-
- return rv;
-
- if (shdr->sh_type == type)
- {
- return rv;
- }
- }
-
- *i = -1;
-
- return rv;
-}
-
-el_status el_init(el_ctx *ctx)
-{
- el_status rv = EL_OK;
- if ((rv = el_pread(ctx, &ctx->ehdr, sizeof ctx->ehdr, 0)))
- return rv;
-
- /* validate header */
-
- if (!IS_ELF(ctx->ehdr))
- return EL_NOTELF;
-
- if (ctx->ehdr.e_ident[EI_CLASS] != ELFCLASS)
- return EL_WRONGBITS;
-
- if (ctx->ehdr.e_ident[EI_DATA] != ELFDATATHIS)
- return EL_WRONGENDIAN;
-
- if (ctx->ehdr.e_ident[EI_VERSION] != EV_CURRENT)
- return EL_NOTELF;
-
- if (ctx->ehdr.e_type != ET_EXEC && ctx->ehdr.e_type != ET_DYN)
- return EL_NOTEXEC;
-
- if (ctx->ehdr.e_machine != EM_THIS)
- return EL_WRONGARCH;
-
- if (ctx->ehdr.e_version != EV_CURRENT)
- return EL_NOTELF;
-
- /* load phdrs */
- Elf_Phdr ph;
-
- /* iterate through, calculate extents */
- ctx->base_load_paddr = ctx->base_load_vaddr = 0;
- ctx->align = 1;
- ctx->memsz = 0;
-
- unsigned i = 0;
- for (;;)
- {
- if ((rv = el_findphdr(ctx, &ph, PT_LOAD, &i)))
- return rv;
-
- if (i == (unsigned)-1)
- break;
-
- Elf_Addr phend = ph.p_vaddr + ph.p_memsz;
- if (phend > ctx->memsz)
- ctx->memsz = phend;
-
- if (ph.p_align > ctx->align)
- ctx->align = ph.p_align;
-
- i++;
- }
-
- // Program Header
- if (ctx->ehdr.e_type == ET_DYN)
- {
- i = 0;
-
- if ((rv = el_findphdr(ctx, &ph, PT_DYNAMIC, &i)))
- return rv;
-
- if (i == (unsigned)-1)
- return EL_NODYN;
-
- ctx->dynoff = ph.p_offset;
- ctx->dynsize = ph.p_filesz;
- }
- else
- {
- ctx->dynoff = 0;
- ctx->dynsize = 0;
- }
-
- // Section String Table
- if (ctx->ehdr.e_type == ET_DYN)
- {
- i = ctx->ehdr.e_shstrndx - 1;
-
- if ((rv = el_findshdr(ctx, &ctx->shstr, SHT_STRTAB, &i)))
- return rv;
-
- // Reset
- i = 0;
-
- if ((rv = el_findshdr(ctx, &ctx->symtab, SHT_SYMTAB, &i)))
- return rv;
-
- if (i == (unsigned)-1)
- return EL_NODYN;
- }
-
- return rv;
-}
-
-/*
-typedef void* (*el_alloc_cb)(
- el_ctx *ctx,
- Elf_Addr phys,
- Elf_Addr virt,
- Elf_Addr size);
-*/
-
-el_status el_load(el_ctx *ctx, el_alloc_cb alloc)
-{
- el_status rv = EL_OK;
-
- /* address deltas */
- Elf_Addr pdelta = ctx->base_load_paddr;
- Elf_Addr vdelta = ctx->base_load_vaddr;
-
- /* iterate paddrs */
- Elf_Phdr ph;
- unsigned i = 0;
- for (;;)
- {
- if ((rv = el_findphdr(ctx, &ph, PT_LOAD, &i)))
- return rv;
-
- if (i == (unsigned)-1)
- break;
-
- Elf_Addr pload = ph.p_paddr + pdelta;
- Elf_Addr vload = ph.p_vaddr + vdelta;
-
- /* allocate mem */
- char *dest = alloc(ctx, pload, vload, ph.p_memsz);
- if (!dest)
- return EL_ENOMEM;
-
- EL_DEBUG("Loading seg fileoff %x, vaddr %x to %p\n",
- ph.p_offset, ph.p_vaddr, dest);
-
- /* read loaded portion */
- if ((rv = el_pread(ctx, dest, ph.p_filesz, ph.p_offset)))
- return rv;
-
- /* zero mem-only portion */
- memset(dest + ph.p_filesz, 0, ph.p_memsz - ph.p_filesz);
-
- i++;
- }
-
- return rv;
-}
-
-el_status el_finddyn(el_ctx *ctx, Elf_Dyn *dyn, uint32_t tag)
-{
- el_status rv = EL_OK;
- size_t ndyn = ctx->dynsize / sizeof(Elf_Dyn);
-
- for (unsigned i = 0; i < ndyn; i++)
- {
- if ((rv = el_pread(ctx, dyn, sizeof *dyn, ctx->dynoff + i * sizeof *dyn)))
- return rv;
-
- if (dyn->d_tag == tag)
- return EL_OK;
- }
-
- dyn->d_tag = DT_NULL;
- return EL_OK;
-}
-
-el_status el_findrelocs(el_ctx *ctx, el_relocinfo *ri, uint32_t type)
-{
- el_status rv = EL_OK;
-
- Elf_Dyn rel, relsz, relent;
-
- if ((rv = el_finddyn(ctx, &rel, type)))
- return rv;
-
- if ((rv = el_finddyn(ctx, &relsz, type + 1)))
- return rv;
-
- if ((rv = el_finddyn(ctx, &relent, type + 2)))
- return rv;
-
- if (rel.d_tag == DT_NULL || relsz.d_tag == DT_NULL || relent.d_tag == DT_NULL)
- {
- ri->entrysize = 0;
- ri->tablesize = 0;
- ri->tableoff = 0;
- }
- else
- {
- ri->tableoff = rel.d_un.d_ptr;
- ri->tablesize = relsz.d_un.d_val;
- ri->entrysize = relent.d_un.d_val;
- }
-
- return rv;
-}
-
-extern el_status el_applyrel(el_ctx *ctx, Elf_Rel *rel);
-extern el_status el_applyrela(el_ctx *ctx, Elf_RelA *rela);
-
-el_status el_relocate(el_ctx *ctx)
-{
- el_status rv = EL_OK;
-
- // not dynamic
- if (ctx->ehdr.e_type != ET_DYN)
- return EL_OK;
-
- char *base = (char *)ctx->base_load_paddr;
-
- el_relocinfo ri;
-#ifdef EL_ARCH_USES_REL
- if ((rv = el_findrelocs(ctx, &ri, DT_REL)))
- return rv;
-
- if (ri.entrysize != sizeof(Elf_Rel) && ri.tablesize)
- {
- EL_DEBUG("Relocation size %u doesn't match expected %u\n",
- ri.entrysize, sizeof(Elf_Rel));
- return EL_BADREL;
- }
-
- size_t relcnt = ri.tablesize / sizeof(Elf_Rel);
- Elf_Rel *reltab = (Elf_Rel *)(base + ri.tableoff);
- for (size_t i = 0; i < relcnt; i++)
- {
- if ((rv = el_applyrel(ctx, &reltab[i])))
- return rv;
- }
-#endif
-
-#ifdef EL_ARCH_USES_RELA
- if ((rv = el_findrelocs(ctx, &ri, DT_RELA)))
- return rv;
-
- if (ri.entrysize != sizeof(Elf_RelA) && ri.tablesize)
- {
- EL_DEBUG("Relocation size %u doesn't match expected %u\n",
- ri.entrysize, sizeof(Elf_RelA));
- return EL_BADREL;
- }
-
- size_t relacnt = ri.tablesize / sizeof(Elf_RelA);
- Elf_RelA *relatab = (Elf_RelA *)(base + ri.tableoff);
- for (size_t i = 0; i < relacnt; i++)
- {
- if ((rv = el_applyrela(ctx, &relatab[i])))
- return rv;
- }
-#endif
-
-#if !defined(EL_ARCH_USES_REL) && !defined(EL_ARCH_USES_RELA)
-#error No relocation type defined!
-#endif
-
- return rv;
-}
+/*
+ * Copyright © 2018, M4xw
+ * Copyright © 2014, Owen Shepherd
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include
+
+#include "elfload.h"
+
+el_status el_pread(el_ctx *ctx, void *def, size_t nb, size_t offset)
+{
+ return ctx->pread(ctx, def, nb, offset);
+}
+
+#define EL_PHOFF(ctx, num) (((ctx)->ehdr.e_phoff + (num) *(ctx)->ehdr.e_phentsize))
+el_status el_findphdr(el_ctx *ctx, Elf_Phdr *phdr, u32 type, unsigned *i)
+{
+ el_status rv = EL_OK;
+ for (; *i < ctx->ehdr.e_phnum; (*i)++)
+ {
+ if ((rv = el_pread(ctx, phdr, sizeof *phdr, EL_PHOFF(ctx, *i))))
+ return rv;
+
+ if (phdr->p_type == type)
+ {
+ return rv;
+ }
+ }
+
+ *i = -1;
+ return rv;
+}
+
+#define EL_SHOFF(ctx, num) (((ctx)->ehdr.e_shoff + (num) *(ctx)->ehdr.e_shentsize))
+el_status el_findshdr(el_ctx *ctx, Elf_Shdr *shdr, u32 type, unsigned *i)
+{
+ el_status rv = EL_OK;
+
+ for (; *i < ctx->ehdr.e_shnum; (*i)++)
+ {
+ if ((rv = el_pread(ctx, shdr, sizeof *shdr, EL_SHOFF(ctx, *i))))
+
+ return rv;
+
+ if (shdr->sh_type == type)
+ {
+ return rv;
+ }
+ }
+
+ *i = -1;
+
+ return rv;
+}
+
+el_status el_init(el_ctx *ctx)
+{
+ el_status rv = EL_OK;
+ if ((rv = el_pread(ctx, &ctx->ehdr, sizeof ctx->ehdr, 0)))
+ return rv;
+
+ /* validate header */
+
+ if (!IS_ELF(ctx->ehdr))
+ return EL_NOTELF;
+
+ if (ctx->ehdr.e_ident[EI_CLASS] != ELFCLASS)
+ return EL_WRONGBITS;
+
+ if (ctx->ehdr.e_ident[EI_DATA] != ELFDATATHIS)
+ return EL_WRONGENDIAN;
+
+ if (ctx->ehdr.e_ident[EI_VERSION] != EV_CURRENT)
+ return EL_NOTELF;
+
+ if (ctx->ehdr.e_type != ET_EXEC && ctx->ehdr.e_type != ET_DYN)
+ return EL_NOTEXEC;
+
+ if (ctx->ehdr.e_machine != EM_THIS)
+ return EL_WRONGARCH;
+
+ if (ctx->ehdr.e_version != EV_CURRENT)
+ return EL_NOTELF;
+
+ /* load phdrs */
+ Elf_Phdr ph;
+
+ /* iterate through, calculate extents */
+ ctx->base_load_paddr = ctx->base_load_vaddr = 0;
+ ctx->align = 1;
+ ctx->memsz = 0;
+
+ unsigned i = 0;
+ for (;;)
+ {
+ if ((rv = el_findphdr(ctx, &ph, PT_LOAD, &i)))
+ return rv;
+
+ if (i == (unsigned)-1)
+ break;
+
+ Elf_Addr phend = ph.p_vaddr + ph.p_memsz;
+ if (phend > ctx->memsz)
+ ctx->memsz = phend;
+
+ if (ph.p_align > ctx->align)
+ ctx->align = ph.p_align;
+
+ i++;
+ }
+
+ // Program Header
+ if (ctx->ehdr.e_type == ET_DYN)
+ {
+ i = 0;
+
+ if ((rv = el_findphdr(ctx, &ph, PT_DYNAMIC, &i)))
+ return rv;
+
+ if (i == (unsigned)-1)
+ return EL_NODYN;
+
+ ctx->dynoff = ph.p_offset;
+ ctx->dynsize = ph.p_filesz;
+ }
+ else
+ {
+ ctx->dynoff = 0;
+ ctx->dynsize = 0;
+ }
+
+ // Section String Table
+ if (ctx->ehdr.e_type == ET_DYN)
+ {
+ i = ctx->ehdr.e_shstrndx - 1;
+
+ if ((rv = el_findshdr(ctx, &ctx->shstr, SHT_STRTAB, &i)))
+ return rv;
+
+ // Reset
+ i = 0;
+
+ if ((rv = el_findshdr(ctx, &ctx->symtab, SHT_SYMTAB, &i)))
+ return rv;
+
+ if (i == (unsigned)-1)
+ return EL_NODYN;
+ }
+
+ return rv;
+}
+
+/*
+typedef void* (*el_alloc_cb)(
+ el_ctx *ctx,
+ Elf_Addr phys,
+ Elf_Addr virt,
+ Elf_Addr size);
+*/
+
+el_status el_load(el_ctx *ctx, el_alloc_cb alloc)
+{
+ el_status rv = EL_OK;
+
+ /* address deltas */
+ Elf_Addr pdelta = ctx->base_load_paddr;
+ Elf_Addr vdelta = ctx->base_load_vaddr;
+
+ /* iterate paddrs */
+ Elf_Phdr ph;
+ unsigned i = 0;
+ for (;;)
+ {
+ if ((rv = el_findphdr(ctx, &ph, PT_LOAD, &i)))
+ return rv;
+
+ if (i == (unsigned)-1)
+ break;
+
+ Elf_Addr pload = ph.p_paddr + pdelta;
+ Elf_Addr vload = ph.p_vaddr + vdelta;
+
+ /* allocate mem */
+ char *dest = alloc(ctx, pload, vload, ph.p_memsz);
+ if (!dest)
+ return EL_ENOMEM;
+
+ EL_DEBUG("Loading seg fileoff %x, vaddr %x to %p\n",
+ ph.p_offset, ph.p_vaddr, dest);
+
+ /* read loaded portion */
+ if ((rv = el_pread(ctx, dest, ph.p_filesz, ph.p_offset)))
+ return rv;
+
+ /* zero mem-only portion */
+ memset(dest + ph.p_filesz, 0, ph.p_memsz - ph.p_filesz);
+
+ i++;
+ }
+
+ return rv;
+}
+
+el_status el_finddyn(el_ctx *ctx, Elf_Dyn *dyn, u32 tag)
+{
+ el_status rv = EL_OK;
+ size_t ndyn = ctx->dynsize / sizeof(Elf_Dyn);
+
+ for (unsigned i = 0; i < ndyn; i++)
+ {
+ if ((rv = el_pread(ctx, dyn, sizeof *dyn, ctx->dynoff + i * sizeof *dyn)))
+ return rv;
+
+ if (dyn->d_tag == tag)
+ return EL_OK;
+ }
+
+ dyn->d_tag = DT_NULL;
+ return EL_OK;
+}
+
+el_status el_findrelocs(el_ctx *ctx, el_relocinfo *ri, u32 type)
+{
+ el_status rv = EL_OK;
+
+ Elf_Dyn rel, relsz, relent;
+
+ if ((rv = el_finddyn(ctx, &rel, type)))
+ return rv;
+
+ if ((rv = el_finddyn(ctx, &relsz, type + 1)))
+ return rv;
+
+ if ((rv = el_finddyn(ctx, &relent, type + 2)))
+ return rv;
+
+ if (rel.d_tag == DT_NULL || relsz.d_tag == DT_NULL || relent.d_tag == DT_NULL)
+ {
+ ri->entrysize = 0;
+ ri->tablesize = 0;
+ ri->tableoff = 0;
+ }
+ else
+ {
+ ri->tableoff = rel.d_un.d_ptr;
+ ri->tablesize = relsz.d_un.d_val;
+ ri->entrysize = relent.d_un.d_val;
+ }
+
+ return rv;
+}
+
+extern el_status el_applyrel(el_ctx *ctx, Elf_Rel *rel);
+extern el_status el_applyrela(el_ctx *ctx, Elf_RelA *rela);
+
+el_status el_relocate(el_ctx *ctx)
+{
+ el_status rv = EL_OK;
+
+ // not dynamic
+ if (ctx->ehdr.e_type != ET_DYN)
+ return EL_OK;
+
+ char *base = (char *)ctx->base_load_paddr;
+
+ el_relocinfo ri;
+#ifdef EL_ARCH_USES_REL
+ if ((rv = el_findrelocs(ctx, &ri, DT_REL)))
+ return rv;
+
+ if (ri.entrysize != sizeof(Elf_Rel) && ri.tablesize)
+ {
+ EL_DEBUG("Relocation size %u doesn't match expected %u\n",
+ ri.entrysize, sizeof(Elf_Rel));
+ return EL_BADREL;
+ }
+
+ size_t relcnt = ri.tablesize / sizeof(Elf_Rel);
+ Elf_Rel *reltab = (Elf_Rel *)(base + ri.tableoff);
+ for (size_t i = 0; i < relcnt; i++)
+ {
+ if ((rv = el_applyrel(ctx, &reltab[i])))
+ return rv;
+ }
+#endif
+
+#ifdef EL_ARCH_USES_RELA
+ if ((rv = el_findrelocs(ctx, &ri, DT_RELA)))
+ return rv;
+
+ if (ri.entrysize != sizeof(Elf_RelA) && ri.tablesize)
+ {
+ EL_DEBUG("Relocation size %u doesn't match expected %u\n",
+ ri.entrysize, sizeof(Elf_RelA));
+ return EL_BADREL;
+ }
+
+ size_t relacnt = ri.tablesize / sizeof(Elf_RelA);
+ Elf_RelA *relatab = (Elf_RelA *)(base + ri.tableoff);
+ for (size_t i = 0; i < relacnt; i++)
+ {
+ if ((rv = el_applyrela(ctx, &relatab[i])))
+ return rv;
+ }
+#endif
+
+#if !defined(EL_ARCH_USES_REL) && !defined(EL_ARCH_USES_RELA)
+#error No relocation type defined!
+#endif
+
+ return rv;
+}
diff --git a/bdk/ianos/elfload/elfload.h b/bdk/ianos/elfload/elfload.h
index b4df76a..6394279 100644
--- a/bdk/ianos/elfload/elfload.h
+++ b/bdk/ianos/elfload/elfload.h
@@ -1,127 +1,128 @@
-/*
- * Copyright © 2018, M4xw
- * Copyright © 2014, Owen Shepherd
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef ELFLOAD_H
-#define ELFLOAD_H
-#include
-
-#include "elfarch.h"
-#include "elf.h"
-
-#include
-
-#ifdef DEBUG
-#include
-#define EL_DEBUG(format, ...) \
- gfx_printf(format __VA_OPT__(, ) __VA_ARGS__)
-#else
-#define EL_DEBUG(...) \
- do \
- { \
- } while (0)
-#endif
-
-typedef enum
-{
- EL_OK = 0,
-
- EL_EIO,
- EL_ENOMEM,
-
- EL_NOTELF,
- EL_WRONGBITS,
- EL_WRONGENDIAN,
- EL_WRONGARCH,
- EL_WRONGOS,
- EL_NOTEXEC,
- EL_NODYN,
- EL_BADREL,
-
-} el_status;
-
-typedef struct el_ctx
-{
- bool (*pread)(struct el_ctx *ctx, void *dest, size_t nb, size_t offset);
-
- /* base_load_* -> address we are actually going to load at
- */
- Elf_Addr
- base_load_paddr,
- base_load_vaddr;
-
- /* size in memory of binary */
- Elf_Addr memsz;
-
- /* required alignment */
- Elf_Addr align;
-
- /* ELF header */
- Elf_Ehdr ehdr;
-
- // Section Header Str Table
- Elf_Shdr shstr;
- Elf_Shdr symtab;
-
- /* Offset of dynamic table (0 if not ET_DYN) */
- Elf_Off dynoff;
- /* Size of dynamic table (0 if not ET_DYN) */
- Elf_Addr dynsize;
-} el_ctx;
-
-el_status el_pread(el_ctx *ctx, void *def, size_t nb, size_t offset);
-
-el_status el_init(el_ctx *ctx);
-typedef void *(*el_alloc_cb)(
- el_ctx *ctx,
- Elf_Addr phys,
- Elf_Addr virt,
- Elf_Addr size);
-
-el_status el_load(el_ctx *ctx, el_alloc_cb alloccb);
-
-/* find the next phdr of type \p type, starting at \p *i.
- * On success, returns EL_OK with *i set to the phdr number, and the phdr loaded
- * in *phdr.
- *
- * If the end of the phdrs table was reached, *i is set to -1 and the contents
- * of *phdr are undefined
- */
-el_status el_findphdr(el_ctx *ctx, Elf_Phdr *phdr, uint32_t type, unsigned *i);
-
-/* Relocate the loaded executable */
-el_status el_relocate(el_ctx *ctx);
-
-/* find a dynamic table entry
- * returns the entry on success, dyn->d_tag = DT_NULL on failure
- */
-el_status el_finddyn(el_ctx *ctx, Elf_Dyn *dyn, uint32_t type);
-
-typedef struct
-{
- Elf_Off tableoff;
- Elf_Addr tablesize;
- Elf_Addr entrysize;
-} el_relocinfo;
-
-/* find all information regarding relocations of a specific type.
- *
- * pass DT_REL or DT_RELA for type
- * sets ri->entrysize = 0 if not found
- */
-el_status el_findrelocs(el_ctx *ctx, el_relocinfo *ri, uint32_t type);
-
-#endif
+/*
+ * Copyright © 2018, M4xw
+ * Copyright © 2014, Owen Shepherd
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ELFLOAD_H
+#define ELFLOAD_H
+#include
+
+#include "elfarch.h"
+#include "elf.h"
+
+#ifdef DEBUG
+#include
+#define EL_DEBUG(format, ...) \
+ gfx_printf(format __VA_OPT__(, ) __VA_ARGS__)
+#else
+#define EL_DEBUG(...) \
+ do \
+ { \
+ } while (0)
+#endif
+
+typedef enum
+{
+ EL_OK = 0,
+
+ EL_EIO,
+ EL_ENOMEM,
+
+ EL_NOTELF,
+ EL_WRONGBITS,
+ EL_WRONGENDIAN,
+ EL_WRONGARCH,
+ EL_WRONGOS,
+ EL_NOTEXEC,
+ EL_NODYN,
+ EL_BADREL,
+
+} el_status;
+
+typedef struct el_ctx
+{
+ el_status (*pread)(struct el_ctx *ctx, void *dest, size_t nb, size_t offset);
+
+ /* base_load_* -> address we are actually going to load at
+ */
+ Elf_Addr
+ base_load_paddr,
+ base_load_vaddr;
+
+ /* original memory of binary */
+ Elf_Addr eaddr;
+
+ /* size in memory of binary */
+ Elf_Addr memsz;
+
+ /* required alignment */
+ Elf_Addr align;
+
+ /* ELF header */
+ Elf_Ehdr ehdr;
+
+ // Section Header Str Table
+ Elf_Shdr shstr;
+ Elf_Shdr symtab;
+
+ /* Offset of dynamic table (0 if not ET_DYN) */
+ Elf_Off dynoff;
+ /* Size of dynamic table (0 if not ET_DYN) */
+ Elf_Addr dynsize;
+} el_ctx;
+
+el_status el_pread(el_ctx *ctx, void *def, size_t nb, size_t offset);
+
+el_status el_init(el_ctx *ctx);
+typedef void *(*el_alloc_cb)(
+ el_ctx *ctx,
+ Elf_Addr phys,
+ Elf_Addr virt,
+ Elf_Addr size);
+
+el_status el_load(el_ctx *ctx, el_alloc_cb alloccb);
+
+/* find the next phdr of type \p type, starting at \p *i.
+ * On success, returns EL_OK with *i set to the phdr number, and the phdr loaded
+ * in *phdr.
+ *
+ * If the end of the phdrs table was reached, *i is set to -1 and the contents
+ * of *phdr are undefined
+ */
+el_status el_findphdr(el_ctx *ctx, Elf_Phdr *phdr, u32 type, unsigned *i);
+
+/* Relocate the loaded executable */
+el_status el_relocate(el_ctx *ctx);
+
+/* find a dynamic table entry
+ * returns the entry on success, dyn->d_tag = DT_NULL on failure
+ */
+el_status el_finddyn(el_ctx *ctx, Elf_Dyn *dyn, u32 type);
+
+typedef struct
+{
+ Elf_Off tableoff;
+ Elf_Addr tablesize;
+ Elf_Addr entrysize;
+} el_relocinfo;
+
+/* find all information regarding relocations of a specific type.
+ *
+ * pass DT_REL or DT_RELA for type
+ * sets ri->entrysize = 0 if not found
+ */
+el_status el_findrelocs(el_ctx *ctx, el_relocinfo *ri, u32 type);
+
+#endif
diff --git a/bdk/ianos/elfload/elfreloc_aarch64.c b/bdk/ianos/elfload/elfreloc_aarch64.c
index 12a2dc6..736ad46 100644
--- a/bdk/ianos/elfload/elfreloc_aarch64.c
+++ b/bdk/ianos/elfload/elfreloc_aarch64.c
@@ -1,84 +1,84 @@
-/*
- * Copyright © 2014, Owen Shepherd
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "elfload.h"
-
-#if defined(__aarch64__)
-
-#define R_AARCH64_NONE 0
-#define R_AARCH64_RELATIVE 1027
-
-el_status el_applyrela(el_ctx *ctx, Elf_RelA *rel)
-{
- uintptr_t *p = (uintptr_t *)(rel->r_offset + ctx->base_load_paddr);
- uint32_t type = ELF_R_TYPE(rel->r_info);
- uint32_t sym = ELF_R_SYM(rel->r_info);
-
- switch (type)
- {
- case R_AARCH64_NONE:
- EL_DEBUG("R_AARCH64_NONE\n");
- break;
- case R_AARCH64_RELATIVE:
- if (sym)
- {
- EL_DEBUG("R_AARCH64_RELATIVE with symbol ref!\n");
- return EL_BADREL;
- }
-
- EL_DEBUG("Applying R_AARCH64_RELATIVE reloc @%p\n", p);
- *p = rel->r_addend + ctx->base_load_vaddr;
- break;
-
- default:
- EL_DEBUG("Bad relocation %u\n", type);
- return EL_BADREL;
- }
-
- return EL_OK;
-}
-
-el_status el_applyrel(el_ctx *ctx, Elf_Rel *rel)
-{
- uintptr_t *p = (uintptr_t *)(rel->r_offset + ctx->base_load_paddr);
- uint32_t type = ELF_R_TYPE(rel->r_info);
- uint32_t sym = ELF_R_SYM(rel->r_info);
-
- switch (type)
- {
- case R_AARCH64_NONE:
- EL_DEBUG("R_AARCH64_NONE\n");
- break;
- case R_AARCH64_RELATIVE:
- if (sym)
- {
- EL_DEBUG("R_AARCH64_RELATIVE with symbol ref!\n");
- return EL_BADREL;
- }
-
- EL_DEBUG("Applying R_AARCH64_RELATIVE reloc @%p\n", p);
- *p += ctx->base_load_vaddr;
- break;
-
- default:
- EL_DEBUG("Bad relocation %u\n", type);
- return EL_BADREL;
- }
-
- return EL_OK;
-}
-
-#endif
+/*
+ * Copyright © 2014, Owen Shepherd
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "elfload.h"
+
+#if defined(__aarch64__)
+
+#define R_AARCH64_NONE 0
+#define R_AARCH64_RELATIVE 1027
+
+el_status el_applyrela(el_ctx *ctx, Elf_RelA *rel)
+{
+ uptr *p = (uptr *)(rel->r_offset + ctx->base_load_paddr);
+ u32 type = ELF_R_TYPE(rel->r_info);
+ u32 sym = ELF_R_SYM(rel->r_info);
+
+ switch (type)
+ {
+ case R_AARCH64_NONE:
+ EL_DEBUG("R_AARCH64_NONE\n");
+ break;
+ case R_AARCH64_RELATIVE:
+ if (sym)
+ {
+ EL_DEBUG("R_AARCH64_RELATIVE with symbol ref!\n");
+ return EL_BADREL;
+ }
+
+ EL_DEBUG("Applying R_AARCH64_RELATIVE reloc @%p\n", p);
+ *p = rel->r_addend + ctx->base_load_vaddr;
+ break;
+
+ default:
+ EL_DEBUG("Bad relocation %u\n", type);
+ return EL_BADREL;
+ }
+
+ return EL_OK;
+}
+
+el_status el_applyrel(el_ctx *ctx, Elf_Rel *rel)
+{
+ uptr *p = (uptr *)(rel->r_offset + ctx->base_load_paddr);
+ u32 type = ELF_R_TYPE(rel->r_info);
+ u32 sym = ELF_R_SYM(rel->r_info);
+
+ switch (type)
+ {
+ case R_AARCH64_NONE:
+ EL_DEBUG("R_AARCH64_NONE\n");
+ break;
+ case R_AARCH64_RELATIVE:
+ if (sym)
+ {
+ EL_DEBUG("R_AARCH64_RELATIVE with symbol ref!\n");
+ return EL_BADREL;
+ }
+
+ EL_DEBUG("Applying R_AARCH64_RELATIVE reloc @%p\n", p);
+ *p += ctx->base_load_vaddr;
+ break;
+
+ default:
+ EL_DEBUG("Bad relocation %u\n", type);
+ return EL_BADREL;
+ }
+
+ return EL_OK;
+}
+
+#endif
diff --git a/bdk/ianos/elfload/elfreloc_arm.c b/bdk/ianos/elfload/elfreloc_arm.c
index 37562eb..77ce654 100644
--- a/bdk/ianos/elfload/elfreloc_arm.c
+++ b/bdk/ianos/elfload/elfreloc_arm.c
@@ -1,66 +1,66 @@
-/*
- * ----------------------------------------------------------------------------
- * "THE BEER-WARE LICENSE" (Revision 42):
- * wrote this file. As long as you retain this notice you can do
- * whatever you want with this stuff. If we meet some day, and you think this
- * stuff is worth it, you can buy me a beer in return. M4xw
- * ----------------------------------------------------------------------------
- */
-
-#include "elfload.h"
-
-#if defined(__arm__)
-
-// Taken from http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf
-#define R_ARM_NONE 0
-#define R_ARM_ABS32 2
-#define R_ARM_JUMP_SLOT 22
-#define R_ARM_GLOB_DAT 21
-#define R_ARM_RELATIVE 23
-
-el_status el_applyrel(el_ctx *ctx, Elf_Rel *rel)
-{
- uint32_t sym = ELF_R_SYM(rel->r_info); // Symbol offset
- uint32_t type = ELF_R_TYPE(rel->r_info); // Relocation Type
- uintptr_t *p = (uintptr_t *)(rel->r_offset + ctx->base_load_paddr); // Target Addr
-
-#if 0 // For later symbol usage
- Elf32_Sym *elfSym;
- const char *symbolName;
-
- // We resolve relocs from the originating elf-image
- elfSym = (Elf32_Sym *)(ctx->symtab.sh_offset + (char *)buffteg) + sym;
- int strtab_offset = ctx->shstr.sh_offset;
- char *strtab = (char *)buffteg + strtab_offset;
- symbolName = strtab + elfSym->st_name;
- //EL_DEBUG("Str: %s sz: %x val: %x\n", symbolName, elfSym->st_size, elfSym->st_value);
-#endif
-
- switch (type)
- {
- case R_ARM_NONE:
- EL_DEBUG("R_ARM_NONE\n");
- break;
- case R_ARM_JUMP_SLOT:
- case R_ARM_ABS32:
- case R_ARM_GLOB_DAT:
- // Stubbed for later purpose
- //*p += elfSym->st_value; // + vaddr from sec
- //*p |= 0; // 1 if Thumb && STT_FUNC, ignored for now
- break;
- case R_ARM_RELATIVE: // Needed for PIE
- if (sym)
- {
- return EL_BADREL;
- }
- *p += ctx->base_load_vaddr;
- break;
-
- default:
- return EL_BADREL;
- }
-
- return EL_OK;
-}
-
-#endif
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * wrote this file. As long as you retain this notice you can do
+ * whatever you want with this stuff. If we meet some day, and you think this
+ * stuff is worth it, you can buy me a beer in return. M4xw
+ * ----------------------------------------------------------------------------
+ */
+
+#include "elfload.h"
+
+#if defined(__arm__)
+
+// Taken from http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf
+#define R_ARM_NONE 0
+#define R_ARM_ABS32 2
+#define R_ARM_JUMP_SLOT 22
+#define R_ARM_GLOB_DAT 21
+#define R_ARM_RELATIVE 23
+
+el_status el_applyrel(el_ctx *ctx, Elf_Rel *rel)
+{
+ u32 sym = ELF_R_SYM(rel->r_info); // Symbol offset
+ u32 type = ELF_R_TYPE(rel->r_info); // Relocation Type
+ uptr *p = (uptr *)(rel->r_offset + ctx->base_load_paddr); // Target Addr
+
+#if 0 // For later symbol usage
+ Elf32_Sym *elfSym;
+ const char *symbolName;
+
+ // We resolve relocs from the originating elf-image
+ elfSym = (Elf32_Sym *)(ctx->symtab.sh_offset + (char *)buffteg) + sym;
+ int strtab_offset = ctx->shstr.sh_offset;
+ char *strtab = (char *)buffteg + strtab_offset;
+ symbolName = strtab + elfSym->st_name;
+ //EL_DEBUG("Str: %s sz: %x val: %x\n", symbolName, elfSym->st_size, elfSym->st_value);
+#endif
+
+ switch (type)
+ {
+ case R_ARM_NONE:
+ EL_DEBUG("R_ARM_NONE\n");
+ break;
+ case R_ARM_JUMP_SLOT:
+ case R_ARM_ABS32:
+ case R_ARM_GLOB_DAT:
+ // Stubbed for later purpose
+ //*p += elfSym->st_value; // + vaddr from sec
+ //*p |= 0; // 1 if Thumb && STT_FUNC, ignored for now
+ break;
+ case R_ARM_RELATIVE: // Needed for PIE
+ if (sym)
+ {
+ return EL_BADREL;
+ }
+ *p += ctx->base_load_vaddr;
+ break;
+
+ default:
+ return EL_BADREL;
+ }
+
+ return EL_OK;
+}
+
+#endif
diff --git a/bdk/ianos/ianos.c b/bdk/ianos/ianos.c
index 0aad1ab..12a4644 100644
--- a/bdk/ianos/ianos.c
+++ b/bdk/ianos/ianos.c
@@ -1,126 +1,161 @@
-/*
- * Copyright (c) 2018 M4xw
- * Copyright (c) 2018-2019 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "ianos.h"
-#include "elfload/elfload.h"
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#define IRAM_LIB_ADDR 0x4002B000
-#define DRAM_LIB_ADDR 0xE0000000
-
-extern heap_t _heap;
-
-void *elfBuf = NULL;
-void *fileBuf = NULL;
-
-static void _ianos_call_ep(moduleEntrypoint_t entrypoint, void *moduleConfig)
-{
- bdkParams_t bdkParameters = (bdkParams_t)malloc(sizeof(struct _bdkParams_t));
- bdkParameters->gfxCon = (void *)&gfx_con;
- bdkParameters->gfxCtx = (void *)&gfx_ctxt;
- bdkParameters->memcpy = (memcpy_t)&memcpy;
- bdkParameters->memset = (memset_t)&memset;
- bdkParameters->sharedHeap = &_heap;
-
- // Extra functions.
- bdkParameters->extension_magic = IANOS_EXT0;
- bdkParameters->reg_voltage_set = (reg_voltage_set_t)&max7762x_regulator_set_voltage;
-
- entrypoint(moduleConfig, bdkParameters);
-}
-
-static void *_ianos_alloc_cb(el_ctx *ctx, Elf_Addr phys, Elf_Addr virt, Elf_Addr size)
-{
- (void)ctx;
- (void)phys;
- (void)size;
- return (void *)virt;
-}
-
-static bool _ianos_read_cb(el_ctx *ctx, void *dest, size_t numberBytes, size_t offset)
-{
- (void)ctx;
-
- memcpy(dest, fileBuf + offset, numberBytes);
-
- return true;
-}
-
-//TODO: Support shared libraries.
-uintptr_t ianos_loader(char *path, elfType_t type, void *moduleConfig)
-{
- el_ctx ctx;
- uintptr_t epaddr = 0;
-
- if (!sd_mount())
- goto elfLoadFinalOut;
-
- // Read library.
- fileBuf = sd_file_read(path, NULL);
-
- if (!fileBuf)
- goto elfLoadFinalOut;
-
- ctx.pread = _ianos_read_cb;
-
- if (el_init(&ctx))
- goto elfLoadFinalOut;
-
- // Set our relocated library's buffer.
- switch (type & 0xFFFF)
- {
- case EXEC_ELF:
- case AR64_ELF:
- elfBuf = (void *)DRAM_LIB_ADDR;
- break;
- default:
- elfBuf = malloc(ctx.memsz); // Aligned to 0x10 by default.
- }
-
- if (!elfBuf)
- goto elfLoadFinalOut;
-
- // Load and relocate library.
- ctx.base_load_vaddr = ctx.base_load_paddr = (uintptr_t)elfBuf;
- if (el_load(&ctx, _ianos_alloc_cb))
- goto elfFreeOut;
-
- if (el_relocate(&ctx))
- goto elfFreeOut;
-
- // Launch.
- epaddr = ctx.ehdr.e_entry + (uintptr_t)elfBuf;
- moduleEntrypoint_t ep = (moduleEntrypoint_t)epaddr;
-
- _ianos_call_ep(ep, moduleConfig);
-
-elfFreeOut:
- free(fileBuf);
- elfBuf = NULL;
- fileBuf = NULL;
-
-elfLoadFinalOut:
- return epaddr;
-}
\ No newline at end of file
+/*
+ * Copyright (c) 2018 M4xw
+ * Copyright (c) 2018-2026 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "ianos.h"
+#include "elfload/elfload.h"
+#include
+#include
+#include
+#include
+
+#include
+
+extern heap_t _heap;
+
+static bdk_params_t _bdk_params = {
+ .gfx_con = (void *)&gfx_con,
+ .gfx_ctx = (void *)&gfx_ctxt,
+ .heap = &_heap,
+ .memcpy = (memcpy_t)&memcpy,
+ .memset = (memset_t)&memset,
+
+ .extension_magic = 0
+};
+
+static void *_ianos_alloc_cb(el_ctx *ctx, Elf_Addr phys, Elf_Addr virt, Elf_Addr size)
+{
+ return (void *)virt;
+}
+
+static el_status _ianos_read_cb(el_ctx *ctx, void *dest, size_t nb, size_t offset)
+{
+ memcpy(dest, (void *)(ctx->eaddr + offset), nb);
+
+ return EL_OK;
+}
+
+//TODO: Support shared libraries.
+int ianos_loader(ianos_lib_t *lib, char *path)
+{
+ el_ctx ctx;
+ lib->buf = NULL;
+ if (!lib->bdk)
+ lib->bdk = &_bdk_params;
+
+ // Read library.
+ ctx.eaddr = (Elf_Addr)sd_file_read(path, NULL);
+ if (!ctx.eaddr)
+ goto error;
+
+ ctx.pread = _ianos_read_cb;
+
+ if (el_init(&ctx))
+ goto error;
+
+ if (lib->type & IA_SHARED_LIB)
+ goto error; // No support for shared libs now.
+
+ // Set our relocated library's buffer.
+ switch (lib->type & ~IA_SHARED_LIB)
+ {
+ case IA_DRAM_LIB:
+ lib->buf = malloc(ctx.memsz); // Aligned to 0x10 by default.
+ break;
+
+ case IA_IRAM_LIB:
+ break;
+
+ case IA_AUTO_LIB: // Default to DRAM for now.
+ default:
+ lib->buf = malloc(ctx.memsz); // Aligned to 0x10 by default.
+ break;
+ }
+
+ if (!lib->buf)
+ goto error;
+
+ // Load and relocate library.
+ ctx.base_load_vaddr = ctx.base_load_paddr = (Elf_Addr)lib->buf;
+ if (el_load(&ctx, _ianos_alloc_cb))
+ goto error;
+
+ if (el_relocate(&ctx))
+ goto error;
+
+ free((void *)ctx.eaddr);
+
+ // Launch.
+ Elf_Addr epaddr = ctx.ehdr.e_entry + (Elf_Addr)lib->buf;
+ moduleEntrypoint ep = (moduleEntrypoint)epaddr;
+ ep(lib->private, lib->bdk);
+
+ return 0;
+
+error:
+ free((void *)ctx.eaddr);
+ free(lib->buf);
+
+ return 1;
+}
+
+uintptr_t ianos_static_module(char *path, void *private)
+{
+ el_ctx ctx;
+ Elf_Addr buf = 0;
+ Elf_Addr epaddr = 0;
+
+ // Read library.
+ ctx.eaddr = (Elf_Addr)sd_file_read(path, NULL);
+ if (!ctx.eaddr)
+ goto error;
+
+ ctx.pread = _ianos_read_cb;
+
+ // Initialize elfload context.
+ if (el_init(&ctx))
+ goto error;
+
+ // Set our relocated library's buffer.
+ buf = (Elf_Addr)malloc(ctx.memsz); // Aligned to 0x10 by default.
+ if (!buf)
+ goto error;
+
+ // Load and relocate library.
+ ctx.base_load_vaddr = ctx.base_load_paddr = buf;
+ if (el_load(&ctx, _ianos_alloc_cb))
+ goto error;
+
+ if (el_relocate(&ctx))
+ goto error;
+
+ free((void *)ctx.eaddr);
+
+ // Launch.
+ epaddr = ctx.ehdr.e_entry + buf;
+ moduleEntrypoint ep = (moduleEntrypoint)epaddr;
+ ep(private, &_bdk_params);
+
+ return (uintptr_t)epaddr;
+
+error:
+ free((void *)ctx.eaddr);
+ free((void *)buf);
+
+ return 0;
+}
diff --git a/bdk/ianos/ianos.h b/bdk/ianos/ianos.h
index 5c609e8..79d2647 100644
--- a/bdk/ianos/ianos.h
+++ b/bdk/ianos/ianos.h
@@ -1,34 +1,44 @@
-/*
- * Copyright (c) 2018 M4xw
- * Copyright (c) 2018 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef IANOS_H
-#define IANOS_H
-
-#include
-
-typedef enum
-{
- DRAM_LIB = 0, // DRAM library.
- EXEC_ELF = 1, // Executable elf that does not return.
- DR64_LIB = 2, // AARCH64 DRAM library.
- AR64_ELF = 3, // Executable elf that does not return.
- KEEP_IN_RAM = (1 << 31) // Shared library mask.
-} elfType_t;
-
-uintptr_t ianos_loader(char *path, elfType_t type, void* config);
-
-#endif
\ No newline at end of file
+/*
+ * Copyright (c) 2018 M4xw
+ * Copyright (c) 2018-2026 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef IANOS_H
+#define IANOS_H
+
+#include
+#include
+
+typedef enum
+{
+ IA_DRAM_LIB = 0, // DRAM library.
+ IA_IRAM_LIB = 1, // IRAM library. No support for now.
+ IA_AUTO_LIB = 2, // AUTO library. Defaults to DRAM for now.
+ IA_SHARED_LIB = BIT(7) // Shared library mask. No support for now.
+} ianos_type_t;
+
+typedef struct _ianos_lib_t
+{
+ uintptr_t epaddr;
+ void *buf;
+ void *private;
+ ianos_type_t type;
+ bdk_params_t *bdk;
+} ianos_lib_t;
+
+int ianos_loader(ianos_lib_t *lib, char *path);
+uintptr_t ianos_static_module(char *path, void *private); // Session-lived DRAM lib.
+
+#endif
diff --git a/bdk/input/als.c b/bdk/input/als.c
index 1cd20a7..277f0d5 100644
--- a/bdk/input/als.c
+++ b/bdk/input/als.c
@@ -1,156 +1,154 @@
-/*
- * Ambient light sensor driver for Nintendo Switch's Rohm BH1730
- *
- * Copyright (c) 2018 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include "als.h"
-#include
-#include
-#include
-#include
-#include
-
-#define BH1730_DEFAULT_GAIN BH1730_GAIN_64X
-#define BH1730_DEFAULT_ICYCLE 38
-
-#define BH1730_INTERNAL_CLOCK_NS 2800
-#define BH1730_ADC_CALC_DELAY_US 2000 /* BH1730_INTERNAL_CLOCK_MS * 714 */
-#define BH1730_ITIME_CYCLE_TO_US 2700 /* BH1730_INTERNAL_CLOCK_MS * 964 */
-
-#define BH1730_DEFAULT_ITIME_MS 100
-
-#define BH1730_LUX_MULTIPLIER 3600
-#define BH1730_LUX_MULTIPLIER_AULA 1410
-
-#define BH1730_LUX_MAX 100000
-
-typedef struct _opt_win_cal_t
-{
- u32 rc;
- u32 cv;
- u32 ci;
-} opt_win_cal_t;
-
-// Nintendo Switch Icosa/Iowa Optical Window calibration.
-const opt_win_cal_t opt_win_cal_default[] = {
- { 500, 5002, 7502 },
- { 754, 2250, 2000 },
- { 1029, 1999, 1667 },
- { 1373, 884, 583 },
- { 1879, 309, 165 }
-};
-
-// Nintendo Switch Aula Optical Window calibration.
-const opt_win_cal_t opt_win_cal_aula[] = {
- { 231, 9697, 30300 },
- { 993, 3333, 2778 },
- { 1478, 1621, 1053 },
- { 7500, 81, 10 }
-};
-
-const u32 als_gain_idx_tbl[4] = { 1, 2, 64, 128 };
-
-void set_als_cfg(als_ctxt_t *als_ctxt, u8 gain, u8 cycle)
-{
- if (gain > BH1730_GAIN_128X)
- gain = BH1730_GAIN_128X;
-
- if (!cycle)
- cycle = 1;
- else if (cycle > 255)
- cycle = 255;
-
- i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_GAIN_REG), gain);
- i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_TIMING_REG), (256 - cycle));
-
- als_ctxt->gain = gain;
- als_ctxt->cycle = cycle;
-}
-
-void get_als_lux(als_ctxt_t *als_ctxt)
-{
- u32 data[2];
- u32 visible_light;
- u32 ir_light;
- u64 lux = 0;
- u32 itime_us = BH1730_ITIME_CYCLE_TO_US * als_ctxt->cycle;
-
- // Get visible and ir light raw data. Mode is continuous so waiting for new values doesn't matter.
- data[0] = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA0LOW_REG)) +
- (i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA0HIGH_REG)) << 8);
- data[1] = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1LOW_REG)) +
- (i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1HIGH_REG)) << 8);
-
- visible_light = data[0];
- ir_light = data[1];
-
- als_ctxt->over_limit = visible_light > 65534 || ir_light > 65534;
- als_ctxt->vi_light = visible_light;
- als_ctxt->ir_light = ir_light;
-
- if (!visible_light)
- {
- als_ctxt->lux = 0;
-
- return;
- }
-
- // Set calibration parameters.
- u32 lux_multiplier = BH1730_LUX_MULTIPLIER;
- u32 opt_win_cal_count = ARRAY_SIZE(opt_win_cal_default);
- const opt_win_cal_t *opt_win_cal = opt_win_cal_default;
-
- // Apply optical window calibration coefficients.
- for (u32 i = 0; i < opt_win_cal_count; i++)
- {
- if (1000 * ir_light / visible_light < opt_win_cal[i].rc)
- {
- lux = ((u64)opt_win_cal[i].cv * data[0]) - (opt_win_cal[i].ci * data[1]);
- break;
- }
- }
-
- lux *= BH1730_DEFAULT_ITIME_MS * lux_multiplier;
- lux /= als_gain_idx_tbl[als_ctxt->gain] * itime_us;
- lux /= 1000;
-
- if (lux > BH1730_LUX_MAX)
- lux = BH1730_LUX_MAX;
-
- als_ctxt->lux = lux;
-}
-
-u8 als_power_on(als_ctxt_t *als_ctxt)
-{
- // Enable power to ALS IC.
- max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
- max7762x_regulator_enable(REGULATOR_LDO6, true);
-
- // Init I2C2.
- pinmux_config_i2c(I2C_2);
- clock_enable_i2c(I2C_2);
- i2c_init(I2C_2);
-
- // Initialize ALS.
- u8 id = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(0x12));
- i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_SPEC(BH1730_SPECCMD_RESET), 0);
-
- set_als_cfg(als_ctxt, BH1730_DEFAULT_GAIN, BH1730_DEFAULT_ICYCLE);
-
- i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_CONTROL_REG), BH1730_CTL_POWER_ON | BH1730_CTL_ADC_EN);
-
- return id;
-}
+/*
+ * Ambient light sensor driver for Nintendo Switch's Rohm BH1730
+ *
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "als.h"
+#include
+#include
+#include
+#include
+#include
+
+#define BH1730_DEFAULT_GAIN BH1730_GAIN_64X
+#define BH1730_DEFAULT_ICYCLE 38
+
+#define BH1730_INTERNAL_CLOCK_NS 2800
+#define BH1730_ADC_CALC_DELAY_US 2000 /* BH1730_INTERNAL_CLOCK_MS * 714 */
+#define BH1730_ITIME_CYCLE_TO_US 2700 /* BH1730_INTERNAL_CLOCK_MS * 964 */
+
+#define BH1730_DEFAULT_ITIME_MS 100
+
+#define BH1730_LUX_MULTIPLIER 3600
+#define BH1730_LUX_MULTIPLIER_AULA 1410
+
+#define BH1730_LUX_MAX 100000
+
+typedef struct _opt_win_cal_t
+{
+ u32 rc;
+ u32 cv;
+ u32 ci;
+} opt_win_cal_t;
+
+// Nintendo Switch Icosa/Iowa Optical Window calibration.
+static const opt_win_cal_t opt_win_cal_default[] = {
+ { 500, 5002, 7502 },
+ { 754, 2250, 2000 },
+ { 1029, 1999, 1667 },
+ { 1373, 884, 583 },
+ { 1879, 309, 165 }
+};
+
+// Nintendo Switch Aula Optical Window calibration.
+static const opt_win_cal_t opt_win_cal_aula[] = {
+ { 231, 9697, 30300 },
+ { 993, 3333, 2778 },
+ { 1478, 1621, 1053 },
+ { 7500, 81, 10 }
+};
+
+static const u32 als_gain_idx_tbl[4] = { 1, 2, 64, 128 };
+
+void set_als_cfg(als_ctxt_t *als_ctxt, u8 gain, u8 cycle)
+{
+ if (gain > BH1730_GAIN_128X)
+ gain = BH1730_GAIN_128X;
+
+ if (!cycle)
+ cycle = 1;
+
+ i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_GAIN_REG), gain);
+ i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_TIMING_REG), (256 - cycle));
+
+ als_ctxt->gain = gain;
+ als_ctxt->cycle = cycle;
+}
+
+void get_als_lux(als_ctxt_t *als_ctxt)
+{
+ u32 data[2];
+ u32 vi_light;
+ u32 ir_light;
+ u64 lux = 0;
+ u32 itime_us = BH1730_ITIME_CYCLE_TO_US * als_ctxt->cycle;
+
+ // Get visible and ir light raw data. Mode is continuous so waiting for new values doesn't matter.
+ data[0] = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA0LOW_REG)) +
+ (i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA0HIGH_REG)) << 8);
+ data[1] = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1LOW_REG)) +
+ (i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1HIGH_REG)) << 8);
+
+ vi_light = data[0];
+ ir_light = data[1];
+
+ als_ctxt->vi_light = vi_light;
+ als_ctxt->ir_light = ir_light;
+ als_ctxt->over_limit = vi_light > 65534 || ir_light > 65534;
+
+ if (!vi_light)
+ {
+ als_ctxt->lux = 0;
+
+ return;
+ }
+
+ // Set calibration parameters.
+ u32 lux_multiplier = BH1730_LUX_MULTIPLIER;
+ u32 opt_win_cal_count = ARRAY_SIZE(opt_win_cal_default);
+ const opt_win_cal_t *opt_win_cal = opt_win_cal_default;
+
+ // Apply optical window calibration coefficients.
+ for (u32 i = 0; i < opt_win_cal_count; i++)
+ {
+ if (1000 * ir_light / vi_light < opt_win_cal[i].rc)
+ {
+ lux = ((u64)opt_win_cal[i].cv * data[0]) - (opt_win_cal[i].ci * data[1]);
+ break;
+ }
+ }
+
+ lux *= BH1730_DEFAULT_ITIME_MS * lux_multiplier;
+ lux /= als_gain_idx_tbl[als_ctxt->gain] * itime_us;
+ lux /= 1000;
+
+ if (lux > BH1730_LUX_MAX)
+ lux = BH1730_LUX_MAX;
+
+ als_ctxt->lux = lux;
+}
+
+u8 als_power_on(als_ctxt_t *als_ctxt)
+{
+ // Enable power to ALS IC.
+ max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
+ max7762x_regulator_enable(REGULATOR_LDO6, true);
+
+ // Init I2C2.
+ pinmux_config_i2c(I2C_2);
+ clock_enable_i2c(I2C_2);
+ i2c_init(I2C_2);
+
+ // Initialize ALS.
+ u8 id = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(0x12));
+ i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_SPEC(BH1730_SPECCMD_RESET), 0);
+
+ set_als_cfg(als_ctxt, BH1730_DEFAULT_GAIN, BH1730_DEFAULT_ICYCLE);
+
+ i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_CONTROL_REG), BH1730_CTL_POWER_ON | BH1730_CTL_ADC_EN);
+
+ return id;
+}
diff --git a/bdk/input/als.h b/bdk/input/als.h
index b077d20..0ce0956 100644
--- a/bdk/input/als.h
+++ b/bdk/input/als.h
@@ -1,65 +1,65 @@
-/*
- * Ambient light sensor driver for Nintendo Switch's Rohm BH1730
- *
- * Copyright (c) 2018 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef __ALS_H_
-#define __ALS_H_
-
-#include
-
-#define BH1730_I2C_ADDR 0x29
-
-#define BH1730_CMD_MAGIC 0x80
-#define BH1730_CMD_SETADDR 0x00
-#define BH1730_CMD_SPECCMD 0x60
-#define BH1730_SPECCMD_RESET 0x4
-
-#define BH1730_CONTROL_REG 0x00
-#define BH1730_CTL_ADC_VALID 0x10
-#define BH1730_CTL_ONE_TIME 0x08
-#define BH1730_CTL_DAT0_ONLY 0x04
-#define BH1730_CTL_ADC_EN 0x02
-#define BH1730_CTL_POWER_ON 0x01
-#define BH1730_TIMING_REG 0x01
-#define BH1730_GAIN_REG 0x07
-#define BH1730_GAIN_1X 0x00
-#define BH1730_GAIN_2X 0x01
-#define BH1730_GAIN_64X 0x02
-#define BH1730_GAIN_128X 0x03
-#define BH1730_DATA0LOW_REG 0x14
-#define BH1730_DATA0HIGH_REG 0x15
-#define BH1730_DATA1LOW_REG 0x16
-#define BH1730_DATA1HIGH_REG 0x17
-
-#define BH1730_ADDR(reg) (BH1730_CMD_MAGIC | BH1730_CMD_SETADDR | (reg))
-#define BH1730_SPEC(cmd) (BH1730_CMD_MAGIC | BH1730_CMD_SPECCMD | (cmd))
-
-typedef struct _als_ctxt_t
-{
- u32 lux;
- bool over_limit;
- u32 vi_light;
- u32 ir_light;
- u8 gain;
- u8 cycle;
-} als_ctxt_t;
-
-void set_als_cfg(als_ctxt_t *als_ctxt, u8 gain, u8 cycle);
-void get_als_lux(als_ctxt_t *als_ctxt);
-u8 als_power_on(als_ctxt_t *als_ctxt);
-
-#endif /* __ALS_H_ */
+/*
+ * Ambient light sensor driver for Nintendo Switch's Rohm BH1730
+ *
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __ALS_H_
+#define __ALS_H_
+
+#include
+
+#define BH1730_I2C_ADDR 0x29
+
+#define BH1730_CMD_MAGIC 0x80
+#define BH1730_CMD_SETADDR 0x00
+#define BH1730_CMD_SPECCMD 0x60
+#define BH1730_SPECCMD_RESET 0x4
+
+#define BH1730_CONTROL_REG 0x00
+#define BH1730_CTL_ADC_VALID 0x10
+#define BH1730_CTL_ONE_TIME 0x08
+#define BH1730_CTL_DAT0_ONLY 0x04
+#define BH1730_CTL_ADC_EN 0x02
+#define BH1730_CTL_POWER_ON 0x01
+#define BH1730_TIMING_REG 0x01
+#define BH1730_GAIN_REG 0x07
+#define BH1730_GAIN_1X 0x00
+#define BH1730_GAIN_2X 0x01
+#define BH1730_GAIN_64X 0x02
+#define BH1730_GAIN_128X 0x03
+#define BH1730_DATA0LOW_REG 0x14
+#define BH1730_DATA0HIGH_REG 0x15
+#define BH1730_DATA1LOW_REG 0x16
+#define BH1730_DATA1HIGH_REG 0x17
+
+#define BH1730_ADDR(reg) (BH1730_CMD_MAGIC | BH1730_CMD_SETADDR | (reg))
+#define BH1730_SPEC(cmd) (BH1730_CMD_MAGIC | BH1730_CMD_SPECCMD | (cmd))
+
+typedef struct _als_ctxt_t
+{
+ u32 lux;
+ bool over_limit;
+ u32 vi_light;
+ u32 ir_light;
+ u8 gain;
+ u8 cycle;
+} als_ctxt_t;
+
+void set_als_cfg(als_ctxt_t *als_ctxt, u8 gain, u8 cycle);
+void get_als_lux(als_ctxt_t *als_ctxt);
+u8 als_power_on(als_ctxt_t *als_ctxt);
+
+#endif /* __ALS_H_ */
diff --git a/bdk/input/joycon.c b/bdk/input/joycon.c
index 6116298..68b6ee5 100644
--- a/bdk/input/joycon.c
+++ b/bdk/input/joycon.c
@@ -1,934 +1,1542 @@
-/*
- * Joy-Con UART driver for Nintendo Switch
- *
- * Copyright (c) 2019-2021 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include "joycon.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// For disabling driver when logging is enabled.
-#include
-
-#define JC_WIRED_CMD 0x91
-#define JC_WIRED_HID 0x92
-#define JC_WIRED_INIT_REPLY 0x94
-#define JC_INIT_HANDSHAKE 0xA5
-
-#define JC_HORI_INPUT_RPT_CMD 0x9A
-#define JC_HORI_INPUT_RPT 0x00
-
-#define JC_WIRED_CMD_MAC 0x01
-#define JC_WIRED_CMD_10 0x10
-
-#define JC_HID_OUTPUT_RPT 0x01
-#define JC_HID_RUMBLE_RPT 0x10
-
-#define JC_HID_INPUT_RPT 0x30
-#define JC_HID_SUBMCD_RPT 0x21
-
-#define JC_HID_SUBCMD_HCI_STATE 0x06
-#define HCI_STATE_SLEEP 0x00
-#define HCI_STATE_RECONNECT 0x01
-#define HCI_STATE_PAIR 0x02
-#define HCI_STATE_HOME 0x04
-#define JC_HID_SUBCMD_SPI_READ 0x10
-#define SPI_READ_OFFSET 0x20
-#define JC_HID_SUBCMD_RUMBLE_CTL 0x48
-#define JC_HID_SUBCMD_SND_RUMBLE 0xFF
-
-#define JC_BTN_MASK_L 0xFF2900 // 0xFFE900: with charge status.
-#define JC_BTN_MASK_R 0x0056FF
-
-#define JC_ID_L 0x01
-#define JC_ID_R 0x02
-#define JC_ID_HORI 0x20
-
-#define JC_CRC8_INIT 0x00
-#define JC_CRC8_POLY 0x8D
-
-enum
-{
- JC_BATT_EMTPY = 0,
- JC_BATT_CRIT = 2,
- JC_BATT_LOW = 4,
- JC_BATT_MID = 6,
- JC_BATT_FULL = 8
-};
-
-static const u8 init_jc[] = {
- 0xA1, 0xA2, 0xA3, 0xA4
-};
-
-static const u8 init_handshake[] = {
- 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
- JC_INIT_HANDSHAKE, 0x02, // Wired cmd and wired subcmd.
- 0x01, 0x7E, 0x00, 0x00, 0x00 // Wired subcmd data and crc.
-};
-
-static const u8 init_get_info[] = {
- 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
- JC_WIRED_CMD, JC_WIRED_CMD_MAC, // Wired cmd and subcmd.
- 0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc.
-};
-
-static const u8 init_finalize[] = {
- 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
- JC_WIRED_CMD, JC_WIRED_CMD_10, // Wired cmd and subcmd.
- 0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data and crc.
-};
-
-static const u8 nx_pad_status[] = {
- 0x19, 0x01, 0x03, 0x08, 0x00, // Uart header.
- JC_WIRED_HID, 0x00, // Wired cmd and hid cmd.
- 0x01, 0x00, 0x00, 0x69, 0x2D, 0x1F // hid data and crc.
-};
-
-static const u8 hori_pad_status[] = {
- 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
- JC_HORI_INPUT_RPT_CMD, 0x01, // Hori cmd and hori subcmd.
- 0x00, 0x00, 0x00, 0x00, 0x48 // Hori cmd data and crc.
-};
-
-typedef struct _jc_uart_hdr_t
-{
- u8 magic[3];
- u8 total_size_lsb;
- u8 total_size_msb;
-} jc_uart_hdr_t;
-
-typedef struct _jc_wired_hdr_t
-{
- jc_uart_hdr_t uart_hdr;
- u8 cmd;
- u8 data[5];
- u8 crc;
- u8 payload[];
-} jc_wired_hdr_t;
-
-typedef struct _jc_hid_out_rpt_t
-{
- u8 cmd;
- u8 pkt_id;
- u8 rumble[8];
- u8 subcmd;
- u8 subcmd_data[];
-} jc_hid_out_rpt_t;
-
-typedef struct _jc_hid_out_spi_read_t
-{
- u32 addr;
- u8 size;
-} jc_hid_out_spi_read_t;
-
-typedef struct _jc_hid_in_rpt_t
-{
- u8 cmd;
- u8 pkt_id;
- u8 conn_info:4;
- u8 batt_info:4;
- u8 btn_right;
- u8 btn_shared;
- u8 btn_left;
- u8 stick_h_left;
- u8 stick_m_left;
- u8 stick_v_left;
- u8 stick_h_right;
- u8 stick_m_right;
- u8 stick_v_right;
- u8 vib_decider;
- u8 submcd_ack;
- u8 subcmd;
- u8 subcmd_data[];
-} jc_hid_in_rpt_t;
-
-typedef struct _jc_hid_in_spi_read_t
-{
- u32 addr;
- u8 size;
- u8 data[];
-} jc_hid_in_spi_read_t;
-
-typedef struct _jc_hid_in_pair_data_t
-{
- u8 magic;
- u8 size;
- u16 checksum;
- u8 mac[6];
- u8 ltk[16];
- u8 pad0[10];
- u8 bt_caps; // bit3: Secure conn supported host, bit5: Paired to TBFC supported host, bit6: iTBFC page supported
- u8 pad1;
-} jc_hid_in_pair_data_t;
-
-typedef struct _joycon_ctxt_t
-{
- u8 buf[0x100]; //FIXME: If heap is used, dumping breaks.
- u8 uart;
- u8 type;
- u8 mac[6];
- u32 hw_init_done;
- u32 last_received_time;
- u32 last_status_req_time;
- u8 rumble_sent;
- u8 connected;
-} joycon_ctxt_t;
-
-static joycon_ctxt_t jc_l = {0};
-static joycon_ctxt_t jc_r = {0};
-
-static bool jc_init_done = false;
-static u32 hid_pkt_inc = 0;
-
-static jc_gamepad_rpt_t jc_gamepad;
-
-void jc_power_supply(u8 uart, bool enable);
-
-static u8 jc_crc(u8 *data, u16 len)
-{
- u8 crc = JC_CRC8_INIT;
- u16 i, j;
- for (i = 0; i < len; i++) {
- crc ^= data[i];
- for (j = 0; j < 8; j++) {
- if ((crc & 0x80) != 0)
- crc = (u8)((crc << 1) ^ JC_CRC8_POLY);
- else
- crc <<= 1;
- }
- }
- return crc;
-}
-
-void joycon_send_raw(u8 uart_port, const u8 *buf, u16 size)
-{
- uart_send(uart_port, buf, size);
- uart_wait_idle(uart_port, UART_TX_IDLE);
-}
-
-static u16 jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u16 size, bool crc)
-{
- out->uart_hdr.magic[0] = 0x19;
- out->uart_hdr.magic[1] = 0x01;
- out->uart_hdr.magic[2] = 0x3;
-
- out->uart_hdr.total_size_lsb = sizeof(jc_wired_hdr_t) - sizeof(jc_uart_hdr_t);
- out->uart_hdr.total_size_msb = 0;
- out->cmd = wired_cmd;
-
- if (data)
- memcpy(out->data, data, size);
-
- out->crc = crc ? jc_crc(&out->uart_hdr.total_size_msb, sizeof(out->uart_hdr.total_size_msb) + sizeof(out->cmd) + sizeof(out->data)) : 0;
-
- return sizeof(jc_wired_hdr_t);
-}
-
-static u16 jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, u8 *payload, u16 size, bool crc)
-{
- u16 pkt_size = jc_packet_add_uart_hdr(rpt, JC_WIRED_HID, NULL, 0, crc);
- pkt_size += size;
-
- rpt->uart_hdr.total_size_lsb += size;
- rpt->data[0] = size >> 8;
- rpt->data[1] = size & 0xFF;
-
- if (payload)
- memcpy(rpt->payload, payload, size);
-
- return pkt_size;
-}
-
-void jc_send_hid_output_rpt(u8 uart, u8 *payload, u16 size, bool crc)
-{
- u8 rpt[0x50];
- memset(rpt, 0, sizeof(rpt));
-
- u32 rpt_size = jc_hid_output_rpt_craft((jc_wired_hdr_t *)rpt, payload, size, crc);
-
- joycon_send_raw(uart, rpt, rpt_size);
-}
-
-static u8 jc_hid_pkt_id_incr()
-{
- u8 curr_id = hid_pkt_inc;
- hid_pkt_inc++;
-
- return (curr_id & 0xF);
-}
-
-void jc_send_hid_cmd(u8 uart, u8 subcmd, u8 *data, u16 size)
-{
- u8 temp[0x30];
- u8 rumble_neutral[8] = {0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40};
- u8 rumble_init[8] = {0xc2, 0xc8, 0x03, 0x72, 0xc2, 0xc8, 0x03, 0x72};
-
- memset(temp, 0, sizeof(temp));
-
- jc_hid_out_rpt_t *hid_pkt = (jc_hid_out_rpt_t *)temp;
-
- memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral));
-
- if (subcmd == JC_HID_SUBCMD_SND_RUMBLE)
- {
- bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent;
- bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent;
-
- // Enable rumble.
- hid_pkt->cmd = JC_HID_OUTPUT_RPT;
- hid_pkt->pkt_id = jc_hid_pkt_id_incr();
- hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL;
- hid_pkt->subcmd_data[0] = 1;
- if (send_r_rumble)
- jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 0x10, false);
- if (send_l_rumble)
- jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 0x10, false);
-
- // Send rumble.
- hid_pkt->cmd = JC_HID_RUMBLE_RPT;
- hid_pkt->pkt_id = jc_hid_pkt_id_incr();
- memcpy(hid_pkt->rumble, rumble_init, sizeof(rumble_init));
- if (send_r_rumble)
- jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 10, false);
- if (send_l_rumble)
- jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 10, false);
-
- msleep(15);
-
- // Disable rumble.
- hid_pkt->cmd = JC_HID_OUTPUT_RPT;
- hid_pkt->pkt_id = jc_hid_pkt_id_incr();
- hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL;
- hid_pkt->subcmd_data[0] = 0;
- memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral));
- if (send_r_rumble)
- jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 0x10, false);
- if (send_l_rumble)
- jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 0x10, false);
- }
- else
- {
- bool crc_needed = (jc_l.uart == uart) ? (jc_l.type & JC_ID_HORI) : (jc_r.type & JC_ID_HORI);
-
- hid_pkt->cmd = JC_HID_OUTPUT_RPT;
- hid_pkt->pkt_id = jc_hid_pkt_id_incr();
- hid_pkt->subcmd = subcmd;
- if (data)
- memcpy(hid_pkt->subcmd_data, data, size);
-
- jc_send_hid_output_rpt(uart, (u8 *)hid_pkt, sizeof(jc_hid_out_rpt_t) + size, crc_needed);
- }
-}
-
-static void jc_charging_decider(u8 batt, u8 uart)
-{
- u32 system_batt_enough = max17050_get_cached_batt_volt() > 4000;
-
- // Power supply control based on battery levels and charging.
- if ((batt >> 1 << 1) < JC_BATT_LOW) // Level without checking charging.
- jc_power_supply(uart, true);
- else if (batt > (system_batt_enough ? JC_BATT_FULL : JC_BATT_MID)) // Addresses the charging bit.
- jc_power_supply(uart, false);
-}
-
-static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size)
-{
- u32 btn_tmp;
- jc_hid_in_rpt_t *hid_pkt = (jc_hid_in_rpt_t *)packet;
-
- switch (hid_pkt->cmd)
- {
- case JC_HORI_INPUT_RPT:
- case JC_HID_INPUT_RPT:
- btn_tmp = hid_pkt->btn_right | hid_pkt->btn_shared << 8 | hid_pkt->btn_left << 16;
-
- if (jc->type & JC_ID_L)
- {
- jc_gamepad.buttons &= ~JC_BTN_MASK_L;
- jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_L);
-
- jc_gamepad.lstick_x = hid_pkt->stick_h_left | ((hid_pkt->stick_m_left & 0xF) << 8);
- jc_gamepad.lstick_y = (hid_pkt->stick_m_left >> 4) | (hid_pkt->stick_v_left << 4);
-
- jc_gamepad.batt_info_l = hid_pkt->batt_info;
- }
- else if (jc->type & JC_ID_R)
- {
- jc_gamepad.buttons &= ~JC_BTN_MASK_R;
- jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_R);
-
- jc_gamepad.rstick_x = hid_pkt->stick_h_right | ((hid_pkt->stick_m_right & 0xF) << 8);
- jc_gamepad.rstick_y = (hid_pkt->stick_m_right >> 4) | (hid_pkt->stick_v_right << 4);
-
- jc_gamepad.batt_info_r = hid_pkt->batt_info;
- }
-
- jc_gamepad.conn_l = jc_l.connected;
- jc_gamepad.conn_r = jc_r.connected;
-
- jc_charging_decider(hid_pkt->batt_info, jc->uart);
- break;
- case JC_HID_SUBMCD_RPT:
- if (hid_pkt->subcmd == JC_HID_SUBCMD_SPI_READ)
- {
- jc_bt_conn_t *bt_conn;
-
- if (jc->type & JC_ID_L)
- bt_conn = &jc_gamepad.bt_conn_l;
- else
- bt_conn = &jc_gamepad.bt_conn_r;
-
- jc_hid_in_spi_read_t *spi_info = (jc_hid_in_spi_read_t *)hid_pkt->subcmd_data;
- jc_hid_in_pair_data_t *pair_data = (jc_hid_in_pair_data_t *)spi_info->data;
-
- // Check if we reply is pairing info.
- if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22)
- {
- bt_conn->type = jc->type;
-
- memcpy(bt_conn->mac, jc->mac, 6);
- memcpy(bt_conn->host_mac, pair_data->mac, 6);
- for (u32 i = 16; i > 0; i--)
- bt_conn->ltk[16 - i] = pair_data->ltk[i - 1];
- }
- }
- break;
- default:
- break;
- }
- jc->last_received_time = get_tmr_ms();
-}
-
-static void jc_parse_wired_init(joycon_ctxt_t *jc, const u8* data, u32 size)
-{
- switch (data[0])
- {
- case JC_WIRED_CMD_MAC:
- for (int i = 12; i > 6; i--)
- jc->mac[12 - i] = data[i];
- jc->type = data[6];
- jc->connected = true;
- default:
- break;
- }
-}
-
-static void jc_uart_pkt_parse(joycon_ctxt_t *jc, const u8* packet, size_t size)
-{
- jc_wired_hdr_t *pkt = (jc_wired_hdr_t *)packet;
- switch (pkt->cmd)
- {
- case JC_HORI_INPUT_RPT_CMD:
- case JC_WIRED_HID:
- jc_parse_wired_hid(jc, pkt->payload, (pkt->data[0] << 8) | pkt->data[1]);
- break;
- case JC_WIRED_INIT_REPLY:
- jc_parse_wired_init(jc, pkt->data, size - sizeof(jc_uart_hdr_t) - 1);
- break;
- default:
- break;
- }
-}
-
-static void jc_rcv_pkt(joycon_ctxt_t *jc)
-{
- if (gpio_read(GPIO_PORT_E, GPIO_PIN_6) && jc->uart == UART_C)
- return;
- else if (gpio_read(GPIO_PORT_H, GPIO_PIN_6) && jc->uart == UART_B)
- return;
-
- // Check if device stopped sending data.
- u32 uart_irq = uart_get_IIR(jc->uart);
- if (uart_irq != UART_IIR_REDI)
- return;
-
- u32 len = uart_recv(jc->uart, (u8 *)jc->buf, 0x100);
-
- // Check valid size and uart reply magic.
- if (len > 7 && !memcmp(jc->buf, "\x19\x81\x03", 3))
- {
- jc_wired_hdr_t *pkt = (jc_wired_hdr_t *)(jc->buf);
-
- jc_uart_pkt_parse(jc, jc->buf, pkt->uart_hdr.total_size_lsb + sizeof(jc_uart_hdr_t));
- }
-}
-
-static bool jc_send_init_rumble(joycon_ctxt_t *jc)
-{
- // Send init rumble or request nx pad status report.
- if ((jc_r.connected && !jc_r.rumble_sent) || (jc_l.connected && !jc_l.rumble_sent))
- {
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
-
- jc_send_hid_cmd(jc->uart, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0);
-
- if (jc_l.connected)
- jc_l.rumble_sent = true;
- if (jc_r.connected)
- jc_r.rumble_sent = true;
-
- if (jc->uart != UART_B)
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
- else
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
-
- return 1;
- }
-
- return 0;
-}
-
-static void jc_req_nx_pad_status(joycon_ctxt_t *jc)
-{
- bool is_nxpad = !(jc->type & JC_ID_HORI);
-
- if (is_nxpad)
- {
- bool sent_rumble = jc_send_init_rumble(jc);
-
- if (sent_rumble)
- return;
- }
-
- if (jc->last_status_req_time > get_tmr_ms() || !jc->connected)
- return;
-
- // Turn off Joy-Con detect.
- if (jc->uart == UART_B)
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
- else
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
-
- if (is_nxpad)
- joycon_send_raw(jc->uart, nx_pad_status, sizeof(nx_pad_status));
- else
- joycon_send_raw(jc->uart, hori_pad_status, sizeof(hori_pad_status));
-
- // Turn Joy-Con detect on.
- if (jc->uart == UART_B)
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
- else
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
-
- jc->last_status_req_time = get_tmr_ms() + 15;
-}
-
-static bool _jc_validate_pairing_info(u8 *buf, bool *is_hos)
-{
- u8 crc = 0;
- for (u32 i = 0; i < 0x22; i++)
- crc += buf[4 + i];
-
- crc += 0x68; // Host is Switch.
-
- if ((crc ^ 0x55) == buf[2])
- *is_hos = true;
-
- crc -= 0x68;
- crc += 0x08; // Host is PC.
-
- if (*is_hos || (crc ^ 0x55) == buf[2])
- return true;
-
- return false;
-}
-
-jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos)
-{
- u8 retries;
- jc_bt_conn_t *bt_conn;
-
- if (!jc_init_done)
- return NULL;
-
- bt_conn = &jc_gamepad.bt_conn_l;
- memset(bt_conn->host_mac, 0, 6);
- memset(bt_conn->ltk, 0, 16);
-
- bt_conn = &jc_gamepad.bt_conn_r;
- memset(bt_conn->host_mac, 0, 6);
- memset(bt_conn->ltk, 0, 16);
-
- while (jc_l.last_status_req_time > get_tmr_ms())
- {
- jc_rcv_pkt(&jc_r);
- jc_rcv_pkt(&jc_l);
- }
-
- jc_hid_in_spi_read_t subcmd_data_l;
- subcmd_data_l.addr = 0x2000;
- subcmd_data_l.size = 0x1A;
-
- jc_hid_in_spi_read_t subcmd_data_r;
- subcmd_data_r.addr = 0x2000;
- subcmd_data_r.size = 0x1A;
-
- // Turn off Joy-Con detect.
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
-
- bool jc_r_found = jc_r.connected ? false : true;
- bool jc_l_found = jc_l.connected ? false : true;
-
- u32 total_retries = 10;
-retry:
- retries = 10;
- while (retries)
- {
- u32 time_now = get_tmr_ms();
- if ((!jc_l_found && jc_l.last_status_req_time < time_now) || (!jc_r_found && jc_r.last_status_req_time < time_now))
- {
- if (!jc_l_found)
- {
- jc_send_hid_cmd(jc_l.uart, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_l, 5);
- jc_l.last_status_req_time = get_tmr_ms() + 15;
- }
-
- if (!jc_r_found)
- {
- jc_send_hid_cmd(jc_r.uart, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_r, 5);
- jc_r.last_status_req_time = get_tmr_ms() + 15;
- }
-
- retries--;
- }
-
- if (!jc_l_found)
- {
- memset(jc_l.buf, 0, 0x100);
- jc_rcv_pkt(&jc_l);
-
- bool is_hos = false;
- if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos))
- {
- bool is_active = jc_l.buf[SPI_READ_OFFSET] == 0x95;
-
- if (!is_active)
- subcmd_data_l.addr += 0x26; // Get next slot.
- else
- jc_l_found = true; // Entry is active.
-
- if (jc_l_found && is_hos)
- *is_l_hos = true;
- }
- }
-
- if (!jc_r_found)
- {
- memset(jc_r.buf, 0, 0x100);
- jc_rcv_pkt(&jc_r);
-
- bool is_hos = false;
- if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos))
- {
- bool is_active = jc_r.buf[SPI_READ_OFFSET] == 0x95;
-
- if (!is_active)
- subcmd_data_r.addr += 0x26; // Get next slot.
- else
- jc_r_found = true; // Entry is active.
-
- if (jc_r_found && is_hos)
- *is_r_hos = true;
- }
- }
-
- if (jc_l_found && jc_r_found)
- break;
- }
-
- if (!jc_l_found || !jc_r_found)
- {
- if (total_retries)
- {
- total_retries--;
- goto retry;
- }
-
- if (!jc_l_found)
- {
- bt_conn = &jc_gamepad.bt_conn_l;
- memset(bt_conn->host_mac, 0, 6);
- memset(bt_conn->ltk, 0, 16);
- }
-
- if (!jc_r_found)
- {
- bt_conn = &jc_gamepad.bt_conn_r;
- memset(bt_conn->host_mac, 0, 6);
- memset(bt_conn->ltk, 0, 16);
- }
- }
-
- // Turn Joy-Con detect on.
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
-
- return &jc_gamepad;
-}
-
-void jc_deinit()
-{
- // Disable power.
- jc_power_supply(UART_B, false);
- jc_power_supply(UART_C, false);
-
- // Turn off Joy-Con detect.
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
-
- // Send sleep command.
- u8 data = HCI_STATE_SLEEP;
-
- if (jc_r.connected && !(jc_r.type & JC_ID_HORI))
- {
- jc_send_hid_cmd(UART_B, JC_HID_SUBCMD_HCI_STATE, &data, 1);
- jc_rcv_pkt(&jc_r);
- }
- if (jc_l.connected && !(jc_l.type & JC_ID_HORI))
- {
- jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1);
- jc_rcv_pkt(&jc_l);
- }
-
- // Disable UART B and C clocks.
- clock_disable_uart(UART_B);
- clock_disable_uart(UART_C);
-}
-
-/* Drop current button state without power-cycling the Joy-Cons. */
-void jc_clear_input(void)
-{
- jc_gamepad.buttons = 0;
-}
-
-static void jc_init_conn(joycon_ctxt_t *jc)
-{
- if (((u32)get_tmr_ms() - jc->last_received_time) > 1000)
- {
- jc_power_supply(jc->uart, true);
-
- // Turn off Joy-Con detect.
- if (jc->uart == UART_B)
- {
- jc_gamepad.buttons &= ~JC_BTN_MASK_R;
- jc_gamepad.conn_r = false;
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
- }
- else
- {
- jc_gamepad.buttons &= ~JC_BTN_MASK_L;
- jc_gamepad.conn_l = false;
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
- }
-
- uart_init(jc->uart, 1000000);
- uart_invert(jc->uart, true, UART_INVERT_TXD);
- uart_set_IIR(jc->uart);
-
- joycon_send_raw(jc->uart, init_jc, 4);
- joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake));
-
- msleep(5);
- jc_rcv_pkt(jc);
-
- joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info));
- msleep(5);
- jc_rcv_pkt(jc);
-
- if (!(jc->type & JC_ID_HORI))
- {
- joycon_send_raw(jc->uart, init_finalize, sizeof(init_finalize));
- msleep(5);
- jc_rcv_pkt(jc);
- }
-
- // Turn Joy-Con detect on.
- if (jc->uart == UART_B)
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
- else
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
-
- jc->last_received_time = get_tmr_ms();
-
- if (jc->connected)
- jc_power_supply(jc->uart, false);
- }
-}
-
-static void jc_conn_check()
-{
- // Check if a Joy-Con was disconnected.
- if (gpio_read(GPIO_PORT_E, GPIO_PIN_6))
- {
- jc_power_supply(UART_C, false);
-
- hid_pkt_inc = 0;
-
- jc_l.connected = false;
- jc_l.rumble_sent = false;
-
- jc_gamepad.buttons &= ~JC_BTN_MASK_L;
- jc_gamepad.conn_l = false;
-
- jc_gamepad.batt_info_l = 0;
- jc_gamepad.bt_conn_l.type = 0;
- }
-
- if (gpio_read(GPIO_PORT_H, GPIO_PIN_6))
- {
- jc_power_supply(UART_B, false);
-
- hid_pkt_inc = 0;
-
- jc_r.connected = false;
- jc_r.rumble_sent = false;
-
- jc_gamepad.buttons &= ~JC_BTN_MASK_R;
- jc_gamepad.conn_r = false;
-
- jc_gamepad.batt_info_r = 0;
- jc_gamepad.bt_conn_r.type = 0;
- }
-}
-
-void jc_power_supply(u8 uart, bool enable)
-{
- if (enable)
- {
- if (regulator_5v_get_dev_enabled(1 << uart))
- return;
-
- regulator_5v_enable(1 << uart);
-
- if (jc_init_done)
- {
- if (uart == UART_C)
- gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
- else
- gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
- return;
- }
-
- if (uart == UART_C)
- {
- // Joy-Con(L) Charge Detect.
- PINMUX_AUX(PINMUX_AUX_SPDIF_IN) = PINMUX_PULL_DOWN | 1;
- gpio_config(GPIO_PORT_CC, GPIO_PIN_3, GPIO_MODE_GPIO);
- gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_3, GPIO_OUTPUT_ENABLE);
- gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
- }
- else
- {
- // Joy-Con(R) Charge Detect.
- PINMUX_AUX(PINMUX_AUX_GPIO_PK3) = PINMUX_DRIVE_4X | PINMUX_PULL_DOWN | 2;
- gpio_config(GPIO_PORT_K, GPIO_PIN_3, GPIO_MODE_GPIO);
- gpio_output_enable(GPIO_PORT_K, GPIO_PIN_3, GPIO_OUTPUT_ENABLE);
- gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
- }
- }
- else
- {
- if (!regulator_5v_get_dev_enabled(1 << uart))
- return;
-
- regulator_5v_disable(1 << uart);
-
- if (uart == UART_C)
- gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW);
- else
- gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_LOW);
- }
-}
-
-void jc_init_hw()
-{
- jc_l.uart = UART_C;
- jc_r.uart = UART_B;
-
-#if !defined(DEBUG_UART_PORT) || !(DEBUG_UART_PORT)
- if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG)
- return;
-
- jc_power_supply(UART_C, true);
- jc_power_supply(UART_B, true);
-
- // Joy-Con (R) IsAttached.
- PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
- gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO);
-
- // Joy-Con (L) IsAttached.
- PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
- gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
-
- // Configure pinmuxing for UART B and C.
- pinmux_config_uart(UART_B);
- pinmux_config_uart(UART_C);
-
- // Ease the stress to APB.
- bpmp_freq_t prev_fid = bpmp_clk_rate_set(BPMP_CLK_NORMAL);
-
- // Enable UART B and C clocks.
- clock_enable_uart(UART_B);
- clock_enable_uart(UART_C);
-
- // Restore OC.
- bpmp_clk_rate_set(prev_fid);
-
- // Turn Joy-Con detect on.
- gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
- gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
-
- jc_init_done = true;
-#endif
-}
-
-jc_gamepad_rpt_t *joycon_poll()
-{
- if (!jc_init_done)
- return NULL;
-
- if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6))
- jc_init_conn(&jc_r);
- if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6))
- jc_init_conn(&jc_l);
-
- if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6))
- jc_req_nx_pad_status(&jc_r);
- if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6))
- jc_req_nx_pad_status(&jc_l);
-
- if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6))
- jc_rcv_pkt(&jc_r);
- if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6))
- jc_rcv_pkt(&jc_l);
-
- jc_conn_check();
-
- return &jc_gamepad;
-}
+/*
+ * Joy-Con UART driver for Nintendo Switch
+ *
+ * Copyright (c) 2019-2026 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "joycon.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// For disabling driver when logging is enabled.
+#include
+
+#define JC_WIRED_SND_MAGIC "\x19\x01\x03" // Type, Destination, Group.
+#define JC_WIRED_RCV_MAGIC "\x19\x81\x03" // Type, Destination, Group.
+
+#define JC_WIRED_RPT_OUT 0x43
+#define JC_WIRED_RPT_IN 0x53
+#define JC_WIRED_CMD 0x91
+#define JC_WIRED_HID 0x92
+#define JC_WIRED_ECHO 0x93 // CMD OUT.
+#define JC_WIRED_INIT_REPLY 0x94
+#define JC_WIRED_HANDSHAKE 0xA5 // Enable Wired CMDs (JC_WIRED_CMD).
+
+#define JC_HORI_INPUT_RPT_CMD 0x9A
+#define JC_HORI_INPUT_RPT 0x00
+
+#define JC_WIRED_CMD_GET_INFO 0x01
+#define JC_WIRED_CMD_SET_CHARGER 0x02
+#define JC_WIRED_CMD_GET_CHARGER 0x03
+#define JC_WIRED_CMD_SET_ATTACH 0x04
+#define JC_WIRED_CMD_GET_ATTACH 0x05
+#define JC_WIRED_CMD_BATT_VOLT 0x06
+#define JC_WIRED_CMD_WAKE_REASON 0x07
+#define JC_WIRED_CMD_HID_CONN 0x10 // Enable HID CMDs (JC_WIRED_HID).
+#define JC_WIRED_CMD_HID_DISC 0x11
+#define JC_WIRED_CMD_SET_HIDRATE 0x12 // Output report rate.
+#define JC_WIRED_CMD_GET_HIDRATE 0x13
+#define JC_WIRED_CMD_SET_PAIRING 0x18 // Manual pairing.
+#define JC_WIRED_CMD_SET_BRATE 0x20
+#define JC_WIRED_CMD_ECHO_TEST 0x40
+
+#define JC_HID_OUTPUT_RPT 0x01
+#define JC_HID_RUMBLE_RPT 0x10
+
+#define JC_HID_INPUT_RPT 0x30
+#define JC_HID_SUBMCD_RPT 0x21
+
+#define JC_HID_SUBCMD_HCI_STATE 0x06
+#define HCI_STATE_SLEEP 0x00
+#define HCI_STATE_RECONNECT 0x01
+#define HCI_STATE_PAIR 0x02
+#define HCI_STATE_HOME 0x04
+#define JC_HID_SUBCMD_SPI_READ 0x10
+#define SPI_READ_OFFSET 0x20
+#define JC_HID_SUBCMD_RUMBLE_CTL 0x48
+#define JC_HID_SUBCMD_CHARGE_SET 0x51
+#define JC_HID_SUBCMD_SND_RUMBLE 0xFF // Custom.
+
+#define JC_SIO_OUTPUT_RPT 0x91
+#define JC_SIO_INPUT_RPT 0x92
+#define JC_SIO_CMD_ACK 0x80
+
+#define JC_SIO_CMD_INIT 0x01
+#define JC_SIO_CMD_UNK02 0x02
+#define JC_SIO_CMD_VER_RPT 0x03
+#define JC_SIO_CMD_UNK20 0x20 // JC_WIRED_CMD_SET_BRATE
+#define JC_SIO_CMD_UNK21 0x21
+#define JC_SIO_CMD_UNK22 0x22
+#define JC_SIO_CMD_UNK40 0x40
+#define JC_SIO_CMD_STATUS 0x41
+#define JC_SIO_CMD_IAP_VER 0x42
+
+
+#define JC_BTN_MASK_L 0xFF2900 // 0xFFE900: with charge status.
+#define JC_BTN_MASK_R 0x0056FF
+
+#define JC_ID_L 0x01 // Joycon (L). Mask for Hori (L).
+#define JC_ID_R 0x02 // Joycon (R). Mask for Hori (R).
+#define JC_ID_HORI 0x20 // Mask for Hori. Actual ids: 0x21, 0x22.
+
+#define JC_CRC8_POLY 0x8D
+
+enum
+{
+ JC_STATE_START = 0,
+ JC_STATE_HANDSHAKED = 1,
+ JC_STATE_INFO_PARSED = 2,
+ JC_STATE_BRATE_CHANGED = 3,
+ JC_STATE_HID_NO_CONN = 4,
+ JC_STATE_HID_CONN = 5,
+ JC_STATE_INIT_DONE = 6
+};
+
+enum
+{
+ JC_BATT_EMPTY = 0,
+ JC_BATT_CRIT = 1, // 3300 - 3599 mV.
+ JC_BATT_LOW = 2, // 3600 - 3759 mV.
+ JC_BATT_MID = 3, // 3760 - 3899 mV.
+ JC_BATT_FULL = 4 // 3900 - 4200 mV.
+};
+
+#define JC_CHRG_CFG_SUPL0 0x00 // 100 OFF.
+#define JC_CHRG_CFG_SUPL1 0x10 // EN2 OFF.
+#define JC_CHRG_CFG_100MA 0x04 // 100 ON.
+#define JC_CHRG_CFG_200MA 0x14 // EN2 ON.
+
+enum
+{
+ JC_CHRG_STATE_INIT = 0,
+ JC_CHRG_STATE_OFF = 1, // Regulator OFF.
+ JC_CHRG_STATE_SUPL = 2, // JC_CHRG_CFG_SUPL0.
+ JC_CHRG_STATE_SLOW = 3, // JC_CHRG_CFG_100MA.
+ JC_CHRG_STATE_FAST = 4, // JC_CHRG_CFG_200MA.
+};
+
+static const u8 _sio_init[] = {
+ JC_SIO_OUTPUT_RPT, JC_SIO_CMD_INIT,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x95
+};
+
+static const u8 _sio_set_rpt_version[] = {
+ JC_SIO_OUTPUT_RPT, JC_SIO_CMD_VER_RPT,
+ // old fw: 0x00, 0x0D (0.13). New 3.4.
+ // force_update_en: 0x01
+ 0x00, 0x00, 0x03, 0x04, 0x00, 0xDA
+};
+
+// Every 8ms.
+static const u8 _sio_pad_status[] = {
+ JC_SIO_OUTPUT_RPT, JC_SIO_CMD_STATUS,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0
+};
+
+static const u8 _jc_init_wake[] = {
+ 0xA1, 0xA2, 0xA3, 0xA4
+};
+
+static const u8 _jc_init_handshake[] = {
+ 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
+ JC_WIRED_HANDSHAKE, 0x02, // Cmd and Replay echo.
+ 0x01, 0x7E, 0x00, 0x00, 0x00 // Handshake magic.
+};
+
+static const u8 _jc_init_get_info[] = {
+ 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
+ JC_WIRED_CMD, JC_WIRED_CMD_GET_INFO, // Cmd and subcmd.
+ 0x00, 0x00, 0x00, 0x00, 0x24 // Hdr crc.
+};
+
+static const u8 _jc_init_switch_brate[] = {
+ 0x19, 0x01, 0x03, 0x0F, 0x00, // Uart header.
+ JC_WIRED_CMD, JC_WIRED_CMD_SET_BRATE, // Cmd and subcmd.
+ 0x08, 0x00, 0x00, 0xBD, 0xB1, // Subcmd data size, data crc and hdr crc.
+ // Baudrate 3 megabaud.
+ 0xC0, 0xC6, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 _jc_init_hid_disconnect[] = {
+ 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
+ JC_WIRED_CMD, JC_WIRED_CMD_HID_DISC, // Cmd and subcmd.
+ 0x00, 0x00, 0x00, 0x00, 0x0E // Hdr crc.
+};
+
+static const u8 _jc_init_set_hid_rate[] = {
+ 0x19, 0x01, 0x03, 0x0B, 0x00, // Uart header.
+ JC_WIRED_CMD, JC_WIRED_CMD_SET_HIDRATE, // Cmd and subcmd.
+ 0x04, 0x00, 0x00, 0x12, 0xA6, // Subcmd data size, data crc and hdr crc.
+ // Output report rate 15 ms. (5/10/15 supported).
+ 0x0F, 0x00, 0x00, 0x00
+
+ // 5 ms.
+ // 0x04, 0x00, 0x00, 0x0E, 0xD5,
+ // 0x05, 0x00, 0x00, 0x00
+};
+
+static const u8 _jc_init_hid_connect[] = {
+ 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
+ JC_WIRED_CMD, JC_WIRED_CMD_HID_CONN, // Cmd and subcmd.
+ 0x00, 0x00, 0x00, 0x00, 0x3D // Hdr crc.
+};
+
+static const u8 _jc_nx_pad_status[] = {
+ 0x19, 0x01, 0x03, 0x08, 0x00, // Uart header.
+ JC_WIRED_HID, 0x00, // Cmd.
+ 0x01, 0x00, 0x00, 0x69, 0x2D, // Subcmd data size, data crc and hdr crc.
+ // Contents do not matter.
+ 0x1F
+};
+
+static const u8 _jc_hori_pad_status[] = {
+ 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
+ JC_HORI_INPUT_RPT_CMD, 0x01, // Hori cmd and hori subcmd.
+ 0x00, 0x00, 0x00, 0x00, 0x48 // Hdr crc.
+};
+
+// code: Channel code. vibc: Vibration code.
+typedef struct
+{
+ u32 rsvd :20;
+ u32 code_hi:5;
+ u32 code_lo:5;
+ u32 fmt :2; // Must be 1.
+} jc_rumble_fmt1_t;
+
+typedef struct
+{
+ u32 rsvd :2;
+ u32 freq_hi:7;
+ u32 amp_hi :7;
+ u32 freq_lo:7;
+ u32 amp_lo :7;
+ u32 fmt :2; // Must be 1.
+} jc_rumble_fmt1_28bit_t;
+
+typedef struct
+{
+ u32 rdvd :10;
+ u32 vibc_hi:5;
+ u32 vibc_lo:5;
+ u32 code_hi:5;
+ u32 code_lo:5;
+ u32 fmt :2; // Must be 2.
+} jc_rumble_fmt2_t;
+
+typedef struct
+{
+ u32 is_high :1;
+ u32 freq :7;
+ u32 vibc_hi1:5;
+ u32 vibc_lo1:5;
+ u32 vibc_hi0:5; // vibc_lo0 if is_high.
+ u32 amp :7;
+ u32 fmt :2; // Must be 2.
+} jc_rumble_fmt2_14bit_t;
+
+typedef struct
+{
+ u32 vibc_hi1:5;
+ u32 vibc_lo1:5;
+ u32 vibc_hi0:5;
+ u32 vibc_lo0:5;
+ u32 code_hi :5;
+ u32 code_lo :5;
+ u32 fmt :2; // Must be 3.
+} jc_rumble_fmt3_t;
+
+typedef struct
+{
+ u32 is_high:1;
+ u32 is_7bit:1;
+ u32 is_fm :1;
+ u32 vibc_hi1:5;
+ u32 vibc_lo1:5;
+ u32 vibc_hi0:5;
+ u32 vibc_lo0:5;
+ u32 amfm :7;
+ u32 fmt :2; // Must be 1 (not 3).
+} jc_rumble_fmt3_7bit_t;
+
+typedef struct _jc_rumble_t
+{
+ union {
+ jc_rumble_fmt1_t fmt1;
+ jc_rumble_fmt1_28bit_t fmt1_28b;
+ jc_rumble_fmt3_7bit_t fmt3_7b;
+ jc_rumble_fmt2_t fmt2;
+ jc_rumble_fmt2_14bit_t fmt2_14b;
+ jc_rumble_fmt3_t fmt3;
+ u32 r32;
+ u8 r8[4];
+ };
+} __attribute__((packed)) jc_rumble_t;
+
+typedef struct _jc_uart_hdr_t
+{
+ u8 magic[3]; // Type, Destination, Group.
+ u16 total_size;
+} __attribute__((packed)) jc_uart_hdr_t;
+
+typedef struct _jc_wired_hdr_t
+{
+ jc_uart_hdr_t uart_hdr;
+ u8 cmd;
+ u8 subcmd;
+ u16 payload_size;
+ // As out, cmd exists query. For JC_WIRED_CMD_SET_PAIRING: 1-4 pairing step.
+ // As in, 0xF: exists. For JC_WIRED_CMD_SET_HIDRATE: 1 wrong rate.
+ u8 status;
+ u8 crc_payload;
+ u8 crc_hdr;
+ u8 payload[];
+} __attribute__((packed)) jc_wired_hdr_t;
+
+typedef struct _jc_hid_out_rpt_t
+{
+ u8 cmd;
+ u8 pkt_id;
+ jc_rumble_t rumble[2];
+ u8 subcmd;
+ u8 subcmd_data[];
+} __attribute__((packed)) jc_hid_out_rpt_t;
+
+typedef struct _jc_hid_in_rpt_t
+{
+ u8 cmd;
+ u8 pkt_id; // Latency timer. 5 ms lsb (every 4 x 1.25 ms).
+ u8 conn_info:4; // Connection detect.
+ u8 batt_info:4; // Power info.
+ u16 btn_right:12;
+ u16 btn_left:12;
+ u16 stick_left_x:12;
+ u16 stick_left_y:12;
+ u16 stick_right_x:12;
+ u16 stick_right_y:12;
+ u8 vib_decider; // right:4, left:4 (bit3 en, bit2-0 buffer avail).
+ u8 submcd_ack;
+ u8 subcmd;
+ u8 subcmd_data[];
+} __attribute__((packed)) jc_hid_in_rpt_t;
+
+typedef struct _jc_hid_out_spi_read_t
+{
+ u32 addr;
+ u8 size;
+} __attribute__((packed)) jc_hid_out_spi_read_t;
+
+typedef struct _jc_hid_in_spi_read_t
+{
+ u32 addr;
+ u8 size;
+ u8 data[];
+} __attribute__((packed)) jc_hid_in_spi_read_t;
+
+typedef struct _jc_hid_in_pair_data_t
+{
+ u8 magic;
+ u8 size;
+ u16 checksum;
+ u8 mac[6];
+ u8 ltk[16];
+ u8 pad0[10];
+ u8 bt_caps; // bit3: Secure conn supported host, bit5: Paired to TBFC supported host, bit6: iTBFC page supported
+ u8 pad1;
+} __attribute__((packed)) jc_hid_in_pair_data_t;
+
+typedef struct _jc_sio_out_rpt_t
+{
+ u8 cmd;
+ u8 subcmd;
+ u16 payload_size;
+ u8 data[2];
+ u8 crc_payload;
+ u8 crc_hdr;
+ u8 payload[];
+} __attribute__((packed)) jc_sio_out_rpt_t;
+
+typedef struct _jc_sio_in_rpt_t
+{
+ u8 cmd;
+ u8 subcmd;
+ u16 payload_size;
+ u8 status;
+ u8 unk;
+ u8 crc_payload;
+ u8 crc_hdr;
+ u8 payload[];
+} __attribute__((packed)) jc_sio_in_rpt_t;
+
+typedef struct _jc_hid_in_sixaxis_rpt_t
+{
+ s16 acc_x;
+ s16 acc_y;
+ s16 acc_z;
+ s16 gyr_x;
+ s16 gyr_y;
+ s16 gyr_z;
+} __attribute__((packed)) jc_hid_in_sixaxis_rpt_t;
+
+typedef struct _jc_sio_hid_in_rpt_t
+{
+ u8 type;
+ u8 pkt_id; // Latency timer.
+ u8 unk;
+ u16 btn_right:12;
+ u16 btn_left:12;
+ u16 stick_left_x:12;
+ u16 stick_left_y:12;
+ u16 stick_right_x:12;
+ u16 stick_right_y:12;
+ u8 sixaxis_rpt; // bit0-3: report num. bit4-7: imu type.
+ // Each report is 800 us?
+ jc_hid_in_sixaxis_rpt_t sixaxis[15];
+} __attribute__((packed)) jc_sio_hid_in_rpt_t;
+
+typedef struct _jc_dev_t
+{
+ u8 buf[0x100];
+ u8 uart;
+ u8 type;
+ u8 state;
+ u8 pkt_id;
+ bool detected;
+ bool connected;
+ bool sio_mode;
+ bool rumble_sent;
+ bool charger_req;
+ u32 last_received_time; // Reset with JC_WIRED_CMD_HID_CONN/JC_WIRED_HID. If exceeded HID mode disconnects.
+ u32 last_status_req_time;
+ u32 last_chrger_chk_time;
+ u8 mac[6];
+} jc_dev_t;
+
+static jc_dev_t jc_l = {0};
+static jc_dev_t jc_r = {0};
+
+static bool jc_init_done = false;
+
+static jc_gamepad_rpt_t jc_gamepad;
+
+static void _jc_rcv_pkt(jc_dev_t *jc);
+
+static u8 _jc_crc(const u8 *data, u16 len)
+{
+ u8 crc = 0;
+ for (u16 i = 0; i < len; i++)
+ {
+ crc ^= data[i];
+ for (u16 j = 0; j < 8; j++)
+ {
+ if ((crc & 0x80) != 0)
+ crc = (u8)((crc << 1) ^ JC_CRC8_POLY);
+ else
+ crc <<= 1;
+ }
+ }
+ return crc;
+}
+
+static void _jc_power_supply(u8 uart, bool enable)
+{
+ if (enable)
+ {
+ if (regulator_5v_get_dev_enabled(1 << uart))
+ return;
+
+ regulator_5v_enable(1 << uart);
+
+ if (jc_gamepad.sio_mode)
+ return;
+
+ if (jc_init_done)
+ {
+ if (uart == UART_C)
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
+ else
+ gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
+ return;
+ }
+
+ if (uart == UART_C)
+ {
+ // Joy-Con(L) Charge Enable.
+ PINMUX_AUX(PINMUX_AUX_SPDIF_IN) = PINMUX_PULL_DOWN | 1;
+ gpio_direction_output(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
+ }
+ else
+ {
+ // Joy-Con(R) Charge Enable.
+ PINMUX_AUX(PINMUX_AUX_GPIO_PK3) = PINMUX_DRIVE_4X | PINMUX_PULL_DOWN | 2;
+ gpio_direction_output(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
+ }
+ }
+ else
+ {
+ if (!regulator_5v_get_dev_enabled(1 << uart))
+ return;
+
+ regulator_5v_disable(1 << uart);
+
+ if (jc_gamepad.sio_mode)
+ return;
+
+ if (uart == UART_C)
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW);
+ else
+ gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_LOW);
+ }
+}
+
+static void _jc_rail_detect()
+{
+ // Turn on Joy-Con detect. (UARTB/C TX). UART CTS also if HW flow control and irq is enabled.
+ PINMUX_AUX(PINMUX_AUX_UART2_TX) = PINMUX_INPUT_ENABLE;
+ PINMUX_AUX(PINMUX_AUX_UART3_TX) = PINMUX_INPUT_ENABLE;
+ gpio_direction_input(GPIO_PORT_G, GPIO_PIN_0);
+ gpio_direction_input(GPIO_PORT_D, GPIO_PIN_1);
+ usleep(20);
+
+ //! HW BUG: Unlatch gpio buffer.
+ (void)gpio_read(GPIO_PORT_H, GPIO_PIN_6);
+ (void)gpio_read(GPIO_PORT_E, GPIO_PIN_6);
+
+ // Read H6/E6 which are shared with UART TX pins.
+ jc_r.detected = !gpio_read(GPIO_PORT_H, GPIO_PIN_6);
+ jc_l.detected = !gpio_read(GPIO_PORT_E, GPIO_PIN_6);
+
+ // Turn off Joy-Con detect. (UARTB/C TX).
+ PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0;
+ PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0;
+ gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
+ gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
+ usleep(20);
+}
+
+static void _jc_conn_check()
+{
+ if (jc_gamepad.sio_mode)
+ {
+ //! TODO: Is there a way to detect a broken Sio?
+ jc_l.detected = true;
+
+ return;
+ }
+
+ _jc_rail_detect();
+
+ // Check if a Joy-Con was disconnected.
+ if (!jc_l.detected)
+ {
+ jc_l.pkt_id = 0;
+
+ jc_l.connected = false;
+ jc_l.rumble_sent = false;
+ jc_l.charger_req = false;
+ jc_l.last_received_time = 0;
+
+ jc_gamepad.conn_l = false;
+
+ jc_gamepad.batt_info_l = 0;
+ jc_gamepad.batt_chrg_l = 0;
+ jc_gamepad.bt_conn_l.type = 0;
+ jc_gamepad.buttons &= ~JC_BTN_MASK_L;
+ }
+
+ if (!jc_r.detected)
+ {
+ jc_r.pkt_id = 0;
+
+ jc_r.connected = false;
+ jc_r.rumble_sent = false;
+ jc_r.charger_req = false;
+ jc_r.last_received_time = 0;
+
+ jc_gamepad.conn_r = false;
+
+ jc_gamepad.batt_info_r = 0;
+ jc_gamepad.batt_chrg_r = 0;
+ jc_gamepad.bt_conn_r.type = 0;
+ jc_gamepad.buttons &= ~JC_BTN_MASK_R;
+ }
+}
+
+static void _joycon_send_raw(u8 uart_port, const u8 *buf, u16 size)
+{
+ uart_send(uart_port, buf, size);
+ uart_wait_xfer(uart_port, UART_TX_IDLE);
+}
+
+static u16 _jc_packet_add_uart_hdr(jc_wired_hdr_t *rpt, u8 cmd, u8 subcmd, u16 payload_size, bool crc)
+{
+ memcpy(rpt->uart_hdr.magic, JC_WIRED_SND_MAGIC, sizeof(rpt->uart_hdr.magic));
+
+ rpt->uart_hdr.total_size = sizeof(jc_wired_hdr_t) - sizeof(jc_uart_hdr_t) + payload_size;
+ rpt->cmd = cmd;
+ rpt->subcmd = subcmd;
+ rpt->payload_size = payload_size; // Only used if JC_WIRED_CMD.
+
+ // Only calculated if JC_WIRED_CMD/JC_WIRED_HID and result never checked?
+ // if (payload_size)
+ // rpt->crc_payload = crc ? _jc_crc(rpt->payload, payload_size) : 0;
+
+ // Only calculated if JC_WIRED_CMD/JC_WIRED_HID/JC_WIRED_HANDSHAKE and result only checked on HORI.
+ rpt->crc_hdr = crc ? _jc_crc(&rpt->cmd, sizeof(jc_wired_hdr_t) - sizeof(jc_uart_hdr_t) - 1) : 0;
+
+ return (rpt->uart_hdr.total_size + sizeof(jc_uart_hdr_t));
+}
+
+static u16 _jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, const u8 *payload, u16 payload_size, bool crc)
+{
+ if (payload)
+ memcpy(rpt->payload, payload, payload_size);
+
+ return _jc_packet_add_uart_hdr(rpt, JC_WIRED_HID, 0, payload_size, crc);
+}
+
+static void _jc_send_hid_output_rpt(jc_dev_t *jc, jc_hid_out_rpt_t *hid_pkt, u16 size, bool crc)
+{
+ u8 rpt[0x50];
+ memset(rpt, 0, sizeof(rpt));
+
+ hid_pkt->pkt_id = (jc->pkt_id++ & 0xF);
+ u32 rpt_size = _jc_hid_output_rpt_craft((jc_wired_hdr_t *)rpt, (u8 *)hid_pkt, size, crc);
+
+ _joycon_send_raw(jc->uart, rpt, rpt_size);
+}
+
+static void _jc_send_hid_cmd(jc_dev_t *jc, u8 subcmd, const u8 *data, u16 size)
+{
+ static const u8 rumble_mute[8] = { 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40 };
+ static const u8 rumble_init[8] = { 0xc2, 0xc8, 0x03, 0x72, 0xc2, 0xc8, 0x03, 0x72 };
+
+ u8 temp[0x30] = {0};
+
+ jc_hid_out_rpt_t *hid_pkt = (jc_hid_out_rpt_t *)temp;
+ memcpy(hid_pkt->rumble, rumble_mute, sizeof(rumble_mute));
+
+ if (subcmd == JC_HID_SUBCMD_SND_RUMBLE)
+ {
+ bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent;
+ bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent;
+
+ // Enable rumble.
+ hid_pkt->cmd = JC_HID_OUTPUT_RPT;
+ hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL;
+ hid_pkt->subcmd_data[0] = 1;
+ if (send_r_rumble)
+ _jc_send_hid_output_rpt(&jc_r, hid_pkt, 0x10, false);
+ if (send_l_rumble)
+ _jc_send_hid_output_rpt(&jc_l, hid_pkt, 0x10, false);
+
+ msleep(15);
+
+ if (send_r_rumble)
+ _jc_rcv_pkt(&jc_r);
+ if (send_l_rumble)
+ _jc_rcv_pkt(&jc_l);
+
+ // Send rumble.
+ hid_pkt->cmd = JC_HID_RUMBLE_RPT;
+ memcpy(hid_pkt->rumble, rumble_init, sizeof(rumble_init));
+ if (send_r_rumble)
+ _jc_send_hid_output_rpt(&jc_r, hid_pkt, 10, false);
+ if (send_l_rumble)
+ _jc_send_hid_output_rpt(&jc_l, hid_pkt, 10, false);
+
+ msleep(15);
+
+ // Mute rumble.
+ hid_pkt->cmd = JC_HID_RUMBLE_RPT;
+ memcpy(hid_pkt->rumble, rumble_mute, sizeof(rumble_mute));
+ if (send_r_rumble)
+ _jc_send_hid_output_rpt(&jc_r, hid_pkt, 10, false);
+ if (send_l_rumble)
+ _jc_send_hid_output_rpt(&jc_l, hid_pkt, 10, false);
+ }
+ else
+ {
+ bool crc_needed = jc->type & JC_ID_HORI;
+
+ hid_pkt->cmd = JC_HID_OUTPUT_RPT;
+ hid_pkt->subcmd = subcmd;
+ if (data)
+ memcpy(hid_pkt->subcmd_data, data, size);
+
+ _jc_send_hid_output_rpt(jc, hid_pkt, sizeof(jc_hid_out_rpt_t) + size, crc_needed);
+ }
+}
+
+static void _jc_parse_input(jc_dev_t *jc, const jc_hid_in_rpt_t *hid_pkt)
+{
+ u32 btn_tmp;
+ btn_tmp = hid_pkt->btn_right | hid_pkt->btn_left << 12;
+
+ if (jc->type & JC_ID_L)
+ {
+ jc_gamepad.buttons &= ~JC_BTN_MASK_L;
+ jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_L);
+
+ jc_gamepad.lstick_x = hid_pkt->stick_left_x;
+ jc_gamepad.lstick_y = hid_pkt->stick_left_y;
+
+ jc_gamepad.batt_info_l = hid_pkt->batt_info;
+ }
+ else if (jc->type & JC_ID_R)
+ {
+ jc_gamepad.buttons &= ~JC_BTN_MASK_R;
+ jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_R);
+
+ jc_gamepad.rstick_x = hid_pkt->stick_right_x;
+ jc_gamepad.rstick_y = hid_pkt->stick_right_y;
+
+ jc_gamepad.batt_info_r = hid_pkt->batt_info;
+ }
+}
+
+static void _jc_parse_wired_hid(jc_dev_t *jc, const u8 *packet, int size)
+{
+ const jc_hid_in_rpt_t *hid_pkt = (jc_hid_in_rpt_t *)packet;
+
+ switch (hid_pkt->cmd)
+ {
+ case JC_HORI_INPUT_RPT:
+ if (!(jc->type & JC_ID_HORI))
+ return;
+
+ case JC_HID_INPUT_RPT:
+ // Discard incomplete hid packets.
+ if (size < 12)
+ break;
+
+ _jc_parse_input(jc, hid_pkt);
+
+ jc_gamepad.conn_l = jc_l.connected;
+ jc_gamepad.conn_r = jc_r.connected;
+ break;
+
+ case JC_HID_SUBMCD_RPT:
+ if (hid_pkt->subcmd == JC_HID_SUBCMD_SPI_READ)
+ {
+ jc_bt_conn_t *bt_conn;
+
+ if (jc->type & JC_ID_L)
+ bt_conn = &jc_gamepad.bt_conn_l;
+ else
+ bt_conn = &jc_gamepad.bt_conn_r;
+
+ jc_hid_in_spi_read_t *spi_info = (jc_hid_in_spi_read_t *)hid_pkt->subcmd_data;
+ jc_hid_in_pair_data_t *pair_data = (jc_hid_in_pair_data_t *)spi_info->data;
+
+ // Check if the reply is pairing info.
+ if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22)
+ {
+ bt_conn->type = jc->type;
+
+ memcpy(bt_conn->mac, jc->mac, 6);
+ memcpy(bt_conn->host_mac, pair_data->mac, 6);
+ for (u32 i = 16; i > 0; i--)
+ bt_conn->ltk[16 - i] = pair_data->ltk[i - 1];
+ }
+ }
+ else if (hid_pkt->subcmd == JC_HID_SUBCMD_CHARGE_SET)
+ jc->charger_req = false;
+
+ _jc_parse_input(jc, hid_pkt);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void _jc_parse_wired_init(jc_dev_t *jc, const jc_wired_hdr_t *pkt, int size)
+{
+ // Discard empty packets.
+ if (size <= 0)
+ return;
+
+ const u8 *payload = pkt->payload;
+
+ switch (pkt->subcmd)
+ {
+ case JC_WIRED_CMD_GET_INFO:
+ if (!pkt->status)
+ {
+ for (int i = 6; i > 0; i--)
+ jc->mac[6 - i] = payload[i];
+ jc->type = payload[0];
+ jc->state = JC_STATE_INFO_PARSED;
+ }
+ break;
+
+ case JC_WIRED_CMD_SET_BRATE:
+ if (!pkt->status)
+ jc->state = JC_STATE_BRATE_CHANGED;
+ break;
+
+ case JC_WIRED_CMD_HID_DISC:
+ if (pkt->status == 0xF)
+ jc->state = JC_STATE_HID_NO_CONN;
+ break;
+
+ case JC_WIRED_CMD_HID_CONN:
+ if (!pkt->status)
+ jc->state = JC_STATE_HID_CONN;
+ break;
+
+ case JC_WIRED_CMD_SET_HIDRATE:
+ jc->state = JC_STATE_INIT_DONE;
+ jc->connected = true;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void _jc_uart_pkt_parse(jc_dev_t *jc, const jc_wired_hdr_t *pkt, int size)
+{
+ switch (pkt->cmd)
+ {
+ case JC_HORI_INPUT_RPT_CMD:
+ case JC_WIRED_HID:
+ _jc_parse_wired_hid(jc, pkt->payload, size - sizeof(jc_wired_hdr_t));
+ break;
+
+ case JC_WIRED_INIT_REPLY:
+ _jc_parse_wired_init(jc, pkt, size);
+ break;
+
+ case JC_WIRED_HANDSHAKE:
+ jc->state = JC_STATE_HANDSHAKED;
+ break;
+
+ default:
+ break;
+ }
+
+ jc->last_received_time = get_tmr_ms();
+}
+
+static void _jc_sio_parse_payload(jc_dev_t *jc, u8 cmd, const u8 *payload, int size)
+{
+ switch (cmd)
+ {
+ case JC_SIO_CMD_STATUS:
+ // Discard incomplete packets.
+ if (size < 12)
+ break;
+
+ jc_sio_hid_in_rpt_t *hid_pkt = (jc_sio_hid_in_rpt_t *)payload;
+ jc_gamepad.buttons = hid_pkt->btn_right | hid_pkt->btn_left << 12;
+ jc_gamepad.home = !gpio_read(GPIO_PORT_V, GPIO_PIN_3);
+
+ jc_gamepad.lstick_x = hid_pkt->stick_left_x;
+ jc_gamepad.lstick_y = hid_pkt->stick_left_y;
+ jc_gamepad.rstick_x = hid_pkt->stick_right_x;
+ jc_gamepad.rstick_y = hid_pkt->stick_right_y;
+
+ jc_gamepad.batt_info_l = jc_l.connected;
+ jc_gamepad.batt_info_r = gpio_read(GPIO_PORT_E, GPIO_PIN_7); // Set IRQ status.
+
+ jc_gamepad.conn_l = jc_l.connected;
+ jc_gamepad.conn_r = jc_l.connected;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void _jc_sio_uart_pkt_parse(jc_dev_t *jc, const jc_sio_in_rpt_t *pkt, int size)
+{
+ if (pkt->crc_hdr != _jc_crc((u8 *)pkt, sizeof(jc_sio_in_rpt_t) - 1))
+ return;
+
+ u8 cmd = pkt->subcmd & (~JC_SIO_CMD_ACK);
+ switch (cmd)
+ {
+ case JC_SIO_CMD_INIT:
+ if (!pkt->status)
+ jc->state = JC_STATE_HANDSHAKED;
+ break;
+
+ case JC_SIO_CMD_VER_RPT:
+ if (!pkt->status)
+ jc->state = JC_STATE_HID_CONN;
+ break;
+
+ case JC_SIO_CMD_IAP_VER:
+ case JC_SIO_CMD_STATUS:
+ _jc_sio_parse_payload(jc, cmd, pkt->payload, size - sizeof(jc_sio_in_rpt_t));
+ break;
+
+ case JC_SIO_CMD_UNK02:
+ case JC_SIO_CMD_UNK20:
+ case JC_SIO_CMD_UNK21:
+ case JC_SIO_CMD_UNK22:
+ case JC_SIO_CMD_UNK40:
+ default:
+ break;
+ }
+
+ jc->last_received_time = get_tmr_ms();
+}
+
+static void _jc_rcv_pkt(jc_dev_t *jc)
+{
+ if (!jc->detected)
+ return;
+
+ u32 len = uart_recv(jc->uart, (u8 *)jc->buf, sizeof(jc->buf));
+ if (len < 8)
+ return;
+
+ // For Joycon, check uart reply magic.
+ jc_wired_hdr_t *jc_pkt = (jc_wired_hdr_t *)jc->buf;
+ if (!jc->sio_mode && !memcmp(jc_pkt->uart_hdr.magic, JC_WIRED_RCV_MAGIC, sizeof(jc_pkt->uart_hdr.magic)))
+ {
+ _jc_uart_pkt_parse(jc, jc_pkt, len);
+
+ return;
+ }
+
+ // For Sio, check uart output report and command ack.
+ jc_sio_in_rpt_t *sio_pkt = (jc_sio_in_rpt_t *)(jc->buf);
+ if (jc->sio_mode && sio_pkt->cmd == JC_SIO_INPUT_RPT && (sio_pkt->subcmd & JC_SIO_CMD_ACK) == JC_SIO_CMD_ACK)
+ {
+ _jc_sio_uart_pkt_parse(jc, sio_pkt, len);
+
+ return;
+ }
+}
+
+static bool _jc_handle_charging(jc_dev_t *jc)
+{
+ if (jc->last_chrger_chk_time > get_tmr_ms())
+ return false;
+
+ jc->last_chrger_chk_time = get_tmr_ms() + 5000;
+
+ u8 old_mode, batt_now;
+ if (jc->uart == UART_B)
+ {
+ batt_now = jc_gamepad.batt_info_r;
+ old_mode = jc_gamepad.batt_chrg_r;
+ }
+ else
+ {
+ batt_now = jc_gamepad.batt_info_l;
+ old_mode = jc_gamepad.batt_chrg_l;
+ }
+
+ // Let battery level settle if init.
+ u8 new_mode = old_mode;
+ if (!old_mode)
+ {
+ new_mode = JC_CHRG_STATE_SUPL;
+ if ((batt_now >> 1) <= JC_BATT_LOW)
+ new_mode = JC_CHRG_STATE_SLOW;
+
+ goto set_mode;
+ }
+
+ // Resend if no reply.
+ if (jc->charger_req)
+ goto set_mode;
+
+ // Power supply control based on battery levels and charging.
+ switch (batt_now >> 1)
+ {
+ case JC_BATT_EMPTY:
+ case JC_BATT_CRIT:
+ case JC_BATT_LOW:
+ new_mode = JC_CHRG_STATE_SLOW;
+ break;
+
+ case JC_BATT_MID:
+ if (!(batt_now & 1))
+ new_mode = JC_CHRG_STATE_SUPL;
+ break;
+
+ case JC_BATT_FULL:
+ new_mode = JC_CHRG_STATE_OFF;
+ break;
+ }
+
+ // Check if already configured.
+ if (new_mode == old_mode)
+ return false;
+
+set_mode:
+ if (jc->uart == UART_B)
+ jc_gamepad.batt_chrg_r = new_mode;
+ else
+ jc_gamepad.batt_chrg_l = new_mode;
+
+ switch (new_mode)
+ {
+ case JC_CHRG_STATE_OFF:
+ _jc_power_supply(jc->uart, false);
+ new_mode = JC_CHRG_CFG_SUPL0;
+ break;
+ case JC_CHRG_STATE_SUPL:
+ _jc_power_supply(jc->uart, true);
+ new_mode = JC_CHRG_CFG_SUPL0;
+ break;
+ case JC_CHRG_STATE_SLOW:
+ _jc_power_supply(jc->uart, true);
+ new_mode = JC_CHRG_CFG_100MA;
+ break;
+ }
+
+ _jc_send_hid_cmd(jc, JC_HID_SUBCMD_CHARGE_SET, (u8 *)&new_mode, sizeof(new_mode));
+ jc->charger_req = true;
+
+ jc->last_status_req_time = get_tmr_ms() + 15;
+
+ return true;
+}
+
+static bool _jc_send_enable_rumble(jc_dev_t *jc)
+{
+ bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent;
+ bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent;
+
+ // Do not sent report yet if second Joy-Con is expected to be initialized.
+ if ((send_r_rumble && !jc_l.rumble_sent && jc_l.state == JC_STATE_HID_CONN) ||
+ (send_l_rumble && !jc_r.rumble_sent && jc_r.state == JC_STATE_HID_CONN))
+ return 1;
+
+ // Send init rumble or request nx pad status report.
+ if (send_r_rumble || send_l_rumble)
+ {
+ _jc_send_hid_cmd(jc, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0);
+
+ if (jc_l.connected)
+ jc_l.rumble_sent = true;
+ if (jc_r.connected)
+ jc_r.rumble_sent = true;
+
+ jc->last_chrger_chk_time = get_tmr_ms() + 5000;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void _jc_req_status(jc_dev_t *jc)
+{
+ if (!jc->connected)
+ return;
+
+ if (jc->last_status_req_time > get_tmr_ms())
+ return;
+
+ bool is_nxpad = !(jc->type & JC_ID_HORI) && !jc->sio_mode;
+
+ // Init/maintenance for Joy-Con.
+ if (is_nxpad)
+ {
+ if (_jc_send_enable_rumble(jc))
+ return;
+
+ if (_jc_handle_charging(jc))
+ return;
+ }
+
+ if (is_nxpad)
+ _joycon_send_raw(jc->uart, _jc_nx_pad_status, sizeof(_jc_nx_pad_status));
+ else if (jc->sio_mode)
+ _joycon_send_raw(jc->uart, _sio_pad_status, sizeof(_sio_pad_status));
+ else
+ _joycon_send_raw(jc->uart, _jc_hori_pad_status, sizeof(_jc_hori_pad_status));
+
+ jc->last_status_req_time = get_tmr_ms() + (!jc->sio_mode ? 15 : 7);
+}
+
+static bool _jc_validate_pairing_info(const u8 *buf, bool *is_hos)
+{
+ u8 crc = 0;
+ for (u32 i = 0; i < 0x22; i++)
+ crc += buf[4 + i];
+
+ crc += 0x68; // Host is Switch.
+
+ if ((crc ^ 0x55) == buf[2])
+ *is_hos = true;
+
+ crc -= 0x68;
+ crc += 0x08; // Host is PC.
+
+ if (*is_hos || (crc ^ 0x55) == buf[2])
+ return true;
+
+ return false;
+}
+
+jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos)
+{
+ u8 retries;
+ jc_bt_conn_t *bt_conn;
+
+ if (!jc_init_done || jc_gamepad.sio_mode)
+ return NULL;
+
+ // Detect and init if needed.
+ for (u32 i = 0; i < 6; i++)
+ {
+ joycon_poll();
+ msleep(15);
+ }
+
+ while ((jc_l.last_status_req_time + 15) > get_tmr_ms())
+ {
+ _jc_rcv_pkt(&jc_r);
+ _jc_rcv_pkt(&jc_l);
+ }
+
+ bt_conn = &jc_gamepad.bt_conn_l;
+ memset(bt_conn->host_mac, 0, 6);
+ memset(bt_conn->ltk, 0, 16);
+
+ bt_conn = &jc_gamepad.bt_conn_r;
+ memset(bt_conn->host_mac, 0, 6);
+ memset(bt_conn->ltk, 0, 16);
+
+ // Setup initial SPI address.
+ jc_hid_out_spi_read_t subcmd_data_l;
+ subcmd_data_l.addr = 0x2000;
+ subcmd_data_l.size = 0x1A;
+
+ jc_hid_out_spi_read_t subcmd_data_r;
+ subcmd_data_r.addr = 0x2000;
+ subcmd_data_r.size = 0x1A;
+
+ bool jc_r_found = jc_r.connected ? false : true;
+ bool jc_l_found = jc_l.connected ? false : true;
+
+ // Set mode to HW controlled RTS.
+ uart_set_mode(jc_l.uart, UART_AO_TX_HW_RX);
+ uart_set_mode(jc_r.uart, UART_AO_TX_HW_RX);
+
+ u32 total_retries = 10;
+retry:
+ retries = 10;
+ while (retries)
+ {
+ u32 time_now = get_tmr_ms();
+ if ((!jc_l_found && jc_l.last_status_req_time < time_now) || (!jc_r_found && jc_r.last_status_req_time < time_now))
+ {
+ if (!jc_l_found)
+ {
+ _jc_send_hid_cmd(&jc_l, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_l, sizeof(jc_hid_out_spi_read_t));
+ jc_l.last_status_req_time = get_tmr_ms() + 15;
+ }
+
+ if (!jc_r_found)
+ {
+ _jc_send_hid_cmd(&jc_r, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_r, sizeof(jc_hid_out_spi_read_t));
+ jc_r.last_status_req_time = get_tmr_ms() + 15;
+ }
+
+ retries--;
+ }
+
+ // Wait for the first 36 bytes to arrive.
+ msleep(5);
+
+ if (!jc_l_found)
+ {
+ memset(jc_l.buf, 0, 0x100);
+ _jc_rcv_pkt(&jc_l);
+
+ bool is_hos = false;
+ if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos))
+ {
+ bool is_active = jc_l.buf[SPI_READ_OFFSET] == 0x95;
+
+ if (!is_active)
+ subcmd_data_l.addr += sizeof(jc_hid_in_pair_data_t); // Get next slot.
+ else
+ jc_l_found = true; // Entry is active.
+
+ if (jc_l_found && is_hos)
+ *is_l_hos = true;
+ }
+ }
+
+ if (!jc_r_found)
+ {
+ memset(jc_r.buf, 0, 0x100);
+ _jc_rcv_pkt(&jc_r);
+
+ bool is_hos = false;
+ if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos))
+ {
+ bool is_active = jc_r.buf[SPI_READ_OFFSET] == 0x95;
+
+ if (!is_active)
+ subcmd_data_r.addr += sizeof(jc_hid_in_pair_data_t); // Get next slot.
+ else
+ jc_r_found = true; // Entry is active.
+
+ if (jc_r_found && is_hos)
+ *is_r_hos = true;
+ }
+ }
+
+ if (jc_l_found && jc_r_found)
+ break;
+ }
+
+ if (!jc_l_found || !jc_r_found)
+ {
+ if (total_retries)
+ {
+ total_retries--;
+ goto retry;
+ }
+
+ if (!jc_l_found)
+ {
+ bt_conn = &jc_gamepad.bt_conn_l;
+ memset(bt_conn->host_mac, 0, 6);
+ memset(bt_conn->ltk, 0, 16);
+ }
+
+ if (!jc_r_found)
+ {
+ bt_conn = &jc_gamepad.bt_conn_r;
+ memset(bt_conn->host_mac, 0, 6);
+ memset(bt_conn->ltk, 0, 16);
+ }
+ }
+
+ // Restore mode to manual RTS.
+ uart_set_mode(jc_l.uart, UART_AO_TX_MN_RX);
+ uart_set_mode(jc_r.uart, UART_AO_TX_MN_RX);
+
+ return &jc_gamepad;
+}
+
+static void _jc_conn_init(jc_dev_t *jc)
+{
+ if (!jc->detected)
+ return;
+
+ /*
+ * Try to reinit if no input report.
+ * Actual connection timeout is 2000ms on official Joycon.
+ * Based on last JC_WIRED_CMD_HID_CONN/JC_WIRED_HID sent.
+ */
+ if (((u32)get_tmr_ms() - jc->last_received_time) > 1800)
+ {
+ if (!jc->sio_mode)
+ _jc_power_supply(jc->uart, true);
+
+ // Mask out buttons and set connected to false.
+ if (jc->uart == UART_B)
+ {
+ jc_gamepad.batt_chrg_r = 0;
+ jc_gamepad.buttons &= ~JC_BTN_MASK_R;
+ jc_gamepad.conn_r = false;
+ }
+ else
+ {
+ jc_gamepad.batt_chrg_l = 0;
+ jc_gamepad.buttons &= ~JC_BTN_MASK_L;
+ jc_gamepad.conn_l = false;
+ }
+
+ // Initialize uart to 1 megabaud and manual RTS.
+ uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX);
+
+ jc->state = JC_STATE_START;
+
+ if (!jc->sio_mode)
+ {
+ jc_gamepad.buttons = 0;
+
+ // Set TX and RTS inversion for Joycon.
+ uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
+
+ // Initialize.
+ u32 retries = 10;
+ while (retries && jc->state != JC_STATE_HANDSHAKED)
+ {
+ // Wake up the controller.
+ _joycon_send_raw(jc->uart, _jc_init_wake, sizeof(_jc_init_wake));
+ _jc_rcv_pkt(jc); // Clear RX FIFO.
+
+ // Do a handshake.
+ _joycon_send_raw(jc->uart, _jc_init_handshake, sizeof(_jc_init_handshake));
+ msleep(4);
+ _jc_rcv_pkt(jc);
+ retries--;
+ }
+
+ if (jc->state != JC_STATE_HANDSHAKED)
+ goto out;
+
+ // Get info about the controller.
+ _joycon_send_raw(jc->uart, _jc_init_get_info, sizeof(_jc_init_get_info));
+ msleep(2);
+ _jc_rcv_pkt(jc);
+
+ if (jc->state != JC_STATE_INFO_PARSED)
+ goto out;
+
+ if (!(jc->type & JC_ID_HORI))
+ {
+ // Request 3 megabaud change.
+ _joycon_send_raw(jc->uart, _jc_init_switch_brate, sizeof(_jc_init_switch_brate));
+ msleep(2);
+ _jc_rcv_pkt(jc);
+
+ if (jc->state == JC_STATE_BRATE_CHANGED)
+ {
+ // Reinitialize uart to 3 megabaud and manual RTS.
+ uart_init(jc->uart, 3000000, UART_AO_TX_MN_RX);
+ uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
+
+ // Make sure HID is disconnected and check connection. Reply expected after 30ms.
+ retries = 10;
+ while (retries && jc->state != JC_STATE_HID_NO_CONN)
+ {
+ _joycon_send_raw(jc->uart, _jc_init_hid_disconnect, sizeof(_jc_init_hid_disconnect));
+ msleep(5);
+ _jc_rcv_pkt(jc);
+ retries--;
+ }
+
+ // Was connected before or no response. Do a reinit.
+ if (jc->state != JC_STATE_HID_NO_CONN)
+ goto out;
+ }
+
+ // Create HID connection with the new rate.
+ _joycon_send_raw(jc->uart, _jc_init_hid_connect, sizeof(_jc_init_hid_connect));
+ msleep(2);
+ _jc_rcv_pkt(jc);
+
+ if (jc->state != JC_STATE_HID_CONN)
+ goto out;
+
+ // Set hid packet rate.
+ _joycon_send_raw(jc->uart, _jc_init_set_hid_rate, sizeof(_jc_init_set_hid_rate));
+ msleep(2);
+ _jc_rcv_pkt(jc);
+
+ goto out; // Wait for set hid rate reply.
+ }
+ else // Hori. Unset RTS inversion.
+ uart_invert(jc->uart, false, UART_INVERT_RTS);
+ }
+ else
+ {
+ // Set Sio NPOR low to configure BOOT0 mode.
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
+ usleep(300);
+ gpio_write(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_HIGH);
+ msleep(100);
+
+ // Clear RX FIFO.
+ _jc_rcv_pkt(jc);
+
+ // Initialize the controller.
+ u32 retries = 10;
+ while (retries && jc->state != JC_STATE_HANDSHAKED)
+ {
+ _joycon_send_raw(jc->uart, _sio_init, sizeof(_sio_init));
+ msleep(5);
+ _jc_rcv_pkt(jc);
+ retries--;
+ }
+
+ if (jc->state != JC_STATE_HANDSHAKED)
+ goto out;
+
+ // Set output report version.
+ retries = 10;
+ while (retries && jc->state != JC_STATE_HID_CONN)
+ {
+ _joycon_send_raw(jc->uart, _sio_set_rpt_version, sizeof(_sio_set_rpt_version));
+ msleep(5);
+ _jc_rcv_pkt(jc);
+ retries--;
+ }
+
+ if (jc->state != JC_STATE_HID_CONN)
+ goto out;
+ }
+
+ // Initialization done.
+ jc->state = JC_STATE_INIT_DONE;
+ jc->connected = true;
+
+out:
+ jc->last_received_time = get_tmr_ms();
+ }
+}
+
+void jc_init_hw()
+{
+ jc_l.uart = UART_C;
+ jc_r.uart = UART_B;
+
+ jc_l.sio_mode = fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG;
+ jc_gamepad.sio_mode = jc_l.sio_mode;
+
+#if !defined(DEBUG_UART_PORT) || !(DEBUG_UART_PORT)
+ // Sio Initialization.
+ if (jc_gamepad.sio_mode)
+ {
+ // Enable 4 MHz clock to Sio.
+ clock_enable_extperiph2();
+ PINMUX_AUX(PINMUX_AUX_TOUCH_CLK) = PINMUX_PULL_DOWN;
+
+ // Configure Sio HOME BUTTON.
+ PINMUX_AUX(PINMUX_AUX_LCD_GPIO1) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 1;
+ gpio_direction_input(GPIO_PORT_V, GPIO_PIN_3);
+
+ // Configure Sio IRQ
+ PINMUX_AUX(PINMUX_AUX_GPIO_PE7) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP;
+ gpio_direction_input(GPIO_PORT_E, GPIO_PIN_7);
+
+ // Configure Sio NRST and BOOT0.
+ PINMUX_AUX(PINMUX_AUX_CAM1_STROBE) = PINMUX_PULL_DOWN | 1;
+ PINMUX_AUX(PINMUX_AUX_CAM2_PWDN) = PINMUX_PULL_DOWN | 1;
+
+ // Set BOOT0 to flash mode. (output high is sram mode).
+ gpio_direction_output(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
+
+ // NRST to pull down.
+ gpio_direction_input(GPIO_PORT_T, GPIO_PIN_1);
+
+ // Configure Sio NPOR.
+ PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN1) = PINMUX_IO_HV | PINMUX_LPDR | 1;
+ gpio_direction_output(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
+ }
+ else
+ {
+ _jc_power_supply(UART_C, true);
+ _jc_power_supply(UART_B, true);
+ }
+
+#if 0 // Already set by hw init.
+ // Set Joy-Con IsAttached pinmux. Shared with UARTB/UARTC TX.
+ PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
+ PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
+
+ // Set Joy-Con IsAttached mode. Shared with UARTB/UARTC TX.
+ gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
+ gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO);
+#endif
+
+ // Configure pinmuxing for UART B and C.
+ if (!jc_gamepad.sio_mode)
+ pinmux_config_uart(UART_B);
+ pinmux_config_uart(UART_C);
+
+ // Enable UART B and C clocks.
+ if (!jc_gamepad.sio_mode)
+ clock_enable_uart(UART_B);
+ clock_enable_uart(UART_C);
+
+ jc_init_done = true;
+#endif
+}
+
+void jc_deinit()
+{
+ if (!jc_init_done)
+ return;
+
+ jc_init_done = false;
+
+ if (!jc_gamepad.sio_mode)
+ {
+ // Disable power.
+ _jc_power_supply(UART_B, false);
+ _jc_power_supply(UART_C, false);
+
+ // Send sleep command.
+ u8 data = HCI_STATE_SLEEP;
+ if (jc_r.connected && !(jc_r.type & JC_ID_HORI))
+ {
+ _jc_send_hid_cmd(&jc_r, JC_HID_SUBCMD_HCI_STATE, &data, 1);
+ _jc_rcv_pkt(&jc_r);
+ }
+ if (jc_l.connected && !(jc_l.type & JC_ID_HORI))
+ {
+ _jc_send_hid_cmd(&jc_l, JC_HID_SUBCMD_HCI_STATE, &data, 1);
+ _jc_rcv_pkt(&jc_l);
+ }
+ }
+ else
+ {
+ // Disable Sio NPOR.
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
+
+ // Disable 4 MHz clock to Sio.
+ clock_disable_extperiph2();
+ }
+
+ // Disable UART B and C clocks.
+ if (!jc_gamepad.sio_mode)
+ clock_disable_uart(UART_B);
+ clock_disable_uart(UART_C);
+}
+
+jc_gamepad_rpt_t *joycon_poll()
+{
+ if (!jc_init_done)
+ return NULL;
+
+ _jc_conn_check();
+
+ _jc_conn_init(&jc_r);
+ _jc_conn_init(&jc_l);
+
+ _jc_req_status(&jc_r);
+ _jc_req_status(&jc_l);
+
+ _jc_rcv_pkt(&jc_r);
+ _jc_rcv_pkt(&jc_l);
+
+ return &jc_gamepad;
+}
+
+/* OmniNX: drop current button state without power-cycling the Joy-Cons. */
+void jc_clear_input()
+{
+ jc_gamepad.buttons = 0;
+}
diff --git a/bdk/input/joycon.h b/bdk/input/joycon.h
index c4afb93..9e5e098 100644
--- a/bdk/input/joycon.h
+++ b/bdk/input/joycon.h
@@ -1,99 +1,112 @@
-/*
- * Ambient light sensor driver for Nintendo Switch's Rohm BH1730
- *
- * Copyright (c) 2018 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef __JOYCON_H_
-#define __JOYCON_H_
-
-#include
-
-#define JC_BTNS_DIRECTION_PAD 0xF0000
-#define JC_BTNS_PREV_NEXT 0x800080
-#define JC_BTNS_ENTER 0x400008
-#define JC_BTNS_ESC 0x4
-
-#define JC_BTNS_ALL (JC_BTNS_PREV_NEXT | JC_BTNS_ENTER | JC_BTNS_DIRECTION_PAD | JC_BTNS_ESC)
-
-typedef struct _jc_bt_conn_t
-{
- u8 type;
- u8 mac[6];
- u8 host_mac[6];
- u8 ltk[16];
-} jc_bt_conn_t;
-
-typedef struct _jc_gamepad_rpt_t
-{
- union
- {
- struct
- {
- // Joy-Con (R).
- u32 y:1;
- u32 x:1;
- u32 b:1;
- u32 a:1;
- u32 sr_r:1;
- u32 sl_r:1;
- u32 r:1;
- u32 zr:1;
-
- // Shared
- u32 minus:1;
- u32 plus:1;
- u32 r3:1;
- u32 l3:1;
- u32 home:1;
- u32 cap:1;
- u32 pad:1;
- u32 wired:1;
-
- // Joy-Con (L).
- u32 down:1;
- u32 up:1;
- u32 right:1;
- u32 left:1;
- u32 sr_l:1;
- u32 sl_l:1;
- u32 l:1;
- u32 zl:1;
- };
- u32 buttons;
- };
-
- u16 lstick_x;
- u16 lstick_y;
- u16 rstick_x;
- u16 rstick_y;
- bool center_stick_l;
- bool center_stick_r;
- bool conn_l;
- bool conn_r;
- u8 batt_info_l;
- u8 batt_info_r;
- jc_bt_conn_t bt_conn_l;
- jc_bt_conn_t bt_conn_r;
-} jc_gamepad_rpt_t;
-
-void jc_power_supply(u8 uart, bool enable);
-void jc_init_hw();
-void jc_deinit();
-void jc_clear_input(void);
-jc_gamepad_rpt_t *joycon_poll();
-jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos);
-
-#endif
\ No newline at end of file
+/*
+ * Ambient light sensor driver for Nintendo Switch's Rohm BH1730
+ *
+ * Copyright (c) 2018-2025 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __JOYCON_H_
+#define __JOYCON_H_
+
+#include
+
+#define JC_BTNS_DIRECTION_PAD 0xF0000
+#define JC_BTNS_PREV_NEXT 0x800080
+#define JC_BTNS_ENTER 0x400008
+#define JC_BTNS_ESC 0x4
+
+#define JC_BTNS_ALL (JC_BTNS_PREV_NEXT | JC_BTNS_ENTER | JC_BTNS_DIRECTION_PAD | JC_BTNS_ESC)
+
+typedef struct _jc_bt_conn_t
+{
+ u8 type;
+ u8 mac[6];
+ u8 host_mac[6];
+ u8 ltk[16];
+} jc_bt_conn_t;
+
+typedef struct _jc_gamepad_rpt_t
+{
+ union
+ {
+ struct
+ {
+ // Joy-Con (R).
+/*00*/ u32 y:1;
+/*01*/ u32 x:1;
+/*02*/ u32 b:1;
+/*03*/ u32 a:1;
+/*04*/ u32 sr_r:1;
+/*05*/ u32 sl_r:1;
+/*06*/ u32 r:1;
+/*07*/ u32 zr:1;
+
+ // Shared
+/*08*/ u32 minus:1;
+/*09*/ u32 plus:1;
+/*10*/ u32 r3:1;
+/*11*/ u32 l3:1;
+/*12*/ u32 home:1;
+/*13*/ u32 cap:1;
+/*14*/ u32 pad:1;
+/*15*/ u32 wired:1;
+
+ // Joy-Con (L).
+/*16*/ u32 down:1;
+/*17*/ u32 up:1;
+/*18*/ u32 right:1;
+/*19*/ u32 left:1;
+/*20*/ u32 sr_l:1;
+/*21*/ u32 sl_l:1;
+/*22*/ u32 l:1;
+/*23*/ u32 zl:1;
+ };
+ u32 buttons;
+ };
+
+ u16 lstick_x;
+ u16 lstick_y;
+ u16 rstick_x;
+ u16 rstick_y;
+ bool center_stick_l;
+ bool center_stick_r;
+ bool conn_l;
+ bool conn_r;
+ bool sio_mode;
+ u8 batt_info_l; // Also Sio Connected status.
+ u8 batt_info_r; // Also Sio IRQ.
+ u8 batt_chrg_l;
+ u8 batt_chrg_r;
+ jc_bt_conn_t bt_conn_l;
+ jc_bt_conn_t bt_conn_r;
+} jc_gamepad_rpt_t;
+
+typedef struct _jc_calib_t
+{
+ u16 x_max:12;
+ u16 y_max:12;
+ u16 x_center:12;
+ u16 y_center:12;
+ u16 x_min:12;
+ u16 y_min:12;
+} __attribute__((packed)) jc_calib_t;
+
+void jc_init_hw();
+void jc_deinit();
+jc_gamepad_rpt_t *joycon_poll();
+/* OmniNX: drop current button state without power-cycling the Joy-Cons. */
+void jc_clear_input();
+jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos);
+
+#endif
diff --git a/bdk/input/touch.c b/bdk/input/touch.c
index 4ee44fc..40e0a64 100644
--- a/bdk/input/touch.c
+++ b/bdk/input/touch.c
@@ -1,461 +1,468 @@
-/*
- * Touch driver for Nintendo Switch's STM FingerTip S (4CD60D) touch controller
- *
- * Copyright (c) 2018 langerhans
- * Copyright (c) 2018-2020 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "touch.h"
-
-
-#include
-#define DPRINTF(...) gfx_printf(__VA_ARGS__)
-
-static touch_panel_info_t _panels[] =
-{
- { 0, 1, 1, 1, "NISSHA NFT-K12D" },
- { 1, 0, 1, 1, "GiS GGM6 B2X" },
- { 2, 0, 0, 0, "NISSHA NBF-K9A" },
- { 3, 1, 0, 0, "GiS 5.5\"" },
- { 4, 0, 0, 1, "Samsung BH2109" },
- { -1, 1, 0, 1, "GiS VA 6.2\"" }
-};
-
-static int touch_command(u8 cmd, u8 *buf, u8 size)
-{
- int res = i2c_send_buf_small(I2C_3, STMFTS_I2C_ADDR, cmd, buf, size);
- if (!res)
- return 1;
- return 0;
-}
-
-static int touch_read_reg(u8 *cmd, u32 csize, u8 *buf, u32 size)
-{
- int res = i2c_send_buf_small(I2C_3, STMFTS_I2C_ADDR, cmd[0], &cmd[1], csize - 1);
- if (res)
- res = i2c_recv_buf(buf, size, I2C_3, STMFTS_I2C_ADDR);
- if (!res)
- return 1;
-
- return 0;
-}
-
-static int touch_wait_event(u8 event, u8 status, u32 timeout, u8 *buf)
-{
- u32 timer = get_tmr_ms() + timeout;
- while (true)
- {
- u8 tmp[8] = {0};
- i2c_recv_buf_small(tmp, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_READ_ONE_EVENT);
- if (tmp[1] == event && tmp[2] == status)
- {
- if (buf)
- memcpy(buf, &tmp[3], 5);
- return 0;
- }
-
- if (get_tmr_ms() > timer)
- return 1;
- }
-}
-
-#define X_REAL_MAX 1264
-#define Y_REAL_MAX 704
-#define EDGE_OFFSET 15
-
-static void _touch_compensate_limits(touch_event *event, bool touching)
-{
- event->x = MAX(event->x, EDGE_OFFSET);
- event->x = MIN(event->x, X_REAL_MAX);
- event->x -= EDGE_OFFSET;
- u32 x_adj = (1280 * 1000) / (X_REAL_MAX - EDGE_OFFSET);
- event->x = ((u32)event->x * x_adj) / 1000;
-
- if (touching)
- {
- event->y = MAX(event->y, EDGE_OFFSET);
- event->y = MIN(event->y, Y_REAL_MAX);
- event->y -= EDGE_OFFSET;
- u32 y_adj = (720 * 1000) / (Y_REAL_MAX - EDGE_OFFSET);
- event->y = ((u32)event->y * y_adj) / 1000;
- }
-}
-
-static void _touch_process_contact_event(touch_event *event, bool touching)
-{
- event->x = (event->raw[2] << 4) | ((event->raw[4] & STMFTS_MASK_Y_LSB) >> 4);
-
- // Normally, GUI elements have bigger horizontal estate.
- // Avoid parsing y axis when finger is removed to minimize touch noise.
- if (touching)
- {
- event->y = (event->raw[3] << 4) | (event->raw[4] & STMFTS_MASK_X_MSB);
-
- event->z = event->raw[5] | (event->raw[6] << 8);
- event->z = event->z << 6;
- u16 tmp = 0x40;
- if ((event->raw[7] & 0x3F) != 1 && (event->raw[7] & 0x3F) != 0x3F)
- tmp = event->raw[7] & 0x3F;
- event->z /= tmp + 0x40;
-
- event->fingers = ((event->raw[1] & STMFTS_MASK_TOUCH_ID) >> 4) + 1;
- }
- else
- event->fingers = 0;
-
- _touch_compensate_limits(event, touching);
-}
-
-static void _touch_parse_event(touch_event *event)
-{
- event->type = event->raw[1] & STMFTS_MASK_EVENT_ID;
-
- switch (event->type)
- {
- case STMFTS_EV_MULTI_TOUCH_ENTER:
- case STMFTS_EV_MULTI_TOUCH_MOTION:
- _touch_process_contact_event(event, true);
- if (event->z < 255) // Reject palm rest.
- event->touch = true;
- else
- {
- event->touch = false;
- event->type = STMFTS_EV_MULTI_TOUCH_LEAVE;
- }
- break;
- case STMFTS_EV_MULTI_TOUCH_LEAVE:
- event->touch = false;
- _touch_process_contact_event(event, false);
- break;
- case STMFTS_EV_NO_EVENT:
- if (event->touch)
- event->type = STMFTS_EV_MULTI_TOUCH_MOTION;
- break;
- default:
- if (event->touch && event->raw[0] == STMFTS_EV_MULTI_TOUCH_MOTION)
- event->type = STMFTS_EV_MULTI_TOUCH_MOTION;
- else
- event->type = STMFTS_EV_MULTI_TOUCH_LEAVE;
- }
-
- // gfx_con_setpos(0, 300);
- // DPRINTF("x = %d \ny = %d \nz = %d \n", event->x, event->y, event->z);
- // DPRINTF("0 = %02X\n1 = %02X\n2 = %02X\n3 = %02X\n", event->raw[0], event->raw[1], event->raw[2], event->raw[3]);
- // DPRINTF("4 = %02X\n5 = %02X\n6 = %02X\n7 = %02X\n", event->raw[4], event->raw[5], event->raw[6], event->raw[7]);
-}
-
-void touch_poll(touch_event *event)
-{
- i2c_recv_buf_small(event->raw, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_LATEST_EVENT);
-
- _touch_parse_event(event);
-}
-
-touch_event touch_poll_wait()
-{
- touch_event event;
- do
- {
- touch_poll(&event);
- } while (event.type != STMFTS_EV_MULTI_TOUCH_LEAVE);
-
- return event;
-}
-
-touch_info touch_get_info()
-{
- touch_info info;
- u8 buf[8];
- memset(&buf, 0, 8);
- i2c_recv_buf_small(buf, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_READ_INFO);
-
- info.chip_id = buf[0] << 8 | buf[1];
- info.fw_ver = buf[2] << 8 | buf[3];
- info.config_id = buf[4];
- info.config_ver = buf[5];
-
- //DPRINTF("ID: %04X, FW Ver: %d.%02d\nCfg ID: %02X, Cfg Ver: %d\n",
- // info.chip_id, info.fw_ver >> 8, info.fw_ver & 0xFF, info.config_id, info.config_ver);
-
- return info;
-}
-
-touch_panel_info_t *touch_get_panel_vendor()
-{
- u8 buf[5] = {0};
- u8 cmd = STMFTS_VENDOR_GPIO_STATE;
- static touch_panel_info_t panel_info = { -2, 0, 0, 0, ""};
-
- if (touch_command(STMFTS_VENDOR, &cmd, 1))
- return NULL;
-
- if (touch_wait_event(STMFTS_EV_VENDOR, STMFTS_VENDOR_GPIO_STATE, 2000, buf))
- return NULL;
-
- for (u32 i = 0; i < ARRAY_SIZE(_panels); i++)
- {
- touch_panel_info_t *panel = &_panels[i];
- if (buf[0] == panel->gpio0 && buf[1] == panel->gpio1 && buf[2] == panel->gpio2)
- return panel;
- }
-
- // Touch panel not found, return current gpios.
- panel_info.gpio0 = buf[0];
- panel_info.gpio1 = buf[1];
- panel_info.gpio2 = buf[2];
-
- return &panel_info;
-}
-
-int touch_get_fw_info(touch_fw_info_t *fw)
-{
- u8 buf[8] = {0};
-
- memset(fw, 0, sizeof(touch_fw_info_t));
-
- // Get fw address info.
- u8 cmd[3] = { STMFTS_RW_FRAMEBUFFER_REG, 0, 0x60 };
- int res = touch_read_reg(cmd, 3, buf, 3);
- if (!res)
- {
- // Get fw info.
- cmd[1] = buf[2]; cmd[2] = buf[1];
- res = touch_read_reg(cmd, 3, buf, 8);
- if (!res)
- {
- fw->fw_id = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
- fw->ftb_ver = (buf[6] << 8) | buf[5];
- }
-
- cmd[2]++;
- res = touch_read_reg(cmd, 3, buf, 8);
- if (!res)
- fw->fw_rev = (buf[7] << 8) | buf[6];
- }
-
- return res;
-}
-
-int touch_sys_reset()
-{
- u8 cmd[3] = { 0, 0x28, 0x80 }; // System reset cmd.
- for (u8 retries = 0; retries < 3; retries++)
- {
- if (touch_command(STMFTS_WRITE_REG, cmd, 3))
- {
- msleep(10);
- continue;
- }
- msleep(10);
- if (touch_wait_event(STMFTS_EV_CONTROLLER_READY, 0, 20, NULL))
- continue;
- else
- return 0;
- }
-
- return 1;
-}
-
-int touch_panel_ito_test(u8 *err)
-{
- int res = 0;
-
- // Reset touchscreen module.
- if (touch_sys_reset())
- return res;
-
- // Do ITO Production test.
- u8 cmd[2] = { 1, 0 };
- if (touch_command(STMFTS_ITO_CHECK, cmd, 2))
- return res;
-
- u32 timer = get_tmr_ms() + 2000;
- while (true)
- {
- u8 tmp[8] = {0};
- i2c_recv_buf_small(tmp, 8, I2C_3, STMFTS_I2C_ADDR, STMFTS_READ_ONE_EVENT);
- if (tmp[1] == 0xF && tmp[2] == 0x5)
- {
- if (err)
- {
- err[0] = tmp[3];
- err[1] = tmp[4];
- }
-
- res = 1;
- break;
- }
-
- if (get_tmr_ms() > timer)
- break;
- }
-
- // Reset touchscreen module.
- touch_sys_reset();
-
- return res;
-}
-
-int touch_get_fb_info(u8 *buf)
-{
- u8 tmp[5];
-
- u8 cmd[3] = { STMFTS_RW_FRAMEBUFFER_REG, 0, 0 };
- int res = 0;
-
-
- for (u32 i = 0; i < 0x10000; i += 4)
- {
- if (!res)
- {
- cmd[1] = (i >> 8) & 0xFF;
- cmd[2] = i & 0xFF;
- memset(tmp, 0xCC, 5);
- res = touch_read_reg(cmd, 3, tmp, 5);
- memcpy(&buf[i], tmp + 1, 4);
- }
- }
-
- return res;
-}
-
-int touch_sense_enable()
-{
- // Switch sense mode and enable multi-touch sensing.
- u8 cmd = STMFTS_FINGER_MODE;
- if (touch_command(STMFTS_SWITCH_SENSE_MODE, &cmd, 1))
- return 0;
-
- if (touch_command(STMFTS_MS_MT_SENSE_ON, NULL, 0))
- return 0;
-
- if (touch_command(STMFTS_CLEAR_EVENT_STACK, NULL, 0))
- return 0;
-
- return 1;
-}
-
-int touch_execute_autotune()
-{
- // Reset touchscreen module.
- if (touch_sys_reset())
- return 0;
-
- // Trim low power oscillator.
- if (touch_command(STMFTS_LP_TIMER_CALIB, NULL, 0))
- return 0;
- msleep(200);
-
- // Apply Mutual Sense Compensation tuning.
- if (touch_command(STMFTS_MS_CX_TUNING, NULL, 0))
- return 0;
- if (touch_wait_event(STMFTS_EV_STATUS, STMFTS_EV_STATUS_MS_CX_TUNING_DONE, 2000, NULL))
- return 0;
-
- // Apply Self Sense Compensation tuning.
- if (touch_command(STMFTS_SS_CX_TUNING, NULL, 0))
- return 0;
- if (touch_wait_event(STMFTS_EV_STATUS, STMFTS_EV_STATUS_SS_CX_TUNING_DONE, 2000, NULL))
- return 0;
-
- // Save Compensation data to EEPROM.
- if (touch_command(STMFTS_SAVE_CX_TUNING, NULL, 0))
- return 0;
- if (touch_wait_event(STMFTS_EV_STATUS, STMFTS_EV_STATUS_WRITE_CX_TUNE_DONE, 2000, NULL))
- return 0;
-
- return touch_sense_enable();
-}
-
-static int touch_init()
-{
- // Initialize touchscreen module.
- if (touch_sys_reset())
- return 0;
-
- return touch_sense_enable();
-}
-
-int touch_power_on()
-{
- // Enable LDO6 for touchscreen AVDD supply.
- max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
- max7762x_regulator_enable(REGULATOR_LDO6, true);
-
- // Configure touchscreen VDD GPIO.
- PINMUX_AUX(PINMUX_AUX_DAP4_SCLK) = PINMUX_PULL_DOWN | 1;
- gpio_config(GPIO_PORT_J, GPIO_PIN_7, GPIO_MODE_GPIO);
- gpio_output_enable(GPIO_PORT_J, GPIO_PIN_7, GPIO_OUTPUT_ENABLE);
- gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_HIGH);
-
- // IRQ and more.
- // PINMUX_AUX(PINMUX_AUX_TOUCH_INT) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 3;
- // gpio_config(GPIO_PORT_X, GPIO_PIN_1, GPIO_MODE_GPIO);
- // gpio_write(GPIO_PORT_X, GPIO_PIN_1, GPIO_LOW);
-
- // Configure Touscreen and GCAsic shared GPIO.
- PINMUX_AUX(PINMUX_AUX_CAM_I2C_SDA) = PINMUX_LPDR | PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 2;
- PINMUX_AUX(PINMUX_AUX_CAM_I2C_SCL) = PINMUX_IO_HV | PINMUX_LPDR | PINMUX_TRISTATE | PINMUX_PULL_DOWN | 2;
- gpio_config(GPIO_PORT_S, GPIO_PIN_3, GPIO_MODE_GPIO); // GC detect.
-
- // Initialize I2C3.
- pinmux_config_i2c(I2C_3);
- clock_enable_i2c(I2C_3);
- i2c_init(I2C_3);
-
- // Wait for the touchscreen module to get ready.
- touch_wait_event(STMFTS_EV_CONTROLLER_READY, 0, 20, NULL);
-
- // Check for forced boot time calibration.
- if (btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
- {
- u8 err[2];
- if (touch_panel_ito_test(err))
- if (!err[0] && !err[1])
- return touch_execute_autotune();
- }
-
- // Initialize touchscreen.
- u32 retries = 3;
- while (retries)
- {
- if (touch_init())
- return 1;
- retries--;
- }
-
- return 0;
-}
-
-void touch_power_off()
-{
- // Disable touchscreen power.
- gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
-
- // Disables LDO6 for touchscreen VDD, AVDD supply
- max7762x_regulator_enable(REGULATOR_LDO6, false);
-
- clock_disable_i2c(I2C_3);
-}
\ No newline at end of file
+/*
+ * Touch driver for Nintendo Switch's STM FingerTip S (FTM4CD60DA1BE/FTM4CD50TA1BE) touch controller
+ *
+ * Copyright (c) 2018 langerhans
+ * Copyright (c) 2018-2026 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "touch.h"
+
+static touch_panel_info_t _panels[] =
+{
+ { 0, 1, 1, 1, "NISSHA NFT-K12D" },// 0.
+ { 1, 0, 1, 1, "GiS GGM6 B2X" },// 1.
+ { 2, 0, 0, 0, "NISSHA NBF-K9A" },// 3.
+ { 3, 1, 0, 0, "GiS 5.5\"" },// 4.
+ { 4, 0, 0, 1, "Samsung TSP" },// 5?
+ { -1, 1, 0, 1, "GiS VA 6.2\"" } // 2.
+};
+
+static touch_info_t _touch_info = { 0 };
+static touch_panel_info_t _touch_panel_info = { 0 };
+
+static int _touch_command(u8 cmd, u8 *buf, u8 size)
+{
+ return i2c_send_buf_small(I2C_3, FTS4_I2C_ADDR, cmd, buf, size);
+}
+
+static int _touch_read_reg(u8 *cmd, u32 csize, u8 *buf, u32 size)
+{
+ return i2c_xfer_packet(I2C_3, FTS4_I2C_ADDR, cmd, csize, buf, size);
+}
+
+int touch_get_event_count()
+{
+ u8 cmd[3] = { FTS4_CMD_HW_REG_READ, 0, FTS4_HW_REG_EVENT_COUNT };
+ u8 buf[2];
+
+ if (_touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf)))
+ return 0;
+
+ return (buf[1] >> 1);
+}
+
+static int _touch_wait_event(u8 event, u8 status, u32 timeout, u8 *buf)
+{
+ u32 timer = get_tmr_ms() + timeout;
+ while (true)
+ {
+ if (!touch_get_event_count())
+ goto retry;
+
+ u8 tmp[FTS4_EVENT_SIZE];
+ int res = i2c_recv_buf_big(tmp, FTS4_EVENT_SIZE, I2C_3, FTS4_I2C_ADDR, FTS4_CMD_READ_ONE_EVENT);
+
+ // Check that event type and status match.
+ if (!res && tmp[0] == event && tmp[1] == status)
+ {
+ if (buf)
+ memcpy(buf, &tmp[2], 6);
+ return 0;
+ }
+
+retry:
+ usleep(500);
+
+ if (get_tmr_ms() > timer)
+ return 1;
+ }
+}
+
+#define X_REAL_MAX 1264
+#define Y_REAL_MAX 704
+#define EDGE_OFFSET 15
+
+static void _touch_compensate_limits(touch_event_t *event, bool touching)
+{
+ event->x = MAX(event->x, EDGE_OFFSET);
+ event->x = MIN(event->x, X_REAL_MAX);
+ event->x -= EDGE_OFFSET;
+ u32 x_adj = (1280 * 1000) / (X_REAL_MAX - EDGE_OFFSET);
+ event->x = ((u32)event->x * x_adj) / 1000;
+
+ if (touching)
+ {
+ event->y = MAX(event->y, EDGE_OFFSET);
+ event->y = MIN(event->y, Y_REAL_MAX);
+ event->y -= EDGE_OFFSET;
+ u32 y_adj = (720 * 1000) / (Y_REAL_MAX - EDGE_OFFSET);
+ event->y = ((u32)event->y * y_adj) / 1000;
+ }
+}
+
+static void _touch_process_contact_event(touch_event_t *event, bool touching)
+{
+ event->x = (event->raw[1] << 4) | ((event->raw[3] & FTS4_MASK_Y_LSB) >> 4);
+
+ // Normally, GUI elements have bigger horizontal estate.
+ // Avoid parsing y axis when finger is removed to minimize touch noise.
+ if (touching)
+ {
+ event->y = (event->raw[2] << 4) | (event->raw[3] & FTS4_MASK_X_MSB);
+
+ event->z = event->raw[4] | (event->raw[5] << 8);
+ event->z = event->z << 6;
+
+ u16 tmp = 0x40;
+ if ((event->raw[6] & 0x3F) != 1 && (event->raw[6] & 0x3F) != 0x3F)
+ tmp = event->raw[6] & 0x3F;
+ event->z /= tmp + 0x40;
+
+ event->finger = ((event->raw[0] & FTS4_MASK_TOUCH_ID) >> 4) + 1;
+ }
+ else
+ event->finger = 0;
+
+ _touch_compensate_limits(event, touching);
+}
+
+static int _touch_parse_input_event(touch_event_t *event)
+{
+ switch (event->raw[0] & FTS4_MASK_EVENT_ID)
+ {
+ case FTS4_EV_MULTI_TOUCH_ENTER:
+ case FTS4_EV_MULTI_TOUCH_MOTION:
+ _touch_process_contact_event(event, true);
+ if (event->z < 500) // Reject palm rest.
+ event->touch = true;
+ else
+ event->touch = false;
+ return 0;
+
+ case FTS4_EV_MULTI_TOUCH_LEAVE:
+ event->touch = false;
+ _touch_process_contact_event(event, false);
+ return 0;
+
+ default:
+ return 1; // No event.
+ }
+}
+
+int touch_poll(touch_event_t *event)
+{
+ u8 cmd = !_touch_info.clone ? FTS4_CMD_LATEST_EVENT : FTS4_CMD_READ_ONE_EVENT;
+
+ int res = i2c_recv_buf_big(event->raw, FTS4_EVENT_SIZE, I2C_3, FTS4_I2C_ADDR, cmd);
+ if (!res)
+ res = _touch_parse_input_event(event);
+
+ return res;
+}
+
+touch_info_t *touch_get_chip_info()
+{
+ u8 buf[7] = { 0 };
+
+ // Get chip info.
+ u8 cmd[3] = { FTS4_CMD_HW_REG_READ, 0, FTS4_HW_REG_CHIP_ID_INFO };
+ if (_touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf)))
+ {
+ memset(&_touch_info, 0, sizeof(touch_info_t));
+ goto exit;
+ }
+
+ _touch_info.chip_id = buf[1] << 8 | buf[2];
+ _touch_info.fw_ver = buf[3] << 8 | buf[4];
+ _touch_info.config_id = buf[5];
+ _touch_info.config_ver = buf[6];
+
+ // Validate that device is genuine or proper.
+ cmd[2] = 2;
+ _touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf));
+ _touch_info.clone = _touch_info.chip_id != (buf[3] << 8 | buf[4]);
+
+exit:
+ return &_touch_info;
+}
+
+touch_panel_info_t *touch_get_panel_vendor()
+{
+ _touch_panel_info.idx = -2;
+
+ u8 cmd = FTS4_VENDOR_GPIO_STATE;
+ if (_touch_command(FTS4_CMD_VENDOR, &cmd, 1))
+ return NULL;
+
+ u8 buf[6] = { 0 };
+ if (_touch_wait_event(FTS4_EV_VENDOR, FTS4_VENDOR_GPIO_STATE, 2000, buf))
+ return NULL;
+
+ for (u32 i = 0; i < ARRAY_SIZE(_panels); i++)
+ {
+ touch_panel_info_t *panel = &_panels[i];
+ if (buf[0] == panel->gpio0 && buf[1] == panel->gpio1 && buf[2] == panel->gpio2)
+ return panel;
+ }
+
+ // Touch panel not found, return current gpios.
+ _touch_panel_info.gpio0 = buf[0];
+ _touch_panel_info.gpio1 = buf[1];
+ _touch_panel_info.gpio2 = buf[2];
+
+ return &_touch_panel_info;
+}
+
+int touch_get_fw_info(touch_fw_info_t *fw)
+{
+ u8 buf[9] = { 0 };
+
+ memset(fw, 0, sizeof(touch_fw_info_t));
+
+ // Get fw address info.
+ u8 cmd[3] = { FTS4_CMD_FB_REG_READ, 0, 0x60 };
+ int res = _touch_read_reg(cmd, sizeof(cmd), buf, 3);
+ if (!res)
+ {
+ // Get fw info.
+ cmd[1] = buf[2]; cmd[2] = buf[1];
+ res = _touch_read_reg(cmd, sizeof(cmd), buf, sizeof(buf));
+ if (!res)
+ {
+ fw->fw_id = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
+ fw->ftb_ver = (buf[6] << 8) | buf[5];
+ fw->fw_rev = (buf[8] << 8) | buf[7];
+ }
+ }
+
+ return res;
+}
+
+int touch_sys_reset()
+{
+ u8 cmd[3] = { 0, FTS4_HW_REG_SYS_RESET, 0x80 }; // System reset cmd.
+ for (u8 retries = 0; retries < 3; retries++)
+ {
+ if (_touch_command(FTS4_CMD_HW_REG_WRITE, cmd, 3))
+ {
+ msleep(10);
+ continue;
+ }
+ msleep(10);
+
+ if (_touch_wait_event(FTS4_EV_CONTROLLER_READY, 0, 20, NULL))
+ continue;
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+int touch_panel_ito_test(u8 *err)
+{
+ // Check that touch IC is supported.
+ touch_info_t *info = touch_get_chip_info();
+ if (info->chip_id != FTS4_I2C_CHIP_ID || info->clone)
+ return 1;
+
+ // Reset touchscreen module.
+ if (touch_sys_reset())
+ return 1;
+
+ // Do ITO Production test.
+ if (_touch_command(FTS4_CMD_ITO_CHECK, NULL, 0))
+ return 1;
+
+ u8 buf[6] = { 0 };
+ int res = _touch_wait_event(FTS4_EV_ERROR, FTS4_EV_ERROR_ITO_TEST, 2000, buf);
+ if (!res && err)
+ {
+ err[0] = buf[0];
+ err[1] = buf[1];
+ }
+
+ // Reset touchscreen module.
+ touch_sys_reset();
+
+ return res;
+}
+
+int touch_get_fb_info(u8 *buf)
+{
+ u8 cmd[3] = { FTS4_CMD_FB_REG_READ, 0, 0 };
+ int res = 0;
+
+ for (u32 i = 0; i < 0x10000; i += 4)
+ {
+ if (!res)
+ {
+ cmd[1] = (i >> 8) & 0xFF;
+ cmd[2] = i & 0xFF;
+
+ u8 tmp[5];
+ memset(tmp, 0xCC, sizeof(tmp));
+ res = _touch_read_reg(cmd, sizeof(cmd), tmp, sizeof(tmp));
+ memcpy(&buf[i], tmp + 1, 4);
+ }
+ }
+
+ return res;
+}
+
+int touch_switch_sense_mode(u8 mode, bool gis_6_2)
+{
+ // Set detection config.
+ u8 cmd[3] = { 1, 0x64, 0 };
+
+ switch (mode)
+ {
+ case FTS4_STYLUS_MODE:
+ cmd[2] = !gis_6_2 ? 0xC8 : 0xAD;
+ break;
+ case FTS4_FINGER_MODE:
+ cmd[2] = !gis_6_2 ? 0x8C : 0x79;
+ break;
+ }
+
+ if (_touch_command(FTS4_CMD_DETECTION_CONFIG, cmd, 3))
+ return 1;
+
+ // Sense mode.
+ cmd[0] = mode;
+
+ return _touch_command(FTS4_CMD_SWITCH_SENSE_MODE, cmd, 1);
+}
+
+int touch_sense_enable()
+{
+ // Switch sense mode and enable multi-touch sensing.
+ u8 cmd = FTS4_FINGER_MODE;
+ if (_touch_command(FTS4_CMD_SWITCH_SENSE_MODE, &cmd, 1))
+ return 1;
+
+ if (_touch_command(FTS4_CMD_MS_MT_SENSE_ON, NULL, 0))
+ return 1;
+
+ if (_touch_command(FTS4_CMD_CLEAR_EVENT_STACK, NULL, 0))
+ return 1;
+
+ return 0;
+}
+
+int touch_execute_autotune()
+{
+ u8 buf[6] = { 0 };
+
+ // Reset touchscreen module.
+ if (touch_sys_reset())
+ return 1;
+
+ // Trim low power oscillator.
+ if (_touch_command(FTS4_CMD_LP_TIMER_CALIB, NULL, 0))
+ return 1;
+
+ msleep(200);
+
+ // Apply Mutual Sense Compensation tuning.
+ if (_touch_command(FTS4_CMD_MS_CX_TUNING, NULL, 0))
+ return 1;
+ if (_touch_wait_event(FTS4_EV_STATUS, FTS4_EV_STATUS_MS_CX_TUNING_DONE, 2000, buf) || buf[0] || buf[1])
+ return 1;
+
+ // Apply Self Sense Compensation tuning.
+ if (_touch_command(FTS4_CMD_SS_CX_TUNING, NULL, 0))
+ return 1;
+ if (_touch_wait_event(FTS4_EV_STATUS, FTS4_EV_STATUS_SS_CX_TUNING_DONE, 2000, buf) || buf[0] || buf[1])
+ return 1;
+
+ // Save Compensation data to EEPROM.
+ if (_touch_command(FTS4_CMD_SAVE_CX_TUNING, NULL, 0))
+ return 1;
+ if (_touch_wait_event(FTS4_EV_STATUS, FTS4_EV_STATUS_WRITE_CX_TUNE_DONE, 2000, buf) || buf[0] || buf[1])
+ return 1;
+
+ return touch_sense_enable();
+}
+
+static int touch_init()
+{
+ // Check that touch IC is supported.
+ touch_info_t *info = touch_get_chip_info();
+ if (info->chip_id != FTS4_I2C_CHIP_ID)
+ return 1;
+
+ // Initialize touchscreen module.
+ if (touch_sys_reset())
+ return 1;
+
+ return touch_sense_enable();
+}
+
+int touch_power_on()
+{
+ // Configure touchscreen Touch Reset pin.
+ PINMUX_AUX(PINMUX_AUX_DAP4_SCLK) = PINMUX_PULL_DOWN | 1;
+ gpio_direction_output(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
+ usleep(20);
+
+ // Enable LDO6 for touchscreen AVDD and DVDD supply.
+ max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
+ max7762x_regulator_enable(REGULATOR_LDO6, true);
+
+ // Initialize I2C3.
+ pinmux_config_i2c(I2C_3);
+ clock_enable_i2c(I2C_3);
+ i2c_init(I2C_3);
+ usleep(1000);
+
+ // Set Touch Reset pin.
+ gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_HIGH);
+ usleep(10000);
+
+ // Wait for the touchscreen module to get ready.
+ _touch_wait_event(FTS4_EV_CONTROLLER_READY, 0, 20, NULL);
+
+ // Check for forced boot time calibration.
+ if (btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ {
+ u8 err[2];
+ if (!touch_panel_ito_test(err))
+ if (!err[0] && !err[1])
+ return touch_execute_autotune();
+ }
+
+ // Initialize touchscreen.
+ u32 retries = 3;
+ while (retries)
+ {
+ if (!touch_init())
+ return 0;
+ retries--;
+ }
+
+ return 1;
+}
+
+void touch_power_off()
+{
+ // Disable touchscreen power.
+ gpio_write(GPIO_PORT_J, GPIO_PIN_7, GPIO_LOW);
+
+ // Disables LDO6 for touchscreen VDD, AVDD supply
+ max7762x_regulator_enable(REGULATOR_LDO6, false);
+
+ clock_disable_i2c(I2C_3);
+}
diff --git a/bdk/input/touch.h b/bdk/input/touch.h
index 199db0d..48893b9 100644
--- a/bdk/input/touch.h
+++ b/bdk/input/touch.h
@@ -1,176 +1,184 @@
-/*
- * Touch driver for Nintendo Switch's STM FingerTip S (4CD60D) touch controller
- *
- * Copyright (c) 2018 langerhans
- * Copyright (c) 2018-2020 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef __TOUCH_H_
-#define __TOUCH_H_
-
-#include
-
-#define STMFTS_I2C_ADDR 0x49
-
-/* I2C commands */
-#define STMFTS_READ_INFO 0x80
-#define STMFTS_READ_STATUS 0x84
-#define STMFTS_READ_ONE_EVENT 0x85
-#define STMFTS_READ_ALL_EVENT 0x86
-#define STMFTS_LATEST_EVENT 0x87
-#define STMFTS_SLEEP_IN 0x90
-#define STMFTS_SLEEP_OUT 0x91
-#define STMFTS_MS_MT_SENSE_OFF 0x92
-#define STMFTS_MS_MT_SENSE_ON 0x93
-#define STMFTS_SS_HOVER_SENSE_OFF 0x94
-#define STMFTS_SS_HOVER_SENSE_ON 0x95
-#define STMFTS_LP_TIMER_CALIB 0x97
-#define STMFTS_MS_KEY_SENSE_OFF 0x9A
-#define STMFTS_MS_KEY_SENSE_ON 0x9B
-#define STMFTS_SYSTEM_RESET 0xA0
-#define STMFTS_CLEAR_EVENT_STACK 0xA1
-#define STMFTS_FULL_FORCE_CALIBRATION 0xA2
-#define STMFTS_MS_CX_TUNING 0xA3
-#define STMFTS_SS_CX_TUNING 0xA4
-#define STMFTS_ITO_CHECK 0xA7
-#define STMFTS_RELEASEINFO 0xAA
-#define STMFTS_WRITE_REG 0xB6
-#define STMFTS_SWITCH_SENSE_MODE 0xC3
-#define STMFTS_NOISE_WRITE 0xC7
-#define STMFTS_NOISE_READ 0xC8
-#define STMFTS_RW_FRAMEBUFFER_REG 0xD0
-#define STMFTS_SAVE_CX_TUNING 0xFC
-
-#define STMFTS_DETECTION_CONFIG 0xB0
-#define STMFTS_REQU_COMP_DATA 0xB8
-#define STMFTS_VENDOR 0xCF
-#define STMFTS_FLASH_UNLOCK 0xF7
-#define STMFTS_FLASH_WRITE_64K 0xF8
-#define STMFTS_FLASH_STATUS 0xF9
-#define STMFTS_FLASH_OP 0xFA
-#define STMFTS_UNK5 0x62
-
-/* cmd parameters */
-#define STMFTS_VENDOR_GPIO_STATE 0x01
-#define STMFTS_VENDOR_SENSE_MODE 0x02
-#define STMFTS_STYLUS_MODE 0x00
-#define STMFTS_FINGER_MODE 0x01
-#define STMFTS_HOVER_MODE 0x02
-
-/* events */
-#define STMFTS_EV_NO_EVENT 0x00
-#define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02
-#define STMFTS_EV_MULTI_TOUCH_ENTER 0x03
-#define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04
-#define STMFTS_EV_MULTI_TOUCH_MOTION 0x05
-#define STMFTS_EV_HOVER_ENTER 0x07
-#define STMFTS_EV_HOVER_LEAVE 0x08
-#define STMFTS_EV_HOVER_MOTION 0x09
-#define STMFTS_EV_KEY_STATUS 0x0e
-#define STMFTS_EV_ERROR 0x0f
-#define STMFTS_EV_NOISE_READ 0x17
-#define STMFTS_EV_NOISE_WRITE 0x18
-#define STMFTS_EV_VENDOR 0x20
-
-#define STMFTS_EV_CONTROLLER_READY 0x10
-#define STMFTS_EV_STATUS 0x16
-#define STMFTS_EV_DEBUG 0xDB
-
-#define STMFTS_EV_STATUS_MS_CX_TUNING_DONE 1
-#define STMFTS_EV_STATUS_SS_CX_TUNING_DONE 2
-#define STMFTS_EV_STATUS_WRITE_CX_TUNE_DONE 4
-
-/* multi touch related event masks */
-#define STMFTS_MASK_EVENT_ID 0x0F
-#define STMFTS_MASK_TOUCH_ID 0xF0
-#define STMFTS_MASK_LEFT_EVENT 0x0F
-#define STMFTS_MASK_X_MSB 0x0F
-#define STMFTS_MASK_Y_LSB 0xF0
-
-/* key related event masks */
-#define STMFTS_MASK_KEY_NO_TOUCH 0x00
-#define STMFTS_MASK_KEY_MENU 0x01
-#define STMFTS_MASK_KEY_BACK 0x02
-
-#define STMFTS_EVENT_SIZE 8
-#define STMFTS_STACK_DEPTH 32
-#define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH)
-#define STMFTS_MAX_FINGERS 10
-
-typedef enum _touch_ito_error {
- ITO_NO_ERROR = 0,
- ITO_FORCE_OPEN,
- ITO_SENSE_OPEN,
- ITO_FORCE_SHRT_GND,
- ITO_SENSE_SHRT_GND,
- ITO_FORCE_SHRT_VCM,
- ITO_SENSE_SHRT_VCM,
- ITO_FORCE_SHRT_FORCE,
- ITO_SENSE_SHRT_SENSE,
- ITO_F2E_SENSE,
- ITO_FPC_FORCE_OPEN,
- ITO_FPC_SENSE_OPEN,
- ITO_KEY_FORCE_OPEN,
- ITO_KEY_SENSE_OPEN,
- ITO_RESERVED0,
- ITO_RESERVED1,
- ITO_RESERVED2,
- ITO_MAX_ERR_REACHED = 0xFF
-} touch_ito_error;
-
-typedef struct _touch_event {
- u8 raw[8];
- u16 type; // Event type.
- u16 x; // Horizontal coordinates.
- u16 y; // Vertical coordinates.
- u32 z;
- u8 fingers;
- bool touch;
-} touch_event;
-
-typedef struct _touch_panel_info_t
-{
- u8 idx;
- u8 gpio0;
- u8 gpio1;
- u8 gpio2;
- char *vendor;
-} touch_panel_info_t;
-
-typedef struct _touch_info {
- u16 chip_id;
- u16 fw_ver;
- u16 config_id;
- u16 config_ver;
-} touch_info;
-
-typedef struct _touch_fw_info_t {
- u32 fw_id;
- u16 ftb_ver;
- u16 fw_rev;
-} touch_fw_info_t;
-
-void touch_poll(touch_event *event);
-touch_event touch_poll_wait();
-touch_panel_info_t *touch_get_panel_vendor();
-int touch_get_fw_info(touch_fw_info_t *fw);
-touch_info touch_get_info();
-int touch_panel_ito_test(u8 *err);
-int touch_execute_autotune();
-int touch_sense_enable();
-int touch_power_on();
-void touch_power_off();
-
-#endif /* __TOUCH_H_ */
\ No newline at end of file
+/*
+ * Touch driver for Nintendo Switch's STM FingerTip S (4CD60D) touch controller
+ *
+ * Copyright (c) 2018 langerhans
+ * Copyright (c) 2018-2026 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __TOUCH_H_
+#define __TOUCH_H_
+
+#include
+
+#define FTS4_I2C_ADDR 0x49
+
+#define FTS4_I2C_CHIP_ID 0x3670
+
+/* I2C commands. */
+#define FTS4_CMD_READ_INFO 0x80
+#define FTS4_CMD_READ_STATUS 0x84
+#define FTS4_CMD_READ_ONE_EVENT 0x85
+#define FTS4_CMD_READ_ALL_EVENT 0x86
+#define FTS4_CMD_LATEST_EVENT 0x87 // Clears event stack.
+#define FTS4_CMD_SLEEP_IN 0x90
+#define FTS4_CMD_SLEEP_OUT 0x91
+#define FTS4_CMD_MS_MT_SENSE_OFF 0x92
+#define FTS4_CMD_MS_MT_SENSE_ON 0x93
+#define FTS4_CMD_SS_HOVER_SENSE_OFF 0x94
+#define FTS4_CMD_SS_HOVER_SENSE_ON 0x95
+#define FTS4_CMD_LP_TIMER_CALIB 0x97
+#define FTS4_CMD_MS_KEY_SENSE_OFF 0x9A
+#define FTS4_CMD_MS_KEY_SENSE_ON 0x9B
+#define FTS4_CMD_SYSTEM_RESET 0xA0
+#define FTS4_CMD_CLEAR_EVENT_STACK 0xA1
+#define FTS4_CMD_FULL_FORCE_CALIB 0xA2
+#define FTS4_CMD_MS_CX_TUNING 0xA3
+#define FTS4_CMD_SS_CX_TUNING 0xA4
+#define FTS4_CMD_ITO_CHECK 0xA7
+#define FTS4_CMD_RELEASEINFO 0xAA
+#define FTS4_CMD_HW_REG_READ 0xB6 // u16be address offset. Any size read.
+#define FTS4_CMD_HW_REG_WRITE FTS4_CMD_HW_REG_READ // u16be address offset, bytes to write.
+#define FTS4_CMD_SWITCH_SENSE_MODE 0xC3
+#define FTS4_CMD_NOISE_WRITE 0xC7
+#define FTS4_CMD_NOISE_READ 0xC8
+#define FTS4_CMD_FB_REG_READ 0xD0
+#define FTS4_CMD_FB_REG_WRITE FTS4_CMD_FB_REG_READ
+#define FTS4_CMD_SAVE_CX_TUNING 0xFC
+
+#define FTS4_CMD_DETECTION_CONFIG 0xB0
+#define FTS4_CMD_REQ_CX_DATA 0xB8
+#define FTS4_CMD_VENDOR 0xCF
+#define FTS4_CMD_FLASH_UNLOCK 0xF7
+#define FTS4_CMD_FLASH_WRITE_64K 0xF8
+#define FTS4_CMD_FLASH_STATUS 0xF9
+#define FTS4_CMD_FLASH_OP 0xFA
+#define FTS4_CMD_UNK_62 0x62
+
+/* Command parameters. */
+#define FTS4_VENDOR_GPIO_STATE 0x01
+#define FTS4_VENDOR_SENSE_MODE 0x02
+#define FTS4_STYLUS_MODE 0x00
+#define FTS4_FINGER_MODE 0x01
+#define FTS4_HOVER_MODE 0x02
+
+/* HW Registers */
+#define FTS4_HW_REG_CHIP_ID_INFO 0x0004
+#define FTS4_HW_REG_EVENT_COUNT 0x0023
+#define FTS4_HW_REG_SYS_RESET 0x0028
+
+/* FB Addresses */
+#define FTS4_FB_REG_FW_INFO_ADDRESS 0x0060
+
+/* Events. */
+#define FTS4_EV_NO_EVENT 0x00
+#define FTS4_EV_MULTI_TOUCH_DETECTED 0x02
+#define FTS4_EV_MULTI_TOUCH_ENTER 0x03
+#define FTS4_EV_MULTI_TOUCH_LEAVE 0x04
+#define FTS4_EV_MULTI_TOUCH_MOTION 0x05
+#define FTS4_EV_HOVER_ENTER 0x07
+#define FTS4_EV_HOVER_LEAVE 0x08
+#define FTS4_EV_HOVER_MOTION 0x09
+#define FTS4_EV_KEY_STATUS 0x0E
+#define FTS4_EV_ERROR 0x0F
+#define FTS4_EV_CONTROLLER_READY 0x10
+#define FTS4_EV_STATUS 0x16
+#define FTS4_EV_NOISE_READ 0x17
+#define FTS4_EV_NOISE_WRITE 0x18
+#define FTS4_EV_VENDOR 0x20
+#define FTS4_EV_DEBUG 0xDB
+
+/* FTS4_EV_STATUS Events. */
+#define FTS4_EV_STATUS_MS_CX_TUNING_DONE 0x01
+#define FTS4_EV_STATUS_SS_CX_TUNING_DONE 0x02
+#define FTS4_EV_STATUS_WRITE_CX_TUNE_DONE 0x04
+
+#define FTS4_EV_ERROR_ITO_TEST 0x05
+
+/* Multi touch related event masks. */
+#define FTS4_MASK_EVENT_ID 0x0F
+#define FTS4_MASK_TOUCH_ID 0xF0
+#define FTS4_MASK_X_MSB 0x0F
+#define FTS4_MASK_Y_LSB 0xF0
+
+#define FTS4_EVENT_SIZE 8
+#define FTS4_STACK_DEPTH 32 // Actual 128.
+#define FTS4_DATA_MAX_SIZE (FTS4_EVENT_SIZE * FTS4_STACK_DEPTH)
+#define FTS4_MAX_FINGERS 10
+
+typedef enum _touch_ito_error {
+ ITO_NO_ERROR = 0,
+ ITO_FORCE_OPEN,
+ ITO_SENSE_OPEN,
+ ITO_FORCE_SHRT_GND,
+ ITO_SENSE_SHRT_GND,
+ ITO_FORCE_SHRT_VCM,
+ ITO_SENSE_SHRT_VCM,
+ ITO_FORCE_SHRT_FORCE,
+ ITO_SENSE_SHRT_SENSE,
+ ITO_F2E_SENSE,
+ ITO_FPC_FORCE_OPEN,
+ ITO_FPC_SENSE_OPEN,
+ ITO_KEY_FORCE_OPEN,
+ ITO_KEY_SENSE_OPEN,
+ ITO_RESERVED0,
+ ITO_RESERVED1,
+ ITO_RESERVED2,
+ ITO_MAX_ERR_REACHED = 0xFF
+} touch_ito_error;
+
+typedef struct _touch_event_t {
+ u8 raw[FTS4_EVENT_SIZE];
+ u16 x, y; // Coordinates.
+ u32 z; // Orientation.
+ bool touch;
+ int finger;
+} touch_event_t;
+
+typedef struct _touch_panel_info_t
+{
+ u8 idx;
+ u8 gpio0;
+ u8 gpio1;
+ u8 gpio2;
+ char *vendor;
+} touch_panel_info_t;
+
+typedef struct _touch_info_t {
+ u16 chip_id;
+ u16 fw_ver;
+ u16 config_id;
+ u16 config_ver;
+ bool clone;
+} touch_info_t;
+
+typedef struct _touch_fw_info_t {
+ u32 fw_id;
+ u16 ftb_ver;
+ u16 fw_rev;
+} touch_fw_info_t;
+
+int touch_poll(touch_event_t *event);
+touch_info_t *touch_get_chip_info();
+touch_panel_info_t *touch_get_panel_vendor();
+int touch_get_fw_info(touch_fw_info_t *fw);
+int touch_get_event_count();
+int touch_panel_ito_test(u8 *err);
+int touch_execute_autotune();
+int touch_switch_sense_mode(u8 mode, bool gis_6_2);
+int touch_sense_enable();
+int touch_power_on();
+void touch_power_off();
+
+#endif /* __TOUCH_H_ */
diff --git a/bdk/libs/compr/blz.c b/bdk/libs/compr/blz.c
new file mode 100644
index 0000000..5bc9e49
--- /dev/null
+++ b/bdk/libs/compr/blz.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018 rajkosto
+ * Copyright (c) 2018 SciresM
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "blz.h"
+
+const blz_footer *blz_get_footer(const u8 *comp_data, u32 comp_data_size, blz_footer *out_footer)
+{
+ if (comp_data_size < sizeof(blz_footer))
+ return NULL;
+
+ const blz_footer *src_footer = (const blz_footer *)&comp_data[comp_data_size - sizeof(blz_footer)];
+ if (out_footer)
+ memcpy(out_footer, src_footer, sizeof(blz_footer)); // Must be a memcpy because no unaligned accesses on ARMv4.
+
+ return src_footer;
+}
+
+// From https://github.com/SciresM/hactool/blob/master/kip.c which is exactly how kernel does it, thanks SciresM!
+int blz_uncompress_inplace(u8 *data, u32 comp_size, const blz_footer *footer)
+{
+ u32 addl_size = footer->addl_size;
+ u32 header_size = footer->header_size;
+ u32 cmp_and_hdr_size = footer->cmp_and_hdr_size;
+
+ u8 *cmp_start = &data[comp_size] - cmp_and_hdr_size;
+ u32 cmp_ofs = cmp_and_hdr_size - header_size;
+ u32 out_ofs = cmp_and_hdr_size + addl_size;
+
+ while (out_ofs)
+ {
+ u8 control = cmp_start[--cmp_ofs];
+ for (u32 i = 0; i < 8; i++)
+ {
+ if (control & 0x80)
+ {
+ if (cmp_ofs < 2)
+ return 0; // Out of bounds.
+
+ cmp_ofs -= 2;
+ u16 seg_val = ((u32)(cmp_start[cmp_ofs + 1]) << 8) | cmp_start[cmp_ofs];
+ u32 seg_size = ((seg_val >> 12) & 0xF) + 3;
+ u32 seg_ofs = (seg_val & 0x0FFF) + 3;
+
+ // Kernel restricts segment copy to stay in bounds.
+ if (out_ofs < seg_size)
+ seg_size = out_ofs;
+
+ out_ofs -= seg_size;
+
+ for (u32 j = 0; j < seg_size; j++)
+ cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs];
+ }
+ else // Copy directly.
+ {
+ if (cmp_ofs < 1)
+ return 0; // Out of bounds.
+
+ cmp_start[--out_ofs] = cmp_start[--cmp_ofs];
+ }
+
+ control <<= 1;
+
+ if (!out_ofs) // Blz works backwards, so if it reaches byte 0, it's done.
+ return 1;
+ }
+ }
+
+ return 1;
+}
+
+int blz_uncompress_srcdest(const u8 *comp_data, u32 comp_data_size, u8 *dst_data, u32 dst_size)
+{
+ blz_footer footer;
+ const blz_footer *comp_footer = blz_get_footer(comp_data, comp_data_size, &footer);
+ if (!comp_footer)
+ return 0;
+
+ // Decompression happens in-place, so need to copy the relevant compressed data first.
+ u32 comp_bytes = (const u8 *)comp_footer - comp_data;
+ memcpy(dst_data, comp_data, comp_bytes);
+ memset(&dst_data[comp_bytes], 0, dst_size - comp_bytes);
+
+ return blz_uncompress_inplace(dst_data, comp_data_size, &footer);
+}
diff --git a/bdk/libs/compr/blz.h b/bdk/libs/compr/blz.h
new file mode 100644
index 0000000..bb058d8
--- /dev/null
+++ b/bdk/libs/compr/blz.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 rajkosto
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _BLZ_H_
+#define _BLZ_H_
+
+#include
+
+typedef struct _blz_footer
+{
+ u32 cmp_and_hdr_size;
+ u32 header_size;
+ u32 addl_size;
+} blz_footer;
+
+// Returns pointer to footer in comp_data if present, additionally copies it to out_footer if not NULL.
+const blz_footer *blz_get_footer(const u8 *comp_data, u32 comp_data_size, blz_footer *out_footer);
+// Returns 0 on failure.
+int blz_uncompress_inplace(u8 *data, u32 comp_size, const blz_footer *footer);
+// Returns 0 on failure.
+int blz_uncompress_srcdest(const u8 *comp_data, u32 comp_data_size, u8 *dst_data, u32 dst_size);
+
+#endif
diff --git a/bdk/libs/compr/lz.c b/bdk/libs/compr/lz.c
new file mode 100644
index 0000000..94b64c6
--- /dev/null
+++ b/bdk/libs/compr/lz.c
@@ -0,0 +1,181 @@
+/*************************************************************************
+* Name: lz.c
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder implementation.
+* Reentrant: Yes
+*
+* The LZ77 compression scheme is a substitutional compression scheme
+* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in
+* its design, and uses no fancy bit level compression.
+*
+* This is my first attempt at an implementation of a LZ77 code/decoder.
+*
+* The principle of the LZ77 compression algorithm is to store repeated
+* occurrences of strings as references to previous occurrences of the same
+* string. The point is that the reference consumes less space than the
+* string itself, provided that the string is long enough (in this
+* implementation, the string has to be at least 4 bytes long, since the
+* minimum coded reference is 3 bytes long). Also note that the term
+* "string" refers to any kind of byte sequence (it does not have to be
+* an ASCII string, for instance).
+*
+* The coder uses a brute force approach to finding string matches in the
+* history buffer (or "sliding window", if you wish), which is very, very
+* slow. I recon the complexity is somewhere between O(n^2) and O(n^3),
+* depending on the input data.
+*
+* There is also a faster implementation that uses a large working buffer
+* in which a "jump table" is stored, which is used to quickly find
+* possible string matches (see the source code for LZ_CompressFast() for
+* more information). The faster method is an order of magnitude faster,
+* but still quite slow compared to other compression methods.
+*
+* The upside is that decompression is very fast, and the compression ratio
+* is often very good.
+*
+* The reference to a string is coded as a (length,offset) pair, where the
+* length indicates the length of the string, and the offset gives the
+* offset from the current data position. To distinguish between string
+* references and literal strings (uncompressed bytes), a string reference
+* is preceded by a marker byte, which is chosen as the least common byte
+* symbol in the input data stream (this marker byte is stored in the
+* output stream as the first byte).
+*
+* Occurrences of the marker byte in the stream are encoded as the marker
+* byte followed by a zero byte, which means that occurrences of the marker
+* byte have to be coded with two bytes.
+*
+* The lengths and offsets are coded in a variable length fashion, allowing
+* values of any magnitude (up to 4294967295 in this implementation).
+*
+* With this compression scheme, the worst case compression result is
+* (257/256)*insize + 1.
+*
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2006 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+
+/*************************************************************************
+* INTERNAL FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* _LZ_ReadVarSize() - Read unsigned integer with variable number of
+* bytes depending on value.
+*************************************************************************/
+
+static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf )
+{
+ unsigned int y, b, num_bytes;
+
+ /* Read complete value (stop when byte contains zero in 8:th bit) */
+ y = 0;
+ num_bytes = 0;
+ do
+ {
+ b = (unsigned int) (*buf ++);
+ y = (y << 7) | (b & 0x0000007f);
+ ++ num_bytes;
+ }
+ while( b & 0x00000080 );
+
+ /* Store value in x */
+ *x = y;
+
+ /* Return number of bytes read */
+ return num_bytes;
+}
+
+
+
+/*************************************************************************
+* PUBLIC FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
+* in - Input (compressed) buffer.
+* out - Output (uncompressed) buffer. This buffer must be large
+* enough to hold the uncompressed data.
+* insize - Number of input bytes.
+*************************************************************************/
+
+unsigned int LZ_Uncompress( const unsigned char *in, unsigned char *out,
+ unsigned int insize )
+{
+ unsigned char marker, symbol;
+ unsigned int i, inpos, outpos, length, offset;
+
+ /* Do we have anything to uncompress? */
+ if( insize < 1 )
+ {
+ return 0;
+ }
+
+ /* Get marker symbol from input stream */
+ marker = in[ 0 ];
+ inpos = 1;
+
+ /* Main decompression loop */
+ outpos = 0;
+ do
+ {
+ symbol = in[ inpos ++ ];
+ if( symbol == marker )
+ {
+ /* We had a marker byte */
+ if( in[ inpos ] == 0 )
+ {
+ /* It was a single occurrence of the marker byte */
+ out[ outpos ++ ] = marker;
+ ++ inpos;
+ }
+ else
+ {
+ /* Extract true length and offset */
+ inpos += _LZ_ReadVarSize( &length, &in[ inpos ] );
+ inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] );
+
+ /* Copy corresponding data from history window */
+ for( i = 0; i < length; ++ i )
+ {
+ out[ outpos ] = out[ outpos - offset ];
+ ++ outpos;
+ }
+ }
+ }
+ else
+ {
+ /* No marker, plain copy */
+ out[ outpos ++ ] = symbol;
+ }
+ }
+ while( inpos < insize );
+
+ return outpos;
+}
diff --git a/bdk/libs/compr/lz.h b/bdk/libs/compr/lz.h
new file mode 100644
index 0000000..ef67055
--- /dev/null
+++ b/bdk/libs/compr/lz.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+* Name: lz.h
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder interface.
+* Reentrant: Yes
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2006 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+#ifndef _lz_h_
+#define _lz_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************************************************************************
+* Function prototypes
+*************************************************************************/
+
+unsigned int LZ_Uncompress( const unsigned char *in, unsigned char *out,
+ unsigned int insize );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _lz_h_ */
diff --git a/bdk/libs/compr/lz4.c b/bdk/libs/compr/lz4.c
new file mode 100644
index 0000000..d6c22c6
--- /dev/null
+++ b/bdk/libs/compr/lz4.c
@@ -0,0 +1,1671 @@
+/*
+ LZ4 - Fast LZ compression algorithm
+ Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
+
+
+/*-************************************
+* Tuning parameters
+**************************************/
+/*
+ * ACCELERATION_DEFAULT :
+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
+ */
+#define ACCELERATION_DEFAULT 1
+
+
+/*-************************************
+* Dependency
+**************************************/
+#define LZ4_STATIC_LINKING_ONLY
+#include "lz4.h"
+/* see also "memory routines" below */
+
+
+/*-************************************
+* Compiler Options
+**************************************/
+#ifndef LZ4_FORCE_INLINE
+# ifdef _MSC_VER /* Visual Studio */
+# define LZ4_FORCE_INLINE static __forceinline
+# else
+# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# ifdef __GNUC__
+# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
+# else
+# define LZ4_FORCE_INLINE static inline
+# endif
+# else
+# define LZ4_FORCE_INLINE static
+# endif /* __STDC_VERSION__ */
+# endif /* _MSC_VER */
+#endif /* LZ4_FORCE_INLINE */
+
+/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE
+ * Gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy,
+ * together with a simple 8-byte copy loop as a fall-back path.
+ * However, this optimization hurts the decompression speed by >30%,
+ * because the execution does not go to the optimized loop
+ * for typical compressible data, and all of the preamble checks
+ * before going to the fall-back path become useless overhead.
+ * This optimization happens only with the -O3 flag, and -O2 generates
+ * a simple 8-byte copy loop.
+ * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy
+ * functions are annotated with __attribute__((optimize("O2"))),
+ * and also LZ4_wildCopy is forcibly inlined, so that the O2 attribute
+ * of LZ4_wildCopy does not affect the compression speed.
+ */
+#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__)
+# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2")))
+# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE
+#else
+# define LZ4_FORCE_O2_GCC_PPC64LE
+# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static
+#endif
+
+/*-************************************
+* Memory routines
+**************************************/
+#include /* malloc, calloc, free */
+#define ALLOC(s) malloc(s)
+#define ALLOC_AND_ZERO(s) zalloc(s)
+#define FREEMEM free
+#include /* memset, memcpy */
+#define MEM_INIT memset
+
+
+/*-************************************
+* Basic Types
+**************************************/
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+typedef uintptr_t uptrval;
+typedef size_t reg_t; /* 32-bits in x32 mode */
+
+/*-************************************
+* Reading and writing into memory
+**************************************/
+static unsigned LZ4_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+
+static U16 LZ4_read16(const void* memPtr)
+{
+ U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static U32 LZ4_read32(const void* memPtr)
+{
+ U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static reg_t LZ4_read_ARCH(const void* memPtr)
+{
+ reg_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static void LZ4_write16(void* memPtr, U16 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static void LZ4_write32(void* memPtr, U32 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static U16 LZ4_readLE16(const void* memPtr)
+{
+ if (LZ4_isLittleEndian()) {
+ return LZ4_read16(memPtr);
+ } else {
+ const BYTE* p = (const BYTE*)memPtr;
+ return (U16)((U16)p[0] + (p[1]<<8));
+ }
+}
+
+static void LZ4_writeLE16(void* memPtr, U16 value)
+{
+ if (LZ4_isLittleEndian()) {
+ LZ4_write16(memPtr, value);
+ } else {
+ BYTE* p = (BYTE*)memPtr;
+ p[0] = (BYTE) value;
+ p[1] = (BYTE)(value>>8);
+ }
+}
+
+/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
+LZ4_FORCE_O2_INLINE_GCC_PPC64LE
+void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* const e = (BYTE*)dstEnd;
+
+ do { memcpy(d,s,8); d+=8; s+=8; } while (d=2)
+# include
+static int g_debuglog_enable = 1;
+# define DEBUGLOG(l, ...) { \
+ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
+ fprintf(stderr, __FILE__ ": "); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, " \n"); \
+ } }
+#else
+# define DEBUGLOG(l, ...) {} /* disabled */
+#endif
+
+
+/*-************************************
+* Common functions
+**************************************/
+static unsigned LZ4_NbCommonBytes (reg_t val)
+{
+ if (LZ4_isLittleEndian()) {
+ if (sizeof(val)==8) {
+ return (__builtin_ctzll((U64)val) >> 3);
+ } else /* 32 bits */ {
+ return (__builtin_ctz((U32)val) >> 3);
+ }
+ } else /* Big Endian CPU */ {
+ if (sizeof(val)==8) { /* 64-bits */
+ return (__builtin_clzll((U64)val) >> 3);
+ } else /* 32 bits */ {
+ return (__builtin_clz((U32)val) >> 3);
+ }
+ }
+}
+
+#define STEPSIZE sizeof(reg_t)
+LZ4_FORCE_INLINE
+unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+ const BYTE* const pStart = pIn;
+
+ if (likely(pIn < pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) {
+ pIn+=STEPSIZE; pMatch+=STEPSIZE;
+ } else {
+ return LZ4_NbCommonBytes(diff);
+ } }
+
+ while (likely(pIn < pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+ pIn += LZ4_NbCommonBytes(diff);
+ return (unsigned)(pIn - pStart);
+ }
+
+ if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+ if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+ if ((pIn compression run slower on incompressible data */
+
+
+/*-************************************
+* Local Structures and types
+**************************************/
+typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
+typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
+
+/**
+ * This enum distinguishes several different modes of accessing previous
+ * content in the stream.
+ *
+ * - noDict : There is no preceding content.
+ * - withPrefix64k : Table entries up to ctx->dictSize before the current blob
+ * blob being compressed are valid and refer to the preceding
+ * content (of length ctx->dictSize), which is available
+ * contiguously preceding in memory the content currently
+ * being compressed.
+ * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere
+ * else in memory, starting at ctx->dictionary with length
+ * ctx->dictSize.
+ * - usingDictCtx : Like usingExtDict, but everything concerning the preceding
+ * content is in a separate context, pointed to by
+ * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table
+ * entries in the current context that refer to positions
+ * preceding the beginning of the current compression are
+ * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx
+ * ->dictSize describe the location and size of the preceding
+ * content, and matches are found by looking in the ctx
+ * ->dictCtx->hashTable.
+ */
+typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+
+/*-************************************
+* Local Utils
+**************************************/
+int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
+const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
+int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
+
+
+/*-******************************
+* Compression functions
+********************************/
+static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+ if (tableType == byU16)
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+ else
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+ static const U64 prime5bytes = 889523592379ULL;
+ static const U64 prime8bytes = 11400714785074694791ULL;
+ const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+ if (LZ4_isLittleEndian())
+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+ else
+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+}
+
+LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+ if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+ return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)
+{
+ switch (tableType)
+ {
+ default: /* fallthrough */
+ case clearedTable: /* fallthrough */
+ case byPtr: { /* illegal! */ return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)idx; return; }
+ }
+}
+
+static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+{
+ switch (tableType)
+ {
+ case clearedTable: { /* illegal! */ return; }
+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
+ }
+}
+
+LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+/* LZ4_getIndexOnHash() :
+ * Index of match position registered in hash table.
+ * hash position must be calculated by using base+index, or dictBase+index.
+ * Assumption 1 : only valid if tableType == byU32 or byU16.
+ * Assumption 2 : h is presumed valid (within limits of hash table)
+ */
+static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)
+{
+ LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);
+ if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h]; }
+ if (tableType == byU16) { const U16* const hashTable = (const U16*) tableBase; return hashTable[h]; }
+ return 0; /* forbidden case */
+}
+
+static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
+ if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }
+ { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
+}
+
+LZ4_FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, const void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+LZ4_FORCE_INLINE void LZ4_prepareTable(
+ LZ4_stream_t_internal* const cctx,
+ const int inputSize,
+ const tableType_t tableType) {
+ /* If the table hasn't been used, it's guaranteed to be zeroed out, and is
+ * therefore safe to use no matter what mode we're in. Otherwise, we figure
+ * out if it's safe to leave as is or whether it needs to be reset.
+ */
+ if (cctx->tableType != clearedTable) {
+ if (cctx->tableType != tableType
+ || (tableType == byU16 && cctx->currentOffset + inputSize >= 0xFFFFU)
+ || (tableType == byU32 && cctx->currentOffset > 1 GB)
+ || tableType == byPtr
+ || inputSize >= 4 KB)
+ {
+ DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
+ MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
+ cctx->currentOffset = 0;
+ cctx->tableType = clearedTable;
+ } else {
+ DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");
+ }
+ }
+
+ /* Adding a gap, so all previous entries are > MAX_DISTANCE back, is faster
+ * than compressing without a gap. However, compressing with
+ * currentOffset == 0 is faster still, so we preserve that case.
+ */
+ if (cctx->currentOffset != 0 && tableType == byU32) {
+ DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");
+ cctx->currentOffset += 64 KB;
+ }
+
+ /* Finally, clear history */
+ cctx->dictCtx = NULL;
+ cctx->dictionary = NULL;
+ cctx->dictSize = 0;
+}
+
+/** LZ4_compress_generic() :
+ inlined, to ensure branches are decided at compilation time */
+LZ4_FORCE_INLINE int LZ4_compress_generic(
+ LZ4_stream_t_internal* const cctx,
+ const char* const source,
+ char* const dest,
+ const int inputSize,
+ const int maxOutputSize,
+ const limitedOutput_directive outputLimited,
+ const tableType_t tableType,
+ const dict_directive dictDirective,
+ const dictIssue_directive dictIssue,
+ const U32 acceleration)
+{
+ const BYTE* ip = (const BYTE*) source;
+
+ U32 const startIndex = cctx->currentOffset;
+ const BYTE* base = (const BYTE*) source - startIndex;
+ const BYTE* lowLimit;
+
+ const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
+ const BYTE* const dictionary =
+ dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
+ const U32 dictSize =
+ dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
+ const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */
+
+ int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
+ U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */
+ const BYTE* const dictEnd = dictionary + dictSize;
+ const BYTE* anchor = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ /* the dictCtx currentOffset is indexed on the start of the dictionary,
+ * while a dictionary in the current context precedes the currentOffset */
+ const BYTE* dictBase = dictDirective == usingDictCtx ?
+ dictionary + dictSize - dictCtx->currentOffset : /* is it possible that dictCtx->currentOffset != dictCtx->dictSize ? Yes if the dictionary context is not reset */
+ dictionary + dictSize - startIndex;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const olimit = op + maxOutputSize;
+
+ U32 offset = 0;
+ U32 forwardH;
+
+ DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType);
+ /* Init conditions */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */
+
+ lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
+
+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+
+ /* Update context state */
+ if (dictDirective == usingDictCtx) {
+ /* Subsequent linked blocks can't use the dictionary. */
+ /* Instead, they use the block we just compressed. */
+ cctx->dictCtx = NULL;
+ cctx->dictSize = (U32)inputSize;
+ } else {
+ cctx->dictSize += (U32)inputSize;
+ }
+ cctx->currentOffset += (U32)inputSize;
+ cctx->tableType = tableType;
+
+ if (inputSizehashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ if (tableType == byPtr) {
+ const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+ } while ( (match+MAX_DISTANCE < ip)
+ || (LZ4_read32(match) != LZ4_read32(ip)) );
+
+ } else { /* byU32, byU16 */
+
+ const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ U32 const current = (U32)(forwardIp - base);
+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+
+ if (dictDirective == usingDictCtx) {
+ if (matchIndex < startIndex) {
+ /* there was no match, try the dictionary */
+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+ match = dictBase + matchIndex;
+ matchIndex += dictDelta; /* make dictCtx index comparable with current context */
+ lowLimit = dictionary;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source;
+ }
+ } else if (dictDirective==usingExtDict) {
+ if (matchIndex < startIndex) {
+ DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex);
+ match = dictBase + matchIndex;
+ lowLimit = dictionary;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source;
+ }
+ } else { /* single continuous memory segment */
+ match = base + matchIndex;
+ }
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+
+ if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) continue; /* match outside of valid area */
+ if ((tableType != byU16) && (current - matchIndex > MAX_DISTANCE)) continue; /* too far - note: works even if matchIndex overflows */
+
+ if (LZ4_read32(match) == LZ4_read32(ip)) {
+ if (maybe_extMem) offset = current - matchIndex;
+ break; /* match found */
+ }
+
+ } while(1);
+ }
+
+ /* Catch up */
+ while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+ /* Encode Literals */
+ { unsigned const litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if ((outputLimited) && /* Check output buffer overflow */
+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
+ return 0;
+ if (litLength >= RUN_MASK) {
+ int len = (int)litLength-RUN_MASK;
+ *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength< matchlimit) limit = matchlimit;
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+ ip += MINMATCH + matchCode;
+ if (ip==limit) {
+ unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);
+ matchCode += more;
+ ip += more;
+ }
+ DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);
+ } else {
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+ ip += MINMATCH + matchCode;
+ DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);
+ }
+
+ if ( outputLimited && /* Check output buffer overflow */
+ (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
+ return 0;
+ if (matchCode >= ML_MASK) {
+ *token += ML_MASK;
+ matchCode -= ML_MASK;
+ LZ4_write32(op, 0xFFFFFFFF);
+ while (matchCode >= 4*255) {
+ op+=4;
+ LZ4_write32(op, 0xFFFFFFFF);
+ matchCode -= 4*255;
+ }
+ op += matchCode / 255;
+ *op++ = (BYTE)(matchCode % 255);
+ } else
+ *token += (BYTE)(matchCode);
+ }
+
+ anchor = ip;
+
+ /* Test end of chunk */
+ if (ip >= mflimitPlusOne) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
+
+ /* Test next position */
+ if (tableType == byPtr) {
+
+ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ if ( (match+MAX_DISTANCE >= ip)
+ && (LZ4_read32(match) == LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ } else { /* byU32, byU16 */
+
+ U32 const h = LZ4_hashPosition(ip, tableType);
+ U32 const current = (U32)(ip-base);
+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+ if (dictDirective == usingDictCtx) {
+ if (matchIndex < startIndex) {
+ /* there was no match, try the dictionary */
+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+ match = dictBase + matchIndex;
+ lowLimit = dictionary; /* required for match length counter */
+ matchIndex += dictDelta;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source; /* required for match length counter */
+ }
+ } else if (dictDirective==usingExtDict) {
+ if (matchIndex < startIndex) {
+ match = dictBase + matchIndex;
+ lowLimit = dictionary; /* required for match length counter */
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source; /* required for match length counter */
+ }
+ } else { /* single memory segment */
+ match = base + matchIndex;
+ }
+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+ if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)
+ && ((tableType==byU16) ? 1 : (current - matchIndex <= MAX_DISTANCE))
+ && (LZ4_read32(match) == LZ4_read32(ip)) ) {
+ token=op++;
+ *token=0;
+ if (maybe_extMem) offset = current - matchIndex;
+ DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));
+ goto _next_match;
+ }
+ }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t const lastRun = (size_t)(iend - anchor);
+ if ( (outputLimited) && /* Check output buffer overflow */
+ ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
+ return 0;
+ if (lastRun >= RUN_MASK) {
+ size_t accumulator = lastRun - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRun<internal_donotuse;
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit) {
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > MAX_DISTANCE)) ? byPtr : byU32;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ if (inputSize < LZ4_64Klimit) {;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > MAX_DISTANCE)) ? byPtr : byU32;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ }
+}
+
+/**
+ * LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step. It is only safe
+ * to call if the state buffer is known to be correctly initialized already
+ * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of
+ * "correctly initialized").
+ */
+int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
+{
+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ if (dstCapacity >= LZ4_compressBound(srcSize)) {
+ if (srcSize < LZ4_64Klimit) {
+ const tableType_t tableType = byU16;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ if (ctx->currentOffset) {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, dictSmall, acceleration);
+ } else {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ if (srcSize < LZ4_64Klimit) {
+ const tableType_t tableType = byU16;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ if (ctx->currentOffset) {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);
+ } else {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ }
+}
+
+
+int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ int result;
+ LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
+ LZ4_stream_t* const ctxPtr = ctx;
+ result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+ FREEMEM(ctx);
+
+ return result;
+}
+
+
+int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
+{
+ return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
+}
+
+
+/* hidden debug function */
+/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
+int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ int result;
+ LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
+ LZ4_resetStream(ctx);
+
+ if (inputSize < LZ4_64Klimit)
+ result = LZ4_compress_generic(&ctx->internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ else
+ result = LZ4_compress_generic(&ctx->internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+
+ FREEMEM(ctx);
+
+ return result;
+}
+
+
+/*-******************************
+* *_destSize() variant
+********************************/
+
+static int LZ4_compress_destSize_generic(
+ LZ4_stream_t_internal* const ctx,
+ const char* const src,
+ char* const dst,
+ int* const srcSizePtr,
+ const int targetDstSize,
+ const tableType_t tableType)
+{
+ const BYTE* ip = (const BYTE*) src;
+ const BYTE* base = (const BYTE*) src;
+ const BYTE* lowLimit = (const BYTE*) src;
+ const BYTE* anchor = ip;
+ const BYTE* const iend = ip + *srcSizePtr;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ BYTE* op = (BYTE*) dst;
+ BYTE* const oend = op + targetDstSize;
+ BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
+ BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
+ BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
+
+ U32 forwardH;
+
+
+ /* Init conditions */
+ if (targetDstSize < 1) return 0; /* Impossible to store anything */
+ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
+ if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+ if (*srcSizePtrhashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ { const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = 1 << LZ4_skipTrigger;
+
+ do {
+ U32 h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base);
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base);
+
+ } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match) != LZ4_read32(ip)) );
+ }
+
+ /* Catch up */
+ while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+ /* Encode Literal length */
+ { unsigned litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if (op + ((litLength+240)/255) + litLength > oMaxLit) {
+ /* Not enough space for a last match */
+ op--;
+ goto _last_literals;
+ }
+ if (litLength>=RUN_MASK) {
+ unsigned len = litLength - RUN_MASK;
+ *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength< oMaxMatch) {
+ /* Match description too long : reduce it */
+ matchLength = (15-1) + (oMaxMatch-op) * 255;
+ }
+ ip += MINMATCH + matchLength;
+
+ if (matchLength>=ML_MASK) {
+ *token += ML_MASK;
+ matchLength -= ML_MASK;
+ while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
+ *op++ = (BYTE)matchLength;
+ }
+ else *token += (BYTE)(matchLength);
+ }
+
+ anchor = ip;
+
+ /* Test end of block */
+ if (ip > mflimit) break;
+ if (op > oMaxSeq) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, ctx->hashTable, tableType, base);
+
+ /* Test next position */
+ match = LZ4_getPosition(ip, ctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+ if ( (match+MAX_DISTANCE>=ip)
+ && (LZ4_read32(match)==LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t lastRunSize = (size_t)(iend - anchor);
+ if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) {
+ /* adapt lastRunSize to fill 'dst' */
+ lastRunSize = (oend-op) - 1;
+ lastRunSize -= (lastRunSize+240)/255;
+ }
+ ip = anchor + lastRunSize;
+
+ if (lastRunSize >= RUN_MASK) {
+ size_t accumulator = lastRunSize - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
+ return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
+ } else {
+ if (*srcSizePtr < LZ4_64Klimit) {
+ return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16);
+ } else {
+ tableType_t const tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32;
+ return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, tableType);
+ } }
+}
+
+
+int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+ LZ4_stream_t* ctxBody = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));;
+ LZ4_stream_t* ctx = ctxBody;
+
+ int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
+
+ FREEMEM(ctxBody);
+
+ return result;
+}
+
+
+
+/*-******************************
+* Streaming functions
+********************************/
+
+LZ4_stream_t* LZ4_createStream(void)
+{
+ LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
+ LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
+ DEBUGLOG(4, "LZ4_createStream %p", lz4s);
+ if (lz4s == NULL) return NULL;
+ LZ4_resetStream(lz4s);
+ return lz4s;
+}
+
+void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+ DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
+ MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
+ LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);
+}
+
+int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
+{
+ if (!LZ4_stream) return 0; /* support free on NULL */
+ DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
+ FREEMEM(LZ4_stream);
+ return (0);
+}
+
+
+#define HASH_UNIT sizeof(reg_t)
+int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
+{
+ LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;
+ const tableType_t tableType = byU32;
+ const BYTE* p = (const BYTE*)dictionary;
+ const BYTE* const dictEnd = p + dictSize;
+ const BYTE* base;
+
+ DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
+
+ LZ4_prepareTable(dict, 0, tableType);
+
+ /* We always increment the offset by 64 KB, since, if the dict is longer,
+ * we truncate it to the last 64k, and if it's shorter, we still want to
+ * advance by a whole window length so we can provide the guarantee that
+ * there are only valid offsets in the window, which allows an optimization
+ * in LZ4_compress_fast_continue() where it uses noDictIssue even when the
+ * dictionary isn't a full 64k. */
+
+ if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
+ base = dictEnd - 64 KB - dict->currentOffset;
+ dict->dictionary = p;
+ dict->dictSize = (U32)(dictEnd - p);
+ dict->currentOffset += 64 KB;
+ dict->tableType = tableType;
+
+ if (dictSize < (int)HASH_UNIT) {
+ return 0;
+ }
+
+ while (p <= dictEnd-HASH_UNIT) {
+ LZ4_putPosition(p, dict->hashTable, tableType, base);
+ p+=3;
+ }
+
+ return dict->dictSize;
+}
+
+void LZ4_attach_dictionary(LZ4_stream_t *working_stream, const LZ4_stream_t *dictionary_stream) {
+ if (dictionary_stream != NULL) {
+ /* If the current offset is zero, we will never look in the
+ * external dictionary context, since there is no value a table
+ * entry can take that indicate a miss. In that case, we need
+ * to bump the offset to something non-zero.
+ */
+ if (working_stream->internal_donotuse.currentOffset == 0) {
+ working_stream->internal_donotuse.currentOffset = 64 KB;
+ }
+ working_stream->internal_donotuse.dictCtx = &(dictionary_stream->internal_donotuse);
+ } else {
+ working_stream->internal_donotuse.dictCtx = NULL;
+ }
+}
+
+
+static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)
+{
+ if (LZ4_dict->currentOffset + nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */
+ /* rescale hash table */
+ U32 const delta = LZ4_dict->currentOffset - 64 KB;
+ const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+ int i;
+ DEBUGLOG(4, "LZ4_renormDictT");
+ for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0;
+ else LZ4_dict->hashTable[i] -= delta;
+ }
+ LZ4_dict->currentOffset = 64 KB;
+ if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
+ LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+ }
+}
+
+
+int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ const tableType_t tableType = byU32;
+ LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;
+ const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+ if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */
+ LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ /* Check overlapping input/dictionary space */
+ { const BYTE* sourceEnd = (const BYTE*) source + inputSize;
+ if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {
+ streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+ if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
+ if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
+ streamPtr->dictionary = dictEnd - streamPtr->dictSize;
+ }
+ }
+
+ /* prefix mode : source data follows dictionary */
+ if (dictEnd == (const BYTE*)source) {
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);
+ else
+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);
+ }
+
+ /* external dictionary mode */
+ { int result;
+ if (streamPtr->dictCtx) {
+ /* We depend here on the fact that dictCtx'es (produced by
+ * LZ4_loadDict) guarantee that their tables contain no references
+ * to offsets between dictCtx->currentOffset - 64 KB and
+ * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe
+ * to use noDictIssue even when the dict isn't a full 64 KB.
+ */
+ if (inputSize > 4 KB) {
+ /* For compressing large blobs, it is faster to pay the setup
+ * cost to copy the dictionary's tables into the active context,
+ * so that the compression loop is only looking into one table.
+ */
+ memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t));
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);
+ }
+ } else {
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+ }
+ }
+ streamPtr->dictionary = (const BYTE*)source;
+ streamPtr->dictSize = (U32)inputSize;
+ return result;
+ }
+}
+
+
+/* Hidden debug function, to force-test external dictionary mode */
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
+{
+ LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;
+ int result;
+
+ LZ4_renormDictT(streamPtr, srcSize);
+
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, 0, notLimited, byU32, usingExtDict, dictSmall, 1);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+ }
+
+ streamPtr->dictionary = (const BYTE*)source;
+ streamPtr->dictSize = (U32)srcSize;
+
+ return result;
+}
+
+
+/*! LZ4_saveDict() :
+ * If previously compressed data block is not guaranteed to remain available at its memory location,
+ * save it into a safer place (char* safeBuffer).
+ * Note : you don't need to call LZ4_loadDict() afterwards,
+ * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
+ * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
+{
+ LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
+ const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
+
+ if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
+ if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
+
+ memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
+
+ dict->dictionary = (const BYTE*)safeBuffer;
+ dict->dictSize = (U32)dictSize;
+
+ return dictSize;
+}
+
+
+
+/*-*****************************
+* Decompression functions
+*******************************/
+/*! LZ4_decompress_generic() :
+ * This generic decompression function covers all use cases.
+ * It shall be instantiated several times, using different sets of directives.
+ * Note that it is important for performance that this function really get inlined,
+ * in order to remove useless branches during compilation optimization.
+ */
+LZ4_FORCE_O2_GCC_PPC64LE
+LZ4_FORCE_INLINE int LZ4_decompress_generic(
+ const char* const src,
+ char* const dst,
+ int srcSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
+
+ int endOnInput, /* endOnOutputSize, endOnInputSize */
+ int partialDecoding, /* full, partial */
+ int targetOutputSize, /* only used if partialDecoding==partial */
+ int dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */
+ const BYTE* const dictStart, /* only if dict==usingExtDict */
+ const size_t dictSize /* note : = 0 if noDict */
+ )
+{
+ const BYTE* ip = (const BYTE*) src;
+ const BYTE* const iend = ip + srcSize;
+
+ BYTE* op = (BYTE*) dst;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
+ BYTE* oexit = op + targetOutputSize;
+
+ const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+ const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4};
+ const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3};
+
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+ /* Special cases */
+ if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => just decode everything */
+ if ((endOnInput) && (unlikely(outputSize==0))) return ((srcSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+ /* Main Loop : decode sequences */
+ while (1) {
+ size_t length;
+ const BYTE* match;
+ size_t offset;
+
+ unsigned const token = *ip++;
+
+ /* shortcut for common case :
+ * in most circumstances, we expect to decode small matches (<= 18 bytes) separated by few literals (<= 14 bytes).
+ * this shortcut was tested on x86 and x64, where it improves decoding speed.
+ * it has not yet been benchmarked on ARM, Power, mips, etc. */
+ if (((ip + 14 /*maxLL*/ + 2 /*offset*/ <= iend)
+ & (op + 14 /*maxLL*/ + 18 /*maxML*/ <= oend))
+ & ((token < (15<> ML_BITS;
+ size_t const off = LZ4_readLE16(ip+ll);
+ const BYTE* const matchPtr = op + ll - off; /* pointer underflow risk ? */
+ if ((off >= 8) /* do not deal with overlapping matches */ & (matchPtr >= lowPrefix)) {
+ size_t const ml = (token & ML_MASK) + MINMATCH;
+ memcpy(op, ip, 16); op += ll; ip += ll + 2 /*offset*/;
+ memcpy(op + 0, matchPtr + 0, 8);
+ memcpy(op + 8, matchPtr + 8, 8);
+ memcpy(op +16, matchPtr +16, 2);
+ op += ml;
+ continue;
+ }
+ }
+
+ /* decode literal length */
+ if ((length=(token>>ML_BITS)) == RUN_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ length += s;
+ } while ( likely(endOnInput ? ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ {
+ if (partialDecoding) {
+ if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ } else {
+ if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ }
+ memcpy(op, ip, length);
+ ip += length;
+ op += length;
+ break; /* Necessarily EOF, due to parsing restrictions */
+ }
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+ if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */
+ LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */
+
+ /* get matchlength */
+ length = token & ML_MASK;
+ if (length == ML_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+ length += s;
+ } while (s==255);
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match can be copied as a single segment from external dictionary */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
+ } else {
+ /* match encompass external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix-match);
+ size_t const restSize = length - copySize;
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ } else {
+ memcpy(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+ if (unlikely(offset<8)) {
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += inc32table[offset];
+ memcpy(op+4, match, 4);
+ match -= dec64table[offset];
+ } else { memcpy(op, match, 8); match+=8; }
+ op += 8;
+
+ if (unlikely(cpy>oend-12)) {
+ BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+ if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+ if (op < oCopyLimit) {
+ LZ4_wildCopy(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
+ while (op16) LZ4_wildCopy(op+8, match+8, cpy);
+ }
+ op = cpy; /* correction */
+ }
+
+ /* end of decoding */
+ if (endOnInput)
+ return (int) (((char*)op)-dst); /* Nb of output bytes decoded */
+ else
+ return (int) (((const char*)ip)-src); /* Nb of input bytes read */
+
+ /* Overflow error detected */
+_output_error:
+ return (int) (-(((const char*)ip)-src))-1;
+}
+
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
+}
+
+
+/*===== streaming decompression functions =====*/
+
+LZ4_streamDecode_t* LZ4_createStreamDecode(void)
+{
+ LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));
+ return lz4s;
+}
+
+int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
+{
+ if (!LZ4_stream) return 0; /* support free on NULL */
+ FREEMEM(LZ4_stream);
+ return 0;
+}
+
+/*!
+ * LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * This function is not necessary if previous data is still available where it was decoded.
+ * Loading a size of 0 is allowed (same effect as no dictionary).
+ * Return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ lz4sd->prefixSize = (size_t) dictSize;
+ lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
+ lz4sd->externalDict = NULL;
+ lz4sd->extDictSize = 0;
+ return 1;
+}
+
+/*
+*_continue() :
+ These decoding functions allow decompression of multiple blocks in "streaming" mode.
+ Previously decoded blocks must still be available at the memory position where they were decoded.
+ If it's not possible, save the relevant part of decoded data into a safe buffer,
+ and indicate where it stands using LZ4_setStreamDecode()
+*/
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE*)dest) {
+ result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize += result;
+ lz4sd->prefixEnd += result;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = result;
+ lz4sd->prefixEnd = (BYTE*)dest + result;
+ }
+
+ return result;
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE*)dest) {
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize += originalSize;
+ lz4sd->prefixEnd += originalSize;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = originalSize;
+ lz4sd->prefixEnd = (BYTE*)dest + originalSize;
+ }
+
+ return result;
+}
+
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+ These decoding functions work the same as "_continue" ones,
+ the dictionary must be explicitly provided within parameters
+*/
+
+LZ4_FORCE_O2_GCC_PPC64LE
+LZ4_FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
+{
+ if (dictSize==0)
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
+ if (dictStart+dictSize == dest) {
+ if (dictSize >= (int)(64 KB - 1))
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
+ }
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
+}
+
+/* debug function */
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+
+/*=*************************************************
+* Obsolete Functions
+***************************************************/
+/* obsolete compression functions */
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
+int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
+
+/*
+These function names are deprecated and should no longer be used.
+They are only provided here for compatibility with older user programs.
+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
+*/
+int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
+
+/* Obsolete Streaming functions */
+
+int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
+
+int LZ4_resetStreamState(void* state, char* inputBuffer)
+{
+ (void)inputBuffer;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ return 0;
+}
+
+void* LZ4_create (char* inputBuffer)
+{
+ (void)inputBuffer;
+ return LZ4_createStream();
+}
+
+char* LZ4_slideInputBuffer (void* state)
+{
+ /* avoid const char * -> char * conversion warning */
+ return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;
+}
+
+/* Obsolete streaming decompression functions */
+
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+#endif /* LZ4_COMMONDEFS_ONLY */
diff --git a/bdk/libs/compr/lz4.h b/bdk/libs/compr/lz4.h
new file mode 100644
index 0000000..0dfa19e
--- /dev/null
+++ b/bdk/libs/compr/lz4.h
@@ -0,0 +1,569 @@
+/*
+ * LZ4 - Fast LZ compression algorithm
+ * Header File
+ * Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef LZ4_H_2983827168210
+#define LZ4_H_2983827168210
+
+/* --- Dependency --- */
+#include /* size_t */
+
+
+/**
+ Introduction
+
+ LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+ scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+ multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+ The LZ4 compression library provides in-memory compression and decompression functions.
+ Compression can be done in:
+ - a single step (described as Simple Functions)
+ - a single step, reusing a context (described in Advanced Functions)
+ - unbounded multiple steps (described as Streaming compression)
+
+ lz4.h provides block compression functions. It gives full buffer control to user.
+ Decompressing an lz4-compressed block also requires metadata (such as compressed size).
+ Each application is free to encode such metadata in whichever way it wants.
+
+ An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
+ take care of encoding standard metadata alongside LZ4-compressed blocks.
+ If your application requires interoperability, it's recommended to use it.
+ A library is provided to take care of it, see lz4frame.h.
+*/
+
+/*^***************************************************************
+* Export parameters
+*****************************************************************/
+/*
+* LZ4_DLL_EXPORT :
+* Enable exporting of functions when building a Windows DLL
+* LZ4LIB_VISIBILITY :
+* Control library symbols visibility.
+*/
+#ifndef LZ4LIB_VISIBILITY
+# if defined(__GNUC__) && (__GNUC__ >= 4)
+# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
+# else
+# define LZ4LIB_VISIBILITY
+# endif
+#endif
+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
+# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
+# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+# define LZ4LIB_API LZ4LIB_VISIBILITY
+#endif
+
+/*------ Version ------*/
+#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
+#define LZ4_VERSION_MINOR 8 /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */
+
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
+
+LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */
+LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; unseful to check dll version */
+
+
+/*-************************************
+* Tuning parameter
+**************************************/
+/*!
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ * Increasing memory usage improves compression ratio
+ * Reduced memory usage may improve speed, thanks to cache effect
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#ifndef LZ4_MEMORY_USAGE
+# define LZ4_MEMORY_USAGE 14
+#endif
+
+/*-************************************
+* Simple Functions
+**************************************/
+/*! LZ4_compress_default() :
+ Compresses 'srcSize' bytes from buffer 'src'
+ into already allocated 'dst' buffer of size 'dstCapacity'.
+ Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
+ It also runs faster, so it's a recommended setting.
+ If the function cannot compress 'src' into a more limited 'dst' budget,
+ compression stops *immediately*, and the function result is zero.
+ Note : as a consequence, 'dst' content is not valid.
+ Note 2 : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
+ srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
+ dstCapacity : size of buffer 'dst' (which must be already allocated)
+ return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
+ or 0 if compression fails */
+LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
+
+/*! LZ4_decompress_safe() :
+ compressedSize : is the exact complete size of the compressed block.
+ dstCapacity : is the size of destination buffer, which must be already allocated.
+ return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
+ If destination buffer is not large enough, decoding will stop and output an error code (negative value).
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ This function is protected against malicious data packets.
+*/
+LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
+
+
+/*-************************************
+* Advanced Functions
+**************************************/
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+/*!
+LZ4_compressBound() :
+ Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
+ This function is primarily useful for memory allocation purposes (destination buffer size).
+ Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
+ Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
+ inputSize : max supported value is LZ4_MAX_INPUT_SIZE
+ return : maximum output size in a "worst case" scenario
+ or 0, if input size is incorrect (too large or negative)
+*/
+LZ4LIB_API int LZ4_compressBound(int inputSize);
+
+/*!
+LZ4_compress_fast() :
+ Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
+ The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
+ It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
+ An acceleration value of "1" is the same as regular LZ4_compress_default()
+ Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c).
+*/
+LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*!
+LZ4_compress_fast_extState() :
+ Same compression function, just using an externally allocated memory space to store compression state.
+ Use LZ4_sizeofState() to know how much memory must be allocated,
+ and allocate it on 8-bytes boundaries (using malloc() typically).
+ Then, provide it as 'void* state' to compression function.
+*/
+LZ4LIB_API int LZ4_sizeofState(void);
+LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*!
+LZ4_compress_destSize() :
+ Reverse the logic : compresses as much data as possible from 'src' buffer
+ into already allocated buffer 'dst' of size 'targetDestSize'.
+ This function either compresses the entire 'src' content into 'dst' if it's large enough,
+ or fill 'dst' buffer completely with as much data as possible from 'src'.
+ *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
+ New value is necessarily <= old value.
+ return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
+ or 0 if compression fails
+*/
+LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
+
+
+/*!
+LZ4_decompress_fast() : **unsafe!**
+This function is a bit faster than LZ4_decompress_safe(),
+but doesn't provide any security guarantee.
+ originalSize : is the uncompressed size to regenerate
+ Destination buffer must be already allocated, and its size must be >= 'originalSize' bytes.
+ return : number of bytes read from source buffer (== compressed size).
+ If the source stream is detected malformed, the function stops decoding and return a negative result.
+ note : This function respects memory boundaries for *properly formed* compressed data.
+ However, it does not provide any protection against malicious input.
+ It also doesn't know 'src' size, and implies it's >= compressed size.
+ Use this function in trusted environment **only**.
+*/
+LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
+
+/*!
+LZ4_decompress_safe_partial() :
+ This function decompress a compressed block of size 'srcSize' at position 'src'
+ into destination buffer 'dst' of size 'dstCapacity'.
+ The function will decompress a minimum of 'targetOutputSize' bytes, and stop after that.
+ However, it's not accurate, and may write more than 'targetOutputSize' (but always <= dstCapacity).
+ @return : the number of bytes decoded in the destination buffer (necessarily <= dstCapacity)
+ Note : this number can also be < targetOutputSize, if compressed block contains less data.
+ Therefore, always control how many bytes were decoded.
+ If source stream is detected malformed, function returns a negative result.
+ This function is protected against malicious data packets.
+*/
+LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
+
+
+/*-*********************************************
+* Streaming Compression Functions
+***********************************************/
+typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStream() and LZ4_freeStream() :
+ * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
+ * LZ4_freeStream() releases its memory.
+ */
+LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
+LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_resetStream() :
+ * An LZ4_stream_t structure can be allocated once and re-used multiple times.
+ * Use this function to start compressing a new stream.
+ */
+LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_loadDict() :
+ * Use this function to load a static dictionary into LZ4_stream_t.
+ * Any previous data will be forgotten, only 'dictionary' will remain in memory.
+ * Loading a size of 0 is allowed, and is the same as reset.
+ * @return : dictionary size, in bytes (necessarily <= 64 KB)
+ */
+LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
+
+/*! LZ4_compress_fast_continue() :
+ * Compress 'src' content using data from previously compressed blocks, for better compression ratio.
+ * 'dst' buffer must be already allocated.
+ * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ *
+ * Important : The previous 64KB of compressed data is assumed to remain present and unmodified in memory!
+ *
+ * Special 1 : When input is a double-buffer, they can have any size, including < 64 KB.
+ * Make sure that buffers are separated by at least one byte.
+ * This way, each block only depends on previous block.
+ * Special 2 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
+ *
+ * @return : size of compressed block
+ * or 0 if there is an error (typically, cannot fit into 'dst').
+ * After an error, the stream status is invalid, it can only be reset or freed.
+ */
+LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_saveDict() :
+ * If last 64KB data cannot be guaranteed to remain available at its current memory location,
+ * save it into a safer place (char* safeBuffer).
+ * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
+ * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
+ * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
+ */
+LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
+
+
+/*-**********************************************
+* Streaming Decompression Functions
+* Bufferless synchronous API
+************************************************/
+typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
+ * creation / destruction of streaming decompression tracking structure.
+ * A tracking structure can be re-used multiple times sequentially. */
+LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
+LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
+
+/*! LZ4_setStreamDecode() :
+ * An LZ4_streamDecode_t structure can be allocated once and re-used multiple times.
+ * Use this function to start decompression of a new stream of blocks.
+ * A dictionary can optionnally be set. Use NULL or size 0 for a reset order.
+ * @return : 1 if OK, 0 if error
+ */
+LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
+
+/*! LZ4_decompress_*_continue() :
+ * These decoding functions allow decompression of consecutive blocks in "streaming" mode.
+ * A block is an unsplittable entity, it must be presented entirely to a decompression function.
+ * Decompression functions only accept one block at a time.
+ * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.
+ * If less than 64KB of data has been decoded all the data must be present.
+ *
+ * Special : if application sets a ring buffer for decompression, it must respect one of the following conditions :
+ * - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
+ * In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
+ * - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ * maxBlockSize is implementation dependent. It's the maximum size of any single block.
+ * In which case, encoding and decoding buffers do not need to be synchronized,
+ * and encoding ring buffer can have any size, including small ones ( < 64 KB).
+ * - _At least_ 64 KB + 8 bytes + maxBlockSize.
+ * In which case, encoding and decoding buffers do not need to be synchronized,
+ * and encoding ring buffer can have any size, including larger than decoding buffer.
+ * Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
+ * and indicate where it is saved using LZ4_setStreamDecode() before decompressing next block.
+*/
+LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);
+LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
+
+
+/*! LZ4_decompress_*_usingDict() :
+ * These decoding functions work the same as
+ * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
+ * They are stand-alone, and don't need an LZ4_streamDecode_t structure.
+ */
+LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);
+LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
+
+
+/*^**********************************************
+ * !!!!!! STATIC LINKING ONLY !!!!!!
+ ***********************************************/
+
+/*-************************************
+ * Unstable declarations
+ **************************************
+ * Declarations in this section should be considered unstable.
+ * Use at your own peril, etc., etc.
+ * They may be removed in the future.
+ * Their signatures may change.
+ **************************************/
+
+#ifdef LZ4_STATIC_LINKING_ONLY
+
+/*! LZ4_resetStream_fast() :
+ * When an LZ4_stream_t is known to be in a internally coherent state,
+ * it can often be prepared for a new compression with almost no work, only
+ * sometimes falling back to the full, expensive reset that is always required
+ * when the stream is in an indeterminate state (i.e., the reset performed by
+ * LZ4_resetStream()).
+ *
+ * LZ4_streams are guaranteed to be in a valid state when:
+ * - returned from LZ4_createStream()
+ * - reset by LZ4_resetStream()
+ * - memset(stream, 0, sizeof(LZ4_stream_t))
+ * - the stream was in a valid state and was reset by LZ4_resetStream_fast()
+ * - the stream was in a valid state and was then used in any compression call
+ * that returned success
+ * - the stream was in an indeterminate state and was used in a compression
+ * call that fully reset the state (LZ4_compress_fast_extState()) and that
+ * returned success
+ */
+LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
+
+/*! LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step. It is only safe
+ * to call if the state buffer is known to be correctly initialized already
+ * (see above comment on LZ4_resetStream_fast() for a definition of "correctly
+ * initialized"). From a high level, the difference is that this function
+ * initializes the provided state with a call to LZ4_resetStream_fast() while
+ * LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
+ */
+LZ4LIB_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_attach_dictionary() :
+ * This is an experimental API that allows for the efficient use of a
+ * static dictionary many times.
+ *
+ * Rather than re-loading the dictionary buffer into a working context before
+ * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
+ * working LZ4_stream_t, this function introduces a no-copy setup mechanism,
+ * in which the working stream references the dictionary stream in-place.
+ *
+ * Several assumptions are made about the state of the dictionary stream.
+ * Currently, only streams which have been prepared by LZ4_loadDict() should
+ * be expected to work.
+ *
+ * Alternatively, the provided dictionary stream pointer may be NULL, in which
+ * case any existing dictionary stream is unset.
+ *
+ * If a dictionary is provided, it replaces any pre-existing stream history.
+ * The dictionary contents are the only history that can be referenced and
+ * logically immediately precede the data compressed in the first subsequent
+ * compression call.
+ *
+ * The dictionary will only remain attached to the working stream through the
+ * first compression call, at the end of which it is cleared. The dictionary
+ * stream (and source buffer) must remain in-place / accessible / unchanged
+ * through the completion of the first compression call on the stream.
+ */
+LZ4LIB_API void LZ4_attach_dictionary(LZ4_stream_t *working_stream, const LZ4_stream_t *dictionary_stream);
+
+#endif
+
+/*-************************************
+ * Private definitions
+ **************************************
+ * Do not use these definitions.
+ * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
+ * Using these definitions will expose code to API and/or ABI break in future versions of the library.
+ **************************************/
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
+
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+#include
+
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
+ uint32_t hashTable[LZ4_HASH_SIZE_U32];
+ uint32_t currentOffset;
+ uint16_t initCheck;
+ uint16_t tableType;
+ const uint8_t* dictionary;
+ const LZ4_stream_t_internal* dictCtx;
+ uint32_t dictSize;
+};
+
+typedef struct {
+ const uint8_t* externalDict;
+ size_t extDictSize;
+ const uint8_t* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#else
+
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
+ unsigned int hashTable[LZ4_HASH_SIZE_U32];
+ unsigned int currentOffset;
+ unsigned short initCheck;
+ unsigned short tableType;
+ const unsigned char* dictionary;
+ const LZ4_stream_t_internal* dictCtx;
+ unsigned int dictSize;
+};
+
+typedef struct {
+ const unsigned char* externalDict;
+ size_t extDictSize;
+ const unsigned char* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#endif
+
+/*!
+ * LZ4_stream_t :
+ * information structure to track an LZ4 stream.
+ * init this structure before first use.
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * it may change in a future version !
+ */
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+union LZ4_stream_u {
+ unsigned long long table[LZ4_STREAMSIZE_U64];
+ LZ4_stream_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_stream_t */
+
+
+/*!
+ * LZ4_streamDecode_t :
+ * information structure to track an LZ4 stream during decompression.
+ * init this structure using LZ4_setStreamDecode (or memset()) before first use
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * and may change in a future version !
+ */
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+union LZ4_streamDecode_u {
+ unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+ LZ4_streamDecode_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_streamDecode_t */
+
+
+/*-************************************
+* Obsolete Functions
+**************************************/
+
+/*! Deprecation warnings
+ Should deprecation warnings be a problem,
+ it is generally possible to disable them,
+ typically with -Wno-deprecated-declarations for gcc
+ or _CRT_SECURE_NO_WARNINGS in Visual.
+ Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */
+#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
+# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
+#else
+# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define LZ4_DEPRECATED(message) [[deprecated(message)]]
+# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
+# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
+# elif (LZ4_GCC_VERSION >= 301)
+# define LZ4_DEPRECATED(message) __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
+# define LZ4_DEPRECATED(message)
+# endif
+#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
+
+/* Obsolete compression functions */
+LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* source, char* dest, int sourceSize);
+LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/* Obsolete decompression functions */
+LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
+LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
+
+/* Obsolete streaming functions; degraded functionality; do not use!
+ *
+ * In order to perform streaming compression, these functions depended on data
+ * that is no longer tracked in the state. They have been preserved as well as
+ * possible: using them will still produce a correct output. However, they don't
+ * actually retain any history between compression calls. The compression ratio
+ * achieved will therefore be no better than compressing each chunk
+ * independently.
+ */
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
+LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
+
+/* Obsolete streaming decoding functions */
+LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
+LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+
+#endif /* LZ4_H_2983827168210 */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/bdk/libs/fatfs/diskio.h b/bdk/libs/fatfs/diskio.h
index ca9894b..d4357eb 100644
--- a/bdk/libs/fatfs/diskio.h
+++ b/bdk/libs/fatfs/diskio.h
@@ -1,90 +1,70 @@
-/*-----------------------------------------------------------------------/
-/ Low level disk interface modlue include file (C)ChaN, 2014 /
-/-----------------------------------------------------------------------*/
-
-#ifndef _DISKIO_DEFINED
-#define _DISKIO_DEFINED
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-
-/* Status of Disk Functions */
-typedef BYTE DSTATUS;
-
-/* Results of Disk Functions */
-typedef enum {
- RES_OK = 0, /* 0: Successful */
- RES_ERROR, /* 1: R/W Error */
- RES_WRPRT, /* 2: Write Protected */
- RES_NOTRDY, /* 3: Not Ready */
- RES_PARERR /* 4: Invalid Parameter */
-} DRESULT;
-
-typedef enum {
- DRIVE_SD = 0,
- DRIVE_RAM = 1,
- DRIVE_EMMC = 2,
- DRIVE_BIS = 3,
- DRIVE_EMU = 4
-} DDRIVE;
-
-
-/*---------------------------------------*/
-/* Prototypes for disk control functions */
-
-
-DSTATUS disk_initialize (BYTE pdrv);
-DSTATUS disk_status (BYTE pdrv);
-DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
-DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
-DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
-DRESULT disk_set_info (BYTE pdrv, BYTE cmd, void *buff);
-
-
-/* Disk Status Bits (DSTATUS) */
-
-#define STA_NOINIT 0x01 /* Drive not initialized */
-#define STA_NODISK 0x02 /* No medium in the drive */
-#define STA_PROTECT 0x04 /* Write protected */
-
-
-/* Command code for disk_ioctrl fucntion */
-
-/* Generic command (Used by FatFs) */
-#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
-#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
-#define SET_SECTOR_COUNT 1 /* Set media size (needed at FF_USE_MKFS == 1) */
-#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
-#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
-#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
-#define SET_SECTOR_OFFSET 5 /* Set media logical offset */
-
-/* Generic command (Not used by FatFs) */
-#define CTRL_POWER 5 /* Get/Set power status */
-#define CTRL_LOCK 6 /* Lock/Unlock media removal */
-#define CTRL_EJECT 7 /* Eject media */
-#define CTRL_FORMAT 8 /* Create physical format on the media */
-
-/* MMC/SDC specific ioctl command */
-#define MMC_GET_TYPE 10 /* Get card type */
-#define MMC_GET_CSD 11 /* Get CSD */
-#define MMC_GET_CID 12 /* Get CID */
-#define MMC_GET_OCR 13 /* Get OCR */
-#define MMC_GET_SDSTAT 14 /* Get SD status */
-#define ISDIO_READ 55 /* Read data form SD iSDIO register */
-#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
-#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
-
-/* ATA/CF specific ioctl command */
-#define ATA_GET_REV 20 /* Get F/W revision */
-#define ATA_GET_MODEL 21 /* Get model name */
-#define ATA_GET_SN 22 /* Get serial number */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+/*-----------------------------------------------------------------------/
+/ Low level disk interface modlue include file (C)ChaN, 2014 /
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/* Status of Disk Functions */
+typedef BYTE DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+ RES_OK = 0, /* 0: Successful */
+ RES_ERROR, /* 1: R/W Error */
+ RES_WRPRT, /* 2: Write Protected */
+ RES_NOTRDY, /* 3: Not Ready */
+ RES_PARERR /* 4: Invalid Parameter */
+} DRESULT;
+
+typedef enum {
+ DRIVE_SD = 0,
+ DRIVE_RAM = 1,
+ DRIVE_EMMC = 2,
+ DRIVE_BIS = 3,
+ DRIVE_EMU = 4
+} DDRIVE;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+
+DSTATUS disk_initialize (BYTE pdrv);
+DSTATUS disk_status (BYTE pdrv);
+DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
+DRESULT disk_set_info (BYTE pdrv, BYTE cmd, void *buff);
+
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT 0x01 /* Drive not initialized */
+#define STA_NODISK 0x02 /* No medium in the drive */
+#define STA_PROTECT 0x04 /* Write protected */
+
+
+/* Command code for disk_ioctrl fucntion */
+
+/* Generic command (Used by FatFs) */
+#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
+#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
+#define SET_SECTOR_COUNT 1 /* Set media size (needed at FF_USE_MKFS == 1) */
+#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
+#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
+#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
+#define SET_SECTOR_OFFSET 5 /* Set media logical offset */
+#define SET_WRITE_PROTECT 6 /* Lock/Unlock media removal */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bdk/libs/fatfs/ff.c b/bdk/libs/fatfs/ff.c
index cc7f5ed..c6b05e3 100644
--- a/bdk/libs/fatfs/ff.c
+++ b/bdk/libs/fatfs/ff.c
@@ -1,6937 +1,6923 @@
-/*
- * Copyright (c) 2018 naehrwert
- * Copyright (c) 2018-2019 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-/*----------------------------------------------------------------------------/
-/ FatFs - Generic FAT Filesystem Module R0.13c (p4) /
-/-----------------------------------------------------------------------------/
-/
-/ Copyright (C) 2018, ChaN, all right reserved.
-/
-/ FatFs module is an open source software. Redistribution and use of FatFs in
-/ source and binary forms, with or without modification, are permitted provided
-/ that the following condition is met:
-/
-/ 1. Redistributions of source code must retain the above copyright notice,
-/ this condition and the following disclaimer.
-/
-/ This software is provided by the copyright holder and contributors "AS IS"
-/ and any warranties related to this software are DISCLAIMED.
-/ The copyright owner or contributors be NOT LIABLE for any damages caused
-/ by use of this software.
-/
-/----------------------------------------------------------------------------*/
-
-
-#include "ff.h" /* Declarations of FatFs API */
-#include "diskio.h" /* Declarations of device I/O functions */
-#include
-#include "../../storage/nx_sd.h"
-
-#define EFSPRINTF(text, ...) print_error(); gfx_printf("%k"text"%k\n", 0xFFFFFF00, 0xFFFFFFFF);
-//#define EFSPRINTF(...)
-
-/*--------------------------------------------------------------------------
-
- Module Private Definitions
-
----------------------------------------------------------------------------*/
-
-#if FF_DEFINED != 86604 /* Revision ID */
-#error Wrong include file (ff.h).
-#endif
-
-
-/* Limits and boundaries */
-#define MAX_DIR 0x200000 /* Max size of FAT directory */
-#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
-#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
-#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
-#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
-#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
-
-
-/* Character code support macros */
-#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z')
-#define IsLower(c) ((c) >= 'a' && (c) <= 'z')
-#define IsDigit(c) ((c) >= '0' && (c) <= '9')
-#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
-#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
-#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
-
-
-/* Additional file access control and file status flags for internal use */
-#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
-#define FA_MODIFIED 0x40 /* File has been modified */
-#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
-
-
-/* Additional file attribute bits for internal use */
-#define AM_VOL 0x08 /* Volume label */
-#define AM_LFN 0x0F /* LFN entry */
-#define AM_MASK 0x3F /* Mask of defined bits */
-
-
-/* Name status flags in fn[11] */
-#define NSFLAG 11 /* Index of the name status byte */
-#define NS_LOSS 0x01 /* Out of 8.3 format */
-#define NS_LFN 0x02 /* Force to create LFN entry */
-#define NS_LAST 0x04 /* Last segment */
-#define NS_BODY 0x08 /* Lower case flag (body) */
-#define NS_EXT 0x10 /* Lower case flag (ext) */
-#define NS_DOT 0x20 /* Dot entry */
-#define NS_NOLFN 0x40 /* Do not find LFN */
-#define NS_NONAME 0x80 /* Not followed */
-
-
-/* exFAT directory entry types */
-#define ET_BITMAP 0x81 /* Allocation bitmap */
-#define ET_UPCASE 0x82 /* Up-case table */
-#define ET_VLABEL 0x83 /* Volume label */
-#define ET_FILEDIR 0x85 /* File and directory */
-#define ET_STREAM 0xC0 /* Stream extension */
-#define ET_FILENAME 0xC1 /* Name extension */
-
-
-/* FatFs refers the FAT structure as simple byte array instead of structure member
-/ because the C structure is not binary compatible between different platforms */
-
-#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
-#define BS_OEMName 3 /* OEM name (8-byte) */
-#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
-#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
-#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
-#define BPB_NumFATs 16 /* Number of FATs (BYTE) */
-#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
-#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
-#define BPB_Media 21 /* Media descriptor byte (BYTE) */
-#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
-#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
-#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
-#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
-#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
-#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
-#define BS_NTres 37 /* WindowsNT error flag (BYTE) */
-#define BS_BootSig 38 /* Extended boot signature (BYTE) */
-#define BS_VolID 39 /* Volume serial number (DWORD) */
-#define BS_VolLab 43 /* Volume label string (8-byte) */
-#define BS_FilSysType 54 /* Filesystem type string (8-byte) */
-#define BS_BootCode 62 /* Boot code (448-byte) */
-#define BS_55AA 510 /* Signature word (WORD) */
-
-#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
-#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
-#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
-#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
-#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
-#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
-#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
-#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
-#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
-#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
-#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
-#define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
-#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
-
-#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
-#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
-#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
-#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
-#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
-#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
-#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
-#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
-#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
-#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
-#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
-#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
-#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
-#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
-#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
-#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
-#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
-#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
-
-#define DIR_Name 0 /* Short file name (11-byte) */
-#define DIR_Attr 11 /* Attribute (BYTE) */
-#define DIR_NTres 12 /* Lower case flag (BYTE) */
-#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
-#define DIR_CrtTime 14 /* Created time (DWORD) */
-#define DIR_LstAccDate 18 /* Last accessed date (WORD) */
-#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
-#define DIR_ModTime 22 /* Modified time (DWORD) */
-#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
-#define DIR_FileSize 28 /* File size (DWORD) */
-#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
-#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
-#define LDIR_Type 12 /* LFN: Entry type (BYTE) */
-#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
-#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
-#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
-#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
-#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
-#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
-#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
-#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
-#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
-#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
-#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
-#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
-#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
-#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
-#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
-#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
-#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
-#define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
-#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
-#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
-#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
-#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
-#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
-
-#define SZDIRE 32 /* Size of a directory entry */
-#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
-#define RDDEM 0x05 /* Replacement of the character collides with DDEM */
-#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
-
-#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
-#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
-#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
-#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
-
-#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
-#define SZ_PTE 16 /* MBR: Size of a partition table entry */
-#define PTE_Boot 0 /* MBR PTE: Boot indicator */
-#define PTE_StHead 1 /* MBR PTE: Start head */
-#define PTE_StSec 2 /* MBR PTE: Start sector */
-#define PTE_StCyl 3 /* MBR PTE: Start cylinder */
-#define PTE_System 4 /* MBR PTE: System ID */
-#define PTE_EdHead 5 /* MBR PTE: End head */
-#define PTE_EdSec 6 /* MBR PTE: End sector */
-#define PTE_EdCyl 7 /* MBR PTE: End cylinder */
-#define PTE_StLba 8 /* MBR PTE: Start in LBA */
-#define PTE_SizLba 12 /* MBR PTE: Size in LBA */
-
-
-/* Post process on fatal error in the file operations */
-#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
-
-
-/* Re-entrancy related */
-#if FF_FS_REENTRANT
-#if FF_USE_LFN == 1
-#error Static LFN work area cannot be used at thread-safe configuration
-#endif
-#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
-#else
-#define LEAVE_FF(fs, res) return res
-#endif
-
-
-/* Definitions of volume - physical location conversion */
-#if FF_MULTI_PARTITION
-#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
-#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */
-#else
-#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
-#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
-#endif
-
-
-/* Definitions of sector size */
-#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
-#error Wrong sector size configuration
-#endif
-#if FF_MAX_SS == FF_MIN_SS
-#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
-#else
-#define SS(fs) ((fs)->ssize) /* Variable sector size */
-#endif
-
-
-/* Timestamp */
-#if FF_FS_NORTC == 1
-#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
-#error Invalid FF_FS_NORTC settings
-#endif
-#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
-#else
-#define GET_FATTIME() get_fattime()
-#endif
-
-
-/* File lock controls */
-#if FF_FS_LOCK != 0
-#if FF_FS_READONLY
-#error FF_FS_LOCK must be 0 at read-only configuration
-#endif
-typedef struct {
- FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */
- DWORD clu; /* Object ID 2, containing directory (0:root) */
- DWORD ofs; /* Object ID 3, offset in the directory */
- WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
-} FILESEM;
-#endif
-
-
-/* SBCS up-case tables (\x80-\xFF) */
-#define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
- 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
- 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
-#define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
- 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
- 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
- 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
- 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
- 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
- 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
- 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
-#define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
- 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
- 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
- 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
- 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
- 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
- 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
- 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
- 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
- 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
- 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
- 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
- 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
- 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
- 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
- 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
-#define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
- 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
- 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
- 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
- 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
- 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
- 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
- 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
-
-
-/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
-#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
-#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
-#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
-#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
-
-
-/* Macros for table definitions */
-#define MERGE_2STR(a, b) a ## b
-#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
-
-
-
-
-/*--------------------------------------------------------------------------
-
- Module Private Work Area
-
----------------------------------------------------------------------------*/
-/* Remark: Variables defined here without initial value shall be guaranteed
-/ zero/null at start-up. If not, the linker option or start-up routine is
-/ not compliance with C standard. */
-
-/*--------------------------------*/
-/* File/Volume controls */
-/*--------------------------------*/
-
-#if FF_VOLUMES < 1 || FF_VOLUMES > 10
-#error Wrong FF_VOLUMES setting
-#endif
-static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */
-static WORD Fsid; /* Filesystem mount ID */
-
-#if FF_FS_RPATH != 0
-static BYTE CurrVol; /* Current drive */
-#endif
-
-#if FF_FS_LOCK != 0
-static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
-#endif
-
-#if FF_STR_VOLUME_ID
-#ifdef FF_VOLUME_STRS
-static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */
-#endif
-#endif
-
-
-/*--------------------------------*/
-/* LFN/Directory working buffer */
-/*--------------------------------*/
-
-#if FF_USE_LFN == 0 /* Non-LFN configuration */
-#if FF_FS_EXFAT
-#error LFN must be enabled when enable exFAT
-#endif
-#define DEF_NAMBUF
-#define INIT_NAMBUF(fs)
-#define FREE_NAMBUF()
-#define LEAVE_MKFS(res) return res
-
-#else /* LFN configurations */
-#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
-#error Wrong setting of FF_MAX_LFN
-#endif
-#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
-#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
-#endif
-#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
-#error Wrong setting of FF_LFN_UNICODE
-#endif
-static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* FAT: Offset of LFN characters in the directory entry */
-#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
-
-#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
-#if FF_FS_EXFAT
-static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry block scratchpad buffer */
-#endif
-static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */
-#define DEF_NAMBUF
-#define INIT_NAMBUF(fs)
-#define FREE_NAMBUF()
-#define LEAVE_MKFS(res) return res
-
-#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
-#if FF_FS_EXFAT
-#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
-#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
-#define FREE_NAMBUF()
-#else
-#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
-#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
-#define FREE_NAMBUF()
-#endif
-#define LEAVE_MKFS(res) return res
-
-#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
-#if FF_FS_EXFAT
-#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
-#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
-#define FREE_NAMBUF() ff_memfree(lfn)
-#else
-#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
-#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
-#define FREE_NAMBUF() ff_memfree(lfn)
-#endif
-#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
-#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
-
-#else
-#error Wrong setting of FF_USE_LFN
-
-#endif /* FF_USE_LFN == 1 */
-#endif /* FF_USE_LFN == 0 */
-
-
-
-/*--------------------------------*/
-/* Code conversion tables */
-/*--------------------------------*/
-
-#if FF_CODE_PAGE == 0 /* Run-time code page configuration */
-#define CODEPAGE CodePage
-static WORD CodePage; /* Current code page */
-static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */
-
-static const BYTE Ct437[] = TBL_CT437;
-static const BYTE Ct720[] = TBL_CT720;
-static const BYTE Ct737[] = TBL_CT737;
-static const BYTE Ct771[] = TBL_CT771;
-static const BYTE Ct775[] = TBL_CT775;
-static const BYTE Ct850[] = TBL_CT850;
-static const BYTE Ct852[] = TBL_CT852;
-static const BYTE Ct855[] = TBL_CT855;
-static const BYTE Ct857[] = TBL_CT857;
-static const BYTE Ct860[] = TBL_CT860;
-static const BYTE Ct861[] = TBL_CT861;
-static const BYTE Ct862[] = TBL_CT862;
-static const BYTE Ct863[] = TBL_CT863;
-static const BYTE Ct864[] = TBL_CT864;
-static const BYTE Ct865[] = TBL_CT865;
-static const BYTE Ct866[] = TBL_CT866;
-static const BYTE Ct869[] = TBL_CT869;
-static const BYTE Dc932[] = TBL_DC932;
-static const BYTE Dc936[] = TBL_DC936;
-static const BYTE Dc949[] = TBL_DC949;
-static const BYTE Dc950[] = TBL_DC950;
-
-#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
-#define CODEPAGE FF_CODE_PAGE
-static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE);
-
-#else /* Static code page configuration (DBCS) */
-#define CODEPAGE FF_CODE_PAGE
-static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE);
-
-#endif
-
-
-
-
-/*--------------------------------------------------------------------------
-
- Module Private Functions
-
----------------------------------------------------------------------------*/
-
-/*-----------------------------------------------------------------------*/
-/* Print error header */
-/*-----------------------------------------------------------------------*/
-
-void print_error()
-{
- gfx_printf("\n\n\n%k[FatFS] Error: %k", 0xFFFFFF00, 0xFFFFFFFF);
-}
-
-
-/*-----------------------------------------------------------------------*/
-/* Load/Store multi-byte word in the FAT structure */
-/*-----------------------------------------------------------------------*/
-
-static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */
-{
- WORD rv;
-
- rv = ptr[1];
- rv = rv << 8 | ptr[0];
- return rv;
-}
-
-static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */
-{
- DWORD rv;
-
- rv = ptr[3];
- rv = rv << 8 | ptr[2];
- rv = rv << 8 | ptr[1];
- rv = rv << 8 | ptr[0];
- return rv;
-}
-
-#if FF_FS_EXFAT
-static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */
-{
- QWORD rv;
-
- rv = ptr[7];
- rv = rv << 8 | ptr[6];
- rv = rv << 8 | ptr[5];
- rv = rv << 8 | ptr[4];
- rv = rv << 8 | ptr[3];
- rv = rv << 8 | ptr[2];
- rv = rv << 8 | ptr[1];
- rv = rv << 8 | ptr[0];
- return rv;
-}
-#endif
-
-#if !FF_FS_READONLY
-static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */
-{
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val;
-}
-
-static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */
-{
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val;
-}
-
-#if FF_FS_EXFAT
-static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */
-{
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val; val >>= 8;
- *ptr++ = (BYTE)val;
-}
-#endif
-#endif /* !FF_FS_READONLY */
-
-
-
-/*-----------------------------------------------------------------------*/
-/* String functions */
-/*-----------------------------------------------------------------------*/
-
-/* Copy memory to memory */
-static void mem_cpy (void* dst, const void* src, UINT cnt)
-{
- BYTE *d = (BYTE*)dst;
- const BYTE *s = (const BYTE*)src;
-
- if (cnt != 0) {
- do {
- *d++ = *s++;
- } while (--cnt);
- }
-}
-
-
-/* Fill memory block */
-static void mem_set (void* dst, int val, UINT cnt)
-{
- BYTE *d = (BYTE*)dst;
-
- do {
- *d++ = (BYTE)val;
- } while (--cnt);
-}
-
-
-/* Compare memory block */
-static int mem_cmp (const void* dst, const void* src, UINT cnt) /* ZR:same, NZ:different */
-{
- const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
- int r = 0;
-
- do {
- r = *d++ - *s++;
- } while (--cnt && r == 0);
-
- return r;
-}
-
-
-/* Check if chr is contained in the string */
-static int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */
-{
- while (*str && *str != chr) str++;
- return *str;
-}
-
-
-/* Test if the character is DBC 1st byte */
-static int dbc_1st (BYTE c)
-{
-#if FF_CODE_PAGE == 0 /* Variable code page */
- if (DbcTbl && c >= DbcTbl[0]) {
- if (c <= DbcTbl[1]) return 1; /* 1st byte range 1 */
- if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */
- }
-#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
- if (c >= DbcTbl[0]) {
- if (c <= DbcTbl[1]) return 1;
- if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1;
- }
-#else /* SBCS fixed code page */
- if (c != 0) return 0; /* Always false */
-#endif
- return 0;
-}
-
-
-/* Test if the character is DBC 2nd byte */
-static int dbc_2nd (BYTE c)
-{
-#if FF_CODE_PAGE == 0 /* Variable code page */
- if (DbcTbl && c >= DbcTbl[4]) {
- if (c <= DbcTbl[5]) return 1; /* 2nd byte range 1 */
- if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */
- if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */
- }
-#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
- if (c >= DbcTbl[4]) {
- if (c <= DbcTbl[5]) return 1;
- if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1;
- if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1;
- }
-#else /* SBCS fixed code page */
- if (c != 0) return 0; /* Always false */
-#endif
- return 0;
-}
-
-
-#if FF_USE_LFN
-
-/* Get a character from TCHAR string in defined API encodeing */
-static DWORD tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */
- const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */
-)
-{
- DWORD uc;
- const TCHAR *p = *str;
-
-#if FF_LFN_UNICODE == 1 /* UTF-16 input */
- WCHAR wc;
-
- uc = *p++; /* Get a unit */
- if (IsSurrogate(uc)) { /* Surrogate? */
- wc = *p++; /* Get low surrogate */
- if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */
- uc = uc << 16 | wc;
- }
-
-#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
- BYTE b;
- int nf;
-
- uc = (BYTE)*p++; /* Get a unit */
- if (uc & 0x80) { /* Multiple byte code? */
- if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */
- uc &= 0x1F; nf = 1;
- } else {
- if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */
- uc &= 0x0F; nf = 2;
- } else {
- if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */
- uc &= 0x07; nf = 3;
- } else { /* Wrong sequence */
- return 0xFFFFFFFF;
- }
- }
- }
- do { /* Get trailing bytes */
- b = (BYTE)*p++;
- if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */
- uc = uc << 6 | (b & 0x3F);
- } while (--nf != 0);
- if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */
- if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
- }
-
-#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
- uc = (TCHAR)*p++; /* Get a unit */
- if (uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */
- if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
-
-#else /* ANSI/OEM input */
- BYTE b;
- WCHAR wc;
-
- wc = (BYTE)*p++; /* Get a byte */
- if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */
- b = (BYTE)*p++; /* Get 2nd byte */
- if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */
- wc = (wc << 8) + b; /* Make a DBC */
- }
- if (wc != 0) {
- wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */
- if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */
- }
- uc = wc;
-
-#endif
- *str = p; /* Next read pointer */
- return uc;
-}
-
-
-/* Output a TCHAR string in defined API encoding */
-static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
- DWORD chr, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */
- TCHAR* buf, /* Output buffer */
- UINT szb /* Size of the buffer */
-)
-{
-#if FF_LFN_UNICODE == 1 /* UTF-16 output */
- WCHAR hs, wc;
-
- hs = (WCHAR)(chr >> 16);
- wc = (WCHAR)chr;
- if (hs == 0) { /* Single encoding unit? */
- if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */
- *buf = wc;
- return 1;
- }
- if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */
- *buf++ = hs;
- *buf++ = wc;
- return 2;
-
-#elif FF_LFN_UNICODE == 2 /* UTF-8 output */
- DWORD hc;
-
- if (chr < 0x80) { /* Single byte code? */
- if (szb < 1) return 0; /* Buffer overflow? */
- *buf = (TCHAR)chr;
- return 1;
- }
- if (chr < 0x800) { /* 2-byte sequence? */
- if (szb < 2) return 0; /* Buffer overflow? */
- *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F));
- *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
- return 2;
- }
- if (chr < 0x10000) { /* 3-byte sequence? */
- if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */
- *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F));
- *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
- *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
- return 3;
- }
- /* 4-byte sequence */
- if (szb < 4) return 0; /* Buffer overflow? */
- hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
- chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
- if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
- chr = (hc | chr) + 0x10000;
- *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07));
- *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F));
- *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
- *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
- return 4;
-
-#elif FF_LFN_UNICODE == 3 /* UTF-32 output */
- DWORD hc;
-
- if (szb < 1) return 0; /* Buffer overflow? */
- if (chr >= 0x10000) { /* Out of BMP? */
- hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
- chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
- if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
- chr = (hc | chr) + 0x10000;
- }
- *buf++ = (TCHAR)chr;
- return 1;
-
-#else /* ANSI/OEM output */
- WCHAR wc;
-
- wc = ff_uni2oem(chr, CODEPAGE);
- if (wc >= 0x100) { /* Is this a DBC? */
- if (szb < 2) return 0;
- *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */
- *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */
- return 2;
- }
- if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */
- *buf++ = (TCHAR)wc; /* Store the character */
- return 1;
-#endif
-}
-#endif /* FF_USE_LFN */
-
-
-#if FF_FS_REENTRANT
-/*-----------------------------------------------------------------------*/
-/* Request/Release grant to access the volume */
-/*-----------------------------------------------------------------------*/
-static int lock_fs ( /* 1:Ok, 0:timeout */
- FATFS* fs /* Filesystem object */
-)
-{
- return ff_req_grant(fs->sobj);
-}
-
-
-static void unlock_fs (
- FATFS* fs, /* Filesystem object */
- FRESULT res /* Result code to be returned */
-)
-{
- if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
- ff_rel_grant(fs->sobj);
- }
-}
-
-#endif
-
-
-
-#if FF_FS_LOCK != 0
-/*-----------------------------------------------------------------------*/
-/* File lock control functions */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT chk_lock ( /* Check if the file can be accessed */
- DIR* dp, /* Directory object pointing the file to be checked */
- int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
-)
-{
- UINT i, be;
-
- /* Search open object table for the object */
- be = 0;
- for (i = 0; i < FF_FS_LOCK; i++) {
- if (Files[i].fs) { /* Existing entry */
- if (Files[i].fs == dp->obj.fs && /* Check if the object matches with an open object */
- Files[i].clu == dp->obj.sclust &&
- Files[i].ofs == dp->dptr) break;
- } else { /* Blank entry */
- be = 1;
- }
- }
- if (i == FF_FS_LOCK) { /* The object has not been opened */
- return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; /* Is there a blank entry for new object? */
- }
-
- /* The object was opened. Reject any open against writing file and all write mode open */
- return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
-}
-
-
-static int enq_lock (void) /* Check if an entry is available for a new object */
-{
- UINT i;
-
- for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
- return (i == FF_FS_LOCK) ? 0 : 1;
-}
-
-
-static UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
- DIR* dp, /* Directory object pointing the file to register or increment */
- int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
-)
-{
- UINT i;
-
-
- for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */
- if (Files[i].fs == dp->obj.fs &&
- Files[i].clu == dp->obj.sclust &&
- Files[i].ofs == dp->dptr) break;
- }
-
- if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */
- for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
- if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */
- Files[i].fs = dp->obj.fs;
- Files[i].clu = dp->obj.sclust;
- Files[i].ofs = dp->dptr;
- Files[i].ctr = 0;
- }
-
- if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */
-
- Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */
-
- return i + 1; /* Index number origin from 1 */
-}
-
-
-static FRESULT dec_lock ( /* Decrement object open counter */
- UINT i /* Semaphore index (1..) */
-)
-{
- WORD n;
- FRESULT res;
-
-
- if (--i < FF_FS_LOCK) { /* Index number origin from 0 */
- n = Files[i].ctr;
- if (n == 0x100) n = 0; /* If write mode open, delete the entry */
- if (n > 0) n--; /* Decrement read mode open count */
- Files[i].ctr = n;
- if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */
- res = FR_OK;
- } else {
- res = FR_INT_ERR; /* Invalid index nunber */
- }
- return res;
-}
-
-
-static void clear_lock ( /* Clear lock entries of the volume */
- FATFS *fs
-)
-{
- UINT i;
-
- for (i = 0; i < FF_FS_LOCK; i++) {
- if (Files[i].fs == fs) Files[i].fs = 0;
- }
-}
-
-#endif /* FF_FS_LOCK != 0 */
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Move/Flush disk access window in the filesystem object */
-/*-----------------------------------------------------------------------*/
-#if !FF_FS_READONLY
-static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */
- FATFS* fs /* Filesystem object */
-)
-{
- FRESULT res = FR_OK;
-
-
- if (fs->wflag) { /* Is the disk access window dirty */
- if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) { /* Write back the window */
- fs->wflag = 0; /* Clear window dirty flag */
- if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it in the 1st FAT? */
- if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */
- }
- } else {
- res = FR_DISK_ERR;
- }
- }
- return res;
-}
-#endif
-
-
-static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */
- FATFS* fs, /* Filesystem object */
- DWORD sector /* Sector number to make appearance in the fs->win[] */
-)
-{
- FRESULT res = FR_OK;
-
-
- if (sector != fs->winsect) { /* Window offset changed? */
-#if !FF_FS_READONLY
- res = sync_window(fs); /* Write-back changes */
-#endif
- if (res == FR_OK) { /* Fill sector window with new data */
- if (disk_read(fs->pdrv, fs->win, sector, 1) != RES_OK) {
- sector = 0xFFFFFFFF; /* Invalidate window if read data is not valid */
- res = FR_DISK_ERR;
- }
- fs->winsect = sector;
- }
- }
- return res;
-}
-
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Synchronize filesystem and data on the storage */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
- FATFS* fs /* Filesystem object */
-)
-{
- FRESULT res;
-
-
- res = sync_window(fs);
- if (res == FR_OK) {
- if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */
- /* Create FSInfo structure */
- mem_set(fs->win, 0, sizeof fs->win);
- st_word(fs->win + BS_55AA, 0xAA55);
- st_dword(fs->win + FSI_LeadSig, 0x41615252);
- st_dword(fs->win + FSI_StrucSig, 0x61417272);
- st_dword(fs->win + FSI_Free_Count, fs->free_clst);
- st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
- /* Write it into the FSInfo sector */
- fs->winsect = fs->volbase + 1;
- disk_write(fs->pdrv, fs->win, fs->winsect, 1);
- fs->fsi_flag = 0;
- }
- /* Make sure that no pending write process in the lower layer */
- if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;
- }
-
- return res;
-}
-
-#endif
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Get physical sector number from cluster number */
-/*-----------------------------------------------------------------------*/
-
-static DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
- FATFS* fs, /* Filesystem object */
- DWORD clst /* Cluster# to be converted */
-)
-{
- clst -= 2; /* Cluster number is origin from 2 */
- if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */
- return fs->database + fs->csize * clst; /* Start sector number of the cluster */
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* FAT access - Read value of a FAT entry */
-/*-----------------------------------------------------------------------*/
-
-static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
- FFOBJID* obj, /* Corresponding object */
- DWORD clst /* Cluster number to get the value */
-)
-{
- UINT wc, bc;
- DWORD val;
- FATFS *fs = obj->fs;
-
-
- if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */
- val = 1; /* Internal error */
-
- } else {
- val = 0xFFFFFFFF; /* Default value falls on disk error */
-
- switch (fs->fs_type) {
- case FS_FAT12 :
- bc = (UINT)clst; bc += bc / 2;
- if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
- wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of the entry */
- if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
- wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of the entry */
- val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust bit position */
- break;
-
- case FS_FAT16 :
- if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;
- val = ld_word(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */
- break;
-
- case FS_FAT32 :
- if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
- val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */
- break;
-#if FF_FS_EXFAT
- case FS_EXFAT :
- if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat == 0) { /* Object except root dir must have valid data length */
- DWORD cofs = clst - obj->sclust; /* Offset from start cluster */
- DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */
-
- if (obj->stat == 2 && cofs <= clen) { /* Is it a contiguous chain? */
- val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* No data on the FAT, generate the value */
- break;
- }
- if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */
- val = clst + 1; /* Generate the value */
- break;
- }
- if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */
- if (obj->n_frag != 0) { /* Is it on the growing edge? */
- val = 0x7FFFFFFF; /* Generate EOC */
- } else {
- if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
- val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF;
- }
- break;
- }
- }
- /* go to default */
-#endif
- default:
- val = 1; /* Internal error */
- }
- }
-
- return val;
-}
-
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* FAT access - Change value of a FAT entry */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
- FATFS* fs, /* Corresponding filesystem object */
- DWORD clst, /* FAT index number (cluster number) to be changed */
- DWORD val /* New value to be set to the entry */
-)
-{
- UINT bc;
- BYTE *p;
- FRESULT res = FR_INT_ERR;
-
-
- if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
- switch (fs->fs_type) {
- case FS_FAT12 :
- bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */
- res = move_window(fs, fs->fatbase + (bc / SS(fs)));
- if (res != FR_OK) break;
- p = fs->win + bc++ % SS(fs);
- *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Put 1st byte */
- fs->wflag = 1;
- res = move_window(fs, fs->fatbase + (bc / SS(fs)));
- if (res != FR_OK) break;
- p = fs->win + bc % SS(fs);
- *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Put 2nd byte */
- fs->wflag = 1;
- break;
-
- case FS_FAT16 :
- res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
- if (res != FR_OK) break;
- st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */
- fs->wflag = 1;
- break;
-
- case FS_FAT32 :
-#if FF_FS_EXFAT
- case FS_EXFAT :
-#endif
- res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
- if (res != FR_OK) break;
- if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
- val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000);
- }
- st_dword(fs->win + clst * 4 % SS(fs), val);
- fs->wflag = 1;
- break;
- }
- }
- return res;
-}
-
-#endif /* !FF_FS_READONLY */
-
-
-
-
-#if FF_FS_EXFAT && !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* exFAT: Accessing FAT and Allocation Bitmap */
-/*-----------------------------------------------------------------------*/
-
-/*--------------------------------------*/
-/* Find a contiguous free cluster block */
-/*--------------------------------------*/
-
-static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
- FATFS* fs, /* Filesystem object */
- DWORD clst, /* Cluster number to scan from */
- DWORD ncl /* Number of contiguous clusters to find (1..) */
-)
-{
- BYTE bm, bv;
- UINT i;
- DWORD val, scl, ctr;
-
-
- clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */
- if (clst >= fs->n_fatent - 2) clst = 0;
- scl = val = clst; ctr = 0;
- for (;;) {
- if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;
- i = val / 8 % SS(fs); bm = 1 << (val % 8);
- do {
- do {
- bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */
- if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */
- val = 0; bm = 0; i = SS(fs);
- }
- if (bv == 0) { /* Is it a free cluster? */
- if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */
- } else {
- scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */
- }
- if (val == clst) return 0; /* All cluster scanned? */
- } while (bm != 0);
- bm = 1;
- } while (++i < SS(fs));
- }
-}
-
-
-/*----------------------------------------*/
-/* Set/Clear a block of allocation bitmap */
-/*----------------------------------------*/
-
-static FRESULT change_bitmap (
- FATFS* fs, /* Filesystem object */
- DWORD clst, /* Cluster number to change from */
- DWORD ncl, /* Number of clusters to be changed */
- int bv /* bit value to be set (0 or 1) */
-)
-{
- BYTE bm;
- UINT i;
- DWORD sect;
-
-
- clst -= 2; /* The first bit corresponds to cluster #2 */
- sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */
- i = clst / 8 % SS(fs); /* Byte offset in the sector */
- bm = 1 << (clst % 8); /* Bit mask in the byte */
- for (;;) {
- if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;
- do {
- do {
- if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */
- fs->win[i] ^= bm; /* Flip the bit */
- fs->wflag = 1;
- if (--ncl == 0) return FR_OK; /* All bits processed? */
- } while (bm <<= 1); /* Next bit */
- bm = 1;
- } while (++i < SS(fs)); /* Next byte */
- i = 0;
- }
-}
-
-
-/*---------------------------------------------*/
-/* Fill the first fragment of the FAT chain */
-/*---------------------------------------------*/
-
-static FRESULT fill_first_frag (
- FFOBJID* obj /* Pointer to the corresponding object */
-)
-{
- FRESULT res;
- DWORD cl, n;
-
-
- if (obj->stat == 3) { /* Has the object been changed 'fragmented' in this session? */
- for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */
- res = put_fat(obj->fs, cl, cl + 1);
- if (res != FR_OK) return res;
- }
- obj->stat = 0; /* Change status 'FAT chain is valid' */
- }
- return FR_OK;
-}
-
-
-/*---------------------------------------------*/
-/* Fill the last fragment of the FAT chain */
-/*---------------------------------------------*/
-
-static FRESULT fill_last_frag (
- FFOBJID* obj, /* Pointer to the corresponding object */
- DWORD lcl, /* Last cluster of the fragment */
- DWORD term /* Value to set the last FAT entry */
-)
-{
- FRESULT res;
-
-
- while (obj->n_frag > 0) { /* Create the chain of last fragment */
- res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term);
- if (res != FR_OK) return res;
- obj->n_frag--;
- }
- return FR_OK;
-}
-
-#endif /* FF_FS_EXFAT && !FF_FS_READONLY */
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* FAT handling - Remove a cluster chain */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */
- FFOBJID* obj, /* Corresponding object */
- DWORD clst, /* Cluster to remove a chain from */
- DWORD pclst /* Previous cluster of clst (0 if entire chain) */
-)
-{
- FRESULT res = FR_OK;
- DWORD nxt;
- FATFS *fs = obj->fs;
-#if FF_FS_EXFAT || FF_USE_TRIM
- DWORD scl = clst, ecl = clst;
-#endif
-#if FF_USE_TRIM
- DWORD rt[2];
-#endif
-
- if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */
-
- /* Mark the previous cluster 'EOC' on the FAT if it exists */
- if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {
- res = put_fat(fs, pclst, 0xFFFFFFFF);
- if (res != FR_OK) return res;
- }
-
- /* Remove the chain */
- do {
- nxt = get_fat(obj, clst); /* Get cluster status */
- if (nxt == 0) break; /* Empty cluster? */
- if (nxt == 1) return FR_INT_ERR; /* Internal error? */
- if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */
- if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
- res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */
- if (res != FR_OK) return res;
- }
- if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */
- fs->free_clst++;
- fs->fsi_flag |= 1;
- }
-#if FF_FS_EXFAT || FF_USE_TRIM
- if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
- ecl = nxt;
- } else { /* End of contiguous cluster block */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
- if (res != FR_OK) return res;
- }
-#endif
-#if FF_USE_TRIM
- rt[0] = clst2sect(fs, scl); /* Start of data area freed */
- rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area freed */
- disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform device the data in the block is no longer needed */
-#endif
- scl = ecl = nxt;
- }
-#endif
- clst = nxt; /* Next cluster */
- } while (clst < fs->n_fatent); /* Repeat while not the last link */
-
-#if FF_FS_EXFAT
- /* Some post processes for chain status */
- if (fs->fs_type == FS_EXFAT) {
- if (pclst == 0) { /* Has the entire chain been removed? */
- obj->stat = 0; /* Change the chain status 'initial' */
- } else {
- if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */
- clst = obj->sclust; /* Follow the chain to check if it gets contiguous */
- while (clst != pclst) {
- nxt = get_fat(obj, clst);
- if (nxt < 2) return FR_INT_ERR;
- if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;
- if (nxt != clst + 1) break; /* Not contiguous? */
- clst++;
- }
- if (clst == pclst) { /* Has the chain got contiguous again? */
- obj->stat = 2; /* Change the chain status 'contiguous' */
- }
- } else {
- if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */
- obj->stat = 2; /* Change the chain status 'contiguous' */
- }
- }
- }
- }
-#endif
- return FR_OK;
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* FAT handling - Stretch a chain or Create a new chain */
-/*-----------------------------------------------------------------------*/
-
-static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
- FFOBJID* obj, /* Corresponding object */
- DWORD clst /* Cluster# to stretch, 0:Create a new chain */
-)
-{
- DWORD cs, ncl, scl;
- FRESULT res;
- FATFS *fs = obj->fs;
-
-
- if (clst == 0) { /* Create a new chain */
- scl = fs->last_clst; /* Suggested cluster to start to find */
- if (scl == 0 || scl >= fs->n_fatent) scl = 1;
- }
- else { /* Stretch a chain */
- cs = get_fat(obj, clst); /* Check the cluster status */
- if (cs < 2) return 1; /* Test for insanity */
- if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */
- if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */
- scl = clst; /* Cluster to start to find */
- }
- if (fs->free_clst == 0) return 0; /* No free cluster */
-
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */
- if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */
- res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */
- if (res == FR_INT_ERR) return 1;
- if (res == FR_DISK_ERR) return 0xFFFFFFFF;
- if (clst == 0) { /* Is it a new chain? */
- obj->stat = 2; /* Set status 'contiguous' */
- } else { /* It is a stretched chain */
- if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */
- obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */
- obj->stat = 3; /* Change status 'just fragmented' */
- }
- }
- if (obj->stat != 2) { /* Is the file non-contiguous? */
- if (ncl == clst + 1) { /* Is the cluster next to previous one? */
- obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */
- } else { /* New fragment */
- if (obj->n_frag == 0) obj->n_frag = 1;
- res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */
- if (res == FR_OK) obj->n_frag = 1;
- }
- }
- } else
-#endif
- { /* On the FAT/FAT32 volume */
- ncl = 0;
- if (scl == clst) { /* Stretching an existing chain? */
- ncl = scl + 1; /* Test if next cluster is free */
- if (ncl >= fs->n_fatent) ncl = 2;
- cs = get_fat(obj, ncl); /* Get next cluster status */
- if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */
- if (cs != 0) { /* Not free? */
- cs = fs->last_clst; /* Start at suggested cluster if it is valid */
- if (cs >= 2 && cs < fs->n_fatent) scl = cs;
- ncl = 0;
- }
- }
- if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */
- ncl = scl; /* Start cluster */
- for (;;) {
- ncl++; /* Next cluster */
- if (ncl >= fs->n_fatent) { /* Check wrap-around */
- ncl = 2;
- if (ncl > scl) return 0; /* No free cluster found? */
- }
- cs = get_fat(obj, ncl); /* Get the cluster status */
- if (cs == 0) break; /* Found a free cluster? */
- if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */
- if (ncl == scl) return 0; /* No free cluster found? */
- }
- }
- res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */
- if (res == FR_OK && clst != 0) {
- res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */
- }
- }
-
- if (res == FR_OK) { /* Update FSINFO if function succeeded. */
- fs->last_clst = ncl;
- if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--;
- fs->fsi_flag |= 1;
- } else {
- ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */
- }
-
- return ncl; /* Return new cluster number or error status */
-}
-
-#endif /* !FF_FS_READONLY */
-
-
-
-
-#if FF_USE_FASTSEEK
-/*-----------------------------------------------------------------------*/
-/* FAT handling - Convert offset into cluster with link map table */
-/*-----------------------------------------------------------------------*/
-
-static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */
- FIL* fp, /* Pointer to the file object */
- FSIZE_t ofs /* File offset to be converted to cluster# */
-)
-{
- DWORD cl, ncl, *tbl;
- FATFS *fs = fp->obj.fs;
-
-
- tbl = fp->cltbl + 1; /* Top of CLMT */
- cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */
- for (;;) {
- ncl = *tbl++; /* Number of cluters in the fragment */
- if (ncl == 0) return 0; /* End of table? (error) */
- if (cl < ncl) break; /* In this fragment? */
- cl -= ncl; tbl++; /* Next fragment */
- }
- return cl + *tbl; /* Return the cluster number */
-}
-
-#endif /* FF_USE_FASTSEEK */
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Directory handling - Fill a cluster with zeros */
-/*-----------------------------------------------------------------------*/
-
-#if !FF_FS_READONLY
-static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
- FATFS *fs, /* Filesystem object */
- DWORD clst /* Directory table to clear */
-)
-{
- DWORD sect;
- UINT n, szb;
- BYTE *ibuf;
-
-
- if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */
- sect = clst2sect(fs, clst); /* Top of the cluster */
- fs->winsect = sect; /* Set window to top of the cluster */
- mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */
-#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
- /* Allocate a temporary buffer */
- for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ;
- if (szb > SS(fs)) { /* Buffer allocated? */
- mem_set(ibuf, 0, szb);
- szb /= SS(fs); /* Bytes -> Sectors */
- for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */
- ff_memfree(ibuf);
- } else
-#endif
- {
- ibuf = fs->win; szb = 1; /* Use window buffer (many single-sector writes may take a time) */
- for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */
- }
- return (n == fs->csize) ? FR_OK : FR_DISK_ERR;
-}
-#endif /* !FF_FS_READONLY */
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Directory handling - Set directory index */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
- DIR* dp, /* Pointer to directory object */
- DWORD ofs /* Offset of directory table */
-)
-{
- DWORD csz, clst;
- FATFS *fs = dp->obj.fs;
-
-
- if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */
- return FR_INT_ERR;
- }
- dp->dptr = ofs; /* Set current offset */
- clst = dp->obj.sclust; /* Table start cluster (0:root) */
- if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */
- clst = fs->dirbase;
- if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */
- }
-
- if (clst == 0) { /* Static table (root-directory on the FAT volume) */
- if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */
- dp->sect = fs->dirbase;
-
- } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
- csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */
- while (ofs >= csz) { /* Follow cluster chain */
- clst = get_fat(&dp->obj, clst); /* Get next cluster */
- if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
- if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */
- ofs -= csz;
- }
- dp->sect = clst2sect(fs, clst);
- }
- dp->clust = clst; /* Current cluster# */
- if (dp->sect == 0) return FR_INT_ERR;
- dp->sect += ofs / SS(fs); /* Sector# of the directory entry */
- dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */
-
- return FR_OK;
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Directory handling - Move directory table index next */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
- DIR* dp, /* Pointer to the directory object */
- int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
-)
-{
- DWORD ofs, clst;
- FATFS *fs = dp->obj.fs;
-
-
- ofs = dp->dptr + SZDIRE; /* Next entry */
- if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset reached the max value */
- if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has been disabled */
-
- if (ofs % SS(fs) == 0) { /* Sector changed? */
- dp->sect++; /* Next sector */
-
- if (dp->clust == 0) { /* Static table */
- if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */
- dp->sect = 0; return FR_NO_FILE;
- }
- }
- else { /* Dynamic table */
- if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */
- clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */
- if (clst <= 1) return FR_INT_ERR; /* Internal error */
- if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
- if (clst >= fs->n_fatent) { /* It reached end of dynamic table */
-#if !FF_FS_READONLY
- if (!stretch) { /* If no stretch, report EOT */
- dp->sect = 0; return FR_NO_FILE;
- }
- clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */
- if (clst == 0) return FR_DENIED; /* No free cluster */
- if (clst == 1) return FR_INT_ERR; /* Internal error */
- if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
- if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR; /* Clean up the stretched table */
- if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: The directory has been stretched */
-#else
- if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */
- dp->sect = 0; return FR_NO_FILE; /* Report EOT */
-#endif
- }
- dp->clust = clst; /* Initialize data for new cluster */
- dp->sect = clst2sect(fs, clst);
- }
- }
- }
- dp->dptr = ofs; /* Current entry */
- dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */
-
- return FR_OK;
-}
-
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Directory handling - Reserve a block of directory entries */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
- DIR* dp, /* Pointer to the directory object */
- UINT nent /* Number of contiguous entries to allocate */
-)
-{
- FRESULT res;
- UINT n;
- FATFS *fs = dp->obj.fs;
-
-
- res = dir_sdi(dp, 0);
- if (res == FR_OK) {
- n = 0;
- do {
- res = move_window(fs, dp->sect);
- if (res != FR_OK) break;
-#if FF_FS_EXFAT
- if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
-#else
- if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
-#endif
- if (++n == nent) break; /* A block of contiguous free entries is found */
- } else {
- n = 0; /* Not a blank entry. Restart to search */
- }
- res = dir_next(dp, 1);
- } while (res == FR_OK); /* Next entry with table stretch enabled */
- }
-
- if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
- return res;
-}
-
-#endif /* !FF_FS_READONLY */
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* FAT: Directory handling - Load/Store start cluster number */
-/*-----------------------------------------------------------------------*/
-
-static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */
- FATFS* fs, /* Pointer to the fs object */
- const BYTE* dir /* Pointer to the key entry */
-)
-{
- DWORD cl;
-
- cl = ld_word(dir + DIR_FstClusLO);
- if (fs->fs_type == FS_FAT32) {
- cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;
- }
-
- return cl;
-}
-
-
-#if !FF_FS_READONLY
-static void st_clust (
- FATFS* fs, /* Pointer to the fs object */
- BYTE* dir, /* Pointer to the key entry */
- DWORD cl /* Value to be set */
-)
-{
- st_word(dir + DIR_FstClusLO, (WORD)cl);
- if (fs->fs_type == FS_FAT32) {
- st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));
- }
-}
-#endif
-
-
-
-#if FF_USE_LFN
-/*--------------------------------------------------------*/
-/* FAT-LFN: Compare a part of file name with an LFN entry */
-/*--------------------------------------------------------*/
-
-static int cmp_lfn ( /* 1:matched, 0:not matched */
- const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */
- BYTE* dir /* Pointer to the directory entry containing the part of LFN */
-)
-{
- UINT i, s;
- WCHAR wc, uc;
-
-
- if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */
-
- i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */
-
- for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */
- uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */
- if (wc != 0) {
- if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */
- return 0; /* Not matched */
- }
- wc = uc;
- } else {
- if (uc != 0xFFFF) return 0; /* Check filler */
- }
- }
-
- if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */
-
- return 1; /* The part of LFN matched */
-}
-
-
-#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
-/*-----------------------------------------------------*/
-/* FAT-LFN: Pick a part of file name from an LFN entry */
-/*-----------------------------------------------------*/
-
-static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
- WCHAR* lfnbuf, /* Pointer to the LFN working buffer */
- BYTE* dir /* Pointer to the LFN entry */
-)
-{
- UINT i, s;
- WCHAR wc, uc;
-
-
- if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */
-
- i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */
-
- for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */
- uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */
- if (wc != 0) {
- if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
- lfnbuf[i++] = wc = uc; /* Store it */
- } else {
- if (uc != 0xFFFF) return 0; /* Check filler */
- }
- }
-
- if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if it is the last LFN part and not terminated */
- if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
- lfnbuf[i] = 0;
- }
-
- return 1; /* The part of LFN is valid */
-}
-#endif
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------*/
-/* FAT-LFN: Create an entry of LFN entries */
-/*-----------------------------------------*/
-
-static void put_lfn (
- const WCHAR* lfn, /* Pointer to the LFN */
- BYTE* dir, /* Pointer to the LFN entry to be created */
- BYTE ord, /* LFN order (1-20) */
- BYTE sum /* Checksum of the corresponding SFN */
-)
-{
- UINT i, s;
- WCHAR wc;
-
-
- dir[LDIR_Chksum] = sum; /* Set checksum */
- dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */
- dir[LDIR_Type] = 0;
- st_word(dir + LDIR_FstClusLO, 0);
-
- i = (ord - 1) * 13; /* Get offset in the LFN working buffer */
- s = wc = 0;
- do {
- if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */
- st_word(dir + LfnOfs[s], wc); /* Put it */
- if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */
- } while (++s < 13);
- if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */
- dir[LDIR_Ord] = ord; /* Set the LFN order */
-}
-
-#endif /* !FF_FS_READONLY */
-#endif /* FF_USE_LFN */
-
-
-
-#if FF_USE_LFN && !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* FAT-LFN: Create a Numbered SFN */
-/*-----------------------------------------------------------------------*/
-
-static void gen_numname (
- BYTE* dst, /* Pointer to the buffer to store numbered SFN */
- const BYTE* src, /* Pointer to SFN */
- const WCHAR* lfn, /* Pointer to LFN */
- UINT seq /* Sequence number */
-)
-{
- BYTE ns[8], c;
- UINT i, j;
- WCHAR wc;
- DWORD sr;
-
-
- mem_cpy(dst, src, 11);
-
- if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */
- sr = seq;
- while (*lfn) { /* Create a CRC as hash value */
- wc = *lfn++;
- for (i = 0; i < 16; i++) {
- sr = (sr << 1) + (wc & 1);
- wc >>= 1;
- if (sr & 0x10000) sr ^= 0x11021;
- }
- }
- seq = (UINT)sr;
- }
-
- /* itoa (hexdecimal) */
- i = 7;
- do {
- c = (BYTE)((seq % 16) + '0');
- if (c > '9') c += 7;
- ns[i--] = c;
- seq /= 16;
- } while (seq);
- ns[i] = '~';
-
- /* Append the number to the SFN body */
- for (j = 0; j < i && dst[j] != ' '; j++) {
- if (dbc_1st(dst[j])) {
- if (j == i - 1) break;
- j++;
- }
- }
- do {
- dst[j++] = (i < 8) ? ns[i++] : ' ';
- } while (j < 8);
-}
-#endif /* FF_USE_LFN && !FF_FS_READONLY */
-
-
-
-#if FF_USE_LFN
-/*-----------------------------------------------------------------------*/
-/* FAT-LFN: Calculate checksum of an SFN entry */
-/*-----------------------------------------------------------------------*/
-
-static BYTE sum_sfn (
- const BYTE* dir /* Pointer to the SFN entry */
-)
-{
- BYTE sum = 0;
- UINT n = 11;
-
- do {
- sum = (sum >> 1) + (sum << 7) + *dir++;
- } while (--n);
- return sum;
-}
-
-#endif /* FF_USE_LFN */
-
-
-
-#if FF_FS_EXFAT
-/*-----------------------------------------------------------------------*/
-/* exFAT: Checksum */
-/*-----------------------------------------------------------------------*/
-
-static WORD xdir_sum ( /* Get checksum of the directoly entry block */
- const BYTE* dir /* Directory entry block to be calculated */
-)
-{
- UINT i, szblk;
- WORD sum;
-
-
- szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of the entry block */
- for (i = sum = 0; i < szblk; i++) {
- if (i == XDIR_SetSum) { /* Skip 2-byte sum field */
- i++;
- } else {
- sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];
- }
- }
- return sum;
-}
-
-
-
-static WORD xname_sum ( /* Get check sum (to be used as hash) of the file name */
- const WCHAR* name /* File name to be calculated */
-)
-{
- WCHAR chr;
- WORD sum = 0;
-
-
- while ((chr = *name++) != 0) {
- chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be up-case converted */
- sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);
- sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);
- }
- return sum;
-}
-
-
-#if !FF_FS_READONLY && FF_USE_MKFS
-static DWORD xsum32 ( /* Returns 32-bit checksum */
- BYTE dat, /* Byte to be calculated (byte-by-byte processing) */
- DWORD sum /* Previous sum value */
-)
-{
- sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat;
- return sum;
-}
-#endif
-
-
-#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
-/*------------------------------------------------------*/
-/* exFAT: Get object information from a directory block */
-/*------------------------------------------------------*/
-
-static void get_xfileinfo (
- BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */
- FILINFO* fno /* Buffer to store the extracted file information */
-)
-{
- WCHAR wc, hs;
- UINT di, si, nc;
-
- /* Get file name from the entry block */
- si = SZDIRE * 2; /* 1st C1 entry */
- nc = 0; hs = 0; di = 0;
- while (nc < dirb[XDIR_NumName]) {
- if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */
- if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */
- wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */
- if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
- hs = wc; continue; /* Get low surrogate */
- }
- wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
- if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */
- di += wc;
- hs = 0;
- }
- if (hs != 0) di = 0; /* Broken surrogate pair? */
- if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */
- fno->fname[di] = 0; /* Terminate the name */
- fno->altname[0] = 0; /* exFAT does not support SFN */
-
- fno->fattrib = dirb[XDIR_Attr]; /* Attribute */
- fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */
- fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */
- fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */
-}
-
-#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
-
-
-/*-----------------------------------*/
-/* exFAT: Get a directry entry block */
-/*-----------------------------------*/
-
-static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */
- DIR* dp /* Reading direcotry object pointing top of the entry block to load */
-)
-{
- FRESULT res;
- UINT i, sz_ent;
- BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
-
-
- /* Load file-directory entry */
- res = move_window(dp->obj.fs, dp->sect);
- if (res != FR_OK) return res;
- if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order */
- mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE);
- sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE;
- if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR;
-
- /* Load stream-extension entry */
- res = dir_next(dp, 0);
- if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */
- if (res != FR_OK) return res;
- res = move_window(dp->obj.fs, dp->sect);
- if (res != FR_OK) return res;
- if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order */
- mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE);
- if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR;
-
- /* Load file-name entries */
- i = 2 * SZDIRE; /* Name offset to load */
- do {
- res = dir_next(dp, 0);
- if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */
- if (res != FR_OK) return res;
- res = move_window(dp->obj.fs, dp->sect);
- if (res != FR_OK) return res;
- if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order */
- if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE);
- } while ((i += SZDIRE) < sz_ent);
-
- /* Sanity check (do it for only accessible object) */
- if (i <= MAXDIRB(FF_MAX_LFN)) {
- if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR;
- }
- return FR_OK;
-}
-
-
-/*------------------------------------------------------------------*/
-/* exFAT: Initialize object allocation info with loaded entry block */
-/*------------------------------------------------------------------*/
-
-static void init_alloc_info (
- FATFS* fs, /* Filesystem object */
- FFOBJID* obj /* Object allocation information to be initialized */
-)
-{
- obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */
- obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */
- obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */
- obj->n_frag = 0; /* No last fragment info */
-}
-
-
-
-#if !FF_FS_READONLY || FF_FS_RPATH != 0
-/*------------------------------------------------*/
-/* exFAT: Load the object's directory entry block */
-/*------------------------------------------------*/
-
-static FRESULT load_obj_xdir (
- DIR* dp, /* Blank directory object to be used to access containing direcotry */
- const FFOBJID* obj /* Object with its containing directory information */
-)
-{
- FRESULT res;
-
- /* Open object containing directory */
- dp->obj.fs = obj->fs;
- dp->obj.sclust = obj->c_scl;
- dp->obj.stat = (BYTE)obj->c_size;
- dp->obj.objsize = obj->c_size & 0xFFFFFF00;
- dp->obj.n_frag = 0;
- dp->blk_ofs = obj->c_ofs;
-
- res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */
- if (res == FR_OK) {
- res = load_xdir(dp); /* Load the object's entry block */
- }
- return res;
-}
-#endif
-
-
-#if !FF_FS_READONLY
-/*----------------------------------------*/
-/* exFAT: Store the directory entry block */
-/*----------------------------------------*/
-
-static FRESULT store_xdir (
- DIR* dp /* Pointer to the direcotry object */
-)
-{
- FRESULT res;
- UINT nent;
- BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */
-
- /* Create set sum */
- st_word(dirb + XDIR_SetSum, xdir_sum(dirb));
- nent = dirb[XDIR_NumSec] + 1;
-
- /* Store the direcotry entry block to the directory */
- res = dir_sdi(dp, dp->blk_ofs);
- while (res == FR_OK) {
- res = move_window(dp->obj.fs, dp->sect);
- if (res != FR_OK) break;
- mem_cpy(dp->dir, dirb, SZDIRE);
- dp->obj.fs->wflag = 1;
- if (--nent == 0) break;
- dirb += SZDIRE;
- res = dir_next(dp, 0);
- }
- return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;
-}
-
-
-
-/*-------------------------------------------*/
-/* exFAT: Create a new directory enrty block */
-/*-------------------------------------------*/
-
-static void create_xdir (
- BYTE* dirb, /* Pointer to the direcotry entry block buffer */
- const WCHAR* lfn /* Pointer to the object name */
-)
-{
- UINT i;
- BYTE nc1, nlen;
- WCHAR wc;
-
-
- /* Create file-directory and stream-extension entry */
- mem_set(dirb, 0, 2 * SZDIRE);
- dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR;
- dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM;
-
- /* Create file-name entries */
- i = SZDIRE * 2; /* Top of file_name entries */
- nlen = nc1 = 0; wc = 1;
- do {
- dirb[i++] = ET_FILENAME; dirb[i++] = 0;
- do { /* Fill name field */
- if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */
- st_word(dirb + i, wc); /* Store it */
- i += 2;
- } while (i % SZDIRE != 0);
- nc1++;
- } while (lfn[nlen]); /* Fill next entry if any char follows */
-
- dirb[XDIR_NumName] = nlen; /* Set name length */
- dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */
- st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */
-}
-
-#endif /* !FF_FS_READONLY */
-#endif /* FF_FS_EXFAT */
-
-
-
-#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
-/*-----------------------------------------------------------------------*/
-/* Read an object from the directory */
-/*-----------------------------------------------------------------------*/
-
-#define DIR_READ_FILE(dp) dir_read(dp, 0)
-#define DIR_READ_LABEL(dp) dir_read(dp, 1)
-
-static FRESULT dir_read (
- DIR* dp, /* Pointer to the directory object */
- int vol /* Filtered by 0:file/directory or 1:volume label */
-)
-{
- FRESULT res = FR_NO_FILE;
- FATFS *fs = dp->obj.fs;
- BYTE attr, b;
-#if FF_USE_LFN
- BYTE ord = 0xFF, sum = 0xFF;
-#endif
-
- while (dp->sect) {
- res = move_window(fs, dp->sect);
- if (res != FR_OK) break;
- b = dp->dir[DIR_Name]; /* Test for the entry type */
- if (b == 0) {
- res = FR_NO_FILE; break; /* Reached to end of the directory */
- }
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- if (FF_USE_LABEL && vol) {
- if (b == ET_VLABEL) break; /* Volume label entry? */
- } else {
- if (b == ET_FILEDIR) { /* Start of the file entry block? */
- dp->blk_ofs = dp->dptr; /* Get location of the block */
- res = load_xdir(dp); /* Load the entry block */
- if (res == FR_OK) {
- dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */
- }
- break;
- }
- }
- } else
-#endif
- { /* On the FAT/FAT32 volume */
- dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */
-#if FF_USE_LFN /* LFN configuration */
- if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */
- ord = 0xFF;
- } else {
- if (attr == AM_LFN) { /* An LFN entry is found */
- if (b & LLEF) { /* Is it start of an LFN sequence? */
- sum = dp->dir[LDIR_Chksum];
- b &= (BYTE)~LLEF; ord = b;
- dp->blk_ofs = dp->dptr;
- }
- /* Check LFN validity and capture it */
- ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
- } else { /* An SFN entry is found */
- if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */
- dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */
- }
- break;
- }
- }
-#else /* Non LFN configuration */
- if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */
- break;
- }
-#endif
- }
- res = dir_next(dp, 0); /* Next entry */
- if (res != FR_OK) break;
- }
-
- if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */
- return res;
-}
-
-#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Directory handling - Find an object in the directory */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
- DIR* dp /* Pointer to the directory object with the file name */
-)
-{
- FRESULT res;
- FATFS *fs = dp->obj.fs;
- BYTE c;
-#if FF_USE_LFN
- BYTE a, ord, sum;
-#endif
-
- res = dir_sdi(dp, 0); /* Rewind directory object */
- if (res != FR_OK) return res;
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- BYTE nc;
- UINT di, ni;
- WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */
-
- while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */
-#if FF_MAX_LFN < 255
- if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */
-#endif
- if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */
- for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */
- if ((di % SZDIRE) == 0) di += 2;
- if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break;
- }
- if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */
- }
- return res;
- }
-#endif
- /* On the FAT/FAT32 volume */
-#if FF_USE_LFN
- ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
-#endif
- do {
- res = move_window(fs, dp->sect);
- if (res != FR_OK) break;
- c = dp->dir[DIR_Name];
- if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
-#if FF_USE_LFN /* LFN configuration */
- dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;
- if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */
- ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
- } else {
- if (a == AM_LFN) { /* An LFN entry is found */
- if (!(dp->fn[NSFLAG] & NS_NOLFN)) {
- if (c & LLEF) { /* Is it start of LFN sequence? */
- sum = dp->dir[LDIR_Chksum];
- c &= (BYTE)~LLEF; ord = c; /* LFN start order */
- dp->blk_ofs = dp->dptr; /* Start offset of LFN */
- }
- /* Check validity of the LFN entry and compare it with given name */
- ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
- }
- } else { /* An SFN entry is found */
- if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */
- if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */
- ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
- }
- }
-#else /* Non LFN configuration */
- dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
- if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */
-#endif
- res = dir_next(dp, 0); /* Next entry */
- } while (res == FR_OK);
-
- return res;
-}
-
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Register an object to the directory */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
- DIR* dp /* Target directory with object name to be created */
-)
-{
- FRESULT res;
- FATFS *fs = dp->obj.fs;
-#if FF_USE_LFN /* LFN configuration */
- UINT n, nlen, nent;
- BYTE sn[12], sum;
-
-
- if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
- for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */
-
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
- res = dir_alloc(dp, nent); /* Allocate directory entries */
- if (res != FR_OK) return res;
- dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */
-
- if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */
- dp->obj.stat &= ~4;
- res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */
- if (res != FR_OK) return res;
- res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */
- if (res != FR_OK) return res;
- if (dp->obj.sclust != 0) { /* Is it a sub-directory? */
- DIR dj;
-
- res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */
- if (res != FR_OK) return res;
- dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */
- st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */
- st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);
- fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1;
- res = store_xdir(&dj); /* Store the object status */
- if (res != FR_OK) return res;
- }
- }
-
- create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */
- return FR_OK;
- }
-#endif
- /* On the FAT/FAT32 volume */
- mem_cpy(sn, dp->fn, 12);
- if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */
- dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */
- for (n = 1; n < 100; n++) {
- gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */
- res = dir_find(dp); /* Check if the name collides with existing SFN */
- if (res != FR_OK) break;
- }
- if (n == 100) return FR_DENIED; /* Abort if too many collisions */
- if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */
- dp->fn[NSFLAG] = sn[NSFLAG];
- }
-
- /* Create an SFN with/without LFNs. */
- nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */
- res = dir_alloc(dp, nent); /* Allocate entries */
- if (res == FR_OK && --nent) { /* Set LFN entry if needed */
- res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
- if (res == FR_OK) {
- sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */
- do { /* Store LFN entries in bottom first */
- res = move_window(fs, dp->sect);
- if (res != FR_OK) break;
- put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
- fs->wflag = 1;
- res = dir_next(dp, 0); /* Next entry */
- } while (res == FR_OK && --nent);
- }
- }
-
-#else /* Non LFN configuration */
- res = dir_alloc(dp, 1); /* Allocate an entry for SFN */
-
-#endif
-
- /* Set SFN entry */
- if (res == FR_OK) {
- res = move_window(fs, dp->sect);
- if (res == FR_OK) {
- mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */
- mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */
-#if FF_USE_LFN
- dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */
-#endif
- fs->wflag = 1;
- }
- }
-
- return res;
-}
-
-#endif /* !FF_FS_READONLY */
-
-
-
-#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
-/*-----------------------------------------------------------------------*/
-/* Remove an object from the directory */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
- DIR* dp /* Directory object pointing the entry to be removed */
-)
-{
- FRESULT res;
- FATFS *fs = dp->obj.fs;
-#if FF_USE_LFN /* LFN configuration */
- DWORD last = dp->dptr;
-
- res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */
- if (res == FR_OK) {
- do {
- res = move_window(fs, dp->sect);
- if (res != FR_OK) break;
- if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */
- } else { /* On the FAT/FAT32 volume */
- dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */
- }
- fs->wflag = 1;
- if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */
- res = dir_next(dp, 0); /* Next entry */
- } while (res == FR_OK);
- if (res == FR_NO_FILE) res = FR_INT_ERR;
- }
-#else /* Non LFN configuration */
-
- res = move_window(fs, dp->sect);
- if (res == FR_OK) {
- dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/
- fs->wflag = 1;
- }
-#endif
-
- return res;
-}
-
-#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
-
-
-
-#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
-/*-----------------------------------------------------------------------*/
-/* Get file information from directory entry */
-/*-----------------------------------------------------------------------*/
-
-static void get_fileinfo (
- DIR* dp, /* Pointer to the directory object */
- FILINFO* fno /* Pointer to the file information to be filled */
-)
-{
- UINT si, di;
-#if FF_USE_LFN
- BYTE lcf;
- WCHAR wc, hs;
- FATFS *fs = dp->obj.fs;
-#else
- TCHAR c;
-#endif
-
-
- fno->fname[0] = 0; /* Invaidate file info */
- if (dp->sect == 0) return; /* Exit if read pointer has reached end of directory */
-
-#if FF_USE_LFN /* LFN configuration */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- get_xfileinfo(fs->dirbuf, fno);
- return;
- } else
-#endif
- { /* On the FAT/FAT32 volume */
- if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */
- si = di = hs = 0;
- while (fs->lfnbuf[si] != 0) {
- wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */
- if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
- hs = wc; continue; /* Get low surrogate */
- }
- wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */
- if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */
- di += wc;
- hs = 0;
- }
- if (hs != 0) di = 0; /* Broken surrogate pair? */
- fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */
- }
- }
-
- si = di = 0;
- while (si < 11) { /* Get SFN from SFN entry */
- wc = dp->dir[si++]; /* Get a char */
- if (wc == ' ') continue; /* Skip padding spaces */
- if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */
- if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */
-#if FF_LFN_UNICODE >= 1 /* Unicode output */
- if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */
- wc = wc << 8 | dp->dir[si++];
- }
- wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */
- if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */
- wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */
- if (wc == 0) { di = 0; break; } /* Buffer overflow? */
- di += wc;
-#else /* ANSI/OEM output */
- fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */
-#endif
- }
- fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */
-
- if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
- if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */
- fno->fname[di++] = '?';
- } else {
- for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */
- wc = (WCHAR)fno->altname[si];
- if (wc == '.') lcf = NS_EXT;
- if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20;
- fno->fname[di] = (TCHAR)wc;
- }
- }
- fno->fname[di] = 0; /* Terminate the LFN */
- if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */
- }
-
-#else /* Non-LFN configuration */
- si = di = 0;
- while (si < 11) { /* Copy name body and extension */
- c = (TCHAR)dp->dir[si++];
- if (c == ' ') continue; /* Skip padding spaces */
- if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */
- if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */
- fno->fname[di++] = c;
- }
- fno->fname[di] = 0;
-#endif
-
- fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */
- fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */
- fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */
- fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */
-}
-
-#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
-
-
-
-#if FF_USE_FIND && FF_FS_MINIMIZE <= 1
-/*-----------------------------------------------------------------------*/
-/* Pattern matching */
-/*-----------------------------------------------------------------------*/
-
-static DWORD get_achar ( /* Get a character and advances ptr */
- const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
-)
-{
- DWORD chr;
-
-
-#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
- chr = tchar2uni(ptr);
- if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */
- chr = ff_wtoupper(chr);
-
-#else /* ANSI/OEM input */
- chr = (BYTE)*(*ptr)++; /* Get a byte */
- if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */
-#if FF_CODE_PAGE == 0
- if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */
-#elif FF_CODE_PAGE < 900
- if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */
-#endif
-#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
- if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */
- chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0;
- }
-#endif
-
-#endif
- return chr;
-}
-
-
-static int pattern_matching ( /* 0:not matched, 1:matched */
- const TCHAR* pat, /* Matching pattern */
- const TCHAR* nam, /* String to be tested */
- int skip, /* Number of pre-skip chars (number of ?s) */
- int inf /* Infinite search (* specified) */
-)
-{
- const TCHAR *pp, *np;
- DWORD pc, nc;
- int nm, nx;
-
-
- while (skip--) { /* Pre-skip name chars */
- if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
- }
- if (*pat == 0 && inf) return 1; /* (short circuit) */
-
- do {
- pp = pat; np = nam; /* Top of pattern and name to match */
- for (;;) {
- if (*pp == '?' || *pp == '*') { /* Wildcard? */
- nm = nx = 0;
- do { /* Analyze the wildcard block */
- if (*pp++ == '?') nm++; else nx = 1;
- } while (*pp == '?' || *pp == '*');
- if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
- nc = *np; break; /* Branch mismatched */
- }
- pc = get_achar(&pp); /* Get a pattern char */
- nc = get_achar(&np); /* Get a name char */
- if (pc != nc) break; /* Branch mismatched? */
- if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */
- }
- get_achar(&nam); /* nam++ */
- } while (inf && nc); /* Retry until end of name if infinite search is specified */
-
- return 0;
-}
-
-#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Pick a top segment and create the object name in directory form */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
- DIR* dp, /* Pointer to the directory object */
- const TCHAR** path /* Pointer to pointer to the segment in the path string */
-)
-{
-#if FF_USE_LFN /* LFN configuration */
- BYTE b, cf;
- WCHAR wc, *lfn;
- DWORD uc;
- UINT i, ni, si, di;
- const TCHAR *p;
-
-
- /* Create LFN into LFN working buffer */
- p = *path; lfn = dp->obj.fs->lfnbuf; di = 0;
- for (;;) {
- uc = tchar2uni(&p); /* Get a character */
- if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */
- if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */
- wc = (WCHAR)uc;
- if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */
- if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */
- if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */
- lfn[di++] = wc; /* Store the Unicode character */
- }
- while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */
- *path = p; /* Return pointer to the next segment */
- cf = (wc < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */
-
-#if FF_FS_RPATH != 0
- if ((di == 1 && lfn[di - 1] == '.') ||
- (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */
- lfn[di] = 0;
- for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */
- dp->fn[i] = (i < di) ? '.' : ' ';
- }
- dp->fn[i] = cf | NS_DOT; /* This is a dot entry */
- return FR_OK;
- }
-#endif
- while (di) { /* Snip off trailing spaces and dots if exist */
- wc = lfn[di - 1];
- if (wc != ' ' && wc != '.') break;
- di--;
- }
- lfn[di] = 0; /* LFN is created into the working buffer */
- if (di == 0) return FR_INVALID_NAME; /* Reject null name */
-
- /* Create SFN in directory form */
- for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */
- if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */
- while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */
-
- mem_set(dp->fn, ' ', 11);
- i = b = 0; ni = 8;
- for (;;) {
- wc = lfn[si++]; /* Get an LFN character */
- if (wc == 0) break; /* Break on end of the LFN */
- if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */
- cf |= NS_LOSS | NS_LFN;
- continue;
- }
-
- if (i >= ni || si == di) { /* End of field? */
- if (ni == 11) { /* Name extension overflow? */
- cf |= NS_LOSS | NS_LFN;
- break;
- }
- if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */
- if (si > di) break; /* No name extension? */
- si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */
- continue;
- }
-
- if (wc >= 0x80) { /* Is this a non-ASCII character? */
- cf |= NS_LFN; /* LFN entry needs to be created */
-#if FF_CODE_PAGE == 0
- if (ExCvt) { /* At SBCS */
- wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
- if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
- } else { /* At DBCS */
- wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */
- }
-#elif FF_CODE_PAGE < 900 /* SBCS cfg */
- wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
- if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
-#else /* DBCS cfg */
- wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */
-#endif
- }
-
- if (wc >= 0x100) { /* Is this a DBC? */
- if (i >= ni - 1) { /* Field overflow? */
- cf |= NS_LOSS | NS_LFN;
- i = ni; continue; /* Next field */
- }
- dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */
- } else { /* SBC */
- if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */
- wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
- } else {
- if (IsUpper(wc)) { /* ASCII upper case? */
- b |= 2;
- }
- if (IsLower(wc)) { /* ASCII lower case? */
- b |= 1; wc -= 0x20;
- }
- }
- }
- dp->fn[i++] = (BYTE)wc;
- }
-
- if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
-
- if (ni == 8) b <<= 2; /* Shift capital flags if no extension */
- if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */
- if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
- if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */
- if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */
- }
-
- dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */
-
- return FR_OK;
-
-
-#else /* FF_USE_LFN : Non-LFN configuration */
- BYTE c, d, *sfn;
- UINT ni, si, i;
- const char *p;
-
- /* Create file name in directory form */
- p = *path; sfn = dp->fn;
- mem_set(sfn, ' ', 11);
- si = i = 0; ni = 8;
-#if FF_FS_RPATH != 0
- if (p[si] == '.') { /* Is this a dot entry? */
- for (;;) {
- c = (BYTE)p[si++];
- if (c != '.' || si >= 3) break;
- sfn[i++] = c;
- }
- if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
- *path = p + si; /* Return pointer to the next segment */
- sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */
- return FR_OK;
- }
-#endif
- for (;;) {
- c = (BYTE)p[si++]; /* Get a byte */
- if (c <= ' ') break; /* Break if end of the path name */
- if (c == '/' || c == '\\') { /* Break if a separator is found */
- while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */
- break;
- }
- if (c == '.' || i >= ni) { /* End of body or field overflow? */
- if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */
- i = 8; ni = 11; /* Enter file extension field */
- continue;
- }
-#if FF_CODE_PAGE == 0
- if (ExCvt && c >= 0x80) { /* Is SBC extended character? */
- c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
- }
-#elif FF_CODE_PAGE < 900
- if (c >= 0x80) { /* Is SBC extended character? */
- c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
- }
-#endif
- if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */
- d = (BYTE)p[si++]; /* Get 2nd byte */
- if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */
- sfn[i++] = c;
- sfn[i++] = d;
- } else { /* SBC */
- if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
- if (IsLower(c)) c -= 0x20; /* To upper */
- sfn[i++] = c;
- }
- }
- *path = p + si; /* Return pointer to the next segment */
- if (i == 0) return FR_INVALID_NAME; /* Reject nul string */
-
- if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
- sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */
-
- return FR_OK;
-#endif /* FF_USE_LFN */
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Follow a file path */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
- DIR* dp, /* Directory object to return last directory and found object */
- const TCHAR* path /* Full-path string to find a file or directory */
-)
-{
- FRESULT res;
- BYTE ns;
- FATFS *fs = dp->obj.fs;
-
-
-#if FF_FS_RPATH != 0
- if (*path != '/' && *path != '\\') { /* Without heading separator */
- dp->obj.sclust = fs->cdir; /* Start from current directory */
- } else
-#endif
- { /* With heading separator */
- while (*path == '/' || *path == '\\') path++; /* Strip heading separator */
- dp->obj.sclust = 0; /* Start from root directory */
- }
-#if FF_FS_EXFAT
- dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */
-#if FF_FS_RPATH != 0
- if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: Retrieve the sub-directory's status */
- DIR dj;
-
- dp->obj.c_scl = fs->cdc_scl;
- dp->obj.c_size = fs->cdc_size;
- dp->obj.c_ofs = fs->cdc_ofs;
- res = load_obj_xdir(&dj, &dp->obj);
- if (res != FR_OK) return res;
- dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize);
- dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
- }
-#endif
-#endif
-
- if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */
- dp->fn[NSFLAG] = NS_NONAME;
- res = dir_sdi(dp, 0);
-
- } else { /* Follow path */
- for (;;) {
- res = create_name(dp, &path); /* Get a segment name of the path */
- if (res != FR_OK) break;
- res = dir_find(dp); /* Find an object with the segment name */
- ns = dp->fn[NSFLAG];
- if (res != FR_OK) { /* Failed to find the object */
- if (res == FR_NO_FILE) { /* Object is not found */
- if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */
- if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */
- dp->fn[NSFLAG] = NS_NONAME;
- res = FR_OK;
- } else { /* Could not find the object */
- if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */
- }
- }
- break;
- }
- if (ns & NS_LAST) break; /* Last segment matched. Function completed. */
- /* Get into the sub-directory */
- if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */
- res = FR_NO_PATH; break;
- }
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */
- dp->obj.c_scl = dp->obj.sclust;
- dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
- dp->obj.c_ofs = dp->blk_ofs;
- init_alloc_info(fs, &dp->obj); /* Open next directory */
- } else
-#endif
- {
- dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */
- }
- }
- }
-
- return res;
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Get logical drive number from path name */
-/*-----------------------------------------------------------------------*/
-
-static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
- const TCHAR** path /* Pointer to pointer to the path name */
-)
-{
- const TCHAR *tp, *tt;
- TCHAR tc;
- int i, vol = -1;
-#if FF_STR_VOLUME_ID /* Find string volume ID */
- const char *sp;
- char c;
-#endif
-
- tt = tp = *path;
- if (!tp) return vol; /* Invalid path name? */
- do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */
-
- if (tc == ':') { /* DOS/Windows style volume ID? */
- i = FF_VOLUMES;
- if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */
- i = (int)*tp - '0'; /* Get the LD number */
- }
-#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
- else {
- i = 0;
- do {
- sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
- do { /* Compare the volume ID with path name */
- c = *sp++; tc = *tp++;
- if (IsLower(c)) c -= 0x20;
- if (IsLower(tc)) tc -= 0x20;
- } while (c && (TCHAR)c == tc);
- } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
- }
-#endif
- if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
- vol = i; /* Drive number */
- *path = tt; /* Snip the drive prefix off */
- }
- return vol;
- }
-#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
- if (*tp == '/') {
- i = 0;
- do {
- sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
- do { /* Compare the volume ID with path name */
- c = *sp++; tc = *(++tp);
- if (IsLower(c)) c -= 0x20;
- if (IsLower(tc)) tc -= 0x20;
- } while (c && (TCHAR)c == tc);
- } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
- if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
- vol = i; /* Drive number */
- *path = tp; /* Snip the drive prefix off */
- return vol;
- }
- }
-#endif
- /* No drive prefix is found */
-#if FF_FS_RPATH != 0
- vol = CurrVol; /* Default drive is current drive */
-#else
- vol = 0; /* Default drive is 0 */
-#endif
- return vol; /* Return the default drive */
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Load a sector and check if it is an FAT VBR */
-/*-----------------------------------------------------------------------*/
-
-static BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */
- FATFS* fs, /* Filesystem object */
- DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR or not */
-)
-{
- fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */
- if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */
-
- if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always here regardless of the sector size) */
-
-#if FF_FS_EXFAT
- if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */
-#endif
- if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */
- if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */
- if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */
- }
- return 2; /* Valid BS but not FAT */
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Determine logical drive number and mount the volume if needed */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred */
- const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
- FATFS** rfs, /* Pointer to pointer to the found filesystem object */
- BYTE mode /* !=0: Check write protection for write access */
-)
-{
- BYTE fmt, *pt;
- int vol;
- DSTATUS stat;
- DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];
- WORD nrsv;
- FATFS *fs;
- UINT i;
-
-
- /* Get logical drive number */
- *rfs = 0;
- vol = get_ldnumber(path);
- if (vol < 0) return FR_INVALID_DRIVE;
-
- /* Check if the filesystem object is valid or not */
- fs = FatFs[vol]; /* Get pointer to the filesystem object */
- if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
-#if FF_FS_REENTRANT
- if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */
-#endif
- *rfs = fs; /* Return pointer to the filesystem object */
-
- mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */
- if (fs->fs_type != 0) { /* If the volume has been mounted */
- stat = disk_status(fs->pdrv);
- if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
- if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */
- EFSPRINTF("WPEN1");
- return FR_WRITE_PROTECTED;
- }
- return FR_OK; /* The filesystem object is valid */
- }
- }
-
- /* The filesystem object is not valid. */
- /* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */
-
- fs->fs_type = 0; /* Clear the filesystem object */
- fs->pdrv = LD2PD(vol); /* Bind the logical drive and a physical drive */
- stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */
- if (stat & STA_NOINIT) { /* Check if the initialization succeeded */
- EFSPRINTF("MDNR");
- return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */
- }
- if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
- EFSPRINTF("WPEN2");
- return FR_WRITE_PROTECTED;
- }
-#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
- if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
- if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
-#endif
-
- /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK (MBR) and SFD (w/o partition). */
- bsect = 0;
- fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */
- if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */
- for (i = 0; i < 4; i++) { /* Get partition offset */
- pt = fs->win + (MBR_Table + i * SZ_PTE);
- br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0;
- }
- i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */
- if (i != 0) i--;
- do { /* Find an FAT volume */
- bsect = br[i];
- fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */
- } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);
- }
- if (fmt == 4) {
- EFSPRINTF("BRNL");
- return FR_DISK_ERR; /* An error occured in the disk I/O layer */
- }
- if (fmt >= 2) {
- EFSPRINTF("NOFAT");
- return FR_NO_FILESYSTEM; /* No FAT volume is found */
- }
-
- /* An FAT volume is found (bsect). Following code initializes the filesystem object */
-
-#if FF_FS_EXFAT
- if (fmt == 1) {
- QWORD maxlba;
- DWORD so, cv, bcl;
-
- for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
- if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
-
- if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */
-
- if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
- EFSPRINTF("EXSPS");
- return FR_NO_FILESYSTEM;
- }
-
- maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */
- if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */
-
- fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */
-
- fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */
- if (fs->n_fats != 1) {
- EFSPRINTF("EXFNF");
- return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */
- }
-
- fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */
- if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */
-
- nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */
- if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */
- fs->n_fatent = nclst + 2;
-
- /* Boundaries and Limits */
- fs->volbase = bsect;
- fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
- fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
- if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
- fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
-
- /* Get bitmap location and check if it is contiguous (implementation assumption) */
- so = i = 0;
- for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
- if (i == 0) {
- if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */
- if (move_window(fs, clst2sect(fs, fs->dirbase) + so) != FR_OK) {
- EFSPRINTF("EXBM1C");
- return FR_DISK_ERR;
- }
- so++;
- }
- if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */
- i = (i + SZDIRE) % SS(fs); /* Next entry */
- }
- bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */
- if (bcl < 2 || bcl >= fs->n_fatent) {
- EFSPRINTF("EXBMM");
- return FR_NO_FILESYSTEM;
- }
- fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */
- for (;;) { /* Check if bitmap is contiguous */
- if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
- cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4);
- if (cv == 0xFFFFFFFF) break; /* Last link? */
- if (cv != ++bcl) {
- EFSPRINTF("EXBMM");
- return FR_NO_FILESYSTEM; /* Fragmented? */
- }
- }
-
-#if !FF_FS_READONLY
- fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
-#endif
- fmt = FS_EXFAT; /* FAT sub-type */
- } else
-#endif /* FF_FS_EXFAT */
- {
- if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) {
- EFSPRINTF("32SPS");
- return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */
- }
-
- fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */
- if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
- fs->fsize = fasize;
-
- fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */
- if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
- fasize *= fs->n_fats; /* Number of sectors for FAT area */
-
- fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */
- if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */
-
- fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */
- if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */
-
- tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */
- if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
-
- nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */
- if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */
-
- /* Determine the FAT sub type */
- sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */
- if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
- nclst = (tsect - sysect) / fs->csize; /* Number of clusters */
- if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
- fmt = 0;
- if (nclst <= MAX_FAT32) fmt = FS_FAT32;
- if (nclst <= MAX_FAT16) fmt = FS_FAT16;
- if (nclst <= MAX_FAT12) fmt = FS_FAT12;
- if (fmt == 0) return FR_NO_FILESYSTEM;
-
- /* Boundaries and Limits */
- fs->n_fatent = nclst + 2; /* Number of FAT entries */
- fs->volbase = bsect; /* Volume start sector */
- fs->fatbase = bsect + nrsv; /* FAT start sector */
- fs->database = bsect + sysect; /* Data start sector */
- if (fmt == FS_FAT32) {
- if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
- if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
- fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */
- szbfat = fs->n_fatent * 4; /* (Needed FAT size) */
- } else {
- if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
- fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
- szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */
- fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
- }
- if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */
-
-#if !FF_FS_READONLY
- /* Get FSInfo if available */
- fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
- fs->fsi_flag = 0x80;
-#if (FF_FS_NOFSINFO & 3) != 3
- if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
- && ld_word(fs->win + BPB_FSInfo32) == 1
- && move_window(fs, bsect + 1) == FR_OK)
- {
- fs->fsi_flag = 0;
- if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */
- && ld_dword(fs->win + FSI_LeadSig) == 0x41615252
- && ld_dword(fs->win + FSI_StrucSig) == 0x61417272)
- {
-#if (FF_FS_NOFSINFO & 1) == 0
- fs->free_clst = ld_dword(fs->win + FSI_Free_Count);
-#endif
-#if (FF_FS_NOFSINFO & 2) == 0
- fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);
-#endif
- }
- }
-#endif /* (FF_FS_NOFSINFO & 3) != 3 */
-#endif /* !FF_FS_READONLY */
- }
-
- fs->fs_type = fmt; /* FAT sub-type */
- fs->id = ++Fsid; /* Volume mount ID */
-#if FF_USE_LFN == 1
- fs->lfnbuf = LfnBuf; /* Static LFN working buffer */
-#if FF_FS_EXFAT
- fs->dirbuf = DirBuf; /* Static directory block scratchpad buffer */
-#endif
-#endif
-#if FF_FS_RPATH != 0
- fs->cdir = 0; /* Initialize current directory */
-#endif
-#if FF_FS_LOCK != 0 /* Clear file lock semaphores */
- clear_lock(fs);
-#endif
- return FR_OK;
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Check if the file/directory object is valid or not */
-/*-----------------------------------------------------------------------*/
-
-static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
- FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
- FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */
-)
-{
- FRESULT res = FR_INVALID_OBJECT;
-
-
- if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */
-#if FF_FS_REENTRANT
- if (lock_fs(obj->fs)) { /* Obtain the filesystem object */
- if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
- res = FR_OK;
- } else {
- unlock_fs(obj->fs, FR_OK);
- }
- } else {
- res = FR_TIMEOUT;
- }
-#else
- if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
- res = FR_OK;
- }
-#endif
- }
- *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */
- return res;
-}
-
-
-
-
-/*---------------------------------------------------------------------------
-
- Public Functions (FatFs API)
-
-----------------------------------------------------------------------------*/
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Mount/Unmount a Logical Drive */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_mount (
- FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/
- const TCHAR* path, /* Logical drive number to be mounted/unmounted */
- BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
-)
-{
- FATFS *cfs;
- int vol;
- FRESULT res;
- const TCHAR *rp = path;
-
-
- /* Get logical drive number */
- vol = get_ldnumber(&rp);
- if (vol < 0) {
- EFSPRINTF("IDRIVE!");
- return FR_INVALID_DRIVE;
- }
- cfs = FatFs[vol]; /* Pointer to fs object */
-
- if (cfs) {
-#if FF_FS_LOCK != 0
- clear_lock(cfs);
-#endif
-#if FF_FS_REENTRANT /* Discard sync object of the current volume */
- if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
-#endif
- cfs->fs_type = 0; /* Clear old fs object */
- }
-
- if (fs) {
- fs->fs_type = 0; /* Clear new fs object */
-#if FF_FS_REENTRANT /* Create sync object for the new volume */
- if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
-#endif
- }
- FatFs[vol] = fs; /* Register new fs object */
-
- if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted later */
-
- res = find_volume(&path, &fs, 0); /* Force mounted the volume */
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Open or Create a File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_open (
- FIL* fp, /* Pointer to the blank file object */
- const TCHAR* path, /* Pointer to the file name */
- BYTE mode /* Access mode and file open mode flags */
-)
-{
- FRESULT res;
- DIR dj;
- FATFS *fs;
-#if !FF_FS_READONLY
- DWORD dw, cl, bcs, clst, sc;
- FSIZE_t ofs;
-#endif
- DEF_NAMBUF
-
-
- if (!fp) return FR_INVALID_OBJECT;
-
- /* Get logical drive number */
- mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
- res = find_volume(&path, &fs, mode);
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&dj, path); /* Follow the file path */
-#if !FF_FS_READONLY /* Read/Write configuration */
- if (res == FR_OK) {
- if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */
- res = FR_INVALID_NAME;
- }
-#if FF_FS_LOCK != 0
- else {
- res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Check if the file can be used */
- }
-#endif
- }
- /* Create or Open a file */
- if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
- if (res != FR_OK) { /* No file, create new */
- if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */
-#if FF_FS_LOCK != 0
- res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
-#else
- res = dir_register(&dj);
-#endif
- }
- mode |= FA_CREATE_ALWAYS; /* File is created */
- }
- else { /* Any object with the same name is already existing */
- if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */
- res = FR_DENIED;
- } else {
- if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */
- }
- }
- if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- /* Get current allocation info */
- fp->obj.fs = fs;
- init_alloc_info(fs, &fp->obj);
- /* Set directory entry block initial state */
- mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */
- mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */
- fs->dirbuf[XDIR_Attr] = AM_ARC;
- st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME());
- fs->dirbuf[XDIR_GenFlags] = 1;
- res = store_xdir(&dj);
- if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */
- res = remove_chain(&fp->obj, fp->obj.sclust, 0);
- fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */
- }
- } else
-#endif
- {
- /* Set directory entry initial state */
- cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
- st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */
- dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */
- st_clust(fs, dj.dir, 0); /* Reset file allocation info */
- st_dword(dj.dir + DIR_FileSize, 0);
- fs->wflag = 1;
- if (cl != 0) { /* Remove the cluster chain if exist */
- dw = fs->winsect;
- res = remove_chain(&dj.obj, cl, 0);
- if (res == FR_OK) {
- res = move_window(fs, dw);
- fs->last_clst = cl - 1; /* Reuse the cluster hole */
- }
- }
- }
- }
- }
- else { /* Open an existing file */
- if (res == FR_OK) { /* Is the object exsiting? */
- if (dj.obj.attr & AM_DIR) { /* File open against a directory */
- res = FR_NO_FILE;
- } else {
- if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */
- res = FR_DENIED;
- }
- }
- }
- }
- if (res == FR_OK) {
- if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* Set file change flag if created or overwritten */
- fp->dir_sect = fs->winsect; /* Pointer to the directory entry */
- fp->dir_ptr = dj.dir;
-#if FF_FS_LOCK != 0
- fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */
- if (fp->obj.lockid == 0) res = FR_INT_ERR;
-#endif
- }
-#else /* R/O configuration */
- if (res == FR_OK) {
- if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin directory itself? */
- res = FR_INVALID_NAME;
- } else {
- if (dj.obj.attr & AM_DIR) { /* Is it a directory? */
- res = FR_NO_FILE;
- }
- }
- }
-#endif
-
- if (res == FR_OK) {
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */
- fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
- fp->obj.c_ofs = dj.blk_ofs;
- init_alloc_info(fs, &fp->obj);
- } else
-#endif
- {
- fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */
- fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);
- }
-#if FF_USE_FASTSEEK
- fp->cltbl = 0; /* Disable fast seek mode */
-#endif
- fp->obj.fs = fs; /* Validate the file object */
- fp->obj.id = fs->id;
- fp->flag = mode; /* Set file access mode */
- fp->err = 0; /* Clear error flag */
- fp->sect = 0; /* Invalidate current data sector */
- fp->fptr = 0; /* Set file pointer top of the file */
-#if !FF_FS_READONLY
-#if !FF_FS_TINY
- mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */
-#endif
- if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */
- fp->fptr = fp->obj.objsize; /* Offset to seek */
- bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */
- clst = fp->obj.sclust; /* Follow the cluster chain */
- for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {
- clst = get_fat(&fp->obj, clst);
- if (clst <= 1) res = FR_INT_ERR;
- if (clst == 0xFFFFFFFF) res = FR_DISK_ERR;
- }
- fp->clust = clst;
- if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */
- if ((sc = clst2sect(fs, clst)) == 0) {
- res = FR_INT_ERR;
- } else {
- fp->sect = sc + (DWORD)(ofs / SS(fs));
-#if !FF_FS_TINY
- if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;
-#endif
- }
- }
- }
-#endif
- }
-
- FREE_NAMBUF();
- }
-
- if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Read File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_read (
- FIL* fp, /* Pointer to the file object */
- void* buff, /* Pointer to data buffer */
- UINT btr, /* Number of bytes to read */
- UINT* br /* Pointer to number of bytes read */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD clst, sect;
- FSIZE_t remain;
- UINT rcnt, cc, csect;
- BYTE *rbuff = (BYTE*)buff;
-
- UINT br_tmp;
- if (!br)
- br = &br_tmp;
- *br = 0; /* Clear read byte counter */
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
- EFSPRINTF("FOV");
- LEAVE_FF(fs, res); /* Check validity */
- }
- if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
- remain = fp->obj.objsize - fp->fptr;
- if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
-
- for ( ; btr; /* Repeat until btr bytes read */
- btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) {
- if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
- csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
- if (csect == 0) { /* On the cluster boundary? */
- if (fp->fptr == 0) { /* On the top of the file? */
- clst = fp->obj.sclust; /* Follow cluster chain from the origin */
- } else { /* Middle or end of the file */
-#if FF_USE_FASTSEEK
- if (fp->cltbl) {
- clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
- } else
-#endif
- {
- clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */
- }
- }
- if (clst < 2) {
- EFSPRINTF("CCHK");
- ABORT(fs, FR_INT_ERR);
- }
- if (clst == 0xFFFFFFFF) {
- EFSPRINTF("DSKC");
- ABORT(fs, FR_DISK_ERR);
- }
- fp->clust = clst; /* Update current cluster */
- }
- sect = clst2sect(fs, fp->clust); /* Get current sector */
- if (sect == 0) ABORT(fs, FR_INT_ERR);
- sect += csect;
- cc = btr / SS(fs); /* When remaining bytes >= sector size, */
- if (cc > 0) { /* Read maximum contiguous sectors directly */
- if (csect + cc > fs->csize) { /* Clip at cluster boundary */
- cc = fs->csize - csect;
- }
- if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) {
- EFSPRINTF("RLIO");
- ABORT(fs, FR_DISK_ERR);
- }
-#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
-#if FF_FS_TINY
- if (fs->wflag && fs->winsect - sect < cc) {
- mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
- }
-#else
- if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
- mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
- }
-#endif
-#endif
- rcnt = SS(fs) * cc; /* Number of bytes transferred */
- continue;
- }
-#if !FF_FS_TINY
- if (fp->sect != sect) { /* Load data sector if not in cache */
-#if !FF_FS_READONLY
- if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
- EFSPRINTF("RDC");
- ABORT(fs, FR_DISK_ERR);
- }
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
- if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
- EFSPRINTF("RSC");
- ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
- }
- }
-#endif
- fp->sect = sect;
- }
- rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
- if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */
-#if FF_FS_TINY
- if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
- mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
-#else
- mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
-#endif
- }
-
- LEAVE_FF(fs, FR_OK);
-}
-
-
-
-
-#if FF_FASTFS && FF_USE_FASTSEEK
-/*-----------------------------------------------------------------------*/
-/* Fast Read Aligned Sized File Without a Cache */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_read_fast (
- FIL* fp, /* Pointer to the file object */
- const void* buff, /* Pointer to the data to be written */
- UINT btr /* Number of bytes to read */
-)
-{
- FRESULT res;
- FATFS *fs;
- UINT csize_bytes;
- DWORD clst;
- UINT count = 0;
- FSIZE_t work_sector = 0;
- FSIZE_t sector_base = 0;
- BYTE *wbuff = (BYTE*)buff;
-
- // TODO support sector reading inside a cluster
-
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
- EFSPRINTF("FOV");
- LEAVE_FF(fs, res); /* Check validity */
- }
-
- if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
- FSIZE_t remain = fp->obj.objsize - fp->fptr;
- if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
-
- csize_bytes = fs->csize * SS(fs);
-
- if (!fp->fptr) { /* On the top of the file? */
- clst = fp->obj.sclust; /* Follow from the origin */
- } else {
- if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
- else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
- }
- if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
- else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
-
- fp->clust = clst; /* Set working cluster */
-
- sector_base = clst2sect(fs, fp->clust);
- count += fs->csize;
- btr -= csize_bytes;
- fp->fptr += csize_bytes;
-
- while (btr) {
- clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
-
- if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
- else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
-
- fp->clust = clst;
-
- work_sector = clst2sect(fs, fp->clust);
- if ((work_sector - sector_base) == count) count += fs->csize;
- else {
- if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
- wbuff += count * SS(fs);
-
- sector_base = work_sector;
- count = fs->csize;
- }
-
- fp->fptr += MIN(btr, csize_bytes);
- btr -= MIN(btr, csize_bytes);
-
- // TODO: what about if data is smaller than cluster?
- // Must read-write back that cluster.
-
- if (!btr) { /* Final cluster/sectors read. */
- if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
- }
- }
-
- LEAVE_FF(fs, FR_OK);
-}
-#endif
-
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Write File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_write (
- FIL* fp, /* Pointer to the file object */
- const void* buff, /* Pointer to the data to be written */
- UINT btw, /* Number of bytes to write */
- UINT* bw /* Pointer to number of bytes written */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD clst, sect;
- UINT wcnt, cc, csect;
- const BYTE *wbuff = (const BYTE*)buff;
-
- UINT bw_tmp;
- if (!bw)
- bw = &bw_tmp;
- *bw = 0; /* Clear write byte counter */
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
- EFSPRINTF("FOV");
- LEAVE_FF(fs, res); /* Check validity */
- }
- if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
-
- /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
- if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
- btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
- }
-
- for ( ; btw; /* Repeat until all data written */
- btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) {
- if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
- csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */
- if (csect == 0) { /* On the cluster boundary? */
- if (fp->fptr == 0) { /* On the top of the file? */
- clst = fp->obj.sclust; /* Follow from the origin */
- if (clst == 0) { /* If no cluster is allocated, */
- clst = create_chain(&fp->obj, 0); /* create a new cluster chain */
- }
- } else { /* On the middle or end of the file */
-#if FF_USE_FASTSEEK
- if (fp->cltbl) {
- clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
- } else
-#endif
- {
- clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */
- }
- }
- if (clst == 0) {
- EFSPRINTF("DSKFULL");
- fp->flag |= FA_MODIFIED;
- ABORT(fs, FR_DISK_ERR); /* Could not allocate a new cluster (disk full) */
- }
- if (clst == 1) {
- EFSPRINTF("CCHK");
- ABORT(fs, FR_INT_ERR);
- }
- if (clst == 0xFFFFFFFF) {
- EFSPRINTF("DERR");
- ABORT(fs, FR_DISK_ERR);
- }
- fp->clust = clst; /* Update current cluster */
- if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
- }
-#if FF_FS_TINY
- if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */
-#else
- if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
- sect = clst2sect(fs, fp->clust); /* Get current sector */
- if (sect == 0) ABORT(fs, FR_INT_ERR);
- sect += csect;
- cc = btw / SS(fs); /* When remaining bytes >= sector size, */
- if (cc > 0) { /* Write maximum contiguous sectors directly */
- if (csect + cc > fs->csize) { /* Clip at cluster boundary */
- cc = fs->csize - csect;
- }
- if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) {
- EFSPRINTF("WLIO");
- ABORT(fs, FR_DISK_ERR);
- }
-#if FF_FS_MINIMIZE <= 2
-#if FF_FS_TINY
- if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
- mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));
- fs->wflag = 0;
- }
-#else
- if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
- mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
-#endif
- wcnt = SS(fs) * cc; /* Number of bytes transferred */
- continue;
- }
-#if FF_FS_TINY
- if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */
- if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
- fs->winsect = sect;
- }
-#else
- if (fp->sect != sect && /* Fill sector cache with file data */
- fp->fptr < fp->obj.objsize &&
- disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
- ABORT(fs, FR_DISK_ERR);
- }
-#endif
- fp->sect = sect;
- }
- wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
- if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */
-#if FF_FS_TINY
- if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
- mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
- fs->wflag = 1;
-#else
- mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
- fp->flag |= FA_DIRTY;
-#endif
- }
-
- fp->flag |= FA_MODIFIED; /* Set file change flag */
-
- LEAVE_FF(fs, FR_OK);
-}
-
-
-
-
-#if FF_FASTFS && FF_USE_FASTSEEK
-/*-----------------------------------------------------------------------*/
-/* Fast Write Aligned Sized File Without a Cache */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_write_fast (
- FIL* fp, /* Pointer to the file object */
- const void* buff, /* Pointer to the data to be written */
- UINT btw /* Number of bytes to write */
-)
-{
- FRESULT res;
- FATFS *fs;
- UINT csize_bytes;
- DWORD clst;
- UINT count = 0;
- FSIZE_t work_sector = 0;
- FSIZE_t sector_base = 0;
- const BYTE *wbuff = (const BYTE*)buff;
-
- // TODO support sector writing inside a cluster
-
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
- EFSPRINTF("FOV");
- LEAVE_FF(fs, res); /* Check validity */
- }
-
- if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
- /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
- if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
- btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
- }
-
- csize_bytes = fs->csize * SS(fs);
-
- if (!fp->fptr) { /* On the top of the file? */
- clst = fp->obj.sclust; /* Follow from the origin */
- } else {
- if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
- else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
- }
-
- if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
- else if (clst == 0xFFFFFFFF) { EFSPRINTF("DERR"); ABORT(fs, FR_DISK_ERR); }
-
- fp->clust = clst; /* Set working cluster */
-
- sector_base = clst2sect(fs, fp->clust);
- count += fs->csize;
- btw -= csize_bytes;
- fp->fptr += csize_bytes;
-
- while (btw) {
- clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
-
- if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
- else if (clst == 0xFFFFFFFF) { EFSPRINTF("DERR"); ABORT(fs, FR_DISK_ERR); }
-
- fp->clust = clst;
-
- work_sector = clst2sect(fs, fp->clust);
- if ((work_sector - sector_base) == count) count += fs->csize;
- else {
- if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
- wbuff += count * SS(fs);
-
- sector_base = work_sector;
- count = fs->csize;
- }
-
- fp->fptr += MIN(btw, csize_bytes);
- btw -= MIN(btw, csize_bytes);
-
- // what about if data is smaller than cluster?
- // Probably must read-write back that cluster.
- if (!btw) { /* Final cluster/sectors write. */
- if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
- fp->flag &= (BYTE)~FA_DIRTY;
- }
- }
-
- fp->flag |= FA_MODIFIED; /* Set file change flag */
-
- LEAVE_FF(fs, FR_OK);
-}
-#endif
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Synchronize the File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_sync (
- FIL* fp /* Pointer to the file object */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD tm;
- BYTE *dir;
-
-
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res == FR_OK) {
- if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */
-#if !FF_FS_TINY
- if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
- /* Update the directory entry */
- tm = GET_FATTIME(); /* Modified time */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */
- if (res == FR_OK) {
- res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
- }
- if (res == FR_OK) {
- DIR dj;
- DEF_NAMBUF
-
- INIT_NAMBUF(fs);
- res = load_obj_xdir(&dj, &fp->obj); /* Load directory entry block */
- if (res == FR_OK) {
- fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */
- fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation information */
- st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust);
- st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize);
- st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize);
- st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */
- fs->dirbuf[XDIR_ModTime10] = 0;
- st_dword(fs->dirbuf + XDIR_AccTime, 0);
- res = store_xdir(&dj); /* Restore it to the directory */
- if (res == FR_OK) {
- res = sync_fs(fs);
- fp->flag &= (BYTE)~FA_MODIFIED;
- }
- }
- FREE_NAMBUF();
- }
- } else
-#endif
- {
- res = move_window(fs, fp->dir_sect);
- if (res == FR_OK) {
- dir = fp->dir_ptr;
- dir[DIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */
- st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */
- st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */
- st_dword(dir + DIR_ModTime, tm); /* Update modified time */
- st_word(dir + DIR_LstAccDate, 0);
- fs->wflag = 1;
- res = sync_fs(fs); /* Restore it to the directory */
- fp->flag &= (BYTE)~FA_MODIFIED;
- }
- }
- }
- }
-
- LEAVE_FF(fs, res);
-}
-
-#endif /* !FF_FS_READONLY */
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Close File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_close (
- FIL* fp /* Pointer to the file object to be closed */
-)
-{
- FRESULT res;
- FATFS *fs;
-
-#if !FF_FS_READONLY
- res = f_sync(fp); /* Flush cached data */
- if (res == FR_OK)
-#endif
- {
- res = validate(&fp->obj, &fs); /* Lock volume */
- if (res == FR_OK) {
-#if FF_FS_LOCK != 0
- res = dec_lock(fp->obj.lockid); /* Decrement file open counter */
- if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file object */
-#else
- fp->obj.fs = 0; /* Invalidate file object */
-#endif
-#if FF_FS_REENTRANT
- unlock_fs(fs, FR_OK); /* Unlock volume */
-#endif
- }
- }
- return res;
-}
-
-
-
-
-#if FF_FS_RPATH >= 1
-/*-----------------------------------------------------------------------*/
-/* Change Current Directory or Current Drive, Get Current Directory */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_chdrive (
- const TCHAR* path /* Drive number to set */
-)
-{
- int vol;
-
-
- /* Get logical drive number */
- vol = get_ldnumber(&path);
- if (vol < 0) return FR_INVALID_DRIVE;
- CurrVol = (BYTE)vol; /* Set it as current volume */
-
- return FR_OK;
-}
-
-
-
-FRESULT f_chdir (
- const TCHAR* path /* Pointer to the directory path */
-)
-{
-#if FF_STR_VOLUME_ID == 2
- UINT i;
-#endif
- FRESULT res;
- DIR dj;
- FATFS *fs;
- DEF_NAMBUF
-
-
- /* Get logical drive */
- res = find_volume(&path, &fs, 0);
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&dj, path); /* Follow the path */
- if (res == FR_OK) { /* Follow completed */
- if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start directory itself? */
- fs->cdir = dj.obj.sclust;
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- fs->cdc_scl = dj.obj.c_scl;
- fs->cdc_size = dj.obj.c_size;
- fs->cdc_ofs = dj.obj.c_ofs;
- }
-#endif
- } else {
- if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */
- fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */
- fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
- fs->cdc_ofs = dj.blk_ofs;
- } else
-#endif
- {
- fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */
- }
- } else {
- res = FR_NO_PATH; /* Reached but a file */
- }
- }
- }
- FREE_NAMBUF();
- if (res == FR_NO_FILE) res = FR_NO_PATH;
-#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */
- if (res == FR_OK) {
- for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */
- CurrVol = (BYTE)i;
- }
-#endif
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-#if FF_FS_RPATH >= 2
-FRESULT f_getcwd (
- TCHAR* buff, /* Pointer to the directory path */
- UINT len /* Size of buff in unit of TCHAR */
-)
-{
- FRESULT res;
- DIR dj;
- FATFS *fs;
- UINT i, n;
- DWORD ccl;
- TCHAR *tp = buff;
-#if FF_VOLUMES >= 2
- UINT vl;
-#if FF_STR_VOLUME_ID
- const char *vp;
-#endif
-#endif
- FILINFO fno;
- DEF_NAMBUF
-
-
- /* Get logical drive */
- buff[0] = 0; /* Set null string to get current volume */
- res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
-
- /* Follow parent directories and create the path */
- i = len; /* Bottom of buffer (directory stack base) */
- if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */
- dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */
- while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */
- res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */
- if (res != FR_OK) break;
- res = move_window(fs, dj.sect);
- if (res != FR_OK) break;
- dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */
- res = dir_sdi(&dj, 0);
- if (res != FR_OK) break;
- do { /* Find the entry links to the child directory */
- res = DIR_READ_FILE(&dj);
- if (res != FR_OK) break;
- if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */
- res = dir_next(&dj, 0);
- } while (res == FR_OK);
- if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
- if (res != FR_OK) break;
- get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */
- for (n = 0; fno.fname[n]; n++) ; /* Name length */
- if (i < n + 1) { /* Insufficient space to store the path name? */
- res = FR_NOT_ENOUGH_CORE; break;
- }
- while (n) buff[--i] = fno.fname[--n]; /* Stack the name */
- buff[--i] = '/';
- }
- }
- if (res == FR_OK) {
- if (i == len) buff[--i] = '/'; /* Is it the root-directory? */
-#if FF_VOLUMES >= 2 /* Put drive prefix */
- vl = 0;
-#if FF_STR_VOLUME_ID >= 1 /* String volume ID */
- for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ;
- if (i >= n + 2) {
- if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/';
- for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ;
- if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':';
- vl++;
- }
-#else /* Numeric volume ID */
- if (i >= 3) {
- *tp++ = (TCHAR)'0' + CurrVol;
- *tp++ = (TCHAR)':';
- vl = 2;
- }
-#endif
- if (vl == 0) res = FR_NOT_ENOUGH_CORE;
-#endif
- /* Add current directory path */
- if (res == FR_OK) {
- do *tp++ = buff[i++]; while (i < len); /* Copy stacked path string */
- }
- }
- FREE_NAMBUF();
- }
-
- *tp = 0;
- LEAVE_FF(fs, res);
-}
-
-#endif /* FF_FS_RPATH >= 2 */
-#endif /* FF_FS_RPATH >= 1 */
-
-
-
-#if FF_FS_MINIMIZE <= 2
-/*-----------------------------------------------------------------------*/
-/* Seek File Read/Write Pointer */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_lseek (
- FIL* fp, /* Pointer to the file object */
- FSIZE_t ofs /* File pointer from top of file */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD clst, bcs, nsect;
- FSIZE_t ifptr;
-#if FF_USE_FASTSEEK
- DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
-#endif
-
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res == FR_OK) res = (FRESULT)fp->err;
-#if FF_FS_EXFAT && !FF_FS_READONLY
- if (res == FR_OK && fs->fs_type == FS_EXFAT) {
- res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
- }
-#endif
- if (res != FR_OK) LEAVE_FF(fs, res);
-
-#if FF_USE_FASTSEEK
- if (fp->cltbl) { /* Fast seek */
- if (ofs == CREATE_LINKMAP) { /* Create CLMT */
- tbl = fp->cltbl;
- tlen = *tbl++; ulen = 2; /* Given table size and required table size */
- cl = fp->obj.sclust; /* Origin of the chain */
- if (cl != 0) {
- do {
- /* Get a fragment */
- tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */
- do {
- pcl = cl; ncl++;
- cl = get_fat(&fp->obj, cl);
- if (cl <= 1) ABORT(fs, FR_INT_ERR);
- if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
- } while (cl == pcl + 1);
- if (ulen <= tlen) { /* Store the length and top of the fragment */
- *tbl++ = ncl; *tbl++ = tcl;
- }
- } while (cl < fs->n_fatent); /* Repeat until end of chain */
- }
- *fp->cltbl = ulen; /* Number of items used */
- if (ulen <= tlen) {
- *tbl = 0; /* Terminate table */
- } else {
- res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */
- }
- } else { /* Fast seek */
- if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */
- fp->fptr = ofs; /* Set file pointer */
- if (ofs > 0) {
- fp->clust = clmt_clust(fp, ofs - 1);
- dsc = clst2sect(fs, fp->clust);
- if (dsc == 0) ABORT(fs, FR_INT_ERR);
- dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1);
- if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */
-#if !FF_FS_TINY
-#if !FF_FS_READONLY
- if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
- if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */
-#endif
- fp->sect = dsc;
- }
- }
- }
- } else
-#endif
-
- /* Normal Seek */
- {
-#if FF_FS_EXFAT
- if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */
-#endif
- if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */
- ofs = fp->obj.objsize;
- }
- ifptr = fp->fptr;
- fp->fptr = nsect = 0;
- if (ofs > 0) {
- bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */
- if (ifptr > 0 &&
- (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
- fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */
- ofs -= fp->fptr;
- clst = fp->clust;
- } else { /* When seek to back cluster, */
- clst = fp->obj.sclust; /* start from the first cluster */
-#if !FF_FS_READONLY
- if (clst == 0) { /* If no cluster chain, create a new chain */
- clst = create_chain(&fp->obj, 0);
- if (clst == 1) ABORT(fs, FR_INT_ERR);
- if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
- fp->obj.sclust = clst;
- }
-#endif
- fp->clust = clst;
- }
- if (clst != 0) {
- while (ofs > bcs) { /* Cluster following loop */
- ofs -= bcs; fp->fptr += bcs;
-#if !FF_FS_READONLY
- if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
- if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */
- fp->obj.objsize = fp->fptr;
- fp->flag |= FA_MODIFIED;
- }
- clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */
- if (clst == 0) { /* Clip file size in case of disk full */
- ofs = 0; break;
- }
- } else
-#endif
- {
- clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */
- }
- if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
- if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
- fp->clust = clst;
- }
- fp->fptr += ofs;
- if (ofs % SS(fs)) {
- nsect = clst2sect(fs, clst); /* Current sector */
- if (nsect == 0) ABORT(fs, FR_INT_ERR);
- nsect += (DWORD)(ofs / SS(fs));
- }
- }
- }
- if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */
- fp->obj.objsize = fp->fptr;
- fp->flag |= FA_MODIFIED;
- }
- if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */
-#if !FF_FS_TINY
-#if !FF_FS_READONLY
- if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
- if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
-#endif
- fp->sect = nsect;
- }
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-#if FF_FASTFS && FF_USE_FASTSEEK
-/*-----------------------------------------------------------------------*/
-/* Seek File Read/Write Pointer */
-/*-----------------------------------------------------------------------*/
-
-DWORD *f_expand_cltbl (
- FIL* fp, /* Pointer to the file object */
- UINT tblsz, /* Size of table */
- FSIZE_t ofs /* File pointer from top of file */
-)
-{
- if (fp->flag & FA_WRITE) f_lseek(fp, ofs); /* Expand file if write is enabled */
- if (!fp->cltbl) { /* Allocate memory for cluster link table */
- fp->cltbl = (DWORD *)ff_memalloc(tblsz);
- fp->cltbl[0] = tblsz;
- }
- if (f_lseek(fp, CREATE_LINKMAP)) { /* Create cluster link table */
- ff_memfree(fp->cltbl);
- fp->cltbl = NULL;
- EFSPRINTF("CLTBLSZ");
- return NULL;
- }
- f_lseek(fp, 0);
-
- return fp->cltbl;
-}
-#endif
-
-
-
-
-#if FF_FS_MINIMIZE <= 1
-/*-----------------------------------------------------------------------*/
-/* Create a Directory Object */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_opendir (
- DIR* dp, /* Pointer to directory object to create */
- const TCHAR* path /* Pointer to the directory path */
-)
-{
- FRESULT res;
- FATFS *fs;
- DEF_NAMBUF
-
-
- if (!dp) return FR_INVALID_OBJECT;
-
- /* Get logical drive */
- res = find_volume(&path, &fs, 0);
- if (res == FR_OK) {
- dp->obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(dp, path); /* Follow the path to the directory */
- if (res == FR_OK) { /* Follow completed */
- if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */
- if (dp->obj.attr & AM_DIR) { /* This object is a sub-directory */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- dp->obj.c_scl = dp->obj.sclust; /* Get containing directory inforamation */
- dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
- dp->obj.c_ofs = dp->blk_ofs;
- init_alloc_info(fs, &dp->obj); /* Get object allocation info */
- } else
-#endif
- {
- dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */
- }
- } else { /* This object is a file */
- res = FR_NO_PATH;
- }
- }
- if (res == FR_OK) {
- dp->obj.id = fs->id;
- res = dir_sdi(dp, 0); /* Rewind directory */
-#if FF_FS_LOCK != 0
- if (res == FR_OK) {
- if (dp->obj.sclust != 0) {
- dp->obj.lockid = inc_lock(dp, 0); /* Lock the sub directory */
- if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES;
- } else {
- dp->obj.lockid = 0; /* Root directory need not to be locked */
- }
- }
-#endif
- }
- }
- FREE_NAMBUF();
- if (res == FR_NO_FILE) res = FR_NO_PATH;
- }
- if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the directory object if function faild */
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Close Directory */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_closedir (
- DIR *dp /* Pointer to the directory object to be closed */
-)
-{
- FRESULT res;
- FATFS *fs;
-
-
- res = validate(&dp->obj, &fs); /* Check validity of the file object */
- if (res == FR_OK) {
-#if FF_FS_LOCK != 0
- if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* Decrement sub-directory open counter */
- if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory object */
-#else
- dp->obj.fs = 0; /* Invalidate directory object */
-#endif
-#if FF_FS_REENTRANT
- unlock_fs(fs, FR_OK); /* Unlock volume */
-#endif
- }
- return res;
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Read Directory Entries in Sequence */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_readdir (
- DIR* dp, /* Pointer to the open directory object */
- FILINFO* fno /* Pointer to file information to return */
-)
-{
- FRESULT res;
- FATFS *fs;
- DEF_NAMBUF
-
-
- res = validate(&dp->obj, &fs); /* Check validity of the directory object */
- if (res == FR_OK) {
- if (!fno) {
- res = dir_sdi(dp, 0); /* Rewind the directory object */
- } else {
- INIT_NAMBUF(fs);
- res = DIR_READ_FILE(dp); /* Read an item */
- if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */
- if (res == FR_OK) { /* A valid entry is found */
- get_fileinfo(dp, fno); /* Get the object information */
- res = dir_next(dp, 0); /* Increment index for next */
- if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */
- }
- FREE_NAMBUF();
- }
- }
- LEAVE_FF(fs, res);
-}
-
-
-
-#if FF_USE_FIND
-/*-----------------------------------------------------------------------*/
-/* Find Next File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_findnext (
- DIR* dp, /* Pointer to the open directory object */
- FILINFO* fno /* Pointer to the file information structure */
-)
-{
- FRESULT res;
-
-
- for (;;) {
- res = f_readdir(dp, fno); /* Get a directory item */
- if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */
- if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */
-#if FF_USE_LFN && FF_USE_FIND == 2
- if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */
-#endif
- }
- return res;
-}
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Find First File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_findfirst (
- DIR* dp, /* Pointer to the blank directory object */
- FILINFO* fno, /* Pointer to the file information structure */
- const TCHAR* path, /* Pointer to the directory to open */
- const TCHAR* pattern /* Pointer to the matching pattern */
-)
-{
- FRESULT res;
-
-
- dp->pat = pattern; /* Save pointer to pattern string */
- res = f_opendir(dp, path); /* Open the target directory */
- if (res == FR_OK) {
- res = f_findnext(dp, fno); /* Find the first item */
- }
- return res;
-}
-
-#endif /* FF_USE_FIND */
-
-
-
-#if FF_FS_MINIMIZE == 0
-/*-----------------------------------------------------------------------*/
-/* Get File Status */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_stat (
- const TCHAR* path, /* Pointer to the file path */
- FILINFO* fno /* Pointer to file information to return */
-)
-{
- FRESULT res;
- DIR dj;
- DEF_NAMBUF
-
-
- /* Get logical drive */
- res = find_volume(&path, &dj.obj.fs, 0);
- if (res == FR_OK) {
- INIT_NAMBUF(dj.obj.fs);
- res = follow_path(&dj, path); /* Follow the file path */
- if (res == FR_OK) { /* Follow completed */
- if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */
- res = FR_INVALID_NAME;
- } else { /* Found an object */
- if (fno) get_fileinfo(&dj, fno);
- }
- }
- FREE_NAMBUF();
- }
-
- LEAVE_FF(dj.obj.fs, res);
-}
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Get Number of Free Clusters */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_getfree (
- const TCHAR* path, /* Logical drive number */
- DWORD* nclst, /* Pointer to a variable to return number of free clusters */
- FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD nfree, clst, sect, stat;
- UINT i;
- FFOBJID obj;
-
-
- /* Get logical drive */
- res = find_volume(&path, &fs, 0);
- if (res == FR_OK) {
- if (fatfs) *fatfs = fs; /* Return ptr to the fs object */
- /* If free_clst is valid, return it without full FAT scan */
- if (fs->free_clst <= fs->n_fatent - 2) {
- *nclst = fs->free_clst;
- } else {
- /* Scan FAT to obtain number of free clusters */
- nfree = 0;
- if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit field FAT entries */
- clst = 2; obj.fs = fs;
- do {
- stat = get_fat(&obj, clst);
- if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
- if (stat == 1) { res = FR_INT_ERR; break; }
- if (stat == 0) nfree++;
- } while (++clst < fs->n_fatent);
- } else {
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan allocation bitmap */
- BYTE bm;
- UINT b;
-
- clst = fs->n_fatent - 2; /* Number of clusters */
- sect = fs->bitbase; /* Bitmap sector */
- i = 0; /* Offset in the sector */
- do { /* Counts numbuer of bits with zero in the bitmap */
- if (i == 0) {
- res = move_window(fs, sect++);
- if (res != FR_OK) break;
- }
- for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) {
- if (!(bm & 1)) nfree++;
- bm >>= 1;
- }
- i = (i + 1) % SS(fs);
- } while (clst);
- } else
-#endif
- { /* FAT16/32: Scan WORD/DWORD FAT entries */
- clst = fs->n_fatent; /* Number of entries */
- sect = fs->fatbase; /* Top of the FAT */
- i = 0; /* Offset in the sector */
- do { /* Counts numbuer of entries with zero in the FAT */
- if (i == 0) {
- res = move_window(fs, sect++);
- if (res != FR_OK) break;
- }
- if (fs->fs_type == FS_FAT16) {
- if (ld_word(fs->win + i) == 0) nfree++;
- i += 2;
- } else {
- if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++;
- i += 4;
- }
- i %= SS(fs);
- } while (--clst);
- }
- }
- *nclst = nfree; /* Return the free clusters */
- fs->free_clst = nfree; /* Now free_clst is valid */
- fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */
- }
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Truncate File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_truncate (
- FIL* fp /* Pointer to the file object */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD ncl;
-
-
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
- if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
-
- if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */
- if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */
- res = remove_chain(&fp->obj, fp->obj.sclust, 0);
- fp->obj.sclust = 0;
- } else { /* When truncate a part of the file, remove remaining clusters */
- ncl = get_fat(&fp->obj, fp->clust);
- res = FR_OK;
- if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
- if (ncl == 1) res = FR_INT_ERR;
- if (res == FR_OK && ncl < fs->n_fatent) {
- res = remove_chain(&fp->obj, ncl, fp->clust);
- }
- }
- fp->obj.objsize = fp->fptr; /* Set file size to current read/write point */
- fp->flag |= FA_MODIFIED;
-#if !FF_FS_TINY
- if (res == FR_OK && (fp->flag & FA_DIRTY)) {
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
- res = FR_DISK_ERR;
- } else {
- fp->flag &= (BYTE)~FA_DIRTY;
- }
- }
-#endif
- if (res != FR_OK) ABORT(fs, res);
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Delete a File/Directory */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_unlink (
- const TCHAR* path /* Pointer to the file or directory path */
-)
-{
- FRESULT res;
- DIR dj, sdj;
- DWORD dclst = 0;
- FATFS *fs;
-#if FF_FS_EXFAT
- FFOBJID obj;
-#endif
- DEF_NAMBUF
-
-
- /* Get logical drive */
- res = find_volume(&path, &fs, FA_WRITE);
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&dj, path); /* Follow the file path */
- if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {
- res = FR_INVALID_NAME; /* Cannot remove dot entry */
- }
-#if FF_FS_LOCK != 0
- if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */
-#endif
- if (res == FR_OK) { /* The object is accessible */
- if (dj.fn[NSFLAG] & NS_NONAME) {
- res = FR_INVALID_NAME; /* Cannot remove the origin directory */
- } else {
- if (dj.obj.attr & AM_RDO) {
- res = FR_DENIED; /* Cannot remove R/O object */
- }
- }
- if (res == FR_OK) {
-#if FF_FS_EXFAT
- obj.fs = fs;
- if (fs->fs_type == FS_EXFAT) {
- init_alloc_info(fs, &obj);
- dclst = obj.sclust;
- } else
-#endif
- {
- dclst = ld_clust(fs, dj.dir);
- }
- if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */
-#if FF_FS_RPATH != 0
- if (dclst == fs->cdir) { /* Is it the current directory? */
- res = FR_DENIED;
- } else
-#endif
- {
- sdj.obj.fs = fs; /* Open the sub-directory */
- sdj.obj.sclust = dclst;
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- sdj.obj.objsize = obj.objsize;
- sdj.obj.stat = obj.stat;
- }
-#endif
- res = dir_sdi(&sdj, 0);
- if (res == FR_OK) {
- res = DIR_READ_FILE(&sdj); /* Test if the directory is empty */
- if (res == FR_OK) res = FR_DENIED; /* Not empty? */
- if (res == FR_NO_FILE) res = FR_OK; /* Empty? */
- }
- }
- }
- }
- if (res == FR_OK) {
- res = dir_remove(&dj); /* Remove the directory entry */
- if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */
-#if FF_FS_EXFAT
- res = remove_chain(&obj, dclst, 0);
-#else
- res = remove_chain(&dj.obj, dclst, 0);
-#endif
- }
- if (res == FR_OK) res = sync_fs(fs);
- }
- }
- FREE_NAMBUF();
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Create a Directory */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_mkdir (
- const TCHAR* path /* Pointer to the directory path */
-)
-{
- FRESULT res;
- DIR dj;
- FFOBJID sobj;
- FATFS *fs;
- DWORD dcl, pcl, tm;
- DEF_NAMBUF
-
-
- res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&dj, path); /* Follow the file path */
- if (res == FR_OK) res = FR_EXIST; /* Name collision? */
- if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { /* Invalid name? */
- res = FR_INVALID_NAME;
- }
- if (res == FR_NO_FILE) { /* It is clear to create a new directory */
- sobj.fs = fs; /* New object id to create a new chain */
- dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */
- res = FR_OK;
- if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster? */
- if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */
- if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk error? */
- tm = GET_FATTIME();
- if (res == FR_OK) {
- res = dir_clear(fs, dcl); /* Clean up the new table */
- if (res == FR_OK) {
- if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */
- mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */
- fs->win[DIR_Name] = '.';
- fs->win[DIR_Attr] = AM_DIR;
- st_dword(fs->win + DIR_ModTime, tm);
- st_clust(fs, fs->win, dcl);
- mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */
- fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;
- st_clust(fs, fs->win + SZDIRE, pcl);
- fs->wflag = 1;
- }
- res = dir_register(&dj); /* Register the object to the parent directoy */
- }
- }
- if (res == FR_OK) {
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */
- st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */
- st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */
- st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)fs->csize * SS(fs)); /* File size needs to be valid */
- st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs));
- fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */
- fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */
- res = store_xdir(&dj);
- } else
-#endif
- {
- st_dword(dj.dir + DIR_ModTime, tm); /* Created time */
- st_clust(fs, dj.dir, dcl); /* Table start cluster */
- dj.dir[DIR_Attr] = AM_DIR; /* Attribute */
- fs->wflag = 1;
- }
- if (res == FR_OK) {
- res = sync_fs(fs);
- }
- } else {
- remove_chain(&sobj, dcl, 0); /* Could not register, remove the allocated cluster */
- }
- }
- FREE_NAMBUF();
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Rename a File/Directory */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_rename (
- const TCHAR* path_old, /* Pointer to the object name to be renamed */
- const TCHAR* path_new /* Pointer to the new name */
-)
-{
- FRESULT res;
- DIR djo, djn;
- FATFS *fs;
- BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
- DWORD dw;
- DEF_NAMBUF
-
-
- get_ldnumber(&path_new); /* Snip the drive number of new name off */
- res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */
- if (res == FR_OK) {
- djo.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&djo, path_old); /* Check old object */
- if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */
-#if FF_FS_LOCK != 0
- if (res == FR_OK) {
- res = chk_lock(&djo, 2);
- }
-#endif
- if (res == FR_OK) { /* Object to be renamed is found */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */
- BYTE nf, nn;
- WORD nh;
-
- mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */
- mem_cpy(&djn, &djo, sizeof djo);
- res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
- if (res == FR_OK) { /* Is new name already in use by any other object? */
- res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
- }
- if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
- res = dir_register(&djn); /* Register the new entry */
- if (res == FR_OK) {
- nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName];
- nh = ld_word(fs->dirbuf + XDIR_NameHash);
- mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */
- fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn;
- st_word(fs->dirbuf + XDIR_NameHash, nh);
- if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
-/* Start of critical section where an interruption can cause a cross-link */
- res = store_xdir(&djn);
- }
- }
- } else
-#endif
- { /* At FAT/FAT32 volume */
- mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */
- mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */
- res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
- if (res == FR_OK) { /* Is new name already in use by any other object? */
- res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
- }
- if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
- res = dir_register(&djn); /* Register the new entry */
- if (res == FR_OK) {
- dir = djn.dir; /* Copy directory entry of the object except name */
- mem_cpy(dir + 13, buf + 13, SZDIRE - 13);
- dir[DIR_Attr] = buf[DIR_Attr];
- if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
- fs->wflag = 1;
- if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */
- dw = clst2sect(fs, ld_clust(fs, dir));
- if (dw == 0) {
- res = FR_INT_ERR;
- } else {
-/* Start of critical section where an interruption can cause a cross-link */
- res = move_window(fs, dw);
- dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */
- if (res == FR_OK && dir[1] == '.') {
- st_clust(fs, dir, djn.obj.sclust);
- fs->wflag = 1;
- }
- }
- }
- }
- }
- }
- if (res == FR_OK) {
- res = dir_remove(&djo); /* Remove old entry */
- if (res == FR_OK) {
- res = sync_fs(fs);
- }
- }
-/* End of the critical section */
- }
- FREE_NAMBUF();
- }
-
- LEAVE_FF(fs, res);
-}
-
-#endif /* !FF_FS_READONLY */
-#endif /* FF_FS_MINIMIZE == 0 */
-#endif /* FF_FS_MINIMIZE <= 1 */
-#endif /* FF_FS_MINIMIZE <= 2 */
-
-
-
-#if FF_USE_CHMOD && !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Change Attribute */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_chmod (
- const TCHAR* path, /* Pointer to the file path */
- BYTE attr, /* Attribute bits */
- BYTE mask /* Attribute mask to change */
-)
-{
- FRESULT res;
- DIR dj;
- FATFS *fs;
- DEF_NAMBUF
-
-
- res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&dj, path); /* Follow the file path */
- if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */
- if (res == FR_OK) {
- mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */
- res = store_xdir(&dj);
- } else
-#endif
- {
- dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
- fs->wflag = 1;
- }
- if (res == FR_OK) {
- res = sync_fs(fs);
- }
- }
- FREE_NAMBUF();
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Change Timestamp */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_utime (
- const TCHAR* path, /* Pointer to the file/directory name */
- const FILINFO* fno /* Pointer to the timestamp to be set */
-)
-{
- FRESULT res;
- DIR dj;
- FATFS *fs;
- DEF_NAMBUF
-
-
- res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
- if (res == FR_OK) {
- dj.obj.fs = fs;
- INIT_NAMBUF(fs);
- res = follow_path(&dj, path); /* Follow the file path */
- if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */
- if (res == FR_OK) {
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
- res = store_xdir(&dj);
- } else
-#endif
- {
- st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
- fs->wflag = 1;
- }
- if (res == FR_OK) {
- res = sync_fs(fs);
- }
- }
- FREE_NAMBUF();
- }
-
- LEAVE_FF(fs, res);
-}
-
-#endif /* FF_USE_CHMOD && !FF_FS_READONLY */
-
-
-
-#if FF_USE_LABEL
-/*-----------------------------------------------------------------------*/
-/* Get Volume Label */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_getlabel (
- const TCHAR* path, /* Logical drive number */
- TCHAR* label, /* Buffer to store the volume label */
- DWORD* vsn /* Variable to store the volume serial number */
-)
-{
- FRESULT res;
- DIR dj;
- FATFS *fs;
- UINT si, di;
- WCHAR wc;
-
- /* Get logical drive */
- res = find_volume(&path, &fs, 0);
-
- /* Get volume label */
- if (res == FR_OK && label) {
- dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
- res = dir_sdi(&dj, 0);
- if (res == FR_OK) {
- res = DIR_READ_LABEL(&dj); /* Find a volume label entry */
- if (res == FR_OK) {
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- WCHAR hs;
-
- for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */
- wc = ld_word(dj.dir + XDIR_Label + si * 2);
- if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */
- hs = wc; continue;
- }
- wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4);
- if (wc == 0) { di = 0; break; }
- di += wc;
- hs = 0;
- }
- if (hs != 0) di = 0; /* Broken surrogate pair? */
- label[di] = 0;
- } else
-#endif
- {
- si = di = 0; /* Extract volume label from AM_VOL entry */
- while (si < 11) {
- wc = dj.dir[si++];
-#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
- if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */
- wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */
- if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */
- if (wc == 0) { di = 0; break; }
- di += wc;
-#else /* ANSI/OEM output */
- label[di++] = (TCHAR)wc;
-#endif
- }
- do { /* Truncate trailing spaces */
- label[di] = 0;
- if (di == 0) break;
- } while (label[--di] == ' ');
- }
- }
- }
- if (res == FR_NO_FILE) { /* No label entry and return nul string */
- label[0] = 0;
- res = FR_OK;
- }
- }
-
- /* Get volume serial number */
- if (res == FR_OK && vsn) {
- res = move_window(fs, fs->volbase);
- if (res == FR_OK) {
- switch (fs->fs_type) {
- case FS_EXFAT:
- di = BPB_VolIDEx; break;
-
- case FS_FAT32:
- di = BS_VolID32; break;
-
- default:
- di = BS_VolID;
- }
- *vsn = ld_dword(fs->win + di);
- }
- }
-
- LEAVE_FF(fs, res);
-}
-
-
-
-#if !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Set Volume Label */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_setlabel (
- const TCHAR* label /* Volume label to set with heading logical drive number */
-)
-{
- FRESULT res;
- DIR dj;
- FATFS *fs;
- BYTE dirvn[22];
- UINT di;
- WCHAR wc;
- static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */
-#if FF_USE_LFN
- DWORD dc;
-#endif
-
- /* Get logical drive */
- res = find_volume(&label, &fs, FA_WRITE);
- if (res != FR_OK) LEAVE_FF(fs, res);
-
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
- mem_set(dirvn, 0, 22);
- di = 0;
- while ((UINT)*label >= ' ') { /* Create volume label */
- dc = tchar2uni(&label); /* Get a Unicode character */
- if (dc >= 0x10000) {
- if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */
- dc = 0;
- } else {
- st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++;
- }
- }
- if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */
- LEAVE_FF(fs, FR_INVALID_NAME);
- }
- st_word(dirvn + di * 2, (WCHAR)dc); di++;
- }
- } else
-#endif
- { /* On the FAT/FAT32 volume */
- mem_set(dirvn, ' ', 11);
- di = 0;
- while ((UINT)*label >= ' ') { /* Create volume label */
-#if FF_USE_LFN
- dc = tchar2uni(&label);
- wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0;
-#else /* ANSI/OEM input */
- wc = (BYTE)*label++;
- if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0;
- if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */
-#if FF_CODE_PAGE == 0
- if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
-#elif FF_CODE_PAGE < 900
- if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
-#endif
-#endif
- if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */
- LEAVE_FF(fs, FR_INVALID_NAME);
- }
- if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8);
- dirvn[di++] = (BYTE)wc;
- }
- if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */
- while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */
- }
-
- /* Set volume label */
- dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
- res = dir_sdi(&dj, 0);
- if (res == FR_OK) {
- res = DIR_READ_LABEL(&dj); /* Get volume label entry */
- if (res == FR_OK) {
- if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
- dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */
- mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
- } else {
- if (di != 0) {
- mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */
- } else {
- dj.dir[DIR_Name] = DDEM; /* Remove the volume label */
- }
- }
- fs->wflag = 1;
- res = sync_fs(fs);
- } else { /* No volume label entry or an error */
- if (res == FR_NO_FILE) {
- res = FR_OK;
- if (di != 0) { /* Create a volume label entry */
- res = dir_alloc(&dj, 1); /* Allocate an entry */
- if (res == FR_OK) {
- mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */
- if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
- dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */
- dj.dir[XDIR_NumLabel] = (BYTE)di;
- mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
- } else {
- dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */
- mem_cpy(dj.dir, dirvn, 11);
- }
- fs->wflag = 1;
- res = sync_fs(fs);
- }
- }
- }
- }
- }
-
- LEAVE_FF(fs, res);
-}
-
-#endif /* !FF_FS_READONLY */
-#endif /* FF_USE_LABEL */
-
-
-
-#if FF_USE_EXPAND && !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Allocate a Contiguous Blocks to the File */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_expand (
- FIL* fp, /* Pointer to the file object */
- FSIZE_t fsz, /* File size to be expanded to */
- BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD n, clst, stcl, scl, ncl, tcl, lclst;
-
-
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
- if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);
-#if FF_FS_EXFAT
- if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */
-#endif
- n = (DWORD)fs->csize * SS(fs); /* Cluster size */
- tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */
- stcl = fs->last_clst; lclst = 0;
- if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;
-
-#if FF_FS_EXFAT
- if (fs->fs_type == FS_EXFAT) {
- scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */
- if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */
- if (scl == 0xFFFFFFFF) res = FR_DISK_ERR;
- if (res == FR_OK) { /* A contiguous free area is found */
- if (opt) { /* Allocate it now */
- res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */
- lclst = scl + tcl - 1;
- } else { /* Set it as suggested point for next allocation */
- lclst = scl - 1;
- }
- }
- } else
-#endif
- {
- scl = clst = stcl; ncl = 0;
- for (;;) { /* Find a contiguous cluster block */
- n = get_fat(&fp->obj, clst);
- if (++clst >= fs->n_fatent) clst = 2;
- if (n == 1) { res = FR_INT_ERR; break; }
- if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
- if (n == 0) { /* Is it a free cluster? */
- if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */
- } else {
- scl = clst; ncl = 0; /* Not a free cluster */
- }
- if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */
- }
- if (res == FR_OK) { /* A contiguous free area is found */
- if (opt) { /* Allocate it now */
- for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */
- res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1);
- if (res != FR_OK) break;
- lclst = clst;
- }
- } else { /* Set it as suggested point for next allocation */
- lclst = scl - 1;
- }
- }
- }
-
- if (res == FR_OK) {
- fs->last_clst = lclst; /* Set suggested start cluster to start next */
- if (opt) { /* Is it allocated now? */
- fp->obj.sclust = scl; /* Update object allocation information */
- fp->obj.objsize = fsz;
- if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */
- fp->flag |= FA_MODIFIED;
- if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */
- fs->free_clst -= tcl;
- fs->fsi_flag |= 1;
- }
- }
- }
-
- LEAVE_FF(fs, res);
-}
-
-#endif /* FF_USE_EXPAND && !FF_FS_READONLY */
-
-
-
-#if FF_USE_FORWARD
-/*-----------------------------------------------------------------------*/
-/* Forward Data to the Stream Directly */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_forward (
- FIL* fp, /* Pointer to the file object */
- UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
- UINT btf, /* Number of bytes to forward */
- UINT* bf /* Pointer to number of bytes forwarded */
-)
-{
- FRESULT res;
- FATFS *fs;
- DWORD clst, sect;
- FSIZE_t remain;
- UINT rcnt, csect;
- BYTE *dbuf;
-
-
- *bf = 0; /* Clear transfer byte counter */
- res = validate(&fp->obj, &fs); /* Check validity of the file object */
- if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
- if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
-
- remain = fp->obj.objsize - fp->fptr;
- if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */
-
- for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */
- fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
- csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
- if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
- if (csect == 0) { /* On the cluster boundary? */
- clst = (fp->fptr == 0) ? /* On the top of the file? */
- fp->obj.sclust : get_fat(&fp->obj, fp->clust);
- if (clst <= 1) ABORT(fs, FR_INT_ERR);
- if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
- fp->clust = clst; /* Update current cluster */
- }
- }
- sect = clst2sect(fs, fp->clust); /* Get current data sector */
- if (sect == 0) ABORT(fs, FR_INT_ERR);
- sect += csect;
-#if FF_FS_TINY
- if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */
- dbuf = fs->win;
-#else
- if (fp->sect != sect) { /* Fill sector cache with file data */
-#if !FF_FS_READONLY
- if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
- if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
- fp->flag &= (BYTE)~FA_DIRTY;
- }
-#endif
- if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
- }
- dbuf = fp->buf;
-#endif
- fp->sect = sect;
- rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
- if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */
- rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */
- if (rcnt == 0) ABORT(fs, FR_INT_ERR);
- }
-
- LEAVE_FF(fs, FR_OK);
-}
-#endif /* FF_USE_FORWARD */
-
-
-
-#if FF_USE_MKFS && !FF_FS_READONLY
-/*-----------------------------------------------------------------------*/
-/* Create an FAT/exFAT volume */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_mkfs (
- const TCHAR* path, /* Logical drive number */
- BYTE opt, /* Format option */
- DWORD au, /* Size of allocation unit (cluster) [byte] */
- void* work, /* Pointer to working buffer (null: use heap memory) */
- UINT len /* Size of working buffer [byte] */
-)
-{
- const UINT n_fats = 2; /* Number of FATs for FAT/FAT32 volume (1 or 2) */
- const UINT n_rootdir = 512; /* Number of root directory entries for FAT volume */
- static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */
- static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */
- BYTE fmt, sys, *buf, *pte, pdrv, part;
- WORD ss; /* Sector size */
- DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;
- DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */
- DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */
- UINT i;
- int vol;
- DSTATUS stat;
-#if FF_USE_TRIM || FF_FS_EXFAT
- DWORD tbl[3];
-#endif
-
-
- /* Check mounted drive and clear work area */
- vol = get_ldnumber(&path); /* Get target logical drive */
- if (vol < 0) return FR_INVALID_DRIVE;
- if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume if mounted */
- pdrv = LD2PD(vol); /* Physical drive */
- part = 1; /* Partition (0:create as new, 1-4:get from partition table) */
-
- /* Check physical drive status */
- stat = disk_initialize(pdrv);
- if (stat & STA_NOINIT) return FR_NOT_READY;
- if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
- if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */
-#if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */
- if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
- if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
-#else
- ss = FF_MAX_SS;
-#endif
- if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */
- au /= ss; /* Cluster size in unit of sector */
-
- /* Get working buffer */
-#if FF_USE_LFN == 3
- if (!work) { /* Use heap memory for working buffer */
- for (szb_buf = MAX_MALLOC, buf = 0; szb_buf >= ss && (buf = ff_memalloc(szb_buf)) == 0; szb_buf /= 2) ;
- sz_buf = szb_buf / ss; /* Size of working buffer (sector) */
- } else
-#endif
- {
- buf = (BYTE*)work; /* Working buffer */
- sz_buf = len / ss; /* Size of working buffer (sector) */
- szb_buf = sz_buf * ss; /* Size of working buffer (byte) */
- }
- if (!buf || sz_buf == 0) return FR_NOT_ENOUGH_CORE;
-
- /* Determine where the volume to be located (b_vol, sz_vol) */
- if (part > 0) {
- /* Get partition information from partition table in the MBR */
- if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */
- if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */
- pte = buf + (MBR_Table + (part - 1) * SZ_PTE);
- if (pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */
- b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */
- sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */
- } else {
- /* Create a single-partition in this function */
- if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- b_vol = (opt & FM_SFD) ? 0 : 32768; /* Volume start sector. Align to 16MB */
- if (sz_vol < b_vol) LEAVE_MKFS(FR_MKFS_ABORTED);
- sz_vol -= b_vol; /* Volume size */
- }
- if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */
-
- /* Pre-determine the FAT type */
- do {
- if (FF_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */
- if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */
- fmt = FS_EXFAT; break;
- }
- }
- if (au > 128) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Too large au for FAT/FAT32 */
- if (opt & FM_FAT32) { /* FAT32 possible? */
- if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */
- fmt = FS_FAT32; break;
- }
- }
- if (!(opt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */
- fmt = FS_FAT16;
- } while (0);
-
-#if FF_FS_EXFAT
- if (fmt == FS_EXFAT) { /* Create an exFAT volume */
- LEAVE_MKFS(FR_NO_FILESYSTEM); // Muh frii bytes
- DWORD szb_bit, szb_case, sum, nb, cl;
- WCHAR ch, si;
- UINT j, st;
- BYTE b;
-
- if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
-#if FF_USE_TRIM
- tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */
- disk_ioctl(pdrv, CTRL_TRIM, tbl);
-#endif
- /* Determine FAT location, data location and number of clusters */
- if (au == 0) { /* au auto-selection */
- au = 8;
- if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */
- if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */
- }
- b_fat = b_vol + 32; /* FAT start at offset 32 */
- sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */
- b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */
- if (b_data - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
- n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */
- if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
- if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */
-
- szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
- tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */
-
- /* Create a compressed up-case table */
- sect = b_data + au * tbl[0]; /* Table start sector */
- sum = 0; /* Table checksum to be stored in the 82 entry */
- st = 0; si = 0; i = 0; j = 0; szb_case = 0;
- do {
- switch (st) {
- case 0:
- ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */
- if (ch != si) {
- si++; break; /* Store the up-case char if exist */
- }
- for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */
- if (j >= 128) {
- ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */
- }
- st = 1; /* Do not compress short run */
- /* go to next case */
- case 1:
- ch = si++; /* Fill the short run */
- if (--j == 0) st = 0;
- break;
-
- default:
- ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars to skip */
- st = 0;
- }
- sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */
- sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);
- i += 2; szb_case += 2;
- if (si == 0 || i == szb_buf) { /* Write buffered data when buffer full or end of process */
- n = (i + ss - 1) / ss;
- if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- sect += n; i = 0;
- }
- } while (si);
- tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */
- tbl[2] = 1; /* Number of root dir clusters */
-
- /* Initialize the allocation bitmap */
- sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
- nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
- do {
- mem_set(buf, 0, szb_buf);
- for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ;
- for (b = 1; nb != 0 && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ;
- n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
- if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- sect += n; nsect -= n;
- } while (nsect);
-
- /* Initialize the FAT */
- sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
- j = nb = cl = 0;
- do {
- mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */
- if (cl == 0) { /* Set entry 0 and 1 */
- st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
- st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
- }
- do { /* Create chains of bitmap, up-case and root dir */
- while (nb != 0 && i < szb_buf) { /* Create a chain */
- st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
- i += 4; cl++; nb--;
- }
- if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */
- } while (nb != 0 && i < szb_buf);
- n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
- if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- sect += n; nsect -= n;
- } while (nsect);
-
- /* Initialize the root directory */
- mem_set(buf, 0, szb_buf);
- buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry */
- buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */
- st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */
- st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */
- buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
- st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */
- st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */
- st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
- sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */
- do { /* Fill root directory sectors */
- n = (nsect > sz_buf) ? sz_buf : nsect;
- if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- mem_set(buf, 0, ss);
- sect += n; nsect -= n;
- } while (nsect);
-
- /* Create two set of the exFAT VBR blocks */
- sect = b_vol;
- for (n = 0; n < 2; n++) {
- /* Main record (+0) */
- mem_set(buf, 0, ss);
- mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */
- st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */
- st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */
- st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */
- st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
- st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */
- st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
- st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */
- st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */
- st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
- for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
- for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */
- buf[BPB_NumFATsEx] = 1; /* Number of FATs */
- buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */
- st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */
- st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */
- for (i = sum = 0; i < ss; i++) { /* VBR checksum */
- if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);
- }
- if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- /* Extended bootstrap record (+1..+8) */
- mem_set(buf, 0, ss);
- st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */
- for (j = 1; j < 9; j++) {
- for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */
- if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- }
- /* OEM/Reserved record (+9..+10) */
- mem_set(buf, 0, ss);
- for ( ; j < 11; j++) {
- for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */
- if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- }
- /* Sum record (+11) */
- for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */
- if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- }
-
- } else
-#endif /* FF_FS_EXFAT */
- { /* Create an FAT/FAT32 volume */
- do {
- pau = au;
- /* Pre-determine number of clusters and FAT sub-type */
- if (fmt == FS_FAT32) { /* FAT32 volume */
- if (pau == 0) { /* au auto-selection */
- n = sz_vol / 0x20000; /* Volume size in unit of 128KS */
- for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */
- }
- n_clst = sz_vol / pau; /* Number of clusters */
- sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */
- sz_rsv = 32; /* Number of reserved sectors */
- sz_dir = 0; /* No static directory */
- if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED);
- } else { /* FAT volume */
- if (pau == 0) { /* au auto-selection */
- n = sz_vol / 0x1000; /* Volume size in unit of 4KS */
- for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */
- }
- n_clst = sz_vol / pau;
- if (n_clst > MAX_FAT12) {
- n = n_clst * 2 + 4; /* FAT size [byte] */
- } else {
- fmt = FS_FAT12;
- n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */
- }
- sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */
- sz_rsv = 1; /* Number of reserved sectors */
- sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */
- }
- b_fat = b_vol + sz_rsv; /* FAT base */
- b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */
-
- /* Align data base to erase block boundary (for flash memory media) */
- n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */
- if (fmt == FS_FAT32) { /* FAT32: Move FAT base */
- sz_rsv += n; b_fat += n;
- } else { /* FAT: Expand FAT size */
- if (n % n_fats) { /* Adjust fractional error if needed */
- n--; sz_rsv++; b_fat++;
- }
- sz_fat += n / n_fats;
- }
-
- /* Determine number of clusters and final check of validity of the FAT sub-type */
- if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume */
- n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;
- if (fmt == FS_FAT32) {
- if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */
- if (au == 0 && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */
- LEAVE_MKFS(FR_MKFS_ABORTED);
- }
- }
- if (fmt == FS_FAT16) {
- if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */
- if (au == 0 && (pau * 2) <= 64) {
- au = pau * 2; continue; /* Adjust cluster size and retry */
- }
- if ((opt & FM_FAT32)) {
- fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */
- }
- if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
- LEAVE_MKFS(FR_MKFS_ABORTED);
- }
- if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */
- if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
- LEAVE_MKFS(FR_MKFS_ABORTED);
- }
- }
- if (fmt == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */
-
- /* Ok, it is the valid cluster configuration */
- break;
- } while (1);
-
-#if FF_USE_TRIM
- tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */
- disk_ioctl(pdrv, CTRL_TRIM, tbl);
-#endif
- /* Create FAT VBR */
- mem_set(buf, 0, ss);
- mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */
- st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */
- buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
- st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
- buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */
- st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */
- if (sz_vol < 0x10000) {
- st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
- } else {
- st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */
- }
- buf[BPB_Media] = 0xF8; /* Media descriptor byte */
- st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
- st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */
- st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */
- if (fmt == FS_FAT32) {
- st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */
- st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */
- st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */
- st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */
- st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */
- buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */
- buf[BS_BootSig32] = 0x29; /* Extended boot signature */
- mem_cpy(buf + BS_VolLab32, "SWITCH SD " "FAT32 ", 19); /* Volume label, FAT signature */
- } else {
- st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */
- st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */
- buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */
- buf[BS_BootSig] = 0x29; /* Extended boot signature */
- mem_cpy(buf + BS_VolLab, "SWITCH SD " "FAT ", 19); /* Volume label, FAT signature */
- }
- st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */
- if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */
-
- /* Create FSINFO record if needed */
- if (fmt == FS_FAT32) {
- disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */
- mem_set(buf, 0, ss);
- st_dword(buf + FSI_LeadSig, 0x41615252);
- st_dword(buf + FSI_StrucSig, 0x61417272);
- st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */
- st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */
- st_word(buf + BS_55AA, 0xAA55);
- disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */
- disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */
- }
-
- /* Initialize FAT area */
- mem_set(buf, 0, (UINT)szb_buf);
- sect = b_fat; /* FAT start sector */
- for (i = 0; i < n_fats; i++) { /* Initialize FATs each */
- if (fmt == FS_FAT32) {
- st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */
- st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */
- st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */
- } else {
- st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */
- }
- nsect = sz_fat; /* Number of FAT sectors */
- do { /* Fill FAT sectors */
- n = (nsect > sz_buf) ? sz_buf : nsect;
- if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- mem_set(buf, 0, ss);
- sect += n; nsect -= n;
- } while (nsect);
- }
-
- /* Initialize root directory (fill with zero) */
- nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */
- do {
- n = (nsect > sz_buf) ? sz_buf : nsect;
- if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
- sect += n; nsect -= n;
- } while (nsect);
- }
-
- /* Determine system ID in the partition table */
- if (FF_FS_EXFAT && fmt == FS_EXFAT) {
- sys = 0x07; /* HPFS/NTFS/exFAT */
- } else {
- if (fmt == FS_FAT32) {
- sys = 0x0C; /* FAT32X */
- } else {
- if (sz_vol >= 0x10000) {
- sys = 0x06; /* FAT12/16 (large) */
- } else {
- sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */
- }
- }
- }
-
- /* Update partition information */
- if (part != 0) { /* Created in the existing partition */
- /* Update system ID in the partition table */
- if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */
- buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */
- if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */
- } else { /* Created as a new single partition */
- if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */
- mem_set(buf, 0, ss);
- st_word(buf + BS_55AA, 0xAA55); /* MBR signature */
- pte = buf + MBR_Table; /* Create partition table for single partition in the drive */
- pte[PTE_Boot] = 0; /* Boot indicator */
- pte[PTE_StHead] = 1; /* Start head */
- pte[PTE_StSec] = 1; /* Start sector */
- pte[PTE_StCyl] = 0; /* Start cylinder */
- pte[PTE_System] = sys; /* System type */
- n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */
- pte[PTE_EdHead] = 254; /* End head */
- pte[PTE_EdSec] = (BYTE)(((n >> 2) & 0xC0) | 63); /* End sector */
- pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */
- st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */
- st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */
- if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the MBR */
- }
- }
-
- if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
-
- LEAVE_MKFS(FR_OK);
-}
-
-
-
-#if FF_MULTI_PARTITION
-/*-----------------------------------------------------------------------*/
-/* Create Partition Table on the Physical Drive */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_fdisk (
- BYTE pdrv, /* Physical drive number */
- const DWORD* szt, /* Pointer to the size table for each partitions */
- void* work /* Pointer to the working buffer (null: use heap memory) */
-)
-{
- UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
- BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
- DSTATUS stat;
- DWORD sz_disk, sz_part, s_part;
- FRESULT res;
-
-
- stat = disk_initialize(pdrv);
- if (stat & STA_NOINIT) return FR_NOT_READY;
- if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
- if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
-
- buf = (BYTE*)work;
-#if FF_USE_LFN == 3
- if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */
-#endif
- if (!buf) return FR_NOT_ENOUGH_CORE;
-
- /* Determine the CHS without any consideration of the drive geometry */
- for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
- if (n == 256) n--;
- e_hd = (BYTE)(n - 1);
- sz_cyl = 63 * n;
- tot_cyl = sz_disk / sz_cyl;
-
- /* Create partition table */
- mem_set(buf, 0, FF_MAX_SS);
- p = buf + MBR_Table; b_cyl = 0;
- for (i = 0; i < 4; i++, p += SZ_PTE) {
- p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; /* Number of cylinders */
- if (p_cyl == 0) continue;
- s_part = (DWORD)sz_cyl * b_cyl;
- sz_part = (DWORD)sz_cyl * p_cyl;
- if (i == 0) { /* Exclude first track of cylinder 0 */
- s_hd = 1;
- s_part += 63; sz_part -= 63;
- } else {
- s_hd = 0;
- }
- e_cyl = b_cyl + p_cyl - 1; /* End cylinder */
- if (e_cyl >= tot_cyl) LEAVE_MKFS(FR_INVALID_PARAMETER);
-
- /* Set partition table */
- p[1] = s_hd; /* Start head */
- p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 1); /* Start sector */
- p[3] = (BYTE)b_cyl; /* Start cylinder */
- p[4] = 0x07; /* System type (temporary setting) */
- p[5] = e_hd; /* End head */
- p[6] = (BYTE)(((e_cyl >> 2) & 0xC0) | 63); /* End sector */
- p[7] = (BYTE)e_cyl; /* End cylinder */
- st_dword(p + 8, s_part); /* Start sector in LBA */
- st_dword(p + 12, sz_part); /* Number of sectors */
-
- /* Next partition */
- b_cyl += p_cyl;
- }
- st_word(p, 0xAA55); /* MBR signature (always at offset 510) */
-
- /* Write it to the MBR */
- res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
- LEAVE_MKFS(res);
-}
-
-#endif /* FF_MULTI_PARTITION */
-#endif /* FF_USE_MKFS && !FF_FS_READONLY */
-
-extern sdmmc_storage_t sd_storage;
-
-FRESULT f_fdisk_mod (
- BYTE pdrv, /* Physical drive number */
- const DWORD* szt, /* Pointer to the size table for each partitions */
- void* work
-)
-{
- UINT i, n, sz_cyl, tot_cyl, e_cyl;
- BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
- DSTATUS stat;
- DWORD sz_disk, p_sect, b_cyl, b_sect;
- FRESULT res;
-
- stat = disk_initialize(pdrv);
- if (stat & STA_NOINIT) return FR_NOT_READY;
- if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
- sz_disk = sd_storage.csd.capacity;
-
- if (!buf) return FR_NOT_ENOUGH_CORE;
-
- /* Determine the CHS without any consideration of the drive geometry */
- for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
- if (n == 256) n--;
- e_hd = (BYTE)(n - 1);
- sz_cyl = 63 * n;
- tot_cyl = sz_disk / sz_cyl;
-
- /* Create partition table */
- mem_set(buf, 0, 0x10000);
- p = buf + MBR_Table; b_cyl = 0, b_sect = 0;
- for (i = 0; i < 4; i++, p += SZ_PTE) {
- p_sect = szt[i]; /* Number of sectors */
-
- if (p_sect == 0)
- continue;
-
- if (i == 0) { /* Exclude first 16MiB of sd */
- s_hd = 1;
- b_sect += 32768; p_sect -= 32768;
- }
- else
- s_hd = 0;
-
- b_cyl = b_sect / sz_cyl;
- e_cyl = ((b_sect + p_sect) / sz_cyl) - 1; /* End cylinder */
-
- if (e_cyl >= tot_cyl)
- LEAVE_MKFS(FR_INVALID_PARAMETER);
-
-
- /* Set partition table */
- p[1] = s_hd; /* Start head */
- p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 1); /* Start sector */
- p[3] = (BYTE)b_cyl; /* Start cylinder */
- p[4] = 0x07; /* System type (temporary setting) */
- p[5] = e_hd; /* End head */
- p[6] = (BYTE)(((e_cyl >> 2) & 0xC0) | 63); /* End sector */
- p[7] = (BYTE)e_cyl; /* End cylinder */
- st_dword(p + 8, b_sect); /* Start sector in LBA */
- st_dword(p + 12, p_sect); /* Number of sectors */
- /* Next partition */
-
- for (u32 cursect = 0; cursect < 512; cursect++){
- disk_write(pdrv, buf + 0x4000, b_sect + (64 * cursect), 64);
- }
-
- b_sect += p_sect;
- }
- st_word(p, 0xAA55); /* MBR signature (always at offset 510) */
-
- /* Write it to the MBR */
- res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
- LEAVE_MKFS(res);
-}
-
-
-#if FF_USE_STRFUNC
-#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
-#error Wrong FF_STRF_ENCODE setting
-#endif
-/*-----------------------------------------------------------------------*/
-/* Get a String from the File */
-/*-----------------------------------------------------------------------*/
-
-TCHAR* f_gets (
- TCHAR* buff, /* Pointer to the string buffer to read */
- int len, /* Size of string buffer (items) */
- FIL* fp /* Pointer to the file object */
-)
-{
- int nc = 0;
- TCHAR *p = buff;
- BYTE s[4];
- UINT rc;
- DWORD dc;
-#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
- WCHAR wc;
-#endif
-#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
- UINT ct;
-#endif
-
-#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
- /* Make a room for the character and terminator */
- if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2;
- if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4;
- if (FF_LFN_UNICODE == 3) len -= 1;
- while (nc < len) {
-#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
- f_read(fp, s, 1, &rc);
- if (rc != 1) break;
- wc = s[0];
- if (dbc_1st((BYTE)wc)) {
- f_read(fp, s, 1, &rc);
- if (rc != 1 || !dbc_2nd(s[0])) continue;
- wc = wc << 8 | s[0];
- }
- dc = ff_oem2uni(wc, CODEPAGE);
- if (dc == 0) continue;
-#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
- f_read(fp, s, 2, &rc);
- if (rc != 2) break;
- dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
- if (IsSurrogateL(dc)) continue;
- if (IsSurrogateH(dc)) {
- f_read(fp, s, 2, &rc);
- if (rc != 2) break;
- wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
- if (!IsSurrogateL(wc)) continue;
- dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF);
- }
-#else /* Read a character in UTF-8 */
- f_read(fp, s, 1, &rc);
- if (rc != 1) break;
- dc = s[0];
- if (dc >= 0x80) { /* Multi-byte character? */
- ct = 0;
- if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* 2-byte? */
- if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte? */
- if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte? */
- if (ct == 0) continue;
- f_read(fp, s, ct, &rc); /* Get trailing bytes */
- if (rc != ct) break;
- rc = 0;
- do { /* Merge trailing bytes */
- if ((s[rc] & 0xC0) != 0x80) break;
- dc = dc << 6 | (s[rc] & 0x3F);
- } while (++rc < ct);
- if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue; /* Wrong encoding? */
- }
-#endif
- if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */
-#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
- if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */
- *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */
- dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */
- }
- *p++ = (TCHAR)dc; nc++;
- if (dc == '\n') break; /* End of line? */
-#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
- if (dc < 0x80) { /* 1-byte */
- *p++ = (TCHAR)dc;
- nc++;
- if (dc == '\n') break; /* End of line? */
- } else {
- if (dc < 0x800) { /* 2-byte */
- *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
- *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
- nc += 2;
- } else {
- if (dc < 0x10000) { /* 3-byte */
- *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
- *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
- *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
- nc += 3;
- } else { /* 4-byte */
- *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07));
- *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F));
- *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
- *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
- nc += 4;
- }
- }
- }
-#endif
- }
-
-#else /* Byte-by-byte without any conversion (ANSI/OEM API) */
- len -= 1; /* Make a room for the terminator */
- while (nc < len) {
- f_read(fp, s, 1, &rc);
- if (rc != 1) break;
- dc = s[0];
- if (FF_USE_STRFUNC == 2 && dc == '\r') continue;
- *p++ = (TCHAR)dc; nc++;
- if (dc == '\n') break;
- }
-#endif
-
- *p = 0; /* Terminate the string */
- return nc ? buff : 0; /* When no data read due to EOF or error, return with error. */
-}
-
-
-
-
-#if !FF_FS_READONLY
-#include
-/*-----------------------------------------------------------------------*/
-/* Put a Character to the File */
-/*-----------------------------------------------------------------------*/
-
-typedef struct { /* Putchar output buffer and work area */
- FIL *fp; /* Ptr to the writing file */
- int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */
-#if FF_USE_LFN && FF_LFN_UNICODE == 1
- WCHAR hs;
-#elif FF_USE_LFN && FF_LFN_UNICODE == 2
- BYTE bs[4];
- UINT wi, ct;
-#endif
- BYTE buf[64]; /* Write buffer */
-} putbuff;
-
-
-static void putc_bfd ( /* Buffered write with code conversion */
- putbuff* pb,
- TCHAR c
-)
-{
- UINT n;
- int i, nc;
-#if FF_USE_LFN && FF_LFN_UNICODE
- WCHAR hs, wc;
-#if FF_LFN_UNICODE == 2
- DWORD dc;
- TCHAR *tp;
-#endif
-#endif
-
- if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */
- putc_bfd(pb, '\r');
- }
-
- i = pb->idx; /* Write index of pb->buf[] */
- if (i < 0) return;
- nc = pb->nchr; /* Write unit counter */
-
-#if FF_USE_LFN && FF_LFN_UNICODE
-#if FF_LFN_UNICODE == 1 /* UTF-16 input */
- if (IsSurrogateH(c)) {
- pb->hs = c; return;
- }
- hs = pb->hs; pb->hs = 0;
- if (hs != 0) {
- if (!IsSurrogateL(c)) hs = 0;
- } else {
- if (IsSurrogateL(c)) return;
- }
- wc = c;
-#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
- for (;;) {
- if (pb->ct == 0) { /* Out of multi-byte sequence? */
- pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */
- if ((BYTE)c < 0x80) break; /* 1-byte? */
- if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte? */
- if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte? */
- if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte? */
- return;
- } else { /* In the multi-byte sequence */
- if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */
- pb->ct = 0; continue;
- }
- pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */
- if (--pb->ct == 0) break; /* End of multi-byte sequence? */
- return;
- }
- }
- tp = (TCHAR*)pb->bs;
- dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
- if (dc == 0xFFFFFFFF) return;
- wc = (WCHAR)dc;
- hs = (WCHAR)(dc >> 16);
-#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
- if (IsSurrogate(c) || c >= 0x110000) return;
- if (c >= 0x10000) {
- hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */
- wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */
- } else {
- hs = 0;
- wc = (WCHAR)c;
- }
-#endif
-
-#if FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */
- if (hs != 0) {
- st_word(&pb->buf[i], hs);
- i += 2;
- nc++;
- }
- st_word(&pb->buf[i], wc);
- i += 2;
-#elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */
- if (hs != 0) {
- pb->buf[i++] = (BYTE)(hs >> 8);
- pb->buf[i++] = (BYTE)hs;
- nc++;
- }
- pb->buf[i++] = (BYTE)(wc >> 8);
- pb->buf[i++] = (BYTE)wc;
-#elif FF_STRF_ENCODE == 3 /* Write it in UTF-8 */
- if (hs != 0) { /* 4-byte */
- nc += 3;
- hs = (hs & 0x3FF) + 0x40;
- pb->buf[i++] = (BYTE)(0xF0 | hs >> 8);
- pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F));
- pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F));
- pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
- } else {
- if (wc < 0x80) { /* 1-byte */
- pb->buf[i++] = (BYTE)wc;
- } else {
- if (wc < 0x800) { /* 2-byte */
- nc += 1;
- pb->buf[i++] = (BYTE)(0xC0 | wc >> 6);
- } else { /* 3-byte */
- nc += 2;
- pb->buf[i++] = (BYTE)(0xE0 | wc >> 12);
- pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F));
- }
- pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
- }
- }
-#else /* Write it in ANSI/OEM */
- if (hs != 0) return;
- wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */
- if (wc == 0) return;
- if (wc >= 0x100) {
- pb->buf[i++] = (BYTE)(wc >> 8); nc++;
- }
- pb->buf[i++] = (BYTE)wc;
-#endif
-
-#else /* ANSI/OEM input (without re-encode) */
- pb->buf[i++] = (BYTE)c;
-#endif
-
- if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */
- f_write(pb->fp, pb->buf, (UINT)i, &n);
- i = (n == (UINT)i) ? 0 : -1;
- }
- pb->idx = i;
- pb->nchr = nc + 1;
-}
-
-
-static int putc_flush ( /* Flush left characters in the buffer */
- putbuff* pb
-)
-{
- UINT nw;
-
- if ( pb->idx >= 0 /* Flush buffered characters to the file */
- && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
- && (UINT)pb->idx == nw) return pb->nchr;
- return EOF;
-}
-
-
-static void putc_init ( /* Initialize write buffer */
- putbuff* pb,
- FIL* fp
-)
-{
- mem_set(pb, 0, sizeof (putbuff));
- pb->fp = fp;
-}
-
-
-
-int f_putc (
- TCHAR c, /* A character to be output */
- FIL* fp /* Pointer to the file object */
-)
-{
- putbuff pb;
-
-
- putc_init(&pb, fp);
- putc_bfd(&pb, c); /* Put the character */
- return putc_flush(&pb);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Put a String to the File */
-/*-----------------------------------------------------------------------*/
-
-int f_puts (
- const TCHAR* str, /* Pointer to the string to be output */
- FIL* fp /* Pointer to the file object */
-)
-{
- putbuff pb;
-
-
- putc_init(&pb, fp);
- while (*str) putc_bfd(&pb, *str++); /* Put the string */
- return putc_flush(&pb);
-}
-
-
-
-
-/*-----------------------------------------------------------------------*/
-/* Put a Formatted String to the File */
-/*-----------------------------------------------------------------------*/
-
-int f_printf (
- FIL* fp, /* Pointer to the file object */
- const TCHAR* fmt, /* Pointer to the format string */
- ... /* Optional arguments... */
-)
-{
- va_list arp;
- putbuff pb;
- BYTE f, r;
- UINT i, j, w;
- DWORD v;
- TCHAR c, d, str[32], *p;
-
-
- putc_init(&pb, fp);
-
- va_start(arp, fmt);
-
- for (;;) {
- c = *fmt++;
- if (c == 0) break; /* End of string */
- if (c != '%') { /* Non escape character */
- putc_bfd(&pb, c);
- continue;
- }
- w = f = 0;
- c = *fmt++;
- if (c == '0') { /* Flag: '0' padding */
- f = 1; c = *fmt++;
- } else {
- if (c == '-') { /* Flag: left justified */
- f = 2; c = *fmt++;
- }
- }
- if (c == '*') { /* Minimum width by argument */
- w = va_arg(arp, int);
- c = *fmt++;
- } else {
- while (IsDigit(c)) { /* Minimum width */
- w = w * 10 + c - '0';
- c = *fmt++;
- }
- }
- if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */
- f |= 4; c = *fmt++;
- }
- if (c == 0) break;
- d = c;
- if (IsLower(d)) d -= 0x20;
- switch (d) { /* Atgument type is... */
- case 'S' : /* String */
- p = va_arg(arp, TCHAR*);
- for (j = 0; p[j]; j++) ;
- if (!(f & 2)) { /* Right padded */
- while (j++ < w) putc_bfd(&pb, ' ') ;
- }
- while (*p) putc_bfd(&pb, *p++) ; /* String body */
- while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */
- continue;
-
- case 'C' : /* Character */
- putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
-
- case 'B' : /* Unsigned binary */
- r = 2; break;
-
- case 'O' : /* Unsigned octal */
- r = 8; break;
-
- case 'D' : /* Signed decimal */
- case 'U' : /* Unsigned decimal */
- r = 10; break;
-
- case 'X' : /* Unsigned hexdecimal */
- r = 16; break;
-
- default: /* Unknown type (pass-through) */
- putc_bfd(&pb, c); continue;
- }
-
- /* Get an argument and put it in numeral */
- v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
- if (d == 'D' && (v & 0x80000000)) {
- v = 0 - v;
- f |= 8;
- }
- i = 0;
- do {
- d = (TCHAR)(v % r); v /= r;
- if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
- str[i++] = d + '0';
- } while (v && i < sizeof str / sizeof *str);
- if (f & 8) str[i++] = '-';
- j = i; d = (f & 1) ? '0' : ' ';
- if (!(f & 2)) {
- while (j++ < w) putc_bfd(&pb, d); /* Right pad */
- }
- do {
- putc_bfd(&pb, str[--i]); /* Number body */
- } while (i);
- while (j++ < w) putc_bfd(&pb, d); /* Left pad */
- }
-
- va_end(arp);
-
- return putc_flush(&pb);
-}
-
-#endif /* !FF_FS_READONLY */
-#endif /* FF_USE_STRFUNC */
-
-
-
-#if FF_CODE_PAGE == 0
-/*-----------------------------------------------------------------------*/
-/* Set Active Codepage for the Path Name */
-/*-----------------------------------------------------------------------*/
-
-FRESULT f_setcp (
- WORD cp /* Value to be set as active code page */
-)
-{
- static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0};
- static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0};
- UINT i;
-
-
- for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */
- if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */
-
- CodePage = cp;
- if (cp >= 900) { /* DBCS */
- ExCvt = 0;
- DbcTbl = tables[i];
- } else { /* SBCS */
- ExCvt = tables[i];
- DbcTbl = 0;
- }
- return FR_OK;
-}
-#endif /* FF_CODE_PAGE == 0 */
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018-2025 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/*----------------------------------------------------------------------------/
+/ FatFs - Generic FAT Filesystem Module R0.13c (p4) /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2018, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/ this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/
+/----------------------------------------------------------------------------*/
+
+
+#include "ff.h" /* Declarations of FatFs API */
+#include "diskio.h" /* Declarations of device I/O functions */
+#include
+#include
+#include
+
+#define EFSPRINTF(text, ...) print_error(); gfx_printf("%k"text"%k\n", 0xFFFFFF00, 0xFFFFFFFF);
+//#define EFSPRINTF(...)
+
+/*--------------------------------------------------------------------------
+
+ Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if FF_DEFINED != 86604 /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* Limits and boundaries */
+#define MAX_DIR 0x200000 /* Max size of FAT directory */
+#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
+#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
+#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
+#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
+#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
+
+
+/* Character code support macros */
+#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z')
+#define IsLower(c) ((c) >= 'a' && (c) <= 'z')
+#define IsDigit(c) ((c) >= '0' && (c) <= '9')
+#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
+#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
+#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
+
+
+/* Additional file access control and file status flags for internal use */
+#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
+#define FA_MODIFIED 0x40 /* File has been modified */
+#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
+
+
+/* Additional file attribute bits for internal use */
+#define AM_VOL 0x08 /* Volume label */
+#define AM_LFN 0x0F /* LFN entry */
+#define AM_MASK 0x3F /* Mask of defined bits */
+
+
+/* Name status flags in fn[11] */
+#define NSFLAG 11 /* Index of the name status byte */
+#define NS_LOSS 0x01 /* Out of 8.3 format */
+#define NS_LFN 0x02 /* Force to create LFN entry */
+#define NS_LAST 0x04 /* Last segment */
+#define NS_BODY 0x08 /* Lower case flag (body) */
+#define NS_EXT 0x10 /* Lower case flag (ext) */
+#define NS_DOT 0x20 /* Dot entry */
+#define NS_NOLFN 0x40 /* Do not find LFN */
+#define NS_NONAME 0x80 /* Not followed */
+
+
+/* exFAT directory entry types */
+#define ET_BITMAP 0x81 /* Allocation bitmap */
+#define ET_UPCASE 0x82 /* Up-case table */
+#define ET_VLABEL 0x83 /* Volume label */
+#define ET_FILEDIR 0x85 /* File and directory */
+#define ET_STREAM 0xC0 /* Stream extension */
+#define ET_FILENAME 0xC1 /* Name extension */
+
+
+/* FatFs refers the FAT structure as simple byte array instead of structure member
+/ because the C structure is not binary compatible between different platforms */
+
+#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
+#define BS_OEMName 3 /* OEM name (8-byte) */
+#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
+#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
+#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
+#define BPB_NumFATs 16 /* Number of FATs (BYTE) */
+#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
+#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
+#define BPB_Media 21 /* Media descriptor byte (BYTE) */
+#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
+#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
+#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
+#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
+#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
+#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
+#define BS_NTres 37 /* WindowsNT error flag (BYTE) */
+#define BS_BootSig 38 /* Extended boot signature (BYTE) */
+#define BS_VolID 39 /* Volume serial number (DWORD) */
+#define BS_VolLab 43 /* Volume label string (8-byte) */
+#define BS_FilSysType 54 /* Filesystem type string (8-byte) */
+#define BS_BootCode 62 /* Boot code (448-byte) */
+#define BS_55AA 510 /* Signature word (WORD) */
+
+#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
+#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
+#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
+#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
+#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
+#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
+#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
+#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
+#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
+#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
+#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
+#define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
+#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
+
+#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
+#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
+#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
+#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
+#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
+#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
+#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
+#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
+#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
+#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
+#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
+#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
+#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
+#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
+#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
+#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
+#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
+#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
+
+#define DIR_Name 0 /* Short file name (11-byte) */
+#define DIR_Attr 11 /* Attribute (BYTE) */
+#define DIR_NTres 12 /* Lower case flag (BYTE) */
+#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
+#define DIR_CrtTime 14 /* Created time (DWORD) */
+#define DIR_LstAccDate 18 /* Last accessed date (WORD) */
+#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
+#define DIR_ModTime 22 /* Modified time (DWORD) */
+#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
+#define DIR_FileSize 28 /* File size (DWORD) */
+#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
+#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
+#define LDIR_Type 12 /* LFN: Entry type (BYTE) */
+#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
+#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
+#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
+#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
+#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
+#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
+#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
+#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
+#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
+#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
+#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
+#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
+#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
+#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
+#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
+#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
+#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
+#define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
+#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
+#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
+#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
+#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
+#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
+
+#define SZDIRE 32 /* Size of a directory entry */
+#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
+#define RDDEM 0x05 /* Replacement of the character collides with DDEM */
+#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
+
+#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
+#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
+#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
+#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
+
+#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
+#define SZ_PTE 16 /* MBR: Size of a partition table entry */
+#define PTE_Boot 0 /* MBR PTE: Boot indicator */
+#define PTE_StHead 1 /* MBR PTE: Start head */
+#define PTE_StSec 2 /* MBR PTE: Start sector */
+#define PTE_StCyl 3 /* MBR PTE: Start cylinder */
+#define PTE_System 4 /* MBR PTE: System ID */
+#define PTE_EdHead 5 /* MBR PTE: End head */
+#define PTE_EdSec 6 /* MBR PTE: End sector */
+#define PTE_EdCyl 7 /* MBR PTE: End cylinder */
+#define PTE_StLba 8 /* MBR PTE: Start in LBA */
+#define PTE_SizLba 12 /* MBR PTE: Size in LBA */
+
+
+/* Post process on fatal error in the file operations */
+#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
+
+
+/* Re-entrancy related */
+#if FF_FS_REENTRANT
+#if FF_USE_LFN == 1
+#error Static LFN work area cannot be used at thread-safe configuration
+#endif
+#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
+#else
+#define LEAVE_FF(fs, res) return res
+#endif
+
+
+/* Definitions of volume - physical location conversion */
+#if FF_MULTI_PARTITION
+#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
+#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */
+#else
+#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
+#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
+#endif
+
+
+/* Definitions of sector size */
+#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
+#error Wrong sector size configuration
+#endif
+#if FF_MAX_SS == FF_MIN_SS
+#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
+#else
+#define SS(fs) ((fs)->ssize) /* Variable sector size */
+#endif
+
+
+/* Timestamp */
+#if FF_FS_NORTC == 1
+#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
+#error Invalid FF_FS_NORTC settings
+#endif
+#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
+#else
+#define GET_FATTIME() get_fattime()
+#endif
+
+
+/* File lock controls */
+#if FF_FS_LOCK != 0
+#if FF_FS_READONLY
+#error FF_FS_LOCK must be 0 at read-only configuration
+#endif
+typedef struct {
+ FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */
+ DWORD clu; /* Object ID 2, containing directory (0:root) */
+ DWORD ofs; /* Object ID 3, offset in the directory */
+ WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+/* SBCS up-case tables (\x80-\xFF) */
+#define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+ 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+ 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
+#define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
+ 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
+ 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
+ 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
+ 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
+ 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+#define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
+ 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+ 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+ 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
+ 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
+ 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+ 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
+ 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
+ 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+ 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
+ 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
+ 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+ 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+ 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+ 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
+ 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+ 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+ 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
+ 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
+ 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
+
+
+/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
+#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
+#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
+#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
+#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
+
+
+/* Macros for table definitions */
+#define MERGE_2STR(a, b) a ## b
+#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Work Area
+
+---------------------------------------------------------------------------*/
+/* Remark: Variables defined here without initial value shall be guaranteed
+/ zero/null at start-up. If not, the linker option or start-up routine is
+/ not compliance with C standard. */
+
+/*--------------------------------*/
+/* File/Volume controls */
+/*--------------------------------*/
+
+#if FF_VOLUMES < 1 || FF_VOLUMES > 10
+#error Wrong FF_VOLUMES setting
+#endif
+static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */
+static WORD Fsid; /* Filesystem mount ID */
+
+#if FF_FS_RPATH != 0
+static BYTE CurrVol; /* Current drive */
+#endif
+
+#if FF_FS_LOCK != 0
+static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifdef FF_VOLUME_STRS
+static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */
+#endif
+#endif
+
+
+/*--------------------------------*/
+/* LFN/Directory working buffer */
+/*--------------------------------*/
+
+#if FF_USE_LFN == 0 /* Non-LFN configuration */
+#if FF_FS_EXFAT
+#error LFN must be enabled when enable exFAT
+#endif
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+#define LEAVE_MKFS(res) return res
+
+#else /* LFN configurations */
+#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
+#error Wrong setting of FF_MAX_LFN
+#endif
+#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
+#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
+#endif
+#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
+#error Wrong setting of FF_LFN_UNICODE
+#endif
+static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* FAT: Offset of LFN characters in the directory entry */
+#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
+
+#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
+#if FF_FS_EXFAT
+static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry block scratchpad buffer */
+#endif
+static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+#define LEAVE_MKFS(res) return res
+
+#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
+#if FF_FS_EXFAT
+#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
+#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
+#define FREE_NAMBUF()
+#else
+#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
+#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
+#define FREE_NAMBUF()
+#endif
+#define LEAVE_MKFS(res) return res
+
+#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
+#if FF_FS_EXFAT
+#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
+#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
+#define FREE_NAMBUF() ff_memfree(lfn)
+#else
+#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
+#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
+#define FREE_NAMBUF() ff_memfree(lfn)
+#endif
+#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
+#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
+
+#else
+#error Wrong setting of FF_USE_LFN
+
+#endif /* FF_USE_LFN == 1 */
+#endif /* FF_USE_LFN == 0 */
+
+
+
+/*--------------------------------*/
+/* Code conversion tables */
+/*--------------------------------*/
+
+#if FF_CODE_PAGE == 0 /* Run-time code page configuration */
+#define CODEPAGE CodePage
+static WORD CodePage; /* Current code page */
+static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */
+
+static const BYTE Ct437[] = TBL_CT437;
+static const BYTE Ct720[] = TBL_CT720;
+static const BYTE Ct737[] = TBL_CT737;
+static const BYTE Ct771[] = TBL_CT771;
+static const BYTE Ct775[] = TBL_CT775;
+static const BYTE Ct850[] = TBL_CT850;
+static const BYTE Ct852[] = TBL_CT852;
+static const BYTE Ct855[] = TBL_CT855;
+static const BYTE Ct857[] = TBL_CT857;
+static const BYTE Ct860[] = TBL_CT860;
+static const BYTE Ct861[] = TBL_CT861;
+static const BYTE Ct862[] = TBL_CT862;
+static const BYTE Ct863[] = TBL_CT863;
+static const BYTE Ct864[] = TBL_CT864;
+static const BYTE Ct865[] = TBL_CT865;
+static const BYTE Ct866[] = TBL_CT866;
+static const BYTE Ct869[] = TBL_CT869;
+static const BYTE Dc932[] = TBL_DC932;
+static const BYTE Dc936[] = TBL_DC936;
+static const BYTE Dc949[] = TBL_DC949;
+static const BYTE Dc950[] = TBL_DC950;
+
+#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
+#define CODEPAGE FF_CODE_PAGE
+static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE);
+
+#else /* Static code page configuration (DBCS) */
+#define CODEPAGE FF_CODE_PAGE
+static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE);
+
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------*/
+/* Print error header */
+/*-----------------------------------------------------------------------*/
+
+void print_error()
+{
+ gfx_printf("\n\n\n%k[FatFS] Error: %k", 0xFFFFFF00, 0xFFFFFFFF);
+}
+
+
+/*-----------------------------------------------------------------------*/
+/* Load/Store multi-byte word in the FAT structure */
+/*-----------------------------------------------------------------------*/
+
+static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */
+{
+ WORD rv;
+
+ rv = ptr[1];
+ rv = rv << 8 | ptr[0];
+ return rv;
+}
+
+static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */
+{
+ DWORD rv;
+
+ rv = ptr[3];
+ rv = rv << 8 | ptr[2];
+ rv = rv << 8 | ptr[1];
+ rv = rv << 8 | ptr[0];
+ return rv;
+}
+
+#if FF_FS_EXFAT
+static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */
+{
+ QWORD rv;
+
+ rv = ptr[7];
+ rv = rv << 8 | ptr[6];
+ rv = rv << 8 | ptr[5];
+ rv = rv << 8 | ptr[4];
+ rv = rv << 8 | ptr[3];
+ rv = rv << 8 | ptr[2];
+ rv = rv << 8 | ptr[1];
+ rv = rv << 8 | ptr[0];
+ return rv;
+}
+#endif
+
+#if !FF_FS_READONLY
+static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */
+{
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val;
+}
+
+static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */
+{
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val;
+}
+
+#if FF_FS_EXFAT
+static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */
+{
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val; val >>= 8;
+ *ptr++ = (BYTE)val;
+}
+#endif
+#endif /* !FF_FS_READONLY */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static void mem_cpy (void* dst, const void* src, UINT cnt)
+{
+ BYTE *d = (BYTE*)dst;
+ const BYTE *s = (const BYTE*)src;
+
+ if (cnt != 0) {
+ do {
+ *d++ = *s++;
+ } while (--cnt);
+ }
+}
+
+
+/* Fill memory block */
+static void mem_set (void* dst, int val, UINT cnt)
+{
+ BYTE *d = (BYTE*)dst;
+
+ do {
+ *d++ = (BYTE)val;
+ } while (--cnt);
+}
+
+
+/* Compare memory block */
+static int mem_cmp (const void* dst, const void* src, UINT cnt) /* ZR:same, NZ:different */
+{
+ const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+ int r = 0;
+
+ do {
+ r = *d++ - *s++;
+ } while (--cnt && r == 0);
+
+ return r;
+}
+
+
+/* Check if chr is contained in the string */
+static int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */
+{
+ while (*str && *str != chr) str++;
+ return *str;
+}
+
+
+/* Test if the character is DBC 1st byte */
+static int dbc_1st (BYTE c)
+{
+#if FF_CODE_PAGE == 0 /* Variable code page */
+ if (DbcTbl && c >= DbcTbl[0]) {
+ if (c <= DbcTbl[1]) return 1; /* 1st byte range 1 */
+ if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */
+ }
+#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
+ if (c >= DbcTbl[0]) {
+ if (c <= DbcTbl[1]) return 1;
+ if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1;
+ }
+#else /* SBCS fixed code page */
+ if (c != 0) return 0; /* Always false */
+#endif
+ return 0;
+}
+
+
+/* Test if the character is DBC 2nd byte */
+static int dbc_2nd (BYTE c)
+{
+#if FF_CODE_PAGE == 0 /* Variable code page */
+ if (DbcTbl && c >= DbcTbl[4]) {
+ if (c <= DbcTbl[5]) return 1; /* 2nd byte range 1 */
+ if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */
+ if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */
+ }
+#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
+ if (c >= DbcTbl[4]) {
+ if (c <= DbcTbl[5]) return 1;
+ if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1;
+ if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1;
+ }
+#else /* SBCS fixed code page */
+ if (c != 0) return 0; /* Always false */
+#endif
+ return 0;
+}
+
+
+#if FF_USE_LFN
+
+/* Get a character from TCHAR string in defined API encodeing */
+static DWORD tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */
+ const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */
+)
+{
+ DWORD uc;
+ const TCHAR *p = *str;
+
+#if FF_LFN_UNICODE == 1 /* UTF-16 input */
+ WCHAR wc;
+
+ uc = *p++; /* Get a unit */
+ if (IsSurrogate(uc)) { /* Surrogate? */
+ wc = *p++; /* Get low surrogate */
+ if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */
+ uc = uc << 16 | wc;
+ }
+
+#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
+ BYTE b;
+ int nf;
+
+ uc = (BYTE)*p++; /* Get a unit */
+ if (uc & 0x80) { /* Multiple byte code? */
+ if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */
+ uc &= 0x1F; nf = 1;
+ } else {
+ if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */
+ uc &= 0x0F; nf = 2;
+ } else {
+ if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */
+ uc &= 0x07; nf = 3;
+ } else { /* Wrong sequence */
+ return 0xFFFFFFFF;
+ }
+ }
+ }
+ do { /* Get trailing bytes */
+ b = (BYTE)*p++;
+ if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */
+ uc = uc << 6 | (b & 0x3F);
+ } while (--nf != 0);
+ if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */
+ if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
+ }
+
+#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
+ uc = (TCHAR)*p++; /* Get a unit */
+ if (uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */
+ if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
+
+#else /* ANSI/OEM input */
+ BYTE b;
+ WCHAR wc;
+
+ wc = (BYTE)*p++; /* Get a byte */
+ if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */
+ b = (BYTE)*p++; /* Get 2nd byte */
+ if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */
+ wc = (wc << 8) + b; /* Make a DBC */
+ }
+ if (wc != 0) {
+ wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */
+ if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */
+ }
+ uc = wc;
+
+#endif
+ *str = p; /* Next read pointer */
+ return uc;
+}
+
+
+/* Output a TCHAR string in defined API encoding */
+static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
+ DWORD chr, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */
+ TCHAR* buf, /* Output buffer */
+ UINT szb /* Size of the buffer */
+)
+{
+#if FF_LFN_UNICODE == 1 /* UTF-16 output */
+ WCHAR hs, wc;
+
+ hs = (WCHAR)(chr >> 16);
+ wc = (WCHAR)chr;
+ if (hs == 0) { /* Single encoding unit? */
+ if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */
+ *buf = wc;
+ return 1;
+ }
+ if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */
+ *buf++ = hs;
+ *buf++ = wc;
+ return 2;
+
+#elif FF_LFN_UNICODE == 2 /* UTF-8 output */
+ DWORD hc;
+
+ if (chr < 0x80) { /* Single byte code? */
+ if (szb < 1) return 0; /* Buffer overflow? */
+ *buf = (TCHAR)chr;
+ return 1;
+ }
+ if (chr < 0x800) { /* 2-byte sequence? */
+ if (szb < 2) return 0; /* Buffer overflow? */
+ *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+ return 2;
+ }
+ if (chr < 0x10000) { /* 3-byte sequence? */
+ if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */
+ *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+ return 3;
+ }
+ /* 4-byte sequence */
+ if (szb < 4) return 0; /* Buffer overflow? */
+ hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
+ chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
+ if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
+ chr = (hc | chr) + 0x10000;
+ *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07));
+ *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+ return 4;
+
+#elif FF_LFN_UNICODE == 3 /* UTF-32 output */
+ DWORD hc;
+
+ if (szb < 1) return 0; /* Buffer overflow? */
+ if (chr >= 0x10000) { /* Out of BMP? */
+ hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
+ chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
+ if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
+ chr = (hc | chr) + 0x10000;
+ }
+ *buf++ = (TCHAR)chr;
+ return 1;
+
+#else /* ANSI/OEM output */
+ WCHAR wc;
+
+ wc = ff_uni2oem(chr, CODEPAGE);
+ if (wc >= 0x100) { /* Is this a DBC? */
+ if (szb < 2) return 0;
+ *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */
+ *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */
+ return 2;
+ }
+ if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */
+ *buf++ = (TCHAR)wc; /* Store the character */
+ return 1;
+#endif
+}
+#endif /* FF_USE_LFN */
+
+
+#if FF_FS_REENTRANT
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume */
+/*-----------------------------------------------------------------------*/
+static int lock_fs ( /* 1:Ok, 0:timeout */
+ FATFS* fs /* Filesystem object */
+)
+{
+ return ff_req_grant(fs->sobj);
+}
+
+
+static void unlock_fs (
+ FATFS* fs, /* Filesystem object */
+ FRESULT res /* Result code to be returned */
+)
+{
+ if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
+ ff_rel_grant(fs->sobj);
+ }
+}
+
+#endif
+
+
+
+#if FF_FS_LOCK != 0
+/*-----------------------------------------------------------------------*/
+/* File lock control functions */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT chk_lock ( /* Check if the file can be accessed */
+ DIR* dp, /* Directory object pointing the file to be checked */
+ int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
+)
+{
+ UINT i, be;
+
+ /* Search open object table for the object */
+ be = 0;
+ for (i = 0; i < FF_FS_LOCK; i++) {
+ if (Files[i].fs) { /* Existing entry */
+ if (Files[i].fs == dp->obj.fs && /* Check if the object matches with an open object */
+ Files[i].clu == dp->obj.sclust &&
+ Files[i].ofs == dp->dptr) break;
+ } else { /* Blank entry */
+ be = 1;
+ }
+ }
+ if (i == FF_FS_LOCK) { /* The object has not been opened */
+ return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; /* Is there a blank entry for new object? */
+ }
+
+ /* The object was opened. Reject any open against writing file and all write mode open */
+ return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static int enq_lock (void) /* Check if an entry is available for a new object */
+{
+ UINT i;
+
+ for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
+ return (i == FF_FS_LOCK) ? 0 : 1;
+}
+
+
+static UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
+ DIR* dp, /* Directory object pointing the file to register or increment */
+ int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+ UINT i;
+
+
+ for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */
+ if (Files[i].fs == dp->obj.fs &&
+ Files[i].clu == dp->obj.sclust &&
+ Files[i].ofs == dp->dptr) break;
+ }
+
+ if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */
+ for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
+ if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */
+ Files[i].fs = dp->obj.fs;
+ Files[i].clu = dp->obj.sclust;
+ Files[i].ofs = dp->dptr;
+ Files[i].ctr = 0;
+ }
+
+ if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */
+
+ Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */
+
+ return i + 1; /* Index number origin from 1 */
+}
+
+
+static FRESULT dec_lock ( /* Decrement object open counter */
+ UINT i /* Semaphore index (1..) */
+)
+{
+ WORD n;
+ FRESULT res;
+
+
+ if (--i < FF_FS_LOCK) { /* Index number origin from 0 */
+ n = Files[i].ctr;
+ if (n == 0x100) n = 0; /* If write mode open, delete the entry */
+ if (n > 0) n--; /* Decrement read mode open count */
+ Files[i].ctr = n;
+ if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */
+ res = FR_OK;
+ } else {
+ res = FR_INT_ERR; /* Invalid index nunber */
+ }
+ return res;
+}
+
+
+static void clear_lock ( /* Clear lock entries of the volume */
+ FATFS *fs
+)
+{
+ UINT i;
+
+ for (i = 0; i < FF_FS_LOCK; i++) {
+ if (Files[i].fs == fs) Files[i].fs = 0;
+ }
+}
+
+#endif /* FF_FS_LOCK != 0 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Move/Flush disk access window in the filesystem object */
+/*-----------------------------------------------------------------------*/
+#if !FF_FS_READONLY
+static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */
+ FATFS* fs /* Filesystem object */
+)
+{
+ FRESULT res = FR_OK;
+
+
+ if (fs->wflag) { /* Is the disk access window dirty */
+ if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) { /* Write back the window */
+ fs->wflag = 0; /* Clear window dirty flag */
+ if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it in the 1st FAT? */
+ if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */
+ }
+ } else {
+ res = FR_DISK_ERR;
+ }
+ }
+ return res;
+}
+#endif
+
+
+static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */
+ FATFS* fs, /* Filesystem object */
+ DWORD sector /* Sector number to make appearance in the fs->win[] */
+)
+{
+ FRESULT res = FR_OK;
+
+
+ if (sector != fs->winsect) { /* Window offset changed? */
+#if !FF_FS_READONLY
+ res = sync_window(fs); /* Write-back changes */
+#endif
+ if (res == FR_OK) { /* Fill sector window with new data */
+ if (disk_read(fs->pdrv, fs->win, sector, 1) != RES_OK) {
+ sector = 0xFFFFFFFF; /* Invalidate window if read data is not valid */
+ res = FR_DISK_ERR;
+ }
+ fs->winsect = sector;
+ }
+ }
+ return res;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Synchronize filesystem and data on the storage */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
+ FATFS* fs /* Filesystem object */
+)
+{
+ FRESULT res;
+
+
+ res = sync_window(fs);
+ if (res == FR_OK) {
+ if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */
+ /* Create FSInfo structure */
+ mem_set(fs->win, 0, sizeof fs->win);
+ st_word(fs->win + BS_55AA, 0xAA55);
+ st_dword(fs->win + FSI_LeadSig, 0x41615252);
+ st_dword(fs->win + FSI_StrucSig, 0x61417272);
+ st_dword(fs->win + FSI_Free_Count, fs->free_clst);
+ st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
+ /* Write it into the FSInfo sector */
+ fs->winsect = fs->volbase + 1;
+ disk_write(fs->pdrv, fs->win, fs->winsect, 1);
+ fs->fsi_flag = 0;
+ }
+ /* Make sure that no pending write process in the lower layer */
+ if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;
+ }
+
+ return res;
+}
+
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get physical sector number from cluster number */
+/*-----------------------------------------------------------------------*/
+
+static DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
+ FATFS* fs, /* Filesystem object */
+ DWORD clst /* Cluster# to be converted */
+)
+{
+ clst -= 2; /* Cluster number is origin from 2 */
+ if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */
+ return fs->database + fs->csize * clst; /* Start sector number of the cluster */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry */
+/*-----------------------------------------------------------------------*/
+
+static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
+ FFOBJID* obj, /* Corresponding object */
+ DWORD clst /* Cluster number to get the value */
+)
+{
+ UINT wc, bc;
+ DWORD val;
+ FATFS *fs = obj->fs;
+
+
+ if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */
+ val = 1; /* Internal error */
+
+ } else {
+ val = 0xFFFFFFFF; /* Default value falls on disk error */
+
+ switch (fs->fs_type) {
+ case FS_FAT12 :
+ bc = (UINT)clst; bc += bc / 2;
+ if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+ wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of the entry */
+ if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+ wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of the entry */
+ val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust bit position */
+ break;
+
+ case FS_FAT16 :
+ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;
+ val = ld_word(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */
+ break;
+
+ case FS_FAT32 :
+ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+ val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */
+ break;
+#if FF_FS_EXFAT
+ case FS_EXFAT :
+ if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat == 0) { /* Object except root dir must have valid data length */
+ DWORD cofs = clst - obj->sclust; /* Offset from start cluster */
+ DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */
+
+ if (obj->stat == 2 && cofs <= clen) { /* Is it a contiguous chain? */
+ val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* No data on the FAT, generate the value */
+ break;
+ }
+ if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */
+ val = clst + 1; /* Generate the value */
+ break;
+ }
+ if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */
+ if (obj->n_frag != 0) { /* Is it on the growing edge? */
+ val = 0x7FFFFFFF; /* Generate EOC */
+ } else {
+ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+ val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF;
+ }
+ break;
+ }
+ }
+ /* go to default */
+#endif
+ default:
+ val = 1; /* Internal error */
+ }
+ }
+
+ return val;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
+ FATFS* fs, /* Corresponding filesystem object */
+ DWORD clst, /* FAT index number (cluster number) to be changed */
+ DWORD val /* New value to be set to the entry */
+)
+{
+ UINT bc;
+ BYTE *p;
+ FRESULT res = FR_INT_ERR;
+
+
+ if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
+ switch (fs->fs_type) {
+ case FS_FAT12 :
+ bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */
+ res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+ if (res != FR_OK) break;
+ p = fs->win + bc++ % SS(fs);
+ *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Put 1st byte */
+ fs->wflag = 1;
+ res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+ if (res != FR_OK) break;
+ p = fs->win + bc % SS(fs);
+ *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Put 2nd byte */
+ fs->wflag = 1;
+ break;
+
+ case FS_FAT16 :
+ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+ if (res != FR_OK) break;
+ st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */
+ fs->wflag = 1;
+ break;
+
+ case FS_FAT32 :
+#if FF_FS_EXFAT
+ case FS_EXFAT :
+#endif
+ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+ if (res != FR_OK) break;
+ if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
+ val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000);
+ }
+ st_dword(fs->win + clst * 4 % SS(fs), val);
+ fs->wflag = 1;
+ break;
+ }
+ }
+ return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+#if FF_FS_EXFAT && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* exFAT: Accessing FAT and Allocation Bitmap */
+/*-----------------------------------------------------------------------*/
+
+/*--------------------------------------*/
+/* Find a contiguous free cluster block */
+/*--------------------------------------*/
+
+static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
+ FATFS* fs, /* Filesystem object */
+ DWORD clst, /* Cluster number to scan from */
+ DWORD ncl /* Number of contiguous clusters to find (1..) */
+)
+{
+ BYTE bm, bv;
+ UINT i;
+ DWORD val, scl, ctr;
+
+
+ clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */
+ if (clst >= fs->n_fatent - 2) clst = 0;
+ scl = val = clst; ctr = 0;
+ for (;;) {
+ if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;
+ i = val / 8 % SS(fs); bm = 1 << (val % 8);
+ do {
+ do {
+ bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */
+ if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */
+ val = 0; bm = 0; i = SS(fs);
+ }
+ if (bv == 0) { /* Is it a free cluster? */
+ if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */
+ } else {
+ scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */
+ }
+ if (val == clst) return 0; /* All cluster scanned? */
+ } while (bm != 0);
+ bm = 1;
+ } while (++i < SS(fs));
+ }
+}
+
+
+/*----------------------------------------*/
+/* Set/Clear a block of allocation bitmap */
+/*----------------------------------------*/
+
+static FRESULT change_bitmap (
+ FATFS* fs, /* Filesystem object */
+ DWORD clst, /* Cluster number to change from */
+ DWORD ncl, /* Number of clusters to be changed */
+ int bv /* bit value to be set (0 or 1) */
+)
+{
+ BYTE bm;
+ UINT i;
+ DWORD sect;
+
+
+ clst -= 2; /* The first bit corresponds to cluster #2 */
+ sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */
+ i = clst / 8 % SS(fs); /* Byte offset in the sector */
+ bm = 1 << (clst % 8); /* Bit mask in the byte */
+ for (;;) {
+ if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;
+ do {
+ do {
+ if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */
+ fs->win[i] ^= bm; /* Flip the bit */
+ fs->wflag = 1;
+ if (--ncl == 0) return FR_OK; /* All bits processed? */
+ } while (bm <<= 1); /* Next bit */
+ bm = 1;
+ } while (++i < SS(fs)); /* Next byte */
+ i = 0;
+ }
+}
+
+
+/*---------------------------------------------*/
+/* Fill the first fragment of the FAT chain */
+/*---------------------------------------------*/
+
+static FRESULT fill_first_frag (
+ FFOBJID* obj /* Pointer to the corresponding object */
+)
+{
+ FRESULT res;
+ DWORD cl, n;
+
+
+ if (obj->stat == 3) { /* Has the object been changed 'fragmented' in this session? */
+ for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */
+ res = put_fat(obj->fs, cl, cl + 1);
+ if (res != FR_OK) return res;
+ }
+ obj->stat = 0; /* Change status 'FAT chain is valid' */
+ }
+ return FR_OK;
+}
+
+
+/*---------------------------------------------*/
+/* Fill the last fragment of the FAT chain */
+/*---------------------------------------------*/
+
+static FRESULT fill_last_frag (
+ FFOBJID* obj, /* Pointer to the corresponding object */
+ DWORD lcl, /* Last cluster of the fragment */
+ DWORD term /* Value to set the last FAT entry */
+)
+{
+ FRESULT res;
+
+
+ while (obj->n_frag > 0) { /* Create the chain of last fragment */
+ res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term);
+ if (res != FR_OK) return res;
+ obj->n_frag--;
+ }
+ return FR_OK;
+}
+
+#endif /* FF_FS_EXFAT && !FF_FS_READONLY */
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */
+ FFOBJID* obj, /* Corresponding object */
+ DWORD clst, /* Cluster to remove a chain from */
+ DWORD pclst /* Previous cluster of clst (0 if entire chain) */
+)
+{
+ FRESULT res = FR_OK;
+ DWORD nxt;
+ FATFS *fs = obj->fs;
+#if FF_FS_EXFAT || FF_USE_TRIM
+ DWORD scl = clst, ecl = clst;
+#endif
+#if FF_USE_TRIM
+ DWORD rt[2];
+#endif
+
+ if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */
+
+ /* Mark the previous cluster 'EOC' on the FAT if it exists */
+ if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {
+ res = put_fat(fs, pclst, 0xFFFFFFFF);
+ if (res != FR_OK) return res;
+ }
+
+ /* Remove the chain */
+ do {
+ nxt = get_fat(obj, clst); /* Get cluster status */
+ if (nxt == 0) break; /* Empty cluster? */
+ if (nxt == 1) return FR_INT_ERR; /* Internal error? */
+ if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */
+ if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
+ res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */
+ if (res != FR_OK) return res;
+ }
+ if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */
+ fs->free_clst++;
+ fs->fsi_flag |= 1;
+ }
+#if FF_FS_EXFAT || FF_USE_TRIM
+ if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
+ ecl = nxt;
+ } else { /* End of contiguous cluster block */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
+ if (res != FR_OK) return res;
+ }
+#endif
+#if FF_USE_TRIM
+ rt[0] = clst2sect(fs, scl); /* Start of data area freed */
+ rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area freed */
+ disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform device the data in the block is no longer needed */
+#endif
+ scl = ecl = nxt;
+ }
+#endif
+ clst = nxt; /* Next cluster */
+ } while (clst < fs->n_fatent); /* Repeat while not the last link */
+
+#if FF_FS_EXFAT
+ /* Some post processes for chain status */
+ if (fs->fs_type == FS_EXFAT) {
+ if (pclst == 0) { /* Has the entire chain been removed? */
+ obj->stat = 0; /* Change the chain status 'initial' */
+ } else {
+ if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */
+ clst = obj->sclust; /* Follow the chain to check if it gets contiguous */
+ while (clst != pclst) {
+ nxt = get_fat(obj, clst);
+ if (nxt < 2) return FR_INT_ERR;
+ if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;
+ if (nxt != clst + 1) break; /* Not contiguous? */
+ clst++;
+ }
+ if (clst == pclst) { /* Has the chain got contiguous again? */
+ obj->stat = 2; /* Change the chain status 'contiguous' */
+ }
+ } else {
+ if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */
+ obj->stat = 2; /* Change the chain status 'contiguous' */
+ }
+ }
+ }
+ }
+#endif
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch a chain or Create a new chain */
+/*-----------------------------------------------------------------------*/
+
+static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+ FFOBJID* obj, /* Corresponding object */
+ DWORD clst /* Cluster# to stretch, 0:Create a new chain */
+)
+{
+ DWORD cs, ncl, scl;
+ FRESULT res;
+ FATFS *fs = obj->fs;
+
+
+ if (clst == 0) { /* Create a new chain */
+ scl = fs->last_clst; /* Suggested cluster to start to find */
+ if (scl == 0 || scl >= fs->n_fatent) scl = 1;
+ }
+ else { /* Stretch a chain */
+ cs = get_fat(obj, clst); /* Check the cluster status */
+ if (cs < 2) return 1; /* Test for insanity */
+ if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */
+ if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */
+ scl = clst; /* Cluster to start to find */
+ }
+ if (fs->free_clst == 0) return 0; /* No free cluster */
+
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */
+ if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */
+ res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */
+ if (res == FR_INT_ERR) return 1;
+ if (res == FR_DISK_ERR) return 0xFFFFFFFF;
+ if (clst == 0) { /* Is it a new chain? */
+ obj->stat = 2; /* Set status 'contiguous' */
+ } else { /* It is a stretched chain */
+ if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */
+ obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */
+ obj->stat = 3; /* Change status 'just fragmented' */
+ }
+ }
+ if (obj->stat != 2) { /* Is the file non-contiguous? */
+ if (ncl == clst + 1) { /* Is the cluster next to previous one? */
+ obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */
+ } else { /* New fragment */
+ if (obj->n_frag == 0) obj->n_frag = 1;
+ res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */
+ if (res == FR_OK) obj->n_frag = 1;
+ }
+ }
+ } else
+#endif
+ { /* On the FAT/FAT32 volume */
+ ncl = 0;
+ if (scl == clst) { /* Stretching an existing chain? */
+ ncl = scl + 1; /* Test if next cluster is free */
+ if (ncl >= fs->n_fatent) ncl = 2;
+ cs = get_fat(obj, ncl); /* Get next cluster status */
+ if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */
+ if (cs != 0) { /* Not free? */
+ cs = fs->last_clst; /* Start at suggested cluster if it is valid */
+ if (cs >= 2 && cs < fs->n_fatent) scl = cs;
+ ncl = 0;
+ }
+ }
+ if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */
+ ncl = scl; /* Start cluster */
+ for (;;) {
+ ncl++; /* Next cluster */
+ if (ncl >= fs->n_fatent) { /* Check wrap-around */
+ ncl = 2;
+ if (ncl > scl) return 0; /* No free cluster found? */
+ }
+ cs = get_fat(obj, ncl); /* Get the cluster status */
+ if (cs == 0) break; /* Found a free cluster? */
+ if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */
+ if (ncl == scl) return 0; /* No free cluster found? */
+ }
+ }
+ res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */
+ if (res == FR_OK && clst != 0) {
+ res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */
+ }
+ }
+
+ if (res == FR_OK) { /* Update FSINFO if function succeeded. */
+ fs->last_clst = ncl;
+ if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--;
+ fs->fsi_flag |= 1;
+ } else {
+ ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */
+ }
+
+ return ncl; /* Return new cluster number or error status */
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+#if FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table */
+/*-----------------------------------------------------------------------*/
+
+static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */
+ FIL* fp, /* Pointer to the file object */
+ FSIZE_t ofs /* File offset to be converted to cluster# */
+)
+{
+ DWORD cl, ncl, *tbl;
+ FATFS *fs = fp->obj.fs;
+
+
+ tbl = fp->cltbl + 1; /* Top of CLMT */
+ cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */
+ for (;;) {
+ ncl = *tbl++; /* Number of cluters in the fragment */
+ if (ncl == 0) return 0; /* End of table? (error) */
+ if (cl < ncl) break; /* In this fragment? */
+ cl -= ncl; tbl++; /* Next fragment */
+ }
+ return cl + *tbl; /* Return the cluster number */
+}
+
+#endif /* FF_USE_FASTSEEK */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Fill a cluster with zeros */
+/*-----------------------------------------------------------------------*/
+
+#if !FF_FS_READONLY
+static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
+ FATFS *fs, /* Filesystem object */
+ DWORD clst /* Directory table to clear */
+)
+{
+ DWORD sect;
+ UINT n, szb;
+ BYTE *ibuf;
+
+
+ if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */
+ sect = clst2sect(fs, clst); /* Top of the cluster */
+ fs->winsect = sect; /* Set window to top of the cluster */
+ mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */
+#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
+ /* Allocate a temporary buffer */
+ for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ;
+ if (szb > SS(fs)) { /* Buffer allocated? */
+ mem_set(ibuf, 0, szb);
+ szb /= SS(fs); /* Bytes -> Sectors */
+ for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */
+ ff_memfree(ibuf);
+ } else
+#endif
+ {
+ ibuf = fs->win; szb = 1; /* Use window buffer (many single-sector writes may take a time) */
+ for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */
+ }
+ return (n == fs->csize) ? FR_OK : FR_DISK_ERR;
+}
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
+ DIR* dp, /* Pointer to directory object */
+ DWORD ofs /* Offset of directory table */
+)
+{
+ DWORD csz, clst;
+ FATFS *fs = dp->obj.fs;
+
+
+ if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */
+ return FR_INT_ERR;
+ }
+ dp->dptr = ofs; /* Set current offset */
+ clst = dp->obj.sclust; /* Table start cluster (0:root) */
+ if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */
+ clst = fs->dirbase;
+ if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */
+ }
+
+ if (clst == 0) { /* Static table (root-directory on the FAT volume) */
+ if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */
+ dp->sect = fs->dirbase;
+
+ } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
+ csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */
+ while (ofs >= csz) { /* Follow cluster chain */
+ clst = get_fat(&dp->obj, clst); /* Get next cluster */
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
+ if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */
+ ofs -= csz;
+ }
+ dp->sect = clst2sect(fs, clst);
+ }
+ dp->clust = clst; /* Current cluster# */
+ if (dp->sect == 0) return FR_INT_ERR;
+ dp->sect += ofs / SS(fs); /* Sector# of the directory entry */
+ dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory table index next */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
+ DIR* dp, /* Pointer to the directory object */
+ int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+ DWORD ofs, clst;
+ FATFS *fs = dp->obj.fs;
+
+
+ ofs = dp->dptr + SZDIRE; /* Next entry */
+ if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset reached the max value */
+ if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has been disabled */
+
+ if (ofs % SS(fs) == 0) { /* Sector changed? */
+ dp->sect++; /* Next sector */
+
+ if (dp->clust == 0) { /* Static table */
+ if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */
+ dp->sect = 0; return FR_NO_FILE;
+ }
+ }
+ else { /* Dynamic table */
+ if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */
+ clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */
+ if (clst <= 1) return FR_INT_ERR; /* Internal error */
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
+ if (clst >= fs->n_fatent) { /* It reached end of dynamic table */
+#if !FF_FS_READONLY
+ if (!stretch) { /* If no stretch, report EOT */
+ dp->sect = 0; return FR_NO_FILE;
+ }
+ clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */
+ if (clst == 0) return FR_DENIED; /* No free cluster */
+ if (clst == 1) return FR_INT_ERR; /* Internal error */
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
+ if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR; /* Clean up the stretched table */
+ if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: The directory has been stretched */
+#else
+ if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */
+ dp->sect = 0; return FR_NO_FILE; /* Report EOT */
+#endif
+ }
+ dp->clust = clst; /* Initialize data for new cluster */
+ dp->sect = clst2sect(fs, clst);
+ }
+ }
+ }
+ dp->dptr = ofs; /* Current entry */
+ dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */
+
+ return FR_OK;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Reserve a block of directory entries */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
+ DIR* dp, /* Pointer to the directory object */
+ UINT nent /* Number of contiguous entries to allocate */
+)
+{
+ FRESULT res;
+ UINT n;
+ FATFS *fs = dp->obj.fs;
+
+
+ res = dir_sdi(dp, 0);
+ if (res == FR_OK) {
+ n = 0;
+ do {
+ res = move_window(fs, dp->sect);
+ if (res != FR_OK) break;
+#if FF_FS_EXFAT
+ if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
+#else
+ if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
+#endif
+ if (++n == nent) break; /* A block of contiguous free entries is found */
+ } else {
+ n = 0; /* Not a blank entry. Restart to search */
+ }
+ res = dir_next(dp, 1);
+ } while (res == FR_OK); /* Next entry with table stretch enabled */
+ }
+
+ if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
+ return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT: Directory handling - Load/Store start cluster number */
+/*-----------------------------------------------------------------------*/
+
+static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */
+ FATFS* fs, /* Pointer to the fs object */
+ const BYTE* dir /* Pointer to the key entry */
+)
+{
+ DWORD cl;
+
+ cl = ld_word(dir + DIR_FstClusLO);
+ if (fs->fs_type == FS_FAT32) {
+ cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;
+ }
+
+ return cl;
+}
+
+
+#if !FF_FS_READONLY
+static void st_clust (
+ FATFS* fs, /* Pointer to the fs object */
+ BYTE* dir, /* Pointer to the key entry */
+ DWORD cl /* Value to be set */
+)
+{
+ st_word(dir + DIR_FstClusLO, (WORD)cl);
+ if (fs->fs_type == FS_FAT32) {
+ st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));
+ }
+}
+#endif
+
+
+
+#if FF_USE_LFN
+/*--------------------------------------------------------*/
+/* FAT-LFN: Compare a part of file name with an LFN entry */
+/*--------------------------------------------------------*/
+
+static int cmp_lfn ( /* 1:matched, 0:not matched */
+ const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */
+ BYTE* dir /* Pointer to the directory entry containing the part of LFN */
+)
+{
+ UINT i, s;
+ WCHAR wc, uc;
+
+
+ if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */
+
+ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */
+
+ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */
+ uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */
+ if (wc != 0) {
+ if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */
+ return 0; /* Not matched */
+ }
+ wc = uc;
+ } else {
+ if (uc != 0xFFFF) return 0; /* Check filler */
+ }
+ }
+
+ if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */
+
+ return 1; /* The part of LFN matched */
+}
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
+/*-----------------------------------------------------*/
+/* FAT-LFN: Pick a part of file name from an LFN entry */
+/*-----------------------------------------------------*/
+
+static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
+ WCHAR* lfnbuf, /* Pointer to the LFN working buffer */
+ BYTE* dir /* Pointer to the LFN entry */
+)
+{
+ UINT i, s;
+ WCHAR wc, uc;
+
+
+ if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */
+
+ i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */
+
+ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */
+ uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */
+ if (wc != 0) {
+ if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
+ lfnbuf[i++] = wc = uc; /* Store it */
+ } else {
+ if (uc != 0xFFFF) return 0; /* Check filler */
+ }
+ }
+
+ if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if it is the last LFN part and not terminated */
+ if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
+ lfnbuf[i] = 0;
+ }
+
+ return 1; /* The part of LFN is valid */
+}
+#endif
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------*/
+/* FAT-LFN: Create an entry of LFN entries */
+/*-----------------------------------------*/
+
+static void put_lfn (
+ const WCHAR* lfn, /* Pointer to the LFN */
+ BYTE* dir, /* Pointer to the LFN entry to be created */
+ BYTE ord, /* LFN order (1-20) */
+ BYTE sum /* Checksum of the corresponding SFN */
+)
+{
+ UINT i, s;
+ WCHAR wc;
+
+
+ dir[LDIR_Chksum] = sum; /* Set checksum */
+ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */
+ dir[LDIR_Type] = 0;
+ st_word(dir + LDIR_FstClusLO, 0);
+
+ i = (ord - 1) * 13; /* Get offset in the LFN working buffer */
+ s = wc = 0;
+ do {
+ if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */
+ st_word(dir + LfnOfs[s], wc); /* Put it */
+ if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */
+ } while (++s < 13);
+ if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */
+ dir[LDIR_Ord] = ord; /* Set the LFN order */
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_LFN */
+
+
+
+#if FF_USE_LFN && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT-LFN: Create a Numbered SFN */
+/*-----------------------------------------------------------------------*/
+
+static void gen_numname (
+ BYTE* dst, /* Pointer to the buffer to store numbered SFN */
+ const BYTE* src, /* Pointer to SFN */
+ const WCHAR* lfn, /* Pointer to LFN */
+ UINT seq /* Sequence number */
+)
+{
+ BYTE ns[8], c;
+ UINT i, j;
+ WCHAR wc;
+ DWORD sr;
+
+
+ mem_cpy(dst, src, 11);
+
+ if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */
+ sr = seq;
+ while (*lfn) { /* Create a CRC as hash value */
+ wc = *lfn++;
+ for (i = 0; i < 16; i++) {
+ sr = (sr << 1) + (wc & 1);
+ wc >>= 1;
+ if (sr & 0x10000) sr ^= 0x11021;
+ }
+ }
+ seq = (UINT)sr;
+ }
+
+ /* itoa (hexdecimal) */
+ i = 7;
+ do {
+ c = (BYTE)((seq % 16) + '0');
+ if (c > '9') c += 7;
+ ns[i--] = c;
+ seq /= 16;
+ } while (seq);
+ ns[i] = '~';
+
+ /* Append the number to the SFN body */
+ for (j = 0; j < i && dst[j] != ' '; j++) {
+ if (dbc_1st(dst[j])) {
+ if (j == i - 1) break;
+ j++;
+ }
+ }
+ do {
+ dst[j++] = (i < 8) ? ns[i++] : ' ';
+ } while (j < 8);
+}
+#endif /* FF_USE_LFN && !FF_FS_READONLY */
+
+
+
+#if FF_USE_LFN
+/*-----------------------------------------------------------------------*/
+/* FAT-LFN: Calculate checksum of an SFN entry */
+/*-----------------------------------------------------------------------*/
+
+static BYTE sum_sfn (
+ const BYTE* dir /* Pointer to the SFN entry */
+)
+{
+ BYTE sum = 0;
+ UINT n = 11;
+
+ do {
+ sum = (sum >> 1) + (sum << 7) + *dir++;
+ } while (--n);
+ return sum;
+}
+
+#endif /* FF_USE_LFN */
+
+
+
+#if FF_FS_EXFAT
+/*-----------------------------------------------------------------------*/
+/* exFAT: Checksum */
+/*-----------------------------------------------------------------------*/
+
+static WORD xdir_sum ( /* Get checksum of the directoly entry block */
+ const BYTE* dir /* Directory entry block to be calculated */
+)
+{
+ UINT i, szblk;
+ WORD sum;
+
+
+ szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of the entry block */
+ for (i = sum = 0; i < szblk; i++) {
+ if (i == XDIR_SetSum) { /* Skip 2-byte sum field */
+ i++;
+ } else {
+ sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];
+ }
+ }
+ return sum;
+}
+
+
+
+static WORD xname_sum ( /* Get check sum (to be used as hash) of the file name */
+ const WCHAR* name /* File name to be calculated */
+)
+{
+ WCHAR chr;
+ WORD sum = 0;
+
+
+ while ((chr = *name++) != 0) {
+ chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be up-case converted */
+ sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);
+ sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);
+ }
+ return sum;
+}
+
+
+#if !FF_FS_READONLY && FF_USE_MKFS
+static DWORD xsum32 ( /* Returns 32-bit checksum */
+ BYTE dat, /* Byte to be calculated (byte-by-byte processing) */
+ DWORD sum /* Previous sum value */
+)
+{
+ sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat;
+ return sum;
+}
+#endif
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
+/*------------------------------------------------------*/
+/* exFAT: Get object information from a directory block */
+/*------------------------------------------------------*/
+
+static void get_xfileinfo (
+ BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */
+ FILINFO* fno /* Buffer to store the extracted file information */
+)
+{
+ WCHAR wc, hs;
+ UINT di, si, nc;
+
+ /* Get file name from the entry block */
+ si = SZDIRE * 2; /* 1st C1 entry */
+ nc = 0; hs = 0; di = 0;
+ while (nc < dirb[XDIR_NumName]) {
+ if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */
+ if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */
+ wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */
+ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
+ hs = wc; continue; /* Get low surrogate */
+ }
+ wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
+ if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */
+ di += wc;
+ hs = 0;
+ }
+ if (hs != 0) di = 0; /* Broken surrogate pair? */
+ if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */
+ fno->fname[di] = 0; /* Terminate the name */
+ fno->altname[0] = 0; /* exFAT does not support SFN */
+
+ fno->fattrib = dirb[XDIR_Attr]; /* Attribute */
+ fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */
+ fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */
+ fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+/*-----------------------------------*/
+/* exFAT: Get a directry entry block */
+/*-----------------------------------*/
+
+static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */
+ DIR* dp /* Reading direcotry object pointing top of the entry block to load */
+)
+{
+ FRESULT res;
+ UINT i, sz_ent;
+ BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
+
+
+ /* Load file-directory entry */
+ res = move_window(dp->obj.fs, dp->sect);
+ if (res != FR_OK) return res;
+ if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order */
+ mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE);
+ sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE;
+ if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR;
+
+ /* Load stream-extension entry */
+ res = dir_next(dp, 0);
+ if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */
+ if (res != FR_OK) return res;
+ res = move_window(dp->obj.fs, dp->sect);
+ if (res != FR_OK) return res;
+ if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order */
+ mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE);
+ if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR;
+
+ /* Load file-name entries */
+ i = 2 * SZDIRE; /* Name offset to load */
+ do {
+ res = dir_next(dp, 0);
+ if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */
+ if (res != FR_OK) return res;
+ res = move_window(dp->obj.fs, dp->sect);
+ if (res != FR_OK) return res;
+ if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order */
+ if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE);
+ } while ((i += SZDIRE) < sz_ent);
+
+ /* Sanity check (do it for only accessible object) */
+ if (i <= MAXDIRB(FF_MAX_LFN)) {
+ if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR;
+ }
+ return FR_OK;
+}
+
+
+/*------------------------------------------------------------------*/
+/* exFAT: Initialize object allocation info with loaded entry block */
+/*------------------------------------------------------------------*/
+
+static void init_alloc_info (
+ FATFS* fs, /* Filesystem object */
+ FFOBJID* obj /* Object allocation information to be initialized */
+)
+{
+ obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */
+ obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */
+ obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */
+ obj->n_frag = 0; /* No last fragment info */
+}
+
+
+
+#if !FF_FS_READONLY || FF_FS_RPATH != 0
+/*------------------------------------------------*/
+/* exFAT: Load the object's directory entry block */
+/*------------------------------------------------*/
+
+static FRESULT load_obj_xdir (
+ DIR* dp, /* Blank directory object to be used to access containing direcotry */
+ const FFOBJID* obj /* Object with its containing directory information */
+)
+{
+ FRESULT res;
+
+ /* Open object containing directory */
+ dp->obj.fs = obj->fs;
+ dp->obj.sclust = obj->c_scl;
+ dp->obj.stat = (BYTE)obj->c_size;
+ dp->obj.objsize = obj->c_size & 0xFFFFFF00;
+ dp->obj.n_frag = 0;
+ dp->blk_ofs = obj->c_ofs;
+
+ res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */
+ if (res == FR_OK) {
+ res = load_xdir(dp); /* Load the object's entry block */
+ }
+ return res;
+}
+#endif
+
+
+#if !FF_FS_READONLY
+/*----------------------------------------*/
+/* exFAT: Store the directory entry block */
+/*----------------------------------------*/
+
+static FRESULT store_xdir (
+ DIR* dp /* Pointer to the direcotry object */
+)
+{
+ FRESULT res;
+ UINT nent;
+ BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */
+
+ /* Create set sum */
+ st_word(dirb + XDIR_SetSum, xdir_sum(dirb));
+ nent = dirb[XDIR_NumSec] + 1;
+
+ /* Store the direcotry entry block to the directory */
+ res = dir_sdi(dp, dp->blk_ofs);
+ while (res == FR_OK) {
+ res = move_window(dp->obj.fs, dp->sect);
+ if (res != FR_OK) break;
+ mem_cpy(dp->dir, dirb, SZDIRE);
+ dp->obj.fs->wflag = 1;
+ if (--nent == 0) break;
+ dirb += SZDIRE;
+ res = dir_next(dp, 0);
+ }
+ return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;
+}
+
+
+
+/*-------------------------------------------*/
+/* exFAT: Create a new directory enrty block */
+/*-------------------------------------------*/
+
+static void create_xdir (
+ BYTE* dirb, /* Pointer to the direcotry entry block buffer */
+ const WCHAR* lfn /* Pointer to the object name */
+)
+{
+ UINT i;
+ BYTE nc1, nlen;
+ WCHAR wc;
+
+
+ /* Create file-directory and stream-extension entry */
+ mem_set(dirb, 0, 2 * SZDIRE);
+ dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR;
+ dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM;
+
+ /* Create file-name entries */
+ i = SZDIRE * 2; /* Top of file_name entries */
+ nlen = nc1 = 0; wc = 1;
+ do {
+ dirb[i++] = ET_FILENAME; dirb[i++] = 0;
+ do { /* Fill name field */
+ if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */
+ st_word(dirb + i, wc); /* Store it */
+ i += 2;
+ } while (i % SZDIRE != 0);
+ nc1++;
+ } while (lfn[nlen]); /* Fill next entry if any char follows */
+
+ dirb[XDIR_NumName] = nlen; /* Set name length */
+ dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */
+ st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_FS_EXFAT */
+
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory */
+/*-----------------------------------------------------------------------*/
+
+#define DIR_READ_FILE(dp) dir_read(dp, 0)
+#define DIR_READ_LABEL(dp) dir_read(dp, 1)
+
+static FRESULT dir_read (
+ DIR* dp, /* Pointer to the directory object */
+ int vol /* Filtered by 0:file/directory or 1:volume label */
+)
+{
+ FRESULT res = FR_NO_FILE;
+ FATFS *fs = dp->obj.fs;
+ BYTE attr, b;
+#if FF_USE_LFN
+ BYTE ord = 0xFF, sum = 0xFF;
+#endif
+
+ while (dp->sect) {
+ res = move_window(fs, dp->sect);
+ if (res != FR_OK) break;
+ b = dp->dir[DIR_Name]; /* Test for the entry type */
+ if (b == 0) {
+ res = FR_NO_FILE; break; /* Reached to end of the directory */
+ }
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ if (FF_USE_LABEL && vol) {
+ if (b == ET_VLABEL) break; /* Volume label entry? */
+ } else {
+ if (b == ET_FILEDIR) { /* Start of the file entry block? */
+ dp->blk_ofs = dp->dptr; /* Get location of the block */
+ res = load_xdir(dp); /* Load the entry block */
+ if (res == FR_OK) {
+ dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */
+ }
+ break;
+ }
+ }
+ } else
+#endif
+ { /* On the FAT/FAT32 volume */
+ dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */
+#if FF_USE_LFN /* LFN configuration */
+ if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */
+ ord = 0xFF;
+ } else {
+ if (attr == AM_LFN) { /* An LFN entry is found */
+ if (b & LLEF) { /* Is it start of an LFN sequence? */
+ sum = dp->dir[LDIR_Chksum];
+ b &= (BYTE)~LLEF; ord = b;
+ dp->blk_ofs = dp->dptr;
+ }
+ /* Check LFN validity and capture it */
+ ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+ } else { /* An SFN entry is found */
+ if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */
+ dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */
+ }
+ break;
+ }
+ }
+#else /* Non LFN configuration */
+ if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */
+ break;
+ }
+#endif
+ }
+ res = dir_next(dp, 0); /* Next entry */
+ if (res != FR_OK) break;
+ }
+
+ if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */
+ return res;
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
+ DIR* dp /* Pointer to the directory object with the file name */
+)
+{
+ FRESULT res;
+ FATFS *fs = dp->obj.fs;
+ BYTE c;
+#if FF_USE_LFN
+ BYTE a, ord, sum;
+#endif
+
+ res = dir_sdi(dp, 0); /* Rewind directory object */
+ if (res != FR_OK) return res;
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ BYTE nc;
+ UINT di, ni;
+ WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */
+
+ while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */
+#if FF_MAX_LFN < 255
+ if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */
+#endif
+ if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */
+ for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */
+ if ((di % SZDIRE) == 0) di += 2;
+ if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break;
+ }
+ if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */
+ }
+ return res;
+ }
+#endif
+ /* On the FAT/FAT32 volume */
+#if FF_USE_LFN
+ ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
+#endif
+ do {
+ res = move_window(fs, dp->sect);
+ if (res != FR_OK) break;
+ c = dp->dir[DIR_Name];
+ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
+#if FF_USE_LFN /* LFN configuration */
+ dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;
+ if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */
+ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
+ } else {
+ if (a == AM_LFN) { /* An LFN entry is found */
+ if (!(dp->fn[NSFLAG] & NS_NOLFN)) {
+ if (c & LLEF) { /* Is it start of LFN sequence? */
+ sum = dp->dir[LDIR_Chksum];
+ c &= (BYTE)~LLEF; ord = c; /* LFN start order */
+ dp->blk_ofs = dp->dptr; /* Start offset of LFN */
+ }
+ /* Check validity of the LFN entry and compare it with given name */
+ ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+ }
+ } else { /* An SFN entry is found */
+ if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */
+ if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */
+ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
+ }
+ }
+#else /* Non LFN configuration */
+ dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
+ if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */
+#endif
+ res = dir_next(dp, 0); /* Next entry */
+ } while (res == FR_OK);
+
+ return res;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
+ DIR* dp /* Target directory with object name to be created */
+)
+{
+ FRESULT res;
+ FATFS *fs = dp->obj.fs;
+#if FF_USE_LFN /* LFN configuration */
+ UINT n, nlen, nent;
+ BYTE sn[12], sum;
+
+
+ if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
+ for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */
+
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
+ res = dir_alloc(dp, nent); /* Allocate directory entries */
+ if (res != FR_OK) return res;
+ dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */
+
+ if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */
+ dp->obj.stat &= ~4;
+ res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */
+ if (res != FR_OK) return res;
+ res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */
+ if (res != FR_OK) return res;
+ if (dp->obj.sclust != 0) { /* Is it a sub-directory? */
+ DIR dj;
+
+ res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */
+ if (res != FR_OK) return res;
+ dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */
+ st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */
+ st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);
+ fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1;
+ res = store_xdir(&dj); /* Store the object status */
+ if (res != FR_OK) return res;
+ }
+ }
+
+ create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */
+ return FR_OK;
+ }
+#endif
+ /* On the FAT/FAT32 volume */
+ mem_cpy(sn, dp->fn, 12);
+ if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */
+ dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */
+ for (n = 1; n < 100; n++) {
+ gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */
+ res = dir_find(dp); /* Check if the name collides with existing SFN */
+ if (res != FR_OK) break;
+ }
+ if (n == 100) return FR_DENIED; /* Abort if too many collisions */
+ if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */
+ dp->fn[NSFLAG] = sn[NSFLAG];
+ }
+
+ /* Create an SFN with/without LFNs. */
+ nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */
+ res = dir_alloc(dp, nent); /* Allocate entries */
+ if (res == FR_OK && --nent) { /* Set LFN entry if needed */
+ res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
+ if (res == FR_OK) {
+ sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */
+ do { /* Store LFN entries in bottom first */
+ res = move_window(fs, dp->sect);
+ if (res != FR_OK) break;
+ put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
+ fs->wflag = 1;
+ res = dir_next(dp, 0); /* Next entry */
+ } while (res == FR_OK && --nent);
+ }
+ }
+
+#else /* Non LFN configuration */
+ res = dir_alloc(dp, 1); /* Allocate an entry for SFN */
+
+#endif
+
+ /* Set SFN entry */
+ if (res == FR_OK) {
+ res = move_window(fs, dp->sect);
+ if (res == FR_OK) {
+ mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */
+ mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */
+#if FF_USE_LFN
+ dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */
+#endif
+ fs->wflag = 1;
+ }
+ }
+
+ return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
+ DIR* dp /* Directory object pointing the entry to be removed */
+)
+{
+ FRESULT res;
+ FATFS *fs = dp->obj.fs;
+#if FF_USE_LFN /* LFN configuration */
+ DWORD last = dp->dptr;
+
+ res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */
+ if (res == FR_OK) {
+ do {
+ res = move_window(fs, dp->sect);
+ if (res != FR_OK) break;
+ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */
+ } else { /* On the FAT/FAT32 volume */
+ dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */
+ }
+ fs->wflag = 1;
+ if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */
+ res = dir_next(dp, 0); /* Next entry */
+ } while (res == FR_OK);
+ if (res == FR_NO_FILE) res = FR_INT_ERR;
+ }
+#else /* Non LFN configuration */
+
+ res = move_window(fs, dp->sect);
+ if (res == FR_OK) {
+ dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/
+ fs->wflag = 1;
+ }
+#endif
+
+ return res;
+}
+
+#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
+
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry */
+/*-----------------------------------------------------------------------*/
+
+static void get_fileinfo (
+ DIR* dp, /* Pointer to the directory object */
+ FILINFO* fno /* Pointer to the file information to be filled */
+)
+{
+ UINT si, di;
+#if FF_USE_LFN
+ BYTE lcf;
+ WCHAR wc, hs;
+ FATFS *fs = dp->obj.fs;
+#else
+ TCHAR c;
+#endif
+
+
+ fno->fname[0] = 0; /* Invaidate file info */
+ if (dp->sect == 0) return; /* Exit if read pointer has reached end of directory */
+
+#if FF_USE_LFN /* LFN configuration */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ get_xfileinfo(fs->dirbuf, fno);
+ return;
+ } else
+#endif
+ { /* On the FAT/FAT32 volume */
+ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */
+ si = di = hs = 0;
+ while (fs->lfnbuf[si] != 0) {
+ wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */
+ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
+ hs = wc; continue; /* Get low surrogate */
+ }
+ wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */
+ if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */
+ di += wc;
+ hs = 0;
+ }
+ if (hs != 0) di = 0; /* Broken surrogate pair? */
+ fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */
+ }
+ }
+
+ si = di = 0;
+ while (si < 11) { /* Get SFN from SFN entry */
+ wc = dp->dir[si++]; /* Get a char */
+ if (wc == ' ') continue; /* Skip padding spaces */
+ if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */
+ if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */
+#if FF_LFN_UNICODE >= 1 /* Unicode output */
+ if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */
+ wc = wc << 8 | dp->dir[si++];
+ }
+ wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */
+ if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */
+ wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */
+ if (wc == 0) { di = 0; break; } /* Buffer overflow? */
+ di += wc;
+#else /* ANSI/OEM output */
+ fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */
+#endif
+ }
+ fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */
+
+ if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
+ if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */
+ fno->fname[di++] = '?';
+ } else {
+ for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */
+ wc = (WCHAR)fno->altname[si];
+ if (wc == '.') lcf = NS_EXT;
+ if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20;
+ fno->fname[di] = (TCHAR)wc;
+ }
+ }
+ fno->fname[di] = 0; /* Terminate the LFN */
+ if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */
+ }
+
+#else /* Non-LFN configuration */
+ si = di = 0;
+ while (si < 11) { /* Copy name body and extension */
+ c = (TCHAR)dp->dir[si++];
+ if (c == ' ') continue; /* Skip padding spaces */
+ if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */
+ if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */
+ fno->fname[di++] = c;
+ }
+ fno->fname[di] = 0;
+#endif
+
+ fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */
+ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */
+ fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */
+ fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+
+#if FF_USE_FIND && FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Pattern matching */
+/*-----------------------------------------------------------------------*/
+
+static DWORD get_achar ( /* Get a character and advances ptr */
+ const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
+)
+{
+ DWORD chr;
+
+
+#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
+ chr = tchar2uni(ptr);
+ if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */
+ chr = ff_wtoupper(chr);
+
+#else /* ANSI/OEM input */
+ chr = (BYTE)*(*ptr)++; /* Get a byte */
+ if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */
+#if FF_CODE_PAGE == 0
+ if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */
+#elif FF_CODE_PAGE < 900
+ if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */
+#endif
+#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
+ if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */
+ chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0;
+ }
+#endif
+
+#endif
+ return chr;
+}
+
+
+static int pattern_matching ( /* 0:not matched, 1:matched */
+ const TCHAR* pat, /* Matching pattern */
+ const TCHAR* nam, /* String to be tested */
+ int skip, /* Number of pre-skip chars (number of ?s) */
+ int inf /* Infinite search (* specified) */
+)
+{
+ const TCHAR *pp, *np;
+ DWORD pc, nc;
+ int nm, nx;
+
+
+ while (skip--) { /* Pre-skip name chars */
+ if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
+ }
+ if (*pat == 0 && inf) return 1; /* (short circuit) */
+
+ do {
+ pp = pat; np = nam; /* Top of pattern and name to match */
+ for (;;) {
+ if (*pp == '?' || *pp == '*') { /* Wildcard? */
+ nm = nx = 0;
+ do { /* Analyze the wildcard block */
+ if (*pp++ == '?') nm++; else nx = 1;
+ } while (*pp == '?' || *pp == '*');
+ if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
+ nc = *np; break; /* Branch mismatched */
+ }
+ pc = get_achar(&pp); /* Get a pattern char */
+ nc = get_achar(&np); /* Get a name char */
+ if (pc != nc) break; /* Branch mismatched? */
+ if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */
+ }
+ get_achar(&nam); /* nam++ */
+ } while (inf && nc); /* Retry until end of name if infinite search is specified */
+
+ return 0;
+}
+
+#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a top segment and create the object name in directory form */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
+ DIR* dp, /* Pointer to the directory object */
+ const TCHAR** path /* Pointer to pointer to the segment in the path string */
+)
+{
+#if FF_USE_LFN /* LFN configuration */
+ BYTE b, cf;
+ WCHAR wc, *lfn;
+ DWORD uc;
+ UINT i, ni, si, di;
+ const TCHAR *p;
+
+
+ /* Create LFN into LFN working buffer */
+ p = *path; lfn = dp->obj.fs->lfnbuf; di = 0;
+ for (;;) {
+ uc = tchar2uni(&p); /* Get a character */
+ if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */
+ if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */
+ wc = (WCHAR)uc;
+ if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */
+ if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */
+ if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */
+ lfn[di++] = wc; /* Store the Unicode character */
+ }
+ while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */
+ *path = p; /* Return pointer to the next segment */
+ cf = (wc < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */
+
+#if FF_FS_RPATH != 0
+ if ((di == 1 && lfn[di - 1] == '.') ||
+ (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */
+ lfn[di] = 0;
+ for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */
+ dp->fn[i] = (i < di) ? '.' : ' ';
+ }
+ dp->fn[i] = cf | NS_DOT; /* This is a dot entry */
+ return FR_OK;
+ }
+#endif
+ while (di) { /* Snip off trailing spaces and dots if exist */
+ wc = lfn[di - 1];
+ if (wc != ' ' && wc != '.') break;
+ di--;
+ }
+ lfn[di] = 0; /* LFN is created into the working buffer */
+ if (di == 0) return FR_INVALID_NAME; /* Reject null name */
+
+ /* Create SFN in directory form */
+ for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */
+ if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */
+ while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */
+
+ mem_set(dp->fn, ' ', 11);
+ i = b = 0; ni = 8;
+ for (;;) {
+ wc = lfn[si++]; /* Get an LFN character */
+ if (wc == 0) break; /* Break on end of the LFN */
+ if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */
+ cf |= NS_LOSS | NS_LFN;
+ continue;
+ }
+
+ if (i >= ni || si == di) { /* End of field? */
+ if (ni == 11) { /* Name extension overflow? */
+ cf |= NS_LOSS | NS_LFN;
+ break;
+ }
+ if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */
+ if (si > di) break; /* No name extension? */
+ si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */
+ continue;
+ }
+
+ if (wc >= 0x80) { /* Is this a non-ASCII character? */
+ cf |= NS_LFN; /* LFN entry needs to be created */
+#if FF_CODE_PAGE == 0
+ if (ExCvt) { /* At SBCS */
+ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
+ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
+ } else { /* At DBCS */
+ wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */
+ }
+#elif FF_CODE_PAGE < 900 /* SBCS cfg */
+ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
+ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
+#else /* DBCS cfg */
+ wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */
+#endif
+ }
+
+ if (wc >= 0x100) { /* Is this a DBC? */
+ if (i >= ni - 1) { /* Field overflow? */
+ cf |= NS_LOSS | NS_LFN;
+ i = ni; continue; /* Next field */
+ }
+ dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */
+ } else { /* SBC */
+ if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */
+ wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+ } else {
+ if (IsUpper(wc)) { /* ASCII upper case? */
+ b |= 2;
+ }
+ if (IsLower(wc)) { /* ASCII lower case? */
+ b |= 1; wc -= 0x20;
+ }
+ }
+ }
+ dp->fn[i++] = (BYTE)wc;
+ }
+
+ if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
+
+ if (ni == 8) b <<= 2; /* Shift capital flags if no extension */
+ if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */
+ if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
+ if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */
+ if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */
+ }
+
+ dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */
+
+ return FR_OK;
+
+
+#else /* FF_USE_LFN : Non-LFN configuration */
+ BYTE c, d, *sfn;
+ UINT ni, si, i;
+ const char *p;
+
+ /* Create file name in directory form */
+ p = *path; sfn = dp->fn;
+ mem_set(sfn, ' ', 11);
+ si = i = 0; ni = 8;
+#if FF_FS_RPATH != 0
+ if (p[si] == '.') { /* Is this a dot entry? */
+ for (;;) {
+ c = (BYTE)p[si++];
+ if (c != '.' || si >= 3) break;
+ sfn[i++] = c;
+ }
+ if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+ *path = p + si; /* Return pointer to the next segment */
+ sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */
+ return FR_OK;
+ }
+#endif
+ for (;;) {
+ c = (BYTE)p[si++]; /* Get a byte */
+ if (c <= ' ') break; /* Break if end of the path name */
+ if (c == '/' || c == '\\') { /* Break if a separator is found */
+ while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */
+ break;
+ }
+ if (c == '.' || i >= ni) { /* End of body or field overflow? */
+ if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */
+ i = 8; ni = 11; /* Enter file extension field */
+ continue;
+ }
+#if FF_CODE_PAGE == 0
+ if (ExCvt && c >= 0x80) { /* Is SBC extended character? */
+ c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
+ }
+#elif FF_CODE_PAGE < 900
+ if (c >= 0x80) { /* Is SBC extended character? */
+ c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
+ }
+#endif
+ if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */
+ d = (BYTE)p[si++]; /* Get 2nd byte */
+ if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */
+ sfn[i++] = c;
+ sfn[i++] = d;
+ } else { /* SBC */
+ if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
+ if (IsLower(c)) c -= 0x20; /* To upper */
+ sfn[i++] = c;
+ }
+ }
+ *path = p + si; /* Return pointer to the next segment */
+ if (i == 0) return FR_INVALID_NAME; /* Reject nul string */
+
+ if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
+ sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */
+
+ return FR_OK;
+#endif /* FF_USE_LFN */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
+ DIR* dp, /* Directory object to return last directory and found object */
+ const TCHAR* path /* Full-path string to find a file or directory */
+)
+{
+ FRESULT res;
+ BYTE ns;
+ FATFS *fs = dp->obj.fs;
+
+
+#if FF_FS_RPATH != 0
+ if (*path != '/' && *path != '\\') { /* Without heading separator */
+ dp->obj.sclust = fs->cdir; /* Start from current directory */
+ } else
+#endif
+ { /* With heading separator */
+ while (*path == '/' || *path == '\\') path++; /* Strip heading separator */
+ dp->obj.sclust = 0; /* Start from root directory */
+ }
+#if FF_FS_EXFAT
+ dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */
+#if FF_FS_RPATH != 0
+ if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: Retrieve the sub-directory's status */
+ DIR dj;
+
+ dp->obj.c_scl = fs->cdc_scl;
+ dp->obj.c_size = fs->cdc_size;
+ dp->obj.c_ofs = fs->cdc_ofs;
+ res = load_obj_xdir(&dj, &dp->obj);
+ if (res != FR_OK) return res;
+ dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize);
+ dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+ }
+#endif
+#endif
+
+ if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */
+ dp->fn[NSFLAG] = NS_NONAME;
+ res = dir_sdi(dp, 0);
+
+ } else { /* Follow path */
+ for (;;) {
+ res = create_name(dp, &path); /* Get a segment name of the path */
+ if (res != FR_OK) break;
+ res = dir_find(dp); /* Find an object with the segment name */
+ ns = dp->fn[NSFLAG];
+ if (res != FR_OK) { /* Failed to find the object */
+ if (res == FR_NO_FILE) { /* Object is not found */
+ if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */
+ if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */
+ dp->fn[NSFLAG] = NS_NONAME;
+ res = FR_OK;
+ } else { /* Could not find the object */
+ if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */
+ }
+ }
+ break;
+ }
+ if (ns & NS_LAST) break; /* Last segment matched. Function completed. */
+ /* Get into the sub-directory */
+ if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */
+ res = FR_NO_PATH; break;
+ }
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */
+ dp->obj.c_scl = dp->obj.sclust;
+ dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
+ dp->obj.c_ofs = dp->blk_ofs;
+ init_alloc_info(fs, &dp->obj); /* Open next directory */
+ } else
+#endif
+ {
+ dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */
+ }
+ }
+ }
+
+ return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get logical drive number from path name */
+/*-----------------------------------------------------------------------*/
+
+static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
+ const TCHAR** path /* Pointer to pointer to the path name */
+)
+{
+ const TCHAR *tp, *tt;
+ TCHAR tc;
+ int i, vol = -1;
+#if FF_STR_VOLUME_ID /* Find string volume ID */
+ const char *sp;
+ char c;
+#endif
+
+ tt = tp = *path;
+ if (!tp) return vol; /* Invalid path name? */
+ do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */
+
+ if (tc == ':') { /* DOS/Windows style volume ID? */
+ i = FF_VOLUMES;
+ if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */
+ i = (int)*tp - '0'; /* Get the LD number */
+ }
+#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
+ else {
+ i = 0;
+ do {
+ sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
+ do { /* Compare the volume ID with path name */
+ c = *sp++; tc = *tp++;
+ if (IsLower(c)) c -= 0x20;
+ if (IsLower(tc)) tc -= 0x20;
+ } while (c && (TCHAR)c == tc);
+ } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
+ }
+#endif
+ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
+ vol = i; /* Drive number */
+ *path = tt; /* Snip the drive prefix off */
+ }
+ return vol;
+ }
+#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
+ if (*tp == '/') {
+ i = 0;
+ do {
+ sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
+ do { /* Compare the volume ID with path name */
+ c = *sp++; tc = *(++tp);
+ if (IsLower(c)) c -= 0x20;
+ if (IsLower(tc)) tc -= 0x20;
+ } while (c && (TCHAR)c == tc);
+ } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
+ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
+ vol = i; /* Drive number */
+ *path = tp; /* Snip the drive prefix off */
+ return vol;
+ }
+ }
+#endif
+ /* No drive prefix is found */
+#if FF_FS_RPATH != 0
+ vol = CurrVol; /* Default drive is current drive */
+#else
+ vol = 0; /* Default drive is 0 */
+#endif
+ return vol; /* Return the default drive */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT VBR */
+/*-----------------------------------------------------------------------*/
+
+static BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */
+ FATFS* fs, /* Filesystem object */
+ DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR or not */
+)
+{
+ fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */
+ if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */
+
+ if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always here regardless of the sector size) */
+
+#if FF_FS_EXFAT
+ if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */
+#endif
+ if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */
+ if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */
+ if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */
+ }
+ return 2; /* Valid BS but not FAT */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Determine logical drive number and mount the volume if needed */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred */
+ const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
+ FATFS** rfs, /* Pointer to pointer to the found filesystem object */
+ BYTE mode /* !=0: Check write protection for write access */
+)
+{
+ BYTE fmt, *pt;
+ int vol;
+ DSTATUS stat;
+ DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];
+ WORD nrsv;
+ FATFS *fs;
+ UINT i;
+
+
+ /* Get logical drive number */
+ *rfs = 0;
+ vol = get_ldnumber(path);
+ if (vol < 0) return FR_INVALID_DRIVE;
+
+ /* Check if the filesystem object is valid or not */
+ fs = FatFs[vol]; /* Get pointer to the filesystem object */
+ if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
+#if FF_FS_REENTRANT
+ if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */
+#endif
+ *rfs = fs; /* Return pointer to the filesystem object */
+
+ mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */
+ if (fs->fs_type != 0) { /* If the volume has been mounted */
+ stat = disk_status(fs->pdrv);
+ if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
+ if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */
+ return FR_WRITE_PROTECTED;
+ }
+ return FR_OK; /* The filesystem object is valid */
+ }
+ }
+
+ /* The filesystem object is not valid. */
+ /* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */
+
+ fs->fs_type = 0; /* Clear the filesystem object */
+ fs->part_type = 0; /* Clear the Partition object */
+ fs->pdrv = LD2PD(vol); /* Bind the logical drive and a physical drive */
+ stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */
+ if (stat & STA_NOINIT) { /* Check if the initialization succeeded */
+ return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */
+ }
+ if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
+ return FR_WRITE_PROTECTED;
+ }
+#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
+ if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
+ if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
+#endif
+
+ /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK (MBR) and SFD (w/o partition). */
+ bsect = 0;
+ fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */
+ if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */
+ for (i = 0; i < 4; i++) { /* Get partition offset */
+ pt = fs->win + (MBR_Table + i * SZ_PTE);
+ br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0;
+ }
+ i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */
+ if (i != 0) i--;
+ do { /* Find an FAT volume */
+ bsect = br[i];
+ fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */
+ } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);
+ }
+ if (fmt == 4) {
+ EFSPRINTF("BRNL");
+ return FR_DISK_ERR; /* An error occured in the disk I/O layer */
+ }
+#if FF_SIMPLE_GPT
+ if (fmt >= 2) {
+ /* If GPT Check the first partition */
+ gpt_header_t *gpt_header = (gpt_header_t *)fs->win;
+ if (move_window(fs, 1) != FR_OK) return FR_DISK_ERR;
+ if (!mem_cmp(&gpt_header->signature, "EFI PART", 8)) {
+ if (move_window(fs, gpt_header->part_ent_lba) != FR_OK) return FR_DISK_ERR;
+ gpt_entry_t *gpt_entry = (gpt_entry_t *)fs->win;
+ fs->part_type = 1;
+ bsect = gpt_entry->lba_start;
+ fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */
+ }
+ }
+#endif
+ if (fmt >= 2) {
+ EFSPRINTF("NOFAT");
+ return FR_NO_FILESYSTEM; /* No FAT volume is found */
+ }
+
+ /* An FAT volume is found (bsect). Following code initializes the filesystem object */
+
+#if FF_FS_EXFAT
+ if (fmt == 1) {
+ QWORD maxlba;
+ DWORD so, cv, bcl;
+
+ for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
+ if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
+
+ if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */
+
+ if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
+ EFSPRINTF("EXSPS");
+ return FR_NO_FILESYSTEM;
+ }
+
+ maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */
+ if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */
+
+ fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */
+
+ fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */
+ if (fs->n_fats != 1) {
+ EFSPRINTF("EXFNF");
+ return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */
+ }
+
+ fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */
+ if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */
+
+ nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */
+ if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */
+ fs->n_fatent = nclst + 2;
+
+ /* Boundaries and Limits */
+ fs->volbase = bsect;
+ fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
+ fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
+ if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
+ fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
+
+ /* Get bitmap location and check if it is contiguous (implementation assumption) */
+ so = i = 0;
+ for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
+ if (i == 0) {
+ if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */
+ if (move_window(fs, clst2sect(fs, fs->dirbase) + so) != FR_OK) {
+ EFSPRINTF("EXBM1C");
+ return FR_DISK_ERR;
+ }
+ so++;
+ }
+ if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */
+ i = (i + SZDIRE) % SS(fs); /* Next entry */
+ }
+ bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */
+ if (bcl < 2 || bcl >= fs->n_fatent) {
+ EFSPRINTF("EXBMM");
+ return FR_NO_FILESYSTEM;
+ }
+ fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */
+ for (;;) { /* Check if bitmap is contiguous */
+ if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
+ cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4);
+ if (cv == 0xFFFFFFFF) break; /* Last link? */
+ if (cv != ++bcl) {
+ EFSPRINTF("EXBMM");
+ return FR_NO_FILESYSTEM; /* Fragmented? */
+ }
+ }
+
+#if !FF_FS_READONLY
+ fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
+#endif
+ fmt = FS_EXFAT; /* FAT sub-type */
+ } else
+#endif /* FF_FS_EXFAT */
+ {
+ if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) {
+ EFSPRINTF("32SPS");
+ return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */
+ }
+
+ fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */
+ if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
+ fs->fsize = fasize;
+
+ fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */
+ if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
+ fasize *= fs->n_fats; /* Number of sectors for FAT area */
+
+ fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */
+ if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */
+
+ fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */
+ if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */
+
+ tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */
+ if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
+
+ nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */
+ if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */
+
+ /* Determine the FAT sub type */
+ sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */
+ if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
+ nclst = (tsect - sysect) / fs->csize; /* Number of clusters */
+ if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
+ fmt = 0;
+ if (nclst <= MAX_FAT32) fmt = FS_FAT32;
+ if (nclst <= MAX_FAT16) fmt = FS_FAT16;
+ if (nclst <= MAX_FAT12) fmt = FS_FAT12;
+ if (fmt == 0) return FR_NO_FILESYSTEM;
+
+ /* Boundaries and Limits */
+ fs->n_fatent = nclst + 2; /* Number of FAT entries */
+ fs->volbase = bsect; /* Volume start sector */
+ fs->fatbase = bsect + nrsv; /* FAT start sector */
+ fs->database = bsect + sysect; /* Data start sector */
+ if (fmt == FS_FAT32) {
+ if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
+ if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
+ fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */
+ szbfat = fs->n_fatent * 4; /* (Needed FAT size) */
+ } else {
+ if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
+ fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
+ szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */
+ fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+ }
+ if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */
+
+#if !FF_FS_READONLY
+ /* Get FSInfo if available */
+ fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
+ fs->fsi_flag = 0x80;
+#if (FF_FS_NOFSINFO & 3) != 3
+ if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
+ && ld_word(fs->win + BPB_FSInfo32) == 1
+ && move_window(fs, bsect + 1) == FR_OK)
+ {
+ fs->fsi_flag = 0;
+ if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */
+ && ld_dword(fs->win + FSI_LeadSig) == 0x41615252
+ && ld_dword(fs->win + FSI_StrucSig) == 0x61417272)
+ {
+#if (FF_FS_NOFSINFO & 1) == 0
+ fs->free_clst = ld_dword(fs->win + FSI_Free_Count);
+#endif
+#if (FF_FS_NOFSINFO & 2) == 0
+ fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);
+#endif
+ }
+ }
+#endif /* (FF_FS_NOFSINFO & 3) != 3 */
+#endif /* !FF_FS_READONLY */
+ }
+
+ fs->fs_type = fmt; /* FAT sub-type */
+ fs->id = ++Fsid; /* Volume mount ID */
+#if FF_USE_LFN == 1
+ fs->lfnbuf = LfnBuf; /* Static LFN working buffer */
+#if FF_FS_EXFAT
+ fs->dirbuf = DirBuf; /* Static directory block scratchpad buffer */
+#endif
+#endif
+#if FF_FS_RPATH != 0
+ fs->cdir = 0; /* Initialize current directory */
+#endif
+#if FF_FS_LOCK != 0 /* Clear file lock semaphores */
+ clear_lock(fs);
+#endif
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/directory object is valid or not */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
+ FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
+ FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */
+)
+{
+ FRESULT res = FR_INVALID_OBJECT;
+
+
+ if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */
+#if FF_FS_REENTRANT
+ if (lock_fs(obj->fs)) { /* Obtain the filesystem object */
+ if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
+ res = FR_OK;
+ } else {
+ unlock_fs(obj->fs, FR_OK);
+ }
+ } else {
+ res = FR_TIMEOUT;
+ }
+#else
+ if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
+ res = FR_OK;
+ }
+#endif
+ }
+ *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */
+ return res;
+}
+
+
+
+
+/*---------------------------------------------------------------------------
+
+ Public Functions (FatFs API)
+
+----------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+ FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/
+ const TCHAR* path, /* Logical drive number to be mounted/unmounted */
+ BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
+)
+{
+ FATFS *cfs;
+ int vol;
+ FRESULT res;
+ const TCHAR *rp = path;
+
+
+ /* Get logical drive number */
+ vol = get_ldnumber(&rp);
+ if (vol < 0) {
+ EFSPRINTF("IDRIVE!");
+ return FR_INVALID_DRIVE;
+ }
+ cfs = FatFs[vol]; /* Pointer to fs object */
+
+ if (cfs) {
+#if FF_FS_LOCK != 0
+ clear_lock(cfs);
+#endif
+#if FF_FS_REENTRANT /* Discard sync object of the current volume */
+ if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
+#endif
+ cfs->fs_type = 0; /* Clear old fs object */
+ }
+
+ if (fs) {
+ fs->fs_type = 0; /* Clear new fs object */
+#if FF_FS_REENTRANT /* Create sync object for the new volume */
+ if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+ }
+ FatFs[vol] = fs; /* Register new fs object */
+
+ if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted later */
+
+ res = find_volume(&path, &fs, 0); /* Force mounted the volume */
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+ FIL* fp, /* Pointer to the blank file object */
+ const TCHAR* path, /* Pointer to the file name */
+ BYTE mode /* Access mode and file open mode flags */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+#if !FF_FS_READONLY
+ DWORD dw, cl, bcs, clst, sc;
+ FSIZE_t ofs;
+#endif
+ DEF_NAMBUF
+
+
+ if (!fp) return FR_INVALID_OBJECT;
+
+ /* Get logical drive number */
+ mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
+ res = find_volume(&path, &fs, mode);
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&dj, path); /* Follow the file path */
+#if !FF_FS_READONLY /* Read/Write configuration */
+ if (res == FR_OK) {
+ if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */
+ res = FR_INVALID_NAME;
+ }
+#if FF_FS_LOCK != 0
+ else {
+ res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Check if the file can be used */
+ }
+#endif
+ }
+ /* Create or Open a file */
+ if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+ if (res != FR_OK) { /* No file, create new */
+ if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */
+#if FF_FS_LOCK != 0
+ res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+ res = dir_register(&dj);
+#endif
+ }
+ mode |= FA_CREATE_ALWAYS; /* File is created */
+ }
+ else { /* Any object with the same name is already existing */
+ if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */
+ res = FR_DENIED;
+ } else {
+ if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */
+ }
+ }
+ if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ /* Get current allocation info */
+ fp->obj.fs = fs;
+ init_alloc_info(fs, &fp->obj);
+ /* Set directory entry block initial state */
+ mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */
+ mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */
+ fs->dirbuf[XDIR_Attr] = AM_ARC;
+ st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME());
+ fs->dirbuf[XDIR_GenFlags] = 1;
+ res = store_xdir(&dj);
+ if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */
+ res = remove_chain(&fp->obj, fp->obj.sclust, 0);
+ fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */
+ }
+ } else
+#endif
+ {
+ /* Set directory entry initial state */
+ cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
+ st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */
+ dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */
+ st_clust(fs, dj.dir, 0); /* Reset file allocation info */
+ st_dword(dj.dir + DIR_FileSize, 0);
+ fs->wflag = 1;
+ if (cl != 0) { /* Remove the cluster chain if exist */
+ dw = fs->winsect;
+ res = remove_chain(&dj.obj, cl, 0);
+ if (res == FR_OK) {
+ res = move_window(fs, dw);
+ fs->last_clst = cl - 1; /* Reuse the cluster hole */
+ }
+ }
+ }
+ }
+ }
+ else { /* Open an existing file */
+ if (res == FR_OK) { /* Is the object exsiting? */
+ if (dj.obj.attr & AM_DIR) { /* File open against a directory */
+ res = FR_NO_FILE;
+ } else {
+ if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */
+ res = FR_DENIED;
+ }
+ }
+ }
+ }
+ if (res == FR_OK) {
+ if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* Set file change flag if created or overwritten */
+ fp->dir_sect = fs->winsect; /* Pointer to the directory entry */
+ fp->dir_ptr = dj.dir;
+#if FF_FS_LOCK != 0
+ fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */
+ if (fp->obj.lockid == 0) res = FR_INT_ERR;
+#endif
+ }
+#else /* R/O configuration */
+ if (res == FR_OK) {
+ if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin directory itself? */
+ res = FR_INVALID_NAME;
+ } else {
+ if (dj.obj.attr & AM_DIR) { /* Is it a directory? */
+ res = FR_NO_FILE;
+ }
+ }
+ }
+#endif
+
+ if (res == FR_OK) {
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */
+ fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
+ fp->obj.c_ofs = dj.blk_ofs;
+ init_alloc_info(fs, &fp->obj);
+ } else
+#endif
+ {
+ fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */
+ fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);
+ }
+#if FF_USE_FASTSEEK
+ fp->cltbl = 0; /* Disable fast seek mode */
+#endif
+ fp->obj.fs = fs; /* Validate the file object */
+ fp->obj.id = fs->id;
+ fp->flag = mode; /* Set file access mode */
+ fp->err = 0; /* Clear error flag */
+ fp->sect = 0; /* Invalidate current data sector */
+ fp->fptr = 0; /* Set file pointer top of the file */
+#if !FF_FS_READONLY
+#if !FF_FS_TINY
+ mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */
+#endif
+ if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */
+ fp->fptr = fp->obj.objsize; /* Offset to seek */
+ bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */
+ clst = fp->obj.sclust; /* Follow the cluster chain */
+ for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {
+ clst = get_fat(&fp->obj, clst);
+ if (clst <= 1) res = FR_INT_ERR;
+ if (clst == 0xFFFFFFFF) res = FR_DISK_ERR;
+ }
+ fp->clust = clst;
+ if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */
+ if ((sc = clst2sect(fs, clst)) == 0) {
+ res = FR_INT_ERR;
+ } else {
+ fp->sect = sc + (DWORD)(ofs / SS(fs));
+#if !FF_FS_TINY
+ if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;
+#endif
+ }
+ }
+ }
+#endif
+ }
+
+ FREE_NAMBUF();
+ }
+
+ if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+ FIL* fp, /* Pointer to the file object */
+ void* buff, /* Pointer to data buffer */
+ UINT btr, /* Number of bytes to read */
+ UINT* br /* Pointer to number of bytes read */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD clst, sect;
+ FSIZE_t remain;
+ UINT rcnt, cc, csect;
+ BYTE *rbuff = (BYTE*)buff;
+
+ UINT br_tmp;
+ if (!br)
+ br = &br_tmp;
+ *br = 0; /* Clear read byte counter */
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
+ EFSPRINTF("FOV");
+ LEAVE_FF(fs, res); /* Check validity */
+ }
+ if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+ remain = fp->obj.objsize - fp->fptr;
+ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
+
+ for ( ; btr; /* Repeat until btr bytes read */
+ btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) {
+ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
+ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
+ if (csect == 0) { /* On the cluster boundary? */
+ if (fp->fptr == 0) { /* On the top of the file? */
+ clst = fp->obj.sclust; /* Follow cluster chain from the origin */
+ } else { /* Middle or end of the file */
+#if FF_USE_FASTSEEK
+ if (fp->cltbl) {
+ clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+ } else
+#endif
+ {
+ clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */
+ }
+ }
+ if (clst < 2) {
+ EFSPRINTF("CCHK");
+ ABORT(fs, FR_INT_ERR);
+ }
+ if (clst == 0xFFFFFFFF) {
+ EFSPRINTF("DSKC");
+ ABORT(fs, FR_DISK_ERR);
+ }
+ fp->clust = clst; /* Update current cluster */
+ }
+ sect = clst2sect(fs, fp->clust); /* Get current sector */
+ if (sect == 0) ABORT(fs, FR_INT_ERR);
+ sect += csect;
+ cc = btr / SS(fs); /* When remaining bytes >= sector size, */
+ if (cc > 0) { /* Read maximum contiguous sectors directly */
+ if (csect + cc > fs->csize) { /* Clip at cluster boundary */
+ cc = fs->csize - csect;
+ }
+ if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) {
+ EFSPRINTF("RLIO");
+ ABORT(fs, FR_DISK_ERR);
+ }
+#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if FF_FS_TINY
+ if (fs->wflag && fs->winsect - sect < cc) {
+ mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
+ }
+#else
+ if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
+ mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
+ }
+#endif
+#endif
+ rcnt = SS(fs) * cc; /* Number of bytes transferred */
+ continue;
+ }
+#if !FF_FS_TINY
+ if (fp->sect != sect) { /* Load data sector if not in cache */
+#if !FF_FS_READONLY
+ if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
+ EFSPRINTF("RDC");
+ ABORT(fs, FR_DISK_ERR);
+ }
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
+ EFSPRINTF("RSC");
+ ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
+ }
+ }
+#endif
+ fp->sect = sect;
+ }
+ rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
+ if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */
+#if FF_FS_TINY
+ if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
+ mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
+#else
+ mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
+#endif
+ }
+
+ LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+#if FF_FASTFS && FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* Fast Read Aligned Sized File Without a Cache */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read_fast (
+ FIL* fp, /* Pointer to the file object */
+ const void* buff, /* Pointer to the data to be written */
+ UINT btr /* Number of bytes to read */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ UINT csize_bytes, count;
+ DWORD clst, work_bytes;
+ FSIZE_t work_sector = 0;
+ FSIZE_t sector_base = 0;
+ BYTE *wbuff = (BYTE*)buff;
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
+ EFSPRINTF("FOV");
+ LEAVE_FF(fs, res); /* Check validity */
+ }
+
+ if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+ FSIZE_t remain = fp->obj.objsize - fp->fptr;
+ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
+ if (!btr)
+ goto out;
+
+ csize_bytes = fs->csize * SS(fs);
+
+ //!TODO: support sector reading inside a cluster
+ DWORD csect = (UINT)((fp->fptr / SS(fs)) & (fs->csize - 1)); /* Sector offset in the cluster */
+ if (csect) { EFSPRINTF("ICSR"); ABORT(fs, FR_INT_ERR); }
+
+ if (!fp->fptr) { /* On the top of the file? */
+ clst = fp->obj.sclust; /* Follow from the origin */
+ } else {
+ if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+ else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
+ }
+ if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst; /* Set working cluster */
+
+ sector_base = clst2sect(fs, fp->clust);
+ work_bytes = MIN(btr, csize_bytes);
+ count = work_bytes / SS(fs);
+ fp->fptr += work_bytes;
+ btr -= work_bytes;
+
+ while (btr) {
+ clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+
+ if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst;
+
+ work_sector = clst2sect(fs, fp->clust);
+ work_bytes = MIN(btr, csize_bytes);
+ if ((work_sector - sector_base) == count) count += work_bytes / SS(fs);
+ else {
+ if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ wbuff += count * SS(fs);
+
+ sector_base = work_sector;
+ count = work_bytes / SS(fs);
+ }
+
+ fp->fptr += work_bytes;
+ btr -= work_bytes;
+ }
+
+ if (!btr) { /* Final cluster/sectors read. */
+ if (work_bytes % SS(fs))
+ count++; /* Read an extra block. Expects buffer being big enough. */
+ if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ }
+
+out:
+ LEAVE_FF(fs, FR_OK);
+}
+#endif
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+ FIL* fp, /* Pointer to the file object */
+ const void* buff, /* Pointer to the data to be written */
+ UINT btw, /* Number of bytes to write */
+ UINT* bw /* Pointer to number of bytes written */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD clst, sect;
+ UINT wcnt, cc, csect;
+ const BYTE *wbuff = (const BYTE*)buff;
+
+ UINT bw_tmp;
+ if (!bw)
+ bw = &bw_tmp;
+ *bw = 0; /* Clear write byte counter */
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
+ EFSPRINTF("FOV");
+ LEAVE_FF(fs, res); /* Check validity */
+ }
+ if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+
+ /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
+ if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
+ btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
+ }
+
+ for ( ; btw; /* Repeat until all data written */
+ btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) {
+ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
+ csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */
+ if (csect == 0) { /* On the cluster boundary? */
+ if (fp->fptr == 0) { /* On the top of the file? */
+ clst = fp->obj.sclust; /* Follow from the origin */
+ if (clst == 0) { /* If no cluster is allocated, */
+ clst = create_chain(&fp->obj, 0); /* create a new cluster chain */
+ }
+ } else { /* On the middle or end of the file */
+#if FF_USE_FASTSEEK
+ if (fp->cltbl) {
+ clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+ } else
+#endif
+ {
+ clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */
+ }
+ }
+ if (clst == 0) {
+ EFSPRINTF("DSKFULL");
+ break; /* Could not allocate a new cluster (disk full) */
+ }
+ if (clst == 1) {
+ EFSPRINTF("CCHK");
+ ABORT(fs, FR_INT_ERR);
+ }
+ if (clst == 0xFFFFFFFF) {
+ EFSPRINTF("DERR");
+ ABORT(fs, FR_DISK_ERR);
+ }
+ fp->clust = clst; /* Update current cluster */
+ if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
+ }
+#if FF_FS_TINY
+ if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */
+#else
+ if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ sect = clst2sect(fs, fp->clust); /* Get current sector */
+ if (sect == 0) ABORT(fs, FR_INT_ERR);
+ sect += csect;
+ cc = btw / SS(fs); /* When remaining bytes >= sector size, */
+ if (cc > 0) { /* Write maximum contiguous sectors directly */
+ if (csect + cc > fs->csize) { /* Clip at cluster boundary */
+ cc = fs->csize - csect;
+ }
+ if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) {
+ EFSPRINTF("WLIO");
+ ABORT(fs, FR_DISK_ERR);
+ }
+#if FF_FS_MINIMIZE <= 2
+#if FF_FS_TINY
+ if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+ mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));
+ fs->wflag = 0;
+ }
+#else
+ if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+ mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+#endif
+ wcnt = SS(fs) * cc; /* Number of bytes transferred */
+ continue;
+ }
+#if FF_FS_TINY
+ if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */
+ if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
+ fs->winsect = sect;
+ }
+#else
+ if (fp->sect != sect && /* Fill sector cache with file data */
+ fp->fptr < fp->obj.objsize &&
+ disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
+ ABORT(fs, FR_DISK_ERR);
+ }
+#endif
+ fp->sect = sect;
+ }
+ wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
+ if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */
+#if FF_FS_TINY
+ if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
+ mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
+ fs->wflag = 1;
+#else
+ mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
+ fp->flag |= FA_DIRTY;
+#endif
+ }
+
+ fp->flag |= FA_MODIFIED; /* Set file change flag */
+
+ LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+#if FF_FASTFS && FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* Fast Write Aligned Sized File Without a Cache */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write_fast (
+ FIL* fp, /* Pointer to the file object */
+ const void* buff, /* Pointer to the data to be written */
+ UINT btw /* Number of bytes to write */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ UINT csize_bytes, count;
+ DWORD clst, work_bytes;
+ FSIZE_t work_sector = 0;
+ FSIZE_t sector_base = 0;
+ const BYTE *wbuff = (const BYTE*)buff;
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
+ EFSPRINTF("FOV");
+ LEAVE_FF(fs, res); /* Check validity */
+ }
+
+ if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+ if (!btw)
+ goto out;
+
+ /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
+ if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
+ btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
+ }
+
+ csize_bytes = fs->csize * SS(fs);
+
+ // TODO support sector writing inside a cluster
+ DWORD csect = (UINT)((fp->fptr / SS(fs)) & (fs->csize - 1)); /* Sector offset in the cluster */
+ if (csect) { EFSPRINTF("ICSR"); ABORT(fs, FR_INT_ERR); }
+
+ if (!fp->fptr) { /* On the top of the file? */
+ clst = fp->obj.sclust; /* Follow from the origin */
+ } else {
+ if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+ else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
+ }
+ if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DERR"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst; /* Set working cluster */
+
+ sector_base = clst2sect(fs, fp->clust);
+ work_bytes = MIN(btw, csize_bytes);
+ count = work_bytes / SS(fs);
+ fp->fptr += work_bytes;
+ btw -= work_bytes;
+
+ while (btw) {
+ clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+
+ if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DERR"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst;
+
+ work_sector = clst2sect(fs, fp->clust);
+ work_bytes = MIN(btw, csize_bytes);
+ if ((work_sector - sector_base) == count) count += work_bytes / SS(fs);
+ else {
+ if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ wbuff += count * SS(fs);
+
+ sector_base = work_sector;
+ count = work_bytes / SS(fs);
+ }
+
+ fp->fptr += work_bytes;
+ btw -= work_bytes;
+ }
+
+ if (!btw) { /* Final cluster/sectors write. */
+ if (work_bytes % SS(fs))
+ count++; /* Write an extra block. Cluster is always > storage block. */
+ if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+
+ fp->flag |= FA_MODIFIED; /* Set file change flag */
+
+out:
+ LEAVE_FF(fs, FR_OK);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+ FIL* fp /* Pointer to the file object */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD tm;
+ BYTE *dir;
+
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res == FR_OK) {
+ if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */
+#if !FF_FS_TINY
+ if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ /* Update the directory entry */
+ tm = GET_FATTIME(); /* Modified time */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */
+ if (res == FR_OK) {
+ res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
+ }
+ if (res == FR_OK) {
+ DIR dj;
+ DEF_NAMBUF
+
+ INIT_NAMBUF(fs);
+ res = load_obj_xdir(&dj, &fp->obj); /* Load directory entry block */
+ if (res == FR_OK) {
+ fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */
+ fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation information */
+ st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust);
+ st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize);
+ st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize);
+ st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */
+ fs->dirbuf[XDIR_ModTime10] = 0;
+ st_dword(fs->dirbuf + XDIR_AccTime, 0);
+ res = store_xdir(&dj); /* Restore it to the directory */
+ if (res == FR_OK) {
+ res = sync_fs(fs);
+ fp->flag &= (BYTE)~FA_MODIFIED;
+ }
+ }
+ FREE_NAMBUF();
+ }
+ } else
+#endif
+ {
+ res = move_window(fs, fp->dir_sect);
+ if (res == FR_OK) {
+ dir = fp->dir_ptr;
+ dir[DIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */
+ st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */
+ st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */
+ st_dword(dir + DIR_ModTime, tm); /* Update modified time */
+ st_word(dir + DIR_LstAccDate, 0);
+ fs->wflag = 1;
+ res = sync_fs(fs); /* Restore it to the directory */
+ fp->flag &= (BYTE)~FA_MODIFIED;
+ }
+ }
+ }
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+ FIL* fp /* Pointer to the file object to be closed */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+
+#if !FF_FS_READONLY
+ res = f_sync(fp); /* Flush cached data */
+ if (res == FR_OK)
+#endif
+ {
+ res = validate(&fp->obj, &fs); /* Lock volume */
+ if (res == FR_OK) {
+#if FF_FS_LOCK != 0
+ res = dec_lock(fp->obj.lockid); /* Decrement file open counter */
+ if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file object */
+#else
+ fp->obj.fs = 0; /* Invalidate file object */
+#endif
+#if FF_FS_REENTRANT
+ unlock_fs(fs, FR_OK); /* Unlock volume */
+#endif
+ }
+ }
+ return res;
+}
+
+
+
+
+#if FF_FS_RPATH >= 1
+/*-----------------------------------------------------------------------*/
+/* Change Current Directory or Current Drive, Get Current Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chdrive (
+ const TCHAR* path /* Drive number to set */
+)
+{
+ int vol;
+
+
+ /* Get logical drive number */
+ vol = get_ldnumber(&path);
+ if (vol < 0) return FR_INVALID_DRIVE;
+ CurrVol = (BYTE)vol; /* Set it as current volume */
+
+ return FR_OK;
+}
+
+
+
+FRESULT f_chdir (
+ const TCHAR* path /* Pointer to the directory path */
+)
+{
+#if FF_STR_VOLUME_ID == 2
+ UINT i;
+#endif
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ DEF_NAMBUF
+
+
+ /* Get logical drive */
+ res = find_volume(&path, &fs, 0);
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&dj, path); /* Follow the path */
+ if (res == FR_OK) { /* Follow completed */
+ if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start directory itself? */
+ fs->cdir = dj.obj.sclust;
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ fs->cdc_scl = dj.obj.c_scl;
+ fs->cdc_size = dj.obj.c_size;
+ fs->cdc_ofs = dj.obj.c_ofs;
+ }
+#endif
+ } else {
+ if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */
+ fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */
+ fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
+ fs->cdc_ofs = dj.blk_ofs;
+ } else
+#endif
+ {
+ fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */
+ }
+ } else {
+ res = FR_NO_PATH; /* Reached but a file */
+ }
+ }
+ }
+ FREE_NAMBUF();
+ if (res == FR_NO_FILE) res = FR_NO_PATH;
+#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */
+ if (res == FR_OK) {
+ for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */
+ CurrVol = (BYTE)i;
+ }
+#endif
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+#if FF_FS_RPATH >= 2
+FRESULT f_getcwd (
+ TCHAR* buff, /* Pointer to the directory path */
+ UINT len /* Size of buff in unit of TCHAR */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ UINT i, n;
+ DWORD ccl;
+ TCHAR *tp = buff;
+#if FF_VOLUMES >= 2
+ UINT vl;
+#if FF_STR_VOLUME_ID
+ const char *vp;
+#endif
+#endif
+ FILINFO fno;
+ DEF_NAMBUF
+
+
+ /* Get logical drive */
+ buff[0] = 0; /* Set null string to get current volume */
+ res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+
+ /* Follow parent directories and create the path */
+ i = len; /* Bottom of buffer (directory stack base) */
+ if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */
+ dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */
+ while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */
+ res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */
+ if (res != FR_OK) break;
+ res = move_window(fs, dj.sect);
+ if (res != FR_OK) break;
+ dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */
+ res = dir_sdi(&dj, 0);
+ if (res != FR_OK) break;
+ do { /* Find the entry links to the child directory */
+ res = DIR_READ_FILE(&dj);
+ if (res != FR_OK) break;
+ if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */
+ res = dir_next(&dj, 0);
+ } while (res == FR_OK);
+ if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+ if (res != FR_OK) break;
+ get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */
+ for (n = 0; fno.fname[n]; n++) ; /* Name length */
+ if (i < n + 1) { /* Insufficient space to store the path name? */
+ res = FR_NOT_ENOUGH_CORE; break;
+ }
+ while (n) buff[--i] = fno.fname[--n]; /* Stack the name */
+ buff[--i] = '/';
+ }
+ }
+ if (res == FR_OK) {
+ if (i == len) buff[--i] = '/'; /* Is it the root-directory? */
+#if FF_VOLUMES >= 2 /* Put drive prefix */
+ vl = 0;
+#if FF_STR_VOLUME_ID >= 1 /* String volume ID */
+ for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ;
+ if (i >= n + 2) {
+ if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/';
+ for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ;
+ if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':';
+ vl++;
+ }
+#else /* Numeric volume ID */
+ if (i >= 3) {
+ *tp++ = (TCHAR)'0' + CurrVol;
+ *tp++ = (TCHAR)':';
+ vl = 2;
+ }
+#endif
+ if (vl == 0) res = FR_NOT_ENOUGH_CORE;
+#endif
+ /* Add current directory path */
+ if (res == FR_OK) {
+ do *tp++ = buff[i++]; while (i < len); /* Copy stacked path string */
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ *tp = 0;
+ LEAVE_FF(fs, res);
+}
+
+#endif /* FF_FS_RPATH >= 2 */
+#endif /* FF_FS_RPATH >= 1 */
+
+
+
+#if FF_FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File Read/Write Pointer */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+ FIL* fp, /* Pointer to the file object */
+ FSIZE_t ofs /* File pointer from top of file */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD clst, bcs, nsect;
+ FSIZE_t ifptr;
+#if FF_USE_FASTSEEK
+ DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
+#endif
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res == FR_OK) res = (FRESULT)fp->err;
+#if FF_FS_EXFAT && !FF_FS_READONLY
+ if (res == FR_OK && fs->fs_type == FS_EXFAT) {
+ res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
+ }
+#endif
+ if (res != FR_OK) LEAVE_FF(fs, res);
+
+#if FF_USE_FASTSEEK
+ if (fp->cltbl) { /* Fast seek */
+ if (ofs == CREATE_LINKMAP) { /* Create CLMT */
+ tbl = fp->cltbl;
+ tlen = *tbl++; ulen = 2; /* Given table size and required table size */
+ cl = fp->obj.sclust; /* Origin of the chain */
+ if (cl != 0) {
+ do {
+ /* Get a fragment */
+ tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */
+ do {
+ pcl = cl; ncl++;
+ cl = get_fat(&fp->obj, cl);
+ if (cl <= 1) ABORT(fs, FR_INT_ERR);
+ if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+ } while (cl == pcl + 1);
+ if (ulen <= tlen) { /* Store the length and top of the fragment */
+ *tbl++ = ncl; *tbl++ = tcl;
+ }
+ } while (cl < fs->n_fatent); /* Repeat until end of chain */
+ }
+ *fp->cltbl = ulen; /* Number of items used */
+ if (ulen <= tlen) {
+ *tbl = 0; /* Terminate table */
+ } else {
+ res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */
+ }
+ } else { /* Fast seek */
+ if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */
+ fp->fptr = ofs; /* Set file pointer */
+ if (ofs > 0) {
+ fp->clust = clmt_clust(fp, ofs - 1);
+ dsc = clst2sect(fs, fp->clust);
+ if (dsc == 0) ABORT(fs, FR_INT_ERR);
+ dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1);
+ if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */
+#if !FF_FS_TINY
+#if !FF_FS_READONLY
+ if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */
+#endif
+ fp->sect = dsc;
+ }
+ }
+ }
+ } else
+#endif
+
+ /* Normal Seek */
+ {
+#if FF_FS_EXFAT
+ if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */
+#endif
+ if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */
+ ofs = fp->obj.objsize;
+ }
+ ifptr = fp->fptr;
+ fp->fptr = nsect = 0;
+ if (ofs > 0) {
+ bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */
+ if (ifptr > 0 &&
+ (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
+ fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */
+ ofs -= fp->fptr;
+ clst = fp->clust;
+ } else { /* When seek to back cluster, */
+ clst = fp->obj.sclust; /* start from the first cluster */
+#if !FF_FS_READONLY
+ if (clst == 0) { /* If no cluster chain, create a new chain */
+ clst = create_chain(&fp->obj, 0);
+ if (clst == 1) ABORT(fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+ fp->obj.sclust = clst;
+ }
+#endif
+ fp->clust = clst;
+ }
+ if (clst != 0) {
+ while (ofs > bcs) { /* Cluster following loop */
+ ofs -= bcs; fp->fptr += bcs;
+#if !FF_FS_READONLY
+ if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
+ if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */
+ fp->obj.objsize = fp->fptr;
+ fp->flag |= FA_MODIFIED;
+ }
+ clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */
+ if (clst == 0) { /* Clip file size in case of disk full */
+ ofs = 0; break;
+ }
+ } else
+#endif
+ {
+ clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */
+ }
+ if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+ if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
+ fp->clust = clst;
+ }
+ fp->fptr += ofs;
+ if (ofs % SS(fs)) {
+ nsect = clst2sect(fs, clst); /* Current sector */
+ if (nsect == 0) ABORT(fs, FR_INT_ERR);
+ nsect += (DWORD)(ofs / SS(fs));
+ }
+ }
+ }
+ if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */
+ fp->obj.objsize = fp->fptr;
+ fp->flag |= FA_MODIFIED;
+ }
+ if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */
+#if !FF_FS_TINY
+#if !FF_FS_READONLY
+ if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
+#endif
+ fp->sect = nsect;
+ }
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+#if FF_FASTFS && FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* Seek File Read/Write Pointer */
+/*-----------------------------------------------------------------------*/
+
+DWORD *f_expand_cltbl (
+ FIL* fp, /* Pointer to the file object */
+ UINT tblsz, /* Size of table (2 DWORDs + 2 DWORDs per fragment) */
+ FSIZE_t ofs /* File pointer from top of file */
+)
+{
+ /*
+ * Cluster table structure:
+ * Size (DWORD)
+ * Padding (DWORD)
+ * (Cluster Offset (DWORD) + Sequential clusters (DWORD)) * Fragments
+ */
+ if (fp->flag & FA_WRITE) f_lseek(fp, ofs); /* Expand file if write is enabled */
+ if (!fp->cltbl) { /* Allocate memory for cluster link table */
+ fp->cltbl = (DWORD *)ff_memalloc(tblsz);
+ if (!fp->cltbl) return (void *)0;
+ fp->cltbl[0] = tblsz;
+ }
+ if (f_lseek(fp, CREATE_LINKMAP)) { /* Create cluster link table */
+ ff_memfree(fp->cltbl);
+ fp->cltbl = (void *)0;
+ EFSPRINTF("CLTBLSZ");
+ return (void *)0;
+ }
+ f_lseek(fp, 0);
+
+ return fp->cltbl;
+}
+#endif
+
+
+
+
+#if FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directory Object */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+ DIR* dp, /* Pointer to directory object to create */
+ const TCHAR* path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DEF_NAMBUF
+
+
+ if (!dp) return FR_INVALID_OBJECT;
+
+ /* Get logical drive */
+ res = find_volume(&path, &fs, 0);
+ if (res == FR_OK) {
+ dp->obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(dp, path); /* Follow the path to the directory */
+ if (res == FR_OK) { /* Follow completed */
+ if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */
+ if (dp->obj.attr & AM_DIR) { /* This object is a sub-directory */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ dp->obj.c_scl = dp->obj.sclust; /* Get containing directory inforamation */
+ dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
+ dp->obj.c_ofs = dp->blk_ofs;
+ init_alloc_info(fs, &dp->obj); /* Get object allocation info */
+ } else
+#endif
+ {
+ dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */
+ }
+ } else { /* This object is a file */
+ res = FR_NO_PATH;
+ }
+ }
+ if (res == FR_OK) {
+ dp->obj.id = fs->id;
+ res = dir_sdi(dp, 0); /* Rewind directory */
+#if FF_FS_LOCK != 0
+ if (res == FR_OK) {
+ if (dp->obj.sclust != 0) {
+ dp->obj.lockid = inc_lock(dp, 0); /* Lock the sub directory */
+ if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES;
+ } else {
+ dp->obj.lockid = 0; /* Root directory need not to be locked */
+ }
+ }
+#endif
+ }
+ }
+ FREE_NAMBUF();
+ if (res == FR_NO_FILE) res = FR_NO_PATH;
+ }
+ if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the directory object if function faild */
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_closedir (
+ DIR *dp /* Pointer to the directory object to be closed */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+
+
+ res = validate(&dp->obj, &fs); /* Check validity of the file object */
+ if (res == FR_OK) {
+#if FF_FS_LOCK != 0
+ if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* Decrement sub-directory open counter */
+ if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory object */
+#else
+ dp->obj.fs = 0; /* Invalidate directory object */
+#endif
+#if FF_FS_REENTRANT
+ unlock_fs(fs, FR_OK); /* Unlock volume */
+#endif
+ }
+ return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entries in Sequence */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+ DIR* dp, /* Pointer to the open directory object */
+ FILINFO* fno /* Pointer to file information to return */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DEF_NAMBUF
+
+
+ res = validate(&dp->obj, &fs); /* Check validity of the directory object */
+ if (res == FR_OK) {
+ if (!fno) {
+ res = dir_sdi(dp, 0); /* Rewind the directory object */
+ } else {
+ INIT_NAMBUF(fs);
+ res = DIR_READ_FILE(dp); /* Read an item */
+ if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */
+ if (res == FR_OK) { /* A valid entry is found */
+ get_fileinfo(dp, fno); /* Get the object information */
+ res = dir_next(dp, 0); /* Increment index for next */
+ if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */
+ }
+ FREE_NAMBUF();
+ }
+ }
+ LEAVE_FF(fs, res);
+}
+
+
+
+#if FF_USE_FIND
+/*-----------------------------------------------------------------------*/
+/* Find Next File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findnext (
+ DIR* dp, /* Pointer to the open directory object */
+ FILINFO* fno /* Pointer to the file information structure */
+)
+{
+ FRESULT res;
+
+
+ for (;;) {
+ res = f_readdir(dp, fno); /* Get a directory item */
+ if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */
+ if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */
+#if FF_USE_LFN && FF_USE_FIND == 2
+ if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */
+#endif
+ }
+ return res;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find First File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findfirst (
+ DIR* dp, /* Pointer to the blank directory object */
+ FILINFO* fno, /* Pointer to the file information structure */
+ const TCHAR* path, /* Pointer to the directory to open */
+ const TCHAR* pattern /* Pointer to the matching pattern */
+)
+{
+ FRESULT res;
+
+
+ dp->pat = pattern; /* Save pointer to pattern string */
+ res = f_opendir(dp, path); /* Open the target directory */
+ if (res == FR_OK) {
+ res = f_findnext(dp, fno); /* Find the first item */
+ }
+ return res;
+}
+
+#endif /* FF_USE_FIND */
+
+
+
+#if FF_FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+ const TCHAR* path, /* Pointer to the file path */
+ FILINFO* fno /* Pointer to file information to return */
+)
+{
+ FRESULT res;
+ DIR dj;
+ DEF_NAMBUF
+
+
+ /* Get logical drive */
+ res = find_volume(&path, &dj.obj.fs, 0);
+ if (res == FR_OK) {
+ INIT_NAMBUF(dj.obj.fs);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) { /* Follow completed */
+ if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */
+ res = FR_INVALID_NAME;
+ } else { /* Found an object */
+ if (fno) get_fileinfo(&dj, fno);
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ LEAVE_FF(dj.obj.fs, res);
+}
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+ const TCHAR* path, /* Logical drive number */
+ DWORD* nclst, /* Pointer to a variable to return number of free clusters */
+ FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD nfree, clst, sect, stat;
+ UINT i;
+ FFOBJID obj;
+
+
+ /* Get logical drive */
+ res = find_volume(&path, &fs, 0);
+ if (res == FR_OK) {
+ if (fatfs) *fatfs = fs; /* Return ptr to the fs object */
+ /* If free_clst is valid, return it without full FAT scan */
+ if (fs->free_clst <= fs->n_fatent - 2) {
+ *nclst = fs->free_clst;
+ } else {
+ /* Scan FAT to obtain number of free clusters */
+ nfree = 0;
+ if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit field FAT entries */
+ clst = 2; obj.fs = fs;
+ do {
+ stat = get_fat(&obj, clst);
+ if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+ if (stat == 1) { res = FR_INT_ERR; break; }
+ if (stat == 0) nfree++;
+ } while (++clst < fs->n_fatent);
+ } else {
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan allocation bitmap */
+ BYTE bm;
+ UINT b;
+
+ clst = fs->n_fatent - 2; /* Number of clusters */
+ sect = fs->bitbase; /* Bitmap sector */
+ i = 0; /* Offset in the sector */
+ do { /* Counts numbuer of bits with zero in the bitmap */
+ if (i == 0) {
+ res = move_window(fs, sect++);
+ if (res != FR_OK) break;
+ }
+ for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) {
+ if (!(bm & 1)) nfree++;
+ bm >>= 1;
+ }
+ i = (i + 1) % SS(fs);
+ } while (clst);
+ } else
+#endif
+ { /* FAT16/32: Scan WORD/DWORD FAT entries */
+ clst = fs->n_fatent; /* Number of entries */
+ sect = fs->fatbase; /* Top of the FAT */
+ i = 0; /* Offset in the sector */
+ do { /* Counts numbuer of entries with zero in the FAT */
+ if (i == 0) {
+ res = move_window(fs, sect++);
+ if (res != FR_OK) break;
+ }
+ if (fs->fs_type == FS_FAT16) {
+ if (ld_word(fs->win + i) == 0) nfree++;
+ i += 2;
+ } else {
+ if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++;
+ i += 4;
+ }
+ i %= SS(fs);
+ } while (--clst);
+ }
+ }
+ *nclst = nfree; /* Return the free clusters */
+ fs->free_clst = nfree; /* Now free_clst is valid */
+ fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */
+ }
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+ FIL* fp /* Pointer to the file object */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD ncl;
+
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+ if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+
+ if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */
+ if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */
+ res = remove_chain(&fp->obj, fp->obj.sclust, 0);
+ fp->obj.sclust = 0;
+ } else { /* When truncate a part of the file, remove remaining clusters */
+ ncl = get_fat(&fp->obj, fp->clust);
+ res = FR_OK;
+ if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+ if (ncl == 1) res = FR_INT_ERR;
+ if (res == FR_OK && ncl < fs->n_fatent) {
+ res = remove_chain(&fp->obj, ncl, fp->clust);
+ }
+ }
+ fp->obj.objsize = fp->fptr; /* Set file size to current read/write point */
+ fp->flag |= FA_MODIFIED;
+#if !FF_FS_TINY
+ if (res == FR_OK && (fp->flag & FA_DIRTY)) {
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
+ res = FR_DISK_ERR;
+ } else {
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+ }
+#endif
+ if (res != FR_OK) ABORT(fs, res);
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File/Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+ const TCHAR* path /* Pointer to the file or directory path */
+)
+{
+ FRESULT res;
+ DIR dj, sdj;
+ DWORD dclst = 0;
+ FATFS *fs;
+#if FF_FS_EXFAT
+ FFOBJID obj;
+#endif
+ DEF_NAMBUF
+
+
+ /* Get logical drive */
+ res = find_volume(&path, &fs, FA_WRITE);
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {
+ res = FR_INVALID_NAME; /* Cannot remove dot entry */
+ }
+#if FF_FS_LOCK != 0
+ if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */
+#endif
+ if (res == FR_OK) { /* The object is accessible */
+ if (dj.fn[NSFLAG] & NS_NONAME) {
+ res = FR_INVALID_NAME; /* Cannot remove the origin directory */
+ } else {
+ if (dj.obj.attr & AM_RDO) {
+ res = FR_DENIED; /* Cannot remove R/O object */
+ }
+ }
+ if (res == FR_OK) {
+#if FF_FS_EXFAT
+ obj.fs = fs;
+ if (fs->fs_type == FS_EXFAT) {
+ init_alloc_info(fs, &obj);
+ dclst = obj.sclust;
+ } else
+#endif
+ {
+ dclst = ld_clust(fs, dj.dir);
+ }
+ if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */
+#if FF_FS_RPATH != 0
+ if (dclst == fs->cdir) { /* Is it the current directory? */
+ res = FR_DENIED;
+ } else
+#endif
+ {
+ sdj.obj.fs = fs; /* Open the sub-directory */
+ sdj.obj.sclust = dclst;
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ sdj.obj.objsize = obj.objsize;
+ sdj.obj.stat = obj.stat;
+ }
+#endif
+ res = dir_sdi(&sdj, 0);
+ if (res == FR_OK) {
+ res = DIR_READ_FILE(&sdj); /* Test if the directory is empty */
+ if (res == FR_OK) res = FR_DENIED; /* Not empty? */
+ if (res == FR_NO_FILE) res = FR_OK; /* Empty? */
+ }
+ }
+ }
+ }
+ if (res == FR_OK) {
+ res = dir_remove(&dj); /* Remove the directory entry */
+ if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */
+#if FF_FS_EXFAT
+ res = remove_chain(&obj, dclst, 0);
+#else
+ res = remove_chain(&dj.obj, dclst, 0);
+#endif
+ }
+ if (res == FR_OK) res = sync_fs(fs);
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+ const TCHAR* path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FFOBJID sobj;
+ FATFS *fs;
+ DWORD dcl, pcl, tm;
+ DEF_NAMBUF
+
+
+ res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) res = FR_EXIST; /* Name collision? */
+ if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { /* Invalid name? */
+ res = FR_INVALID_NAME;
+ }
+ if (res == FR_NO_FILE) { /* It is clear to create a new directory */
+ sobj.fs = fs; /* New object id to create a new chain */
+ dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */
+ res = FR_OK;
+ if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster? */
+ if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */
+ if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk error? */
+ tm = GET_FATTIME();
+ if (res == FR_OK) {
+ res = dir_clear(fs, dcl); /* Clean up the new table */
+ if (res == FR_OK) {
+ if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */
+ mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */
+ fs->win[DIR_Name] = '.';
+ fs->win[DIR_Attr] = AM_DIR;
+ st_dword(fs->win + DIR_ModTime, tm);
+ st_clust(fs, fs->win, dcl);
+ mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */
+ fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;
+ st_clust(fs, fs->win + SZDIRE, pcl);
+ fs->wflag = 1;
+ }
+ res = dir_register(&dj); /* Register the object to the parent directoy */
+ }
+ }
+ if (res == FR_OK) {
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */
+ st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */
+ st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */
+ st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)fs->csize * SS(fs)); /* File size needs to be valid */
+ st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs));
+ fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */
+ fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */
+ res = store_xdir(&dj);
+ } else
+#endif
+ {
+ st_dword(dj.dir + DIR_ModTime, tm); /* Created time */
+ st_clust(fs, dj.dir, dcl); /* Table start cluster */
+ dj.dir[DIR_Attr] = AM_DIR; /* Attribute */
+ fs->wflag = 1;
+ }
+ if (res == FR_OK) {
+ res = sync_fs(fs);
+ }
+ } else {
+ remove_chain(&sobj, dcl, 0); /* Could not register, remove the allocated cluster */
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename a File/Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+ const TCHAR* path_old, /* Pointer to the object name to be renamed */
+ const TCHAR* path_new /* Pointer to the new name */
+)
+{
+ FRESULT res;
+ DIR djo, djn;
+ FATFS *fs;
+ BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
+ DWORD dw;
+ DEF_NAMBUF
+
+
+ get_ldnumber(&path_new); /* Snip the drive number of new name off */
+ res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */
+ if (res == FR_OK) {
+ djo.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&djo, path_old); /* Check old object */
+ if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */
+#if FF_FS_LOCK != 0
+ if (res == FR_OK) {
+ res = chk_lock(&djo, 2);
+ }
+#endif
+ if (res == FR_OK) { /* Object to be renamed is found */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */
+ BYTE nf, nn;
+ WORD nh;
+
+ mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */
+ mem_cpy(&djn, &djo, sizeof djo);
+ res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
+ if (res == FR_OK) { /* Is new name already in use by any other object? */
+ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
+ }
+ if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
+ res = dir_register(&djn); /* Register the new entry */
+ if (res == FR_OK) {
+ nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName];
+ nh = ld_word(fs->dirbuf + XDIR_NameHash);
+ mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */
+ fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn;
+ st_word(fs->dirbuf + XDIR_NameHash, nh);
+ if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
+/* Start of critical section where an interruption can cause a cross-link */
+ res = store_xdir(&djn);
+ }
+ }
+ } else
+#endif
+ { /* At FAT/FAT32 volume */
+ mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */
+ mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */
+ res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
+ if (res == FR_OK) { /* Is new name already in use by any other object? */
+ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
+ }
+ if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
+ res = dir_register(&djn); /* Register the new entry */
+ if (res == FR_OK) {
+ dir = djn.dir; /* Copy directory entry of the object except name */
+ mem_cpy(dir + 13, buf + 13, SZDIRE - 13);
+ dir[DIR_Attr] = buf[DIR_Attr];
+ if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
+ fs->wflag = 1;
+ if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */
+ dw = clst2sect(fs, ld_clust(fs, dir));
+ if (dw == 0) {
+ res = FR_INT_ERR;
+ } else {
+/* Start of critical section where an interruption can cause a cross-link */
+ res = move_window(fs, dw);
+ dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */
+ if (res == FR_OK && dir[1] == '.') {
+ st_clust(fs, dir, djn.obj.sclust);
+ fs->wflag = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (res == FR_OK) {
+ res = dir_remove(&djo); /* Remove old entry */
+ if (res == FR_OK) {
+ res = sync_fs(fs);
+ }
+ }
+/* End of the critical section */
+ }
+ FREE_NAMBUF();
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_FS_MINIMIZE == 0 */
+#endif /* FF_FS_MINIMIZE <= 1 */
+#endif /* FF_FS_MINIMIZE <= 2 */
+
+
+
+#if FF_USE_CHMOD && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Change Attribute */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+ const TCHAR* path, /* Pointer to the file path */
+ BYTE attr, /* Attribute bits */
+ BYTE mask /* Attribute mask to change */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ DEF_NAMBUF
+
+
+ res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */
+ if (res == FR_OK) {
+ mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */
+ res = store_xdir(&dj);
+ } else
+#endif
+ {
+ dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
+ fs->wflag = 1;
+ }
+ if (res == FR_OK) {
+ res = sync_fs(fs);
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+ const TCHAR* path, /* Pointer to the file/directory name */
+ const FILINFO* fno /* Pointer to the timestamp to be set */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ DEF_NAMBUF
+
+
+ res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */
+ if (res == FR_OK) {
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
+ res = store_xdir(&dj);
+ } else
+#endif
+ {
+ st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
+ fs->wflag = 1;
+ }
+ if (res == FR_OK) {
+ res = sync_fs(fs);
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+#endif /* FF_USE_CHMOD && !FF_FS_READONLY */
+
+
+
+#if FF_USE_LABEL
+/*-----------------------------------------------------------------------*/
+/* Get Volume Label */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getlabel (
+ const TCHAR* path, /* Logical drive number */
+ TCHAR* label, /* Buffer to store the volume label */
+ DWORD* vsn /* Variable to store the volume serial number */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ UINT si, di;
+ WCHAR wc;
+
+ /* Get logical drive */
+ res = find_volume(&path, &fs, 0);
+
+ /* Get volume label */
+ if (res == FR_OK && label) {
+ dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
+ res = dir_sdi(&dj, 0);
+ if (res == FR_OK) {
+ res = DIR_READ_LABEL(&dj); /* Find a volume label entry */
+ if (res == FR_OK) {
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ WCHAR hs;
+
+ for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */
+ wc = ld_word(dj.dir + XDIR_Label + si * 2);
+ if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */
+ hs = wc; continue;
+ }
+ wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4);
+ if (wc == 0) { di = 0; break; }
+ di += wc;
+ hs = 0;
+ }
+ if (hs != 0) di = 0; /* Broken surrogate pair? */
+ label[di] = 0;
+ } else
+#endif
+ {
+ si = di = 0; /* Extract volume label from AM_VOL entry */
+ while (si < 11) {
+ wc = dj.dir[si++];
+#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
+ if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */
+ wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */
+ if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */
+ if (wc == 0) { di = 0; break; }
+ di += wc;
+#else /* ANSI/OEM output */
+ label[di++] = (TCHAR)wc;
+#endif
+ }
+ do { /* Truncate trailing spaces */
+ label[di] = 0;
+ if (di == 0) break;
+ } while (label[--di] == ' ');
+ }
+ }
+ }
+ if (res == FR_NO_FILE) { /* No label entry and return nul string */
+ label[0] = 0;
+ res = FR_OK;
+ }
+ }
+
+ /* Get volume serial number */
+ if (res == FR_OK && vsn) {
+ res = move_window(fs, fs->volbase);
+ if (res == FR_OK) {
+ switch (fs->fs_type) {
+ case FS_EXFAT:
+ di = BPB_VolIDEx; break;
+
+ case FS_FAT32:
+ di = BS_VolID32; break;
+
+ default:
+ di = BS_VolID;
+ }
+ *vsn = ld_dword(fs->win + di);
+ }
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Set Volume Label */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setlabel (
+ const TCHAR* label /* Volume label to set with heading logical drive number */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ BYTE dirvn[22];
+ UINT di;
+ WCHAR wc;
+ static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */
+#if FF_USE_LFN
+ DWORD dc;
+#endif
+
+ /* Get logical drive */
+ res = find_volume(&label, &fs, FA_WRITE);
+ if (res != FR_OK) LEAVE_FF(fs, res);
+
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ mem_set(dirvn, 0, 22);
+ di = 0;
+ while ((UINT)*label >= ' ') { /* Create volume label */
+ dc = tchar2uni(&label); /* Get a Unicode character */
+ if (dc >= 0x10000) {
+ if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */
+ dc = 0;
+ } else {
+ st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++;
+ }
+ }
+ if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */
+ LEAVE_FF(fs, FR_INVALID_NAME);
+ }
+ st_word(dirvn + di * 2, (WCHAR)dc); di++;
+ }
+ } else
+#endif
+ { /* On the FAT/FAT32 volume */
+ mem_set(dirvn, ' ', 11);
+ di = 0;
+ while ((UINT)*label >= ' ') { /* Create volume label */
+#if FF_USE_LFN
+ dc = tchar2uni(&label);
+ wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0;
+#else /* ANSI/OEM input */
+ wc = (BYTE)*label++;
+ if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0;
+ if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */
+#if FF_CODE_PAGE == 0
+ if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
+#elif FF_CODE_PAGE < 900
+ if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
+#endif
+#endif
+ if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */
+ LEAVE_FF(fs, FR_INVALID_NAME);
+ }
+ if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8);
+ dirvn[di++] = (BYTE)wc;
+ }
+ if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */
+ while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */
+ }
+
+ /* Set volume label */
+ dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
+ res = dir_sdi(&dj, 0);
+ if (res == FR_OK) {
+ res = DIR_READ_LABEL(&dj); /* Get volume label entry */
+ if (res == FR_OK) {
+ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+ dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */
+ mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
+ } else {
+ if (di != 0) {
+ mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */
+ } else {
+ dj.dir[DIR_Name] = DDEM; /* Remove the volume label */
+ }
+ }
+ fs->wflag = 1;
+ res = sync_fs(fs);
+ } else { /* No volume label entry or an error */
+ if (res == FR_NO_FILE) {
+ res = FR_OK;
+ if (di != 0) { /* Create a volume label entry */
+ res = dir_alloc(&dj, 1); /* Allocate an entry */
+ if (res == FR_OK) {
+ mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */
+ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+ dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */
+ dj.dir[XDIR_NumLabel] = (BYTE)di;
+ mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
+ } else {
+ dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */
+ mem_cpy(dj.dir, dirvn, 11);
+ }
+ fs->wflag = 1;
+ res = sync_fs(fs);
+ }
+ }
+ }
+ }
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_LABEL */
+
+
+
+#if FF_USE_EXPAND && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Allocate a Contiguous Blocks to the File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_expand (
+ FIL* fp, /* Pointer to the file object */
+ FSIZE_t fsz, /* File size to be expanded to */
+ BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD n, clst, stcl, scl, ncl, tcl, lclst;
+
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+ if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);
+#if FF_FS_EXFAT
+ if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */
+#endif
+ n = (DWORD)fs->csize * SS(fs); /* Cluster size */
+ tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */
+ stcl = fs->last_clst; lclst = 0;
+ if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;
+
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */
+ if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */
+ if (scl == 0xFFFFFFFF) res = FR_DISK_ERR;
+ if (res == FR_OK) { /* A contiguous free area is found */
+ if (opt) { /* Allocate it now */
+ res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */
+ lclst = scl + tcl - 1;
+ } else { /* Set it as suggested point for next allocation */
+ lclst = scl - 1;
+ }
+ }
+ } else
+#endif
+ {
+ scl = clst = stcl; ncl = 0;
+ for (;;) { /* Find a contiguous cluster block */
+ n = get_fat(&fp->obj, clst);
+ if (++clst >= fs->n_fatent) clst = 2;
+ if (n == 1) { res = FR_INT_ERR; break; }
+ if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+ if (n == 0) { /* Is it a free cluster? */
+ if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */
+ } else {
+ scl = clst; ncl = 0; /* Not a free cluster */
+ }
+ if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */
+ }
+ if (res == FR_OK) { /* A contiguous free area is found */
+ if (opt) { /* Allocate it now */
+ for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */
+ res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1);
+ if (res != FR_OK) break;
+ lclst = clst;
+ }
+ } else { /* Set it as suggested point for next allocation */
+ lclst = scl - 1;
+ }
+ }
+ }
+
+ if (res == FR_OK) {
+ fs->last_clst = lclst; /* Set suggested start cluster to start next */
+ if (opt) { /* Is it allocated now? */
+ fp->obj.sclust = scl; /* Update object allocation information */
+ fp->obj.objsize = fsz;
+ if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */
+ fp->flag |= FA_MODIFIED;
+ if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */
+ fs->free_clst -= tcl;
+ fs->fsi_flag |= 1;
+ }
+ }
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+#endif /* FF_USE_EXPAND && !FF_FS_READONLY */
+
+
+
+#if FF_USE_FORWARD
+/*-----------------------------------------------------------------------*/
+/* Forward Data to the Stream Directly */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_forward (
+ FIL* fp, /* Pointer to the file object */
+ UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
+ UINT btf, /* Number of bytes to forward */
+ UINT* bf /* Pointer to number of bytes forwarded */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD clst, sect;
+ FSIZE_t remain;
+ UINT rcnt, csect;
+ BYTE *dbuf;
+
+
+ *bf = 0; /* Clear transfer byte counter */
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+ if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+
+ remain = fp->obj.objsize - fp->fptr;
+ if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */
+
+ for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */
+ fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
+ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
+ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
+ if (csect == 0) { /* On the cluster boundary? */
+ clst = (fp->fptr == 0) ? /* On the top of the file? */
+ fp->obj.sclust : get_fat(&fp->obj, fp->clust);
+ if (clst <= 1) ABORT(fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+ fp->clust = clst; /* Update current cluster */
+ }
+ }
+ sect = clst2sect(fs, fp->clust); /* Get current data sector */
+ if (sect == 0) ABORT(fs, FR_INT_ERR);
+ sect += csect;
+#if FF_FS_TINY
+ if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */
+ dbuf = fs->win;
+#else
+ if (fp->sect != sect) { /* Fill sector cache with file data */
+#if !FF_FS_READONLY
+ if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
+ if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ }
+ dbuf = fp->buf;
+#endif
+ fp->sect = sect;
+ rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
+ if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */
+ rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */
+ if (rcnt == 0) ABORT(fs, FR_INT_ERR);
+ }
+
+ LEAVE_FF(fs, FR_OK);
+}
+#endif /* FF_USE_FORWARD */
+
+
+
+#if FF_USE_MKFS && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create an FAT/exFAT volume */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkfs (
+ const TCHAR* path, /* Logical drive number */
+ BYTE opt, /* Format option */
+ DWORD au, /* Size of allocation unit (cluster) [byte] */
+ void* work, /* Pointer to working buffer (null: use heap memory) */
+ UINT len /* Size of working buffer [byte] */
+)
+{
+ const UINT n_fats = 2; /* Number of FATs for FAT/FAT32 volume (1 or 2) */
+ const UINT n_rootdir = 512; /* Number of root directory entries for FAT volume */
+ static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */
+ static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */
+ BYTE fmt, sys, *buf, *pte, pdrv, part;
+ WORD ss; /* Sector size */
+ DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;
+ DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */
+ DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */
+ UINT i;
+ int vol;
+ DSTATUS stat;
+#if FF_USE_TRIM || FF_FS_EXFAT
+ DWORD tbl[3];
+#endif
+
+
+ /* Check mounted drive and clear work area */
+ vol = get_ldnumber(&path); /* Get target logical drive */
+ if (vol < 0) return FR_INVALID_DRIVE;
+ if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume if mounted */
+ pdrv = LD2PD(vol); /* Physical drive */
+ part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */
+
+ /* Check physical drive status */
+ stat = disk_initialize(pdrv);
+ if (stat & STA_NOINIT) return FR_NOT_READY;
+ if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+ if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 131072 || (sz_blk & (sz_blk - 1))) sz_blk = 2048; /* Erase block to align data area. 1MB minimum */
+#if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */
+ if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
+ if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
+#else
+ ss = FF_MAX_SS;
+#endif
+ if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */
+ au /= ss; /* Cluster size in unit of sector */
+
+ /* Get working buffer */
+#if FF_USE_LFN == 3
+ if (!work) { /* Use heap memory for working buffer */
+ for (szb_buf = MAX_MALLOC, buf = 0; szb_buf >= ss && (buf = ff_memalloc(szb_buf)) == 0; szb_buf /= 2) ;
+ sz_buf = szb_buf / ss; /* Size of working buffer (sector) */
+ } else
+#endif
+ {
+ buf = (BYTE*)work; /* Working buffer */
+ sz_buf = len / ss; /* Size of working buffer (sector) */
+ szb_buf = sz_buf * ss; /* Size of working buffer (byte) */
+ }
+ if (!buf || sz_buf == 0) return FR_NOT_ENOUGH_CORE;
+
+ /* Determine where the volume to be located (b_vol, sz_vol) */
+ if (FF_MULTI_PARTITION && part != 0) {
+ /* Get partition information from partition table in the MBR */
+ if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */
+ if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */
+ pte = buf + (MBR_Table + (part - 1) * SZ_PTE);
+ if (pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */
+ b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */
+ sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */
+ } else {
+ /* Create a single-partition in this function */
+ if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ b_vol = (opt & FM_SFD) ? 0 : sz_blk; /* Volume start sector */
+ if (sz_vol < b_vol) LEAVE_MKFS(FR_MKFS_ABORTED);
+ sz_vol -= b_vol; /* Volume size */
+ }
+ if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */
+
+ /* Pre-determine the FAT type */
+ do {
+ if (FF_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */
+ if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */
+ fmt = FS_EXFAT; break;
+ }
+ }
+ if (au > 128) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Too large au for FAT/FAT32 */
+ if (opt & FM_FAT32) { /* FAT32 possible? */
+ if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */
+ fmt = FS_FAT32; break;
+ }
+ }
+ if (!(opt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */
+ fmt = FS_FAT16;
+ } while (0);
+
+#if FF_FS_EXFAT
+ if (fmt == FS_EXFAT) { /* Create an exFAT volume */
+ DWORD szb_bit, szb_case, sum, nb, cl;
+ WCHAR ch, si;
+ UINT j, st;
+ BYTE b;
+
+ if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
+#if FF_USE_TRIM
+ tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */
+ disk_ioctl(pdrv, CTRL_TRIM, tbl);
+#endif
+ /* Determine FAT location, data location and number of clusters */
+ if (au == 0) { /* au auto-selection */
+ au = 8;
+ if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */
+ if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */
+ }
+ b_fat = b_vol + 32; /* FAT start at offset 32 */
+ sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */
+ b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */
+ if (b_data - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
+ n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */
+ if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
+ if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */
+
+ szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
+ tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */
+
+ /* Create a compressed up-case table */
+ sect = b_data + au * tbl[0]; /* Table start sector */
+ sum = 0; /* Table checksum to be stored in the 82 entry */
+ st = 0; si = 0; i = 0; j = 0; szb_case = 0;
+ do {
+ switch (st) {
+ case 0:
+ ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */
+ if (ch != si) {
+ si++; break; /* Store the up-case char if exist */
+ }
+ for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */
+ if (j >= 128) {
+ ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */
+ }
+ st = 1; /* Do not compress short run */
+ /* go to next case */
+ case 1:
+ ch = si++; /* Fill the short run */
+ if (--j == 0) st = 0;
+ break;
+
+ default:
+ ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars to skip */
+ st = 0;
+ }
+ sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */
+ sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);
+ i += 2; szb_case += 2;
+ if (si == 0 || i == szb_buf) { /* Write buffered data when buffer full or end of process */
+ n = (i + ss - 1) / ss;
+ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ sect += n; i = 0;
+ }
+ } while (si);
+ tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */
+ tbl[2] = 1; /* Number of root dir clusters */
+
+ /* Initialize the allocation bitmap */
+ sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
+ nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
+ do {
+ mem_set(buf, 0, szb_buf);
+ for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ;
+ for (b = 1; nb != 0 && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ;
+ n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
+ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ sect += n; nsect -= n;
+ } while (nsect);
+
+ /* Initialize the FAT */
+ sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
+ j = nb = cl = 0;
+ do {
+ mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */
+ if (cl == 0) { /* Set entry 0 and 1 */
+ st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
+ st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
+ }
+ do { /* Create chains of bitmap, up-case and root dir */
+ while (nb != 0 && i < szb_buf) { /* Create a chain */
+ st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
+ i += 4; cl++; nb--;
+ }
+ if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */
+ } while (nb != 0 && i < szb_buf);
+ n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
+ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ sect += n; nsect -= n;
+ } while (nsect);
+
+ /* Initialize the root directory */
+ mem_set(buf, 0, szb_buf);
+ buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry */
+ buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */
+ st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */
+ st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */
+ buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
+ st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */
+ st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */
+ st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
+ sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */
+ do { /* Fill root directory sectors */
+ n = (nsect > sz_buf) ? sz_buf : nsect;
+ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ mem_set(buf, 0, ss);
+ sect += n; nsect -= n;
+ } while (nsect);
+
+ /* Create two set of the exFAT VBR blocks */
+ sect = b_vol;
+ for (n = 0; n < 2; n++) {
+ /* Main record (+0) */
+ mem_set(buf, 0, ss);
+ mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */
+ st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */
+ st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */
+ st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */
+ st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
+ st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */
+ st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
+ st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */
+ st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */
+ st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
+ for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
+ for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */
+ buf[BPB_NumFATsEx] = 1; /* Number of FATs */
+ buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */
+ st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */
+ st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */
+ for (i = sum = 0; i < ss; i++) { /* VBR checksum */
+ if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);
+ }
+ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ /* Extended bootstrap record (+1..+8) */
+ mem_set(buf, 0, ss);
+ st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */
+ for (j = 1; j < 9; j++) {
+ for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */
+ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ }
+ /* OEM/Reserved record (+9..+10) */
+ mem_set(buf, 0, ss);
+ for ( ; j < 11; j++) {
+ for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */
+ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ }
+ /* Sum record (+11) */
+ for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */
+ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ }
+
+ } else
+#endif /* FF_FS_EXFAT */
+ { /* Create an FAT/FAT32 volume */
+ do {
+ pau = au;
+ /* Pre-determine number of clusters and FAT sub-type */
+ if (fmt == FS_FAT32) { /* FAT32 volume */
+ if (pau == 0) { /* au auto-selection */
+ n = sz_vol / 0x20000; /* Volume size in unit of 128KS */
+ for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */
+ }
+ n_clst = sz_vol / pau; /* Number of clusters */
+ while (1) {
+ /* Do not account for reserved/root cluster on FAT size for better alignment. */
+ sz_fat = (n_clst * 4 + ss - 1) / ss; /* FAT size [sector]. */
+ sz_rsv = 2048; /* Number of reserved sectors. Use 1MB for better performance (for flash memory media) */
+ sz_dir = 0; /* No static directory */
+ if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED);
+ b_fat = b_vol + sz_rsv; /* FAT base */
+ b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */
+
+ /* Align data base to erase block boundary (for flash memory media) */
+ n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */
+ b_fat += n; /* FAT32: Move FAT base */
+ /* Make sure FAT base is aligned to reserved size for performance (PRF2SAFE min cluster alignment) */
+ if ((b_fat & (sz_rsv - 1) || (n_fats == 2 && sz_fat & (sz_rsv - 1)))) {
+ n_clst++; /* FAT size must always be bigger than real free size */
+ continue;
+ }
+ sz_rsv += n;
+ break;
+ }
+ } else { /* FAT volume */
+ if (pau == 0) { /* au auto-selection */
+ n = sz_vol / 0x1000; /* Volume size in unit of 4KS */
+ for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */
+ }
+ n_clst = sz_vol / pau;
+ if (n_clst > MAX_FAT12) {
+ n = n_clst * 2 + 4; /* FAT size [byte] */
+ } else {
+ fmt = FS_FAT12;
+ n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */
+ }
+ sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */
+ sz_rsv = 1; /* Number of reserved sectors */
+ sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */
+ b_fat = b_vol + sz_rsv; /* FAT base */
+ b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */
+
+ /* Align data base to erase block boundary (for flash memory media) */
+ n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */
+ if (n % n_fats) { /* Adjust fractional error if needed */
+ n--; sz_rsv++; b_fat++; /* FAT: Expand FAT size */
+ }
+ sz_fat += n / n_fats;
+ }
+
+ /* Determine number of clusters and final check of validity of the FAT sub-type */
+ if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume */
+ n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;
+ if (fmt == FS_FAT32) {
+ if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */
+ if (au == 0 && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */
+ LEAVE_MKFS(FR_MKFS_ABORTED);
+ }
+ }
+ if (fmt == FS_FAT16) {
+ if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */
+ if (au == 0 && (pau * 2) <= 64) {
+ au = pau * 2; continue; /* Adjust cluster size and retry */
+ }
+ if ((opt & FM_FAT32)) {
+ fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */
+ }
+ if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
+ LEAVE_MKFS(FR_MKFS_ABORTED);
+ }
+ if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */
+ if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
+ LEAVE_MKFS(FR_MKFS_ABORTED);
+ }
+ }
+ if (fmt == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */
+
+ /* Ok, it is the valid cluster configuration */
+ break;
+ } while (1);
+
+#if FF_USE_TRIM
+ tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */
+ disk_ioctl(pdrv, CTRL_TRIM, tbl);
+#endif
+ /* Create FAT VBR */
+ mem_set(buf, 0, ss);
+ /* Boot jump code (x86), OEM name */
+ if (!(opt & FM_PRF2)) mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "NYX1.8.0", 11);
+ else mem_cpy(buf + BS_JmpBoot, "\xEB\xE9\x90\x00\x00\x00\x00\x00\x00\x00\x00", 11);
+ st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */
+ buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
+ st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
+ buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */
+ st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */
+ if (sz_vol < 0x10000) {
+ st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
+ } else {
+ st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */
+ }
+ buf[BPB_Media] = 0xF8; /* Media descriptor byte */
+ st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
+ st_word(buf + BPB_NumHeads, (opt & FM_PRF2) ? 16 : 255); /* Number of heads (for int13) */
+ st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */
+ if (fmt == FS_FAT32) {
+ st_dword(buf + BS_VolID32, (opt & FM_PRF2) ? 0 : GET_FATTIME()); /* VSN */
+ st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */
+ st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */
+ st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */
+ st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */
+ buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */
+ buf[BS_BootSig32] = 0x29; /* Extended boot signature */
+ /* Volume label, FAT signature */
+ if (!(opt & FM_PRF2)) mem_cpy(buf + BS_VolLab32, FF_MKFS_LABEL "FAT32 ", 19);
+ else mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19);
+ } else {
+ st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */
+ st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */
+ buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */
+ buf[BS_BootSig] = 0x29; /* Extended boot signature */
+ /* Volume label, FAT signature */
+ if (!(opt & FM_PRF2)) mem_cpy(buf + BS_VolLab, FF_MKFS_LABEL "FAT ", 19);
+ else mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19);
+ }
+ st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */
+ if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */
+
+ /* Create FSINFO record if needed */
+ if (fmt == FS_FAT32) {
+ disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */
+ mem_set(buf, 0, ss);
+ st_dword(buf + FSI_LeadSig, 0x41615252);
+ st_dword(buf + FSI_StrucSig, 0x61417272);
+ if (opt & FM_PRF2) {
+ st_dword(buf + FSI_Free_Count, 0xFFFFFFFF); /* Invalidate free count */
+ st_dword(buf + FSI_Nxt_Free, 0xFFFFFFFF); /* Invalidate last allocated cluster */
+ } else {
+ st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */
+ st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */
+ }
+ st_word(buf + BS_55AA, 0xAA55);
+ disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */
+ disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */
+ }
+
+ /* Clear PRF2SAFE info so PrFILE2 can recreate it and upgrade it if old */
+ if (fmt == FS_FAT32 && opt & FM_PRF2) {
+ mem_set(buf, 0, ss);
+ disk_write(pdrv, buf, b_vol + 3, 1); /* Write PRF2SAFE info (VBR + 3) */
+ }
+
+ /* Initialize FAT area */
+ mem_set(buf, 0, (UINT)szb_buf);
+ sect = b_fat; /* FAT start sector */
+ for (i = 0; i < n_fats; i++) { /* Initialize FATs each */
+ if (fmt == FS_FAT32) {
+ st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */
+ st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */
+ st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */
+ } else {
+ st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */
+ }
+ nsect = sz_fat; /* Number of FAT sectors */
+ do { /* Fill FAT sectors */
+ n = (nsect > sz_buf) ? sz_buf : nsect;
+ if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ mem_set(buf, 0, ss);
+ sect += n; nsect -= n;
+ } while (nsect);
+ }
+
+ /* Initialize root directory (fill with zero) */
+ nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */
+ do {
+ n = (nsect > sz_buf) ? sz_buf : nsect;
+ if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ sect += n; nsect -= n;
+ } while (nsect);
+ }
+
+ /* Determine system ID in the partition table */
+ if (FF_FS_EXFAT && fmt == FS_EXFAT) {
+ sys = 0x07; /* HPFS/NTFS/exFAT */
+ } else {
+ if (fmt == FS_FAT32) {
+ sys = 0x0C; /* FAT32X */
+ } else {
+ if (sz_vol >= 0x10000) {
+ sys = 0x06; /* FAT12/16 (large) */
+ } else {
+ sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */
+ }
+ }
+ }
+
+ /* Update partition information */
+ if (FF_MULTI_PARTITION && part != 0) { /* Created in the existing partition */
+ /* Update system ID in the partition table */
+ if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */
+ buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */
+ if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */
+ } else { /* Created as a new single partition */
+ if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */
+ mem_set(buf, 0, ss);
+ st_word(buf + BS_55AA, 0xAA55); /* MBR signature */
+ pte = buf + MBR_Table; /* Create partition table for single partition in the drive */
+ pte[PTE_Boot] = 0; /* Boot indicator */
+ pte[PTE_StHead] = 1; /* Start head */
+ pte[PTE_StSec] = 1; /* Start sector */
+ pte[PTE_StCyl] = 0; /* Start cylinder */
+ pte[PTE_System] = sys; /* System type */
+ n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */
+ pte[PTE_EdHead] = 254; /* End head */
+ pte[PTE_EdSec] = (BYTE)(((n >> 2) & 0xC0) | 63); /* End sector */
+ pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */
+ st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */
+ st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */
+ if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the MBR */
+ }
+ }
+
+ if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+
+ LEAVE_MKFS(FR_OK);
+}
+
+
+
+#if FF_MULTI_PARTITION
+/*-----------------------------------------------------------------------*/
+/* Create Partition Table on the Physical Drive */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+ BYTE pdrv, /* Physical drive number */
+ const DWORD* szt, /* Pointer to the size table for each partitions */
+ void* work /* Pointer to the working buffer (null: use heap memory) */
+)
+{
+ UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
+ BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
+ DSTATUS stat;
+ DWORD sz_disk, sz_part, s_part;
+ FRESULT res;
+
+
+ stat = disk_initialize(pdrv);
+ if (stat & STA_NOINIT) return FR_NOT_READY;
+ if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+ if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
+
+ buf = (BYTE*)work;
+#if FF_USE_LFN == 3
+ if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */
+#endif
+ if (!buf) return FR_NOT_ENOUGH_CORE;
+
+ /* Determine the CHS without any consideration of the drive geometry */
+ for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
+ if (n == 256) n--;
+ e_hd = (BYTE)(n - 1);
+ sz_cyl = 63 * n;
+ tot_cyl = sz_disk / sz_cyl;
+
+ /* Create partition table */
+ mem_set(buf, 0, FF_MAX_SS);
+ p = buf + MBR_Table; b_cyl = 0;
+ for (i = 0; i < 4; i++, p += SZ_PTE) {
+ p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; /* Number of cylinders */
+ if (p_cyl == 0) continue;
+ s_part = (DWORD)sz_cyl * b_cyl;
+ sz_part = (DWORD)sz_cyl * p_cyl;
+ if (i == 0) { /* Exclude first track of cylinder 0 */
+ s_hd = 1;
+ s_part += 63; sz_part -= 63;
+ } else {
+ s_hd = 0;
+ }
+ e_cyl = b_cyl + p_cyl - 1; /* End cylinder */
+ if (e_cyl >= tot_cyl) LEAVE_MKFS(FR_INVALID_PARAMETER);
+
+ /* Set partition table */
+ p[1] = s_hd; /* Start head */
+ p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 1); /* Start sector */
+ p[3] = (BYTE)b_cyl; /* Start cylinder */
+ p[4] = 0x07; /* System type (temporary setting) */
+ p[5] = e_hd; /* End head */
+ p[6] = (BYTE)(((e_cyl >> 2) & 0xC0) | 63); /* End sector */
+ p[7] = (BYTE)e_cyl; /* End cylinder */
+ st_dword(p + 8, s_part); /* Start sector in LBA */
+ st_dword(p + 12, sz_part); /* Number of sectors */
+
+ /* Next partition */
+ b_cyl += p_cyl;
+ }
+ st_word(p, 0xAA55); /* MBR signature (always at offset 510) */
+
+ /* Write it to the MBR */
+ res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+ LEAVE_MKFS(res);
+}
+
+#endif /* FF_MULTI_PARTITION */
+#endif /* FF_USE_MKFS && !FF_FS_READONLY */
+
+
+
+
+#if FF_USE_STRFUNC
+#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
+#error Wrong FF_STRF_ENCODE setting
+#endif
+/*-----------------------------------------------------------------------*/
+/* Get a String from the File */
+/*-----------------------------------------------------------------------*/
+
+TCHAR* f_gets (
+ TCHAR* buff, /* Pointer to the string buffer to read */
+ int len, /* Size of string buffer (items) */
+ FIL* fp /* Pointer to the file object */
+)
+{
+ int nc = 0;
+ TCHAR *p = buff;
+ BYTE s[4];
+ UINT rc;
+ DWORD dc;
+#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
+ WCHAR wc;
+#endif
+#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
+ UINT ct;
+#endif
+
+#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
+ /* Make a room for the character and terminator */
+ if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2;
+ if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4;
+ if (FF_LFN_UNICODE == 3) len -= 1;
+ while (nc < len) {
+#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
+ f_read(fp, s, 1, &rc);
+ if (rc != 1) break;
+ wc = s[0];
+ if (dbc_1st((BYTE)wc)) {
+ f_read(fp, s, 1, &rc);
+ if (rc != 1 || !dbc_2nd(s[0])) continue;
+ wc = wc << 8 | s[0];
+ }
+ dc = ff_oem2uni(wc, CODEPAGE);
+ if (dc == 0) continue;
+#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
+ f_read(fp, s, 2, &rc);
+ if (rc != 2) break;
+ dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+ if (IsSurrogateL(dc)) continue;
+ if (IsSurrogateH(dc)) {
+ f_read(fp, s, 2, &rc);
+ if (rc != 2) break;
+ wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+ if (!IsSurrogateL(wc)) continue;
+ dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF);
+ }
+#else /* Read a character in UTF-8 */
+ f_read(fp, s, 1, &rc);
+ if (rc != 1) break;
+ dc = s[0];
+ if (dc >= 0x80) { /* Multi-byte character? */
+ ct = 0;
+ if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* 2-byte? */
+ if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte? */
+ if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte? */
+ if (ct == 0) continue;
+ f_read(fp, s, ct, &rc); /* Get trailing bytes */
+ if (rc != ct) break;
+ rc = 0;
+ do { /* Merge trailing bytes */
+ if ((s[rc] & 0xC0) != 0x80) break;
+ dc = dc << 6 | (s[rc] & 0x3F);
+ } while (++rc < ct);
+ if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue; /* Wrong encoding? */
+ }
+#endif
+ if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */
+#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
+ if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */
+ *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */
+ dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */
+ }
+ *p++ = (TCHAR)dc; nc++;
+ if (dc == '\n') break; /* End of line? */
+#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
+ if (dc < 0x80) { /* 1-byte */
+ *p++ = (TCHAR)dc;
+ nc++;
+ if (dc == '\n') break; /* End of line? */
+ } else {
+ if (dc < 0x800) { /* 2-byte */
+ *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 2;
+ } else {
+ if (dc < 0x10000) { /* 3-byte */
+ *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
+ *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 3;
+ } else { /* 4-byte */
+ *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07));
+ *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 4;
+ }
+ }
+ }
+#endif
+ }
+
+#else /* Byte-by-byte without any conversion (ANSI/OEM API) */
+ len -= 1; /* Make a room for the terminator */
+ while (nc < len) {
+ f_read(fp, s, 1, &rc);
+ if (rc != 1) break;
+ dc = s[0];
+ if (FF_USE_STRFUNC == 2 && dc == '\r') continue;
+ *p++ = (TCHAR)dc; nc++;
+ if (dc == '\n') break;
+ }
+#endif
+
+ *p = 0; /* Terminate the string */
+ return nc ? buff : 0; /* When no data read due to EOF or error, return with error. */
+}
+
+
+
+
+#if !FF_FS_READONLY
+#include
+/*-----------------------------------------------------------------------*/
+/* Put a Character to the File */
+/*-----------------------------------------------------------------------*/
+
+typedef struct { /* Putchar output buffer and work area */
+ FIL *fp; /* Ptr to the writing file */
+ int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */
+#if FF_USE_LFN && FF_LFN_UNICODE == 1
+ WCHAR hs;
+#elif FF_USE_LFN && FF_LFN_UNICODE == 2
+ BYTE bs[4];
+ UINT wi, ct;
+#endif
+ BYTE buf[64]; /* Write buffer */
+} putbuff;
+
+
+static void putc_bfd ( /* Buffered write with code conversion */
+ putbuff* pb,
+ TCHAR c
+)
+{
+ UINT n;
+ int i, nc;
+#if FF_USE_LFN && FF_LFN_UNICODE
+ WCHAR hs, wc;
+#if FF_LFN_UNICODE == 2
+ DWORD dc;
+ TCHAR *tp;
+#endif
+#endif
+
+ if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */
+ putc_bfd(pb, '\r');
+ }
+
+ i = pb->idx; /* Write index of pb->buf[] */
+ if (i < 0) return;
+ nc = pb->nchr; /* Write unit counter */
+
+#if FF_USE_LFN && FF_LFN_UNICODE
+#if FF_LFN_UNICODE == 1 /* UTF-16 input */
+ if (IsSurrogateH(c)) {
+ pb->hs = c; return;
+ }
+ hs = pb->hs; pb->hs = 0;
+ if (hs != 0) {
+ if (!IsSurrogateL(c)) hs = 0;
+ } else {
+ if (IsSurrogateL(c)) return;
+ }
+ wc = c;
+#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
+ for (;;) {
+ if (pb->ct == 0) { /* Out of multi-byte sequence? */
+ pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */
+ if ((BYTE)c < 0x80) break; /* 1-byte? */
+ if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte? */
+ if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte? */
+ if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte? */
+ return;
+ } else { /* In the multi-byte sequence */
+ if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */
+ pb->ct = 0; continue;
+ }
+ pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */
+ if (--pb->ct == 0) break; /* End of multi-byte sequence? */
+ return;
+ }
+ }
+ tp = (TCHAR*)pb->bs;
+ dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
+ if (dc == 0xFFFFFFFF) return;
+ wc = (WCHAR)dc;
+ hs = (WCHAR)(dc >> 16);
+#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
+ if (IsSurrogate(c) || c >= 0x110000) return;
+ if (c >= 0x10000) {
+ hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */
+ wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */
+ } else {
+ hs = 0;
+ wc = (WCHAR)c;
+ }
+#endif
+
+#if FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */
+ if (hs != 0) {
+ st_word(&pb->buf[i], hs);
+ i += 2;
+ nc++;
+ }
+ st_word(&pb->buf[i], wc);
+ i += 2;
+#elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */
+ if (hs != 0) {
+ pb->buf[i++] = (BYTE)(hs >> 8);
+ pb->buf[i++] = (BYTE)hs;
+ nc++;
+ }
+ pb->buf[i++] = (BYTE)(wc >> 8);
+ pb->buf[i++] = (BYTE)wc;
+#elif FF_STRF_ENCODE == 3 /* Write it in UTF-8 */
+ if (hs != 0) { /* 4-byte */
+ nc += 3;
+ hs = (hs & 0x3FF) + 0x40;
+ pb->buf[i++] = (BYTE)(0xF0 | hs >> 8);
+ pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F));
+ pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F));
+ pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
+ } else {
+ if (wc < 0x80) { /* 1-byte */
+ pb->buf[i++] = (BYTE)wc;
+ } else {
+ if (wc < 0x800) { /* 2-byte */
+ nc += 1;
+ pb->buf[i++] = (BYTE)(0xC0 | wc >> 6);
+ } else { /* 3-byte */
+ nc += 2;
+ pb->buf[i++] = (BYTE)(0xE0 | wc >> 12);
+ pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F));
+ }
+ pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
+ }
+ }
+#else /* Write it in ANSI/OEM */
+ if (hs != 0) return;
+ wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */
+ if (wc == 0) return;
+ if (wc >= 0x100) {
+ pb->buf[i++] = (BYTE)(wc >> 8); nc++;
+ }
+ pb->buf[i++] = (BYTE)wc;
+#endif
+
+#else /* ANSI/OEM input (without re-encode) */
+ pb->buf[i++] = (BYTE)c;
+#endif
+
+ if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */
+ f_write(pb->fp, pb->buf, (UINT)i, &n);
+ i = (n == (UINT)i) ? 0 : -1;
+ }
+ pb->idx = i;
+ pb->nchr = nc + 1;
+}
+
+
+static int putc_flush ( /* Flush left characters in the buffer */
+ putbuff* pb
+)
+{
+ UINT nw;
+
+ if ( pb->idx >= 0 /* Flush buffered characters to the file */
+ && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
+ && (UINT)pb->idx == nw) return pb->nchr;
+ return EOF;
+}
+
+
+static void putc_init ( /* Initialize write buffer */
+ putbuff* pb,
+ FIL* fp
+)
+{
+ mem_set(pb, 0, sizeof (putbuff));
+ pb->fp = fp;
+}
+
+
+
+int f_putc (
+ TCHAR c, /* A character to be output */
+ FIL* fp /* Pointer to the file object */
+)
+{
+ putbuff pb;
+
+
+ putc_init(&pb, fp);
+ putc_bfd(&pb, c); /* Put the character */
+ return putc_flush(&pb);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a String to the File */
+/*-----------------------------------------------------------------------*/
+
+int f_puts (
+ const TCHAR* str, /* Pointer to the string to be output */
+ FIL* fp /* Pointer to the file object */
+)
+{
+ putbuff pb;
+
+ if (str == (void *)0) return EOF; /* String is NULL */
+
+ putc_init(&pb, fp);
+ while (*str) putc_bfd(&pb, *str++); /* Put the string */
+ return putc_flush(&pb);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a Formatted String to the File */
+/*-----------------------------------------------------------------------*/
+
+int f_printf (
+ FIL* fp, /* Pointer to the file object */
+ const TCHAR* fmt, /* Pointer to the format string */
+ ... /* Optional arguments... */
+)
+{
+ va_list arp;
+ putbuff pb;
+ BYTE f, r;
+ UINT i, j, w;
+ DWORD v;
+ TCHAR c, d, str[32], *p;
+
+ if (fmt == (void *)0) return EOF; /* String is NULL */
+
+ putc_init(&pb, fp);
+
+ va_start(arp, fmt);
+
+ for (;;) {
+ c = *fmt++;
+ if (c == 0) break; /* End of string */
+ if (c != '%') { /* Non escape character */
+ putc_bfd(&pb, c);
+ continue;
+ }
+ w = f = 0;
+ c = *fmt++;
+ if (c == '0') { /* Flag: '0' padding */
+ f = 1; c = *fmt++;
+ } else {
+ if (c == '-') { /* Flag: left justified */
+ f = 2; c = *fmt++;
+ }
+ }
+ if (c == '*') { /* Minimum width by argument */
+ w = va_arg(arp, int);
+ c = *fmt++;
+ } else {
+ while (IsDigit(c)) { /* Minimum width */
+ w = w * 10 + c - '0';
+ c = *fmt++;
+ }
+ }
+ if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */
+ f |= 4; c = *fmt++;
+ }
+ if (c == 0) break;
+ d = c;
+ if (IsLower(d)) d -= 0x20;
+ switch (d) { /* Atgument type is... */
+ case 'S' : /* String */
+ p = va_arg(arp, TCHAR*);
+ for (j = 0; p[j]; j++) ;
+ if (!(f & 2)) { /* Right padded */
+ while (j++ < w) putc_bfd(&pb, ' ') ;
+ }
+ while (*p) putc_bfd(&pb, *p++) ; /* String body */
+ while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */
+ continue;
+
+ case 'C' : /* Character */
+ putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
+
+ case 'B' : /* Unsigned binary */
+ r = 2; break;
+
+ case 'O' : /* Unsigned octal */
+ r = 8; break;
+
+ case 'D' : /* Signed decimal */
+ case 'U' : /* Unsigned decimal */
+ r = 10; break;
+
+ case 'X' : /* Unsigned hexdecimal */
+ r = 16; break;
+
+ default: /* Unknown type (pass-through) */
+ putc_bfd(&pb, c); continue;
+ }
+
+ /* Get an argument and put it in numeral */
+ v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
+ if (d == 'D' && (v & 0x80000000)) {
+ v = 0 - v;
+ f |= 8;
+ }
+ i = 0;
+ do {
+ d = (TCHAR)(v % r); v /= r;
+ if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+ str[i++] = d + '0';
+ } while (v && i < sizeof str / sizeof *str);
+ if (f & 8) str[i++] = '-';
+ j = i; d = (f & 1) ? '0' : ' ';
+ if (!(f & 2)) {
+ while (j++ < w) putc_bfd(&pb, d); /* Right pad */
+ }
+ do {
+ putc_bfd(&pb, str[--i]); /* Number body */
+ } while (i);
+ while (j++ < w) putc_bfd(&pb, d); /* Left pad */
+ }
+
+ va_end(arp);
+
+ return putc_flush(&pb);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_STRFUNC */
+
+
+
+#if FF_CODE_PAGE == 0
+/*-----------------------------------------------------------------------*/
+/* Set Active Codepage for the Path Name */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setcp (
+ WORD cp /* Value to be set as active code page */
+)
+{
+ static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0};
+ static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0};
+ UINT i;
+
+
+ for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */
+ if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */
+
+ CodePage = cp;
+ if (cp >= 900) { /* DBCS */
+ ExCvt = 0;
+ DbcTbl = tables[i];
+ } else { /* SBCS */
+ ExCvt = tables[i];
+ DbcTbl = 0;
+ }
+ return FR_OK;
+}
+#endif /* FF_CODE_PAGE == 0 */
diff --git a/bdk/libs/fatfs/ff.h b/bdk/libs/fatfs/ff.h
index 8a37029..720fee6 100644
--- a/bdk/libs/fatfs/ff.h
+++ b/bdk/libs/fatfs/ff.h
@@ -1,391 +1,396 @@
-/*----------------------------------------------------------------------------/
-/ FatFs - Generic FAT Filesystem module R0.13c /
-/-----------------------------------------------------------------------------/
-/
-/ Copyright (C) 2018, ChaN, all right reserved.
-/
-/ FatFs module is an open source software. Redistribution and use of FatFs in
-/ source and binary forms, with or without modification, are permitted provided
-/ that the following condition is met:
-
-/ 1. Redistributions of source code must retain the above copyright notice,
-/ this condition and the following disclaimer.
-/
-/ This software is provided by the copyright holder and contributors "AS IS"
-/ and any warranties related to this software are DISCLAIMED.
-/ The copyright owner or contributors be NOT LIABLE for any damages caused
-/ by use of this software.
-/
-/----------------------------------------------------------------------------*/
-
-
-#ifndef FF_DEFINED
-#define FF_DEFINED 86604 /* Revision ID */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include /* Basic integer types */
-#include /* FatFs configuration options */
-
-#if FF_DEFINED != FFCONF_DEF
-#error Wrong configuration file (ffconf.h).
-#endif
-
-
-
-/* Definitions of volume management */
-
-#if FF_MULTI_PARTITION /* Multiple partition configuration */
-typedef struct {
- BYTE pd; /* Physical drive number */
- BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
-} PARTITION;
-extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
-#endif
-
-#if FF_STR_VOLUME_ID
-#ifndef FF_VOLUME_STRS
-extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
-#endif
-#endif
-
-
-
-/* Type of path name strings on FatFs API */
-
-#ifndef _INC_TCHAR
-#define _INC_TCHAR
-
-#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
-typedef WCHAR TCHAR;
-#define _T(x) L ## x
-#define _TEXT(x) L ## x
-#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
-typedef char TCHAR;
-#define _T(x) u8 ## x
-#define _TEXT(x) u8 ## x
-#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
-typedef DWORD TCHAR;
-#define _T(x) U ## x
-#define _TEXT(x) U ## x
-#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
-#error Wrong FF_LFN_UNICODE setting
-#else /* ANSI/OEM code in SBCS/DBCS */
-typedef char TCHAR;
-#define _T(x) x
-#define _TEXT(x) x
-#endif
-
-#endif
-
-
-
-/* Type of file size variables */
-
-#if FF_FS_EXFAT
-typedef QWORD FSIZE_t;
-#else
-typedef DWORD FSIZE_t;
-#endif
-
-
-
-/* Filesystem object structure (FATFS) */
-
-typedef struct {
- BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
- BYTE fs_type; /* Filesystem type (0:not mounted) */
- BYTE pdrv; /* Associated physical drive */
- BYTE n_fats; /* Number of FATs (1 or 2) */
- BYTE wflag; /* win[] flag (b0:dirty) */
- BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
- WORD id; /* Volume mount ID */
- WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
- WORD csize; /* Cluster size [sectors] */
-#if FF_MAX_SS != FF_MIN_SS
- WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
-#endif
-#if FF_USE_LFN
- WCHAR* lfnbuf; /* LFN working buffer */
-#endif
-#if FF_FS_EXFAT
- BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
-#endif
-#if FF_FS_REENTRANT
- FF_SYNC_t sobj; /* Identifier of sync object */
-#endif
-#if !FF_FS_READONLY
- DWORD last_clst; /* Last allocated cluster */
- DWORD free_clst; /* Number of free clusters */
-#endif
-#if FF_FS_RPATH
- DWORD cdir; /* Current directory start cluster (0:root) */
-#if FF_FS_EXFAT
- DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
- DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
- DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
-#endif
-#endif
- DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
- DWORD fsize; /* Size of an FAT [sectors] */
- DWORD volbase; /* Volume base sector */
- DWORD fatbase; /* FAT base sector */
- DWORD dirbase; /* Root directory base sector/cluster */
- DWORD database; /* Data base sector */
-#if FF_FS_EXFAT
- DWORD bitbase; /* Allocation bitmap base sector */
-#endif
- DWORD winsect; /* Current sector appearing in the win[] */
-} FATFS;
-
-
-
-/* Object ID and allocation information (FFOBJID) */
-
-typedef struct {
- FATFS* fs; /* Pointer to the hosting volume of this object */
- WORD id; /* Hosting volume mount ID */
- BYTE attr; /* Object attribute */
- BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
- DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
- FSIZE_t objsize; /* Object size (valid when sclust != 0) */
-#if FF_FS_EXFAT
- DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
- DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
- DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
- DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
- DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
-#endif
-#if FF_FS_LOCK
- UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
-#endif
-} FFOBJID;
-
-
-
-/* File object structure (FIL) */
-
-typedef struct {
-#if !FF_FS_TINY
- BYTE buf[FF_MAX_SS]; /* File private data read/write window */
-#endif
- FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
- BYTE flag; /* File status flags */
- BYTE err; /* Abort flag (error code) */
- FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
- DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
- DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
-#if !FF_FS_READONLY
- DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
- BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
-#endif
-#if FF_USE_FASTSEEK
- DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
-#endif
-} FIL;
-
-
-
-/* Directory object structure (DIR) */
-
-typedef struct {
- FFOBJID obj; /* Object identifier */
- DWORD dptr; /* Current read/write offset */
- DWORD clust; /* Current cluster */
- DWORD sect; /* Current sector (0:Read operation has terminated) */
- BYTE* dir; /* Pointer to the directory item in the win[] */
- BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
-#if FF_USE_LFN
- DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
-#endif
-#if FF_USE_FIND
- const TCHAR* pat; /* Pointer to the name matching pattern */
-#endif
-} DIR;
-
-
-
-/* File information structure (FILINFO) */
-
-typedef struct {
- FSIZE_t fsize; /* File size */
- WORD fdate; /* Modified date */
- WORD ftime; /* Modified time */
- BYTE fattrib; /* File attribute */
-#if FF_USE_LFN
- TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
- TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
-#else
- TCHAR fname[12 + 1]; /* File name */
-#endif
-} FILINFO;
-
-
-
-/* File function return code (FRESULT) */
-
-typedef enum {
- FR_OK = 0, /* (0) Succeeded */
- FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
- FR_INT_ERR, /* (2) Assertion failed */
- FR_NOT_READY, /* (3) The physical drive cannot work */
- FR_NO_FILE, /* (4) Could not find the file */
- FR_NO_PATH, /* (5) Could not find the path */
- FR_INVALID_NAME, /* (6) The path name format is invalid */
- FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
- FR_EXIST, /* (8) Access denied due to prohibited access */
- FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
- FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
- FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
- FR_NOT_ENABLED, /* (12) The volume has no work area */
- FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
- FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
- FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
- FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
- FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
- FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
-#if FF_FASTFS
- FR_INVALID_PARAMETER, /* (19) Given parameter is invalid */
- FR_CLTBL_NO_INIT /* (20) The cluster table for fast seek/read/write was not created */
-#else
- FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
-#endif
-} FRESULT;
-
-
-
-/*--------------------------------------------------------------*/
-/* FatFs module application interface */
-
-FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
-FRESULT f_close (FIL* fp); /* Close an open file object */
-FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
-FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
-FRESULT f_read_fast (FIL* fp, const void* buff, UINT btr); /* Fast read data from the file */
-FRESULT f_write_fast (FIL* fp, const void* buff, UINT btw); /* Fast write data to the file */
-FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
-FRESULT f_truncate (FIL* fp); /* Truncate the file */
-FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
-FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
-FRESULT f_closedir (DIR* dp); /* Close an open directory */
-FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
-FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
-FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
-FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
-FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
-FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
-FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
-FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
-FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
-FRESULT f_chdir (const TCHAR* path); /* Change current directory */
-FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
-FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
-FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
-FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
-FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
-FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
-DWORD *f_expand_cltbl (FIL* fp, UINT tblsz, FSIZE_t ofs); /* Expand file and populate cluster table */
-FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
-FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
-FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
-FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
-FRESULT f_fdisk_mod (BYTE pdrv, const DWORD* szt, void* work); // Modded version of f_fdisk that works:tm:
-FRESULT f_setcp (WORD cp); /* Set current code page */
-int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
-int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
-int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
-TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
-
-#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
-#define f_error(fp) ((fp)->err)
-#define f_tell(fp) ((fp)->fptr)
-#define f_size(fp) ((fp)->obj.objsize)
-#define f_rewind(fp) f_lseek((fp), 0)
-#define f_rewinddir(dp) f_readdir((dp), 0)
-#define f_rmdir(path) f_unlink(path)
-#define f_unmount(path) f_mount(0, path, 0)
-
-#ifndef EOF
-#define EOF (-1)
-#endif
-
-
-
-
-/*--------------------------------------------------------------*/
-/* Additional user defined functions */
-
-/* RTC function */
-#if !FF_FS_READONLY && !FF_FS_NORTC
-DWORD get_fattime (void);
-#endif
-
-/* LFN support functions */
-#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
-WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
-WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
-DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
-#endif
-#if FF_USE_LFN == 3 /* Dynamic memory allocation */
-void* ff_memalloc (UINT msize); /* Allocate memory block */
-void ff_memfree (void* mblock); /* Free memory block */
-#endif
-
-/* Sync functions */
-#if FF_FS_REENTRANT
-int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
-int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
-void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
-int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
-#endif
-
-
-
-
-/*--------------------------------------------------------------*/
-/* Flags and offset address */
-
-
-/* File access mode and open method flags (3rd argument of f_open) */
-#define FA_READ 0x01
-#define FA_WRITE 0x02
-#define FA_OPEN_EXISTING 0x00
-#define FA_CREATE_NEW 0x04
-#define FA_CREATE_ALWAYS 0x08
-#define FA_OPEN_ALWAYS 0x10
-#define FA_OPEN_APPEND 0x30
-
-/* Fast seek controls (2nd argument of f_lseek) */
-#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
-
-/* Format options (2nd argument of f_mkfs) */
-#define FM_FAT 0x01
-#define FM_FAT32 0x02
-#define FM_EXFAT 0x04
-#define FM_ANY 0x07
-#define FM_SFD 0x08
-
-/* Filesystem type (FATFS.fs_type) */
-#define FS_FAT12 1
-#define FS_FAT16 2
-#define FS_FAT32 3
-#define FS_EXFAT 4
-
-/* File attribute bits for directory entry (FILINFO.fattrib) */
-#define AM_RDO 0x01 /* Read only */
-#define AM_HID 0x02 /* Hidden */
-#define AM_SYS 0x04 /* System */
-#define AM_VOL 0x08 /* Volume */
-#define AM_DIR 0x10 /* Directory */
-#define AM_ARC 0x20 /* Archive */
-#define AM_DEV 0x40 /* Device */
-#define AM_RVD 0x80 /* Reserved */
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* FF_DEFINED */
+/*----------------------------------------------------------------------------/
+/ FatFs - Generic FAT Filesystem module R0.13c /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2018, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+
+/ 1. Redistributions of source code must retain the above copyright notice,
+/ this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/
+/----------------------------------------------------------------------------*/
+
+
+#ifndef FF_DEFINED
+#define FF_DEFINED 86604 /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include /* Basic integer types */
+#include /* FatFs configuration options */
+
+#if FF_DEFINED != FFCONF_DEF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+
+/* Definitions of volume management */
+
+#if FF_MULTI_PARTITION /* Multiple partition configuration */
+typedef struct {
+ BYTE pd; /* Physical drive number */
+ BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifndef FF_VOLUME_STRS
+extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
+#endif
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#ifndef _INC_TCHAR
+#define _INC_TCHAR
+
+#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
+typedef char TCHAR;
+#define _T(x) u8 ## x
+#define _TEXT(x) u8 ## x
+#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
+typedef DWORD TCHAR;
+#define _T(x) U ## x
+#define _TEXT(x) U ## x
+#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
+#error Wrong FF_LFN_UNICODE setting
+#else /* ANSI/OEM code in SBCS/DBCS */
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* Type of file size variables */
+
+#if FF_FS_EXFAT
+typedef QWORD FSIZE_t;
+#else
+typedef DWORD FSIZE_t;
+#endif
+
+
+
+/* Filesystem object structure (FATFS) */
+
+typedef struct {
+ BYTE fs_type; /* Filesystem type (0:not mounted) */
+ BYTE part_type; /* Partition type (0:MBR, 1:GPT) */
+ BYTE pdrv; /* Associated physical drive */
+ BYTE n_fats; /* Number of FATs (1 or 2) */
+ BYTE wflag; /* win[] flag (b0:dirty) */
+ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
+ WORD id; /* Volume mount ID */
+ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
+ WORD csize; /* Cluster size [sectors] */
+#if FF_MAX_SS != FF_MIN_SS
+ WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
+#endif
+#if FF_USE_LFN
+ WCHAR* lfnbuf; /* LFN working buffer */
+#endif
+#if FF_FS_EXFAT
+ BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
+#endif
+#if FF_FS_REENTRANT
+ FF_SYNC_t sobj; /* Identifier of sync object */
+#endif
+#if !FF_FS_READONLY
+ DWORD last_clst; /* Last allocated cluster */
+ DWORD free_clst; /* Number of free clusters */
+#endif
+#if FF_FS_RPATH
+ DWORD cdir; /* Current directory start cluster (0:root) */
+#if FF_FS_EXFAT
+ DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
+ DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
+ DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
+#endif
+#endif
+ DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
+ DWORD fsize; /* Size of an FAT [sectors] */
+ DWORD volbase; /* Volume base sector */
+ DWORD fatbase; /* FAT base sector */
+ DWORD dirbase; /* Root directory base sector/cluster */
+ DWORD database; /* Data base sector */
+#if FF_FS_EXFAT
+ DWORD bitbase; /* Allocation bitmap base sector */
+#endif
+ DWORD winsect; /* Current sector appearing in the win[] */
+ BYTE win[FF_MAX_SS] __attribute__((aligned(8))); /* Disk access window for Directory, FAT (and file data at tiny cfg). DMA aligned. */
+} FATFS;
+
+
+
+/* Object ID and allocation information (FFOBJID) */
+
+typedef struct {
+ FATFS* fs; /* Pointer to the hosting volume of this object */
+ WORD id; /* Hosting volume mount ID */
+ BYTE attr; /* Object attribute */
+ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
+ DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
+ FSIZE_t objsize; /* Object size (valid when sclust != 0) */
+#if FF_FS_EXFAT
+ DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
+ DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
+ DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
+ DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
+ DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
+#endif
+#if FF_FS_LOCK
+ UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
+#endif
+} FFOBJID;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+ FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
+ BYTE flag; /* File status flags */
+ BYTE err; /* Abort flag (error code) */
+ FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
+ DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
+ DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
+#if !FF_FS_READONLY
+ DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
+ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
+#endif
+#if FF_USE_FASTSEEK
+ DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
+#endif
+#if !FF_FS_TINY
+ BYTE buf[FF_MAX_SS] __attribute__((aligned(8))); /* File private data read/write window. DMA aligned. */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (DIR) */
+
+typedef struct {
+ FFOBJID obj; /* Object identifier */
+ DWORD dptr; /* Current read/write offset */
+ DWORD clust; /* Current cluster */
+ DWORD sect; /* Current sector (0:Read operation has terminated) */
+ BYTE* dir; /* Pointer to the directory item in the win[] */
+ BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
+#if FF_USE_LFN
+ DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
+#endif
+#if FF_USE_FIND
+ const TCHAR* pat; /* Pointer to the name matching pattern */
+#endif
+} DIR;
+
+
+
+/* File information structure (FILINFO) */
+
+typedef struct {
+ FSIZE_t fsize; /* File size */
+ WORD fdate; /* Modified date */
+ WORD ftime; /* Modified time */
+ BYTE fattrib; /* File attribute */
+#if FF_USE_LFN
+ TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
+ TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
+#else
+ TCHAR fname[12 + 1]; /* File name */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+ FR_OK = 0, /* (0) Succeeded */
+ FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
+ FR_INT_ERR, /* (2) Assertion failed */
+ FR_NOT_READY, /* (3) The physical drive cannot work */
+ FR_NO_FILE, /* (4) Could not find the file */
+ FR_NO_PATH, /* (5) Could not find the path */
+ FR_INVALID_NAME, /* (6) The path name format is invalid */
+ FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
+ FR_EXIST, /* (8) Access denied due to prohibited access */
+ FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
+ FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
+ FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
+ FR_NOT_ENABLED, /* (12) The volume has no work area */
+ FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
+ FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
+ FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
+ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
+ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
+ FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
+#if FF_FASTFS
+ FR_INVALID_PARAMETER, /* (19) Given parameter is invalid */
+ FR_CLTBL_NO_INIT /* (20) The cluster table for fast seek/read/write was not created */
+#else
+ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
+#endif
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface */
+
+FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
+FRESULT f_close (FIL* fp); /* Close an open file object */
+FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
+FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
+#if FF_FASTFS /* buff needs to be block aligned. Intercluster data access is not supported. */
+FRESULT f_read_fast (FIL* fp, const void* buff, UINT btr); /* Fast read data from the file */
+FRESULT f_write_fast (FIL* fp, const void* buff, UINT btw); /* Fast write data to the file */
+#endif
+FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
+FRESULT f_truncate (FIL* fp); /* Truncate the file */
+FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
+FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
+FRESULT f_closedir (DIR* dp); /* Close an open directory */
+FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
+FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
+FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
+FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
+FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
+FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
+FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
+FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
+FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
+FRESULT f_chdir (const TCHAR* path); /* Change current directory */
+FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
+FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
+FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
+FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
+FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
+FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
+#if FF_FASTFS
+DWORD *f_expand_cltbl (FIL* fp, UINT tblsz, FSIZE_t ofs); /* Expand file and populate cluster table */
+#endif
+FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
+FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
+FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
+FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
+FRESULT f_setcp (WORD cp); /* Set current code page */
+int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
+int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
+int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
+
+#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
+#define f_error(fp) ((fp)->err)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->obj.objsize)
+#define f_rewind(fp) f_lseek((fp), 0)
+#define f_rewinddir(dp) f_readdir((dp), 0)
+#define f_rmdir(path) f_unlink(path)
+#define f_unmount(path) f_mount(0, path, 0)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions */
+
+/* RTC function */
+#if !FF_FS_READONLY && !FF_FS_NORTC
+DWORD get_fattime (void);
+#endif
+
+/* LFN support functions */
+#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
+WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
+WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
+DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
+#endif
+#if FF_USE_LFN == 3 /* Dynamic memory allocation */
+void* ff_memalloc (UINT msize); /* Allocate memory block */
+void ff_memfree (void* mblock); /* Free memory block */
+#endif
+
+/* Sync functions */
+#if FF_FS_REENTRANT
+int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
+int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
+void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
+int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address */
+
+
+/* File access mode and open method flags (3rd argument of f_open) */
+#define FA_READ 0x01
+#define FA_WRITE 0x02
+#define FA_OPEN_EXISTING 0x00
+#define FA_CREATE_NEW 0x04
+#define FA_CREATE_ALWAYS 0x08
+#define FA_OPEN_ALWAYS 0x10
+#define FA_OPEN_APPEND 0x30
+
+/* Fast seek controls (2nd argument of f_lseek) */
+#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
+
+/* Format options (2nd argument of f_mkfs) */
+#define FM_FAT 0x01
+#define FM_FAT32 0x02
+#define FM_EXFAT 0x04
+#define FM_ANY 0x07
+#define FM_SFD 0x08
+#define FM_PRF2 0x10
+
+/* Filesystem type (FATFS.fs_type) */
+#define FS_FAT12 1
+#define FS_FAT16 2
+#define FS_FAT32 3
+#define FS_EXFAT 4
+
+/* File attribute bits for directory entry (FILINFO.fattrib) */
+#define AM_RDO 0x01 /* Read only */
+#define AM_HID 0x02 /* Hidden */
+#define AM_SYS 0x04 /* System */
+#define AM_VOL 0x08 /* Volume */
+#define AM_DIR 0x10 /* Directory */
+#define AM_ARC 0x20 /* Archive */
+#define AM_DEV 0x40 /* Device */
+#define AM_RVD 0x80 /* Reserved */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FF_DEFINED */
diff --git a/source/libs/fatfs/ffsystem.c b/bdk/libs/fatfs/ffsystem.c
similarity index 60%
rename from source/libs/fatfs/ffsystem.c
rename to bdk/libs/fatfs/ffsystem.c
index 7addf30..b4af454 100644
--- a/source/libs/fatfs/ffsystem.c
+++ b/bdk/libs/fatfs/ffsystem.c
@@ -1,39 +1,57 @@
-/*------------------------------------------------------------------------*/
-/* Sample Code of OS Dependent Functions for FatFs */
-/* (C) ChaN, 2018 */
-/* (C) CTCaer, 2018 */
-/*------------------------------------------------------------------------*/
-
-
-#include
-#include
-
-
-
-#if FF_USE_LFN == 3 /* Dynamic memory allocation */
-
-/*------------------------------------------------------------------------*/
-/* Allocate a memory block */
-/*------------------------------------------------------------------------*/
-
-void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
- UINT msize /* Number of bytes to allocate */
-)
-{
- return malloc(msize); /* Allocate a new memory block with POSIX API */
-}
-
-
-/*------------------------------------------------------------------------*/
-/* Free a memory block */
-/*------------------------------------------------------------------------*/
-
-void ff_memfree (
- void* mblock /* Pointer to the memory block to free (nothing to do if null) */
-)
-{
- free(mblock); /* Free the memory block with POSIX API */
-}
-
-#endif
-
+/*------------------------------------------------------------------------*/
+/* Sample Code of OS Dependent Functions for FatFs */
+/* (C) ChaN, 2018 */
+/* (C) CTCaer, 2018-2024 */
+/*------------------------------------------------------------------------*/
+
+#include
+
+#include
+
+#if FF_USE_LFN == 3 /* Dynamic memory allocation */
+
+/*------------------------------------------------------------------------*/
+/* Allocate a memory block */
+/*------------------------------------------------------------------------*/
+
+void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
+ UINT msize /* Number of bytes to allocate */
+)
+{
+ // Ensure size is aligned to SDMMC block size.
+ return malloc(ALIGN(msize, SDMMC_DAT_BLOCKSIZE)); /* Allocate a new memory block with POSIX API */
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Free a memory block */
+/*------------------------------------------------------------------------*/
+
+void ff_memfree (
+ void* mblock /* Pointer to the memory block to free (nothing to do if null) */
+)
+{
+ free(mblock); /* Free the memory block with POSIX API */
+}
+
+#endif
+
+#if FF_FS_NORTC == 0
+
+/*------------------------------------------------------------------------*/
+/* Get real time clock */
+/*------------------------------------------------------------------------*/
+
+DWORD get_fattime (
+ void
+)
+{
+ rtc_time_t time;
+
+ max77620_rtc_get_time_adjusted(&time);
+
+ return (((DWORD)(time.year - 1980) << 25) | ((DWORD)time.month << 21) | ((DWORD)time.day << 16) |
+ ((DWORD)time.hour << 11) | ((DWORD)time.min << 5) | (time.sec >> 1));
+}
+
+#endif
diff --git a/bdk/libs/fatfs/ffunicode.c b/bdk/libs/fatfs/ffunicode.c
index d4f7d81..9f03963 100644
--- a/bdk/libs/fatfs/ffunicode.c
+++ b/bdk/libs/fatfs/ffunicode.c
@@ -1,625 +1,625 @@
-/*------------------------------------------------------------------------*/
-/* Unicode handling functions for FatFs R0.13c */
-/*------------------------------------------------------------------------*/
-/* This module will occupy a huge memory in the .const section when the /
-/ FatFs is configured for LFN with DBCS. If the system has any Unicode /
-/ utilitiy for the code conversion, this module should be modified to use /
-/ that function to avoid silly memory consumption. /
-/-------------------------------------------------------------------------*/
-/*
-/ Copyright (C) 2018, ChaN, all right reserved.
-/
-/ FatFs module is an open source software. Redistribution and use of FatFs in
-/ source and binary forms, with or without modification, are permitted provided
-/ that the following condition is met:
-/
-/ 1. Redistributions of source code must retain the above copyright notice,
-/ this condition and the following disclaimer.
-/
-/ This software is provided by the copyright holder and contributors "AS IS"
-/ and any warranties related to this software are DISCLAIMED.
-/ The copyright owner or contributors be NOT LIABLE for any damages caused
-/ by use of this software.
-*/
-
-
-#include "ff.h"
-
-#if FF_USE_LFN /* This module will be blanked at non-LFN configuration */
-
-#if FF_DEFINED != 86604 /* Revision ID */
-#error Wrong include file (ff.h).
-#endif
-
-#define MERGE2(a, b) a ## b
-#define CVTBL(tbl, cp) MERGE2(tbl, cp)
-
-/*------------------------------------------------------------------------*/
-/* Code Conversion Tables */
-/*------------------------------------------------------------------------*/
-
-#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0
-static const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
- 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
- 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 720 || FF_CODE_PAGE == 0
-static const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */
- 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
- 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
- 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 737 || FF_CODE_PAGE == 0
-static const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */
- 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
- 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
- 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
- 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 771 || FF_CODE_PAGE == 0
-static const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
- 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
- 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,
- 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
- 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 775 || FF_CODE_PAGE == 0
-static const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */
- 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
- 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
- 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
- 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
- 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 850 || FF_CODE_PAGE == 0
-static const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
- 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
- 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
- 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
- 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 852 || FF_CODE_PAGE == 0
-static const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
- 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
- 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
- 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
- 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 855 || FF_CODE_PAGE == 0
-static const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */
- 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
- 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
- 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
- 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
- 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
- 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 857 || FF_CODE_PAGE == 0
-static const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
- 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
- 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
- 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
- 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 860 || FF_CODE_PAGE == 0
-static const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
- 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
- 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 861 || FF_CODE_PAGE == 0
-static const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
- 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
- 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 862 || FF_CODE_PAGE == 0
-static const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */
- 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
- 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
- 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 863 || FF_CODE_PAGE == 0
-static const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
- 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
- 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,
- 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 864 || FF_CODE_PAGE == 0
-static const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */
- 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
- 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
- 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
- 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,
- 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
- 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,
- 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,
- 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000
-};
-#endif
-#if FF_CODE_PAGE == 865 || FF_CODE_PAGE == 0
-static const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */
- 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
- 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
- 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
- 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 866 || FF_CODE_PAGE == 0
-static const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
- 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
- 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
- 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
- 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
- 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
-};
-#endif
-#if FF_CODE_PAGE == 869 || FF_CODE_PAGE == 0
-static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
- 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
- 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
- 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
- 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,
- 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,
- 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,
- 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,
- 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0
-};
-#endif
-
-
-
-
-/*------------------------------------------------------------------------*/
-/* OEM <==> Unicode conversions for static code page configuration */
-/* SBCS fixed code page */
-/*------------------------------------------------------------------------*/
-
-#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
-WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
- DWORD uni, /* UTF-16 encoded character to be converted */
- WORD cp /* Code page for the conversion */
-)
-{
- WCHAR c = 0;
- const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
-
-
- if (uni < 0x80) { /* ASCII? */
- c = (WCHAR)uni;
-
- } else { /* Non-ASCII */
- if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
- for (c = 0; c < 0x80 && uni != p[c]; c++) ;
- c = (c + 0x80) & 0xFF;
- }
- }
-
- return c;
-}
-
-WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
- WCHAR oem, /* OEM code to be converted */
- WORD cp /* Code page for the conversion */
-)
-{
- WCHAR c = 0;
- const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
-
-
- if (oem < 0x80) { /* ASCII? */
- c = oem;
-
- } else { /* Extended char */
- if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */
- if (oem < 0x100) c = p[oem - 0x80];
- }
- }
-
- return c;
-}
-
-#endif
-
-
-
-/*------------------------------------------------------------------------*/
-/* OEM <==> Unicode conversions for static code page configuration */
-/* DBCS fixed code page */
-/*------------------------------------------------------------------------*/
-
-#if FF_CODE_PAGE >= 900
-WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
- DWORD uni, /* UTF-16 encoded character to be converted */
- WORD cp /* Code page for the conversion */
-)
-{
- const WCHAR *p;
- WCHAR c = 0, uc;
- UINT i = 0, n, li, hi;
-
-
- if (uni < 0x80) { /* ASCII? */
- c = (WCHAR)uni;
-
- } else { /* Non-ASCII */
- if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
- uc = (WCHAR)uni;
- p = CVTBL(uni2oem, FF_CODE_PAGE);
- hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1;
- li = 0;
- for (n = 16; n; n--) {
- i = li + (hi - li) / 2;
- if (uc == p[i * 2]) break;
- if (uc > p[i * 2]) {
- li = i;
- } else {
- hi = i;
- }
- }
- if (n != 0) c = p[i * 2 + 1];
- }
- }
-
- return c;
-}
-
-
-WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
- WCHAR oem, /* OEM code to be converted */
- WORD cp /* Code page for the conversion */
-)
-{
- const WCHAR *p;
- WCHAR c = 0;
- UINT i = 0, n, li, hi;
-
-
- if (oem < 0x80) { /* ASCII? */
- c = oem;
-
- } else { /* Extended char */
- if (cp == FF_CODE_PAGE) { /* Is it valid code page? */
- p = CVTBL(oem2uni, FF_CODE_PAGE);
- hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1;
- li = 0;
- for (n = 16; n; n--) {
- i = li + (hi - li) / 2;
- if (oem == p[i * 2]) break;
- if (oem > p[i * 2]) {
- li = i;
- } else {
- hi = i;
- }
- }
- if (n != 0) c = p[i * 2 + 1];
- }
- }
-
- return c;
-}
-#endif
-
-
-
-/*------------------------------------------------------------------------*/
-/* OEM <==> Unicode conversions for dynamic code page configuration */
-/*------------------------------------------------------------------------*/
-
-#if FF_CODE_PAGE == 0
-
-static const WORD cp_code[] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 0};
-static const WCHAR* const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0};
-
-
-WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
- DWORD uni, /* UTF-16 encoded character to be converted */
- WORD cp /* Code page for the conversion */
-)
-{
- const WCHAR *p;
- WCHAR c = 0, uc;
- UINT i, n, li, hi;
-
-
- if (uni < 0x80) { /* ASCII? */
- c = (WCHAR)uni;
-
- } else { /* Non-ASCII */
- if (uni < 0x10000) { /* Is it in BMP? */
- uc = (WCHAR)uni;
- p = 0;
- if (cp < 900) { /* SBCS */
- for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get conversion table */
- p = cp_table[i];
- if (p) { /* Is it valid code page ? */
- for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */
- c = (c + 0x80) & 0xFF;
- }
- } else { /* DBCS */
- switch (cp) { /* Get conversion table */
- case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break;
- case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break;
- case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break;
- case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break;
- }
- if (p) { /* Is it valid code page? */
- li = 0;
- for (n = 16; n; n--) { /* Find OEM code */
- i = li + (hi - li) / 2;
- if (uc == p[i * 2]) break;
- if (uc > p[i * 2]) {
- li = i;
- } else {
- hi = i;
- }
- }
- if (n != 0) c = p[i * 2 + 1];
- }
- }
- }
- }
-
- return c;
-}
-
-
-WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
- WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */
- WORD cp /* Code page for the conversion */
-)
-{
- const WCHAR *p;
- WCHAR c = 0;
- UINT i, n, li, hi;
-
-
- if (oem < 0x80) { /* ASCII? */
- c = oem;
-
- } else { /* Extended char */
- p = 0;
- if (cp < 900) { /* SBCS */
- for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */
- p = cp_table[i];
- if (p) { /* Is it a valid CP ? */
- if (oem < 0x100) c = p[oem - 0x80];
- }
- } else { /* DBCS */
- switch (cp) {
- case 932 : p = oem2uni932; hi = sizeof oem2uni932 / 4 - 1; break;
- case 936 : p = oem2uni936; hi = sizeof oem2uni936 / 4 - 1; break;
- case 949 : p = oem2uni949; hi = sizeof oem2uni949 / 4 - 1; break;
- case 950 : p = oem2uni950; hi = sizeof oem2uni950 / 4 - 1; break;
- }
- if (p) {
- li = 0;
- for (n = 16; n; n--) {
- i = li + (hi - li) / 2;
- if (oem == p[i * 2]) break;
- if (oem > p[i * 2]) {
- li = i;
- } else {
- hi = i;
- }
- }
- if (n != 0) c = p[i * 2 + 1];
- }
- }
- }
-
- return c;
-}
-#endif
-
-
-
-/*------------------------------------------------------------------------*/
-/* Unicode up-case conversion */
-/*------------------------------------------------------------------------*/
-
-DWORD ff_wtoupper ( /* Returns up-converted code point */
- DWORD uni /* Unicode code point to be up-converted */
-)
-{
- const WORD *p;
- WORD uc, bc, nc, cmd;
- static const WORD cvt1[] = { /* Compressed up conversion table for U+0000 - U+0FFF */
- /* Basic Latin */
- 0x0061,0x031A,
- /* Latin-1 Supplement */
- 0x00E0,0x0317,
- 0x00F8,0x0307,
- 0x00FF,0x0001,0x0178,
- /* Latin Extended-A */
- 0x0100,0x0130,
- 0x0132,0x0106,
- 0x0139,0x0110,
- 0x014A,0x012E,
- 0x0179,0x0106,
- /* Latin Extended-B */
- 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,
- 0x01CD,0x0110,
- 0x01DD,0x0001,0x018E,
- 0x01DE,0x0112,
- 0x01F3,0x0003,0x01F1,0x01F4,0x01F4,
- 0x01F8,0x0128,
- 0x0222,0x0112,
- 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,
- 0x0246,0x010A,
- /* IPA Extensions */
- 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,
- /* Greek, Coptic */
- 0x037B,0x0003,0x03FD,0x03FE,0x03FF,
- 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,
- 0x03B1,0x0311,
- 0x03C2,0x0002,0x03A3,0x03A3,
- 0x03C4,0x0308,
- 0x03CC,0x0003,0x038C,0x038E,0x038F,
- 0x03D8,0x0118,
- 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,
- /* Cyrillic */
- 0x0430,0x0320,
- 0x0450,0x0710,
- 0x0460,0x0122,
- 0x048A,0x0136,
- 0x04C1,0x010E,
- 0x04CF,0x0001,0x04C0,
- 0x04D0,0x0144,
- /* Armenian */
- 0x0561,0x0426,
-
- 0x0000 /* EOT */
- };
- static const WORD cvt2[] = { /* Compressed up conversion table for U+1000 - U+FFFF */
- /* Phonetic Extensions */
- 0x1D7D,0x0001,0x2C63,
- /* Latin Extended Additional */
- 0x1E00,0x0196,
- 0x1EA0,0x015A,
- /* Greek Extended */
- 0x1F00,0x0608,
- 0x1F10,0x0606,
- 0x1F20,0x0608,
- 0x1F30,0x0608,
- 0x1F40,0x0606,
- 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,
- 0x1F60,0x0608,
- 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,
- 0x1F80,0x0608,
- 0x1F90,0x0608,
- 0x1FA0,0x0608,
- 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
- 0x1FCC,0x0001,0x1FC3,
- 0x1FD0,0x0602,
- 0x1FE0,0x0602,
- 0x1FE5,0x0001,0x1FEC,
- 0x1FF3,0x0001,0x1FFC,
- /* Letterlike Symbols */
- 0x214E,0x0001,0x2132,
- /* Number forms */
- 0x2170,0x0210,
- 0x2184,0x0001,0x2183,
- /* Enclosed Alphanumerics */
- 0x24D0,0x051A,
- 0x2C30,0x042F,
- /* Latin Extended-C */
- 0x2C60,0x0102,
- 0x2C67,0x0106, 0x2C75,0x0102,
- /* Coptic */
- 0x2C80,0x0164,
- /* Georgian Supplement */
- 0x2D00,0x0826,
- /* Full-width */
- 0xFF41,0x031A,
-
- 0x0000 /* EOT */
- };
-
-
- if (uni < 0x10000) { /* Is it in BMP? */
- uc = (WORD)uni;
- p = uc < 0x1000 ? cvt1 : cvt2;
- for (;;) {
- bc = *p++; /* Get the block base */
- if (bc == 0 || uc < bc) break; /* Not matched? */
- nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */
- if (uc < bc + nc) { /* In the block? */
- switch (cmd) {
- case 0: uc = p[uc - bc]; break; /* Table conversion */
- case 1: uc -= (uc - bc) & 1; break; /* Case pairs */
- case 2: uc -= 16; break; /* Shift -16 */
- case 3: uc -= 32; break; /* Shift -32 */
- case 4: uc -= 48; break; /* Shift -48 */
- case 5: uc -= 26; break; /* Shift -26 */
- case 6: uc += 8; break; /* Shift +8 */
- case 7: uc -= 80; break; /* Shift -80 */
- case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */
- }
- break;
- }
- if (cmd == 0) p += nc; /* Skip table if needed */
- }
- uni = uc;
- }
-
- return uni;
-}
-
-#endif /* #if FF_USE_LFN */
+/*------------------------------------------------------------------------*/
+/* Unicode handling functions for FatFs R0.13c */
+/*------------------------------------------------------------------------*/
+/* This module will occupy a huge memory in the .const section when the /
+/ FatFs is configured for LFN with DBCS. If the system has any Unicode /
+/ utilitiy for the code conversion, this module should be modified to use /
+/ that function to avoid silly memory consumption. /
+/-------------------------------------------------------------------------*/
+/*
+/ Copyright (C) 2018, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/ this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+*/
+
+
+#include "ff.h"
+
+#if FF_USE_LFN /* This module will be blanked at non-LFN configuration */
+
+#if FF_DEFINED != 86604 /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+#define MERGE2(a, b) a ## b
+#define CVTBL(tbl, cp) MERGE2(tbl, cp)
+
+/*------------------------------------------------------------------------*/
+/* Code Conversion Tables */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0
+static const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 720 || FF_CODE_PAGE == 0
+static const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */
+ 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
+ 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
+ 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 737 || FF_CODE_PAGE == 0
+static const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */
+ 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+ 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+ 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
+ 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 771 || FF_CODE_PAGE == 0
+static const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 775 || FF_CODE_PAGE == 0
+static const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */
+ 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
+ 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
+ 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
+ 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 850 || FF_CODE_PAGE == 0
+static const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+ 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 852 || FF_CODE_PAGE == 0
+static const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
+ 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
+ 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 855 || FF_CODE_PAGE == 0
+static const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */
+ 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
+ 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
+ 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
+ 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
+ 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 857 || FF_CODE_PAGE == 0
+static const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
+ 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 860 || FF_CODE_PAGE == 0
+static const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
+ 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 861 || FF_CODE_PAGE == 0
+static const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 862 || FF_CODE_PAGE == 0
+static const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */
+ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 863 || FF_CODE_PAGE == 0
+static const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
+ 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
+ 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 864 || FF_CODE_PAGE == 0
+static const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */
+ 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
+ 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
+ 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
+ 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,
+ 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
+ 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,
+ 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,
+ 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000
+};
+#endif
+#if FF_CODE_PAGE == 865 || FF_CODE_PAGE == 0
+static const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 866 || FF_CODE_PAGE == 0
+static const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 869 || FF_CODE_PAGE == 0
+static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
+ 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
+ 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
+ 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,
+ 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,
+ 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,
+ 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0
+};
+#endif
+
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for static code page configuration */
+/* SBCS fixed code page */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
+WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
+ DWORD uni, /* UTF-16 encoded character to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ WCHAR c = 0;
+ const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
+
+
+ if (uni < 0x80) { /* ASCII? */
+ c = (WCHAR)uni;
+
+ } else { /* Non-ASCII */
+ if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
+ for (c = 0; c < 0x80 && uni != p[c]; c++) ;
+ c = (c + 0x80) & 0xFF;
+ }
+ }
+
+ return c;
+}
+
+WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
+ WCHAR oem, /* OEM code to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ WCHAR c = 0;
+ const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
+
+
+ if (oem < 0x80) { /* ASCII? */
+ c = oem;
+
+ } else { /* Extended char */
+ if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */
+ if (oem < 0x100) c = p[oem - 0x80];
+ }
+ }
+
+ return c;
+}
+
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for static code page configuration */
+/* DBCS fixed code page */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE >= 900
+WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
+ DWORD uni, /* UTF-16 encoded character to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0, uc;
+ UINT i = 0, n, li, hi;
+
+
+ if (uni < 0x80) { /* ASCII? */
+ c = (WCHAR)uni;
+
+ } else { /* Non-ASCII */
+ if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
+ uc = (WCHAR)uni;
+ p = CVTBL(uni2oem, FF_CODE_PAGE);
+ hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1;
+ li = 0;
+ for (n = 16; n; n--) {
+ i = li + (hi - li) / 2;
+ if (uc == p[i * 2]) break;
+ if (uc > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+
+ return c;
+}
+
+
+WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
+ WCHAR oem, /* OEM code to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0;
+ UINT i = 0, n, li, hi;
+
+
+ if (oem < 0x80) { /* ASCII? */
+ c = oem;
+
+ } else { /* Extended char */
+ if (cp == FF_CODE_PAGE) { /* Is it valid code page? */
+ p = CVTBL(oem2uni, FF_CODE_PAGE);
+ hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1;
+ li = 0;
+ for (n = 16; n; n--) {
+ i = li + (hi - li) / 2;
+ if (oem == p[i * 2]) break;
+ if (oem > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+
+ return c;
+}
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for dynamic code page configuration */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE == 0
+
+static const WORD cp_code[] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 0};
+static const WCHAR* const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0};
+
+
+WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
+ DWORD uni, /* UTF-16 encoded character to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0, uc;
+ UINT i, n, li, hi;
+
+
+ if (uni < 0x80) { /* ASCII? */
+ c = (WCHAR)uni;
+
+ } else { /* Non-ASCII */
+ if (uni < 0x10000) { /* Is it in BMP? */
+ uc = (WCHAR)uni;
+ p = 0;
+ if (cp < 900) { /* SBCS */
+ for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get conversion table */
+ p = cp_table[i];
+ if (p) { /* Is it valid code page ? */
+ for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */
+ c = (c + 0x80) & 0xFF;
+ }
+ } else { /* DBCS */
+ switch (cp) { /* Get conversion table */
+ case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break;
+ case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break;
+ case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break;
+ case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break;
+ }
+ if (p) { /* Is it valid code page? */
+ li = 0;
+ for (n = 16; n; n--) { /* Find OEM code */
+ i = li + (hi - li) / 2;
+ if (uc == p[i * 2]) break;
+ if (uc > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+ }
+ }
+
+ return c;
+}
+
+
+WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
+ WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0;
+ UINT i, n, li, hi;
+
+
+ if (oem < 0x80) { /* ASCII? */
+ c = oem;
+
+ } else { /* Extended char */
+ p = 0;
+ if (cp < 900) { /* SBCS */
+ for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */
+ p = cp_table[i];
+ if (p) { /* Is it a valid CP ? */
+ if (oem < 0x100) c = p[oem - 0x80];
+ }
+ } else { /* DBCS */
+ switch (cp) {
+ case 932 : p = oem2uni932; hi = sizeof oem2uni932 / 4 - 1; break;
+ case 936 : p = oem2uni936; hi = sizeof oem2uni936 / 4 - 1; break;
+ case 949 : p = oem2uni949; hi = sizeof oem2uni949 / 4 - 1; break;
+ case 950 : p = oem2uni950; hi = sizeof oem2uni950 / 4 - 1; break;
+ }
+ if (p) {
+ li = 0;
+ for (n = 16; n; n--) {
+ i = li + (hi - li) / 2;
+ if (oem == p[i * 2]) break;
+ if (oem > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+ }
+
+ return c;
+}
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* Unicode up-case conversion */
+/*------------------------------------------------------------------------*/
+
+DWORD ff_wtoupper ( /* Returns up-converted code point */
+ DWORD uni /* Unicode code point to be up-converted */
+)
+{
+ const WORD *p;
+ WORD uc, bc, nc, cmd;
+ static const WORD cvt1[] = { /* Compressed up conversion table for U+0000 - U+0FFF */
+ /* Basic Latin */
+ 0x0061,0x031A,
+ /* Latin-1 Supplement */
+ 0x00E0,0x0317,
+ 0x00F8,0x0307,
+ 0x00FF,0x0001,0x0178,
+ /* Latin Extended-A */
+ 0x0100,0x0130,
+ 0x0132,0x0106,
+ 0x0139,0x0110,
+ 0x014A,0x012E,
+ 0x0179,0x0106,
+ /* Latin Extended-B */
+ 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,
+ 0x01CD,0x0110,
+ 0x01DD,0x0001,0x018E,
+ 0x01DE,0x0112,
+ 0x01F3,0x0003,0x01F1,0x01F4,0x01F4,
+ 0x01F8,0x0128,
+ 0x0222,0x0112,
+ 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,
+ 0x0246,0x010A,
+ /* IPA Extensions */
+ 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,
+ /* Greek, Coptic */
+ 0x037B,0x0003,0x03FD,0x03FE,0x03FF,
+ 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,
+ 0x03B1,0x0311,
+ 0x03C2,0x0002,0x03A3,0x03A3,
+ 0x03C4,0x0308,
+ 0x03CC,0x0003,0x038C,0x038E,0x038F,
+ 0x03D8,0x0118,
+ 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,
+ /* Cyrillic */
+ 0x0430,0x0320,
+ 0x0450,0x0710,
+ 0x0460,0x0122,
+ 0x048A,0x0136,
+ 0x04C1,0x010E,
+ 0x04CF,0x0001,0x04C0,
+ 0x04D0,0x0144,
+ /* Armenian */
+ 0x0561,0x0426,
+
+ 0x0000 /* EOT */
+ };
+ static const WORD cvt2[] = { /* Compressed up conversion table for U+1000 - U+FFFF */
+ /* Phonetic Extensions */
+ 0x1D7D,0x0001,0x2C63,
+ /* Latin Extended Additional */
+ 0x1E00,0x0196,
+ 0x1EA0,0x015A,
+ /* Greek Extended */
+ 0x1F00,0x0608,
+ 0x1F10,0x0606,
+ 0x1F20,0x0608,
+ 0x1F30,0x0608,
+ 0x1F40,0x0606,
+ 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,
+ 0x1F60,0x0608,
+ 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,
+ 0x1F80,0x0608,
+ 0x1F90,0x0608,
+ 0x1FA0,0x0608,
+ 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
+ 0x1FCC,0x0001,0x1FC3,
+ 0x1FD0,0x0602,
+ 0x1FE0,0x0602,
+ 0x1FE5,0x0001,0x1FEC,
+ 0x1FF3,0x0001,0x1FFC,
+ /* Letterlike Symbols */
+ 0x214E,0x0001,0x2132,
+ /* Number forms */
+ 0x2170,0x0210,
+ 0x2184,0x0001,0x2183,
+ /* Enclosed Alphanumerics */
+ 0x24D0,0x051A,
+ 0x2C30,0x042F,
+ /* Latin Extended-C */
+ 0x2C60,0x0102,
+ 0x2C67,0x0106, 0x2C75,0x0102,
+ /* Coptic */
+ 0x2C80,0x0164,
+ /* Georgian Supplement */
+ 0x2D00,0x0826,
+ /* Full-width */
+ 0xFF41,0x031A,
+
+ 0x0000 /* EOT */
+ };
+
+
+ if (uni < 0x10000) { /* Is it in BMP? */
+ uc = (WORD)uni;
+ p = uc < 0x1000 ? cvt1 : cvt2;
+ for (;;) {
+ bc = *p++; /* Get the block base */
+ if (bc == 0 || uc < bc) break; /* Not matched? */
+ nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */
+ if (uc < bc + nc) { /* In the block? */
+ switch (cmd) {
+ case 0: uc = p[uc - bc]; break; /* Table conversion */
+ case 1: uc -= (uc - bc) & 1; break; /* Case pairs */
+ case 2: uc -= 16; break; /* Shift -16 */
+ case 3: uc -= 32; break; /* Shift -32 */
+ case 4: uc -= 48; break; /* Shift -48 */
+ case 5: uc -= 26; break; /* Shift -26 */
+ case 6: uc += 8; break; /* Shift +8 */
+ case 7: uc -= 80; break; /* Shift -80 */
+ case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */
+ }
+ break;
+ }
+ if (cmd == 0) p += nc; /* Skip table if needed */
+ }
+ uni = uc;
+ }
+
+ return uni;
+}
+
+#endif /* #if FF_USE_LFN */
diff --git a/bdk/libs/lv_conf.h b/bdk/libs/lv_conf.h
index 4b3c96c..bb63fea 100644
--- a/bdk/libs/lv_conf.h
+++ b/bdk/libs/lv_conf.h
@@ -1,373 +1,376 @@
-/*
- * Copyright (c) 2018 CTCaer
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef LV_CONF_H
-#define LV_CONF_H
-
-#include
-#include
-/*===================
- Dynamic memory
- *===================*/
-
-/* Memory size which will be used by the library
- * to store the graphical objects and other data */
-#define LV_MEM_CUSTOM 0 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/
-#if LV_MEM_CUSTOM == 0
-# define LV_MEM_SIZE NYX_LV_MEM_SZ /*Size memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
-# define LV_MEM_ATTR /*Complier prefix for big array declaration*/
-# define LV_MEM_ADR NYX_LV_MEM_ADR /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/
-# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/
-#else /*LV_MEM_CUSTOM*/
-# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/
-# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
-# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
-#endif /*LV_MEM_CUSTOM*/
-
-/* Garbage Collector settings
- * Used if lvgl is binded to higher language and the memory is managed by that language */
-#define LV_ENABLE_GC 0
-#if LV_ENABLE_GC != 0
-# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
-# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
-# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
-#endif /* LV_ENABLE_GC */
-
-/*===================
- Graphical settings
- *===================*/
-
-/* Horizontal and vertical resolution of the library.*/
-#define LV_HOR_RES (1280)
-#define LV_VER_RES (720)
-
-/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide
- * (Not so important, you can adjust it to modify default sizes and spaces)*/
-#define LV_DPI 100
-
-/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
-#define LV_ANTIALIAS 1 /*1: Enable anti-aliasing*/
-
-/*Screen refresh period in milliseconds*/
-#define LV_REFR_PERIOD 33
-
-/*-----------------
- * VDB settings
- *----------------*/
-
-/* VDB (Virtual Display Buffer) is an internal graphics buffer.
- * The GUI will be drawn into this buffer first and then
- * the buffer will be passed to your `disp_drv.disp_flush` function to
- * copy it to your frame buffer.
- * VDB is required for: buffered drawing, opacity, anti-aliasing and shadows
- * Learn more: https://docs.littlevgl.com/#Drawing*/
-
-/* Size of the VDB in pixels. Typical size: ~1/10 screen. Must be >= LV_HOR_RES
- * Setting it to 0 will disable VDB and `disp_drv.disp_fill` and `disp_drv.disp_map` functions
- * will be called to draw to the frame buffer directly*/
-#define LV_VDB_SIZE (LV_VER_RES * LV_HOR_RES)
-
- /* Bit-per-pixel of VDB. Useful for monochrome or non-standard color format displays.
- * Special formats are handled with `disp_drv.vdb_wr`)*/
-#define LV_VDB_PX_BPP LV_COLOR_SIZE /*LV_COLOR_SIZE comes from LV_COLOR_DEPTH below to set 8, 16 or 32 bit pixel size automatically */
-
- /* Place VDB to a specific address (e.g. in external RAM)
- * 0: allocate automatically into RAM
- * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/
-#define LV_VDB_ADR NYX_LV_VDB_ADR
-
-/* Use two Virtual Display buffers (VDB) to parallelize rendering and flushing
- * The flushing should use DMA to write the frame buffer in the background */
-#define LV_VDB_DOUBLE 0
-
-/* Place VDB2 to a specific address (e.g. in external RAM)
- * 0: allocate automatically into RAM
- * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/
-#define LV_VDB2_ADR 0
-
-/* Using true double buffering in `disp_drv.disp_flush` you will always get the image of the whole screen.
- * Your only task is to set the rendered image (`color_p` parameter) as frame buffer address or send it to your display.
- * The best if you do in the blank period of you display to avoid tearing effect.
- * Requires:
- * - LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES
- * - LV_VDB_DOUBLE = 1
- */
-#define LV_VDB_TRUE_DOUBLE_BUFFERED 0
-
-/*=================
- Misc. setting
- *=================*/
-
-/*Input device settings*/
-#define LV_INDEV_READ_PERIOD 33 /*Input device read period in milliseconds*/
-#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: USE_LV_REAL_DRAW = 1)*/
-#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */
-#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */
-#define LV_INDEV_LONG_PRESS_TIME 400 /*Long press time in milliseconds*/
-#define LV_INDEV_LONG_PRESS_REP_TIME 1000 //Fix keyb /*Repeated trigger period in long press [ms] */
-
-/*Color settings*/
-#define LV_COLOR_DEPTH 32 /*Color depth: 1/8/16/32*/
-#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/
-#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/
-#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/
-
-/*Text settings*/
-#define LV_TXT_UTF8 0 /*Enable UTF-8 coded Unicode character usage */
-#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/
-#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */
-#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */
-#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */
-
-/*Feature usage*/
-#define USE_LV_ANIMATION 1 /*1: Enable all animations*/
-#define USE_LV_SHADOW 1 /*1: Enable shadows*/
-#define USE_LV_GROUP 0 /*1: Enable object groups (for keyboards)*/
-#define USE_LV_GPU 0 /*1: Enable GPU interface*/
-#define USE_LV_REAL_DRAW 0 /*1: Enable function which draw directly to the frame buffer instead of VDB (required if LV_VDB_SIZE = 0)*/
-#define USE_LV_FILESYSTEM 0 /*1: Enable file system (might be required for images*/
-#define USE_LV_MULTI_LANG 0 /* Number of languages for labels to store (0: to disable this feature)*/
-
-/*Compiler settings*/
-#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */
-#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */
-#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/
-
-/*HAL settings*/
-#define LV_TICK_CUSTOM 1 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */
-#if LV_TICK_CUSTOM == 1
-#define LV_TICK_CUSTOM_INCLUDE /*Header for the sys time function*/
-#define LV_TICK_CUSTOM_SYS_TIME_EXPR (get_tmr_ms()) /*Expression evaluating to current systime in ms*/
-#endif /*LV_TICK_CUSTOM*/
-
-
-/*Log settings*/
-#ifdef DEBUG_UART_LV_LOG
-# define USE_LV_LOG 1 /*Enable/disable the log module*/
-#else
-# define USE_LV_LOG 0 /*Enable/disable the log module*/
-#endif
-#if USE_LV_LOG
-/* How important log should be added:
- * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
- * LV_LOG_LEVEL_INFO Log important events
- * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't caused problem
- * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
- */
-# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
-/* 1: Print the log with 'printf'; 0: user need to register a callback*/
-# define LV_LOG_PRINTF 1
-#endif /*USE_LV_LOG*/
-
-/*================
- * THEME USAGE
- *================*/
-#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
-
-#define USE_LV_THEME_HEKATE 1 /*Flat theme with bold colors and light shadows*/
-
-/*==================
- * FONT USAGE
- *===================*/
-
-/* More info about fonts: https://docs.littlevgl.com/#Fonts
- * To enable a built-in font use 1,2,4 or 8 values
- * which will determine the bit-per-pixel. Higher value means smoother fonts */
-#define LV_FONT_QUALITY 8
-
-#define USE_UBUNTU_MONO LV_FONT_QUALITY
-
-#define USE_INTERUI_20 LV_FONT_QUALITY
-#define USE_INTERUI_30 LV_FONT_QUALITY
-
-#define USE_HEKATE_SYMBOL_20 USE_INTERUI_20
-#define USE_HEKATE_SYMBOL_30 USE_INTERUI_30
-#define USE_HEKATE_SYMBOL_120 LV_FONT_QUALITY
-
-/* Optionally declare your custom fonts here.
- * You can use these fonts as default font too
- * and they will be available globally. E.g.
- * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
- * LV_FONT_DECLARE(my_font_2) \
- */
-#define LV_FONT_CUSTOM_DECLARE
-
-#define LV_FONT_DEFAULT &interui_30 /*Always set a default font from the built-in fonts*/
-
-/*===================
- * LV_OBJ SETTINGS
- *==================*/
-#define LV_OBJ_FREE_NUM_TYPE uint32_t /*Type of free number attribute (comment out disable free number)*/
-#define LV_OBJ_FREE_PTR 1 /*Enable the free pointer attribute*/
-#define LV_OBJ_REALIGN 1 // 0 in OG gui /*Enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/
-
-/*==================
- * LV OBJ X USAGE
- *================*/
-/*
- * Documentation of the object types: https://docs.littlevgl.com/#Object-types
- */
-
-/*****************
- * Simple object
- *****************/
-
-/*Label (dependencies: -*/
-#define USE_LV_LABEL 1
-#if USE_LV_LABEL != 0
-# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
-#endif
-
-/*Image (dependencies: lv_label*/
-#define USE_LV_IMG 1
-#if USE_LV_IMG != 0
-# define LV_IMG_CF_INDEXED 0 /*Enable indexed (palette) images*/
-# define LV_IMG_CF_ALPHA 0 /*Enable alpha indexed images*/
-#endif
-
-/*Line (dependencies: -*/
-#define USE_LV_LINE 1
-
-/*Arc (dependencies: -)*/
-#define USE_LV_ARC 0
-
-/*******************
- * Container objects
- *******************/
-
-/*Container (dependencies: -*/
-#define USE_LV_CONT 1
-
-/*Page (dependencies: lv_cont)*/
-#define USE_LV_PAGE 1
-
-/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
-#define USE_LV_WIN 1
-
-/*Tab (dependencies: lv_page, lv_btnm)*/
-#define USE_LV_TABVIEW 1
-# if USE_LV_TABVIEW != 0
-# define LV_TABVIEW_ANIM_TIME 0 /*Time of slide animation [ms] (0: no animation)*/
-#endif
-
-/*Tileview (dependencies: lv_page) */
-#define USE_LV_TILEVIEW 0
-#if USE_LV_TILEVIEW
-# define LV_TILEVIEW_ANIM_TIME 0 /*Time of slide animation [ms] (0: no animation)*/
-#endif
-
-/*************************
- * Data visualizer objects
- *************************/
-
-/*Bar (dependencies: -)*/
-#define USE_LV_BAR 1
-
-/*Line meter (dependencies: *;)*/
-#define USE_LV_LMETER 0
-
-/*Gauge (dependencies:lv_bar, lv_lmeter)*/
-#define USE_LV_GAUGE 0
-
-/*Chart (dependencies: -)*/
-#define USE_LV_CHART 0
-
-/*Table (dependencies: lv_label)*/
-#define USE_LV_TABLE 1
-#if USE_LV_TABLE
-# define LV_TABLE_COL_MAX 12
-#endif
-
-/*LED (dependencies: -)*/
-#define USE_LV_LED 0
-
-/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
-#define USE_LV_MBOX 1
-
-/*Text area (dependencies: lv_label, lv_page)*/
-#define USE_LV_TA 1
-#if USE_LV_TA != 0
-# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/
-# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/
-#endif
-
-/*Spinbox (dependencies: lv_ta)*/
-#define USE_LV_SPINBOX 0
-
-/*Calendar (dependencies: -)*/
-#define USE_LV_CALENDAR 0
-
-/*Preload (dependencies: lv_arc)*/
-#define USE_LV_PRELOAD 0
-#if USE_LV_PRELOAD != 0
-# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
-# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
-# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC
-#endif
-
-/*Canvas (dependencies: lv_img)*/
-#define USE_LV_CANVAS 0
-/*************************
- * User input objects
- *************************/
-
-/*Button (dependencies: lv_cont*/
-#define USE_LV_BTN 1
-#if USE_LV_BTN != 0
-# define LV_BTN_INK_EFFECT 0 /*Enable button-state animations - draw a circle on click (dependencies: USE_LV_ANIMATION)*/
-#endif
-
-/*Image Button (dependencies: lv_btn*/
-#define USE_LV_IMGBTN 1
-#if USE_LV_IMGBTN
-# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
-#endif
-
-/*Button matrix (dependencies: -)*/
-#define USE_LV_BTNM 1
-
-/*Keyboard (dependencies: lv_btnm)*/
-#define USE_LV_KB 0
-
-/*Check box (dependencies: lv_btn, lv_label)*/
-#define USE_LV_CB 1
-
-/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
-#define USE_LV_LIST 1
-#if USE_LV_LIST != 0
-# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */
-#endif
-
-/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
-#define USE_LV_DDLIST 1
-#if USE_LV_DDLIST != 0
-# define LV_DDLIST_ANIM_TIME 100 /*Open and close default animation time [ms] (0: no animation)*/
-#endif
-
-/*Roller (dependencies: lv_ddlist)*/
-#define USE_LV_ROLLER 1
-#if USE_LV_ROLLER != 0
-# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/
-#endif
-
-/*Slider (dependencies: lv_bar)*/
-#define USE_LV_SLIDER 1
-
-/*Switch (dependencies: lv_slider)*/
-#define USE_LV_SW 1
-
-#endif /*LV_CONF_H*/
-
+/*
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef LV_CONF_H
+#define LV_CONF_H
+
+#include
+#include
+/*===================
+ Dynamic memory
+ *===================*/
+
+/* Memory size which will be used by the library
+ * to store the graphical objects and other data */
+#define LV_MEM_CUSTOM 0 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/
+#if LV_MEM_CUSTOM == 0
+# define LV_MEM_SIZE NYX_LV_MEM_SZ /*Size memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
+# define LV_MEM_ATTR /*Complier prefix for big array declaration*/
+# define LV_MEM_ADR NYX_LV_MEM_ADR /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/
+# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/
+#else /*LV_MEM_CUSTOM*/
+# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/
+# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
+# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
+#endif /*LV_MEM_CUSTOM*/
+
+/* Garbage Collector settings
+ * Used if lvgl is binded to higher language and the memory is managed by that language */
+#define LV_ENABLE_GC 0
+#if LV_ENABLE_GC != 0
+# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
+# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
+# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
+#endif /* LV_ENABLE_GC */
+
+/*===================
+ Graphical settings
+ *===================*/
+
+/* Horizontal and vertical resolution of the library.*/
+#define LV_HOR_RES (1280)
+#define LV_VER_RES (720)
+
+/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide
+ * (Not so important, you can adjust it to modify default sizes and spaces)*/
+#define LV_DPI 100
+
+/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
+#define LV_ANTIALIAS 1 /*1: Enable anti-aliasing*/
+
+/*Screen refresh period in milliseconds*/
+#define LV_REFR_PERIOD 33
+
+/*-----------------
+ * VDB settings
+ *----------------*/
+
+/* VDB (Virtual Display Buffer) is an internal graphics buffer.
+ * The GUI will be drawn into this buffer first and then
+ * the buffer will be passed to your `disp_drv.disp_flush` function to
+ * copy it to your frame buffer.
+ * VDB is required for: buffered drawing, opacity, anti-aliasing and shadows
+ * Learn more: https://docs.littlevgl.com/#Drawing*/
+
+/* Size of the VDB in pixels. Typical size: ~1/10 screen. Must be >= LV_HOR_RES
+ * Setting it to 0 will disable VDB and `disp_drv.disp_fill` and `disp_drv.disp_map` functions
+ * will be called to draw to the frame buffer directly*/
+#define LV_VDB_SIZE (LV_VER_RES * LV_HOR_RES)
+
+ /* Bit-per-pixel of VDB. Useful for monochrome or non-standard color format displays.
+ * Special formats are handled with `disp_drv.vdb_wr`)*/
+#define LV_VDB_PX_BPP LV_COLOR_SIZE /*LV_COLOR_SIZE comes from LV_COLOR_DEPTH below to set 8, 16 or 32 bit pixel size automatically */
+
+ /* Place VDB to a specific address (e.g. in external RAM)
+ * 0: allocate automatically into RAM
+ * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/
+#define LV_VDB_ADR NYX_LV_VDB_ADR
+
+/* Use two Virtual Display buffers (VDB) to parallelize rendering and flushing
+ * The flushing should use DMA to write the frame buffer in the background */
+#define LV_VDB_DOUBLE 0
+
+/* Place VDB2 to a specific address (e.g. in external RAM)
+ * 0: allocate automatically into RAM
+ * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/
+#define LV_VDB2_ADR 0
+
+/* Using true double buffering in `disp_drv.disp_flush` you will always get the image of the whole screen.
+ * Your only task is to set the rendered image (`color_p` parameter) as frame buffer address or send it to your display.
+ * The best if you do in the blank period of you display to avoid tearing effect.
+ * Requires:
+ * - LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES
+ * - LV_VDB_DOUBLE = 1
+ */
+#define LV_VDB_TRUE_DOUBLE_BUFFERED 0
+
+/*=================
+ Misc. setting
+ *=================*/
+
+/*Input device settings*/
+#define LV_INDEV_READ_PERIOD 33 /*Input device read period in milliseconds*/
+#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: USE_LV_REAL_DRAW = 1)*/
+#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */
+#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */
+#define LV_INDEV_LONG_PRESS_TIME 5000 /*Long press time in milliseconds*/
+#define LV_INDEV_LONG_PRESS_REP_TIME 1000 //Fix lv_kb /*Repeated trigger period in long press [ms] */
+
+/*Color settings*/
+#define LV_COLOR_DEPTH 32 /*Color depth: 1/8/16/32*/
+#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/
+#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/
+#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/
+
+/*Text settings*/
+#define LV_TXT_UTF8 0 /*Enable UTF-8 coded Unicode character usage */
+#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/
+#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */
+#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */
+#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */
+
+/*Feature usage*/
+#define USE_LV_ANIMATION 1 /*1: Enable all animations*/
+#define USE_LV_SHADOW 1 /*1: Enable shadows*/
+#define USE_LV_GROUP 0 /*1: Enable object groups (for keyboards)*/
+#define USE_LV_GPU 0 /*1: Enable GPU interface*/
+#define USE_LV_REAL_DRAW 0 /*1: Enable function which draw directly to the frame buffer instead of VDB (required if LV_VDB_SIZE = 0)*/
+#define USE_LV_FILESYSTEM 0 /*1: Enable file system (might be required for images*/
+#define USE_LV_MULTI_LANG 0 /* Number of languages for labels to store (0: to disable this feature)*/
+
+/*Compiler settings*/
+#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */
+#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */
+#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/
+
+/*HAL settings*/
+#define LV_TICK_CUSTOM 1 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */
+#if LV_TICK_CUSTOM == 1
+#define LV_TICK_CUSTOM_INCLUDE /*Header for the sys time function*/
+#define LV_TICK_CUSTOM_SYS_TIME_EXPR ((u32)get_tmr_ms()) /*Expression evaluating to current systime in ms*/
+#endif /*LV_TICK_CUSTOM*/
+
+
+/*Log settings*/
+#ifdef DEBUG_UART_LV_LOG
+# define USE_LV_LOG 1 /*Enable/disable the log module*/
+#else
+# define USE_LV_LOG 0 /*Enable/disable the log module*/
+#endif
+#if USE_LV_LOG
+/* How important log should be added:
+ * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
+ * LV_LOG_LEVEL_INFO Log important events
+ * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't caused problem
+ * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
+ */
+# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
+/* 1: Print the log with 'printf'; 0: user need to register a callback*/
+# define LV_LOG_PRINTF 1
+#endif /*USE_LV_LOG*/
+
+/*================
+ * THEME USAGE
+ *================*/
+#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
+
+#define USE_LV_THEME_HEKATE 1 /*Flat theme with bold colors and light shadows*/
+
+/*==================
+ * FONT USAGE
+ *===================*/
+
+/* More info about fonts: https://docs.littlevgl.com/#Fonts
+ * To enable a built-in font use 1,2,4 or 8 values
+ * which will determine the bit-per-pixel. Higher value means smoother fonts */
+#define LV_FONT_QUALITY 8
+
+#define USE_UBUNTU_MONO LV_FONT_QUALITY
+
+#define USE_INTERUI_20 LV_FONT_QUALITY
+#define USE_INTERUI_30 LV_FONT_QUALITY
+
+#define USE_HEKATE_SYMBOL_20 USE_INTERUI_20
+#define USE_HEKATE_SYMBOL_30 USE_INTERUI_30
+#define USE_HEKATE_SYMBOL_120 LV_FONT_QUALITY
+
+/* Optionally declare your custom fonts here.
+ * You can use these fonts as default font too
+ * and they will be available globally. E.g.
+ * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
+ * LV_FONT_DECLARE(my_font_2) \
+ */
+#define LV_FONT_CUSTOM_DECLARE
+
+#define LV_FONT_DEFAULT &interui_30 /*Always set a default font from the built-in fonts*/
+
+/*===================
+ * LV_OBJ SETTINGS
+ *==================*/
+#define LV_OBJ_FREE_NUM_TYPE uint32_t /*Type of free number attribute (comment out disable free number)*/
+#define LV_OBJ_FREE_PTR 1 /*Enable the free pointer attribute*/
+#define LV_OBJ_REALIGN 1 // 0 in OG gui /*Enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/
+
+/*==================
+ * LV OBJ X USAGE
+ *================*/
+/*
+ * Documentation of the object types: https://docs.littlevgl.com/#Object-types
+ */
+
+/*****************
+ * Simple object
+ *****************/
+
+/*Label (dependencies: -*/
+#define USE_LV_LABEL 1
+#if USE_LV_LABEL != 0
+# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
+#endif
+
+/*Image (dependencies: lv_label*/
+#define USE_LV_IMG 1
+#if USE_LV_IMG != 0
+# define LV_IMG_CF_INDEXED 0 /*Enable indexed (palette) images*/
+# define LV_IMG_CF_ALPHA 0 /*Enable alpha indexed images*/
+#endif
+
+/*Line (dependencies: -*/
+#define USE_LV_LINE 1
+
+/*Arc (dependencies: -)*/
+#define USE_LV_ARC 0
+
+/*******************
+ * Container objects
+ *******************/
+
+/*Container (dependencies: -*/
+#define USE_LV_CONT 1
+
+/*Page (dependencies: lv_cont)*/
+#define USE_LV_PAGE 1
+
+/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
+#define USE_LV_WIN 1
+
+/*Tab (dependencies: lv_page, lv_btnm)*/
+#define USE_LV_TABVIEW 1
+# if USE_LV_TABVIEW != 0
+# define LV_TABVIEW_ANIM_TIME 0 /*Time of slide animation [ms] (0: no animation)*/
+#endif
+
+/*Tileview (dependencies: lv_page) */
+#define USE_LV_TILEVIEW 0
+#if USE_LV_TILEVIEW
+# define LV_TILEVIEW_ANIM_TIME 0 /*Time of slide animation [ms] (0: no animation)*/
+#endif
+
+/*************************
+ * Data visualizer objects
+ *************************/
+
+/*Bar (dependencies: -)*/
+#define USE_LV_BAR 1
+
+/*Line meter (dependencies: *;)*/
+#define USE_LV_LMETER 0
+
+/*Gauge (dependencies:lv_bar, lv_lmeter)*/
+#define USE_LV_GAUGE 0
+
+/*Chart (dependencies: -)*/
+#define USE_LV_CHART 0
+
+/*Table (dependencies: lv_label)*/
+#define USE_LV_TABLE 1
+#if USE_LV_TABLE
+# define LV_TABLE_COL_MAX 12
+#endif
+
+/*LED (dependencies: -)*/
+#define USE_LV_LED 0
+
+/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
+#define USE_LV_MBOX 1
+#if USE_LV_MBOX != 0
+# define LV_MBOX_CLOSE_ANIM_TIME 200 /*ms*/
+#endif
+
+/*Text area (dependencies: lv_label, lv_page)*/
+#define USE_LV_TA 1
+#if USE_LV_TA != 0
+# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/
+# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/
+#endif
+
+/*Spinbox (dependencies: lv_ta)*/
+#define USE_LV_SPINBOX 0
+
+/*Calendar (dependencies: -)*/
+#define USE_LV_CALENDAR 0
+
+/*Preload (dependencies: lv_arc)*/
+#define USE_LV_PRELOAD 0
+#if USE_LV_PRELOAD != 0
+# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
+# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
+# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC
+#endif
+
+/*Canvas (dependencies: lv_img)*/
+#define USE_LV_CANVAS 0
+/*************************
+ * User input objects
+ *************************/
+
+/*Button (dependencies: lv_cont*/
+#define USE_LV_BTN 1
+#if USE_LV_BTN != 0
+# define LV_BTN_INK_EFFECT 0 /*Enable button-state animations - draw a circle on click (dependencies: USE_LV_ANIMATION)*/
+#endif
+
+/*Image Button (dependencies: lv_btn*/
+#define USE_LV_IMGBTN 1
+#if USE_LV_IMGBTN
+# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
+#endif
+
+/*Button matrix (dependencies: -)*/
+#define USE_LV_BTNM 1
+
+/*Keyboard (dependencies: lv_btnm)*/
+#define USE_LV_KB 0
+
+/*Check box (dependencies: lv_btn, lv_label)*/
+#define USE_LV_CB 0
+
+/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
+#define USE_LV_LIST 1
+#if USE_LV_LIST != 0
+# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */
+#endif
+
+/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
+#define USE_LV_DDLIST 1
+#if USE_LV_DDLIST != 0
+# define LV_DDLIST_ANIM_TIME 100 /*Open and close default animation time [ms] (0: no animation)*/
+#endif
+
+/*Roller (dependencies: lv_ddlist)*/
+#define USE_LV_ROLLER 1
+#if USE_LV_ROLLER != 0
+# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/
+#endif
+
+/*Slider (dependencies: lv_bar)*/
+#define USE_LV_SLIDER 1
+
+/*Switch (dependencies: lv_slider)*/
+#define USE_LV_SW 0
+
+#endif /*LV_CONF_H*/
+
diff --git a/bdk/libs/lvgl/docs/CODE_OF_CONDUCT.md b/bdk/libs/lvgl/docs/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..c7d7eeb
--- /dev/null
+++ b/bdk/libs/lvgl/docs/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/bdk/libs/lvgl/docs/CONTRIBUTING.md b/bdk/libs/lvgl/docs/CONTRIBUTING.md
new file mode 100644
index 0000000..5d1d912
--- /dev/null
+++ b/bdk/libs/lvgl/docs/CONTRIBUTING.md
@@ -0,0 +1,103 @@
+# Contributing to Littlev Graphics Library
+
+**Welcome! It's glad to see that you are interested in contributing to LittlevGL! There are several types of task where you can help to build a better library! Let's see how to get started!**
+
+
+There are many different possibilities to join the community. If you have some time to work with us I'm sure you will find something that fits you! You can:
+- answer other's questions
+- report and/or fix bugs
+- suggest and/or implement new features
+- improve and/or translate the documentation
+- write a blog post about your experiences
+
+But first, start with the most Frequently Asked Questions.
+
+## FAQ about contributing
+
+### What license does my code need to be under?
+
+Any code added to LittlevGL must be licensed under [MIT](https://choosealicense.com/licenses/mit/) or another license that is fully compatible. Contributions under other licenses are highly likely to be rejected.
+
+If you borrow code from another project, please make sure to add their copyright notice to your contribution.
+
+### Where do I ask questions, give feedback, or report bugs?
+
+We use the [forum](http://forum.littlevgl.com/) for questions, feature suggestions, and discussions.
+
+We use [GitHub's issue tracker](https://github.com/littlevgl/lvgl/issues) to report bugs.
+
+For both of these there are some rules:
+- Be kind and friendly.
+- Speak about one thing in one issue.
+- Give feedback and close the issue if your question is answered.
+- Explain exactly what you experience or expect. _"The button is not working"_ is not enough info to get help.
+- For most issues you should send an absolute minimal code example in order to reproduce the issue. Ideally this should be easily usable in the PC simulator.
+- Use [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) to format your post.
+- If you don't get any answer in a week write a comment like "Can somebody help?". Maybe your issue wasn't noticed.
+
+### How can I send fixes and improvements?
+Merging new code happens via Pull Requests. If you are still not familiar with the Pull Requests (PR for short) here is a quick guide about them:
+1. **Fork** the [lvgl repository](https://github.com/littlevgl/lvgl). To do this click the "Fork" button in the top right corner. It will "copy" the `lvgl` repository to your GitHub account (`https://github.com/your_name?tab=repositories`)
+2. **Clone** the forked repository and add your updates
+3. **Create a PR** on the GitHub on the page of you `lvgl` repository(`https://github.com/your_name/lvgl`) by hitting the "New pull request" button
+4. **Set the base branch**. It means where you want to merge your update. Bugfixes for the last release go to `master`, new features to the actual `dev-x.y` branch.
+5. **Describe** what is in the update. An example code is welcome if applicable.
+
+Some advice:
+- If you are not sure about your fix or feature it's better to open an issue first, and discuss the details there.
+- Maybe your fix or update won't be perfect at first. Don't be afraid, just improve it and push the new commits. The PR will be updated accordingly.
+- If your update needs some extra work it's okay to say: _"I'm busy now and I will improve it soon"_ or _"Sorry, I don't have time to improve it, I hope it helps in this form too"_. So it's better to say don't have time to continue then saying nothing.
+- Please read and follow this [guide about the coding style](https://docs.littlevgl.com/#Coding-Style-Guide)
+
+
+### Where is the documentation?
+
+You can read the documentation here: https://docs.littlevgl.com/
+You can edit the documentation here: https://github.com/littlevgl/doc
+
+### Where is the blog?
+
+You can read the blog here: https://blog.littlevgl.com/
+You can edit the blog here: https://github.com/littlevgl/blog
+
+
+## So how and where can I contribute?
+
+### Answering other's questions
+
+It's a great way to contribute to the library if you already use it. Just go the [issue tracker](https://github.com/littlevgl/lvgl/issues), read the titles and if you are already familiar with a topic, don't be shy, and write your suggestion.
+
+### Reporting and/or fixing bugs
+For simple bugfixes (typos, missing error handling, fixing a warning) is fine to send a Pull request directly. However, for more complex bugs it's better to open an issue first. In the issue, you should describe how to reproduce the bug and even add the minimal code snippet.
+
+### Suggesting and/or implementing new features
+If you have a good idea don't hesitate to share with us. It's even better if you have time to deal with its implementation. Don't be afraid if you still don't know LittlevGL well enough. We will help you to get started.
+
+During the implementation don't forget the [Code style guide](https://docs.littlevgl.com/#Coding-Style-Guide).
+
+### Improving and/or translating the documentation
+
+The documentation of LittlevGL is written in Markdown and available [here](https://github.com/littlevgl/doc) for editing. If you find some parts of the documentation obscure or insufficient just search the related `.md` file, hit the edit icon and add your updates. This way a new Pull request will be generated automatically.
+
+If you can devote more time to improve the documentation you can translate it!
+1. Just copy the English `.md` files from the root folder to `locale/LANGUAGE_CODE` (language code is e.g. DE, FR, ES etc)
+2. Append the language code the end of files (e.g. Welcome_fr.md)
+3. Update the filenames in `_Sidebar.md`
+4. Translate the page(s) you want
+5. Create a Pull request
+
+### Writing a blog post about your experiences
+
+Have ported LittlevGL to a new platform? Have you created a fancy GUI? Do you know a great trick?
+You can share your knowledge on LittelvGL's blog! It's super easy to add your own post:
+- Fork and clone the [blog repository](https://github.com/littlevgl/blog)
+- Add your post in Markdown to the `_posts` folder.
+- Store the images and other resources in a dedicated folder in `assets`
+- Create a Pull Request
+
+The blog uses [Jekyll](https://jekyllrb.com/) to convert the `.md` files to a webpage. You can easily [run Jekyll offline](https://jekyllrb.com/docs/) to check your post before creating the Pull request
+
+## Summary
+
+I hope you have taken a liking to contribute to LittelvGL. A helpful and friendly community is waiting for you! :)
+
diff --git a/bdk/libs/lvgl/docs/astyle_c b/bdk/libs/lvgl/docs/astyle_c
new file mode 100644
index 0000000..9b9d7f3
--- /dev/null
+++ b/bdk/libs/lvgl/docs/astyle_c
@@ -0,0 +1 @@
+--style=kr --convert-tabs --indent=spaces=4 --indent-switches --pad-oper --unpad-paren --align-pointer=middle --suffix=.bak --lineend=linux --min-conditional-indent=
diff --git a/bdk/libs/lvgl/docs/astyle_h b/bdk/libs/lvgl/docs/astyle_h
new file mode 100644
index 0000000..d9c7633
--- /dev/null
+++ b/bdk/libs/lvgl/docs/astyle_h
@@ -0,0 +1 @@
+--convert-tabs --indent=spaces=4
diff --git a/bdk/libs/lvgl/licence.txt b/bdk/libs/lvgl/licence.txt
new file mode 100644
index 0000000..beaef1d
--- /dev/null
+++ b/bdk/libs/lvgl/licence.txt
@@ -0,0 +1,8 @@
+MIT licence
+Copyright (c) 2016 Gábor Kiss-Vámosi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/bdk/libs/lvgl/lv_core/lv_core.mk b/bdk/libs/lvgl/lv_core/lv_core.mk
new file mode 100644
index 0000000..9992e3f
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_core.mk
@@ -0,0 +1,12 @@
+CSRCS += lv_group.c
+CSRCS += lv_indev.c
+CSRCS += lv_obj.c
+CSRCS += lv_refr.c
+CSRCS += lv_style.c
+CSRCS += lv_vdb.c
+CSRCS += lv_lang.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_core
+VPATH += :$(LVGL_DIR)/lvgl/lv_core
+
+CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_core"
diff --git a/bdk/libs/lvgl/lv_core/lv_group.c b/bdk/libs/lvgl/lv_core/lv_group.c
new file mode 100644
index 0000000..cedef7f
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_group.c
@@ -0,0 +1,554 @@
+/**
+ * @file lv_group.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_group.h"
+#if USE_LV_GROUP != 0
+#include
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void style_mod_def(lv_style_t * style);
+static void style_mod_edit_def(lv_style_t * style);
+static void lv_group_refocus(lv_group_t *g);
+static void obj_to_foreground(lv_obj_t * obj);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Create a new object group
+ * @return pointer to the new object group
+ */
+lv_group_t * lv_group_create(void)
+{
+ lv_group_t * group = lv_mem_alloc(sizeof(lv_group_t));
+ lv_mem_assert(group);
+ if(group == NULL) return NULL;
+ lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
+
+ group->style_mod = style_mod_def;
+ group->style_mod_edit = style_mod_edit_def;
+ group->obj_focus = NULL;
+ group->frozen = 0;
+ group->focus_cb = NULL;
+ group->click_focus = 1;
+ group->editing = 0;
+
+ return group;
+}
+
+/**
+ * Delete a group object
+ * @param group pointer to a group
+ */
+void lv_group_del(lv_group_t * group)
+{
+ /*Defocus the the currently focused object*/
+ if(group->obj_focus != NULL) {
+ (*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
+ lv_obj_invalidate(*group->obj_focus);
+ }
+
+ /*Remove the objects from the group*/
+ lv_obj_t ** obj;
+ LL_READ(group->obj_ll, obj) {
+ (*obj)->group_p = NULL;
+ }
+
+ lv_ll_clear(&(group->obj_ll));
+ lv_mem_free(group);
+}
+
+/**
+ * Add an object to a group
+ * @param group pointer to a group
+ * @param obj pointer to an object to add
+ */
+void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
+{
+ if(group == NULL) return;
+
+ /*If the object is already in a group and focused then defocus it*/
+ if(obj->group_p) {
+ if(lv_obj_is_focused(obj)) {
+ lv_group_refocus(obj->group_p);
+
+ LV_LOG_INFO("group: assign object to an other group");
+ }
+ }
+
+ obj->group_p = group;
+ lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
+ lv_mem_assert(next);
+ if(next == NULL) return;
+ *next = obj;
+
+ /* If the head and the tail is equal then there is only one object in the linked list.
+ * In this case automatically activate it*/
+ if(lv_ll_get_head(&group->obj_ll) == next) {
+ lv_group_refocus(group);
+ }
+}
+
+/**
+ * Remove an object from its group
+ * @param obj pointer to an object to remove
+ */
+void lv_group_remove_obj(lv_obj_t * obj)
+{
+ lv_group_t * g = obj->group_p;
+ if(g == NULL) return;
+ if(g->obj_focus == NULL) return; /*Just to be sure (Not possible if there is at least one object in the group)*/
+
+ /*Focus on the next object*/
+ if(*g->obj_focus == obj) {
+ /*If this is the only object in the group then focus to nothing.*/
+ if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
+ (*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
+ }
+ /*If there more objects in the group then focus to the next/prev object*/
+ else {
+ lv_group_refocus(g);
+ }
+ }
+
+ /* If the focuses object is still the same then it was the only object in the group but it will be deleted.
+ * Set the `obj_focus` to NULL to get back to the initial state of the group with zero objects*/
+ if(*g->obj_focus == obj) {
+ g->obj_focus = NULL;
+ }
+
+ /*Search the object and remove it from its group */
+ lv_obj_t ** i;
+ LL_READ(g->obj_ll, i) {
+ if(*i == obj) {
+ lv_ll_rem(&g->obj_ll, i);
+ lv_mem_free(i);
+ obj->group_p = NULL;
+ break;
+ }
+ }
+}
+
+/**
+ * Focus on an object (defocus the current)
+ * @param obj pointer to an object to focus on
+ */
+void lv_group_focus_obj(lv_obj_t * obj)
+{
+ lv_group_t * g = obj->group_p;
+ if(g == NULL) return;
+
+ if(g->frozen != 0) return;
+
+ /*On defocus edit mode must be leaved*/
+ lv_group_set_editing(g, false);
+
+ lv_obj_t ** i;
+ LL_READ(g->obj_ll, i) {
+ if(*i == obj) {
+ if(g->obj_focus == i) return; /*Don't focus the already focused object again*/
+ if(g->obj_focus != NULL) {
+ (*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
+ lv_obj_invalidate(*g->obj_focus);
+ }
+
+ g->obj_focus = i;
+
+ if(g->obj_focus != NULL) {
+ (*g->obj_focus)->signal_func(*g->obj_focus, LV_SIGNAL_FOCUS, NULL);
+ if(g->focus_cb) g->focus_cb(g);
+ lv_obj_invalidate(*g->obj_focus);
+
+ /*If the object or its parent has `top == true` bring it to the foregorund*/
+ obj_to_foreground(*g->obj_focus);
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * Focus the next object in a group (defocus the current)
+ * @param group pointer to a group
+ */
+void lv_group_focus_next(lv_group_t * group)
+{
+ if(group->frozen) return;
+
+ if(group->obj_focus) {
+ (*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
+ lv_obj_invalidate(*group->obj_focus);
+ }
+
+ lv_obj_t ** obj_next;
+ if(group->obj_focus == NULL) obj_next = lv_ll_get_head(&group->obj_ll);
+ else obj_next = lv_ll_get_next(&group->obj_ll, group->obj_focus);
+
+ if(obj_next == NULL) {
+ if(group->wrap) obj_next = lv_ll_get_head(&group->obj_ll);
+ else obj_next = lv_ll_get_tail(&group->obj_ll);
+ }
+ group->obj_focus = obj_next;
+
+ if(group->obj_focus) {
+ (*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
+ lv_obj_invalidate(*group->obj_focus);
+
+ if(group->focus_cb) group->focus_cb(group);
+
+ /*If the object or its parent has `top == true` bring it to the foregorund*/
+ obj_to_foreground(*group->obj_focus);
+ }
+}
+
+/**
+ * Focus the previous object in a group (defocus the current)
+ * @param group pointer to a group
+ */
+void lv_group_focus_prev(lv_group_t * group)
+{
+ if(group->frozen) return;
+
+ if(group->obj_focus) {
+ (*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
+ lv_obj_invalidate(*group->obj_focus);
+ }
+
+ lv_obj_t ** obj_next;
+ if(group->obj_focus == NULL) obj_next = lv_ll_get_tail(&group->obj_ll);
+ else obj_next = lv_ll_get_prev(&group->obj_ll, group->obj_focus);
+
+ if(obj_next == NULL) {
+ if(group->wrap) obj_next = lv_ll_get_tail(&group->obj_ll);
+ else obj_next = lv_ll_get_head(&group->obj_ll);
+ }
+ group->obj_focus = obj_next;
+
+ if(group->obj_focus != NULL) {
+ (*group->obj_focus)->signal_func(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
+ lv_obj_invalidate(*group->obj_focus);
+
+ if(group->focus_cb) group->focus_cb(group);
+
+ /*If the object or its parent has `top == true` bring it to the foregorund*/
+ obj_to_foreground(*group->obj_focus);
+ }
+
+}
+
+/**
+ * Do not let to change the focus from the current object
+ * @param group pointer to a group
+ * @param en true: freeze, false: release freezing (normal mode)
+ */
+void lv_group_focus_freeze(lv_group_t * group, bool en)
+{
+ if(en == false) group->frozen = 0;
+ else group->frozen = 1;
+}
+
+/**
+ * Send a control character to the focuses object of a group
+ * @param group pointer to a group
+ * @param c a character (use LV_GROUP_KEY_.. to navigate)
+ * @return result of focused object in group.
+ */
+lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
+{
+ lv_obj_t * act = lv_group_get_focused(group);
+ if(act == NULL) return LV_RES_OK;
+
+ return act->signal_func(act, LV_SIGNAL_CONTROLL, &c);
+}
+
+/**
+ * Set a function for a group which will modify the object's style if it is in focus
+ * @param group pointer to a group
+ * @param style_mod_func the style modifier function pointer
+ */
+void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
+{
+ group->style_mod = style_mod_func;
+ if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
+}
+
+/**
+ * Set a function for a group which will modify the object's style if it is in focus in edit mode
+ * @param group pointer to a group
+ * @param style_mod_func the style modifier function pointer
+ */
+void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
+{
+ group->style_mod_edit = style_mod_func;
+ if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
+}
+
+/**
+ * Set a function for a group which will be called when a new object is focused
+ * @param group pointer to a group
+ * @param focus_cb the call back function or NULL if unused
+ */
+void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
+{
+ group->focus_cb = focus_cb;
+}
+
+/**
+ * Manually set the current mode (edit or navigate).
+ * @param group pointer to group
+ * @param edit: true: edit mode; false: navigate mode
+ */
+void lv_group_set_editing(lv_group_t * group, bool edit)
+{
+ uint8_t en_val = edit ? 1 : 0;
+
+ if(en_val == group->editing) return; /*Do not set the same mode again*/
+
+ group->editing = en_val;
+ lv_obj_t * focused = lv_group_get_focused(group);
+
+ if(focused) focused->signal_func(focused, LV_SIGNAL_FOCUS, NULL); /*Focus again to properly leave edit mode*/
+
+ lv_obj_invalidate(focused);
+}
+
+/**
+ * Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
+ * @param group pointer to group
+ * @param en: true: enable `click_focus`
+ */
+void lv_group_set_click_focus(lv_group_t * group, bool en)
+{
+ group->click_focus = en ? 1 : 0;
+}
+
+void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy) {
+ group->refocus_policy = policy & 0x01;
+}
+
+static void lv_group_refocus(lv_group_t *g) {
+ /*Refocus must temporarily allow wrapping to work correctly*/
+ uint8_t temp_wrap = g->wrap;
+ g->wrap = 1;
+
+ if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
+ lv_group_focus_next(g);
+ else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
+ lv_group_focus_prev(g);
+ /*Restore wrap property*/
+ g->wrap = temp_wrap;
+}
+
+/**
+ * Set whether focus next/prev will allow wrapping from first->last or last->first.
+ * @param group pointer to group
+ * @param en: true: enable `click_focus`
+ */
+void lv_group_set_wrap(lv_group_t * group, bool en)
+{
+ group->wrap = en ? 1 : 0;
+}
+
+/**
+ * Modify a style with the set 'style_mod' function. The input style remains unchanged.
+ * @param group pointer to group
+ * @param style pointer to a style to modify
+ * @return a copy of the input style but modified with the 'style_mod' function
+ */
+lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style)
+{
+ lv_style_copy(&group->style_tmp, style);
+
+ if(group->editing) {
+ if(group->style_mod_edit) group->style_mod_edit(&group->style_tmp);
+ } else {
+ if(group->style_mod) group->style_mod(&group->style_tmp);
+ }
+ return &group->style_tmp;
+}
+
+/**
+ * Get the focused object or NULL if there isn't one
+ * @param group pointer to a group
+ * @return pointer to the focused object
+ */
+lv_obj_t * lv_group_get_focused(const lv_group_t * group)
+{
+ if(!group) return NULL;
+ if(group->obj_focus == NULL) return NULL;
+
+ return *group->obj_focus;
+}
+
+/**
+ * Get a the style modifier function of a group
+ * @param group pointer to a group
+ * @return pointer to the style modifier function
+ */
+lv_group_style_mod_func_t lv_group_get_style_mod_cb(const lv_group_t * group)
+{
+ if(!group) return false;
+ return group->style_mod ;
+}
+
+/**
+ * Get a the style modifier function of a group in edit mode
+ * @param group pointer to a group
+ * @return pointer to the style modifier function
+ */
+lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(const lv_group_t * group)
+{
+ if(!group) return false;
+ return group->style_mod_edit;
+}
+
+/**
+ * Get the focus callback function of a group
+ * @param group pointer to a group
+ * @return the call back function or NULL if not set
+ */
+lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
+{
+ if(!group) return false;
+ return group->focus_cb;
+}
+
+/**
+ * Get the current mode (edit or navigate).
+ * @param group pointer to group
+ * @return true: edit mode; false: navigate mode
+ */
+bool lv_group_get_editing(const lv_group_t * group)
+{
+ if(!group) return false;
+ return group->editing ? true : false;
+}
+
+/**
+ * Get the `click_focus` attribute.
+ * @param group pointer to group
+ * @return true: `click_focus` is enabled; false: disabled
+ */
+bool lv_group_get_click_focus(const lv_group_t * group)
+{
+ if(!group) return false;
+ return group->click_focus ? true : false;
+}
+
+/**
+ * Get whether focus next/prev will allow wrapping from first->last or last->first object.
+ * @param group pointer to group
+ * @param en: true: wrapping enabled; false: wrapping disabled
+ */
+bool lv_group_get_wrap(lv_group_t * group)
+{
+ if(!group) return false;
+ return group->wrap ? true : false;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Default style modifier function
+ * @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
+ */
+static void style_mod_def(lv_style_t * style)
+{
+#if LV_COLOR_DEPTH != 1
+
+ /*Make the style to be a little bit orange*/
+ style->body.border.opa = LV_OPA_COVER;
+ style->body.border.color = LV_COLOR_ORANGE;
+
+ /*If not empty or has border then emphasis the border*/
+ if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
+
+ style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_ORANGE, LV_OPA_70);
+ style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_ORANGE, LV_OPA_70);
+ style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_ORANGE, LV_OPA_60);
+
+ style->text.color = lv_color_mix(style->text.color, LV_COLOR_ORANGE, LV_OPA_70);
+#else
+ style->body.border.opa = LV_OPA_COVER;
+ style->body.border.color = LV_COLOR_BLACK;
+ style->body.border.width = 2;
+
+#endif
+
+}
+
+/**
+ * Default style modifier function
+ * @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
+ */
+static void style_mod_edit_def(lv_style_t * style)
+{
+#if LV_COLOR_DEPTH != 1
+
+ /*Make the style to be a little bit orange*/
+ style->body.border.opa = LV_OPA_COVER;
+ style->body.border.color = LV_COLOR_GREEN;
+
+ /*If not empty or has border then emphasis the border*/
+ if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
+
+ style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_GREEN, LV_OPA_70);
+ style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_GREEN, LV_OPA_70);
+ style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_GREEN, LV_OPA_60);
+
+ style->text.color = lv_color_mix(style->text.color, LV_COLOR_GREEN, LV_OPA_70);
+#else
+ style->body.border.opa = LV_OPA_COVER;
+ style->body.border.color = LV_COLOR_BLACK;
+ style->body.border.width = 3;
+
+#endif
+
+}
+
+static void obj_to_foreground(lv_obj_t * obj)
+{
+ /*Search for 'top' attribute*/
+ lv_obj_t * i = obj;
+ lv_obj_t * last_top = NULL;
+ while(i != NULL) {
+ if(i->top != 0) last_top = i;
+ i = lv_obj_get_parent(i);
+ }
+
+ if(last_top != NULL) {
+ /*Move the last_top object to the foreground*/
+ lv_obj_t * par = lv_obj_get_parent(last_top);
+ /*After list change it will be the new head*/
+ if (lv_ll_chg_list(&par->child_ll, &par->child_ll, last_top))
+ lv_obj_invalidate(last_top); /*Only invalidate if not top*/
+ }
+}
+
+#endif /*USE_LV_GROUP != 0*/
diff --git a/bdk/libs/lvgl/lv_core/lv_group.h b/bdk/libs/lvgl/lv_core/lv_group.h
new file mode 100644
index 0000000..6be9b5c
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_group.h
@@ -0,0 +1,247 @@
+/**
+ * @file lv_group.h
+ *
+ */
+
+#ifndef LV_GROUP_H
+#define LV_GROUP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#include "lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*Predefined keys to control the focused object via lv_group_send(group, c)*/
+/*For compatibility in signal function define the keys regardless to LV_GROUP*/
+#define LV_GROUP_KEY_UP 17 /*0x11*/
+#define LV_GROUP_KEY_DOWN 18 /*0x12*/
+#define LV_GROUP_KEY_RIGHT 19 /*0x13*/
+#define LV_GROUP_KEY_LEFT 20 /*0x14*/
+#define LV_GROUP_KEY_ESC 27 /*0x1B*/
+#define LV_GROUP_KEY_DEL 127 /*0x7F*/
+#define LV_GROUP_KEY_BACKSPACE 8 /*0x08*/
+#define LV_GROUP_KEY_ENTER 10 /*0x0A, '\n'*/
+#define LV_GROUP_KEY_NEXT 9 /*0x09, '\t'*/
+#define LV_GROUP_KEY_PREV 11 /*0x0B, '*/
+
+#if USE_LV_GROUP != 0
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_group_t;
+
+typedef void (*lv_group_style_mod_func_t)(lv_style_t *);
+typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
+
+typedef struct _lv_group_t
+{
+ lv_ll_t obj_ll; /*Linked list to store the objects in the group */
+ lv_obj_t ** obj_focus; /*The object in focus*/
+ lv_group_style_mod_func_t style_mod; /*A function which modifies the style of the focused object*/
+ lv_group_style_mod_func_t style_mod_edit;/*A function which modifies the style of the focused object*/
+ lv_group_focus_cb_t focus_cb; /*A function to call when a new object is focused (optional)*/
+ lv_style_t style_tmp; /*Stores the modified style of the focused object */
+ uint8_t frozen :1; /*1: can't focus to new object*/
+ uint8_t editing :1; /*1: Edit mode, 0: Navigate mode*/
+ uint8_t click_focus :1; /*1: If an object in a group is clicked by an indev then it will be focused */
+ uint8_t refocus_policy :1; /*1: Focus prev if focused on deletion. 0: Focus prev if focused on deletion.*/
+ uint8_t wrap :1; /*1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end of list.*/
+} lv_group_t;
+
+typedef enum _lv_group_refocus_policy_t {
+ LV_GROUP_REFOCUS_POLICY_NEXT = 0,
+ LV_GROUP_REFOCUS_POLICY_PREV = 1
+} lv_group_refocus_policy_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create a new object group
+ * @return pointer to the new object group
+ */
+lv_group_t * lv_group_create(void);
+
+/**
+ * Delete a group object
+ * @param group pointer to a group
+ */
+void lv_group_del(lv_group_t * group);
+
+/**
+ * Add an object to a group
+ * @param group pointer to a group
+ * @param obj pointer to an object to add
+ */
+void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj);
+
+/**
+ * Remove an object from its group
+ * @param obj pointer to an object to remove
+ */
+void lv_group_remove_obj(lv_obj_t * obj);
+
+/**
+ * Focus on an object (defocus the current)
+ * @param obj pointer to an object to focus on
+ */
+void lv_group_focus_obj(lv_obj_t * obj);
+
+/**
+ * Focus the next object in a group (defocus the current)
+ * @param group pointer to a group
+ */
+void lv_group_focus_next(lv_group_t * group);
+
+/**
+ * Focus the previous object in a group (defocus the current)
+ * @param group pointer to a group
+ */
+void lv_group_focus_prev(lv_group_t * group);
+
+/**
+ * Do not let to change the focus from the current object
+ * @param group pointer to a group
+ * @param en true: freeze, false: release freezing (normal mode)
+ */
+void lv_group_focus_freeze(lv_group_t * group, bool en);
+
+/**
+ * Send a control character to the focuses object of a group
+ * @param group pointer to a group
+ * @param c a character (use LV_GROUP_KEY_.. to navigate)
+ * @return result of focused object in group.
+ */
+lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c);
+
+/**
+ * Set a function for a group which will modify the object's style if it is in focus
+ * @param group pointer to a group
+ * @param style_mod_func the style modifier function pointer
+ */
+void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func);
+
+/**
+ * Set a function for a group which will modify the object's style if it is in focus in edit mode
+ * @param group pointer to a group
+ * @param style_mod_func the style modifier function pointer
+ */
+void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func);
+
+/**
+ * Set a function for a group which will be called when a new object is focused
+ * @param group pointer to a group
+ * @param focus_cb the call back function or NULL if unused
+ */
+void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
+
+/**
+ * Set whether the next or previous item in a group is focused if the currently focussed obj is deleted.
+ * @param group pointer to a group
+ * @param new refocus policy enum
+ */
+void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
+
+/**
+ * Manually set the current mode (edit or navigate).
+ * @param group pointer to group
+ * @param edit: true: edit mode; false: navigate mode
+ */
+void lv_group_set_editing(lv_group_t * group, bool edit);
+
+/**
+ * Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
+ * @param group pointer to group
+ * @param en: true: enable `click_focus`
+ */
+void lv_group_set_click_focus(lv_group_t * group, bool en);
+
+/**
+ * Set whether focus next/prev will allow wrapping from first->last or last->first object.
+ * @param group pointer to group
+ * @param en: true: wrapping enabled; false: wrapping disabled
+ */
+void lv_group_set_wrap(lv_group_t * group, bool en);
+
+/**
+ * Modify a style with the set 'style_mod' function. The input style remains unchanged.
+ * @param group pointer to group
+ * @param style pointer to a style to modify
+ * @return a copy of the input style but modified with the 'style_mod' function
+ */
+lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style);
+
+/**
+ * Get the focused object or NULL if there isn't one
+ * @param group pointer to a group
+ * @return pointer to the focused object
+ */
+lv_obj_t * lv_group_get_focused(const lv_group_t * group);
+
+/**
+ * Get a the style modifier function of a group
+ * @param group pointer to a group
+ * @return pointer to the style modifier function
+ */
+lv_group_style_mod_func_t lv_group_get_style_mod_cb(const lv_group_t * group);
+
+/**
+ * Get a the style modifier function of a group in edit mode
+ * @param group pointer to a group
+ * @return pointer to the style modifier function
+ */
+lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(const lv_group_t * group);
+
+/**
+ * Get the focus callback function of a group
+ * @param group pointer to a group
+ * @return the call back function or NULL if not set
+ */
+lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
+
+/**
+ * Get the current mode (edit or navigate).
+ * @param group pointer to group
+ * @return true: edit mode; false: navigate mode
+ */
+bool lv_group_get_editing(const lv_group_t * group);
+
+/**
+ * Get the `click_focus` attribute.
+ * @param group pointer to group
+ * @return true: `click_focus` is enabled; false: disabled
+ */
+bool lv_group_get_click_focus(const lv_group_t * group);
+
+/**
+ * Get whether focus next/prev will allow wrapping from first->last or last->first object.
+ * @param group pointer to group
+ * @param en: true: wrapping enabled; false: wrapping disabled
+ */
+bool lv_group_get_wrap(lv_group_t * group);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*USE_LV_GROUP != 0*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_GROUP_H*/
diff --git a/bdk/libs/lvgl/lv_core/lv_indev.c b/bdk/libs/lvgl/lv_core/lv_indev.c
new file mode 100644
index 0000000..d1dc582
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_indev.c
@@ -0,0 +1,971 @@
+/**
+ * @file lv_indev_proc.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ ********************/
+#include "lv_indev.h"
+
+#include "../lv_hal/lv_hal_tick.h"
+#include "../lv_core/lv_group.h"
+#include "../lv_core/lv_refr.h"
+#include "../lv_misc/lv_task.h"
+#include "../lv_misc/lv_math.h"
+#include "../lv_draw/lv_draw_rbasic.h"
+#include "lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_INDEV_DRAG_THROW <= 0
+#warning "LV_INDEV_DRAG_THROW must be greater than 0"
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+#if LV_INDEV_READ_PERIOD != 0
+static void indev_proc_task(void * param);
+static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_proc_press(lv_indev_proc_t * proc);
+static void indev_proc_release(lv_indev_proc_t * proc);
+static void indev_proc_reset_query_handler(lv_indev_t * indev);
+static lv_obj_t * indev_search_obj(const lv_indev_proc_t * proc, lv_obj_t * obj);
+static void indev_drag(lv_indev_proc_t * state);
+static void indev_drag_throw(lv_indev_proc_t * state);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_indev_t * indev_act;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the display input device subsystem
+ */
+void lv_indev_init(void)
+{
+#if LV_INDEV_READ_PERIOD != 0
+ lv_task_create(indev_proc_task, LV_INDEV_READ_PERIOD, LV_TASK_PRIO_MID, NULL);
+#endif
+
+ lv_indev_reset(NULL); /*Reset all input devices*/
+}
+
+/**
+ * Get the currently processed input device. Can be used in action functions too.
+ * @return pointer to the currently processed input device or NULL if no input device processing right now
+ */
+lv_indev_t * lv_indev_get_act(void)
+{
+ return indev_act;
+}
+
+/**
+ * Get the type of an input device
+ * @param indev pointer to an input device
+ * @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
+ */
+lv_hal_indev_type_t lv_indev_get_type(const lv_indev_t * indev)
+{
+ if(indev == NULL) return LV_INDEV_TYPE_NONE;
+
+ return indev->driver.type;
+}
+/**
+ * Reset one or all input devices
+ * @param indev pointer to an input device to reset or NULL to reset all of them
+ */
+void lv_indev_reset(lv_indev_t * indev)
+{
+ if(indev) indev->proc.reset_query = 1;
+ else {
+ lv_indev_t * i = lv_indev_next(NULL);
+ while(i) {
+ i->proc.reset_query = 1;
+ i = lv_indev_next(i);
+ }
+ }
+}
+
+/**
+ * Reset the long press state of an input device
+ * @param indev pointer to an input device
+ */
+void lv_indev_reset_lpr(lv_indev_t * indev)
+{
+ indev->proc.long_pr_sent = 0;
+ indev->proc.longpr_rep_timestamp = lv_tick_get();
+ indev->proc.pr_timestamp = lv_tick_get();
+}
+
+/**
+ * Enable input devices device by type
+ * @param type Input device type
+ * @param enable true: enable this type; false: disable this type
+ */
+void lv_indev_enable(lv_hal_indev_type_t type, bool enable)
+{
+ lv_indev_t * i = lv_indev_next(NULL);
+
+ while(i) {
+ if(i->driver.type == type) i->proc.disabled = enable == false ? 1 : 0;
+ i = lv_indev_next(i);
+ }
+}
+
+/**
+ * Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param cur_obj pointer to an object to be used as cursor
+ */
+void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
+{
+ if(indev->driver.type != LV_INDEV_TYPE_POINTER) return;
+
+ indev->cursor = cur_obj;
+ if (indev->cursor)
+ {
+ lv_obj_set_parent(indev->cursor, lv_layer_sys());
+ lv_obj_set_pos(indev->cursor, indev->proc.act_point.x, indev->proc.act_point.y);
+ }
+}
+
+#if USE_LV_GROUP
+/**
+ * Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
+ * @param indev pointer to an input device
+ * @param group point to a group
+ */
+void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
+{
+ if(indev->driver.type == LV_INDEV_TYPE_KEYPAD || indev->driver.type == LV_INDEV_TYPE_ENCODER) indev->group = group;
+}
+#endif
+
+/**
+ * Set the an array of points for LV_INDEV_TYPE_BUTTON.
+ * These points will be assigned to the buttons to press a specific point on the screen
+ * @param indev pointer to an input device
+ * @param group point to a group
+ */
+void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t * points)
+{
+ if(indev->driver.type == LV_INDEV_TYPE_BUTTON) indev->btn_points = points;
+}
+
+/**
+ * Set feedback callback for indev.
+ * @param indev pointer to an input device
+ * @param feedback feedback callback
+ */
+void lv_indev_set_feedback(lv_indev_t *indev, lv_indev_feedback_t feedback)
+{
+ indev->feedback = feedback;
+}
+
+/**
+ * Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param point pointer to a point to store the result
+ */
+void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point)
+{
+ if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
+ point->x = -1;
+ point->y = -1;
+ } else {
+ point->x = indev->proc.act_point.x;
+ point->y = indev->proc.act_point.y;
+ }
+}
+
+/**
+ * Get the last key of an input device (for LV_INDEV_TYPE_KEYPAD)
+ * @param indev pointer to an input device
+ * @return the last pressed key (0 on error)
+ */
+uint32_t lv_indev_get_key(const lv_indev_t * indev)
+{
+ if(indev->driver.type != LV_INDEV_TYPE_KEYPAD) return 0;
+ else return indev->proc.last_key;
+}
+
+/**
+ * Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @return true: drag is in progress
+ */
+bool lv_indev_is_dragging(const lv_indev_t * indev)
+{
+ if(indev == NULL) return false;
+ if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) return false;
+ return indev->proc.drag_in_prog == 0 ? false : true;
+}
+
+/**
+ * Get the vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param point pointer to a point to store the vector
+ */
+void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point)
+{
+ if(indev == NULL) {
+ point->x = 0;
+ point->y = 0;
+ return;
+ }
+
+ if(indev->driver.type != LV_INDEV_TYPE_POINTER && indev->driver.type != LV_INDEV_TYPE_BUTTON) {
+ point->x = 0;
+ point->y = 0;
+ } else {
+ point->x = indev->proc.vect.x;
+ point->y = indev->proc.vect.y;
+ }
+}
+
+/**
+ * Get elapsed time since last press
+ * @param indev pointer to an input device (NULL to get the overall smallest inactivity)
+ * @return Elapsed ticks (milliseconds) since last press
+ */
+uint32_t lv_indev_get_inactive_time(const lv_indev_t * indev)
+{
+ uint32_t t;
+
+ if(indev) return t = lv_tick_elaps(indev->last_activity_time);
+
+ lv_indev_t * i;
+ t = UINT16_MAX;
+ i = lv_indev_next(NULL);
+ while(i) {
+ t = LV_MATH_MIN(t, lv_tick_elaps(i->last_activity_time));
+ i = lv_indev_next(i);
+ }
+
+ return t;
+}
+
+/**
+ * Get feedback callback for indev.
+ * @param indev pointer to an input device
+ * @return feedback callback
+ */
+lv_indev_feedback_t lv_indev_get_feedback(const lv_indev_t *indev)
+{
+ return indev->feedback;
+}
+
+/**
+ * Do nothing until the next release
+ * @param indev pointer to an input device
+ */
+void lv_indev_wait_release(lv_indev_t * indev)
+{
+ indev->proc.wait_unil_release = 1;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#if LV_INDEV_READ_PERIOD != 0
+/**
+ * Called periodically to handle the input devices
+ * @param param unused
+ */
+static void indev_proc_task(void * param)
+{
+ (void)param;
+
+
+ LV_LOG_TRACE("indev task started");
+
+ lv_indev_data_t data;
+ lv_indev_t * i;
+ i = lv_indev_next(NULL);
+
+ /*Read and process all indevs*/
+ while(i) {
+ indev_act = i;
+
+ /*Handle reset query before processing the point*/
+ indev_proc_reset_query_handler(i);
+
+ if(i->proc.disabled == 0) {
+ bool more_to_read;
+ do {
+ /*Read the data*/
+ more_to_read = lv_indev_read(i, &data);
+ indev_proc_reset_query_handler(i); /*The active object might deleted even in the read function*/
+ i->proc.state = data.state;
+
+ if(i->proc.state == LV_INDEV_STATE_PR) {
+ i->last_activity_time = lv_tick_get();
+ }
+
+ if(i->driver.type == LV_INDEV_TYPE_POINTER) {
+ indev_pointer_proc(i, &data);
+ } else if(i->driver.type == LV_INDEV_TYPE_KEYPAD) {
+ indev_keypad_proc(i, &data);
+ } else if(i->driver.type == LV_INDEV_TYPE_ENCODER) {
+ indev_encoder_proc(i, &data);
+ } else if(i->driver.type == LV_INDEV_TYPE_BUTTON) {
+ indev_button_proc(i, &data);
+ }
+ /*Handle reset query if it happened in during processing*/
+ indev_proc_reset_query_handler(i);
+ } while(more_to_read);
+ }
+ i = lv_indev_next(i); /*Go to the next indev*/
+ }
+
+ indev_act = NULL; /*End of indev processing, so no act indev*/
+
+ LV_LOG_TRACE("indev task finished");
+}
+
+
+/**
+ * Process a new point from LV_INDEV_TYPE_POINTER input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ */
+static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+ /*Move the cursor if set and moved*/
+ if(i->cursor != NULL &&
+ (i->proc.last_point.x != data->point.x ||
+ i->proc.last_point.y != data->point.y)) {
+ /*Use cursor's center as pointer*/
+ uint32_t off_x = lv_obj_get_width(i->cursor) >> 1;
+ uint32_t off_y = lv_obj_get_height(i->cursor) >> 1;
+ lv_obj_set_pos(i->cursor, data->point.x - off_x, data->point.y - off_y);
+ }
+
+ i->proc.act_point.x = data->point.x;
+ i->proc.act_point.y = data->point.y;
+
+ if(i->proc.state == LV_INDEV_STATE_PR) {
+#if LV_INDEV_POINT_MARKER != 0
+ lv_area_t area;
+ area.x1 = i->proc.act_point.x - (LV_INDEV_POINT_MARKER >> 1);
+ area.y1 = i->proc.act_point.y - (LV_INDEV_POINT_MARKER >> 1);
+ area.x2 = i->proc.act_point.x + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
+ area.y2 = i->proc.act_point.y + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
+ lv_rfill(&area, NULL, LV_COLOR_MAKE(0xFF, 0, 0), LV_OPA_COVER);
+#endif
+ indev_proc_press(&i->proc);
+ } else {
+ indev_proc_release(&i->proc);
+ }
+
+ i->proc.last_point.x = i->proc.act_point.x;
+ i->proc.last_point.y = i->proc.act_point.y;
+}
+
+/**
+ * Process a new point from LV_INDEV_TYPE_KEYPAD input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ */
+static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+#if USE_LV_GROUP
+ if(i->group == NULL) return;
+
+ /*Key press happened*/
+ if(data->state == LV_INDEV_STATE_PR &&
+ i->proc.last_state == LV_INDEV_STATE_REL) {
+ i->proc.pr_timestamp = lv_tick_get();
+ lv_obj_t * focused = lv_group_get_focused(i->group);
+ if(focused && data->key == LV_GROUP_KEY_ENTER) {
+ focused->signal_func(focused, LV_SIGNAL_PRESSED, indev_act);
+ }
+ }
+ /*Pressing*/
+ else if(data->state == LV_INDEV_STATE_PR && i->proc.last_state == LV_INDEV_STATE_PR) {
+ if(data->key == LV_GROUP_KEY_ENTER &&
+ i->proc.long_pr_sent == 0 &&
+ lv_tick_elaps(i->proc.pr_timestamp) > LV_INDEV_LONG_PRESS_TIME) {
+ /*On enter long press leave edit mode.*/
+ lv_obj_t * focused = lv_group_get_focused(i->group);
+ if(focused) {
+ focused->signal_func(focused, LV_SIGNAL_LONG_PRESS, indev_act);
+ i->proc.long_pr_sent = 1;
+ }
+ }
+ }
+ /*Release happened*/
+ else if(data->state == LV_INDEV_STATE_REL && i->proc.last_state == LV_INDEV_STATE_PR) {
+ /*The user might clear the key when it was released. Always release the pressed key*/
+ data->key = i->proc.last_key;
+
+ /* Edit mode is not used by KEYPAD devices.
+ * So leave edit mode if we are in it before focusing on the next/prev object*/
+ if(data->key == LV_GROUP_KEY_NEXT || data->key == LV_GROUP_KEY_PREV) {
+ lv_group_set_editing(i->group, false);
+ }
+
+ if(data->key == LV_GROUP_KEY_NEXT) {
+ lv_group_focus_next(i->group);
+ } else if(data->key == LV_GROUP_KEY_PREV) {
+ lv_group_focus_prev(i->group);
+ } else if(data->key == LV_GROUP_KEY_ENTER) {
+ if(!i->proc.long_pr_sent) {
+ lv_group_send_data(i->group, data->key);
+ }
+ } else {
+ lv_group_send_data(i->group, data->key);
+ }
+
+ if(i->proc.reset_query) return; /*The object might be deleted in `focus_cb` or due to any other user event*/
+
+ i->proc.pr_timestamp = 0;
+ i->proc.long_pr_sent = 0;
+ }
+
+ i->proc.last_state = data->state;
+ i->proc.last_key = data->key;
+#else
+ (void)data; /*Unused*/
+ (void)i; /*Unused*/
+#endif
+}
+
+/**
+ * Process a new point from LV_INDEV_TYPE_ENCODER input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ */
+static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+#if USE_LV_GROUP
+ if(i->group == NULL) return;
+
+ /*Process the steps first. They are valid only with released button*/
+ if(data->state == LV_INDEV_STATE_REL) {
+ /*In edit mode send LEFT/RIGHT keys*/
+ if(lv_group_get_editing(i->group)) {
+ int32_t s;
+ if(data->enc_diff < 0) {
+ for(s = 0; s < -data->enc_diff; s++) lv_group_send_data(i->group, LV_GROUP_KEY_LEFT);
+ } else if(data->enc_diff > 0) {
+ for(s = 0; s < data->enc_diff; s++) lv_group_send_data(i->group, LV_GROUP_KEY_RIGHT);
+ }
+ }
+ /*In navigate mode focus on the next/prev objects*/
+ else {
+ int32_t s;
+ if(data->enc_diff < 0) {
+ for(s = 0; s < -data->enc_diff; s++) lv_group_focus_prev(i->group);
+ } else if(data->enc_diff > 0) {
+ for(s = 0; s < data->enc_diff; s++) lv_group_focus_next(i->group);
+ }
+ }
+ }
+
+ /*Key press happened*/
+ if(data->state == LV_INDEV_STATE_PR &&
+ i->proc.last_state == LV_INDEV_STATE_REL) {
+ i->proc.pr_timestamp = lv_tick_get();
+ }
+ /*Pressing*/
+ else if(data->state == LV_INDEV_STATE_PR && i->proc.last_state == LV_INDEV_STATE_PR) {
+ if(i->proc.long_pr_sent == 0 &&
+ lv_tick_elaps(i->proc.pr_timestamp) > LV_INDEV_LONG_PRESS_TIME) {
+ /*On enter long press leave edit mode.*/
+ lv_obj_t * focused = lv_group_get_focused(i->group);
+
+ bool editable = false;
+ if(focused) focused->signal_func(focused, LV_SIGNAL_GET_EDITABLE, &editable);
+
+ if(editable) {
+ if(i->group->obj_ll.head != i->group->obj_ll.tail)
+ lv_group_set_editing(i->group, lv_group_get_editing(i->group) ? false : true); /*Toggle edit mode on long press*/
+ else if(focused)
+ focused->signal_func(focused, LV_SIGNAL_LONG_PRESS, indev_act);
+ }
+ /*If not editable then just send a long press signal*/
+ else {
+ if(focused)
+ focused->signal_func(focused, LV_SIGNAL_LONG_PRESS, indev_act);
+ }
+ i->proc.long_pr_sent = 1;
+ }
+ }
+ /*Release happened*/
+ else if(data->state == LV_INDEV_STATE_REL && i->proc.last_state == LV_INDEV_STATE_PR) {
+ lv_obj_t * focused = lv_group_get_focused(i->group);
+ bool editable = false;
+ if(focused) focused->signal_func(focused, LV_SIGNAL_GET_EDITABLE, &editable);
+
+ /*The button was released on a non-editable object. Just send enter*/
+ if(!editable) {
+ lv_group_send_data(i->group, LV_GROUP_KEY_ENTER);
+ }
+ /*An object is being edited and the button is releases. Just send enter */
+ else if(i->group->editing) {
+ if(!i->proc.long_pr_sent || i->group->obj_ll.head == i->group->obj_ll.tail)
+ lv_group_send_data(i->group, LV_GROUP_KEY_ENTER); /*Ignore long pressed enter release because it comes from mode switch*/
+ }
+ /*If the focused object is editable and now in navigate mode then enter edit mode*/
+ else if(editable && !i->group->editing && !i->proc.long_pr_sent) {
+ lv_group_set_editing(i->group, lv_group_get_editing(i->group) ? false : true); /*Toggle edit mode on long press*/
+ }
+
+ if(i->proc.reset_query) return; /*The object might be deleted in `focus_cb` or due to any other user event*/
+
+ i->proc.pr_timestamp = 0;
+ i->proc.long_pr_sent = 0;
+ }
+
+ i->proc.last_state = data->state;
+ i->proc.last_key = data->key;
+#else
+ (void)data; /*Unused*/
+ (void)i; /*Unused*/
+#endif
+}
+
+/**
+ * Process new points from a input device. indev->state.pressed has to be set
+ * @param indev pointer to an input device state
+ * @param x x coordinate of the next point
+ * @param y y coordinate of the next point
+ */
+static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+ i->proc.act_point.x = i->btn_points[data->btn].x;
+ i->proc.act_point.y = i->btn_points[data->btn].y;
+
+ /*Still the same point is pressed*/
+ if(i->proc.last_point.x == i->proc.act_point.x &&
+ i->proc.last_point.y == i->proc.act_point.y &&
+ data->state == LV_INDEV_STATE_PR) {
+#if LV_INDEV_POINT_MARKER != 0
+ lv_area_t area;
+ area.x1 = i->proc.act_point.x - (LV_INDEV_POINT_MARKER >> 1);
+ area.y1 = i->proc.act_point.y - (LV_INDEV_POINT_MARKER >> 1);
+ area.x2 = i->proc.act_point.x + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
+ area.y2 = i->proc.act_point.y + ((LV_INDEV_POINT_MARKER >> 1) | 0x1);
+ lv_rfill(&area, NULL, LV_COLOR_MAKE(0xFF, 0, 0), LV_OPA_COVER);
+#endif
+ indev_proc_press(&i->proc);
+ } else {
+ /*If a new point comes always make a release*/
+ indev_proc_release(&i->proc);
+ }
+
+ i->proc.last_point.x = i->proc.act_point.x;
+ i->proc.last_point.y = i->proc.act_point.y;
+}
+
+/**
+ * Process the pressed state of LV_INDEV_TYPE_POINER input devices
+ * @param indev pointer to an input device 'proc'
+ */
+static void indev_proc_press(lv_indev_proc_t * proc)
+{
+ lv_obj_t * pr_obj = proc->act_obj;
+
+ if(proc->wait_unil_release != 0) return;
+
+ /*If there is no last object then search*/
+ if(proc->act_obj == NULL) {
+ pr_obj = indev_search_obj(proc, lv_layer_top());
+ if(pr_obj == NULL) pr_obj = indev_search_obj(proc, lv_scr_act());
+ }
+ /*If there is last object but it is not dragged and not protected also search*/
+ else if(proc->drag_in_prog == 0 &&
+ lv_obj_is_protected(proc->act_obj, LV_PROTECT_PRESS_LOST) == false) {/*Now act_obj != NULL*/
+ pr_obj = indev_search_obj(proc, lv_layer_top());
+ if(pr_obj == NULL) pr_obj = indev_search_obj(proc, lv_scr_act());
+ }
+ /*If a dragable or a protected object was the last then keep it*/
+ else {
+
+ }
+
+ /*If a new object was found reset some variables and send a pressed signal*/
+ if(pr_obj != proc->act_obj) {
+
+ proc->last_point.x = proc->act_point.x;
+ proc->last_point.y = proc->act_point.y;
+
+ /*If a new object found the previous was lost, so send a signal*/
+ if(proc->act_obj != NULL) {
+ proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESS_LOST, indev_act);
+ if(proc->reset_query != 0) return;
+ }
+
+ proc->act_obj = pr_obj; /*Save the pressed object*/
+ proc->last_obj = proc->act_obj; /*Refresh the last_obj*/
+
+ if(proc->act_obj != NULL) {
+ /* Save the time when the obj pressed.
+ * It is necessary to count the long press time.*/
+ proc->pr_timestamp = lv_tick_get();
+ proc->long_pr_sent = 0;
+ proc->drag_range_out = 0;
+ proc->drag_in_prog = 0;
+ proc->drag_sum.x = 0;
+ proc->drag_sum.y = 0;
+ proc->vect.x = 0;
+ proc->vect.y = 0;
+
+ /*Search for 'top' attribute*/
+ lv_obj_t * i = proc->act_obj;
+ lv_obj_t * last_top = NULL;
+ while(i != NULL) {
+ if(i->top != 0) last_top = i;
+ i = lv_obj_get_parent(i);
+ }
+
+ if(last_top != NULL) {
+ /*Move the last_top object to the foreground*/
+ lv_obj_t * par = lv_obj_get_parent(last_top);
+ /*After list change it will be the new head*/
+ if (lv_ll_chg_list(&par->child_ll, &par->child_ll, last_top))
+ lv_obj_invalidate(last_top); /*Only invalidate if not top*/
+ }
+
+ /*Send a signal about the press*/
+ proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESSED, indev_act);
+ if(proc->reset_query != 0) return;
+ }
+ }
+
+ /*Calculate the vector*/
+ proc->vect.x = proc->act_point.x - proc->last_point.x;
+ proc->vect.y = proc->act_point.y - proc->last_point.y;
+
+ /*If there is active object and it can be dragged run the drag*/
+ if(proc->act_obj != NULL) {
+ proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESSING, indev_act);
+ if(proc->reset_query != 0) return;
+
+ indev_drag(proc);
+ if(proc->reset_query != 0) return;
+
+ /*If there is no drag then check for long press time*/
+ if(proc->drag_in_prog == 0 && proc->long_pr_sent == 0) {
+ /*Send a signal about the long press if enough time elapsed*/
+ if(lv_tick_elaps(proc->pr_timestamp) > LV_INDEV_LONG_PRESS_TIME) {
+ pr_obj->signal_func(pr_obj, LV_SIGNAL_LONG_PRESS, indev_act);
+ if(proc->reset_query != 0) return;
+
+ /*Mark the signal sending to do not send it again*/
+ proc->long_pr_sent = 1;
+
+ /*Save the long press time stamp for the long press repeat handler*/
+ proc->longpr_rep_timestamp = lv_tick_get();
+ }
+ }
+ /*Send long press repeated signal*/
+ if(proc->drag_in_prog == 0 && proc->long_pr_sent == 1) {
+ /*Send a signal about the long press repeate if enough time elapsed*/
+ if(lv_tick_elaps(proc->longpr_rep_timestamp) > LV_INDEV_LONG_PRESS_REP_TIME) {
+ pr_obj->signal_func(pr_obj, LV_SIGNAL_LONG_PRESS_REP, indev_act);
+ if(proc->reset_query != 0) return;
+ proc->longpr_rep_timestamp = lv_tick_get();
+
+ }
+ }
+ }
+}
+
+/**
+ * Process the released state of LV_INDEV_TYPE_POINER input devices
+ * @param proc pointer to an input device 'proc'
+ */
+static void indev_proc_release(lv_indev_proc_t * proc)
+{
+ if(proc->wait_unil_release != 0) {
+ proc->act_obj = NULL;
+ proc->last_obj = NULL;
+ proc->pr_timestamp = 0;
+ proc->longpr_rep_timestamp = 0;
+ proc->wait_unil_release = 0;
+ }
+
+ /*Forgot the act obj and send a released signal */
+ if(proc->act_obj != NULL) {
+ /* If the object was protected against press lost then it possible that
+ * the object is already not pressed but still it is the `act_obj`.
+ * In this case send the `LV_SIGNAL_RELEASED` if the indev is ON the `act_obj` */
+ if(lv_obj_is_protected(proc->act_obj, LV_PROTECT_PRESS_LOST)) {
+ /* Search the object on the current current coordinates.
+ * The start object is the object itself. If not ON it the the result will be NULL*/
+ lv_obj_t * obj_on = indev_search_obj(proc, proc->act_obj);
+ if(obj_on == proc->act_obj) proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_RELEASED, indev_act);
+ else proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_PRESS_LOST, indev_act);
+
+ }
+ /* The simple case: `act_obj` was not protected against press lost.
+ * If it is already not pressed then was handled in `indev_proc_press`*/
+ else {
+ proc->act_obj->signal_func(proc->act_obj, LV_SIGNAL_RELEASED, indev_act);
+ }
+
+ if(proc->reset_query != 0) return;
+
+ /*Handle click focus*/
+#if USE_LV_GROUP
+ /*Edit mode is not used by POINTER devices. So leave edit mode if we are in it*/
+ lv_group_t * act_g = lv_obj_get_group(proc->act_obj);
+ if(lv_group_get_editing(act_g)) {
+ lv_group_set_editing(act_g, false);
+ }
+
+ /*Check, if the parent is in a group focus on it.*/
+ if(lv_obj_is_protected(proc->act_obj, LV_PROTECT_CLICK_FOCUS) == false) { /*Respect the click protection*/
+ lv_group_t * g = lv_obj_get_group(proc->act_obj);
+ lv_obj_t * parent = proc->act_obj;
+
+ while(g == NULL) {
+ parent = lv_obj_get_parent(parent);
+ if(parent == NULL) break;
+ if(lv_obj_is_protected(parent, LV_PROTECT_CLICK_FOCUS)) { /*Ignore is the protected against click focus*/
+ parent = NULL;
+ break;
+ }
+ g = lv_obj_get_group(parent);
+ }
+
+ if(g != NULL && parent != NULL)
+ if(lv_group_get_click_focus(g)) {
+ lv_group_focus_obj(parent);
+ }
+ }
+#endif
+
+ if(proc->reset_query != 0) return;
+ proc->act_obj = NULL;
+ proc->pr_timestamp = 0;
+ proc->longpr_rep_timestamp = 0;
+ }
+
+ /*The reset can be set in the signal function.
+ * In case of reset query ignore the remaining parts.*/
+ if(proc->last_obj != NULL && proc->reset_query == 0) {
+ indev_drag_throw(proc);
+ if(proc->reset_query != 0) return;
+ }
+}
+
+/**
+ * Process a new point from LV_INDEV_TYPE_BUTTON input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ * Reset input device if a reset query has been sent to it
+ * @param indev pointer to an input device
+ */
+static void indev_proc_reset_query_handler(lv_indev_t * indev)
+{
+ if(indev->proc.reset_query) {
+ indev->proc.act_obj = NULL;
+ indev->proc.last_obj = NULL;
+ indev->proc.drag_range_out = 0;
+ indev->proc.drag_in_prog = 0;
+ indev->proc.long_pr_sent = 0;
+ indev->proc.pr_timestamp = 0;
+ indev->proc.longpr_rep_timestamp = 0;
+ indev->proc.drag_sum.x = 0;
+ indev->proc.drag_sum.y = 0;
+ indev->proc.reset_query = 0;
+ }
+}
+/**
+ * Search the most top, clickable object on the last point of an input device
+ * @param proc pointer to the `lv_indev_proc_t` part of the input device
+ * @param obj pointer to a start object, typically the screen
+ * @return pointer to the found object or NULL if there was no suitable object
+ */
+static lv_obj_t * indev_search_obj(const lv_indev_proc_t * proc, lv_obj_t * obj)
+{
+ lv_obj_t * found_p = NULL;
+
+ /*If the point is on this object*/
+ /*Check its children too*/
+ if(lv_area_is_point_on(&obj->coords, &proc->act_point)) {
+ lv_obj_t * i;
+
+ LL_READ(obj->child_ll, i) {
+ found_p = indev_search_obj(proc, i);
+
+ /*If a child was found then break*/
+ if(found_p != NULL) {
+ break;
+ }
+ }
+
+ /*If then the children was not ok, and this obj is clickable
+ * and it or its parent is not hidden then save this object*/
+ if(found_p == NULL && lv_obj_get_click(obj) != false) {
+ lv_obj_t * hidden_i = obj;
+ while(hidden_i != NULL) {
+ if(lv_obj_get_hidden(hidden_i) == true) break;
+ hidden_i = lv_obj_get_parent(hidden_i);
+ }
+ /*No parent found with hidden == true*/
+ if(hidden_i == NULL) found_p = obj;
+ }
+
+ }
+
+ return found_p;
+}
+
+/**
+ * Handle the dragging of indev_proc_p->act_obj
+ * @param indev pointer to a input device state
+ */
+static void indev_drag(lv_indev_proc_t * state)
+{
+ lv_obj_t * drag_obj = state->act_obj;
+
+ /*If drag parent is active check recursively the drag_parent attribute*/
+ while(lv_obj_get_drag_parent(drag_obj) != false &&
+ drag_obj != NULL) {
+ drag_obj = lv_obj_get_parent(drag_obj);
+ }
+
+ if(drag_obj == NULL) return;
+
+ if(lv_obj_get_drag(drag_obj) == false) return;
+
+ /*Count the movement by drag*/
+ state->drag_sum.x += state->vect.x;
+ state->drag_sum.y += state->vect.y;
+
+ /*Enough move?*/
+ if(state->drag_range_out == 0) {
+ /*If a move is greater then LV_DRAG_LIMIT then begin the drag*/
+ if(LV_MATH_ABS(state->drag_sum.x) >= LV_INDEV_DRAG_LIMIT ||
+ LV_MATH_ABS(state->drag_sum.y) >= LV_INDEV_DRAG_LIMIT) {
+ state->drag_range_out = 1;
+ }
+ }
+
+ /*If the drag limit is stepped over then handle the dragging*/
+ if(state->drag_range_out != 0) {
+ /*Set new position if the vector is not zero*/
+ if(state->vect.x != 0 ||
+ state->vect.y != 0) {
+ /*Get the coordinates of the object and modify them*/
+ lv_coord_t act_x = lv_obj_get_x(drag_obj);
+ lv_coord_t act_y = lv_obj_get_y(drag_obj);
+ uint16_t inv_buf_size = lv_refr_get_buf_size(); /*Get the number of currently invalidated areas*/
+
+ lv_coord_t prev_x = drag_obj->coords.x1;
+ lv_coord_t prev_y = drag_obj->coords.y1;
+ lv_coord_t prev_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
+ lv_coord_t prev_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
+
+ lv_obj_set_pos(drag_obj, act_x + state->vect.x, act_y + state->vect.y);
+
+ /*Set the drag in progress flag if the object is really moved*/
+
+ if(drag_obj->coords.x1 != prev_x || drag_obj->coords.y1 != prev_y) {
+ if(state->drag_range_out != 0) { /*Send the drag begin signal on first move*/
+ drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_BEGIN, indev_act);
+ if(state->reset_query != 0) return;
+ }
+ state->drag_in_prog = 1;
+ }
+ /*If the object didn't moved then clear the invalidated areas*/
+ else {
+ /*In a special case if the object is moved on a page and
+ * the scrollable has fit == true and the object is dragged of the page then
+ * while its coordinate is not changing only the parent's size is reduced */
+ lv_coord_t act_par_w = lv_obj_get_width(lv_obj_get_parent(drag_obj));
+ lv_coord_t act_par_h = lv_obj_get_height(lv_obj_get_parent(drag_obj));
+ if(act_par_w == prev_par_w && act_par_h == prev_par_h) {
+ uint16_t new_inv_buf_size = lv_refr_get_buf_size();
+ lv_refr_pop_from_buf(new_inv_buf_size - inv_buf_size);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Handle throwing by drag if the drag is ended
+ * @param indev pointer to an input device state
+ */
+static void indev_drag_throw(lv_indev_proc_t * state)
+{
+ if(state->drag_in_prog == 0) return;
+
+ /*Set new position if the vector is not zero*/
+ lv_obj_t * drag_obj = state->last_obj;
+
+ /*If drag parent is active check recursively the drag_parent attribute*/
+ while(lv_obj_get_drag_parent(drag_obj) != false &&
+ drag_obj != NULL) {
+ drag_obj = lv_obj_get_parent(drag_obj);
+ }
+
+ if(drag_obj == NULL) return;
+
+ /*Return if the drag throw is not enabled*/
+ if(lv_obj_get_drag_throw(drag_obj) == false) {
+ state->drag_in_prog = 0;
+ drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
+ return;
+ }
+
+ /*Reduce the vectors*/
+ state->vect.x = state->vect.x * (100 - LV_INDEV_DRAG_THROW) / 100;
+ state->vect.y = state->vect.y * (100 - LV_INDEV_DRAG_THROW) / 100;
+
+ if(state->vect.x != 0 ||
+ state->vect.y != 0) {
+ /*Get the coordinates and modify them*/
+ lv_area_t coords_ori;
+ lv_obj_get_coords(drag_obj, &coords_ori);
+ lv_coord_t act_x = lv_obj_get_x(drag_obj) + state->vect.x;
+ lv_coord_t act_y = lv_obj_get_y(drag_obj) + state->vect.y;
+ lv_obj_set_pos(drag_obj, act_x, act_y);
+
+ lv_area_t coord_new;
+ lv_obj_get_coords(drag_obj, &coord_new);
+
+ /*If non of the coordinates are changed then do not continue throwing*/
+ if((coords_ori.x1 == coord_new.x1 || state->vect.x == 0) &&
+ (coords_ori.y1 == coord_new.y1 || state->vect.y == 0)) {
+ state->drag_in_prog = 0;
+ state->vect.x = 0;
+ state->vect.y = 0;
+ drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
+
+ }
+ }
+ /*If the vectors become 0 -> drag_in_prog = 0 and send a drag end signal*/
+ else {
+ state->drag_in_prog = 0;
+ drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, indev_act);
+ }
+}
+#endif
diff --git a/bdk/libs/lvgl/lv_core/lv_indev.h b/bdk/libs/lvgl/lv_core/lv_indev.h
new file mode 100644
index 0000000..19b047c
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_indev.h
@@ -0,0 +1,157 @@
+/**
+ * @file lv_indev_proc.h
+ *
+ */
+
+#ifndef LV_INDEV_H
+#define LV_INDEV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "../lv_hal/lv_hal_indev.h"
+#include "../lv_core/lv_group.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the display input device subsystem
+ */
+void lv_indev_init(void);
+
+/**
+ * Get the currently processed input device. Can be used in action functions too.
+ * @return pointer to the currently processed input device or NULL if no input device processing right now
+ */
+lv_indev_t * lv_indev_get_act(void);
+
+
+/**
+ * Get the type of an input device
+ * @param indev pointer to an input device
+ * @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
+ */
+lv_hal_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
+
+/**
+ * Reset one or all input devices
+ * @param indev pointer to an input device to reset or NULL to reset all of them
+ */
+void lv_indev_reset(lv_indev_t * indev);
+
+/**
+ * Reset the long press state of an input device
+ * @param indev_proc pointer to an input device
+ */
+void lv_indev_reset_lpr(lv_indev_t * indev);
+
+/**
+ * Enable input devices device by type
+ * @param type Input device type
+ * @param enable true: enable this type; false: disable this type
+ */
+void lv_indev_enable(lv_hal_indev_type_t type, bool enable);
+
+/**
+ * Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param cur_obj pointer to an object to be used as cursor
+ */
+void lv_indev_set_cursor(lv_indev_t *indev, lv_obj_t *cur_obj);
+
+#if USE_LV_GROUP
+/**
+ * Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
+ * @param indev pointer to an input device
+ * @param group point to a group
+ */
+void lv_indev_set_group(lv_indev_t *indev, lv_group_t *group);
+#endif
+
+/**
+ * Set the an array of points for LV_INDEV_TYPE_BUTTON.
+ * These points will be assigned to the buttons to press a specific point on the screen
+ * @param indev pointer to an input device
+ * @param group point to a group
+ */
+void lv_indev_set_button_points(lv_indev_t *indev, const lv_point_t *points);
+
+/**
+ * Set feedback callback for indev.
+ * @param indev pointer to an input device
+ * @param feedback feedback callback
+ */
+void lv_indev_set_feedback(lv_indev_t *indev, lv_indev_feedback_t feedback);
+
+/**
+ * Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param point pointer to a point to store the result
+ */
+void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
+
+/**
+ * Get the last key of an input device (for LV_INDEV_TYPE_KEYPAD)
+ * @param indev pointer to an input device
+ * @return the last pressed key (0 on error)
+ */
+uint32_t lv_indev_get_key(const lv_indev_t * indev);
+
+/**
+ * Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @return true: drag is in progress
+ */
+bool lv_indev_is_dragging(const lv_indev_t * indev);
+
+/**
+ * Get the vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param point pointer to a point to store the vector
+ */
+void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
+/**
+ * Get elapsed time since last press
+ * @param indev pointer to an input device (NULL to get the overall smallest inactivity)
+ * @return Elapsed ticks (milliseconds) since last press
+ */
+uint32_t lv_indev_get_inactive_time(const lv_indev_t * indev);
+
+/**
+ * Get feedback callback for indev.
+ * @param indev pointer to an input device
+ * @return feedback callback
+ */
+lv_indev_feedback_t lv_indev_get_feedback(const lv_indev_t *indev);
+
+/**
+ * Do nothing until the next release
+ * @param indev pointer to an input device
+ */
+void lv_indev_wait_release(lv_indev_t * indev);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_INDEV_H*/
diff --git a/bdk/libs/lvgl/lv_core/lv_lang.c b/bdk/libs/lvgl/lv_core/lv_lang.c
new file mode 100644
index 0000000..c96ed78
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_lang.c
@@ -0,0 +1,117 @@
+/**
+ * @file lv_lang.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_lang.h"
+#if USE_LV_MULTI_LANG
+
+#include "lv_obj.h"
+#include "../lv_misc/lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lang_set_core(lv_obj_t * obj);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static uint8_t lang_act = 0;
+static const void * (*get_txt)(uint16_t);
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Change the language
+ * @param lang_id the id of the
+ */
+void lv_lang_set(uint8_t lang_id)
+{
+ lang_act = lang_id;
+
+ lv_obj_t * i;
+ LL_READ(LV_GC_ROOT(_lv_scr_ll), i) {
+ i->signal_func(i, LV_SIGNAL_LANG_CHG, NULL);
+
+ lang_set_core(i);
+ }
+
+ lang_set_core(lv_scr_act());
+}
+
+/**
+ * Set a function to get the texts of the set languages from a `txt_id`
+ * @param fp a function pointer to get the texts
+ */
+void lv_lang_set_text_func(const void * (*fp)(uint16_t))
+{
+ get_txt = fp;
+}
+
+/**
+ * Use the function set by `lv_lang_set_text_func` to get the `txt_id` text in the set language
+ * @param txt_id an ID of the text to get
+ * @return the `txt_id` txt on the set language
+ */
+const void * lv_lang_get_text(uint16_t txt_id)
+{
+ if(get_txt == NULL) {
+ LV_LOG_WARN("lv_lang_get_text: text_func is not specified");
+ return NULL; /*No text_get function specified */
+ }
+ if(txt_id == LV_LANG_TXT_ID_NONE) {
+ LV_LOG_WARN("lv_lang_get_text: attempts to get invalid text ID");
+ return NULL; /*Invalid txt_id*/
+ }
+
+ return get_txt(txt_id);
+}
+
+
+/**
+ * Return with ID of the currently selected language
+ * @return pointer to the active screen object (loaded by 'lv_scr_load()')
+ */
+uint8_t lv_lang_act(void)
+{
+ return lang_act;
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Change the language of the children. (Called recursively)
+ * @param obj pointer to an object
+ */
+static void lang_set_core(lv_obj_t * obj)
+{
+ lv_obj_t * i;
+ LL_READ(obj->child_ll, i) {
+ i->signal_func(i, LV_SIGNAL_LANG_CHG, NULL);
+
+ lang_set_core(i);
+ }
+}
+
+#endif /*USE_LV_MULTI_LANG*/
diff --git a/bdk/libs/lvgl/lv_core/lv_lang.h b/bdk/libs/lvgl/lv_core/lv_lang.h
new file mode 100644
index 0000000..5d76f64
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_lang.h
@@ -0,0 +1,74 @@
+/**
+ * @file lv_lang.h
+ *
+ */
+
+#ifndef LV_LANG_H
+#define LV_LANG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#if USE_LV_MULTI_LANG
+
+#include
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_LANG_TXT_ID_NONE 0xFFFF /*Used to not assign any text IDs for a multi-language object.*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Change the language
+ * @param lang_id the id of the
+ */
+void lv_lang_set(uint8_t lang_id);
+
+/**
+ * Set a function to get the texts of the set languages from a `txt_id`
+ * @param fp a function pointer to get the texts
+ */
+void lv_lang_set_text_func(const void * (*fp)(uint16_t));
+
+/**
+ * Use the function set by `lv_lang_set_text_func` to get the `txt_id` text in the set language
+ * @param txt_id an ID of the text to get
+ * @return the `txt_id` txt on the set language
+ */
+const void * lv_lang_get_text(uint16_t txt_id);
+
+/**
+ * Return with ID of the currently selected language
+ * @return pointer to the active screen object (loaded by 'lv_scr_load()')
+ */
+uint8_t lv_lang_act(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*USE_LV_MULTI_LANG*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_LANG_H*/
diff --git a/bdk/libs/lvgl/lv_core/lv_obj.c b/bdk/libs/lvgl/lv_core/lv_obj.c
new file mode 100644
index 0000000..95c41a8
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_obj.c
@@ -0,0 +1,1986 @@
+/**
+ * @file lv_base_obj.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "lv_indev.h"
+#include "lv_refr.h"
+#include "lv_group.h"
+#include "../lv_themes/lv_theme.h"
+#include "../lv_draw/lv_draw.h"
+#include "../lv_draw/lv_draw_rbasic.h"
+#include "../lv_misc/lv_anim.h"
+#include "../lv_misc/lv_task.h"
+#include "../lv_misc/lv_fs.h"
+#include "../lv_misc/lv_ufs.h"
+#include
+#include
+#include "../lv_misc/lv_gc.h"
+
+#if defined(LV_GC_INCLUDE)
+# include LV_GC_INCLUDE
+#endif /* LV_ENABLE_GC */
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_OBJ_DEF_WIDTH (LV_DPI)
+#define LV_OBJ_DEF_HEIGHT (2 * LV_DPI / 3)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff);
+static void report_style_mod_core(void * style_p, lv_obj_t * obj);
+static void refresh_children_style(lv_obj_t * obj);
+static void delete_children(lv_obj_t * obj);
+static bool lv_obj_design(lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode);
+static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+static bool _lv_initialized = false;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Init. the 'lv' library.
+ */
+void lv_init(void)
+{
+ /* Do nothing if already initialized */
+ if (_lv_initialized)
+ return;
+
+ LV_GC_ROOT(_lv_def_scr) = NULL;
+ LV_GC_ROOT(_lv_act_scr) = NULL;
+ LV_GC_ROOT(_lv_top_layer) = NULL;
+ LV_GC_ROOT(_lv_sys_layer) = NULL;
+ LV_GC_ROOT(_lv_disp_list) = NULL;
+ LV_GC_ROOT(_lv_indev_list) = NULL;
+
+ LV_LOG_TRACE("lv_init started");
+
+ /*Initialize the lv_misc modules*/
+ lv_mem_init();
+ lv_task_init();
+
+#if USE_LV_FILESYSTEM
+ lv_fs_init();
+ lv_ufs_init();
+#endif
+
+ lv_font_init();
+#if USE_LV_ANIMATION
+ lv_anim_init();
+#endif
+
+ /*Init. the sstyles*/
+ lv_style_init();
+
+ /*Initialize the screen refresh system*/
+ lv_refr_init();
+
+ /*Create the default screen*/
+ lv_ll_init(&LV_GC_ROOT(_lv_scr_ll), sizeof(lv_obj_t));
+ LV_GC_ROOT(_lv_def_scr) = lv_obj_create(NULL, NULL);
+
+ LV_GC_ROOT(_lv_act_scr) = LV_GC_ROOT(_lv_def_scr);
+
+ LV_GC_ROOT(_lv_top_layer) = lv_obj_create(NULL, NULL);
+ lv_obj_set_style(LV_GC_ROOT(_lv_top_layer), &lv_style_transp_fit);
+
+ LV_GC_ROOT(_lv_sys_layer) = lv_obj_create(NULL, NULL);
+ lv_obj_set_style(LV_GC_ROOT(_lv_sys_layer), &lv_style_transp_fit);
+
+ /*Refresh the screen*/
+ lv_obj_invalidate(LV_GC_ROOT(_lv_act_scr));
+
+#if LV_INDEV_READ_PERIOD != 0
+ /*Init the input device handling*/
+ lv_indev_init();
+#endif
+
+ _lv_initialized = true;
+ LV_LOG_INFO("lv_init ready");
+}
+
+/*--------------------
+ * Create and delete
+ *-------------------*/
+
+/**
+ * Create a basic object
+ * @param parent pointer to a parent object.
+ * If NULL then a screen will be created
+ * @param copy pointer to a base object, if not NULL then the new object will be copied from it
+ * @return pointer to the new object
+ */
+lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
+{
+
+ lv_obj_t * new_obj = NULL;
+ /*Create a screen if the parent is NULL*/
+ if(parent == NULL) {
+ LV_LOG_TRACE("Screen create started");
+
+ new_obj = lv_ll_ins_head(&LV_GC_ROOT(_lv_scr_ll));
+ lv_mem_assert(new_obj);
+ if(new_obj == NULL) return NULL;
+
+ new_obj->par = NULL; /*Screens has no a parent*/
+ lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t));
+
+ /*Set coordinates to full screen size*/
+ new_obj->coords.x1 = 0;
+ new_obj->coords.y1 = 0;
+ new_obj->coords.x2 = LV_HOR_RES - 1;
+ new_obj->coords.y2 = LV_VER_RES - 1;
+ new_obj->ext_size = 0;
+
+ /*Init realign*/
+#if LV_OBJ_REALIGN
+ new_obj->realign.align = LV_ALIGN_CENTER;
+ new_obj->realign.xofs = 0;
+ new_obj->realign.yofs = 0;
+ new_obj->realign.base = NULL;
+ new_obj->realign.auto_realign = 0;
+#endif
+
+ /*Set the default styles*/
+ lv_theme_t * th = lv_theme_get_current();
+ if(th) {
+ new_obj->style_p = th->bg;
+ } else {
+ new_obj->style_p = &lv_style_scr;
+ }
+ /*Set virtual functions*/
+ lv_obj_set_signal_func(new_obj, lv_obj_signal);
+ lv_obj_set_design_func(new_obj, lv_obj_design);
+
+ /*Set free data*/
+#ifdef LV_OBJ_FREE_NUM_TYPE
+ new_obj->free_num = 0;
+#endif
+
+#if LV_OBJ_FREE_PTR != 0
+ new_obj->free_ptr = NULL;
+#endif
+
+#if USE_LV_GROUP
+ new_obj->group_p = NULL;
+#endif
+ /*Set attributes*/
+ new_obj->click = 0;
+ new_obj->drag = 0;
+ new_obj->drag_throw = 0;
+ new_obj->drag_parent = 0;
+ new_obj->hidden = 0;
+ new_obj->top = 0;
+ new_obj->opa_scale_en = 0;
+ new_obj->protect = LV_PROTECT_NONE;
+ new_obj->opa_scale = LV_OPA_COVER;
+
+ new_obj->ext_attr = NULL;
+
+ LV_LOG_INFO("Screen create ready");
+ }
+ /*parent != NULL create normal obj. on a parent*/
+ else {
+ LV_LOG_TRACE("Object create started");
+
+ new_obj = lv_ll_ins_head(&(parent)->child_ll);
+ lv_mem_assert(new_obj);
+ if(new_obj == NULL) return NULL;
+
+
+ new_obj->par = parent; /*Set the parent*/
+ lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t));
+
+ /*Set coordinates left top corner of parent*/
+ new_obj->coords.x1 = parent->coords.x1;
+ new_obj->coords.y1 = parent->coords.y1;
+ new_obj->coords.x2 = parent->coords.x1 +
+ LV_OBJ_DEF_WIDTH;
+ new_obj->coords.y2 = parent->coords.y1 +
+ LV_OBJ_DEF_HEIGHT;
+ new_obj->ext_size = 0;
+
+ /*Init realign*/
+#if LV_OBJ_REALIGN
+ new_obj->realign.align = LV_ALIGN_CENTER;
+ new_obj->realign.xofs = 0;
+ new_obj->realign.yofs = 0;
+ new_obj->realign.base = NULL;
+ new_obj->realign.auto_realign = 0;
+#endif
+ /*Set appearance*/
+ lv_theme_t * th = lv_theme_get_current();
+ if(th) {
+ new_obj->style_p = th->panel;
+ } else {
+ new_obj->style_p = &lv_style_plain_color;
+ }
+
+ /*Set virtual functions*/
+ lv_obj_set_signal_func(new_obj, lv_obj_signal);
+ lv_obj_set_design_func(new_obj, lv_obj_design);
+
+ /*Set free data*/
+#ifdef LV_OBJ_FREE_NUM_TYPE
+ new_obj->free_num = 0;
+#endif
+#if LV_OBJ_FREE_PTR != 0
+ new_obj->free_ptr = NULL;
+#endif
+#if USE_LV_GROUP
+ new_obj->group_p = NULL;
+#endif
+
+ /*Set attributes*/
+ new_obj->click = 1;
+ new_obj->drag = 0;
+ new_obj->drag_throw = 0;
+ new_obj->drag_parent = 0;
+ new_obj->hidden = 0;
+ new_obj->top = 0;
+ new_obj->protect = LV_PROTECT_NONE;
+ new_obj->opa_scale = LV_OPA_COVER;
+ new_obj->opa_scale_en = 0;
+
+ new_obj->ext_attr = NULL;
+ }
+
+ if(copy != NULL) {
+ lv_area_copy(&new_obj->coords, ©->coords);
+ new_obj->ext_size = copy->ext_size;
+
+ /*Set free data*/
+#ifdef LV_OBJ_FREE_NUM_TYPE
+ new_obj->free_num = copy->free_num;
+#endif
+#if LV_OBJ_FREE_PTR != 0
+ new_obj->free_ptr = copy->free_ptr;
+#endif
+
+ /*Copy realign*/
+#if LV_OBJ_REALIGN
+ new_obj->realign.align = copy->realign.align;
+ new_obj->realign.xofs = copy->realign.xofs;
+ new_obj->realign.yofs = copy->realign.yofs;
+ new_obj->realign.base = copy->realign.base;
+ new_obj->realign.auto_realign = copy->realign.auto_realign;
+#endif
+
+ /*Set attributes*/
+ new_obj->click = copy->click;
+ new_obj->drag = copy->drag;
+ new_obj->drag_throw = copy->drag_throw;
+ new_obj->drag_parent = copy->drag_parent;
+ new_obj->hidden = copy->hidden;
+ new_obj->top = copy->top;
+
+ new_obj->opa_scale_en = copy->opa_scale_en;
+ new_obj->protect = copy->protect;
+ new_obj->opa_scale = copy->opa_scale;
+
+ new_obj->style_p = copy->style_p;
+
+#if USE_LV_GROUP
+ /*Add to the same group*/
+ if(copy->group_p != NULL) {
+ lv_group_add_obj(copy->group_p, new_obj);
+ }
+#endif
+
+ /*Set the same coordinates for non screen objects*/
+ if(lv_obj_get_parent(copy) != NULL && parent != NULL) {
+ lv_obj_set_pos(new_obj, lv_obj_get_x(copy), lv_obj_get_y(copy));
+ } else {
+ lv_obj_set_pos(new_obj, 0, 0);
+ }
+
+ LV_LOG_INFO("Object create ready");
+ }
+
+
+ /*Send a signal to the parent to notify it about the new child*/
+ if(parent != NULL) {
+ parent->signal_func(parent, LV_SIGNAL_CHILD_CHG, new_obj);
+
+ /*Invalidate the area if not screen created*/
+ lv_obj_invalidate(new_obj);
+ }
+
+ return new_obj;
+}
+
+/**
+ * Delete 'obj' and all of its children
+ * @param obj pointer to an object to delete
+ * @return LV_RES_INV because the object is deleted
+ */
+lv_res_t lv_obj_del(lv_obj_t * obj)
+{
+ lv_obj_invalidate(obj);
+
+ /*Delete from the group*/
+#if USE_LV_GROUP
+ if(obj->group_p != NULL) lv_group_remove_obj(obj);
+#endif
+
+ /*Remove the animations from this object*/
+#if USE_LV_ANIMATION
+ lv_anim_del(obj, NULL);
+#endif
+
+ /*Recursively delete the children*/
+ lv_obj_t * i;
+ lv_obj_t * i_next;
+ i = lv_ll_get_head(&(obj->child_ll));
+ while(i != NULL) {
+ /*Get the next object before delete this*/
+ i_next = lv_ll_get_next(&(obj->child_ll), i);
+
+ /*Call the recursive del to the child too*/
+ delete_children(i);
+
+ /*Set i to the next node*/
+ i = i_next;
+ }
+
+ /*Remove the object from parent's children list*/
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ if(par == NULL) { /*It is a screen*/
+ lv_ll_rem(&LV_GC_ROOT(_lv_scr_ll), obj);
+ } else {
+ lv_ll_rem(&(par->child_ll), obj);
+ }
+
+ /* Reset all input devices if
+ * the currently pressed object is deleted*/
+ lv_indev_t * indev = lv_indev_next(NULL);
+ while(indev) {
+ if(indev->proc.act_obj == obj || indev->proc.last_obj == obj) {
+ lv_indev_reset(indev);
+ }
+ indev = lv_indev_next(indev);
+ }
+
+ /* All children deleted.
+ * Now clean up the object specific data*/
+ obj->signal_func(obj, LV_SIGNAL_CLEANUP, NULL);
+
+ /*Delete the base objects*/
+ if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr);
+ lv_mem_free(obj); /*Free the object itself*/
+
+ /*Send a signal to the parent to notify it about the child delete*/
+ if(par != NULL) {
+ par->signal_func(par, LV_SIGNAL_CHILD_CHG, NULL);
+ }
+
+ return LV_RES_INV;
+}
+
+/**
+ * Delete all children of an object
+ * @param obj pointer to an object
+ */
+void lv_obj_clean(lv_obj_t * obj)
+{
+ lv_obj_t * child = lv_obj_get_child(obj, NULL);
+ lv_obj_t * child_next;
+ while(child) {
+ /* Read the next child before deleting the current
+ * because the next couldn't be read from a deleted (invalid) node*/
+ child_next = lv_obj_get_child(obj, child);
+ lv_obj_del(child);
+ child = child_next;
+ }
+}
+
+/**
+ * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
+ * @param obj pointer to an object
+ */
+void lv_obj_invalidate(const lv_obj_t * obj)
+{
+ if(lv_obj_get_hidden(obj)) return;
+
+ /*Invalidate the object only if it belongs to the 'LV_GC_ROOT(_lv_act_scr)'*/
+ lv_obj_t * obj_scr = lv_obj_get_screen(obj);
+ if(obj_scr == lv_scr_act() ||
+ obj_scr == lv_layer_top() ||
+ obj_scr == lv_layer_sys()) {
+ /*Truncate recursively to the parents*/
+ lv_area_t area_trunc;
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ bool union_ok = true;
+ /*Start with the original coordinates*/
+ lv_coord_t ext_size = obj->ext_size;
+ lv_area_copy(&area_trunc, &obj->coords);
+ area_trunc.x1 -= ext_size;
+ area_trunc.y1 -= ext_size;
+ area_trunc.x2 += ext_size;
+ area_trunc.y2 += ext_size;
+
+ /*Check through all parents*/
+ while(par != NULL) {
+ union_ok = lv_area_intersect(&area_trunc, &area_trunc, &par->coords);
+ if(union_ok == false) break; /*If no common parts with parent break;*/
+ if(lv_obj_get_hidden(par)) return; /*If the parent is hidden then the child is hidden and won't be drawn*/
+
+ par = lv_obj_get_parent(par);
+ }
+
+ if(union_ok != false) lv_inv_area(&area_trunc);
+ }
+}
+
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*--------------
+ * Screen set
+ *--------------*/
+
+/**
+ * Load a new screen
+ * @param scr pointer to a screen
+ */
+void lv_scr_load(lv_obj_t * scr)
+{
+ LV_GC_ROOT(_lv_act_scr) = scr;
+
+ lv_obj_invalidate(LV_GC_ROOT(_lv_act_scr));
+}
+
+/*--------------------
+ * Parent/children set
+ *--------------------*/
+
+/**
+ * Set a new parent for an object. Its relative position will be the same.
+ * @param obj pointer to an object. Can't be a screen.
+ * @param parent pointer to the new parent object. (Can't be NULL)
+ */
+void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
+{
+ if(obj->par == NULL) {
+ LV_LOG_WARN("Can't set the parent of a screen");
+ return;
+ }
+
+ if(parent == NULL) {
+ LV_LOG_WARN("Can't set parent == NULL to an object");
+ return;
+ }
+
+
+ lv_obj_invalidate(obj);
+
+ lv_point_t old_pos;
+ old_pos.x = lv_obj_get_x(obj);
+ old_pos.y = lv_obj_get_y(obj);
+
+ lv_obj_t * old_par = obj->par;
+
+ lv_ll_chg_list(&obj->par->child_ll, &parent->child_ll, obj);
+ obj->par = parent;
+ lv_obj_set_pos(obj, old_pos.x, old_pos.y);
+
+ /*Notify the original parent because one of its children is lost*/
+ old_par->signal_func(old_par, LV_SIGNAL_CHILD_CHG, NULL);
+
+ /*Notify the new parent about the child*/
+ parent->signal_func(parent, LV_SIGNAL_CHILD_CHG, obj);
+
+ lv_obj_invalidate(obj);
+}
+
+/*--------------------
+ * Coordinate set
+ * ------------------*/
+
+/**
+ * Set relative the position of an object (relative to the parent)
+ * @param obj pointer to an object
+ * @param x new distance from the left side of the parent
+ * @param y new distance from the top of the parent
+ */
+void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
+{
+ /*Convert x and y to absolute coordinates*/
+ lv_obj_t * par = obj->par;
+ x = x + par->coords.x1;
+ y = y + par->coords.y1;
+
+ /*Calculate and set the movement*/
+ lv_point_t diff;
+ diff.x = x - obj->coords.x1;
+ diff.y = y - obj->coords.y1;
+
+ /* Do nothing if the position is not changed */
+ /* It is very important else recursive positioning can
+ * occur without position change*/
+ if(diff.x == 0 && diff.y == 0) return;
+
+ /*Invalidate the original area*/
+ lv_obj_invalidate(obj);
+
+ /*Save the original coordinates*/
+ lv_area_t ori;
+ lv_obj_get_coords(obj, &ori);
+
+ obj->coords.x1 += diff.x;
+ obj->coords.y1 += diff.y;
+ obj->coords.x2 += diff.x;
+ obj->coords.y2 += diff.y;
+
+ refresh_children_position(obj, diff.x, diff.y);
+
+ /*Inform the object about its new coordinates*/
+ obj->signal_func(obj, LV_SIGNAL_CORD_CHG, &ori);
+
+ /*Send a signal to the parent too*/
+ par->signal_func(par, LV_SIGNAL_CHILD_CHG, obj);
+
+ /*Invalidate the new area*/
+ lv_obj_invalidate(obj);
+}
+
+
+/**
+ * Set the x coordinate of a object
+ * @param obj pointer to an object
+ * @param x new distance from the left side from the parent
+ */
+void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
+{
+ lv_obj_set_pos(obj, x, lv_obj_get_y(obj));
+}
+
+
+/**
+ * Set the y coordinate of a object
+ * @param obj pointer to an object
+ * @param y new distance from the top of the parent
+ */
+void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
+{
+ lv_obj_set_pos(obj, lv_obj_get_x(obj), y);
+}
+
+/**
+ * Set the size of an object
+ * @param obj pointer to an object
+ * @param w new width
+ * @param h new height
+ */
+void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
+{
+
+ /* Do nothing if the size is not changed */
+ /* It is very important else recursive resizing can
+ * occur without size change*/
+ if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) {
+ return;
+ }
+
+ /*Invalidate the original area*/
+ lv_obj_invalidate(obj);
+
+ /*Save the original coordinates*/
+ lv_area_t ori;
+ lv_obj_get_coords(obj, &ori);
+
+ //Set the length and height
+ obj->coords.x2 = obj->coords.x1 + w - 1;
+ obj->coords.y2 = obj->coords.y1 + h - 1;
+
+
+ /*Send a signal to the object with its new coordinates*/
+ obj->signal_func(obj, LV_SIGNAL_CORD_CHG, &ori);
+
+ /*Send a signal to the parent too*/
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ if(par != NULL) par->signal_func(par, LV_SIGNAL_CHILD_CHG, obj);
+
+ /*Invalidate the new area*/
+ lv_obj_invalidate(obj);
+
+ /*Automatically realign the object if required*/
+#if LV_OBJ_REALIGN
+ if(obj->realign.auto_realign) lv_obj_realign(obj);
+#endif
+}
+
+/**
+ * Set the width of an object
+ * @param obj pointer to an object
+ * @param w new width
+ */
+void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
+{
+ lv_obj_set_size(obj, w, lv_obj_get_height(obj));
+}
+
+/**
+ * Set the height of an object
+ * @param obj pointer to an object
+ * @param h new height
+ */
+void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
+{
+ lv_obj_set_size(obj, lv_obj_get_width(obj), h);
+}
+
+/**
+ * Align an object to an other object.
+ * @param obj pointer to an object to align
+ * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
+ * @param align type of alignment (see 'lv_align_t' enum)
+ * @param x_mod x coordinate shift after alignment
+ * @param y_mod y coordinate shift after alignment
+ */
+void lv_obj_align(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod)
+{
+ lv_coord_t new_x = lv_obj_get_x(obj);
+ lv_coord_t new_y = lv_obj_get_y(obj);
+
+ if(base == NULL) {
+ base = lv_obj_get_parent(obj);
+ }
+
+ switch(align) {
+ case LV_ALIGN_CENTER:
+ new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_IN_TOP_LEFT:
+ new_x = 0;
+ new_y = 0;
+ break;
+ case LV_ALIGN_IN_TOP_MID:
+ new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ new_y = 0;
+ break;
+
+ case LV_ALIGN_IN_TOP_RIGHT:
+ new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ new_y = 0;
+ break;
+
+ case LV_ALIGN_IN_BOTTOM_LEFT:
+ new_x = 0;
+ new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+ case LV_ALIGN_IN_BOTTOM_MID:
+ new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_IN_BOTTOM_RIGHT:
+ new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_IN_LEFT_MID:
+ new_x = 0;
+ new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_IN_RIGHT_MID:
+ new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_OUT_TOP_LEFT:
+ new_x = 0;
+ new_y = -lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_TOP_MID:
+ new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ new_y = - lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_TOP_RIGHT:
+ new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ new_y = - lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_LEFT:
+ new_x = 0;
+ new_y = lv_obj_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_MID:
+ new_x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ new_y = lv_obj_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_RIGHT:
+ new_x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ new_y = lv_obj_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_LEFT_TOP:
+ new_x = - lv_obj_get_width(obj);
+ new_y = 0;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_MID:
+ new_x = - lv_obj_get_width(obj);
+ new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_BOTTOM:
+ new_x = - lv_obj_get_width(obj);
+ new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_TOP:
+ new_x = lv_obj_get_width(base);
+ new_y = 0;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_MID:
+ new_x = lv_obj_get_width(base);
+ new_y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_BOTTOM:
+ new_x = lv_obj_get_width(base);
+ new_y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+ }
+
+ /*Bring together the coordination system of base and obj*/
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ lv_coord_t base_abs_x = base->coords.x1;
+ lv_coord_t base_abs_y = base->coords.y1;
+ lv_coord_t par_abs_x = par->coords.x1;
+ lv_coord_t par_abs_y = par->coords.y1;
+ new_x += x_mod + base_abs_x;
+ new_y += y_mod + base_abs_y;
+ new_x -= par_abs_x;
+ new_y -= par_abs_y;
+
+ lv_obj_set_pos(obj, new_x, new_y);
+
+#if LV_OBJ_REALIGN
+ /*Save the last align parameters to use them in `lv_obj_realign`*/
+ obj->realign.align = align;
+ obj->realign.xofs = x_mod;
+ obj->realign.yofs = y_mod;
+ obj->realign.base = base;
+ obj->realign.origo_align = 0;
+#endif
+}
+
+/**
+ * Align an object to an other object.
+ * @param obj pointer to an object to align
+ * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
+ * @param align type of alignment (see 'lv_align_t' enum)
+ * @param x_mod x coordinate shift after alignment
+ * @param y_mod y coordinate shift after alignment
+ */
+void lv_obj_align_origo(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod)
+{
+ lv_coord_t new_x = lv_obj_get_x(obj);
+ lv_coord_t new_y = lv_obj_get_y(obj);
+
+ lv_coord_t obj_w_half = lv_obj_get_width(obj) / 2;
+ lv_coord_t obj_h_half = lv_obj_get_height(obj) / 2;
+
+ if(base == NULL) {
+ base = lv_obj_get_parent(obj);
+ }
+
+ switch(align) {
+ case LV_ALIGN_CENTER:
+ new_x = lv_obj_get_width(base) / 2 - obj_w_half;
+ new_y = lv_obj_get_height(base) / 2 - obj_h_half;
+ break;
+
+ case LV_ALIGN_IN_TOP_LEFT:
+ new_x = -obj_w_half;
+ new_y = -obj_h_half;
+ break;
+ case LV_ALIGN_IN_TOP_MID:
+ new_x = lv_obj_get_width(base) / 2 - obj_w_half;
+ new_y = -obj_h_half;
+ break;
+
+ case LV_ALIGN_IN_TOP_RIGHT:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = -obj_h_half;
+ break;
+
+ case LV_ALIGN_IN_BOTTOM_LEFT:
+ new_x = -obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+ case LV_ALIGN_IN_BOTTOM_MID:
+ new_x = lv_obj_get_width(base) / 2 - obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+
+ case LV_ALIGN_IN_BOTTOM_RIGHT:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+
+ case LV_ALIGN_IN_LEFT_MID:
+ new_x = -obj_w_half;
+ new_y = lv_obj_get_height(base) / 2 - obj_h_half;
+ break;
+
+ case LV_ALIGN_IN_RIGHT_MID:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = lv_obj_get_height(base) / 2 - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_TOP_LEFT:
+ new_x = -obj_w_half;
+ new_y = -obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_TOP_MID:
+ new_x = lv_obj_get_width(base) / 2 - obj_w_half;
+ new_y = -obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_TOP_RIGHT:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_LEFT:
+ new_x = -obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_MID:
+ new_x = lv_obj_get_width(base) / 2 - obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_RIGHT:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_TOP:
+ new_x = - obj_w_half ;
+ new_y = - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_MID:
+ new_x = - obj_w_half;
+ new_y = lv_obj_get_height(base) / 2 - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_BOTTOM:
+ new_x = - obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_TOP:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = -obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_MID:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = lv_obj_get_height(base) / 2 - obj_h_half;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_BOTTOM:
+ new_x = lv_obj_get_width(base) - obj_w_half;
+ new_y = lv_obj_get_height(base) - obj_h_half;
+ break;
+ }
+
+ /*Bring together the coordination system of base and obj*/
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ lv_coord_t base_abs_x = base->coords.x1;
+ lv_coord_t base_abs_y = base->coords.y1;
+ lv_coord_t par_abs_x = par->coords.x1;
+ lv_coord_t par_abs_y = par->coords.y1;
+ new_x += x_mod + base_abs_x;
+ new_y += y_mod + base_abs_y;
+ new_x -= par_abs_x;
+ new_y -= par_abs_y;
+
+ lv_obj_set_pos(obj, new_x, new_y);
+
+#if LV_OBJ_REALIGN
+ /*Save the last align parameters to use them in `lv_obj_realign`*/
+ obj->realign.align = align;
+ obj->realign.xofs = x_mod;
+ obj->realign.yofs = y_mod;
+ obj->realign.base = base;
+ obj->realign.origo_align = 1;
+#endif
+}
+
+/**
+ * Realign the object based on the last `lv_obj_align` parameters.
+ * @param obj pointer to an object
+ */
+void lv_obj_realign(lv_obj_t * obj)
+{
+#if LV_OBJ_REALIGN
+ if(obj->realign.origo_align) lv_obj_align_origo(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
+ else lv_obj_align(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
+#else
+ (void) obj;
+ LV_LOG_WARN("lv_obj_realaign: no effect because LV_OBJ_REALIGN = 0");
+#endif
+}
+
+/**
+ * Enable the automatic realign of the object when its size has changed based on the last `lv_obj_align` parameters.
+ * @param obj pointer to an object
+ * @param en true: enable auto realign; false: disable auto realign
+ */
+void lv_obj_set_auto_realign(lv_obj_t * obj, bool en)
+{
+#if LV_OBJ_REALIGN
+ obj->realign.auto_realign = en ? 1 : 0;
+#else
+ (void) obj;
+ (void) en;
+ LV_LOG_WARN("lv_obj_set_auto_realign: no effect because LV_OBJ_REALIGN = 0");
+#endif
+}
+
+/*---------------------
+ * Appearance set
+ *--------------------*/
+
+/**
+ * Set a new style for an object
+ * @param obj pointer to an object
+ * @param style_p pointer to the new style
+ */
+void lv_obj_set_style(lv_obj_t * obj, lv_style_t * style)
+{
+ obj->style_p = style;
+
+ /*Send a signal about style change to every children with NULL style*/
+ refresh_children_style(obj);
+
+ /*Notify the object about the style change too*/
+ lv_obj_refresh_style(obj);
+}
+
+/**
+ * Notify an object about its style is modified
+ * @param obj pointer to an object
+ */
+void lv_obj_refresh_style(lv_obj_t * obj)
+{
+ lv_obj_invalidate(obj);
+ obj->signal_func(obj, LV_SIGNAL_STYLE_CHG, NULL);
+ lv_obj_invalidate(obj);
+
+}
+
+/**
+ * Notify all object if a style is modified
+ * @param style pointer to a style. Only the objects with this style will be notified
+ * (NULL to notify all objects)
+ */
+void lv_obj_report_style_mod(lv_style_t * style)
+{
+ lv_obj_t * i;
+ LL_READ(LV_GC_ROOT(_lv_scr_ll), i) {
+ if(i->style_p == style || style == NULL) {
+ lv_obj_refresh_style(i);
+ }
+
+ report_style_mod_core(style, i);
+ }
+}
+
+/*-----------------
+ * Attribute set
+ *----------------*/
+
+/**
+ * Hide an object. It won't be visible and clickable.
+ * @param obj pointer to an object
+ * @param en true: hide the object
+ */
+void lv_obj_set_hidden(lv_obj_t * obj, bool en)
+{
+ if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
+
+ obj->hidden = en == false ? 0 : 1;
+
+ if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
+
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ par->signal_func(par, LV_SIGNAL_CHILD_CHG, obj);
+
+}
+
+/**
+ * Enable or disable the clicking of an object
+ * @param obj pointer to an object
+ * @param en true: make the object clickable
+ */
+void lv_obj_set_click(lv_obj_t * obj, bool en)
+{
+ obj->click = (en == true ? 1 : 0);
+}
+
+/**
+ * Enable to bring this object to the foreground if it
+ * or any of its children is clicked
+ * @param obj pointer to an object
+ * @param en true: enable the auto top feature
+ */
+void lv_obj_set_top(lv_obj_t * obj, bool en)
+{
+ obj->top = (en == true ? 1 : 0);
+}
+
+/**
+ * Enable the dragging of an object
+ * @param obj pointer to an object
+ * @param en true: make the object dragable
+ */
+void lv_obj_set_drag(lv_obj_t * obj, bool en)
+{
+ if(en == true) lv_obj_set_click(obj, true); /*Drag is useless without enabled clicking*/
+ obj->drag = (en == true ? 1 : 0);
+}
+
+/**
+ * Enable the throwing of an object after is is dragged
+ * @param obj pointer to an object
+ * @param en true: enable the drag throw
+ */
+void lv_obj_set_drag_throw(lv_obj_t * obj, bool en)
+{
+ obj->drag_throw = (en == true ? 1 : 0);
+}
+
+/**
+ * Enable to use parent for drag related operations.
+ * If trying to drag the object the parent will be moved instead
+ * @param obj pointer to an object
+ * @param en true: enable the 'drag parent' for the object
+ */
+void lv_obj_set_drag_parent(lv_obj_t * obj, bool en)
+{
+ obj->drag_parent = (en == true ? 1 : 0);
+}
+
+/**
+ * Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`)
+ * @param obj pointer to an object
+ * @param en true: opa scaling is enabled for this object and all children; false: no opa scaling
+ */
+void lv_obj_set_opa_scale_enable(lv_obj_t * obj, bool en)
+{
+ obj->opa_scale_en = en ? 1 : 0;
+}
+
+/**
+ * Set the opa scale of an object
+ * @param obj pointer to an object
+ * @param opa_scale a factor to scale down opacity [0..255]
+ */
+void lv_obj_set_opa_scale(lv_obj_t * obj, lv_opa_t opa_scale)
+{
+ obj->opa_scale = opa_scale;
+ lv_obj_invalidate(obj);
+}
+
+/**
+ * Set a bit or bits in the protect filed
+ * @param obj pointer to an object
+ * @param prot 'OR'-ed values from `lv_protect_t`
+ */
+void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot)
+{
+ obj->protect |= prot;
+}
+
+/**
+ * Clear a bit or bits in the protect filed
+ * @param obj pointer to an object
+ * @param prot 'OR'-ed values from `lv_protect_t`
+ */
+void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot)
+{
+ prot = (~prot) & 0xFF;
+ obj->protect &= prot;
+}
+
+/**
+ * Set the signal function of an object.
+ * Always call the previous signal function in the new.
+ * @param obj pointer to an object
+ * @param fp the new signal function
+ */
+void lv_obj_set_signal_func(lv_obj_t * obj, lv_signal_func_t fp)
+{
+ obj->signal_func = fp;
+}
+
+/**
+ * Set a new design function for an object
+ * @param obj pointer to an object
+ * @param fp the new design function
+ */
+void lv_obj_set_design_func(lv_obj_t * obj, lv_design_func_t fp)
+{
+ obj->design_func = fp;
+}
+
+/*----------------
+ * Other set
+ *--------------*/
+
+/**
+ * Allocate a new ext. data for an object
+ * @param obj pointer to an object
+ * @param ext_size the size of the new ext. data
+ * @return Normal pointer to the allocated ext
+ */
+void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size)
+{
+ obj->ext_attr = lv_mem_realloc(obj->ext_attr, ext_size);
+
+ return (void *)obj->ext_attr;
+}
+
+/**
+ * Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object
+ * @param obj pointer to an object
+ */
+void lv_obj_refresh_ext_size(lv_obj_t * obj)
+{
+ obj->ext_size = 0;
+ obj->signal_func(obj, LV_SIGNAL_REFR_EXT_SIZE, NULL);
+
+ lv_obj_invalidate(obj);
+}
+
+#ifdef LV_OBJ_FREE_NUM_TYPE
+/**
+ * Set an application specific number for an object.
+ * It can help to identify objects in the application.
+ * @param obj pointer to an object
+ * @param free_num the new free number
+ */
+void lv_obj_set_free_num(lv_obj_t * obj, LV_OBJ_FREE_NUM_TYPE free_num)
+{
+ obj->free_num = free_num;
+}
+#endif
+
+#if LV_OBJ_FREE_PTR != 0
+/**
+ * Set an application specific pointer for an object.
+ * It can help to identify objects in the application.
+ * @param obj pointer to an object
+ * @param free_p the new free pinter
+ */
+void lv_obj_set_free_ptr(lv_obj_t * obj, void * free_p)
+{
+ obj->free_ptr = free_p;
+}
+#endif
+
+#if USE_LV_ANIMATION
+/**
+ * Animate an object
+ * @param obj pointer to an object to animate
+ * @param type type of animation from 'lv_anim_builtin_t'. 'OR' it with ANIM_IN or ANIM_OUT
+ * @param time time of animation in milliseconds
+ * @param delay delay before the animation in milliseconds
+ * @param cb a function to call when the animation is ready
+ */
+void lv_obj_animate(lv_obj_t * obj, lv_anim_builtin_t type, uint16_t time, uint16_t delay, void (*cb)(lv_obj_t *))
+{
+ lv_obj_t * par = lv_obj_get_parent(obj);
+
+ /*Get the direction*/
+ bool out = (type & LV_ANIM_DIR_MASK) == LV_ANIM_IN ? false : true;
+ type = type & (~LV_ANIM_DIR_MASK);
+
+ lv_anim_t a;
+ a.var = obj;
+ a.time = time;
+ a.act_time = (int32_t) - delay;
+ a.end_cb = (void(*)(void *))cb;
+ a.path = lv_anim_path_linear;
+ a.playback_pause = 0;
+ a.repeat_pause = 0;
+ a.playback = 0;
+ a.repeat = 0;
+
+ /*Init to ANIM_IN*/
+ switch(type) {
+ case LV_ANIM_FLOAT_LEFT:
+ a.fp = (void(*)(void *, int32_t))lv_obj_set_x;
+ a.start = -lv_obj_get_width(obj);
+ a.end = lv_obj_get_x(obj);
+ break;
+ case LV_ANIM_FLOAT_RIGHT:
+ a.fp = (void(*)(void *, int32_t))lv_obj_set_x;
+ a.start = lv_obj_get_width(par);
+ a.end = lv_obj_get_x(obj);
+ break;
+ case LV_ANIM_FLOAT_TOP:
+ a.fp = (void(*)(void *, int32_t))lv_obj_set_y;
+ a.start = -lv_obj_get_height(obj);
+ a.end = lv_obj_get_y(obj);
+ break;
+ case LV_ANIM_FLOAT_BOTTOM:
+ a.fp = (void(*)(void *, int32_t))lv_obj_set_y;
+ a.start = lv_obj_get_height(par);
+ a.end = lv_obj_get_y(obj);
+ break;
+ case LV_ANIM_GROW_H:
+ a.fp = (void(*)(void *, int32_t))lv_obj_set_width;
+ a.start = 0;
+ a.end = lv_obj_get_width(obj);
+ break;
+ case LV_ANIM_GROW_V:
+ a.fp = (void(*)(void *, int32_t))lv_obj_set_height;
+ a.start = 0;
+ a.end = lv_obj_get_height(obj);
+ break;
+ case LV_ANIM_NONE:
+ a.fp = NULL;
+ a.start = 0;
+ a.end = 0;
+ break;
+ default:
+ break;
+ }
+
+ /*Swap start and end in case of ANIM OUT*/
+ if(out != false) {
+ int32_t tmp = a.start;
+ a.start = a.end;
+ a.end = tmp;
+ }
+
+ lv_anim_create(&a);
+}
+
+#endif
+
+/*=======================
+ * Getter functions
+ *======================*/
+
+/*------------------
+ * Screen get
+ *-----------------*/
+
+/**
+ * Return with a pointer to the active screen
+ * @return pointer to the active screen object (loaded by 'lv_scr_load()')
+ */
+lv_obj_t * lv_scr_act(void)
+{
+ return LV_GC_ROOT(_lv_act_scr);
+}
+
+/**
+ * Return with the top layer. (Same on every screen and it is above the normal screen layer)
+ * @return pointer to the top layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_layer_top(void)
+{
+ return LV_GC_ROOT(_lv_top_layer);
+}
+
+/**
+ * Return with the system layer. (Same on every screen and it is above the all other layers)
+ * It is used for example by the cursor
+ * @return pointer to the system layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_layer_sys(void)
+{
+ return LV_GC_ROOT(_lv_sys_layer);
+}
+
+/**
+ * Return with the screen of an object
+ * @param obj pointer to an object
+ * @return pointer to a screen
+ */
+lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
+{
+ const lv_obj_t * par = obj;
+ const lv_obj_t * act_p;
+
+ do {
+ act_p = par;
+ par = lv_obj_get_parent(act_p);
+ } while(par != NULL);
+
+ return (lv_obj_t *)act_p;
+}
+
+/*---------------------
+ * Parent/children get
+ *--------------------*/
+
+/**
+ * Returns with the parent of an object
+ * @param obj pointer to an object
+ * @return pointer to the parent of 'obj'
+ */
+lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
+{
+ return obj->par;
+}
+
+/**
+ * Iterate through the children of an object (start from the "youngest")
+ * @param obj pointer to an object
+ * @param child NULL at first call to get the next children
+ * and the previous return value later
+ * @return the child after 'act_child' or NULL if no more child
+ */
+lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child)
+{
+ lv_obj_t * result = NULL;
+
+ if(child == NULL) {
+ result = lv_ll_get_head(&obj->child_ll);
+ } else {
+ result = lv_ll_get_next(&obj->child_ll, child);
+ }
+
+ return result;
+}
+
+/**
+ * Iterate through the children of an object (start from the "oldest")
+ * @param obj pointer to an object
+ * @param child NULL at first call to get the next children
+ * and the previous return value later
+ * @return the child after 'act_child' or NULL if no more child
+ */
+lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child)
+{
+ lv_obj_t * result = NULL;
+
+ if(child == NULL) {
+ result = lv_ll_get_tail(&obj->child_ll);
+ } else {
+ result = lv_ll_get_prev(&obj->child_ll, child);
+ }
+
+ return result;
+}
+
+/**
+ * Count the children of an object (only children directly on 'obj')
+ * @param obj pointer to an object
+ * @return children number of 'obj'
+ */
+uint16_t lv_obj_count_children(const lv_obj_t * obj)
+{
+ lv_obj_t * i;
+ uint16_t cnt = 0;
+
+ LL_READ(obj->child_ll, i) cnt++;
+
+ return cnt;
+}
+
+/*---------------------
+ * Coordinate get
+ *--------------------*/
+
+/**
+ * Copy the coordinates of an object to an area
+ * @param obj pointer to an object
+ * @param cords_p pointer to an area to store the coordinates
+ */
+void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p)
+{
+ lv_area_copy(cords_p, &obj->coords);
+}
+
+
+/**
+ * Get the x coordinate of object
+ * @param obj pointer to an object
+ * @return distance of 'obj' from the left side of its parent
+ */
+lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
+{
+ lv_coord_t rel_x;
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ rel_x = obj->coords.x1 - parent->coords.x1;
+
+ return rel_x;
+}
+
+/**
+ * Get the y coordinate of object
+ * @param obj pointer to an object
+ * @return distance of 'obj' from the top of its parent
+ */
+lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
+{
+ lv_coord_t rel_y;
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ rel_y = obj->coords.y1 - parent->coords.y1;
+
+ return rel_y;
+}
+
+/**
+ * Get the width of an object
+ * @param obj pointer to an object
+ * @return the width
+ */
+lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
+{
+ return lv_area_get_width(&obj->coords);
+}
+
+/**
+ * Get the height of an object
+ * @param obj pointer to an object
+ * @return the height
+ */
+lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
+{
+ return lv_area_get_height(&obj->coords);
+}
+
+/**
+ * Get the extended size attribute of an object
+ * @param obj pointer to an object
+ * @return the extended size attribute
+ */
+lv_coord_t lv_obj_get_ext_size(const lv_obj_t * obj)
+{
+ return obj->ext_size;
+}
+
+/**
+ * Get the automatic realign property of the object.
+ * @param obj pointer to an object
+ * @return true: auto realign is enabled; false: auto realign is disabled
+ */
+bool lv_obj_get_auto_realign(lv_obj_t * obj)
+{
+#if LV_OBJ_REALIGN
+ return obj->realign.auto_realign ? true : false;
+#else
+ (void) obj;
+ return false;
+#endif
+}
+
+/*-----------------
+ * Appearance get
+ *---------------*/
+
+/**
+ * Get the style pointer of an object (if NULL get style of the parent)
+ * @param obj pointer to an object
+ * @return pointer to a style
+ */
+lv_style_t * lv_obj_get_style(const lv_obj_t * obj)
+{
+ lv_style_t * style_act = obj->style_p;
+ if(style_act == NULL) {
+ lv_obj_t * par = obj->par;
+
+ while(par) {
+ if(par->style_p) {
+ if(par->style_p->glass == 0) {
+#if USE_LV_GROUP == 0
+ style_act = par->style_p;
+#else
+ /*Is a parent is focused then use then focused style*/
+ lv_group_t * g = lv_obj_get_group(par);
+ if(lv_group_get_focused(g) == par) {
+ style_act = lv_group_mod_style(g, par->style_p);
+ } else {
+ style_act = par->style_p;
+ }
+#endif
+ break;
+ }
+ }
+ par = par->par;
+ }
+ }
+#if USE_LV_GROUP
+ if(obj->group_p) {
+ if(lv_group_get_focused(obj->group_p) == obj) {
+ style_act = lv_group_mod_style(obj->group_p, style_act);
+ }
+ }
+#endif
+
+ if(style_act == NULL) style_act = &lv_style_plain;
+
+ return style_act;
+}
+
+/*-----------------
+ * Attribute get
+ *----------------*/
+
+/**
+ * Get the hidden attribute of an object
+ * @param obj pointer to an object
+ * @return true: the object is hidden
+ */
+bool lv_obj_get_hidden(const lv_obj_t * obj)
+{
+ return obj->hidden == 0 ? false : true;
+}
+
+/**
+ * Get the click enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: the object is clickable
+ */
+bool lv_obj_get_click(const lv_obj_t * obj)
+{
+ return obj->click == 0 ? false : true;
+}
+
+/**
+ * Get the top enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: the auto top feture is enabled
+ */
+bool lv_obj_get_top(const lv_obj_t * obj)
+{
+ return obj->top == 0 ? false : true;
+}
+
+/**
+ * Get the drag enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: the object is dragable
+ */
+bool lv_obj_get_drag(const lv_obj_t * obj)
+{
+ return obj->drag == 0 ? false : true;
+}
+
+/**
+ * Get the drag throw enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: drag throw is enabled
+ */
+bool lv_obj_get_drag_throw(const lv_obj_t * obj)
+{
+ return obj->drag_throw == 0 ? false : true;
+}
+
+/**
+ * Get the drag parent attribute of an object
+ * @param obj pointer to an object
+ * @return true: drag parent is enabled
+ */
+bool lv_obj_get_drag_parent(const lv_obj_t * obj)
+{
+ return obj->drag_parent == 0 ? false : true;
+}
+
+/**
+ * Get the opa scale enable parameter
+ * @param obj pointer to an object
+ * @return true: opa scaling is enabled for this object and all children; false: no opa scaling
+ */
+lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj)
+{
+ return obj->opa_scale_en == 0 ? false : true;
+}
+
+/**
+ * Get the opa scale parameter of an object
+ * @param obj pointer to an object
+ * @return opa scale [0..255]
+ */
+lv_opa_t lv_obj_get_opa_scale(const lv_obj_t * obj)
+{
+ const lv_obj_t * parent = obj;
+
+ while(parent) {
+ if(parent->opa_scale_en) return parent->opa_scale;
+ parent = lv_obj_get_parent(parent);
+ }
+
+ return LV_OPA_COVER;
+}
+
+/**
+ * Get the protect field of an object
+ * @param obj pointer to an object
+ * @return protect field ('OR'ed values of `lv_protect_t`)
+ */
+uint8_t lv_obj_get_protect(const lv_obj_t * obj)
+{
+ return obj->protect ;
+}
+
+/**
+ * Check at least one bit of a given protect bitfield is set
+ * @param obj pointer to an object
+ * @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
+ * @return false: none of the given bits are set, true: at least one bit is set
+ */
+bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot)
+{
+ return (obj->protect & prot) == 0 ? false : true ;
+}
+
+/**
+ * Get the signal function of an object
+ * @param obj pointer to an object
+ * @return the signal function
+ */
+lv_signal_func_t lv_obj_get_signal_func(const lv_obj_t * obj)
+{
+ return obj->signal_func;
+}
+
+/**
+ * Get the design function of an object
+ * @param obj pointer to an object
+ * @return the design function
+ */
+lv_design_func_t lv_obj_get_design_func(const lv_obj_t * obj)
+{
+ return obj->design_func;
+}
+
+/*------------------
+ * Other get
+ *-----------------*/
+
+/**
+ * Get the ext pointer
+ * @param obj pointer to an object
+ * @return the ext pointer but not the dynamic version
+ * Use it as ext->data1, and NOT da(ext)->data1
+ */
+void * lv_obj_get_ext_attr(const lv_obj_t * obj)
+{
+ return obj->ext_attr;
+}
+
+/**
+ * Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
+ * E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
+ * @param obj pointer to an object which type should be get
+ * @param buf pointer to an `lv_obj_type_t` buffer to store the types
+ */
+void lv_obj_get_type(lv_obj_t * obj, lv_obj_type_t * buf)
+{
+ lv_obj_type_t tmp;
+
+ memset(buf, 0, sizeof(lv_obj_type_t));
+ memset(&tmp, 0, sizeof(lv_obj_type_t));
+
+ obj->signal_func(obj, LV_SIGNAL_GET_TYPE, &tmp);
+
+ uint8_t cnt;
+ for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
+ if(tmp.type[cnt] == NULL) break;
+ }
+
+
+ /*Swap the order. The real type comes first*/
+ uint8_t i;
+ for(i = 0; i < cnt; i++) {
+ buf->type[i] = tmp.type[cnt - 1 - i];
+ }
+}
+
+#ifdef LV_OBJ_FREE_NUM_TYPE
+/**
+ * Get the free number
+ * @param obj pointer to an object
+ * @return the free number
+ */
+LV_OBJ_FREE_NUM_TYPE lv_obj_get_free_num(const lv_obj_t * obj)
+{
+ return obj->free_num;
+}
+#endif
+
+#if LV_OBJ_FREE_PTR != 0
+/**
+ * Get the free pointer
+ * @param obj pointer to an object
+ * @return the free pointer
+ */
+void * lv_obj_get_free_ptr(const lv_obj_t * obj)
+{
+ return obj->free_ptr;
+}
+#endif
+
+
+#if USE_LV_GROUP
+/**
+ * Get the group of the object
+ * @param obj pointer to an object
+ * @return the pointer to group of the object
+ */
+void * lv_obj_get_group(const lv_obj_t * obj)
+{
+ return obj->group_p;
+}
+
+/**
+ * Tell whether the ohe object is the focused object of a group or not.
+ * @param obj pointer to an object
+ * @return true: the object is focused, false: the object is not focused or not in a group
+ */
+bool lv_obj_is_focused(const lv_obj_t * obj)
+{
+ if(obj->group_p) {
+ if(lv_group_get_focused(obj->group_p) == obj) return true;
+ }
+
+ return false;
+}
+#endif
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Handle the drawing related tasks of the base objects.
+ * @param obj pointer to an object
+ * @param mask the object will be drawn only in this area
+ * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
+ * (return 'true' if yes)
+ * LV_DESIGN_DRAW: draw the object (always return 'true')
+ * @param return true/false, depends on 'mode'
+ */
+static bool lv_obj_design(lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode)
+{
+ if(mode == LV_DESIGN_COVER_CHK) {
+
+ /* Because of the radius it is not sure the area is covered
+ * Check the areas where there is no radius*/
+ lv_style_t * style = lv_obj_get_style(obj);
+ if(style->body.empty != 0) return false;
+
+ uint16_t r = style->body.radius;
+
+ if(r == LV_RADIUS_CIRCLE) return false;
+
+ lv_area_t area_tmp;
+
+ /*Check horizontally without radius*/
+ lv_obj_get_coords(obj, &area_tmp);
+ area_tmp.x1 += r;
+ area_tmp.x2 -= r;
+ if(lv_area_is_in(mask_p, &area_tmp) == false) return false;
+
+ /*Check vertically without radius*/
+ lv_obj_get_coords(obj, &area_tmp);
+ area_tmp.y1 += r;
+ area_tmp.y2 -= r;
+ if(lv_area_is_in(mask_p, &area_tmp) == false) return false;
+
+ } else if(mode == LV_DESIGN_DRAW_MAIN) {
+ lv_style_t * style = lv_obj_get_style(obj);
+ lv_draw_rect(&obj->coords, mask_p, style, lv_obj_get_opa_scale(obj));
+ }
+
+ return true;
+}
+
+/**
+ * Signal function of the basic object
+ * @param obj pointer to an object
+ * @param sign signal type
+ * @param param parameter for the signal (depends on signal type)
+ * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
+ */
+static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
+{
+ (void)param;
+
+ lv_res_t res = LV_RES_OK;
+
+ lv_style_t * style = lv_obj_get_style(obj);
+
+ lv_indev_t *indev_act = lv_indev_get_act();
+
+ if(sign > _LV_SIGNAL_FEEDBACK_SECTION_START && sign < _LV_SIGNAL_FEEDBACK_SECTION_END) {
+ if(indev_act != NULL && indev_act->feedback != NULL)
+ indev_act->feedback(indev_act, sign);
+ }
+
+ if(sign == LV_SIGNAL_CHILD_CHG) {
+ /*Return 'invalid' if the child change signal is not enabled*/
+ if(lv_obj_is_protected(obj, LV_PROTECT_CHILD_CHG) != false) res = LV_RES_INV;
+ } else if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
+ if(style->body.shadow.width > obj->ext_size) obj->ext_size = style->body.shadow.width;
+ } else if(sign == LV_SIGNAL_STYLE_CHG) {
+ lv_obj_refresh_ext_size(obj);
+ } else if(sign == LV_SIGNAL_GET_TYPE) {
+ lv_obj_type_t * buf = param;
+ buf->type[0] = "lv_obj";
+ }
+
+ return res;
+}
+
+/**
+ * Reposition the children of an object. (Called recursively)
+ * @param obj pointer to an object which children will be repositioned
+ * @param x_diff x coordinate shift
+ * @param y_diff y coordinate shift
+ */
+static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff)
+{
+ lv_obj_t * i;
+ LL_READ(obj->child_ll, i) {
+ i->coords.x1 += x_diff;
+ i->coords.y1 += y_diff;
+ i->coords.x2 += x_diff;
+ i->coords.y2 += y_diff;
+
+ refresh_children_position(i, x_diff, y_diff);
+ }
+}
+
+/**
+ * Refresh the style of all children of an object. (Called recursively)
+ * @param style_p refresh objects only with this style.
+ * @param obj pointer to an object
+ */
+static void report_style_mod_core(void * style_p, lv_obj_t * obj)
+{
+ lv_obj_t * i;
+ LL_READ(obj->child_ll, i) {
+ if(i->style_p == style_p || style_p == NULL) {
+ refresh_children_style(i);
+ lv_obj_refresh_style(i);
+ }
+
+ report_style_mod_core(style_p, i);
+ }
+}
+
+/**
+ * Recursively refresh the style of the children. Go deeper until a not NULL style is found
+ * because the NULL styles are inherited from the parent
+ * @param obj pointer to an object
+ */
+static void refresh_children_style(lv_obj_t * obj)
+{
+ lv_obj_t * child = lv_obj_get_child(obj, NULL);
+ while(child != NULL) {
+ if(child->style_p == NULL) {
+ refresh_children_style(child); /*Check children too*/
+ lv_obj_refresh_style(child); /*Notify the child about the style change*/
+ } else if(child->style_p->glass) {
+ /*Children with 'glass' parent might be effected if their style == NULL*/
+ refresh_children_style(child);
+ }
+ child = lv_obj_get_child(obj, child);
+ }
+}
+
+/**
+ * Called by 'lv_obj_del' to delete the children objects
+ * @param obj pointer to an object (all of its children will be deleted)
+ */
+static void delete_children(lv_obj_t * obj)
+{
+ lv_obj_t * i;
+ lv_obj_t * i_next;
+ i = lv_ll_get_head(&(obj->child_ll));
+
+ /*Remove from the group; remove before transversing children so that
+ * the object still has access to all children during the
+ * LV_SIGNAL_DEFOCUS call*/
+#if USE_LV_GROUP
+ if(obj->group_p != NULL) lv_group_remove_obj(obj);
+#endif
+
+ while(i != NULL) {
+ /*Get the next object before delete this*/
+ i_next = lv_ll_get_next(&(obj->child_ll), i);
+
+ /*Call the recursive del to the child too*/
+ delete_children(i);
+
+ /*Set i to the next node*/
+ i = i_next;
+ }
+
+ /*Remove the animations from this object*/
+#if USE_LV_ANIMATION
+ lv_anim_del(obj, NULL);
+#endif
+
+
+ /* Reset the input devices if
+ * the currently pressed object is deleted*/
+ lv_indev_t * indev = lv_indev_next(NULL);
+ while(indev) {
+ if(indev->proc.act_obj == obj || indev->proc.last_obj == obj) {
+ lv_indev_reset(indev);
+ }
+ indev = lv_indev_next(indev);
+ }
+
+ /*Remove the object from parent's children list*/
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ lv_ll_rem(&(par->child_ll), obj);
+
+ /* Clean up the object specific data*/
+ obj->signal_func(obj, LV_SIGNAL_CLEANUP, NULL);
+
+ /*Delete the base objects*/
+ if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr);
+ lv_mem_free(obj); /*Free the object itself*/
+
+}
diff --git a/bdk/libs/lvgl/lv_core/lv_obj.h b/bdk/libs/lvgl/lv_core/lv_obj.h
new file mode 100644
index 0000000..0ebd916
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_obj.h
@@ -0,0 +1,840 @@
+/**
+ * @file lv_obj.h
+ *
+ */
+
+#ifndef LV_OBJ_H
+#define LV_OBJ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#include
+#include "lv_style.h"
+#include "../lv_misc/lv_area.h"
+#include "../lv_misc/lv_mem.h"
+#include "../lv_misc/lv_ll.h"
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_log.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/*Error check of lv_conf.h*/
+#if LV_HOR_RES == 0 || LV_VER_RES == 0
+#error "LittlevGL: LV_HOR_RES and LV_VER_RES must be greater than 0"
+#endif
+
+#if LV_ANTIALIAS > 1
+#error "LittlevGL: LV_ANTIALIAS can be only 0 or 1"
+#endif
+
+#if LV_VDB_SIZE == 0 && LV_ANTIALIAS != 0
+#error "LittlevGL: If LV_VDB_SIZE == 0 the anti-aliasing must be disabled"
+#endif
+
+#if LV_VDB_SIZE > 0 && LV_VDB_SIZE < LV_HOR_RES
+#error "LittlevGL: Small Virtual Display Buffer (lv_conf.h: LV_VDB_SIZE >= LV_HOR_RES)"
+#endif
+
+#if LV_VDB_SIZE == 0 && USE_LV_REAL_DRAW == 0
+#error "LittlevGL: If LV_VDB_SIZE = 0 Real drawing function are required (lv_conf.h: USE_LV_REAL_DRAW 1)"
+#endif
+
+
+#define LV_ANIM_IN 0x00 /*Animation to show an object. 'OR' it with lv_anim_builtin_t*/
+#define LV_ANIM_OUT 0x80 /*Animation to hide an object. 'OR' it with lv_anim_builtin_t*/
+#define LV_ANIM_DIR_MASK 0x80 /*ANIM_IN/ANIM_OUT mask*/
+
+#define LV_MAX_ANCESTOR_NUM 8
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+
+enum
+{
+ LV_DESIGN_DRAW_MAIN,
+ LV_DESIGN_DRAW_POST,
+ LV_DESIGN_COVER_CHK,
+};
+typedef uint8_t lv_design_mode_t;
+
+typedef bool (* lv_design_func_t) (struct _lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode);
+
+enum
+{
+ LV_RES_INV = 0, /*Typically indicates that the object is deleted (become invalid) in the action function or an operation was failed*/
+ LV_RES_OK, /*The object is valid (no deleted) after the action*/
+};
+typedef uint8_t lv_res_t;
+
+enum
+{
+ /*General signals*/
+ LV_SIGNAL_CLEANUP,
+ LV_SIGNAL_CHILD_CHG,
+ LV_SIGNAL_CORD_CHG,
+ LV_SIGNAL_STYLE_CHG,
+ LV_SIGNAL_REFR_EXT_SIZE,
+ LV_SIGNAL_LANG_CHG,
+ LV_SIGNAL_GET_TYPE,
+
+ _LV_SIGNAL_FEEDBACK_SECTION_START,
+ /*Input device related*/
+ LV_SIGNAL_PRESSED,
+ LV_SIGNAL_PRESSING,
+ LV_SIGNAL_PRESS_LOST,
+ LV_SIGNAL_RELEASED,
+ LV_SIGNAL_LONG_PRESS,
+ LV_SIGNAL_LONG_PRESS_REP,
+ LV_SIGNAL_DRAG_BEGIN,
+ LV_SIGNAL_DRAG_END,
+
+ /*Group related*/
+ LV_SIGNAL_FOCUS,
+ LV_SIGNAL_DEFOCUS,
+ LV_SIGNAL_CONTROLL,
+ _LV_SIGNAL_FEEDBACK_SECTION_END,
+ LV_SIGNAL_GET_EDITABLE,
+};
+typedef uint8_t lv_signal_t;
+
+typedef lv_res_t (* lv_signal_func_t) (struct _lv_obj_t * obj, lv_signal_t sign, void * param);
+
+enum
+{
+ LV_ALIGN_CENTER = 0,
+ LV_ALIGN_IN_TOP_LEFT,
+ LV_ALIGN_IN_TOP_MID,
+ LV_ALIGN_IN_TOP_RIGHT,
+ LV_ALIGN_IN_BOTTOM_LEFT,
+ LV_ALIGN_IN_BOTTOM_MID,
+ LV_ALIGN_IN_BOTTOM_RIGHT,
+ LV_ALIGN_IN_LEFT_MID,
+ LV_ALIGN_IN_RIGHT_MID,
+ LV_ALIGN_OUT_TOP_LEFT,
+ LV_ALIGN_OUT_TOP_MID,
+ LV_ALIGN_OUT_TOP_RIGHT,
+ LV_ALIGN_OUT_BOTTOM_LEFT,
+ LV_ALIGN_OUT_BOTTOM_MID,
+ LV_ALIGN_OUT_BOTTOM_RIGHT,
+ LV_ALIGN_OUT_LEFT_TOP,
+ LV_ALIGN_OUT_LEFT_MID,
+ LV_ALIGN_OUT_LEFT_BOTTOM,
+ LV_ALIGN_OUT_RIGHT_TOP,
+ LV_ALIGN_OUT_RIGHT_MID,
+ LV_ALIGN_OUT_RIGHT_BOTTOM,
+};
+typedef uint8_t lv_align_t;
+
+#if LV_OBJ_REALIGN
+typedef struct {
+ const struct _lv_obj_t * base;
+ lv_coord_t xofs;
+ lv_coord_t yofs;
+ lv_align_t align;
+ uint8_t auto_realign :1;
+ uint8_t origo_align :1; /*1: the oigo (center of the object) was aligned with `lv_obj_align_origo`*/
+}lv_reailgn_t;
+#endif
+
+
+typedef struct _lv_obj_t
+{
+ struct _lv_obj_t * par; /*Pointer to the parent object*/
+ lv_ll_t child_ll; /*Linked list to store the children objects*/
+
+ lv_area_t coords; /*Coordinates of the object (x1, y1, x2, y2)*/
+
+ lv_signal_func_t signal_func; /*Object type specific signal function*/
+ lv_design_func_t design_func; /*Object type specific design function*/
+
+ void * ext_attr; /*Object type specific extended data*/
+ lv_style_t * style_p; /*Pointer to the object's style*/
+
+#if LV_OBJ_FREE_PTR != 0
+ void * free_ptr; /*Application specific pointer (set it freely)*/
+#endif
+
+#if USE_LV_GROUP != 0
+ void * group_p; /*Pointer to the group of the object*/
+#endif
+ /*Attributes and states*/
+ uint8_t click :1; /*1: Can be pressed by an input device*/
+ uint8_t drag :1; /*1: Enable the dragging*/
+ uint8_t drag_throw :1; /*1: Enable throwing with drag*/
+ uint8_t drag_parent :1; /*1: Parent will be dragged instead*/
+ uint8_t hidden :1; /*1: Object is hidden*/
+ uint8_t top :1; /*1: If the object or its children is clicked it goes to the foreground*/
+ uint8_t opa_scale_en :1; /*1: opa_scale is set*/
+ uint8_t protect; /*Automatically happening actions can be prevented. 'OR'ed values from `lv_protect_t`*/
+ lv_opa_t opa_scale; /*Scale down the opacity by this factor. Effects all children as well*/
+
+ lv_coord_t ext_size; /*EXTtend the size of the object in every direction. E.g. for shadow drawing*/
+#if LV_OBJ_REALIGN
+ lv_reailgn_t realign;
+#endif
+
+#ifdef LV_OBJ_FREE_NUM_TYPE
+ LV_OBJ_FREE_NUM_TYPE free_num; /*Application specific identifier (set it freely)*/
+#endif
+} lv_obj_t;
+
+typedef lv_res_t (*lv_action_t) (struct _lv_obj_t * obj);
+
+/*Protect some attributes (max. 8 bit)*/
+enum
+{
+ LV_PROTECT_NONE = 0x00,
+ LV_PROTECT_CHILD_CHG = 0x01, /*Disable the child change signal. Used by the library*/
+ LV_PROTECT_PARENT = 0x02, /*Prevent automatic parent change (e.g. in lv_page)*/
+ LV_PROTECT_POS = 0x04, /*Prevent automatic positioning (e.g. in lv_cont layout)*/
+ LV_PROTECT_FOLLOW = 0x08, /*Prevent the object be followed in automatic ordering (e.g. in lv_cont PRETTY layout)*/
+ LV_PROTECT_PRESS_LOST= 0x10, /*If the `indev` was pressing this object but swiped out while pressing do not search other object.*/
+ LV_PROTECT_CLICK_FOCUS= 0x20,/*Prevent focusing the object by clicking on it*/
+};
+typedef uint8_t lv_protect_t;
+
+
+/*Used by `lv_obj_get_type()`. The object's and its ancestor types are stored here*/
+typedef struct {
+ const char * type[LV_MAX_ANCESTOR_NUM]; /*[0]: the actual type, [1]: ancestor, [2] #1's ancestor ... [x]: "lv_obj" */
+} lv_obj_type_t;
+
+enum
+{
+ LV_ANIM_NONE = 0,
+ LV_ANIM_FLOAT_TOP, /*Float from/to the top*/
+ LV_ANIM_FLOAT_LEFT, /*Float from/to the left*/
+ LV_ANIM_FLOAT_BOTTOM, /*Float from/to the bottom*/
+ LV_ANIM_FLOAT_RIGHT, /*Float from/to the right*/
+ LV_ANIM_GROW_H, /*Grow/shrink horizontally*/
+ LV_ANIM_GROW_V, /*Grow/shrink vertically*/
+};
+typedef uint8_t lv_anim_builtin_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Init. the 'lv' library.
+ */
+void lv_init(void);
+
+/*--------------------
+ * Create and delete
+ *-------------------*/
+
+/**
+ * Create a basic object
+ * @param parent pointer to a parent object.
+ * If NULL then a screen will be created
+ * @param copy pointer to a base object, if not NULL then the new object will be copied from it
+ * @return pointer to the new object
+ */
+lv_obj_t * lv_obj_create(lv_obj_t * parent,const lv_obj_t * copy);
+
+/**
+ * Delete 'obj' and all of its children
+ * @param obj pointer to an object to delete
+ * @return LV_RES_INV because the object is deleted
+ */
+lv_res_t lv_obj_del(lv_obj_t * obj);
+
+/**
+ * Delete all children of an object
+ * @param obj pointer to an object
+ */
+void lv_obj_clean(lv_obj_t *obj);
+
+/**
+ * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
+ * @param obj pointer to an object
+ */
+void lv_obj_invalidate(const lv_obj_t * obj);
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*--------------
+ * Screen set
+ *--------------*/
+
+/**
+ * Load a new screen
+ * @param scr pointer to a screen
+ */
+void lv_scr_load(lv_obj_t * scr);
+
+/*--------------------
+ * Parent/children set
+ *--------------------*/
+
+/**
+ * Set a new parent for an object. Its relative position will be the same.
+ * @param obj pointer to an object. Can't be a screen.
+ * @param parent pointer to the new parent object. (Can't be NULL)
+ */
+void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);
+
+/*--------------------
+ * Coordinate set
+ * ------------------*/
+
+/**
+ * Set relative the position of an object (relative to the parent)
+ * @param obj pointer to an object
+ * @param x new distance from the left side of the parent
+ * @param y new distance from the top of the parent
+ */
+void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
+
+/**
+ * Set the x coordinate of a object
+ * @param obj pointer to an object
+ * @param x new distance from the left side from the parent
+ */
+void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x);
+
+/**
+ * Set the y coordinate of a object
+ * @param obj pointer to an object
+ * @param y new distance from the top of the parent
+ */
+void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y);
+
+/**
+ * Set the size of an object
+ * @param obj pointer to an object
+ * @param w new width
+ * @param h new height
+ */
+void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
+
+/**
+ * Set the width of an object
+ * @param obj pointer to an object
+ * @param w new width
+ */
+void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w);
+
+/**
+ * Set the height of an object
+ * @param obj pointer to an object
+ * @param h new height
+ */
+void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h);
+
+/**
+ * Align an object to an other object.
+ * @param obj pointer to an object to align
+ * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
+ * @param align type of alignment (see 'lv_align_t' enum)
+ * @param x_mod x coordinate shift after alignment
+ * @param y_mod y coordinate shift after alignment
+ */
+void lv_obj_align(lv_obj_t * obj,const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod);
+
+/**
+ * Align an object to an other object.
+ * @param obj pointer to an object to align
+ * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
+ * @param align type of alignment (see 'lv_align_t' enum)
+ * @param x_mod x coordinate shift after alignment
+ * @param y_mod y coordinate shift after alignment
+ */
+void lv_obj_align_origo(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod);
+
+/**
+ * Realign the object based on the last `lv_obj_align` parameters.
+ * @param obj pointer to an object
+ */
+void lv_obj_realign(lv_obj_t * obj);
+
+/**
+ * Enable the automatic realign of the object when its size has changed based on the last `lv_obj_align` parameters.
+ * @param obj pointer to an object
+ * @param en true: enable auto realign; false: disable auto realign
+ */
+void lv_obj_set_auto_realign(lv_obj_t * obj, bool en);
+
+/*---------------------
+ * Appearance set
+ *--------------------*/
+
+/**
+ * Set a new style for an object
+ * @param obj pointer to an object
+ * @param style_p pointer to the new style
+ */
+void lv_obj_set_style(lv_obj_t * obj, lv_style_t * style);
+
+/**
+ * Notify an object about its style is modified
+ * @param obj pointer to an object
+ */
+void lv_obj_refresh_style(lv_obj_t * obj);
+
+/**
+ * Notify all object if a style is modified
+ * @param style pointer to a style. Only the objects with this style will be notified
+ * (NULL to notify all objects)
+ */
+void lv_obj_report_style_mod(lv_style_t * style);
+
+/*-----------------
+ * Attribute set
+ *----------------*/
+
+/**
+ * Hide an object. It won't be visible and clickable.
+ * @param obj pointer to an object
+ * @param en true: hide the object
+ */
+void lv_obj_set_hidden(lv_obj_t * obj, bool en);
+
+/**
+ * Enable or disable the clicking of an object
+ * @param obj pointer to an object
+ * @param en true: make the object clickable
+ */
+void lv_obj_set_click(lv_obj_t * obj, bool en);
+
+/**
+ * Enable to bring this object to the foreground if it
+ * or any of its children is clicked
+ * @param obj pointer to an object
+ * @param en true: enable the auto top feature
+ */
+void lv_obj_set_top(lv_obj_t * obj, bool en);
+
+/**
+ * Enable the dragging of an object
+ * @param obj pointer to an object
+ * @param en true: make the object dragable
+ */
+void lv_obj_set_drag(lv_obj_t * obj, bool en);
+
+/**
+ * Enable the throwing of an object after is is dragged
+ * @param obj pointer to an object
+ * @param en true: enable the drag throw
+ */
+void lv_obj_set_drag_throw(lv_obj_t * obj, bool en);
+
+/**
+ * Enable to use parent for drag related operations.
+ * If trying to drag the object the parent will be moved instead
+ * @param obj pointer to an object
+ * @param en true: enable the 'drag parent' for the object
+ */
+void lv_obj_set_drag_parent(lv_obj_t * obj, bool en);
+
+/**
+ * Set editable parameter Used by groups and keyboard/encoder control.
+ * Editable object has something inside to choose (the elements of a list)
+ * @param obj pointer to an object
+ * @param en true: enable editing
+ */
+//void lv_obj_set_editable(lv_obj_t * obj, bool en);
+
+/**
+ * Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`)
+ * @param obj pointer to an object
+ * @param en true: opa scaling is enabled for this object and all children; false: no opa scaling
+ */
+void lv_obj_set_opa_scale_enable(lv_obj_t * obj, bool en);
+
+/**
+ * Set the opa scale of an object
+ * @param obj pointer to an object
+ * @param opa_scale a factor to scale down opacity [0..255]
+ */
+void lv_obj_set_opa_scale(lv_obj_t * obj, lv_opa_t opa_scale);
+
+/**
+ * Set a bit or bits in the protect filed
+ * @param obj pointer to an object
+ * @param prot 'OR'-ed values from `lv_protect_t`
+ */
+void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot);
+
+/**
+ * Clear a bit or bits in the protect filed
+ * @param obj pointer to an object
+ * @param prot 'OR'-ed values from `lv_protect_t`
+ */
+void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot);
+
+/**
+ * Set the signal function of an object.
+ * Always call the previous signal function in the new.
+ * @param obj pointer to an object
+ * @param fp the new signal function
+ */
+void lv_obj_set_signal_func(lv_obj_t * obj, lv_signal_func_t fp);
+
+/**
+ * Set a new design function for an object
+ * @param obj pointer to an object
+ * @param fp the new design function
+ */
+void lv_obj_set_design_func(lv_obj_t * obj, lv_design_func_t fp);
+
+/*----------------
+ * Other set
+ *--------------*/
+
+/**
+ * Allocate a new ext. data for an object
+ * @param obj pointer to an object
+ * @param ext_size the size of the new ext. data
+ * @return pointer to the allocated ext
+ */
+void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size);
+
+/**
+ * Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object
+ * @param obj pointer to an object
+ */
+void lv_obj_refresh_ext_size(lv_obj_t * obj);
+
+#ifdef LV_OBJ_FREE_NUM_TYPE
+/**
+ * Set an application specific number for an object.
+ * It can help to identify objects in the application.
+ * @param obj pointer to an object
+ * @param free_num the new free number
+ */
+void lv_obj_set_free_num(lv_obj_t * obj, LV_OBJ_FREE_NUM_TYPE free_num);
+#endif
+
+#if LV_OBJ_FREE_PTR != 0
+/**
+ * Set an application specific pointer for an object.
+ * It can help to identify objects in the application.
+ * @param obj pointer to an object
+ * @param free_p the new free pinter
+ */
+void lv_obj_set_free_ptr(lv_obj_t * obj, void * free_p);
+#endif
+
+#if USE_LV_ANIMATION
+/**
+ * Animate an object
+ * @param obj pointer to an object to animate
+ * @param type type of animation from 'lv_anim_builtin_t'. 'OR' it with ANIM_IN or ANIM_OUT
+ * @param time time of animation in milliseconds
+ * @param delay delay before the animation in milliseconds
+ * @param cb a function to call when the animation is ready
+ */
+void lv_obj_animate(lv_obj_t * obj, lv_anim_builtin_t type, uint16_t time, uint16_t delay, void (*cb) (lv_obj_t *));
+#endif
+
+/*=======================
+ * Getter functions
+ *======================*/
+
+/*------------------
+ * Screen get
+ *-----------------*/
+
+/**
+ * Return with a pointer to the active screen
+ * @return pointer to the active screen object (loaded by 'lv_scr_load()')
+ */
+lv_obj_t * lv_scr_act(void);
+
+/**
+ * Return with the top layer. (Same on every screen and it is above the normal screen layer)
+ * @return pointer to the top layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_layer_top(void);
+
+/**
+ * Return with the system layer. (Same on every screen and it is above the all other layers)
+ * It is used for example by the cursor
+ * @return pointer to the system layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_layer_sys(void);
+
+/**
+ * Return with the screen of an object
+ * @param obj pointer to an object
+ * @return pointer to a screen
+ */
+lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj);
+
+/*---------------------
+ * Parent/children get
+ *--------------------*/
+
+/**
+ * Returns with the parent of an object
+ * @param obj pointer to an object
+ * @return pointer to the parent of 'obj'
+ */
+lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj);
+
+/**
+ * Iterate through the children of an object (start from the "youngest, lastly created")
+ * @param obj pointer to an object
+ * @param child NULL at first call to get the next children
+ * and the previous return value later
+ * @return the child after 'act_child' or NULL if no more child
+ */
+lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child);
+
+/**
+ * Iterate through the children of an object (start from the "oldest", firstly created)
+ * @param obj pointer to an object
+ * @param child NULL at first call to get the next children
+ * and the previous return value later
+ * @return the child after 'act_child' or NULL if no more child
+ */
+lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child);
+
+/**
+ * Count the children of an object (only children directly on 'obj')
+ * @param obj pointer to an object
+ * @return children number of 'obj'
+ */
+uint16_t lv_obj_count_children(const lv_obj_t * obj);
+
+/*---------------------
+ * Coordinate get
+ *--------------------*/
+
+/**
+ * Copy the coordinates of an object to an area
+ * @param obj pointer to an object
+ * @param cords_p pointer to an area to store the coordinates
+ */
+void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p);
+
+/**
+ * Get the x coordinate of object
+ * @param obj pointer to an object
+ * @return distance of 'obj' from the left side of its parent
+ */
+lv_coord_t lv_obj_get_x(const lv_obj_t * obj);
+
+/**
+ * Get the y coordinate of object
+ * @param obj pointer to an object
+ * @return distance of 'obj' from the top of its parent
+ */
+lv_coord_t lv_obj_get_y(const lv_obj_t * obj);
+
+/**
+ * Get the width of an object
+ * @param obj pointer to an object
+ * @return the width
+ */
+lv_coord_t lv_obj_get_width(const lv_obj_t * obj);
+
+/**
+ * Get the height of an object
+ * @param obj pointer to an object
+ * @return the height
+ */
+lv_coord_t lv_obj_get_height(const lv_obj_t * obj);
+
+/**
+ * Get the extended size attribute of an object
+ * @param obj pointer to an object
+ * @return the extended size attribute
+ */
+lv_coord_t lv_obj_get_ext_size(const lv_obj_t * obj);
+
+/**
+ * Get the automatic realign property of the object.
+ * @param obj pointer to an object
+ * @return true: auto realign is enabled; false: auto realign is disabled
+ */
+bool lv_obj_get_auto_realign(lv_obj_t * obj);
+
+/*-----------------
+ * Appearance get
+ *---------------*/
+
+/**
+ * Get the style pointer of an object (if NULL get style of the parent)
+ * @param obj pointer to an object
+ * @return pointer to a style
+ */
+lv_style_t * lv_obj_get_style(const lv_obj_t * obj);
+
+/*-----------------
+ * Attribute get
+ *----------------*/
+
+/**
+ * Get the hidden attribute of an object
+ * @param obj pointer to an object
+ * @return true: the object is hidden
+ */
+bool lv_obj_get_hidden(const lv_obj_t * obj);
+
+/**
+ * Get the click enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: the object is clickable
+ */
+bool lv_obj_get_click(const lv_obj_t * obj);
+
+/**
+ * Get the top enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: the auto top feature is enabled
+ */
+bool lv_obj_get_top(const lv_obj_t * obj);
+
+/**
+ * Get the drag enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: the object is dragable
+ */
+bool lv_obj_get_drag(const lv_obj_t * obj);
+
+/**
+ * Get the drag throw enable attribute of an object
+ * @param obj pointer to an object
+ * @return true: drag throw is enabled
+ */
+bool lv_obj_get_drag_throw(const lv_obj_t * obj);
+
+/**
+ * Get the drag parent attribute of an object
+ * @param obj pointer to an object
+ * @return true: drag parent is enabled
+ */
+bool lv_obj_get_drag_parent(const lv_obj_t * obj);
+
+
+/**
+ * Get the opa scale enable parameter
+ * @param obj pointer to an object
+ * @return true: opa scaling is enabled for this object and all children; false: no opa scaling
+ */
+lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj);
+
+/**
+ * Get the opa scale parameter of an object
+ * @param obj pointer to an object
+ * @return opa scale [0..255]
+ */
+lv_opa_t lv_obj_get_opa_scale(const lv_obj_t * obj);
+
+/**
+ * Get the protect field of an object
+ * @param obj pointer to an object
+ * @return protect field ('OR'ed values of `lv_protect_t`)
+ */
+uint8_t lv_obj_get_protect(const lv_obj_t * obj);
+
+/**
+ * Check at least one bit of a given protect bitfield is set
+ * @param obj pointer to an object
+ * @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
+ * @return false: none of the given bits are set, true: at least one bit is set
+ */
+bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot);
+
+/**
+ * Get the signal function of an object
+ * @param obj pointer to an object
+ * @return the signal function
+ */
+lv_signal_func_t lv_obj_get_signal_func(const lv_obj_t * obj);
+
+/**
+ * Get the design function of an object
+ * @param obj pointer to an object
+ * @return the design function
+ */
+lv_design_func_t lv_obj_get_design_func(const lv_obj_t * obj);
+
+/*------------------
+ * Other get
+ *-----------------*/
+
+/**
+ * Get the ext pointer
+ * @param obj pointer to an object
+ * @return the ext pointer but not the dynamic version
+ * Use it as ext->data1, and NOT da(ext)->data1
+ */
+void * lv_obj_get_ext_attr(const lv_obj_t * obj);
+
+/**
+ * Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
+ * E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
+ * @param obj pointer to an object which type should be get
+ * @param buf pointer to an `lv_obj_type_t` buffer to store the types
+ */
+void lv_obj_get_type(lv_obj_t * obj, lv_obj_type_t * buf);
+
+#ifdef LV_OBJ_FREE_NUM_TYPE
+/**
+ * Get the free number
+ * @param obj pointer to an object
+ * @return the free number
+ */
+LV_OBJ_FREE_NUM_TYPE lv_obj_get_free_num(const lv_obj_t * obj);
+#endif
+
+#if LV_OBJ_FREE_PTR != 0
+/**
+ * Get the free pointer
+ * @param obj pointer to an object
+ * @return the free pointer
+ */
+void * lv_obj_get_free_ptr(const lv_obj_t * obj);
+#endif
+
+#if USE_LV_GROUP
+/**
+ * Get the group of the object
+ * @param obj pointer to an object
+ * @return the pointer to group of the object
+ */
+void * lv_obj_get_group(const lv_obj_t * obj);
+
+
+/**
+ * Tell whether the object is the focused object of a group or not.
+ * @param obj pointer to an object
+ * @return true: the object is focused, false: the object is not focused or not in a group
+ */
+bool lv_obj_is_focused(const lv_obj_t * obj);
+
+#endif
+
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_OBJ_H*/
diff --git a/bdk/libs/lvgl/lv_core/lv_refr.c b/bdk/libs/lvgl/lv_core/lv_refr.c
new file mode 100644
index 0000000..1087304
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_refr.c
@@ -0,0 +1,614 @@
+/**
+ * @file lv_refr.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include
+#include "lv_refr.h"
+#include "lv_vdb.h"
+#include "../lv_hal/lv_hal_tick.h"
+#include "../lv_hal/lv_hal_disp.h"
+#include "../lv_misc/lv_task.h"
+#include "../lv_misc/lv_mem.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#ifndef LV_INV_FIFO_SIZE
+#define LV_INV_FIFO_SIZE 32 /*The average count of objects on a screen */
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct {
+ lv_area_t area;
+ uint8_t joined;
+} lv_join_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_refr_task(void * param);
+static void lv_refr_join_area(void);
+static void lv_refr_areas(void);
+#if LV_VDB_SIZE == 0
+static void lv_refr_area_no_vdb(const lv_area_t * area_p);
+#else
+static void lv_refr_area_with_vdb(const lv_area_t * area_p);
+static void lv_refr_area_part_vdb(const lv_area_t * area_p);
+#endif
+static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
+static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p);
+static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_join_t inv_buf[LV_INV_FIFO_SIZE];
+static uint16_t inv_buf_p;
+static void (*monitor_cb)(uint32_t, uint32_t); /*Monitor the rendering time*/
+static void (*round_cb)(lv_area_t *); /*If set then called to modify invalidated areas for special display controllers*/
+static uint32_t px_num;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the screen refresh subsystem
+ */
+void lv_refr_init(void)
+{
+ inv_buf_p = 0;
+ memset(inv_buf, 0, sizeof(inv_buf));
+
+ lv_task_t * task;
+ task = lv_task_create(lv_refr_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);
+ lv_task_ready(task); /*Be sure the screen will be refreshed immediately on start up*/
+}
+
+/**
+ * Redraw the invalidated areas now.
+ * Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process can
+ * prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process (e.g. progress bar)
+ * this function can be called when the screen should be updated.
+ */
+void lv_refr_now(void)
+{
+ lv_refr_task(NULL);
+}
+
+
+/**
+ * Invalidate an area
+ * @param area_p pointer to area which should be invalidated
+ */
+void lv_inv_area(const lv_area_t * area_p)
+{
+ /*Clear the invalidate buffer if the parameter is NULL*/
+ if(area_p == NULL) {
+ inv_buf_p = 0;
+ return;
+ }
+
+ lv_area_t scr_area;
+ scr_area.x1 = 0;
+ scr_area.y1 = 0;
+ scr_area.x2 = LV_HOR_RES - 1;
+ scr_area.y2 = LV_VER_RES - 1;
+
+ lv_area_t com_area;
+ bool suc;
+
+ suc = lv_area_intersect(&com_area, area_p, &scr_area);
+
+ /*The area is truncated to the screen*/
+ if(suc != false) {
+ if(round_cb) round_cb(&com_area);
+
+ /*Save only if this area is not in one of the saved areas*/
+ uint16_t i;
+ for(i = 0; i < inv_buf_p; i++) {
+ if(lv_area_is_in(&com_area, &inv_buf[i].area) != false) return;
+ }
+
+ /*Save the area*/
+ if(inv_buf_p < LV_INV_FIFO_SIZE) {
+ lv_area_copy(&inv_buf[inv_buf_p].area, &com_area);
+ } else {/*If no place for the area add the screen*/
+ inv_buf_p = 0;
+ lv_area_copy(&inv_buf[inv_buf_p].area, &scr_area);
+ }
+ inv_buf_p ++;
+ }
+}
+
+/**
+ * Set a function to call after every refresh to announce the refresh time and the number of refreshed pixels
+ * @param cb pointer to a callback function (void my_refr_cb(uint32_t time_ms, uint32_t px_num))
+ * time_ms: refresh time in [ms]
+ * px_num: not the drawn pixels but the number of affected pixels of the screen
+ * (more pixels are drawn because of overlapping objects)
+ */
+void lv_refr_set_monitor_cb(void (*cb)(uint32_t, uint32_t))
+{
+ monitor_cb = cb;
+}
+
+/**
+ * Called when an area is invalidated to modify the coordinates of the area.
+ * Special display controllers may require special coordinate rounding
+ * @param cb pointer to the a function which will modify the area
+ */
+void lv_refr_set_round_cb(void(*cb)(lv_area_t *))
+{
+ round_cb = cb;
+}
+
+/**
+ * Get the number of areas in the buffer
+ * @return number of invalid areas
+ */
+uint16_t lv_refr_get_buf_size(void)
+{
+ return inv_buf_p;
+}
+
+/**
+ * Pop (delete) the last 'num' invalidated areas from the buffer
+ * @param num number of areas to delete
+ */
+void lv_refr_pop_from_buf(uint16_t num)
+{
+ if(inv_buf_p < num) inv_buf_p = 0;
+ else inv_buf_p -= num;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Called periodically to handle the refreshing
+ * @param param unused
+ */
+static void lv_refr_task(void * param)
+{
+ (void)param;
+
+ LV_LOG_TRACE("display refresh task started");
+
+ uint32_t start = lv_tick_get();
+
+ if(lv_disp_get_active() == NULL) {
+ LV_LOG_TRACE("No display is registered");
+ return;
+ }
+
+ lv_refr_join_area();
+
+ lv_refr_areas();
+
+ /*If refresh happened ...*/
+ if(inv_buf_p != 0) {
+
+ /*In true double buffered mode copy the refreshed areas to the new VDB to keep it up to date*/
+#if LV_VDB_TRUE_DOUBLE_BUFFERED
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ vdb_p->area.x1 = 0;
+ vdb_p->area.x2 = LV_HOR_RES-1;
+ vdb_p->area.y1 = 0;
+ vdb_p->area.y2 = LV_VER_RES - 1;
+
+ /*Flush the content of the VDB*/
+ lv_vdb_flush();
+
+ /* With true double buffering the flushing should be only the address change of the current frame buffer
+ * Wait until the address change is ready and copy the active content to the other frame buffer (new active VDB)
+ * The changes will be written to the new VDB.*/
+ lv_vdb_t * vdb_act = lv_vdb_get_active();
+ lv_vdb_t * vdb_ina = lv_vdb_get_inactive();
+
+ uint8_t * buf_act = (uint8_t *) vdb_act->buf;
+ uint8_t * buf_ina = (uint8_t *) vdb_ina->buf;
+
+ uint16_t a;
+ for(a = 0; a < inv_buf_p; a++) {
+ if(inv_buf[a].joined == 0) {
+ lv_coord_t y;
+ uint32_t start_offs = ((LV_HOR_RES * inv_buf[a].area.y1 + inv_buf[a].area.x1) * LV_VDB_PX_BPP) >> 3;
+ uint32_t line_length = (lv_area_get_width(&inv_buf[a].area) * LV_VDB_PX_BPP) >> 3;
+
+ for(y = inv_buf[a].area.y1; y <= inv_buf[a].area.y2; y++) {
+ memcpy(buf_act + start_offs, buf_ina + start_offs, line_length);
+ start_offs += (LV_HOR_RES * LV_VDB_PX_BPP) >> 3;
+ }
+ }
+ }
+
+#endif
+
+ /*Clean up*/
+ memset(inv_buf, 0, sizeof(inv_buf));
+ inv_buf_p = 0;
+
+ /*Call monitor cb if present*/
+ if(monitor_cb != NULL) {
+ monitor_cb(lv_tick_elaps(start), px_num);
+ }
+ }
+
+ LV_LOG_TRACE("display refresh task finished");
+}
+
+
+/**
+ * Join the areas which has got common parts
+ */
+static void lv_refr_join_area(void)
+{
+ uint32_t join_from;
+ uint32_t join_in;
+ lv_area_t joined_area;
+ for(join_in = 0; join_in < inv_buf_p; join_in++) {
+ if(inv_buf[join_in].joined != 0) continue;
+
+ /*Check all areas to join them in 'join_in'*/
+ for(join_from = 0; join_from < inv_buf_p; join_from++) {
+ /*Handle only unjoined areas and ignore itself*/
+ if(inv_buf[join_from].joined != 0 || join_in == join_from) {
+ continue;
+ }
+
+ /*Check if the areas are on each other*/
+ if(lv_area_is_on(&inv_buf[join_in].area,
+ &inv_buf[join_from].area) == false) {
+ continue;
+ }
+
+ lv_area_join(&joined_area, &inv_buf[join_in].area,
+ &inv_buf[join_from].area);
+
+ /*Join two area only if the joined area size is smaller*/
+ if(lv_area_get_size(&joined_area) <
+ (lv_area_get_size(&inv_buf[join_in].area) + lv_area_get_size(&inv_buf[join_from].area))) {
+ lv_area_copy(&inv_buf[join_in].area, &joined_area);
+
+ /*Mark 'join_form' is joined into 'join_in'*/
+ inv_buf[join_from].joined = 1;
+ }
+ }
+ }
+}
+
+/**
+ * Refresh the joined areas
+ */
+static void lv_refr_areas(void)
+{
+ px_num = 0;
+ uint32_t i;
+
+ for(i = 0; i < inv_buf_p; i++) {
+ /*Refresh the unjoined areas*/
+ if(inv_buf[i].joined == 0) {
+ /*If there is no VDB do simple drawing*/
+#if LV_VDB_SIZE == 0
+ lv_refr_area_no_vdb(&inv_buf[i].area);
+#else
+ /*If VDB is used...*/
+ lv_refr_area_with_vdb(&inv_buf[i].area);
+#endif
+ if(monitor_cb != NULL) px_num += lv_area_get_size(&inv_buf[i].area);
+ }
+ }
+
+}
+
+#if LV_VDB_SIZE == 0
+/**
+ * Refresh an area if there is no Virtual Display Buffer
+ * @param area_p pointer to an area to refresh
+ */
+static void lv_refr_area_no_vdb(const lv_area_t * area_p)
+{
+ lv_obj_t * top_p;
+
+ /*Get top object which is not covered by others*/
+ top_p = lv_refr_get_top_obj(area_p, lv_scr_act());
+
+ /*Do the refreshing*/
+ lv_refr_obj_and_children(top_p, area_p);
+
+ /*Also refresh top and sys layer unconditionally*/
+ lv_refr_obj_and_children(lv_layer_top(), area_p);
+ lv_refr_obj_and_children(lv_layer_sys(), area_p);
+}
+
+#else
+
+/**
+ * Refresh an area if there is Virtual Display Buffer
+ * @param area_p pointer to an area to refresh
+ */
+static void lv_refr_area_with_vdb(const lv_area_t * area_p)
+{
+
+#if LV_VDB_TRUE_DOUBLE_BUFFERED == 0
+ /*Calculate the max row num*/
+ lv_coord_t w = lv_area_get_width(area_p);
+ lv_coord_t h = lv_area_get_height(area_p);
+ lv_coord_t y2 = area_p->y2 >= LV_VER_RES ? y2 = LV_VER_RES - 1 : area_p->y2;
+
+ int32_t max_row = (uint32_t) LV_VDB_SIZE / w;
+
+ if(max_row > h) max_row = h;
+
+
+ /*Round down the lines of VDB if rounding is added*/
+ if(round_cb) {
+ lv_area_t tmp;
+ tmp.x1 = 0;
+ tmp.x2 = 0;
+ tmp.y1 = 0;
+
+ lv_coord_t y_tmp = max_row - 1;
+ do {
+ tmp.y2 = y_tmp;
+ round_cb(&tmp);
+
+ /*If this height fits into `max_row` then fine*/
+ if(lv_area_get_height(&tmp) <= max_row) break;
+
+ /*Decrement the height of the area until it fits into `max_row` after rounding*/
+ y_tmp --;
+ } while(y_tmp != 0);
+
+ if(y_tmp == 0) {
+ LV_LOG_WARN("Can't set VDB height using the round function. (Wrong round_cb or to small VDB)");
+ return;
+ } else {
+ max_row = tmp.y2 + 1;
+ }
+ }
+
+ /*Always use the full row*/
+ lv_coord_t row;
+ lv_coord_t row_last = 0;
+ for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ /*Calc. the next y coordinates of VDB*/
+ vdb_p->area.x1 = area_p->x1;
+ vdb_p->area.x2 = area_p->x2;
+ vdb_p->area.y1 = row;
+ vdb_p->area.y2 = row + max_row - 1;
+ if(vdb_p->area.y2 > y2) vdb_p->area.y2 = y2;
+ row_last = vdb_p->area.y2;
+ lv_refr_area_part_vdb(area_p);
+ }
+
+ /*If the last y coordinates are not handled yet ...*/
+ if(y2 != row_last) {
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ /*Calc. the next y coordinates of VDB*/
+ vdb_p->area.x1 = area_p->x1;
+ vdb_p->area.x2 = area_p->x2;
+ vdb_p->area.y1 = row;
+ vdb_p->area.y2 = y2;
+
+ /*Refresh this part too*/
+ lv_refr_area_part_vdb(area_p);
+ }
+#else
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ vdb_p->area.x1 = 0;
+ vdb_p->area.x2 = LV_HOR_RES-1;
+ vdb_p->area.y1 = 0;
+ vdb_p->area.y2 = LV_VER_RES - 1;
+ lv_refr_area_part_vdb(area_p);
+#endif
+}
+
+/**
+ * Refresh a part of an area which is on the actual Virtual Display Buffer
+ * @param area_p pointer to an area to refresh
+ */
+static void lv_refr_area_part_vdb(const lv_area_t * area_p)
+{
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+ lv_obj_t * top_p;
+
+ /*Get the new mask from the original area and the act. VDB
+ It will be a part of 'area_p'*/
+ lv_area_t start_mask;
+ lv_area_intersect(&start_mask, area_p, &vdb_p->area);
+
+ /*Get the most top object which is not covered by others*/
+ top_p = lv_refr_get_top_obj(&start_mask, lv_scr_act());
+
+ /*Do the refreshing from the top object*/
+ lv_refr_obj_and_children(top_p, &start_mask);
+
+ /*Also refresh top and sys layer unconditionally*/
+ lv_refr_obj_and_children(lv_layer_top(), &start_mask);
+ lv_refr_obj_and_children(lv_layer_sys(), &start_mask);
+
+ /* In true double buffered mode flush only once when all areas were rendered.
+ * In normal mode flush after every area */
+#if LV_VDB_TRUE_DOUBLE_BUFFERED == 0
+ /*Flush the content of the VDB*/
+ lv_vdb_flush();
+#endif
+}
+
+#endif /*LV_VDB_SIZE == 0*/
+
+/**
+ * Search the most top object which fully covers an area
+ * @param area_p pointer to an area
+ * @param obj the first object to start the searching (typically a screen)
+ * @return
+ */
+static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
+{
+ lv_obj_t * i;
+ lv_obj_t * found_p = NULL;
+
+ /*If this object is fully cover the draw area check the children too */
+ if(lv_area_is_in(area_p, &obj->coords) && obj->hidden == 0) {
+ LL_READ(obj->child_ll, i) {
+ found_p = lv_refr_get_top_obj(area_p, i);
+
+ /*If a children is ok then break*/
+ if(found_p != NULL) {
+ break;
+ }
+ }
+
+ /*If no better children check this object*/
+ if(found_p == NULL) {
+ lv_style_t * style = lv_obj_get_style(obj);
+ if(style->body.opa == LV_OPA_COVER &&
+ obj->design_func(obj, area_p, LV_DESIGN_COVER_CHK) != false &&
+ lv_obj_get_opa_scale(obj) == LV_OPA_COVER) {
+ found_p = obj;
+ }
+ }
+ }
+
+ return found_p;
+}
+
+/**
+ * Make the refreshing from an object. Draw all its children and the youngers too.
+ * @param top_p pointer to an objects. Start the drawing from it.
+ * @param mask_p pointer to an area, the objects will be drawn only here
+ */
+static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
+{
+ /* Normally always will be a top_obj (at least the screen)
+ * but in special cases (e.g. if the screen has alpha) it won't.
+ * In this case use the screen directly */
+ if(top_p == NULL) top_p = lv_scr_act();
+
+ /*Refresh the top object and its children*/
+ lv_refr_obj(top_p, mask_p);
+
+ /*Draw the 'younger' sibling objects because they can be on top_obj */
+ lv_obj_t * par;
+ lv_obj_t * i;
+ lv_obj_t * border_p = top_p;
+
+ par = lv_obj_get_parent(top_p);
+
+ /*Do until not reach the screen*/
+ while(par != NULL) {
+ /*object before border_p has to be redrawn*/
+ i = lv_ll_get_prev(&(par->child_ll), border_p);
+
+ while(i != NULL) {
+ /*Refresh the objects*/
+ lv_refr_obj(i, mask_p);
+ i = lv_ll_get_prev(&(par->child_ll), i);
+ }
+
+ /*The new border will be there last parents,
+ *so the 'younger' brothers of parent will be refreshed*/
+ border_p = par;
+ /*Go a level deeper*/
+ par = lv_obj_get_parent(par);
+ }
+
+ /*Call the post draw design function of the parents of the to object*/
+ par = lv_obj_get_parent(top_p);
+ while(par != NULL) {
+ par->design_func(par, mask_p, LV_DESIGN_DRAW_POST);
+ par = lv_obj_get_parent(par);
+ }
+}
+
+/**
+ * Refresh an object an all of its children. (Called recursively)
+ * @param obj pointer to an object to refresh
+ * @param mask_ori_p pointer to an area, the objects will be drawn only here
+ */
+static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
+{
+ /*Do not refresh hidden objects*/
+ if(obj->hidden != 0) return;
+
+ bool union_ok; /* Store the return value of area_union */
+ /* Truncate the original mask to the coordinates of the parent
+ * because the parent and its children are visible only here */
+ lv_area_t obj_mask;
+ lv_area_t obj_ext_mask;
+ lv_area_t obj_area;
+ lv_coord_t ext_size = obj->ext_size;
+ lv_obj_get_coords(obj, &obj_area);
+ obj_area.x1 -= ext_size;
+ obj_area.y1 -= ext_size;
+ obj_area.x2 += ext_size;
+ obj_area.y2 += ext_size;
+ union_ok = lv_area_intersect(&obj_ext_mask, mask_ori_p, &obj_area);
+
+ /*Draw the parent and its children only if they ore on 'mask_parent'*/
+ if(union_ok != false) {
+
+ /* Redraw the object */
+ obj->design_func(obj, &obj_ext_mask, LV_DESIGN_DRAW_MAIN);
+ //usleep(5 * 1000); /*DEBUG: Wait after every object draw to see the order of drawing*/
+
+
+ /*Create a new 'obj_mask' without 'ext_size' because the children can't be visible there*/
+ lv_obj_get_coords(obj, &obj_area);
+ union_ok = lv_area_intersect(&obj_mask, mask_ori_p, &obj_area);
+ if(union_ok != false) {
+ lv_area_t mask_child; /*Mask from obj and its child*/
+ lv_obj_t * child_p;
+ lv_area_t child_area;
+ LL_READ_BACK(obj->child_ll, child_p) {
+ lv_obj_get_coords(child_p, &child_area);
+ ext_size = child_p->ext_size;
+ child_area.x1 -= ext_size;
+ child_area.y1 -= ext_size;
+ child_area.x2 += ext_size;
+ child_area.y2 += ext_size;
+ /* Get the union (common parts) of original mask (from obj)
+ * and its child */
+ union_ok = lv_area_intersect(&mask_child, &obj_mask, &child_area);
+
+ /*If the parent and the child has common area then refresh the child */
+ if(union_ok) {
+ /*Refresh the next children*/
+ lv_refr_obj(child_p, &mask_child);
+ }
+ }
+ }
+
+ /* If all the children are redrawn make 'post draw' design */
+ obj->design_func(obj, &obj_ext_mask, LV_DESIGN_DRAW_POST);
+
+ }
+}
diff --git a/bdk/libs/lvgl/lv_core/lv_refr.h b/bdk/libs/lvgl/lv_core/lv_refr.h
new file mode 100644
index 0000000..b93cb10
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_refr.h
@@ -0,0 +1,94 @@
+/**
+ * @file lv_refr.h
+ *
+ */
+
+#ifndef LV_REFR_H
+#define LV_REFR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the screen refresh subsystem
+ */
+void lv_refr_init(void);
+
+/**
+ * Redraw the invalidated areas now.
+ * Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process can
+ * prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process (e.g. progress bar)
+ * this function can be called when the screen should be updated.
+ */
+void lv_refr_now(void);
+
+/**
+ * Invalidate an area
+ * @param area_p pointer to area which should be invalidated
+ */
+void lv_inv_area(const lv_area_t * area_p);
+
+/**
+ * Set a function to call after every refresh to announce the refresh time and the number of refreshed pixels
+ * @param cb pointer to a callback function (void my_refr_cb(uint32_t time_ms, uint32_t px_num))
+ */
+void lv_refr_set_monitor_cb(void (*cb)(uint32_t, uint32_t));
+
+/**
+ * Called when an area is invalidated to modify the coordinates of the area.
+ * Special display controllers may require special coordinate rounding
+ * @param cb pointer to the a function which will modify the area
+ */
+void lv_refr_set_round_cb(void(*cb)(lv_area_t*));
+
+/**
+ * Get the number of areas in the buffer
+ * @return number of invalid areas
+ */
+uint16_t lv_refr_get_buf_size(void);
+
+/**
+ * Pop (delete) the last 'num' invalidated areas from the buffer
+ * @param num number of areas to delete
+ */
+void lv_refr_pop_from_buf(uint16_t num);
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_REFR_H*/
diff --git a/bdk/libs/lvgl/lv_core/lv_style.c b/bdk/libs/lvgl/lv_core/lv_style.c
new file mode 100644
index 0000000..02fbcb2
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_style.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "../lv_misc/lv_mem.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define STYLE_MIX_MAX 256
+#define STYLE_MIX_SHIFT 8 /*log2(STYLE_MIX_MAX)*/
+
+#define VAL_PROP(v1, v2, r) v1 + (((v2-v1) * r) >> STYLE_MIX_SHIFT)
+#define STYLE_ATTR_MIX(attr, r) if(start->attr != end->attr) {res->attr = VAL_PROP(start->attr, end->attr, r);} else {res->attr = start->attr;}
+
+
+/**********************
+ * TYPEDEFS
+ **********************/
+#if USE_LV_ANIMATION
+typedef struct {
+ lv_style_t style_start; /*Save not only pointers because can be same as 'style_anim' then it will be modified too*/
+ lv_style_t style_end;
+ lv_style_t * style_anim;
+ void (*end_cb)(void *);
+} lv_style_anim_dsc_t;
+#endif
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+#if USE_LV_ANIMATION
+static void style_animator(lv_style_anim_dsc_t * dsc, int32_t val);
+static void style_animation_common_end_cb(void * ptr);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+lv_style_t lv_style_scr;
+lv_style_t lv_style_transp;
+lv_style_t lv_style_transp_fit;
+lv_style_t lv_style_transp_tight;
+lv_style_t lv_style_plain;
+lv_style_t lv_style_plain_color;
+lv_style_t lv_style_pretty;
+lv_style_t lv_style_pretty_color;
+lv_style_t lv_style_btn_rel;
+lv_style_t lv_style_btn_pr;
+lv_style_t lv_style_btn_tgl_rel;
+lv_style_t lv_style_btn_tgl_pr;
+lv_style_t lv_style_btn_ina;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Init the basic styles
+ */
+void lv_style_init(void)
+{
+ /* Not White/Black/Gray colors are created by HSV model with
+ * HUE = 210*/
+
+ /*Screen style*/
+ lv_style_scr.glass = 0;
+ lv_style_scr.body.opa = LV_OPA_COVER;
+ lv_style_scr.body.main_color = LV_COLOR_BLACK;
+ lv_style_scr.body.grad_color = LV_COLOR_BLACK;
+ lv_style_scr.body.radius = 0;
+ lv_style_scr.body.padding.ver = LV_DPI / 12;
+ lv_style_scr.body.padding.hor = LV_DPI / 12;
+ lv_style_scr.body.padding.inner = LV_DPI / 12;
+
+ lv_style_scr.body.border.color = LV_COLOR_WHITE;
+ lv_style_scr.body.border.opa = LV_OPA_COVER;
+ lv_style_scr.body.border.width = 0;
+ lv_style_scr.body.border.part = LV_BORDER_FULL;
+
+ lv_style_scr.body.shadow.color = LV_COLOR_GRAY;
+ lv_style_scr.body.shadow.type = LV_SHADOW_FULL;
+ lv_style_scr.body.shadow.width = 0;
+
+ lv_style_scr.text.opa = LV_OPA_COVER;
+ lv_style_scr.text.color = LV_COLOR_HEX(0xFBFBFB);
+ lv_style_scr.text.font = LV_FONT_DEFAULT;
+ lv_style_scr.text.letter_space = 3; // Important
+ lv_style_scr.text.line_space = 2;
+
+ lv_style_scr.image.opa = LV_OPA_COVER;
+ lv_style_scr.image.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
+ lv_style_scr.image.intense = LV_OPA_TRANSP;
+
+ lv_style_scr.line.opa = LV_OPA_COVER;
+ lv_style_scr.line.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
+ lv_style_scr.line.width = 2;
+ lv_style_scr.line.rounded = 0;
+
+ /*Plain style (by default near the same as the screen style)*/
+ memcpy(&lv_style_plain, &lv_style_scr, sizeof(lv_style_t));
+
+ /*Plain color style*/
+ memcpy(&lv_style_plain_color, &lv_style_plain, sizeof(lv_style_t));
+ lv_style_plain_color.text.color = LV_COLOR_MAKE(0xf0, 0xf0, 0xf0);
+ lv_style_plain_color.image.color = LV_COLOR_MAKE(0xf0, 0xf0, 0xf0);
+ lv_style_plain_color.line.color = LV_COLOR_MAKE(0xf0, 0xf0, 0xf0);
+ lv_style_plain_color.body.main_color = LV_COLOR_MAKE(0x55, 0x96, 0xd8);
+ lv_style_plain_color.body.grad_color = lv_style_plain_color.body.main_color;
+
+ /*Pretty style */
+ memcpy(&lv_style_pretty, &lv_style_plain, sizeof(lv_style_t));
+ lv_style_pretty.text.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
+ lv_style_pretty.image.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
+ lv_style_pretty.line.color = LV_COLOR_MAKE(0x20, 0x20, 0x20);
+ lv_style_pretty.body.main_color = LV_COLOR_WHITE;
+ lv_style_pretty.body.grad_color = LV_COLOR_SILVER;
+ lv_style_pretty.body.radius = LV_DPI / 15;
+ lv_style_pretty.body.border.color = LV_COLOR_MAKE(0x40, 0x40, 0x40);
+ lv_style_pretty.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
+ lv_style_pretty.body.border.opa = LV_OPA_30;
+
+ /*Pretty color style*/
+ memcpy(&lv_style_pretty_color, &lv_style_pretty, sizeof(lv_style_t));
+ lv_style_pretty_color.text.color = LV_COLOR_MAKE(0xe0, 0xe0, 0xe0);
+ lv_style_pretty_color.image.color = LV_COLOR_MAKE(0xe0, 0xe0, 0xe0);
+ lv_style_pretty_color.line.color = LV_COLOR_MAKE(0xc0, 0xc0, 0xc0);
+ lv_style_pretty_color.body.main_color = LV_COLOR_MAKE(0x6b, 0x9a, 0xc7);
+ lv_style_pretty_color.body.grad_color = LV_COLOR_MAKE(0x2b, 0x59, 0x8b);
+ lv_style_pretty_color.body.border.color = LV_COLOR_MAKE(0x15, 0x2c, 0x42);
+
+ /*Transparent style*/
+ memcpy(&lv_style_transp, &lv_style_plain, sizeof(lv_style_t));
+ lv_style_transp.body.empty = 1;
+ lv_style_transp.glass = 1;
+ lv_style_transp.body.border.width = 0;
+
+ /*Transparent fitting size*/
+ memcpy(&lv_style_transp_fit, &lv_style_transp, sizeof(lv_style_t));
+ lv_style_transp_fit.body.padding.hor = 0;
+ lv_style_transp_fit.body.padding.ver = 0;
+
+ /*Transparent tight style*/
+ memcpy(&lv_style_transp_tight, &lv_style_transp_fit, sizeof(lv_style_t));
+ lv_style_transp_tight.body.padding.inner = 0;
+
+ /*Button released style*/
+ memcpy(&lv_style_btn_rel, &lv_style_plain, sizeof(lv_style_t));
+ lv_style_btn_rel.body.main_color = LV_COLOR_MAKE(0x76, 0xa2, 0xd0);
+ lv_style_btn_rel.body.grad_color = LV_COLOR_MAKE(0x19, 0x3a, 0x5d);
+ lv_style_btn_rel.body.radius = LV_DPI / 15;
+ lv_style_btn_rel.body.padding.hor = LV_DPI / 4;
+ lv_style_btn_rel.body.padding.ver = LV_DPI / 6;
+ lv_style_btn_rel.body.padding.inner = LV_DPI / 10;
+ lv_style_btn_rel.body.border.color = LV_COLOR_MAKE(0x0b, 0x19, 0x28);
+ lv_style_btn_rel.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
+ lv_style_btn_rel.body.border.opa = LV_OPA_70;
+ lv_style_btn_rel.body.shadow.color = LV_COLOR_GRAY;
+ lv_style_btn_rel.body.shadow.width = 0;
+ lv_style_btn_rel.text.color = LV_COLOR_MAKE(0xff, 0xff, 0xff);
+ lv_style_btn_rel.image.color = LV_COLOR_MAKE(0xff, 0xff, 0xff);
+
+ /*Button pressed style*/
+ memcpy(&lv_style_btn_pr, &lv_style_btn_rel, sizeof(lv_style_t));
+ lv_style_btn_pr.body.main_color = LV_COLOR_MAKE(0x33, 0x62, 0x94);
+ lv_style_btn_pr.body.grad_color = LV_COLOR_MAKE(0x10, 0x26, 0x3c);
+ lv_style_btn_pr.text.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
+ lv_style_btn_pr.image.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
+ lv_style_btn_pr.line.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
+
+ /*Button toggle released style*/
+ memcpy(&lv_style_btn_tgl_rel, &lv_style_btn_rel, sizeof(lv_style_t));
+ lv_style_btn_tgl_rel.body.main_color = LV_COLOR_MAKE(0x0a, 0x11, 0x22);
+ lv_style_btn_tgl_rel.body.grad_color = LV_COLOR_MAKE(0x37, 0x62, 0x90);
+ lv_style_btn_tgl_rel.body.border.color = LV_COLOR_MAKE(0x01, 0x07, 0x0d);
+ lv_style_btn_tgl_rel.text.color = LV_COLOR_MAKE(0xc8, 0xdd, 0xf4);
+ lv_style_btn_tgl_rel.image.color = LV_COLOR_MAKE(0xc8, 0xdd, 0xf4);
+ lv_style_btn_tgl_rel.line.color = LV_COLOR_MAKE(0xc8, 0xdd, 0xf4);
+
+ /*Button toggle pressed style*/
+ memcpy(&lv_style_btn_tgl_pr, &lv_style_btn_tgl_rel, sizeof(lv_style_t));
+ lv_style_btn_tgl_pr.body.main_color = LV_COLOR_MAKE(0x02, 0x14, 0x27);
+ lv_style_btn_tgl_pr.body.grad_color = LV_COLOR_MAKE(0x2b, 0x4c, 0x70);
+ lv_style_btn_tgl_pr.text.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
+ lv_style_btn_tgl_pr.image.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
+ lv_style_btn_tgl_pr.line.color = LV_COLOR_MAKE(0xa4, 0xb5, 0xc6);
+
+ /*Button inactive style*/
+ memcpy(&lv_style_btn_ina, &lv_style_btn_rel, sizeof(lv_style_t));
+ lv_style_btn_ina.body.main_color = LV_COLOR_MAKE(0xd8, 0xd8, 0xd8);
+ lv_style_btn_ina.body.grad_color = LV_COLOR_MAKE(0xd8, 0xd8, 0xd8);
+ lv_style_btn_ina.body.border.color = LV_COLOR_MAKE(0x90, 0x90, 0x90);
+ lv_style_btn_ina.text.color = LV_COLOR_MAKE(0x70, 0x70, 0x70);
+ lv_style_btn_ina.image.color = LV_COLOR_MAKE(0x70, 0x70, 0x70);
+ lv_style_btn_ina.line.color = LV_COLOR_MAKE(0x70, 0x70, 0x70);
+}
+
+
+/**
+ * Copy a style to an other
+ * @param dest pointer to the destination style
+ * @param src pointer to the source style
+ */
+void lv_style_copy(lv_style_t * dest, const lv_style_t * src)
+{
+ memcpy(dest, src, sizeof(lv_style_t));
+}
+
+
+/**
+ * Mix two styles according to a given ratio
+ * @param start start style
+ * @param end end style
+ * @param res store the result style here
+ * @param ratio the ratio of mix [0..256]; 0: `start` style; 256: `end` style
+ */
+void lv_style_mix(const lv_style_t * start, const lv_style_t * end, lv_style_t * res, uint16_t ratio)
+{
+ STYLE_ATTR_MIX(body.opa, ratio);
+ STYLE_ATTR_MIX(body.radius, ratio);
+ STYLE_ATTR_MIX(body.border.width, ratio);
+ STYLE_ATTR_MIX(body.border.opa, ratio);
+ STYLE_ATTR_MIX(body.shadow.width, ratio);
+ STYLE_ATTR_MIX(body.padding.hor, ratio);
+ STYLE_ATTR_MIX(body.padding.ver, ratio);
+ STYLE_ATTR_MIX(body.padding.inner, ratio);
+ STYLE_ATTR_MIX(text.line_space, ratio);
+ STYLE_ATTR_MIX(text.letter_space, ratio);
+ STYLE_ATTR_MIX(text.opa, ratio);
+ STYLE_ATTR_MIX(line.width, ratio);
+ STYLE_ATTR_MIX(line.opa, ratio);
+ STYLE_ATTR_MIX(image.intense, ratio);
+ STYLE_ATTR_MIX(image.opa, ratio);
+
+ lv_opa_t opa = ratio == STYLE_MIX_MAX ? LV_OPA_COVER : ratio;
+
+ res->body.main_color = lv_color_mix(end->body.main_color, start->body.main_color, opa);
+ res->body.grad_color = lv_color_mix(end->body.grad_color, start->body.grad_color, opa);
+ res->body.border.color = lv_color_mix(end->body.border.color, start->body.border.color, opa);
+ res->body.shadow.color = lv_color_mix(end->body.shadow.color, start->body.shadow.color, opa);
+ res->text.color = lv_color_mix(end->text.color, start->text.color, opa);
+ res->image.color = lv_color_mix(end->image.color, start->image.color, opa);
+ res->line.color = lv_color_mix(end->line.color, start->line.color, opa);
+
+ if(ratio < (STYLE_MIX_MAX >> 1)) {
+ res->body.empty = start->body.empty;
+ res->body.border.part = start->body.border.part;
+ res->glass = start->glass;
+ res->text.font = start->text.font;
+ res->body.shadow.type = start->body.shadow.type;
+ res->line.rounded = start->line.rounded;
+ } else {
+ res->body.empty = end->body.empty;
+ res->body.border.part = end->body.border.part;
+ res->glass = end->glass;
+ res->text.font = end->text.font;
+ res->body.shadow.type = end->body.shadow.type;
+ res->line.rounded = end->line.rounded;
+ }
+}
+
+#if USE_LV_ANIMATION
+
+/**
+ * Create an animation from a pre-configured 'lv_style_anim_t' variable
+ * @param anim pointer to a pre-configured 'lv_style_anim_t' variable (will be copied)
+ * @return pointer to a descriptor. Really this variable will be animated. (Can be used in `lv_anim_del(dsc, NULL)`)
+ */
+void * lv_style_anim_create(lv_style_anim_t * anim)
+{
+ lv_style_anim_dsc_t * dsc;
+ dsc = lv_mem_alloc(sizeof(lv_style_anim_dsc_t));
+ lv_mem_assert(dsc);
+ if(dsc == NULL) return NULL;
+
+ dsc->style_anim = anim->style_anim;
+ memcpy(&dsc->style_start, anim->style_start, sizeof(lv_style_t));
+ memcpy(&dsc->style_end, anim->style_end, sizeof(lv_style_t));
+ memcpy(dsc->style_anim, anim->style_start, sizeof(lv_style_t));
+ dsc->end_cb = anim->end_cb;
+
+
+ lv_anim_t a;
+ a.var = (void *)dsc;
+ a.start = 0;
+ a.end = STYLE_MIX_MAX;
+ a.fp = (lv_anim_fp_t)style_animator;
+ a.path = lv_anim_path_linear;
+ a.end_cb = style_animation_common_end_cb;
+ a.act_time = anim->act_time;
+ a.time = anim->time;
+ a.playback = anim->playback;
+ a.playback_pause = anim->playback_pause;
+ a.repeat = anim->repeat;
+ a.repeat_pause = anim->repeat_pause;
+
+ lv_anim_create(&a);
+
+ return dsc;
+}
+
+#endif
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+#if USE_LV_ANIMATION
+/**
+ * Used by the style animations to set the values of a style according to start and end style.
+ * @param dsc the 'animated variable' set by lv_style_anim_create()
+ * @param val the current state of the animation between 0 and LV_STYLE_ANIM_RES
+ */
+static void style_animator(lv_style_anim_dsc_t * dsc, int32_t val)
+{
+ const lv_style_t * start = &dsc->style_start;
+ const lv_style_t * end = &dsc->style_end;
+ lv_style_t * act = dsc->style_anim;
+
+ lv_style_mix(start, end, act, val);
+
+ lv_obj_report_style_mod(dsc->style_anim);
+}
+
+/**
+ * Called when a style animation is ready
+ * It called the user defined call back and free the allocated memories
+ * @param ptr the 'animated variable' set by lv_style_anim_create()
+ */
+static void style_animation_common_end_cb(void * ptr)
+{
+ lv_style_anim_dsc_t * dsc = ptr; /*To avoid casting*/
+
+ if(dsc->end_cb) dsc->end_cb(dsc);
+
+ lv_mem_free(dsc);
+}
+
+#endif
diff --git a/bdk/libs/lvgl/lv_core/lv_style.h b/bdk/libs/lvgl/lv_core/lv_style.h
new file mode 100644
index 0000000..0ba03c8
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_style.h
@@ -0,0 +1,198 @@
+/**
+ * @file lv_style.h
+ *
+ */
+
+#ifndef LV_STYLE_H
+#define LV_STYLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_area.h"
+#include "../lv_misc/lv_font.h"
+#include "../lv_misc/lv_anim.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_RADIUS_CIRCLE (LV_COORD_MAX) /*A very big radius to always draw as circle*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/*Border types (Use 'OR'ed values)*/
+enum
+{
+ LV_BORDER_NONE = 0x00,
+ LV_BORDER_BOTTOM = 0x01,
+ LV_BORDER_TOP = 0x02,
+ LV_BORDER_LEFT = 0x04,
+ LV_BORDER_RIGHT = 0x08,
+ LV_BORDER_FULL = 0x0F,
+ LV_BORDER_INTERNAL = 0x10, /*FOR matrix-like objects (e.g. Button matrix)*/
+};
+typedef uint8_t lv_border_part_t;
+
+/*Shadow types*/
+enum
+{
+ LV_SHADOW_BOTTOM = 0,
+ LV_SHADOW_FULL,
+};
+typedef uint8_t lv_shadow_type_t;
+
+typedef struct
+{
+ uint8_t glass :1; /*1: Do not inherit this style*/
+
+ struct {
+ lv_color_t main_color;
+ lv_color_t grad_color; /*`grad_color` will be removed in v6.0, use `aux_color` instead*/
+ lv_coord_t radius;
+ lv_opa_t opa;
+
+ struct {
+ lv_color_t color;
+ lv_coord_t width;
+ lv_border_part_t part;
+ lv_opa_t opa;
+ } border;
+
+ struct {
+ lv_color_t color;
+ lv_coord_t width;
+ lv_shadow_type_t type;
+ } shadow;
+
+ struct {
+ lv_coord_t ver;
+ lv_coord_t hor;
+ lv_coord_t inner;
+ } padding;
+
+ uint8_t empty :1; /*Transparent background (border still drawn)*/
+ } body;
+
+
+ struct {
+ lv_color_t color;
+ const lv_font_t * font;
+ lv_coord_t letter_space;
+ lv_coord_t line_space;
+ lv_opa_t opa;
+ } text;
+
+ struct {
+ lv_color_t color;
+ lv_opa_t intense;
+ lv_opa_t opa;
+ } image;
+
+ struct {
+ lv_color_t color;
+ lv_coord_t width;
+ lv_opa_t opa;
+ uint8_t rounded :1; /*1: rounded line endings*/
+ } line;
+} lv_style_t;
+
+#if USE_LV_ANIMATION
+typedef struct {
+ const lv_style_t * style_start; /*Pointer to the starting style*/
+ const lv_style_t * style_end; /*Pointer to the destination style*/
+ lv_style_t * style_anim; /*Pointer to a style to animate*/
+ lv_anim_cb_t end_cb; /*Call it when the animation is ready (NULL if unused)*/
+ int16_t time; /*Animation time in ms*/
+ int16_t act_time; /*Current time in animation. Set to negative to make delay.*/
+ uint16_t playback_pause; /*Wait before play back*/
+ uint16_t repeat_pause; /*Wait before repeat*/
+ uint8_t playback :1; /*When the animation is ready play it back*/
+ uint8_t repeat :1; /*Repeat the animation infinitely*/
+} lv_style_anim_t;
+
+/* Example initialization
+lv_style_anim_t a;
+a.style_anim = &style_to_anim;
+a.style_start = &style_1;
+a.style_end = &style_2;
+a.act_time = 0;
+a.time = 1000;
+a.playback = 0;
+a.playback_pause = 0;
+a.repeat = 0;
+a.repeat_pause = 0;
+a.end_cb = NULL;
+lv_style_anim_create(&a);
+ */
+#endif
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Init the basic styles
+ */
+void lv_style_init (void);
+
+/**
+ * Copy a style to an other
+ * @param dest pointer to the destination style
+ * @param src pointer to the source style
+ */
+void lv_style_copy(lv_style_t * dest, const lv_style_t * src);
+
+
+/**
+ * Mix two styles according to a given ratio
+ * @param start start style
+ * @param end end style
+ * @param res store the result style here
+ * @param ratio the ratio of mix [0..256]; 0: `start` style; 256: `end` style
+ */
+void lv_style_mix(const lv_style_t * start, const lv_style_t * end, lv_style_t * res, uint16_t ratio);
+
+#if USE_LV_ANIMATION
+
+/**
+ * Create an animation from a pre-configured 'lv_style_anim_t' variable
+ * @param anim pointer to a pre-configured 'lv_style_anim_t' variable (will be copied)
+ * @return pointer to a descriptor. Really this variable will be animated. (Can be used in `lv_anim_del(dsc, NULL)`)
+ */
+void * lv_style_anim_create(lv_style_anim_t * anim);
+#endif
+
+/*************************
+ * GLOBAL VARIABLES
+ *************************/
+extern lv_style_t lv_style_scr;
+extern lv_style_t lv_style_transp;
+extern lv_style_t lv_style_transp_fit;
+extern lv_style_t lv_style_transp_tight;
+extern lv_style_t lv_style_plain;
+extern lv_style_t lv_style_plain_color;
+extern lv_style_t lv_style_pretty;
+extern lv_style_t lv_style_pretty_color;
+extern lv_style_t lv_style_btn_rel;
+extern lv_style_t lv_style_btn_pr;
+extern lv_style_t lv_style_btn_tgl_rel;
+extern lv_style_t lv_style_btn_tgl_pr;
+extern lv_style_t lv_style_btn_ina;
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_STYLE_H*/
diff --git a/bdk/libs/lvgl/lv_core/lv_vdb.c b/bdk/libs/lvgl/lv_core/lv_vdb.c
new file mode 100644
index 0000000..38aae34
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_vdb.c
@@ -0,0 +1,207 @@
+/**
+ * @file lv_vdb.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_vdb.h"
+#if LV_VDB_SIZE != 0
+
+#include "../lv_hal/lv_hal_disp.h"
+#include "../lv_misc/lv_log.h"
+#include
+
+/*********************
+ * DEFINES
+ *********************/
+#ifndef LV_ATTRIBUTE_FLUSH_READY
+#define LV_ATTRIBUTE_FLUSH_READY
+#endif
+
+#ifndef LV_ATTRIBUTE_MEM_ALIGN
+#define LV_ATTRIBUTE_MEM_ALIGN
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/*Simple VDB*/
+#if LV_VDB_DOUBLE == 0
+# if LV_VDB_ADR == 0
+/*If the buffer address is not specified simply allocate it*/
+static LV_ATTRIBUTE_MEM_ALIGN uint8_t vdb_buf[LV_VDB_SIZE_IN_BYTES];
+static lv_vdb_t vdb = {.buf = (lv_color_t *)vdb_buf};
+# else /*LV_VDB_ADR != 0*/
+/*If the buffer address is specified use that address*/
+static lv_vdb_t vdb = {.buf = (lv_color_t *)LV_VDB_ADR};
+# endif
+
+/*LV_VDB_DOUBLE != 0*/
+#else
+/*Double VDB*/
+static uint8_t vdb_active = 0;
+# if LV_VDB_ADR == 0
+/*If the buffer address is not specified simply allocate it*/
+static LV_ATTRIBUTE_MEM_ALIGN uint8_t vdb_buf1[LV_VDB_SIZE_IN_BYTES];
+static LV_ATTRIBUTE_MEM_ALIGN uint8_t vdb_buf2[LV_VDB_SIZE_IN_BYTES];
+static lv_vdb_t vdb[2] = {{.buf = (lv_color_t *) vdb_buf1}, {.buf = (lv_color_t *) vdb_buf2}};
+# else /*LV_VDB_ADR != 0*/
+/*If the buffer address is specified use that address*/
+static lv_vdb_t vdb[2] = {{.buf = (lv_color_t *)LV_VDB_ADR}, {.buf = (lv_color_t *)LV_VDB2_ADR}};
+# endif
+#endif
+
+static volatile bool vdb_flushing = false;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Get the 'vdb' variable or allocate one in LV_VDB_DOUBLE mode
+ * @return pointer to a 'vdb' variable
+ */
+lv_vdb_t * lv_vdb_get(void)
+{
+#if LV_VDB_DOUBLE == 0
+ /* Wait until VDB is flushing.
+ * (Until this user calls of 'lv_flush_ready()' in the display drivers's flush function*/
+ while(vdb_flushing);
+
+ return &vdb;
+#else
+ /*If already there is an active do nothing*/
+ return &vdb[vdb_active];
+#endif
+}
+
+/**
+ * Flush the content of the VDB
+ */
+void lv_vdb_flush(void)
+{
+ lv_vdb_t * vdb_act = lv_vdb_get();
+ if(!vdb_act) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ /*Don't start a new flush while the previous is not finished*/
+#if LV_VDB_DOUBLE
+ while(vdb_flushing);
+#endif /*LV_VDB_DOUBLE*/
+
+ vdb_flushing = true;
+
+ /*Flush the rendered content to the display*/
+ lv_disp_flush(vdb_act->area.x1, vdb_act->area.y1, vdb_act->area.x2, vdb_act->area.y2, vdb_act->buf);
+
+
+#if LV_VDB_DOUBLE
+ /*Make the other VDB active. The content of the current will be kept until the next flush*/
+ vdb_active++;
+ vdb_active &= 0x1;
+
+ /*If the screen is transparent initialize it when the new VDB is selected*/
+# if LV_COLOR_SCREEN_TRANSP
+ memset(vdb[vdb_active].buf, 0x00, LV_VDB_SIZE_IN_BYTES);
+# endif /*LV_COLOR_SCREEN_TRANSP*/
+
+#endif /*#if LV_VDB_DOUBLE*/
+
+}
+
+/**
+ * Set the address of VDB buffer(s) manually. To use this set `LV_VDB_ADR` (and `LV_VDB2_ADR`) to `LV_VDB_ADR_INV` in `lv_conf.h`.
+ * It should be called before `lv_init()`. The size of the buffer should be: `LV_VDB_SIZE_IN_BYTES`
+ * @param buf1 address of the VDB.
+ * @param buf2 address of the second buffer. `NULL` if `LV_VDB_DOUBLE 0`
+ */
+void lv_vdb_set_adr(void * buf1, void * buf2)
+{
+#if LV_VDB_DOUBLE == 0
+ (void) buf2; /*unused*/
+ vdb.buf = buf1;
+#else
+ vdb[0].buf = buf1;
+ vdb[1].buf = buf2;
+#endif
+}
+
+/**
+ * Call in the display driver's 'disp_flush' function when the flushing is finished
+ */
+LV_ATTRIBUTE_FLUSH_READY void lv_flush_ready(void)
+{
+ vdb_flushing = false;
+
+ /*If the screen is transparent initialize it when the flushing is ready*/
+#if LV_VDB_DOUBLE == 0 && LV_COLOR_SCREEN_TRANSP
+ memset(vdb_buf, 0x00, LV_VDB_SIZE_IN_BYTES);
+#endif
+}
+
+/**
+ * Get currently active VDB, where the drawing happens. Used with `LV_VDB_DOUBLE 1`
+ * @return pointer to the active VDB. If `LV_VDB_DOUBLE 0` give the single VDB
+ */
+lv_vdb_t * lv_vdb_get_active(void)
+{
+#if LV_VDB_DOUBLE == 0
+ return &vdb;
+#else
+ return &vdb[vdb_active];
+#endif
+}
+
+/**
+ * Get currently inactive VDB, which is being displayed or being flushed. Used with `LV_VDB_DOUBLE 1`
+ * @return pointer to the inactive VDB. If `LV_VDB_DOUBLE 0` give the single VDB
+ */
+lv_vdb_t * lv_vdb_get_inactive(void)
+{
+#if LV_VDB_DOUBLE == 0
+ return &vdb;
+#else
+ return &vdb[(vdb_active + 1) & 0x1];
+#endif
+}
+
+/**
+ * Whether the flushing is in progress or not
+ * @return true: flushing is in progress; false: flushing ready
+ */
+bool lv_vdb_is_flushing(void)
+{
+ return vdb_flushing;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#else
+
+/**
+ * Just for compatibility
+ */
+void lv_flush_ready(void)
+{
+ /*Do nothing. It is used only for VDB*/
+}
+#endif
diff --git a/bdk/libs/lvgl/lv_core/lv_vdb.h b/bdk/libs/lvgl/lv_core/lv_vdb.h
new file mode 100644
index 0000000..e94ba19
--- /dev/null
+++ b/bdk/libs/lvgl/lv_core/lv_vdb.h
@@ -0,0 +1,119 @@
+/**
+ * @file lv_vdb.h
+ *
+ */
+
+#ifndef LV_VDB_H
+#define LV_VDB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#if LV_VDB_SIZE != 0
+
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*Can be used in `lv_conf.h` the set an invalid address for the VDB. It should be replaced later by a valid address using `lv_vdb_set_adr()`*/
+#define LV_VDB_ADR_INV 8 /*8 is still too small to be valid but it's aligned on 64 bit machines as well*/
+
+#ifndef LV_VDB_PX_BPP
+#define LV_VDB_PX_BPP LV_COLOR_SIZE /* Default is LV_COLOR_SIZE */
+#endif
+
+
+#if LV_VDB_TRUE_DOUBLE_BUFFERED && (LV_VDB_SIZE != LV_HOR_RES * LV_VER_RES || LV_VDB_DOUBLE == 0)
+#error "With LV_VDB_TRUE_DOUBLE_BUFFERED: (LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES and LV_VDB_DOUBLE = 1 is required"
+#endif
+
+
+/* The size of VDB in bytes.
+ * (LV_VDB_SIZE * LV_VDB_PX_BPP) >> 3): just divide by 8 to convert bits to bytes
+ * (((LV_VDB_SIZE * LV_VDB_PX_BPP) & 0x7) ? 1 : 0): add an extra byte to round up.
+ * E.g. if LV_VDB_SIZE = 10 and LV_VDB_PX_BPP = 1 -> 10 bits -> 2 bytes*/
+#define LV_VDB_SIZE_IN_BYTES ((LV_VDB_SIZE * LV_VDB_PX_BPP) >> 3) + (((LV_VDB_SIZE * LV_VDB_PX_BPP) & 0x7) ? 1 : 0)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct
+{
+ lv_area_t area;
+ lv_color_t *buf;
+} lv_vdb_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Get the 'vdb' variable or allocate one in LV_VDB_DOUBLE mode
+ * @return pointer to a 'vdb' variable
+ */
+lv_vdb_t * lv_vdb_get(void);
+
+/**
+ * Flush the content of the vdb
+ */
+void lv_vdb_flush(void);
+
+/**
+ * Set the address of VDB buffer(s) manually. To use this set `LV_VDB_ADR` (and `LV_VDB2_ADR`) to `LV_VDB_ADR_INV` in `lv_conf.h`.
+ * It should be called before `lv_init()`. The size of the buffer should be: `LV_VDB_SIZE_IN_BYTES`
+ * @param buf1 address of the VDB.
+ * @param buf2 address of the second buffer. `NULL` if `LV_VDB_DOUBLE 0`
+ */
+void lv_vdb_set_adr(void * buf1, void * buf2);
+
+/**
+ * Call in the display driver's 'disp_flush' function when the flushing is finished
+ */
+void lv_flush_ready(void);
+
+/**
+ * Get currently active VDB, where the drawing happens. Used with `LV_VDB_DOUBLE 1`
+ * @return pointer to the active VDB. If `LV_VDB_DOUBLE 0` give the single VDB
+ */
+lv_vdb_t * lv_vdb_get_active(void);
+
+/**
+ * Get currently inactive VDB, which is being displayed or being flushed. Used with `LV_VDB_DOUBLE 1`
+ * @return pointer to the inactive VDB. If `LV_VDB_DOUBLE 0` give the single VDB
+ */
+lv_vdb_t * lv_vdb_get_inactive(void);
+
+/**
+ * Whether the flushing is in progress or not
+ * @return true: flushing is in progress; false: flushing ready
+ */
+bool lv_vdb_is_flushing(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+#else /*LV_VDB_SIZE != 0*/
+
+/*Just for compatibility*/
+void lv_flush_ready(void);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_VDB_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw.c b/bdk/libs/lvgl/lv_draw/lv_draw.c
new file mode 100644
index 0000000..af374e3
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw.c
@@ -0,0 +1,163 @@
+/**
+ * @file lv_draw.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include
+#include "lv_draw.h"
+#include "lv_draw_rbasic.h"
+#include "lv_draw_vbasic.h"
+#include "../lv_misc/lv_fs.h"
+#include "../lv_misc/lv_math.h"
+#include "../lv_misc/lv_ufs.h"
+#include "../lv_objx/lv_img.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+#if LV_VDB_SIZE != 0
+void (*const px_fp)(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_vpx;
+void (*const fill_fp)(const lv_area_t * coords, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_vfill;
+void (*const letter_fp)(const lv_point_t * pos_p, const lv_area_t * mask, const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa) = lv_vletter;
+void (*const map_fp)(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa) = lv_vmap;
+#else
+void (*const px_fp)(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_rpx;
+void (*const fill_fp)(const lv_area_t * coords, const lv_area_t * mask, lv_color_t color, lv_opa_t opa) = lv_rfill;
+void (*const letter_fp)(const lv_point_t * pos_p, const lv_area_t * mask, const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa) = lv_rletter;
+void (*const map_fp)(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa) = lv_rmap;
+#endif
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#if LV_ANTIALIAS != 0
+
+/**
+ * Get the opacity of a pixel based it's position in a line segment
+ * @param seg segment length
+ * @param px_id position of of a pixel which opacity should be get [0..seg-1]
+ * @param base_opa the base opacity
+ * @return the opacity of the given pixel
+ */
+lv_opa_t lv_draw_aa_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t base_opa)
+{
+ /* How to calculate the opacity of pixels on the edges which makes the anti-aliasing?
+ * For example we have a line like this (y = -0.5 * x):
+ *
+ * | _ _
+ * * * |
+ *
+ * Anti-aliased pixels come to the '*' characters
+ * Calculate what percentage of the pixels should be covered if real line (not rasterized) would be drawn:
+ * 1. A real line should start on (0;0) and end on (2;1)
+ * 2. So the line intersection coordinates on the first pixel: (0;0) (1;0.5) -> 25% covered pixel in average
+ * 3. For the second pixel: (1;0.5) (2;1) -> 75% covered pixel in average
+ * 4. The equation: (px_id * 2 + 1) / (segment_width * 2)
+ * segment_width: the line segment which is being anti-aliased (was 2 in the example)
+ * px_id: pixel ID from 0 to (segment_width - 1)
+ * result: [0..1] coverage of the pixel
+ */
+
+ /*Accelerate the common segment sizes to avoid division*/
+ static const lv_opa_t seg1[1] = {128};
+ static const lv_opa_t seg2[2] = {64, 192};
+ static const lv_opa_t seg3[3] = {42, 128, 212};
+ static const lv_opa_t seg4[4] = {32, 96, 159, 223};
+ static const lv_opa_t seg5[5] = {26, 76, 128, 178, 230};
+ static const lv_opa_t seg6[6] = {21, 64, 106, 148, 191, 234};
+ static const lv_opa_t seg7[7] = {18, 55, 91, 128, 164, 200, 237};
+ static const lv_opa_t seg8[8] = {16, 48, 80, 112, 143, 175, 207, 239};
+
+ static const lv_opa_t * seg_map[] = {seg1, seg2, seg3, seg4,
+ seg5, seg6, seg7, seg8
+ };
+
+ if(seg == 0) return LV_OPA_TRANSP;
+ else if(seg < 8) return (uint32_t)((uint32_t)seg_map[seg - 1][px_id] * base_opa) >> 8;
+ else {
+ return ((px_id * 2 + 1) * base_opa) / (2 * seg);
+ }
+
+}
+
+/**
+ * Add a vertical anti-aliasing segment (pixels with decreasing opacity)
+ * @param x start point x coordinate
+ * @param y start point y coordinate
+ * @param length length of segment (negative value to start from 0 opacity)
+ * @param mask draw only in this area
+ * @param color color of pixels
+ * @param opa maximum opacity
+ */
+void lv_draw_aa_ver_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa)
+{
+ bool aa_inv = false;
+ if(length < 0) {
+ aa_inv = true;
+ length = -length;
+ }
+
+ lv_coord_t i;
+ for(i = 0; i < length; i++) {
+ lv_opa_t px_opa = lv_draw_aa_get_opa(length, i, opa);
+ if(aa_inv) px_opa = opa - px_opa;
+ px_fp(x, y + i, mask, color, px_opa);
+ }
+}
+
+/**
+ * Add a horizontal anti-aliasing segment (pixels with decreasing opacity)
+ * @param x start point x coordinate
+ * @param y start point y coordinate
+ * @param length length of segment (negative value to start from 0 opacity)
+ * @param mask draw only in this area
+ * @param color color of pixels
+ * @param opa maximum opacity
+ */
+void lv_draw_aa_hor_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa)
+{
+ bool aa_inv = false;
+ if(length < 0) {
+ aa_inv = true;
+ length = -length;
+ }
+
+ lv_coord_t i;
+ for(i = 0; i < length; i++) {
+ lv_opa_t px_opa = lv_draw_aa_get_opa(length, i, opa);
+ if(aa_inv) px_opa = opa - px_opa;
+ px_fp(x + i, y, mask, color, px_opa);
+ }
+}
+
+#endif
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw.h b/bdk/libs/lvgl/lv_draw/lv_draw.h
new file mode 100644
index 0000000..cf50e98
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw.h
@@ -0,0 +1,115 @@
+/**
+ * @file lv_draw.h
+ *
+ */
+
+#ifndef LV_DRAW_H
+#define LV_DRAW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#include "../lv_core/lv_style.h"
+#include "../lv_misc/lv_txt.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*If image pixels contains alpha we need to know how much byte is a pixel*/
+#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
+# define LV_IMG_PX_SIZE_ALPHA_BYTE 2
+#elif LV_COLOR_DEPTH == 16
+# define LV_IMG_PX_SIZE_ALPHA_BYTE 3
+#elif LV_COLOR_DEPTH == 32
+# define LV_IMG_PX_SIZE_ALPHA_BYTE 4
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+enum {
+ LV_IMG_SRC_VARIABLE,
+ LV_IMG_SRC_FILE,
+ LV_IMG_SRC_SYMBOL,
+ LV_IMG_SRC_UNKNOWN,
+};
+typedef uint8_t lv_img_src_t;
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+#if LV_ANTIALIAS != 0
+
+/**
+ * Get the opacity of a pixel based it's position in a line segment
+ * @param seg segment length
+ * @param px_id position of of a pixel which opacity should be get [0..seg-1]
+ * @param base_opa the base opacity
+ * @return the opacity of the given pixel
+ */
+lv_opa_t lv_draw_aa_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t base_opa);
+
+/**
+ * Add a vertical anti-aliasing segment (pixels with decreasing opacity)
+ * @param x start point x coordinate
+ * @param y start point y coordinate
+ * @param length length of segment (negative value to start from 0 opacity)
+ * @param mask draw only in this area
+ * @param color color of pixels
+ * @param opa maximum opacity
+ */
+void lv_draw_aa_ver_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa);
+
+/**
+ * Add a horizontal anti-aliasing segment (pixels with decreasing opacity)
+ * @param x start point x coordinate
+ * @param y start point y coordinate
+ * @param length length of segment (negative value to start from 0 opacity)
+ * @param mask draw only in this area
+ * @param color color of pixels
+ * @param opa maximum opacity
+ */
+void lv_draw_aa_hor_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa);
+#endif
+
+/**********************
+ * GLOBAL VARIABLES
+ **********************/
+extern void (*const px_fp)(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_color_t color, lv_opa_t opa);
+extern void (*const fill_fp)(const lv_area_t * coords, const lv_area_t * mask, lv_color_t color, lv_opa_t opa);
+extern void (*const letter_fp)(const lv_point_t * pos_p, const lv_area_t * mask, const lv_font_t * font_p, uint32_t letter, lv_color_t color, lv_opa_t opa);
+extern void (*const map_fp)(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa);
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * POST INCLUDES
+ *********************/
+#include "lv_draw_rect.h"
+#include "lv_draw_label.h"
+#include "lv_draw_img.h"
+#include "lv_draw_line.h"
+#include "lv_draw_triangle.h"
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw.mk b/bdk/libs/lvgl/lv_draw/lv_draw.mk
new file mode 100644
index 0000000..a384eef
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw.mk
@@ -0,0 +1,14 @@
+CSRCS += lv_draw_vbasic.c
+CSRCS += lv_draw_rbasic.c
+CSRCS += lv_draw.c
+CSRCS += lv_draw_rect.c
+CSRCS += lv_draw_label.c
+CSRCS += lv_draw_line.c
+CSRCS += lv_draw_img.c
+CSRCS += lv_draw_arc.c
+CSRCS += lv_draw_triangle.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_draw
+VPATH += :$(LVGL_DIR)/lvgl/lv_draw
+
+CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_draw"
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_arc.c b/bdk/libs/lvgl/lv_draw/lv_draw_arc.c
new file mode 100644
index 0000000..525e56b
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_arc.c
@@ -0,0 +1,264 @@
+/**
+ * @file lv_draw_arc.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_arc.h"
+#include "../lv_misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static uint16_t fast_atan2(int x, int y);
+static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa);
+static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa);
+static bool deg_test_norm(uint16_t deg, uint16_t start, uint16_t end);
+static bool deg_test_inv(uint16_t deg, uint16_t start, uint16_t end);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Draw an arc. (Can draw pie too with great thickness.)
+ * @param center_x the x coordinate of the center of the arc
+ * @param center_y the y coordinate of the center of the arc
+ * @param radius the radius of the arc
+ * @param mask the arc will be drawn only in this mask
+ * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
+ * @param end_angle the end angle of the arc
+ * @param style style of the arc (`body.thickness`, `body.main_color`, `body.opa` is used)
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, const lv_area_t * mask,
+ uint16_t start_angle, uint16_t end_angle, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ lv_coord_t thickness = style->line.width;
+ if(thickness > radius) thickness = radius;
+
+ lv_coord_t r_out = radius;
+ lv_coord_t r_in = r_out - thickness;
+ int16_t deg_base;
+ int16_t deg;
+ lv_coord_t x_start[4];
+ lv_coord_t x_end[4];
+
+ lv_color_t color = style->line.color;
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t) style->body.opa * opa_scale) >> 8;
+
+
+ bool (*deg_test)(uint16_t, uint16_t, uint16_t);
+ if(start_angle <= end_angle) deg_test = deg_test_norm;
+ else deg_test = deg_test_inv;
+
+ if(deg_test(270, start_angle, end_angle)) hor_line(center_x - r_out + 1, center_y, mask, thickness - 1, color, opa); // Left Middle
+ if(deg_test(90, start_angle, end_angle)) hor_line(center_x + r_in, center_y, mask, thickness - 1, color, opa); // Right Middle
+ if(deg_test(180, start_angle, end_angle)) ver_line(center_x, center_y - r_out + 1, mask, thickness - 1, color, opa); // Top Middle
+ if(deg_test(0, start_angle, end_angle)) ver_line(center_x, center_y + r_in, mask, thickness - 1, color, opa); // Bottom middle
+
+ uint32_t r_out_sqr = r_out * r_out;
+ uint32_t r_in_sqr = r_in * r_in;
+ int16_t xi;
+ int16_t yi;
+ for(yi = -r_out; yi < 0; yi++) {
+ x_start[0] = LV_COORD_MIN;
+ x_start[1] = LV_COORD_MIN;
+ x_start[2] = LV_COORD_MIN;
+ x_start[3] = LV_COORD_MIN;
+ x_end[0] = LV_COORD_MIN;
+ x_end[1] = LV_COORD_MIN;
+ x_end[2] = LV_COORD_MIN;
+ x_end[3] = LV_COORD_MIN;
+ for(xi = -r_out; xi < 0; xi++) {
+
+ uint32_t r_act_sqr = xi * xi + yi * yi;
+ if(r_act_sqr > r_out_sqr) continue;
+
+ deg_base = fast_atan2(xi, yi) - 180;
+
+ deg = 180 + deg_base;
+ if(deg_test(deg, start_angle, end_angle)) {
+ if(x_start[0] == LV_COORD_MIN) x_start[0] = xi;
+ } else if(x_start[0] != LV_COORD_MIN && x_end[0] == LV_COORD_MIN) {
+ x_end[0] = xi - 1;
+ }
+
+ deg = 360 - deg_base;
+ if(deg_test(deg, start_angle, end_angle)) {
+ if(x_start[1] == LV_COORD_MIN) x_start[1] = xi;
+ } else if(x_start[1] != LV_COORD_MIN && x_end[1] == LV_COORD_MIN) {
+ x_end[1] = xi - 1;
+ }
+
+ deg = 180 - deg_base;
+ if(deg_test(deg, start_angle, end_angle)) {
+ if(x_start[2] == LV_COORD_MIN) x_start[2] = xi;
+ } else if(x_start[2] != LV_COORD_MIN && x_end[2] == LV_COORD_MIN) {
+ x_end[2] = xi - 1;
+ }
+
+ deg = deg_base;
+ if(deg_test(deg, start_angle, end_angle)) {
+ if(x_start[3] == LV_COORD_MIN) x_start[3] = xi;
+ } else if(x_start[3] != LV_COORD_MIN && x_end[3] == LV_COORD_MIN) {
+ x_end[3] = xi - 1;
+ }
+
+ if(r_act_sqr < r_in_sqr) break; /*No need to continue the iteration in x once we found the inner edge of the arc*/
+ }
+
+
+ if(x_start[0] != LV_COORD_MIN) {
+ if(x_end[0] == LV_COORD_MIN) x_end[0] = xi - 1;
+ hor_line(center_x + x_start[0], center_y + yi, mask, x_end[0] - x_start[0], color, opa);
+ }
+
+ if(x_start[1] != LV_COORD_MIN) {
+ if(x_end[1] == LV_COORD_MIN) x_end[1] = xi - 1;
+ hor_line(center_x + x_start[1], center_y - yi, mask, x_end[1] - x_start[1], color, opa);
+ }
+
+ if(x_start[2] != LV_COORD_MIN) {
+ if(x_end[2] == LV_COORD_MIN) x_end[2] = xi - 1;
+ hor_line(center_x - x_end[2], center_y + yi, mask, LV_MATH_ABS(x_end[2] - x_start[2]), color, opa);
+ }
+
+ if(x_start[3] != LV_COORD_MIN) {
+ if(x_end[3] == LV_COORD_MIN) x_end[3] = xi - 1;
+ hor_line(center_x - x_end[3], center_y - yi, mask, LV_MATH_ABS(x_end[3] - x_start[3]), color, opa);
+ }
+
+
+#if LV_ANTIALIAS
+ /*TODO*/
+
+#endif
+
+ }
+}
+
+static uint16_t fast_atan2(int x, int y)
+{
+ // Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com
+ // Converts any XY values including 0 to a degree value that should be
+ // within +/- 1 degree of the accurate value without needing
+ // large slow trig functions like ArcTan() or ArcCos().
+ // NOTE! at least one of the X or Y values must be non-zero!
+ // This is the full version, for all 4 quadrants and will generate
+ // the angle in integer degrees from 0-360.
+ // Any values of X and Y are usable including negative values provided
+ // they are between -1456 and 1456 so the 16bit multiply does not overflow.
+
+ unsigned char negflag;
+ unsigned char tempdegree;
+ unsigned char comp;
+ unsigned int degree; // this will hold the result
+ //signed int x; // these hold the XY vector at the start
+ //signed int y; // (and they will be destroyed)
+ unsigned int ux;
+ unsigned int uy;
+
+ // Save the sign flags then remove signs and get XY as unsigned ints
+ negflag = 0;
+ if(x < 0) {
+ negflag += 0x01; // x flag bit
+ x = (0 - x); // is now +
+ }
+ ux = x; // copy to unsigned var before multiply
+ if(y < 0) {
+ negflag += 0x02; // y flag bit
+ y = (0 - y); // is now +
+ }
+ uy = y; // copy to unsigned var before multiply
+
+ // 1. Calc the scaled "degrees"
+ if(ux > uy) {
+ degree = (uy * 45) / ux; // degree result will be 0-45 range
+ negflag += 0x10; // octant flag bit
+ } else {
+ degree = (ux * 45) / uy; // degree result will be 0-45 range
+ }
+
+ // 2. Compensate for the 4 degree error curve
+ comp = 0;
+ tempdegree = degree; // use an unsigned char for speed!
+ if(tempdegree > 22) { // if top half of range
+ if(tempdegree <= 44) comp++;
+ if(tempdegree <= 41) comp++;
+ if(tempdegree <= 37) comp++;
+ if(tempdegree <= 32) comp++; // max is 4 degrees compensated
+ } else { // else is lower half of range
+ if(tempdegree >= 2) comp++;
+ if(tempdegree >= 6) comp++;
+ if(tempdegree >= 10) comp++;
+ if(tempdegree >= 15) comp++; // max is 4 degrees compensated
+ }
+ degree += comp; // degree is now accurate to +/- 1 degree!
+
+ // Invert degree if it was X>Y octant, makes 0-45 into 90-45
+ if(negflag & 0x10) degree = (90 - degree);
+
+ // 3. Degree is now 0-90 range for this quadrant,
+ // need to invert it for whichever quadrant it was in
+ if(negflag & 0x02) { // if -Y
+ if(negflag & 0x01) // if -Y -X
+ degree = (180 + degree);
+ else // else is -Y +X
+ degree = (180 - degree);
+ } else { // else is +Y
+ if(negflag & 0x01) // if +Y -X
+ degree = (360 - degree);
+ }
+ return degree;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa)
+{
+ lv_area_t area;
+ lv_area_set(&area, x, y, x, y + len);
+
+ fill_fp(&area, mask, color, opa);
+}
+
+static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa)
+{
+ lv_area_t area;
+ lv_area_set(&area, x, y, x + len, y);
+
+ fill_fp(&area, mask, color, opa);
+}
+
+static bool deg_test_norm(uint16_t deg, uint16_t start, uint16_t end)
+{
+ if(deg >= start && deg <= end) return true;
+ else return false;
+}
+
+static bool deg_test_inv(uint16_t deg, uint16_t start, uint16_t end)
+{
+ if(deg >= start || deg <= end) {
+ return true;
+ } else return false;
+}
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_arc.h b/bdk/libs/lvgl/lv_draw/lv_draw_arc.h
new file mode 100644
index 0000000..203eabe
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_arc.h
@@ -0,0 +1,53 @@
+/**
+ * @file lv_draw_arc.h
+ *
+ */
+
+#ifndef LV_DRAW_ARC_H
+#define LV_DRAW_ARC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Draw an arc. (Can draw pie too with great thickness.)
+ * @param center_x the x coordinate of the center of the arc
+ * @param center_y the y coordinate of the center of the arc
+ * @param radius the radius of the arc
+ * @param mask the arc will be drawn only in this mask
+ * @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
+ * @param end_angle the end angle of the arc
+ * @param style style of the arc (`body.thickness`, `body.main_color`, `body.opa` is used)
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, const lv_area_t * mask,
+ uint16_t start_angle, uint16_t end_angle, const lv_style_t * style, lv_opa_t opa_scale);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_ARC*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_img.c b/bdk/libs/lvgl/lv_draw/lv_draw_img.c
new file mode 100644
index 0000000..5c24aa8
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_img.c
@@ -0,0 +1,759 @@
+/**
+ * @file lv_draw_img.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_img.h"
+#include "../lv_misc/lv_fs.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mask,
+ const void * src, const lv_style_t * style, lv_opa_t opa_scale);
+
+static const uint8_t * lv_img_decoder_open(const void * src, const lv_style_t * style);
+static lv_res_t lv_img_decoder_read_line(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
+static void lv_img_decoder_close(void);
+static lv_res_t lv_img_built_in_decoder_line_alpha(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
+static lv_res_t lv_img_built_in_decoder_line_indexed(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static bool decoder_custom;
+static const void * decoder_src;
+static lv_img_src_t decoder_src_type;
+static lv_img_header_t decoder_header;
+static const lv_style_t * decoder_style;
+#if USE_LV_FILESYSTEM
+static lv_fs_file_t decoder_file;
+#endif
+#if LV_IMG_CF_INDEXED
+static lv_color_t decoder_index_map[256];
+#endif
+
+static lv_img_decoder_info_f_t lv_img_decoder_info_custom;
+static lv_img_decoder_open_f_t lv_img_decoder_open_custom;
+static lv_img_decoder_read_line_f_t lv_img_decoder_read_line_custom;
+static lv_img_decoder_close_f_t lv_img_decoder_close_custom;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Draw an image
+ * @param coords the coordinates of the image
+ * @param mask the image will be drawn only in this area
+ * @param src pointer to a lv_color_t array which contains the pixels of the image
+ * @param style style of the image
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask,
+ const void * src, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ if(src == NULL) {
+ LV_LOG_WARN("Image draw: src is NULL");
+ lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
+ lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL);
+ return;
+ }
+
+ lv_res_t res;
+ res = lv_img_draw_core(coords, mask, src, style, opa_scale);
+
+ if(res == LV_RES_INV) {
+ LV_LOG_WARN("Image draw error");
+ lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
+ lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL);
+ return;
+ }
+}
+
+
+/**
+ *
+ * @param src
+ * @param header
+ * @param style
+ * @return
+ */
+lv_res_t lv_img_dsc_get_info(const char * src, lv_img_header_t * header)
+{
+ header->always_zero = 0;
+ /*Try to get info with the custom functions first*/
+ if(lv_img_decoder_info_custom) {
+ lv_res_t custom_res;
+ custom_res = lv_img_decoder_info_custom(src, header);
+ if(custom_res == LV_RES_OK) return LV_RES_OK; /*Custom info has supported this source*/
+ }
+
+ lv_img_src_t src_type = lv_img_src_get_type(src);
+ if(src_type == LV_IMG_SRC_VARIABLE) {
+ header->w = ((lv_img_dsc_t *)src)->header.w;
+ header->h = ((lv_img_dsc_t *)src)->header.h;
+ header->cf = ((lv_img_dsc_t *)src)->header.cf;
+ }
+#if USE_LV_FILESYSTEM
+ else if(src_type == LV_IMG_SRC_FILE) {
+ lv_fs_file_t file;
+ lv_fs_res_t res;
+ uint32_t rn;
+ res = lv_fs_open(&file, src, LV_FS_MODE_RD);
+ if(res == LV_FS_RES_OK) {
+ res = lv_fs_read(&file, header, sizeof(lv_img_header_t), &rn);
+ }
+
+ /*Create a dummy header on fs error*/
+ if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
+ header->w = LV_DPI;
+ header->h = LV_DPI;
+ header->cf = LV_IMG_CF_UNKOWN;
+ }
+
+ lv_fs_close(&file);
+ }
+#endif
+ else if(src_type == LV_IMG_SRC_SYMBOL) {
+ /*The size depend on the font but it is unknown here. It should be handled outside of the function*/
+ header->w = 1;
+ header->h = 1;
+ /* Symbols always have transparent parts. Important because of cover check in the design function.
+ * The actual value doesn't matter because lv_draw_label will draw it*/
+ header->cf = LV_IMG_CF_ALPHA_1BIT;
+ } else {
+ LV_LOG_WARN("Image get info found unknown src type");
+ return false;
+ }
+ return true;
+
+}
+
+uint8_t lv_img_color_format_get_px_size(lv_img_cf_t cf)
+{
+ uint8_t px_size = 0;
+
+ switch(cf) {
+ case LV_IMG_CF_UNKOWN:
+ case LV_IMG_CF_RAW:
+ px_size = 0;
+ break;
+ case LV_IMG_CF_TRUE_COLOR:
+ case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
+ px_size = LV_COLOR_SIZE;
+ break;
+ case LV_IMG_CF_TRUE_COLOR_ALPHA:
+ px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
+ break;
+ case LV_IMG_CF_INDEXED_1BIT:
+ case LV_IMG_CF_ALPHA_1BIT:
+ px_size = 1;
+ break;
+ case LV_IMG_CF_INDEXED_2BIT:
+ case LV_IMG_CF_ALPHA_2BIT:
+ px_size = 2;
+ break;
+ case LV_IMG_CF_INDEXED_4BIT:
+ case LV_IMG_CF_ALPHA_4BIT:
+ px_size = 4;
+ break;
+ case LV_IMG_CF_INDEXED_8BIT:
+ case LV_IMG_CF_ALPHA_8BIT:
+ px_size = 8;
+ break;
+ default:
+ px_size = 0;
+ break;
+ }
+
+ return px_size;
+}
+
+bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf)
+{
+ bool is_chroma_keyed = false;
+
+ switch(cf) {
+ case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
+ case LV_IMG_CF_RAW_CHROMA_KEYED:
+ case LV_IMG_CF_INDEXED_1BIT:
+ case LV_IMG_CF_INDEXED_2BIT:
+ case LV_IMG_CF_INDEXED_4BIT:
+ case LV_IMG_CF_INDEXED_8BIT:
+ is_chroma_keyed = true;
+ break;
+ default:
+ is_chroma_keyed = false;
+ break;
+ }
+
+ return is_chroma_keyed;
+}
+
+
+bool lv_img_color_format_has_alpha(lv_img_cf_t cf)
+{
+ bool has_alpha = false;
+
+ switch(cf) {
+ case LV_IMG_CF_TRUE_COLOR_ALPHA:
+ case LV_IMG_CF_RAW_ALPHA:
+ case LV_IMG_CF_ALPHA_1BIT:
+ case LV_IMG_CF_ALPHA_2BIT:
+ case LV_IMG_CF_ALPHA_4BIT:
+ case LV_IMG_CF_ALPHA_8BIT:
+ has_alpha = true;
+ break;
+ default:
+ has_alpha = false;
+ break;
+ }
+
+ return has_alpha;
+}
+
+/**
+ * Get the type of an image source
+ * @param src pointer to an image source:
+ * - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
+ * - a path to a file (e.g. "S:/folder/image.bin")
+ * - or a symbol (e.g. SYMBOL_CLOSE)
+ * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKOWN
+ */
+lv_img_src_t lv_img_src_get_type(const void * src)
+{
+ lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
+
+ if(src == NULL) return img_src_type;
+ const uint8_t * u8_p = src;
+
+ /*The first byte shows the type of the image source*/
+ if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
+ img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
+ } else if(u8_p[0] >= 0x80) {
+ img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
+ } else {
+ img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is design to the first byte < 0x20*/
+ }
+
+ if (LV_IMG_SRC_UNKNOWN == img_src_type) {
+ LV_LOG_WARN("lv_img_src_get_type: unknown image type");
+ }
+
+ return img_src_type;
+}
+
+/**
+ * Set custom decoder functions. See the typdefs of the function typed above for more info about them
+ * @param info_fp info get function
+ * @param open_fp open function
+ * @param read_fp read line function
+ * @param close_fp clode function
+ */
+void lv_img_decoder_set_custom(lv_img_decoder_info_f_t info_fp, lv_img_decoder_open_f_t open_fp,
+ lv_img_decoder_read_line_f_t read_fp, lv_img_decoder_close_f_t close_fp)
+{
+ lv_img_decoder_info_custom = info_fp;
+ lv_img_decoder_open_custom = open_fp;
+ lv_img_decoder_read_line_custom = read_fp;
+ lv_img_decoder_close_custom = close_fp;
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+
+static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mask,
+ const void * src, const lv_style_t * style, lv_opa_t opa_scale)
+{
+
+ lv_area_t mask_com; /*Common area of mask and coords*/
+ bool union_ok;
+ union_ok = lv_area_intersect(&mask_com, mask, coords);
+ if(union_ok == false) {
+ return LV_RES_OK; /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
+ }
+
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->image.opa : (uint16_t)((uint16_t) style->image.opa * opa_scale) >> 8;
+
+ lv_img_header_t header;
+ lv_res_t header_res;
+ header_res = lv_img_dsc_get_info(src, &header);
+ if(header_res != LV_RES_OK) {
+ LV_LOG_WARN("Image draw can't get image info");
+ lv_img_decoder_close();
+ return LV_RES_INV;
+ }
+
+ bool chroma_keyed = lv_img_color_format_is_chroma_keyed(header.cf);
+ bool alpha_byte = lv_img_color_format_has_alpha(header.cf);
+
+ const uint8_t * img_data = lv_img_decoder_open(src, style);
+ if(img_data == LV_IMG_DECODER_OPEN_FAIL) {
+ LV_LOG_WARN("Image draw cannot open the image resource");
+ lv_img_decoder_close();
+ return LV_RES_INV;
+ }
+
+ /* The decoder open could open the image and gave the entire uncompressed image.
+ * Just draw it!*/
+ if(img_data) {
+ map_fp(coords, mask, img_data, opa, chroma_keyed, alpha_byte, style->image.color, style->image.intense);
+ }
+ /* The whole uncompressed image is not available. Try to read it line-by-line*/
+ else {
+ lv_coord_t width = lv_area_get_width(&mask_com);
+
+#if LV_COMPILER_VLA_SUPPORTED
+ uint8_t buf[(lv_area_get_width(&mask_com) * ((LV_COLOR_DEPTH >> 3) + 1))];
+#else
+ uint8_t buf[LV_HOR_RES * ((LV_COLOR_DEPTH >> 3) + 1)]; /*+1 because of the possible alpha byte*/
+#endif
+ lv_area_t line;
+ lv_area_copy(&line, &mask_com);
+ lv_area_set_height(&line, 1);
+ lv_coord_t x = mask_com.x1 - coords->x1;
+ lv_coord_t y = mask_com.y1 - coords->y1;
+ lv_coord_t row;
+ lv_res_t read_res;
+ for(row = mask_com.y1; row <= mask_com.y2; row++) {
+ read_res = lv_img_decoder_read_line(x, y, width, buf);
+ if(read_res != LV_RES_OK) {
+ lv_img_decoder_close();
+ LV_LOG_WARN("Image draw can't read the line");
+ return LV_RES_INV;
+ }
+ map_fp(&line, mask, buf, opa, chroma_keyed, alpha_byte, style->image.color, style->image.intense);
+ line.y1++;
+ line.y2++;
+ y++;
+ }
+ }
+
+ lv_img_decoder_close();
+
+ return LV_RES_OK;
+}
+
+
+static const uint8_t * lv_img_decoder_open(const void * src, const lv_style_t * style)
+{
+ decoder_custom = false;
+
+ /*Try to open with the custom functions first*/
+ if(lv_img_decoder_open_custom) {
+ const uint8_t * custom_res;
+ custom_res = lv_img_decoder_open_custom(src, style);
+ if(custom_res != LV_IMG_DECODER_OPEN_FAIL) {
+ decoder_custom = true; /*Mark that custom decoder function should be used for this img source.*/
+ return custom_res; /*Custom open supported this source*/
+ }
+ }
+
+ decoder_src = src;
+ decoder_style = style;
+ decoder_src_type = lv_img_src_get_type(src);
+
+ lv_res_t header_res;
+ header_res = lv_img_dsc_get_info(src, &decoder_header);
+ if(header_res == LV_RES_INV) {
+ decoder_src = NULL;
+ decoder_src_type = LV_IMG_SRC_UNKNOWN;
+ LV_LOG_WARN("Built-in image decoder can't get the header info");
+ return LV_IMG_DECODER_OPEN_FAIL;
+ }
+
+ /*Open the file if it's a file*/
+ if(decoder_src_type == LV_IMG_SRC_FILE) {
+#if USE_LV_FILESYSTEM
+ lv_fs_res_t res = lv_fs_open(&decoder_file, src, LV_FS_MODE_RD);
+ if(res != LV_FS_RES_OK) {
+ LV_LOG_WARN("Built-in image decoder can't open the file");
+ return LV_IMG_DECODER_OPEN_FAIL;
+ }
+#else
+ LV_LOG_WARN("Image built-in decoder can read file because USE_LV_FILESYSTEM = 0");
+ return LV_IMG_DECODER_OPEN_FAIL;
+#endif
+ }
+
+
+ /*Process the different color formats*/
+ lv_img_cf_t cf = decoder_header.cf;
+ if(cf == LV_IMG_CF_TRUE_COLOR ||
+ cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
+ cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
+ if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
+ /*In case of uncompressed formats if the image stored in the ROM/RAM simply give it's pointer*/
+ return ((lv_img_dsc_t *)decoder_src)->data;
+ } else {
+ /*If it's file it need to be read line by line later*/
+ return NULL;
+ }
+ } else if(cf == LV_IMG_CF_INDEXED_1BIT ||
+ cf == LV_IMG_CF_INDEXED_2BIT ||
+ cf == LV_IMG_CF_INDEXED_4BIT ||
+ cf == LV_IMG_CF_INDEXED_8BIT) {
+
+#if LV_IMG_CF_INDEXED
+#if USE_LV_FILESYSTEM
+ lv_color32_t palette_file[256];
+#endif
+
+ lv_color32_t * palette_p = NULL;
+ uint8_t px_size = lv_img_color_format_get_px_size(cf);
+ uint32_t palette_size = 1 << px_size;
+
+ if(decoder_src_type == LV_IMG_SRC_FILE) {
+ /*Read the palette from file*/
+#if USE_LV_FILESYSTEM
+ lv_fs_seek(&decoder_file, 4); /*Skip the header*/
+ lv_fs_read(&decoder_file, palette_file, palette_size * sizeof(lv_color32_t), NULL);
+ palette_p = palette_file;
+#else
+ LV_LOG_WARN("Image built-in decoder can read the palette because USE_LV_FILESYSTEM = 0");
+ return LV_IMG_DECODER_OPEN_FAIL;
+#endif
+ } else {
+ /*The palette begins in the beginning of the image data. Just point to it.*/
+ palette_p = (lv_color32_t *)((lv_img_dsc_t *)decoder_src)->data;
+ }
+
+ uint32_t i;
+ for(i = 0; i < palette_size; i++) {
+ decoder_index_map[i] = LV_COLOR_MAKE(palette_p[i].red, palette_p[i].green, palette_p[i].blue);
+ }
+ return NULL;
+#else
+ LV_LOG_WARN("Indexed (palette) images are not enabled in lv_conf.h. See LV_IMG_CF_INDEXED");
+ return LV_IMG_DECODER_OPEN_FAIL;
+#endif
+ } else if(cf == LV_IMG_CF_ALPHA_1BIT ||
+ cf == LV_IMG_CF_ALPHA_2BIT ||
+ cf == LV_IMG_CF_ALPHA_4BIT ||
+ cf == LV_IMG_CF_ALPHA_8BIT) {
+#if LV_IMG_CF_ALPHA
+ return NULL; /*Nothing to process*/
+#else
+ LV_LOG_WARN("Alpha indexed images are not enabled in lv_conf.h. See LV_IMG_CF_ALPHA");
+ return LV_IMG_DECODER_OPEN_FAIL;
+#endif
+ } else {
+ LV_LOG_WARN("Image decoder open: unknown color format")
+ return LV_IMG_DECODER_OPEN_FAIL;
+ }
+}
+
+
+static lv_res_t lv_img_decoder_read_line(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
+{
+ /*Try to read the line with the custom functions*/
+ if(decoder_custom) {
+ if(lv_img_decoder_read_line_custom) {
+ lv_res_t custom_res;
+ custom_res = lv_img_decoder_read_line_custom(x, y, len, buf);
+ return custom_res;
+ } else {
+ LV_LOG_WARN("Image open with custom decoder but read not supported")
+ }
+ return LV_RES_INV; /*It"s an error if not returned earlier*/
+ }
+
+ if(decoder_src_type == LV_IMG_SRC_FILE) {
+#if USE_LV_FILESYSTEM
+ uint8_t px_size = lv_img_color_format_get_px_size(decoder_header.cf);
+
+ lv_fs_res_t res;
+
+ if(decoder_header.cf == LV_IMG_CF_TRUE_COLOR ||
+ decoder_header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
+ decoder_header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
+ uint32_t pos = ((y * decoder_header.w + x) * px_size) >> 3;
+ pos += 4; /*Skip the header*/
+ res = lv_fs_seek(&decoder_file, pos);
+ if(res != LV_FS_RES_OK) {
+ LV_LOG_WARN("Built-in image decoder seek failed");
+ return false;
+ }
+ uint32_t btr = len * (px_size >> 3);
+ uint32_t br = 0;
+ lv_fs_read(&decoder_file, buf, btr, &br);
+ if(res != LV_FS_RES_OK || btr != br) {
+ LV_LOG_WARN("Built-in image decoder read failed");
+ return false;
+ }
+ } else if(decoder_header.cf == LV_IMG_CF_ALPHA_1BIT ||
+ decoder_header.cf == LV_IMG_CF_ALPHA_2BIT ||
+ decoder_header.cf == LV_IMG_CF_ALPHA_4BIT ||
+ decoder_header.cf == LV_IMG_CF_ALPHA_8BIT) {
+
+ lv_img_built_in_decoder_line_alpha(x, y, len, buf);
+ } else if(decoder_header.cf == LV_IMG_CF_INDEXED_1BIT ||
+ decoder_header.cf == LV_IMG_CF_INDEXED_2BIT ||
+ decoder_header.cf == LV_IMG_CF_INDEXED_4BIT ||
+ decoder_header.cf == LV_IMG_CF_INDEXED_8BIT) {
+ lv_img_built_in_decoder_line_indexed(x, y, len, buf);
+ } else {
+ LV_LOG_WARN("Built-in image decoder read not supports the color format");
+ return false;
+ }
+#else
+ LV_LOG_WARN("Image built-in decoder can't read file because USE_LV_FILESYSTEM = 0");
+ return false;
+#endif
+ } else if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = decoder_src;
+
+ if(img_dsc->header.cf == LV_IMG_CF_ALPHA_1BIT ||
+ img_dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
+ img_dsc->header.cf == LV_IMG_CF_ALPHA_4BIT ||
+ img_dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
+ lv_img_built_in_decoder_line_alpha(x, y, len, buf);
+ } else if(img_dsc->header.cf == LV_IMG_CF_INDEXED_1BIT ||
+ img_dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
+ img_dsc->header.cf == LV_IMG_CF_INDEXED_4BIT ||
+ img_dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
+ lv_img_built_in_decoder_line_indexed(x, y, len, buf);
+ } else {
+ LV_LOG_WARN("Built-in image decoder not supports the color format");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void lv_img_decoder_close(void)
+{
+ /*Try to close with the custom functions*/
+ if(decoder_custom) {
+ if(lv_img_decoder_close_custom) lv_img_decoder_close_custom();
+ return;
+ }
+
+ /*It was opened with built-in decoder*/
+ if(decoder_src) {
+#if USE_LV_FILESYSTEM
+ if(decoder_src_type == LV_IMG_SRC_FILE) {
+ lv_fs_close(&decoder_file);
+ }
+#endif
+ decoder_src_type = LV_IMG_SRC_UNKNOWN;
+ decoder_src = NULL;
+ }
+}
+
+static lv_res_t lv_img_built_in_decoder_line_alpha(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
+{
+
+#if LV_IMG_CF_ALPHA
+ const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
+ const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
+ const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
+ 68, 85, 102, 119,
+ 136, 153, 170, 187,
+ 204, 221, 238, 255
+ };
+
+ /*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
+ lv_color_t bg_color = decoder_style->image.color;
+ lv_coord_t i;
+ for(i = 0; i < len; i++) {
+#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
+#elif LV_COLOR_DEPTH == 16
+ /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
+#elif LV_COLOR_DEPTH == 32
+ *((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
+#else
+#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
+#endif
+ }
+
+ const lv_opa_t * opa_table = NULL;
+ uint8_t px_size = lv_img_color_format_get_px_size(decoder_header.cf);
+ uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
+
+ lv_coord_t w = 0;
+ uint32_t ofs = 0;
+ int8_t pos = 0;
+ switch(decoder_header.cf) {
+ case LV_IMG_CF_ALPHA_1BIT:
+ w = (decoder_header.w >> 3); /*E.g. w = 20 -> w = 2 + 1*/
+ if(decoder_header.w & 0x7) w++;
+ ofs += w * y + (x >> 3); /*First pixel*/
+ pos = 7 - (x & 0x7);
+ opa_table = alpha1_opa_table;
+ break;
+ case LV_IMG_CF_ALPHA_2BIT:
+ w = (decoder_header.w >> 2); /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
+ if(decoder_header.w & 0x3) w++;
+ ofs += w * y + (x >> 2); /*First pixel*/
+ pos = 6 - ((x & 0x3) * 2);
+ opa_table = alpha2_opa_table;
+ break;
+ case LV_IMG_CF_ALPHA_4BIT:
+ w = (decoder_header.w >> 1); /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
+ if(decoder_header.w & 0x1) w++;
+ ofs += w * y + (x >> 1); /*First pixel*/
+ pos = 4 - ((x & 0x1) * 4);
+ opa_table = alpha4_opa_table;
+ break;
+ case LV_IMG_CF_ALPHA_8BIT:
+ w = decoder_header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
+ ofs += w * y + x; /*First pixel*/
+ pos = 0;
+ break;
+ }
+
+#if USE_LV_FILESYSTEM
+# if LV_COMPILER_VLA_SUPPORTED
+ uint8_t fs_buf[w];
+# else
+ uint8_t fs_buf[LV_HOR_RES];
+# endif
+#endif
+ const uint8_t * data_tmp = NULL;
+ if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = decoder_src;
+ data_tmp = img_dsc->data + ofs;
+ } else {
+#if USE_LV_FILESYSTEM
+ lv_fs_seek(&decoder_file, ofs + 4); /*+4 to skip the header*/
+ lv_fs_read(&decoder_file, fs_buf, w, NULL);
+ data_tmp = fs_buf;
+#else
+ LV_LOG_WARN("Image built-in alpha line reader can't read file because USE_LV_FILESYSTEM = 0");
+ data_tmp = NULL; /*To avoid warnings*/
+ return LV_RES_INV;
+#endif
+ }
+
+
+ uint8_t byte_act = 0;
+ uint8_t val_act;
+ for(i = 0; i < len; i ++) {
+ val_act = (data_tmp[byte_act] & (mask << pos)) >> pos;
+
+ buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
+ decoder_header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
+
+ pos -= px_size;
+ if(pos < 0) {
+ pos = 8 - px_size;
+ data_tmp++;
+ }
+ }
+
+ return LV_RES_OK;
+
+#else
+ LV_LOG_WARN("Image built-in alpha line reader failed because LV_IMG_CF_ALPHA is 0 in lv_conf.h");
+ return LV_RES_INV;
+#endif
+}
+
+static lv_res_t lv_img_built_in_decoder_line_indexed(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
+{
+
+#if LV_IMG_CF_INDEXED
+ uint8_t px_size = lv_img_color_format_get_px_size(decoder_header.cf);
+ uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
+
+ lv_coord_t w = 0;
+ int8_t pos = 0;
+ uint32_t ofs = 0;
+ switch(decoder_header.cf) {
+ case LV_IMG_CF_INDEXED_1BIT:
+ w = (decoder_header.w >> 3); /*E.g. w = 20 -> w = 2 + 1*/
+ if(decoder_header.w & 0x7) w++;
+ ofs += w * y + (x >> 3); /*First pixel*/
+ ofs += 8; /*Skip the palette*/
+ pos = 7 - (x & 0x7);
+ break;
+ case LV_IMG_CF_INDEXED_2BIT:
+ w = (decoder_header.w >> 2); /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
+ if(decoder_header.w & 0x3) w++;
+ ofs += w * y + (x >> 2); /*First pixel*/
+ ofs += 16; /*Skip the palette*/
+ pos = 6 - ((x & 0x3) * 2);
+ break;
+ case LV_IMG_CF_INDEXED_4BIT:
+ w = (decoder_header.w >> 1); /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
+ if(decoder_header.w & 0x1) w++;
+ ofs += w * y + (x >> 1); /*First pixel*/
+ ofs += 64; /*Skip the palette*/
+ pos = 4 - ((x & 0x1) * 4);
+ break;
+ case LV_IMG_CF_INDEXED_8BIT:
+ w = decoder_header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
+ ofs += w * y + x; /*First pixel*/
+ ofs += 1024; /*Skip the palette*/
+ pos = 0;
+ break;
+ }
+
+#if USE_LV_FILESYSTEM
+# if LV_COMPILER_VLA_SUPPORTED
+ uint8_t fs_buf[w];
+# else
+ uint8_t fs_buf[LV_HOR_RES];
+# endif
+#endif
+ const uint8_t * data_tmp = NULL;
+ if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
+ const lv_img_dsc_t * img_dsc = decoder_src;
+ data_tmp = img_dsc->data + ofs;
+ } else {
+#if USE_LV_FILESYSTEM
+ lv_fs_seek(&decoder_file, ofs + 4); /*+4 to skip the header*/
+ lv_fs_read(&decoder_file, fs_buf, w, NULL);
+ data_tmp = fs_buf;
+#else
+ LV_LOG_WARN("Image built-in indexed line reader can't read file because USE_LV_FILESYSTEM = 0");
+ data_tmp = NULL; /*To avoid warnings*/
+ return LV_RES_INV;
+#endif
+ }
+
+ uint8_t byte_act = 0;
+ uint8_t val_act;
+ lv_coord_t i;
+ lv_color_t * cbuf = (lv_color_t *) buf;
+ for(i = 0; i < len; i ++) {
+ val_act = (data_tmp[byte_act] & (mask << pos)) >> pos;
+ cbuf[i] = decoder_index_map[val_act];
+
+ pos -= px_size;
+ if(pos < 0) {
+ pos = 8 - px_size;
+ data_tmp++;
+ }
+ }
+
+ return LV_RES_OK;
+#else
+ LV_LOG_WARN("Image built-in indexed line reader failed because LV_IMG_CF_INDEXED is 0 in lv_conf.h");
+ return LV_RES_INV;
+#endif
+}
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_img.h b/bdk/libs/lvgl/lv_draw/lv_draw_img.h
new file mode 100644
index 0000000..31ed827
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_img.h
@@ -0,0 +1,167 @@
+/**
+ * @file lv_draw_img.h
+ *
+ */
+
+#ifndef LV_DRAW_IMG_H
+#define LV_DRAW_IMG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+#include "../lv_core/lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_IMG_DECODER_OPEN_FAIL ((void*)(-1))
+
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_img_t;
+
+typedef struct {
+
+ /* The first 8 bit is very important to distinguish the different source types.
+ * For more info see `lv_img_get_src_type()` in lv_img.c */
+ uint32_t cf :5; /* Color format: See `lv_img_color_format_t`*/
+ uint32_t always_zero :3; /*It the upper bits of the first byte. Always zero to look like a non-printable character*/
+
+ uint32_t reserved :2; /*Reserved to be used later*/
+
+ uint32_t w:11; /*Width of the image map*/
+ uint32_t h:11; /*Height of the image map*/
+} lv_img_header_t;
+
+/*Image color format*/
+enum {
+ LV_IMG_CF_UNKOWN = 0,
+
+ LV_IMG_CF_RAW, /*Contains the file as it is. Needs custom decoder function*/
+ LV_IMG_CF_RAW_ALPHA, /*Contains the file as it is. The image has alpha. Needs custom decoder function*/
+ LV_IMG_CF_RAW_CHROMA_KEYED, /*Contains the file as it is. The image is chroma keyed. Needs custom decoder function*/
+
+ LV_IMG_CF_TRUE_COLOR, /*Color format and depth should match with LV_COLOR settings*/
+ LV_IMG_CF_TRUE_COLOR_ALPHA, /*Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
+ LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /*Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels will be transparent*/
+
+ LV_IMG_CF_INDEXED_1BIT, /*Can have 2 different colors in a palette (always chroma keyed)*/
+ LV_IMG_CF_INDEXED_2BIT, /*Can have 4 different colors in a palette (always chroma keyed)*/
+ LV_IMG_CF_INDEXED_4BIT, /*Can have 16 different colors in a palette (always chroma keyed)*/
+ LV_IMG_CF_INDEXED_8BIT, /*Can have 256 different colors in a palette (always chroma keyed)*/
+
+ LV_IMG_CF_ALPHA_1BIT, /*Can have one color and it can be drawn or not*/
+ LV_IMG_CF_ALPHA_2BIT, /*Can have one color but 4 different alpha value*/
+ LV_IMG_CF_ALPHA_4BIT, /*Can have one color but 16 different alpha value*/
+ LV_IMG_CF_ALPHA_8BIT, /*Can have one color but 256 different alpha value*/
+};
+typedef uint8_t lv_img_cf_t;
+
+/* Image header it is compatible with
+ * the result image converter utility*/
+typedef struct
+{
+ lv_img_header_t header;
+ uint32_t data_size;
+ const uint8_t * data;
+} lv_img_dsc_t;
+
+/* Decoder function definitions */
+
+
+/**
+ * Get info from an image and store in the `header`
+ * @param src the image source. Can be a pointer to a C array or a file name (Use `lv_img_src_get_type` to determine the type)
+ * @param header store the info here
+ * @return LV_RES_OK: info written correctly; LV_RES_INV: failed
+ */
+typedef lv_res_t (*lv_img_decoder_info_f_t)(const void * src, lv_img_header_t * header);
+
+/**
+ * Open an image for decoding. Prepare it as it is required to read it later
+ * @param src the image source. Can be a pointer to a C array or a file name (Use `lv_img_src_get_type` to determine the type)
+ * @param style the style of image (maybe it will be required to determine a color or something)
+ * @return there are 3 possible return values:
+ * 1) buffer with the decoded image
+ * 2) if can decode the whole image NULL. decoder_read_line will be called to read the image line-by-line
+ * 3) LV_IMG_DECODER_OPEN_FAIL if the image format is unknown to the decoder or an error occurred
+ */
+typedef const uint8_t * (*lv_img_decoder_open_f_t)(const void * src, const lv_style_t * style);
+
+/**
+ * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
+ * Required only if the "open" function can't return with the whole decoded pixel array.
+ * @param x start x coordinate
+ * @param y startt y coordinate
+ * @param len number of pixels to decode
+ * @param buf a buffer to store the decoded pixels
+ * @return LV_RES_OK: ok; LV_RES_INV: failed
+ */
+typedef lv_res_t (*lv_img_decoder_read_line_f_t)(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
+
+/**
+ * Close the pending decoding. Free resources etc.
+ */
+typedef void (*lv_img_decoder_close_f_t)(void);
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Draw an image
+ * @param coords the coordinates of the image
+ * @param mask the image will be drawn only in this area
+ * @param src pointer to a lv_color_t array which contains the pixels of the image
+ * @param style style of the image
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask,
+ const void * src, const lv_style_t * style, lv_opa_t opa_scale);
+
+
+/**
+ * Get the type of an image source
+ * @param src pointer to an image source:
+ * - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
+ * - a path to a file (e.g. "S:/folder/image.bin")
+ * - or a symbol (e.g. SYMBOL_CLOSE)
+ * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKOWN
+ */
+lv_img_src_t lv_img_src_get_type(const void * src);
+
+/**
+ * Set custom decoder functions. See the typdefs of the function typed above for more info about them
+ * @param info_fp info get function
+ * @param open_fp open function
+ * @param read_fp read line function
+ * @param close_fp clode function
+ */
+void lv_img_decoder_set_custom(lv_img_decoder_info_f_t info_fp, lv_img_decoder_open_f_t open_fp,
+ lv_img_decoder_read_line_f_t read_fp, lv_img_decoder_close_f_t close_fp);
+
+lv_res_t lv_img_dsc_get_info(const char * src, lv_img_header_t * header);
+
+uint8_t lv_img_color_format_get_px_size(lv_img_cf_t cf);
+
+bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf);
+
+bool lv_img_color_format_has_alpha(lv_img_cf_t cf);
+
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_TEMPL_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_label.c b/bdk/libs/lvgl/lv_draw/lv_draw_label.c
new file mode 100644
index 0000000..0744ff1
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_label.c
@@ -0,0 +1,264 @@
+/**
+ * @file lv_draw_label.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_label.h"
+#include "lv_draw_rbasic.h"
+#include "../lv_misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define LABEL_RECOLOR_PAR_LENGTH 6
+
+/**********************
+ * TYPEDEFS
+ **********************/
+enum {
+ CMD_STATE_WAIT,
+ CMD_STATE_PAR,
+ CMD_STATE_IN,
+};
+typedef uint8_t cmd_state_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static uint8_t hex_char_to_num(char hex);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Write a text
+ * @param coords coordinates of the label
+ * @param mask the label will be drawn only in this area
+ * @param style pointer to a style
+ * @param opa_scale scale down all opacities by the factor
+ * @param txt 0 terminated text to write
+ * @param flag settings for the text from 'txt_flag_t' enum
+ * @param offset text offset in x and y direction (NULL if unused)
+ *
+ */
+void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale,
+ const char * txt, lv_txt_flag_t flag, lv_point_t * offset)
+{
+ const lv_font_t * font = style->text.font;
+ lv_coord_t w;
+ if((flag & LV_TXT_FLAG_EXPAND) == 0) {
+ /*Normally use the label's width as width*/
+ w = lv_area_get_width(coords);
+ } else {
+ /*If EXAPND is enabled then not limit the text's width to the object's width*/
+ lv_point_t p;
+ lv_txt_get_size(&p, txt, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag);
+ w = p.x;
+ }
+
+ lv_coord_t line_height = lv_font_get_height(font) + style->text.line_space;
+
+
+ /*Init variables for the first line*/
+ lv_coord_t line_width = 0;
+ lv_point_t pos;
+ pos.x = coords->x1;
+ pos.y = coords->y1;
+
+ lv_coord_t x_ofs = 0;
+ lv_coord_t y_ofs = 0;
+ if(offset != NULL) {
+ x_ofs = offset->x;
+ y_ofs = offset->y;
+ pos.y += y_ofs;
+ }
+
+ uint32_t line_start = 0;
+ uint32_t line_end = lv_txt_get_next_line(txt, font, style->text.letter_space, w, flag);
+
+ /*Go the first visible line*/
+ while(pos.y + line_height < mask->y1) {
+ /*Go to next line*/
+ line_start = line_end;
+ line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
+ pos.y += line_height;
+
+ if(txt[line_start] == '\0') return;
+ }
+
+ /*Align to middle*/
+ if(flag & LV_TXT_FLAG_CENTER) {
+ line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
+ font, style->text.letter_space, flag);
+
+ pos.x += (lv_area_get_width(coords) - line_width) / 2;
+
+ }
+ /*Align to the right*/
+ else if(flag & LV_TXT_FLAG_RIGHT) {
+ line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
+ font, style->text.letter_space, flag);
+ pos.x += lv_area_get_width(coords) - line_width;
+ }
+
+
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->text.opa : (uint16_t)((uint16_t) style->text.opa * opa_scale) >> 8;
+
+ cmd_state_t cmd_state = CMD_STATE_WAIT;
+ uint32_t i;
+ uint16_t par_start = 0;
+ lv_color_t recolor;
+ lv_coord_t letter_w;
+
+ /*Real draw need a background color for higher bpp letter*/
+#if LV_VDB_SIZE == 0
+ lv_rletter_set_background(style->body.main_color);
+#endif
+
+
+ /*Write out all lines*/
+ while(txt[line_start] != '\0') {
+ if(offset != NULL) {
+ pos.x += x_ofs;
+ }
+ /*Write all letter of a line*/
+ cmd_state = CMD_STATE_WAIT;
+ i = line_start;
+ uint32_t letter;
+ while(i < line_end) {
+ letter = lv_txt_encoded_next(txt, &i);
+
+ /*Handle the re-color command*/
+ if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
+ if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
+ if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
+ par_start = i;
+ cmd_state = CMD_STATE_PAR;
+ continue;
+ } else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char */
+ cmd_state = CMD_STATE_WAIT;
+ } else if(cmd_state == CMD_STATE_IN) { /*Command end */
+ cmd_state = CMD_STATE_WAIT;
+ continue;
+ }
+ }
+
+ /*Skip the color parameter and wait the space after it*/
+ if(cmd_state == CMD_STATE_PAR) {
+ if(letter == ' ') {
+ /*Get the parameter*/
+ if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
+ char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
+ memcpy(buf, &txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
+ buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
+ int r, g, b;
+ r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
+ g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
+ b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
+ recolor = LV_COLOR_MAKE(r, g, b);
+ } else {
+ recolor.full = style->text.color.full;
+ }
+ cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
+ }
+ continue;
+ }
+ }
+
+ lv_color_t color = style->text.color;
+
+ if(cmd_state == CMD_STATE_IN) color = recolor;
+
+ letter_fp(&pos, mask, font, letter, color, opa);
+ letter_w = lv_font_get_width(font, letter);
+
+ if(letter_w > 0){
+ pos.x += letter_w + style->text.letter_space;
+ }
+ }
+ /*Go to next line*/
+ line_start = line_end;
+ line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
+
+ pos.x = coords->x1;
+ /*Align to middle*/
+ if(flag & LV_TXT_FLAG_CENTER) {
+ line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
+ font, style->text.letter_space, flag);
+
+ pos.x += (lv_area_get_width(coords) - line_width) / 2;
+
+ }
+ /*Align to the right*/
+ else if(flag & LV_TXT_FLAG_RIGHT) {
+ line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
+ font, style->text.letter_space, flag);
+ pos.x += lv_area_get_width(coords) - line_width;
+ }
+
+ /*Go the next line position*/
+ pos.y += line_height;
+
+ if(pos.y > mask->y2) return;
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Convert a hexadecimal characters to a number (0..15)
+ * @param hex Pointer to a hexadecimal character (0..9, A..F)
+ * @return the numerical value of `hex` or 0 on error
+ */
+static uint8_t hex_char_to_num(char hex)
+{
+ uint8_t result = 0;
+
+ if(hex >= '0' && hex <= '9') {
+ result = hex - '0';
+ }
+ else {
+ if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
+
+ switch(hex) {
+ case 'A':
+ result = 10;
+ break;
+ case 'B':
+ result = 11;
+ break;
+ case 'C':
+ result = 12;
+ break;
+ case 'D':
+ result = 13;
+ break;
+ case 'E':
+ result = 14;
+ break;
+ case 'F':
+ result = 15;
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_label.h b/bdk/libs/lvgl/lv_draw/lv_draw_label.h
new file mode 100644
index 0000000..8798573
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_label.h
@@ -0,0 +1,53 @@
+/**
+ * @file lv_draw_label.h
+ *
+ */
+
+#ifndef LV_DRAW_LABEL_H
+#define LV_DRAW_LABEL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Write a text
+ * @param coords coordinates of the label
+ * @param mask the label will be drawn only in this area
+ * @param style pointer to a style
+ * @param opa_scale scale down all opacities by the factor
+ * @param txt 0 terminated text to write
+ * @param flag settings for the text from 'txt_flag_t' enum
+ * @param offset text offset in x and y direction (NULL if unused)
+ *
+ */
+void lv_draw_label(const lv_area_t * coords,const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale,
+ const char * txt, lv_txt_flag_t flag, lv_point_t * offset);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_LABEL_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_line.c b/bdk/libs/lvgl/lv_draw/lv_draw_line.c
new file mode 100644
index 0000000..6e72ff4
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_line.c
@@ -0,0 +1,607 @@
+/**
+ * @file lv_draw_line.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include
+#include "lv_draw.h"
+#include "../lv_misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#if LV_COMPILER_VLA_SUPPORTED == 0
+#define LINE_MAX_WIDTH 64
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_point_t p1;
+ lv_point_t p2;
+ lv_point_t p_act;
+ lv_coord_t dx;
+ lv_coord_t sx; /*-1: x1 < x2; 1: x2 >= x1*/
+ lv_coord_t dy;
+ lv_coord_t sy; /*-1: y1 < y2; 1: y2 >= y1*/
+ lv_coord_t err;
+ lv_coord_t e2;
+ bool hor; /*Rather horizontal or vertical*/
+} line_draw_t;
+
+typedef struct {
+ lv_coord_t width;
+ lv_coord_t width_1;
+ lv_coord_t width_half;
+} line_width_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void line_draw_skew(line_draw_t * main_line, bool dir_ori, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2);
+static bool line_next(line_draw_t * line);
+static bool line_next_y(line_draw_t * line);
+static bool line_next_x(line_draw_t * line);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Draw a line
+ * @param point1 first point of the line
+ * @param point2 second point of the line
+ * @param mask the line will be drawn only on this area
+ * @param style pointer to a line's style
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask,
+ const lv_style_t * style, lv_opa_t opa_scale)
+{
+
+ if(style->line.width == 0) return;
+ if(point1->x == point2->x && point1->y == point2->y) return;
+
+ line_draw_t main_line;
+ lv_point_t p1;
+ lv_point_t p2;
+
+ /*If the line if rather vertical then be sure y1 < y2 else x1 < x2*/
+
+ if(LV_MATH_ABS(point1->x - point2->x) > LV_MATH_ABS(point1->y - point2->y)) {
+
+ /*Steps less in y then x -> rather horizontal*/
+ if(point1->x < point2->x) {
+ p1.x = point1->x;
+ p1.y = point1->y;
+ p2.x = point2->x;
+ p2.y = point2->y;
+ } else {
+ p1.x = point2->x;
+ p1.y = point2->y;
+ p2.x = point1->x;
+ p2.y = point1->y;
+ }
+ } else {
+ /*Steps less in x then y -> rather vertical*/
+ if(point1->y < point2->y) {
+ p1.x = point1->x;
+ p1.y = point1->y;
+ p2.x = point2->x;
+ p2.y = point2->y;
+ } else {
+ p1.x = point2->x;
+ p1.y = point2->y;
+ p2.x = point1->x;
+ p2.y = point1->y;
+ }
+ }
+
+ line_init(&main_line, &p1, &p2);
+
+
+ /*Special case draw a horizontal line*/
+ if(main_line.p1.y == main_line.p2.y) {
+ line_draw_hor(&main_line, mask, style, opa_scale);
+ }
+ /*Special case draw a vertical line*/
+ else if(main_line.p1.x == main_line.p2.x) {
+ line_draw_ver(&main_line, mask, style, opa_scale);
+ }
+ /*Arbitrary skew line*/
+ else {
+ bool dir_ori = false;
+#if LV_ANTIALIAS
+ lv_point_t p_tmp;
+
+ if(main_line.hor) {
+ if(main_line.p1.y < main_line.p2.y) {
+ dir_ori = true;
+ p_tmp.x = main_line.p2.x;
+ p_tmp.y = main_line.p2.y - 1;
+ line_init(&main_line, &p1, &p_tmp);
+ main_line.sy = LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
+ }
+ else if(main_line.p1.y > main_line.p2.y) {
+ dir_ori = false;
+ p_tmp.x = main_line.p2.x;
+ p_tmp.y = main_line.p2.y + 1;
+ line_init(&main_line, &p1, &p_tmp);
+ main_line.sy = -LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
+ }
+ }
+ else {
+ if(main_line.p1.x < main_line.p2.x) {
+ dir_ori = true;
+ p_tmp.x = main_line.p2.x - 1;
+ p_tmp.y = main_line.p2.y;
+ line_init(&main_line, &p1, &p_tmp);
+ main_line.sx = LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
+ }
+ else if(main_line.p1.x > main_line.p2.x) {
+ dir_ori = false;
+ p_tmp.x = main_line.p2.x + 1;
+ p_tmp.y = main_line.p2.y;
+ line_init(&main_line, &p1, &p_tmp);
+ main_line.sx = -LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
+ }
+ }
+#endif
+ line_draw_skew(&main_line, dir_ori, mask, style, opa_scale);
+ }
+}
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+
+static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ lv_coord_t width = style->line.width - 1;
+ lv_coord_t width_half = width >> 1;
+ lv_coord_t width_1 = width & 0x1;
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
+
+ lv_area_t act_area;
+ act_area.x1 = main_line->p1.x;
+ act_area.x2 = main_line->p2.x;
+ act_area.y1 = main_line->p1.y - width_half - width_1;
+ act_area.y2 = main_line->p2.y + width_half ;
+
+ lv_area_t draw_area;
+ draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
+ draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
+ draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
+ draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
+ fill_fp(&draw_area, mask, style->line.color, opa);
+}
+
+static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ lv_coord_t width = style->line.width - 1;
+ lv_coord_t width_half = width >> 1;
+ lv_coord_t width_1 = width & 0x1;
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
+
+
+ lv_area_t act_area;
+ act_area.x1 = main_line->p1.x - width_half;
+ act_area.x2 = main_line->p2.x + width_half + width_1;
+ act_area.y1 = main_line->p1.y;
+ act_area.y2 = main_line->p2.y;
+
+ lv_area_t draw_area;
+ draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
+ draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
+ draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
+ draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
+ fill_fp(&draw_area, mask, style->line.color, opa);
+}
+
+static void line_draw_skew(line_draw_t * main_line, bool dir_ori, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
+
+ lv_point_t vect_main, vect_norm;
+ vect_main.x = main_line->p2.x - main_line->p1.x;
+ vect_main.y = main_line->p2.y - main_line->p1.y;
+
+ if(main_line->hor) {
+ if(main_line->p1.y < main_line->p2.y + dir_ori) {
+ vect_norm.x = - vect_main.y;
+ vect_norm.y = vect_main.x;
+ } else {
+ vect_norm.x = vect_main.y;
+ vect_norm.y = -vect_main.x;
+ }
+ } else {
+ if(main_line->p1.x < main_line->p2.x + dir_ori) {
+ vect_norm.x = vect_main.y;
+ vect_norm.y = - vect_main.x;
+ } else {
+ vect_norm.x = - vect_main.y;
+ vect_norm.y = vect_main.x;
+ }
+ }
+
+ /* In case of a short but tick line the perpendicular ending is longer then the real line.
+ * it would break the calculations so make the normal vector larger*/
+ vect_norm.x = vect_norm.x << 4;
+ vect_norm.y = vect_norm.y << 4;
+
+ lv_coord_t width;
+ width = style->line.width;
+
+ /* The pattern stores the points of the line ending. It has the good direction and length.
+ * The worth case is the 45° line where pattern can have 1.41 x `width` points*/
+#if LV_COMPILER_VLA_SUPPORTED
+ lv_point_t pattern[width * 2];
+#else
+ lv_point_t pattern[LINE_MAX_WIDTH];
+#endif
+ lv_coord_t i = 0;
+
+ /*Create a perpendicular pattern (a small line)*/
+ if(width != 0) {
+ line_draw_t pattern_line;
+ lv_point_t p0 = {0, 0};
+ line_init(&pattern_line, &p0, &vect_norm);
+
+ uint32_t width_sqr = width * width;
+ /* Run for a lot of times. Meanwhile the real width will be determined as well */
+ for(i = 0; i < (lv_coord_t)sizeof(pattern); i ++) {
+ pattern[i].x = pattern_line.p_act.x;
+ pattern[i].y = pattern_line.p_act.y;
+
+ /*Finish the pattern line if it's length equal to the desired width (Use Pythagoras theorem)*/
+ uint32_t sqr = pattern_line.p_act.x * pattern_line.p_act.x + pattern_line.p_act.y * pattern_line.p_act.y;
+ if(sqr >= width_sqr) {
+ width = i;
+#if LV_ANTIALIAS
+ width--;
+#endif
+ break;
+ }
+
+ line_next(&pattern_line);
+ }
+ }
+
+#if LV_ANTIALIAS
+ lv_coord_t width_safe = width;
+ if(width == 0) width_safe = 1;
+
+ lv_coord_t aa_last_corner;
+ aa_last_corner = 0;
+#endif
+
+ lv_coord_t x_center_ofs = 0;
+ lv_coord_t y_center_ofs = 0;
+
+ if(width != 0) {
+ x_center_ofs = pattern[width - 1].x / 2;
+ y_center_ofs = pattern[width - 1].y / 2;
+ }
+ else {
+ if(main_line->hor && main_line->p1.y >= main_line->p2.y + dir_ori) pattern[0].y --;
+ if(!main_line->hor && main_line->p1.x >= main_line->p2.x + dir_ori) pattern[0].x --;
+ }
+
+ /* Make the coordinates relative to the center */
+ for(i = 0; i < width; i++) {
+ pattern[i].x -= x_center_ofs;
+ pattern[i].y -= y_center_ofs;
+#if LV_ANTIALIAS
+ if(i != 0) {
+ if(main_line->hor) {
+ if(pattern[i - 1].x != pattern[i].x) {
+ lv_coord_t seg_w = pattern[i].y - pattern[aa_last_corner].y;
+ if(main_line->sy < 0) {
+ lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y + seg_w + 1,
+ seg_w, mask, style->line.color, opa);
+
+ lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y + seg_w + 1,
+ -seg_w, mask, style->line.color, opa);
+ } else {
+ lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y,
+ seg_w, mask, style->line.color, opa);
+
+ lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y,
+ -seg_w, mask, style->line.color, opa);
+ }
+ aa_last_corner = i;
+ }
+ } else {
+ if(pattern[i - 1].y != pattern[i].y) {
+ lv_coord_t seg_w = pattern[i].x - pattern[aa_last_corner].x;
+ if(main_line->sx < 0) {
+ lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w + 1, main_line->p1.y + pattern[aa_last_corner].y - 1,
+ seg_w, mask, style->line.color, opa);
+
+ lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w + 1, main_line->p2.y + pattern[aa_last_corner].y + 1,
+ -seg_w, mask, style->line.color, opa);
+ } else {
+ lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x, main_line->p1.y + pattern[aa_last_corner].y - 1,
+ seg_w, mask, style->line.color, opa);
+
+ lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x, main_line->p2.y + pattern[aa_last_corner].y + 1,
+ -seg_w, mask, style->line.color, opa);
+ }
+ aa_last_corner = i;
+ }
+ }
+
+ }
+#endif
+ }
+
+
+
+#if LV_ANTIALIAS
+ /*Add the last part of anti-aliasing for the perpendicular ending*/
+ if(width != 0) { /*Due to rounding error with very thin lines it looks ugly*/
+ if(main_line->hor) {
+ lv_coord_t seg_w = pattern[width_safe - 1].y - pattern[aa_last_corner].y;
+ if(main_line->sy < 0) {
+ lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y + seg_w,
+ seg_w + main_line->sy, mask, style->line.color, opa);
+
+ lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y + seg_w,
+ -(seg_w + main_line->sy), mask, style->line.color, opa);
+
+ } else {
+ lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y,
+ seg_w + main_line->sy, mask, style->line.color, opa);
+
+ lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y,
+ -(seg_w + main_line->sy), mask, style->line.color, opa);
+ }
+ } else {
+ lv_coord_t seg_w = pattern[width_safe - 1].x - pattern[aa_last_corner].x;
+ if(main_line->sx < 0) {
+ lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w, main_line->p1.y + pattern[aa_last_corner].y - 1,
+ seg_w + main_line->sx, mask, style->line.color, opa);
+
+ lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w, main_line->p2.y + pattern[aa_last_corner].y + 1,
+ -(seg_w + main_line->sx), mask, style->line.color, opa);
+
+ } else {
+ lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x, main_line->p1.y + pattern[aa_last_corner].y - 1,
+ seg_w + main_line->sx, mask, style->line.color, opa);
+
+ lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x, main_line->p2.y + pattern[aa_last_corner].y + 1,
+ -(seg_w + main_line->sx), mask, style->line.color, opa);
+ }
+
+ }
+ }
+#endif
+
+#if LV_ANTIALIAS
+
+ /*Shift the anti aliasing on the edges (-1, 1 or 0 (zero only in case width == 0))*/
+ lv_coord_t aa_shift1;
+ lv_coord_t aa_shift2;
+
+ if(main_line->hor == false) {
+ if(main_line->sx < 0) {
+ aa_shift1 = -1;
+ aa_shift2 = width == 0 ? 0 : aa_shift1;
+ } else {
+ aa_shift2 = 1;
+ aa_shift1 = width == 0 ? 0 : aa_shift2;
+ }
+ } else {
+ if(main_line->sy < 0) {
+ aa_shift1 = -1;
+ aa_shift2 = width == 0 ? 0 : aa_shift1;
+ } else {
+ aa_shift2 = 1;
+ aa_shift1 = width == 0 ? 0 : aa_shift2;
+ }
+ }
+
+#endif
+
+ volatile lv_point_t prev_p;
+ prev_p.x = main_line->p1.x;
+ prev_p.y = main_line->p1.y;
+ lv_area_t draw_area;
+ bool first_run = true;
+
+ if(main_line->hor) {
+ while(line_next_y(main_line)) {
+ for(i = 0; i < width; i++) {
+ draw_area.x1 = prev_p.x + pattern[i].x;
+ draw_area.y1 = prev_p.y + pattern[i].y;
+ draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x - 1;
+ draw_area.y2 = draw_area.y1;
+ fill_fp(&draw_area, mask, style->line.color, opa);
+
+ /* Fill the gaps
+ * When stepping in y one pixel remains empty on every corner (don't do this on the first segment ) */
+ if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
+ px_fp(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
+ }
+ }
+
+#if LV_ANTIALIAS
+ lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
+ -(main_line->p_act.x - prev_p.x), mask, style->line.color, opa);
+ lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
+ main_line->p_act.x - prev_p.x, mask, style->line.color, opa);
+#endif
+
+ first_run = false;
+
+ prev_p.x = main_line->p_act.x;
+ prev_p.y = main_line->p_act.y;
+ }
+
+ for(i = 0; i < width; i++) {
+ draw_area.x1 = prev_p.x + pattern[i].x;
+ draw_area.y1 = prev_p.y + pattern[i].y;
+ draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x;
+ draw_area.y2 = draw_area.y1;
+ fill_fp(&draw_area, mask, style->line.color, opa);
+
+ /* Fill the gaps
+ * When stepping in y one pixel remains empty on every corner */
+ if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
+ px_fp(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
+ }
+ }
+
+#if LV_ANTIALIAS
+ lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
+ -(main_line->p_act.x - prev_p.x + 1), mask, style->line.color, opa);
+ lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
+ main_line->p_act.x - prev_p.x + 1, mask, style->line.color, opa);
+#endif
+ }
+ /*Rather a vertical line*/
+ else {
+
+ while(line_next_x(main_line)) {
+ for(i = 0; i < width; i++) {
+ draw_area.x1 = prev_p.x + pattern[i].x;
+ draw_area.y1 = prev_p.y + pattern[i].y;
+ draw_area.x2 = draw_area.x1;
+ draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y - 1;
+
+ fill_fp(&draw_area, mask, style->line.color, opa);
+
+ /* Fill the gaps
+ * When stepping in x one pixel remains empty on every corner (don't do this on the first segment ) */
+ if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
+ px_fp(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
+ }
+
+ }
+
+#if LV_ANTIALIAS
+ lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
+ -(main_line->p_act.y - prev_p.y), mask, style->line.color, opa);
+ lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
+ main_line->p_act.y - prev_p.y, mask, style->line.color, opa);
+#endif
+
+ first_run = false;
+
+ prev_p.x = main_line->p_act.x;
+ prev_p.y = main_line->p_act.y;
+ }
+
+ /*Draw the last part*/
+ for(i = 0; i < width; i++) {
+ draw_area.x1 = prev_p.x + pattern[i].x;
+ draw_area.y1 = prev_p.y + pattern[i].y;
+ draw_area.x2 = draw_area.x1;
+ draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y;
+
+ fill_fp(&draw_area, mask, style->line.color, opa);
+
+ /* Fill the gaps
+ * When stepping in x one pixel remains empty on every corner */
+ if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
+ px_fp(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
+ }
+ }
+
+#if LV_ANTIALIAS
+ lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
+ -(main_line->p_act.y - prev_p.y + 1), mask, style->line.color, opa);
+ lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
+ main_line->p_act.y - prev_p.y + 1, mask, style->line.color, opa);
+#endif
+ }
+}
+
+
+static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2)
+{
+ line->p1.x = p1->x;
+ line->p1.y = p1->y;
+ line->p2.x = p2->x;
+ line->p2.y = p2->y;
+
+ line->dx = LV_MATH_ABS(line->p2.x - line->p1.x);
+ line->sx = line->p1.x < line->p2.x ? 1 : -1;
+ line->dy = LV_MATH_ABS(line->p2.y - line->p1.y);
+ line->sy = line->p1.y < line->p2.y ? 1 : -1;
+ line->err = (line->dx > line->dy ? line->dx : -line->dy) / 2;
+ line->e2 = 0;
+ line->hor = line->dx > line->dy ? true : false; /*Rather horizontal or vertical*/
+
+ line->p_act.x = line->p1.x;
+ line->p_act.y = line->p1.y;
+}
+
+static bool line_next(line_draw_t * line)
+{
+ if(line->p_act.x == line->p2.x && line->p_act.y == line->p2.y) return false;
+ line->e2 = line->err;
+ if(line->e2 > -line->dx) {
+ line->err -= line->dy;
+ line->p_act.x += line->sx;
+ }
+ if(line->e2 < line->dy) {
+ line->err += line->dx;
+ line->p_act.y += line->sy;
+ }
+ return true;
+}
+
+/**
+ * Iterate until step one in y direction.
+ * @param line
+ * @return
+ */
+static bool line_next_y(line_draw_t * line)
+{
+ lv_coord_t last_y = line->p_act.y;
+
+ do {
+ if(!line_next(line)) return false;
+ } while(last_y == line->p_act.y);
+
+ return true;
+
+}
+
+/**
+ * Iterate until step one in x direction.
+ * @param line
+ * @return
+ */
+static bool line_next_x(line_draw_t * line)
+{
+ lv_coord_t last_x = line->p_act.x;
+
+ do {
+ if(!line_next(line)) return false;
+ } while(last_x == line->p_act.x);
+
+ return true;
+
+}
+
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_line.h b/bdk/libs/lvgl/lv_draw/lv_draw_line.h
new file mode 100644
index 0000000..4269475
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_line.h
@@ -0,0 +1,49 @@
+/**
+ * @file lv_draw_line.h
+ *
+ */
+
+#ifndef LV_DRAW_LINE_H
+#define LV_DRAW_LINE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Draw a line
+ * @param point1 first point of the line
+ * @param point2 second point of the line
+ * @param mask the line will be drawn only on this area
+ * @param style pointer to a line's style
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask,
+ const lv_style_t * style, lv_opa_t opa_scale);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_LINE_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_rbasic.c b/bdk/libs/lvgl/lv_draw/lv_draw_rbasic.c
new file mode 100644
index 0000000..369adf5
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_rbasic.c
@@ -0,0 +1,269 @@
+/**
+ * @file lv_draw_rbasic.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_rbasic.h"
+#if USE_LV_REAL_DRAW != 0
+
+#include "../lv_hal/lv_hal_disp.h"
+#include "../lv_misc/lv_font.h"
+#include "lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_color_t letter_bg_color;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Put a pixel to the display
+ * @param x x coordinate of the pixel
+ * @param y y coordinate of the pixel
+ * @param mask_p the pixel will be drawn on this area
+ * @param color color of the pixel
+ * @param opa opacity (ignored, only for compatibility with lv_vpx)
+ */
+void lv_rpx(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
+{
+ (void)opa; /*Opa is used only for compatibility with lv_vpx*/
+
+ lv_area_t area;
+ area.x1 = x;
+ area.y1 = y;
+ area.x2 = x;
+ area.y2 = y;
+
+ lv_rfill(&area, mask_p, color, LV_OPA_COVER);
+}
+
+/**
+ * Fill an area on the display
+ * @param cords_p coordinates of the area to fill
+ * @param mask_p fill only o this mask
+ * @param color fill color
+ * @param opa opacity (ignored, only for compatibility with lv_vfill)
+ */
+void lv_rfill(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ lv_color_t color, lv_opa_t opa)
+{
+
+ (void)opa; /*Opa is used only for compatibility with lv_vfill*/
+
+ lv_area_t masked_area;
+ bool union_ok = true;
+
+ if(mask_p != NULL) {
+ union_ok = lv_area_intersect(&masked_area, cords_p, mask_p);
+ } else {
+ lv_area_t scr_area;
+ lv_area_set(&scr_area, 0, 0, LV_HOR_RES - 1, LV_VER_RES - 1);
+ union_ok = lv_area_intersect(&masked_area, cords_p, &scr_area);
+ }
+
+ if(union_ok != false) {
+ lv_disp_fill(masked_area.x1, masked_area.y1, masked_area.x2, masked_area.y2, color);
+ }
+}
+
+/**
+ * Draw a letter to the display
+ * @param pos_p left-top coordinate of the latter
+ * @param mask_p the letter will be drawn only on this area
+ * @param font_p pointer to font
+ * @param letter a letter to draw
+ * @param color color of letter
+ * @param opa opacity of letter (ignored, only for compatibility with lv_vletter)
+ */
+void lv_rletter(const lv_point_t * pos_p, const lv_area_t * mask_p,
+ const lv_font_t * font_p, uint32_t letter,
+ lv_color_t color, lv_opa_t opa)
+{
+ (void)opa; /*Opa is used only for compatibility with lv_vletter*/
+
+ static uint8_t bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
+ static uint8_t bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
+ static uint8_t bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
+ 68, 85, 102, 119,
+ 136, 153, 170, 187,
+ 204, 221, 238, 255
+ };
+
+ if(font_p == NULL) return;
+
+ uint8_t letter_w = lv_font_get_width(font_p, letter);
+ uint8_t letter_h = lv_font_get_height(font_p);
+ uint8_t bpp = lv_font_get_bpp(font_p, letter); /*Bit per pixel (1,2, 4 or 8)*/
+ uint8_t * bpp_opa_table;
+ uint8_t mask_init;
+ uint8_t mask;
+
+ switch(bpp) {
+ case 1:
+ bpp_opa_table = bpp1_opa_table;
+ mask_init = 0x80;
+ break;
+ case 2:
+ bpp_opa_table = bpp2_opa_table;
+ mask_init = 0xC0;
+ break;
+ case 4:
+ bpp_opa_table = bpp4_opa_table;
+ mask_init = 0xF0;
+ break;
+ case 8:
+ bpp_opa_table = NULL;
+ mask_init = 0xFF;
+ break; /*No opa table, pixel value will be used directly*/
+ default:
+ return; /*Invalid bpp. Can't render the letter*/
+ }
+
+ const uint8_t * map_p = lv_font_get_bitmap(font_p, letter);
+
+ if(map_p == NULL) return;
+
+ /*If the letter is completely out of mask don't draw it */
+ if(pos_p->x + letter_w < mask_p->x1 || pos_p->x > mask_p->x2 ||
+ pos_p->y + letter_h < mask_p->y1 || pos_p->y > mask_p->y2) return;
+
+ lv_coord_t col, row;
+ uint8_t col_bit;
+ uint8_t col_byte_cnt;
+ uint8_t width_byte_scr = letter_w >> 3; /*Width in bytes (on the screen finally) (e.g. w = 11 -> 2 bytes wide)*/
+ if(letter_w & 0x7) width_byte_scr++;
+ uint8_t width_byte_bpp = (letter_w * bpp) >> 3; /*Letter width in byte. Real width in the font*/
+ if((letter_w * bpp) & 0x7) width_byte_bpp++;
+
+ /* Calculate the col/row start/end on the map*/
+ lv_coord_t col_start = pos_p->x >= mask_p->x1 ? 0 : mask_p->x1 - pos_p->x;
+ lv_coord_t col_end = pos_p->x + letter_w <= mask_p->x2 ? letter_w : mask_p->x2 - pos_p->x + 1;
+ lv_coord_t row_start = pos_p->y >= mask_p->y1 ? 0 : mask_p->y1 - pos_p->y;
+ lv_coord_t row_end = pos_p->y + letter_h <= mask_p->y2 ? letter_h : mask_p->y2 - pos_p->y + 1;
+
+ /*Move on the map too*/
+ map_p += (row_start * width_byte_bpp) + ((col_start * bpp) >> 3);
+
+ uint8_t letter_px;
+ for(row = row_start; row < row_end; row ++) {
+ col_byte_cnt = 0;
+ col_bit = (col_start * bpp) % 8;
+ mask = mask_init >> col_bit;
+ for(col = col_start; col < col_end; col ++) {
+ letter_px = (*map_p & mask) >> (8 - col_bit - bpp);
+ if(letter_px != 0) {
+ lv_rpx(pos_p->x + col, pos_p->y + row, mask_p, lv_color_mix(color, letter_bg_color, bpp == 8 ? letter_px : bpp_opa_table[letter_px]), LV_OPA_COVER);
+ }
+
+ if(col_bit < 8 - bpp) {
+ col_bit += bpp;
+ mask = mask >> bpp;
+ } else {
+ col_bit = 0;
+ col_byte_cnt ++;
+ mask = mask_init;
+ map_p ++;
+ }
+ }
+
+ map_p += (width_byte_bpp) - col_byte_cnt;
+ }
+}
+
+/**
+ * When the letter is ant-aliased it needs to know the background color
+ * @param bg_color the background color of the currently drawn letter
+ */
+void lv_rletter_set_background(lv_color_t color)
+{
+ letter_bg_color = color;
+}
+
+/**
+ * Draw a color map to the display (image)
+ * @param cords_p coordinates the color map
+ * @param mask_p the map will drawn only on this area
+ * @param map_p pointer to a lv_color_t array
+ * @param opa opacity of the map (ignored, only for compatibility with 'lv_vmap')
+ * @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
+ * @param alpha_byte true: extra alpha byte is inserted for every pixel (not supported, only l'v_vmap' can draw it)
+ * @param recolor mix the pixels with this color
+ * @param recolor_opa the intense of recoloring
+ */
+void lv_rmap(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa)
+{
+ if(alpha_byte) return; /*Pixel level opacity i not supported in real map drawing*/
+
+ (void)opa; /*opa is used only for compatibility with lv_vmap*/
+ lv_area_t masked_a;
+ bool union_ok;
+
+ union_ok = lv_area_intersect(&masked_a, cords_p, mask_p);
+
+ /*If there are common part of the mask and map then draw the map*/
+ if(union_ok == false) return;
+
+ /*Go to the first pixel*/
+ lv_coord_t map_width = lv_area_get_width(cords_p);
+ map_p += (masked_a.y1 - cords_p->y1) * map_width * sizeof(lv_color_t);
+ map_p += (masked_a.x1 - cords_p->x1) * sizeof(lv_color_t);
+
+ lv_coord_t row;
+ if(recolor_opa == LV_OPA_TRANSP && chroma_key == false) {
+ lv_coord_t mask_w = lv_area_get_width(&masked_a) - 1;
+ for(row = masked_a.y1; row <= masked_a.y2; row++) {
+ lv_disp_map(masked_a.x1, row, masked_a.x1 + mask_w, row, (lv_color_t *)map_p);
+ map_p += map_width * sizeof(lv_color_t); /*Next row on the map*/
+ }
+ } else {
+ lv_color_t chroma_key_color = LV_COLOR_TRANSP;
+ lv_coord_t col;
+ for(row = masked_a.y1; row <= masked_a.y2; row++) {
+ for(col = masked_a.x1; col <= masked_a.x2; col++) {
+ lv_color_t * px_color = (lv_color_t *) &map_p[(uint32_t)(col - masked_a.x1) * sizeof(lv_color_t)];
+
+ if(chroma_key && chroma_key_color.full == px_color->full) continue;
+
+ if(recolor_opa != LV_OPA_TRANSP) {
+ lv_color_t recolored_px = lv_color_mix(recolor, *px_color, recolor_opa);
+
+ lv_rpx(col, row, mask_p, recolored_px, LV_OPA_COVER);
+ } else {
+ lv_rpx(col, row, mask_p, *px_color, LV_OPA_COVER);
+ }
+
+ }
+ map_p += map_width * sizeof(lv_color_t); /*Next row on the map*/
+ }
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif /*USE_LV_REAL_DRAW*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_rbasic.h b/bdk/libs/lvgl/lv_draw/lv_draw_rbasic.h
new file mode 100644
index 0000000..b1d62f3
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_rbasic.h
@@ -0,0 +1,96 @@
+/**
+ * @file lv_draw_rbasic..h
+ *
+ */
+
+#ifndef LV_DRAW_RBASIC_H
+#define LV_DRAW_RBASIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#if USE_LV_REAL_DRAW != 0
+
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_area.h"
+#include "../lv_misc/lv_font.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_rpx(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa);
+
+/**
+ * Fill an area on the display
+ * @param cords_p coordinates of the area to fill
+ * @param mask_p fill only o this mask
+ * @param color fill color
+ * @param opa opacity (ignored, only for compatibility with lv_vfill)
+ */
+void lv_rfill(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ lv_color_t color, lv_opa_t opa);
+
+/**
+ * Draw a letter to the display
+ * @param pos_p left-top coordinate of the latter
+ * @param mask_p the letter will be drawn only on this area
+ * @param font_p pointer to font
+ * @param letter a letter to draw
+ * @param color color of letter
+ * @param opa opacity of letter (ignored, only for compatibility with lv_vletter)
+ */
+void lv_rletter(const lv_point_t * pos_p, const lv_area_t * mask_p,
+ const lv_font_t * font_p, uint32_t letter,
+ lv_color_t color, lv_opa_t opa);
+
+/**
+ * When the letter is ant-aliased it needs to know the background color
+ * @param bg_color the background color of the currently drawn letter
+ */
+void lv_rletter_set_background(lv_color_t color);
+
+
+/**
+ * Draw a color map to the display (image)
+ * @param cords_p coordinates the color map
+ * @param mask_p the map will drawn only on this area
+ * @param map_p pointer to a lv_color_t array
+ * @param opa opacity of the map (ignored, only for compatibility with 'lv_vmap')
+ * @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
+ * @param alpha_byte true: extra alpha byte is inserted for every pixel (not supported, only l'v_vmap' can draw it)
+ * @param recolor mix the pixels with this color
+ * @param recolor_opa the intense of recoloring
+ */
+void lv_rmap(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa);
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*USE_LV_REAL_DRAW*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_RBASIC_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_rect.c b/bdk/libs/lvgl/lv_draw/lv_draw_rect.c
new file mode 100644
index 0000000..5b4ef16
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_rect.c
@@ -0,0 +1,1435 @@
+/**
+ * @file lv_draw_rect.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_rect.h"
+#include "../lv_misc/lv_circ.h"
+#include "../lv_misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD 5 /*Circle segment greater then this value will be anti-aliased by a non-linear (cos) opacity mapping*/
+
+#define SHADOW_OPA_EXTRA_PRECISION 8 /*Calculate with 2^x bigger shadow opacity values to avoid rounding errors*/
+#define SHADOW_BOTTOM_AA_EXTRA_RADIUS 3 /*Add extra radius with LV_SHADOW_BOTTOM to cover anti-aliased corners*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_draw_rect_main_mid(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void lv_draw_rect_main_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void lv_draw_rect_border_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void lv_draw_rect_border_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+
+#if USE_LV_SHADOW && LV_VDB_SIZE
+static void lv_draw_shadow(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void lv_draw_shadow_full(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void lv_draw_shadow_bottom(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+static void lv_draw_shadow_full_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, const lv_opa_t * map);
+#endif
+
+static uint16_t lv_draw_cont_radius_corr(uint16_t r, lv_coord_t w, lv_coord_t h);
+
+#if LV_ANTIALIAS
+static lv_opa_t antialias_get_opa_circ(lv_coord_t seg, lv_coord_t px_id, lv_opa_t opa);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Draw a rectangle
+ * @param coords the coordinates of the rectangle
+ * @param mask the rectangle will be drawn only in this mask
+ * @param style pointer to a style
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_rect(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ if(lv_area_get_height(coords) < 1 || lv_area_get_width(coords) < 1) return;
+
+#if USE_LV_SHADOW && LV_VDB_SIZE
+ if(style->body.shadow.width != 0) {
+ lv_draw_shadow(coords, mask, style, opa_scale);
+ }
+#endif
+ if(style->body.empty == 0 && style->body.opa >= LV_OPA_MIN) {
+ lv_draw_rect_main_mid(coords, mask, style, opa_scale);
+
+ if(style->body.radius != 0) {
+ lv_draw_rect_main_corner(coords, mask, style, opa_scale);
+ }
+ }
+
+ if(style->body.border.width != 0 && style->body.border.part != LV_BORDER_NONE && style->body.border.opa >= LV_OPA_MIN) {
+ lv_draw_rect_border_straight(coords, mask, style, opa_scale);
+
+ if(style->body.radius != 0) {
+ lv_draw_rect_border_corner(coords, mask, style, opa_scale);
+ }
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Draw the middle part (rectangular) of a rectangle
+ * @param coords the coordinates of the original rectangle
+ * @param mask the rectangle will be drawn only on this area
+ * @param rects_p pointer to a rectangle style
+ * @param opa_scale scale down all opacities by the factor
+ */
+static void lv_draw_rect_main_mid(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ uint16_t radius = style->body.radius;
+
+ lv_color_t mcolor = style->body.main_color;
+ lv_color_t gcolor = style->body.grad_color;
+ uint8_t mix;
+ lv_coord_t height = lv_area_get_height(coords);
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t) style->body.opa * opa_scale) >> 8;
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+
+ /*If the radius is too big then there is no body*/
+ if(radius > height / 2) return;
+
+ lv_area_t work_area;
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x2;
+
+ if(mcolor.full == gcolor.full) {
+ work_area.y1 = coords->y1 + radius;
+ work_area.y2 = coords->y2 - radius;
+
+ if(style->body.radius != 0) {
+#if LV_ANTIALIAS
+ work_area.y1 += 2;
+ work_area.y2 -= 2;
+#else
+ work_area.y1 += 1;
+ work_area.y2 -= 1;
+#endif
+ }
+
+ fill_fp(&work_area, mask, mcolor, opa);
+ } else {
+ lv_coord_t row;
+ lv_coord_t row_start = coords->y1 + radius;
+ lv_coord_t row_end = coords->y2 - radius;
+ lv_color_t act_color;
+
+ if(style->body.radius != 0) {
+#if LV_ANTIALIAS
+ row_start += 2;
+ row_end -= 2;
+#else
+ row_start += 1;
+ row_end -= 1;
+#endif
+ }
+ if(row_start < 0) row_start = 0;
+
+ for(row = row_start; row <= row_end; row ++) {
+ work_area.y1 = row;
+ work_area.y2 = row;
+ mix = (uint32_t)((uint32_t)(coords->y2 - work_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+
+ fill_fp(&work_area, mask, act_color, opa);
+ }
+ }
+}
+/**
+ * Draw the top and bottom parts (corners) of a rectangle
+ * @param coords the coordinates of the original rectangle
+ * @param mask the rectangle will be drawn only on this area
+ * @param rects_p pointer to a rectangle style
+ * @param opa_scale scale down all opacities by the factor
+ */
+static void lv_draw_rect_main_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ uint16_t radius = style->body.radius;
+
+ lv_color_t mcolor = style->body.main_color;
+ lv_color_t gcolor = style->body.grad_color;
+ lv_color_t act_color;
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t) style->body.opa * opa_scale) >> 8;
+ uint8_t mix;
+ lv_coord_t height = lv_area_get_height(coords);
+ lv_coord_t width = lv_area_get_width(coords);
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+
+ lv_point_t lt_origo; /*Left Top origo*/
+ lv_point_t lb_origo; /*Left Bottom origo*/
+ lv_point_t rt_origo; /*Right Top origo*/
+ lv_point_t rb_origo; /*Left Bottom origo*/
+
+ lt_origo.x = coords->x1 + radius + LV_ANTIALIAS;
+ lt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
+
+ lb_origo.x = coords->x1 + radius + LV_ANTIALIAS;
+ lb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
+
+ rt_origo.x = coords->x2 - radius - LV_ANTIALIAS;
+ rt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
+
+ rb_origo.x = coords->x2 - radius - LV_ANTIALIAS;
+ rb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
+
+ lv_area_t edge_top_area;
+ lv_area_t mid_top_area;
+ lv_area_t mid_bot_area;
+ lv_area_t edge_bot_area;
+
+ lv_point_t cir;
+ lv_coord_t cir_tmp;
+ lv_circ_init(&cir, &cir_tmp, radius);
+
+ /*Init the areas*/
+ lv_area_set(&mid_bot_area, lb_origo.x + LV_CIRC_OCT4_X(cir),
+ lb_origo.y + LV_CIRC_OCT4_Y(cir),
+ rb_origo.x + LV_CIRC_OCT1_X(cir),
+ rb_origo.y + LV_CIRC_OCT1_Y(cir));
+
+ lv_area_set(&edge_bot_area, lb_origo.x + LV_CIRC_OCT3_X(cir),
+ lb_origo.y + LV_CIRC_OCT3_Y(cir),
+ rb_origo.x + LV_CIRC_OCT2_X(cir),
+ rb_origo.y + LV_CIRC_OCT2_Y(cir));
+
+ lv_area_set(&mid_top_area, lt_origo.x + LV_CIRC_OCT5_X(cir),
+ lt_origo.y + LV_CIRC_OCT5_Y(cir),
+ rt_origo.x + LV_CIRC_OCT8_X(cir),
+ rt_origo.y + LV_CIRC_OCT8_Y(cir));
+
+ lv_area_set(&edge_top_area, lt_origo.x + LV_CIRC_OCT6_X(cir),
+ lt_origo.y + LV_CIRC_OCT6_Y(cir),
+ rt_origo.x + LV_CIRC_OCT7_X(cir),
+ rt_origo.y + LV_CIRC_OCT7_Y(cir));
+#if LV_ANTIALIAS
+ /*Store some internal states for anti-aliasing*/
+ lv_coord_t out_y_seg_start = 0;
+ lv_coord_t out_y_seg_end = 0;
+ lv_coord_t out_x_last = radius;
+
+ lv_color_t aa_color_hor_top;
+ lv_color_t aa_color_hor_bottom;
+ lv_color_t aa_color_ver;
+#endif
+
+ while(lv_circ_cont(&cir)) {
+#if LV_ANTIALIAS != 0
+ /*New step in y on the outter circle*/
+ if(out_x_last != cir.x) {
+ out_y_seg_end = cir.y;
+ lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
+ lv_point_t aa_p;
+
+ aa_p.x = out_x_last;
+ aa_p.y = out_y_seg_start;
+
+ mix = (uint32_t)((uint32_t)(radius - out_x_last) * 255) / height;
+ aa_color_hor_top = lv_color_mix(gcolor, mcolor, mix);
+ aa_color_hor_bottom = lv_color_mix(mcolor, gcolor, mix);
+
+ lv_coord_t i;
+ for(i = 0; i < seg_size; i++) {
+ lv_opa_t aa_opa;
+ if(seg_size > CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD) { /*Use non-linear opa mapping on the first segment*/
+ aa_opa = antialias_get_opa_circ(seg_size, i, opa);
+ } else {
+ aa_opa = opa - lv_draw_aa_get_opa(seg_size, i, opa);
+ }
+
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, aa_color_hor_bottom, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, aa_color_hor_bottom, aa_opa);
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, aa_color_hor_top, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, aa_color_hor_top, aa_opa);
+
+ mix = (uint32_t)((uint32_t)(radius - out_y_seg_start + i) * 255) / height;
+ aa_color_ver = lv_color_mix(mcolor, gcolor, mix);
+ px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
+
+ aa_color_ver = lv_color_mix(gcolor, mcolor, mix);
+ px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
+ }
+
+ out_x_last = cir.x;
+ out_y_seg_start = out_y_seg_end;
+ }
+#endif
+ uint8_t edge_top_refr = 0;
+ uint8_t mid_top_refr = 0;
+ uint8_t mid_bot_refr = 0;
+ uint8_t edge_bot_refr = 0;
+
+ /* If a new row coming draw the previous
+ * The y coordinate can remain the same so wait for a new*/
+ if(mid_bot_area.y1 != LV_CIRC_OCT4_Y(cir) + lb_origo.y) mid_bot_refr = 1;
+
+ if(edge_bot_area.y1 != LV_CIRC_OCT2_Y(cir) + lb_origo.y) edge_bot_refr = 1;
+
+ if(mid_top_area.y1 != LV_CIRC_OCT8_Y(cir) + lt_origo.y) mid_top_refr = 1;
+
+ if(edge_top_area.y1 != LV_CIRC_OCT7_Y(cir) + lt_origo.y) edge_top_refr = 1;
+
+ /*Draw the areas which are not disabled*/
+ if(edge_top_refr != 0) {
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - edge_top_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&edge_top_area, mask, act_color, opa);
+ }
+
+ if(mid_top_refr != 0) {
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - mid_top_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&mid_top_area, mask, act_color, opa);
+ }
+
+ if(mid_bot_refr != 0) {
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - mid_bot_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&mid_bot_area, mask, act_color, opa);
+ }
+
+ if(edge_bot_refr != 0) {
+
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - edge_bot_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&edge_bot_area, mask, act_color, opa);
+ }
+
+ /*Save the current coordinates*/
+ lv_area_set(&mid_bot_area, lb_origo.x + LV_CIRC_OCT4_X(cir),
+ lb_origo.y + LV_CIRC_OCT4_Y(cir),
+ rb_origo.x + LV_CIRC_OCT1_X(cir),
+ rb_origo.y + LV_CIRC_OCT1_Y(cir));
+
+ lv_area_set(&edge_bot_area, lb_origo.x + LV_CIRC_OCT3_X(cir),
+ lb_origo.y + LV_CIRC_OCT3_Y(cir),
+ rb_origo.x + LV_CIRC_OCT2_X(cir),
+ rb_origo.y + LV_CIRC_OCT2_Y(cir));
+
+ lv_area_set(&mid_top_area, lt_origo.x + LV_CIRC_OCT5_X(cir),
+ lt_origo.y + LV_CIRC_OCT5_Y(cir),
+ rt_origo.x + LV_CIRC_OCT8_X(cir),
+ rt_origo.y + LV_CIRC_OCT8_Y(cir));
+
+ lv_area_set(&edge_top_area, lt_origo.x + LV_CIRC_OCT6_X(cir),
+ lt_origo.y + LV_CIRC_OCT6_Y(cir),
+ rt_origo.x + LV_CIRC_OCT7_X(cir),
+ rt_origo.y + LV_CIRC_OCT7_Y(cir));
+
+ lv_circ_next(&cir, &cir_tmp);
+ }
+
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - edge_top_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&edge_top_area, mask, act_color, opa);
+
+ if(edge_top_area.y1 != mid_top_area.y1) {
+
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - mid_top_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&mid_top_area, mask, act_color, opa);
+ }
+
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - mid_bot_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&mid_bot_area, mask, act_color, opa);
+
+ if(edge_bot_area.y1 != mid_bot_area.y1) {
+
+ if(mcolor.full == gcolor.full) act_color = mcolor;
+ else {
+ mix = (uint32_t)((uint32_t)(coords->y2 - edge_bot_area.y1) * 255) / height;
+ act_color = lv_color_mix(mcolor, gcolor, mix);
+ }
+ fill_fp(&edge_bot_area, mask, act_color, opa);
+ }
+
+
+#if LV_ANTIALIAS
+ /*The first and the last line is not drawn*/
+ edge_top_area.x1 = coords->x1 + radius + 2;
+ edge_top_area.x2 = coords->x2 - radius - 2;
+ edge_top_area.y1 = coords->y1;
+ edge_top_area.y2 = coords->y1;
+ fill_fp(&edge_top_area, mask, style->body.main_color, opa);
+
+ edge_top_area.y1 = coords->y2;
+ edge_top_area.y2 = coords->y2;
+ fill_fp(&edge_top_area, mask, style->body.grad_color, opa);
+
+ /*Last parts of the anti-alias*/
+ out_y_seg_end = cir.y;
+ lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
+ lv_point_t aa_p;
+
+ aa_p.x = out_x_last;
+ aa_p.y = out_y_seg_start;
+
+ mix = (uint32_t)((uint32_t)(radius - out_x_last) * 255) / height;
+ aa_color_hor_bottom = lv_color_mix(gcolor, mcolor, mix);
+ aa_color_hor_top = lv_color_mix(mcolor, gcolor, mix);
+
+ lv_coord_t i;
+ for(i = 0; i < seg_size; i++) {
+ lv_opa_t aa_opa = opa - lv_draw_aa_get_opa(seg_size, i, opa);
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, aa_color_hor_top, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, aa_color_hor_top, aa_opa);
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, aa_color_hor_bottom, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, aa_color_hor_bottom, aa_opa);
+
+ mix = (uint32_t)((uint32_t)(radius - out_y_seg_start + i) * 255) / height;
+ aa_color_ver = lv_color_mix(mcolor, gcolor, mix);
+ px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, aa_color_ver, aa_opa);
+
+ aa_color_ver = lv_color_mix(gcolor, mcolor, mix);
+ px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, aa_color_ver, aa_opa);
+ }
+
+ /*In some cases the last pixel is not drawn*/
+ if(LV_MATH_ABS(aa_p.x - aa_p.y) == seg_size) {
+ aa_p.x = out_x_last;
+ aa_p.y = out_x_last;
+
+ mix = (uint32_t)((uint32_t)(out_x_last) * 255) / height;
+ aa_color_hor_top = lv_color_mix(gcolor, mcolor, mix);
+ aa_color_hor_bottom = lv_color_mix(mcolor, gcolor, mix);
+
+ lv_opa_t aa_opa = opa >> 1;
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p), rb_origo.y + LV_CIRC_OCT2_Y(aa_p), mask, aa_color_hor_bottom, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p), lb_origo.y + LV_CIRC_OCT4_Y(aa_p), mask, aa_color_hor_bottom, aa_opa);
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p), lt_origo.y + LV_CIRC_OCT6_Y(aa_p), mask, aa_color_hor_top, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p), rt_origo.y + LV_CIRC_OCT8_Y(aa_p), mask, aa_color_hor_top, aa_opa);
+ }
+
+#endif
+
+
+}
+
+/**
+ * Draw the straight parts of a rectangle border
+ * @param coords the coordinates of the original rectangle
+ * @param mask_ the rectangle will be drawn only on this area
+ * @param rstyle pointer to a rectangle style
+ * @param opa_scale scale down all opacities by the factor
+ */
+static void lv_draw_rect_border_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ uint16_t radius = style->body.radius;
+
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+ uint16_t bwidth = style->body.border.width;
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.border.opa : (uint16_t)((uint16_t) style->body.border.opa * opa_scale) >> 8;
+ lv_border_part_t part = style->body.border.part;
+ lv_color_t color = style->body.border.color;
+ lv_area_t work_area;
+ lv_coord_t length_corr = 0;
+ lv_coord_t corner_size = 0;
+
+ /*the 0 px border width drawn as 1 px, so decrement the b_width*/
+ bwidth--;
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+
+ if(radius < bwidth) {
+ length_corr = bwidth - radius - LV_ANTIALIAS;
+ corner_size = bwidth;
+ } else {
+ corner_size = radius + LV_ANTIALIAS;
+ }
+
+ /*If radius == 0 is a special case*/
+ if(style->body.radius == 0) {
+ /*Left top corner*/
+ if(part & LV_BORDER_TOP) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y1;
+ work_area.y2 = coords->y1 + bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right top corner*/
+ if(part & LV_BORDER_RIGHT) {
+ work_area.x1 = coords->x2 - bwidth;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y1 + (part & LV_BORDER_TOP ? bwidth + 1 : 0);
+ work_area.y2 = coords->y2 - (part & LV_BORDER_BOTTOM ? bwidth + 1 : 0);
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Left bottom corner*/
+ if(part & LV_BORDER_LEFT) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x1 + bwidth;
+ work_area.y1 = coords->y1 + (part & LV_BORDER_TOP ? bwidth + 1 : 0);
+ work_area.y2 = coords->y2 - (part & LV_BORDER_BOTTOM ? bwidth + 1 : 0);
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right bottom corner*/
+ if(part & LV_BORDER_BOTTOM) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y2 - bwidth;
+ work_area.y2 = coords->y2;
+ fill_fp(&work_area, mask, color, opa);
+ }
+ return;
+ }
+
+ /* Modify the corner_size if corner is drawn */
+ corner_size ++;
+
+ /*Depending one which part's are drawn modify the area lengths */
+ if(part & LV_BORDER_TOP) work_area.y1 = coords->y1 + corner_size;
+ else work_area.y1 = coords->y1 + radius;
+
+ if(part & LV_BORDER_BOTTOM) work_area.y2 = coords->y2 - corner_size;
+ else work_area.y2 = coords->y2 - radius;
+
+ /*Left border*/
+ if(part & LV_BORDER_LEFT) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = work_area.x1 + bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right border*/
+ if(part & LV_BORDER_RIGHT) {
+ work_area.x2 = coords->x2;
+ work_area.x1 = work_area.x2 - bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ work_area.x1 = coords->x1 + corner_size - length_corr;
+ work_area.x2 = coords->x2 - corner_size + length_corr;
+
+ /*Upper border*/
+ if(part & LV_BORDER_TOP) {
+ work_area.y1 = coords->y1;
+ work_area.y2 = coords->y1 + bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Lower border*/
+ if(part & LV_BORDER_BOTTOM) {
+ work_area.y2 = coords->y2;
+ work_area.y1 = work_area.y2 - bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Draw the a remaining rectangles if the radius is smaller then bwidth */
+ if(length_corr != 0) {
+ /*Left top correction*/
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x1 + radius + LV_ANTIALIAS;
+ work_area.y1 = coords->y1 + radius + 1 + LV_ANTIALIAS;
+ work_area.y2 = coords->y1 + bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right top correction*/
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ work_area.x1 = coords->x2 - radius - LV_ANTIALIAS;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y1 + radius + 1 + LV_ANTIALIAS;
+ work_area.y2 = coords->y1 + bwidth;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Left bottom correction*/
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x1 + radius + LV_ANTIALIAS;
+ work_area.y1 = coords->y2 - bwidth;
+ work_area.y2 = coords->y2 - radius - 1 - LV_ANTIALIAS;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right bottom correction*/
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ work_area.x1 = coords->x2 - radius - LV_ANTIALIAS;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y2 - bwidth;
+ work_area.y2 = coords->y2 - radius - 1 - LV_ANTIALIAS;
+ fill_fp(&work_area, mask, color, opa);
+ }
+ }
+
+ /*If radius == 0 one px on the corners are not drawn by main drawer*/
+ if(style->body.radius == 0) {
+ /*Left top corner*/
+ if(part & (LV_BORDER_TOP | LV_BORDER_LEFT)) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x1 + LV_ANTIALIAS;
+ work_area.y1 = coords->y1;
+ work_area.y2 = coords->y1 + LV_ANTIALIAS;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right top corner*/
+ if(part & (LV_BORDER_TOP | LV_BORDER_RIGHT)) {
+ work_area.x1 = coords->x2 - LV_ANTIALIAS;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y1;
+ work_area.y2 = coords->y1 + LV_ANTIALIAS;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Left bottom corner*/
+ if(part & (LV_BORDER_BOTTOM | LV_BORDER_LEFT)) {
+ work_area.x1 = coords->x1;
+ work_area.x2 = coords->x1 + LV_ANTIALIAS;
+ work_area.y1 = coords->y2 - LV_ANTIALIAS;
+ work_area.y2 = coords->y2;
+ fill_fp(&work_area, mask, color, opa);
+ }
+
+ /*Right bottom corner*/
+ if(part & (LV_BORDER_BOTTOM | LV_BORDER_RIGHT)) {
+ work_area.x1 = coords->x2 - LV_ANTIALIAS;
+ work_area.x2 = coords->x2;
+ work_area.y1 = coords->y2 - LV_ANTIALIAS;
+ work_area.y2 = coords->y2;
+ fill_fp(&work_area, mask, color, opa);
+ }
+ }
+}
+
+
+/**
+ * Draw the corners of a rectangle border
+ * @param coords the coordinates of the original rectangle
+ * @param mask the rectangle will be drawn only on this area
+ * @param style pointer to a style
+ * @param opa_scale scale down all opacities by the factor
+ */
+static void lv_draw_rect_border_corner(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ uint16_t radius = style->body.radius ;
+ uint16_t bwidth = style->body.border.width;
+ lv_color_t color = style->body.border.color;
+ lv_border_part_t part = style->body.border.part;
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.border.opa : (uint16_t)((uint16_t) style->body.border.opa * opa_scale) >> 8;
+ /*0 px border width drawn as 1 px, so decrement the bwidth*/
+ bwidth--;
+
+#if LV_ANTIALIAS
+ bwidth--; /*Because of anti-aliasing the border seems one pixel ticker*/
+#endif
+
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+
+ lv_point_t lt_origo; /*Left Top origo*/
+ lv_point_t lb_origo; /*Left Bottom origo*/
+ lv_point_t rt_origo; /*Right Top origo*/
+ lv_point_t rb_origo; /*Left Bottom origo*/
+
+ lt_origo.x = coords->x1 + radius + LV_ANTIALIAS;
+ lt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
+
+ lb_origo.x = coords->x1 + radius + LV_ANTIALIAS;
+ lb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
+
+ rt_origo.x = coords->x2 - radius - LV_ANTIALIAS;
+ rt_origo.y = coords->y1 + radius + LV_ANTIALIAS;
+
+ rb_origo.x = coords->x2 - radius - LV_ANTIALIAS;
+ rb_origo.y = coords->y2 - radius - LV_ANTIALIAS;
+
+ lv_point_t cir_out;
+ lv_coord_t tmp_out;
+ lv_circ_init(&cir_out, &tmp_out, radius);
+
+ lv_point_t cir_in;
+ lv_coord_t tmp_in;
+ lv_coord_t radius_in = radius - bwidth;
+
+ if(radius_in < 0) {
+ radius_in = 0;
+ }
+
+ lv_circ_init(&cir_in, &tmp_in, radius_in);
+
+ lv_area_t circ_area;
+ lv_coord_t act_w1;
+ lv_coord_t act_w2;
+
+#if LV_ANTIALIAS
+ /*Store some internal states for anti-aliasing*/
+ lv_coord_t out_y_seg_start = 0;
+ lv_coord_t out_y_seg_end = 0;
+ lv_coord_t out_x_last = radius;
+
+
+ lv_coord_t in_y_seg_start = 0;
+ lv_coord_t in_y_seg_end = 0;
+ lv_coord_t in_x_last = radius - bwidth;
+#endif
+
+ while(cir_out.y <= cir_out.x) {
+
+ /*Calculate the actual width to avoid overwriting pixels*/
+ if(cir_in.y < cir_in.x) {
+ act_w1 = cir_out.x - cir_in.x;
+ act_w2 = act_w1;
+ } else {
+ act_w1 = cir_out.x - cir_out.y;
+ act_w2 = act_w1 - 1;
+ }
+
+#if LV_ANTIALIAS != 0
+ /*New step in y on the outter circle*/
+ if(out_x_last != cir_out.x) {
+ out_y_seg_end = cir_out.y;
+ lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
+ lv_point_t aa_p;
+
+ aa_p.x = out_x_last;
+ aa_p.y = out_y_seg_start;
+
+ lv_coord_t i;
+ for(i = 0; i < seg_size; i++) {
+ lv_opa_t aa_opa;
+
+ if(seg_size > CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD) { /*Use non-linear opa mapping on the first segment*/
+ aa_opa = antialias_get_opa_circ(seg_size, i, opa);
+ } else {
+ aa_opa = opa - lv_draw_aa_get_opa(seg_size, i, opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ }
+
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ }
+ }
+
+ out_x_last = cir_out.x;
+ out_y_seg_start = out_y_seg_end;
+ }
+
+ /*New step in y on the inner circle*/
+ if(in_x_last != cir_in.x) {
+ in_y_seg_end = cir_out.y;
+ lv_coord_t seg_size = in_y_seg_end - in_y_seg_start;
+ lv_point_t aa_p;
+
+ aa_p.x = in_x_last;
+ aa_p.y = in_y_seg_start;
+
+ lv_coord_t i;
+ for(i = 0; i < seg_size; i++) {
+ lv_opa_t aa_opa;
+
+ if(seg_size > CIRCLE_AA_NON_LINEAR_OPA_THRESHOLD) { /*Use non-linear opa mapping on the first segment*/
+ aa_opa = opa - antialias_get_opa_circ(seg_size, i, opa);
+ } else {
+ aa_opa = lv_draw_aa_get_opa(seg_size, i, opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) - 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) + 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ }
+
+ /*Be sure the pixels on the middle are not drawn twice*/
+ if(LV_CIRC_OCT1_X(aa_p) - 1 != LV_CIRC_OCT2_X(aa_p) + i) {
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) + 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) - 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ }
+ }
+
+ }
+
+ in_x_last = cir_in.x;
+ in_y_seg_start = in_y_seg_end;
+
+ }
+
+#endif
+
+
+ /*Draw the octets to the right bottom corner*/
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ circ_area.x1 = rb_origo.x + LV_CIRC_OCT1_X(cir_out) - act_w2;
+ circ_area.x2 = rb_origo.x + LV_CIRC_OCT1_X(cir_out);
+ circ_area.y1 = rb_origo.y + LV_CIRC_OCT1_Y(cir_out);
+ circ_area.y2 = rb_origo.y + LV_CIRC_OCT1_Y(cir_out);
+ fill_fp(&circ_area, mask, color, opa);
+
+ circ_area.x1 = rb_origo.x + LV_CIRC_OCT2_X(cir_out);
+ circ_area.x2 = rb_origo.x + LV_CIRC_OCT2_X(cir_out);
+ circ_area.y1 = rb_origo.y + LV_CIRC_OCT2_Y(cir_out) - act_w1;
+ circ_area.y2 = rb_origo.y + LV_CIRC_OCT2_Y(cir_out);
+ fill_fp(&circ_area, mask, color, opa);
+ }
+
+ /*Draw the octets to the left bottom corner*/
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ circ_area.x1 = lb_origo.x + LV_CIRC_OCT3_X(cir_out);
+ circ_area.x2 = lb_origo.x + LV_CIRC_OCT3_X(cir_out);
+ circ_area.y1 = lb_origo.y + LV_CIRC_OCT3_Y(cir_out) - act_w2;
+ circ_area.y2 = lb_origo.y + LV_CIRC_OCT3_Y(cir_out);
+ fill_fp(&circ_area, mask, color, opa);
+
+ circ_area.x1 = lb_origo.x + LV_CIRC_OCT4_X(cir_out);
+ circ_area.x2 = lb_origo.x + LV_CIRC_OCT4_X(cir_out) + act_w1;
+ circ_area.y1 = lb_origo.y + LV_CIRC_OCT4_Y(cir_out);
+ circ_area.y2 = lb_origo.y + LV_CIRC_OCT4_Y(cir_out);
+ fill_fp(&circ_area, mask, color, opa);
+ }
+
+ /*Draw the octets to the left top corner*/
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ if(lb_origo.y + LV_CIRC_OCT4_Y(cir_out) > lt_origo.y + LV_CIRC_OCT5_Y(cir_out)) {
+ /*Don't draw if the lines are common in the middle*/
+ circ_area.x1 = lt_origo.x + LV_CIRC_OCT5_X(cir_out);
+ circ_area.x2 = lt_origo.x + LV_CIRC_OCT5_X(cir_out) + act_w2;
+ circ_area.y1 = lt_origo.y + LV_CIRC_OCT5_Y(cir_out);
+ circ_area.y2 = lt_origo.y + LV_CIRC_OCT5_Y(cir_out);
+ fill_fp(&circ_area, mask, color, opa);
+ }
+
+ circ_area.x1 = lt_origo.x + LV_CIRC_OCT6_X(cir_out);
+ circ_area.x2 = lt_origo.x + LV_CIRC_OCT6_X(cir_out);
+ circ_area.y1 = lt_origo.y + LV_CIRC_OCT6_Y(cir_out);
+ circ_area.y2 = lt_origo.y + LV_CIRC_OCT6_Y(cir_out) + act_w1;
+ fill_fp(&circ_area, mask, color, opa);
+ }
+
+ /*Draw the octets to the right top corner*/
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ circ_area.x1 = rt_origo.x + LV_CIRC_OCT7_X(cir_out);
+ circ_area.x2 = rt_origo.x + LV_CIRC_OCT7_X(cir_out);
+ circ_area.y1 = rt_origo.y + LV_CIRC_OCT7_Y(cir_out);
+ circ_area.y2 = rt_origo.y + LV_CIRC_OCT7_Y(cir_out) + act_w2;
+ fill_fp(&circ_area, mask, color, opa);
+
+ /*Don't draw if the lines are common in the middle*/
+ if(rb_origo.y + LV_CIRC_OCT1_Y(cir_out) > rt_origo.y + LV_CIRC_OCT8_Y(cir_out)) {
+ circ_area.x1 = rt_origo.x + LV_CIRC_OCT8_X(cir_out) - act_w1;
+ circ_area.x2 = rt_origo.x + LV_CIRC_OCT8_X(cir_out);
+ circ_area.y1 = rt_origo.y + LV_CIRC_OCT8_Y(cir_out);
+ circ_area.y2 = rt_origo.y + LV_CIRC_OCT8_Y(cir_out);
+ fill_fp(&circ_area, mask, color, opa);
+ }
+ }
+ lv_circ_next(&cir_out, &tmp_out);
+
+ /*The internal circle will be ready faster
+ * so check it! */
+ if(cir_in.y < cir_in.x) {
+ lv_circ_next(&cir_in, &tmp_in);
+ }
+ }
+
+
+#if LV_ANTIALIAS != 0
+
+ /*Last parts of the outer anti-alias*/
+ out_y_seg_end = cir_out.y;
+ lv_coord_t seg_size = out_y_seg_end - out_y_seg_start;
+ lv_point_t aa_p;
+
+ aa_p.x = out_x_last;
+ aa_p.y = out_y_seg_start;
+
+ lv_coord_t i;
+ for(i = 0; i < seg_size; i++) {
+ lv_opa_t aa_opa = opa - lv_draw_aa_get_opa(seg_size, i, opa);
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) + 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) - 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) - 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) + 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ }
+ }
+
+ /*In some cases the last pixel in the outer middle is not drawn*/
+ if(LV_MATH_ABS(aa_p.x - aa_p.y) == seg_size) {
+ aa_p.x = out_x_last;
+ aa_p.y = out_x_last;
+
+ lv_opa_t aa_opa = opa >> 1;
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p), rb_origo.y + LV_CIRC_OCT2_Y(aa_p), mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p), lb_origo.y + LV_CIRC_OCT4_Y(aa_p), mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p), lt_origo.y + LV_CIRC_OCT6_Y(aa_p), mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p), rt_origo.y + LV_CIRC_OCT8_Y(aa_p), mask, style->body.border.color, aa_opa);
+ }
+ }
+
+ /*Last parts of the inner anti-alias*/
+ in_y_seg_end = cir_in.y;
+ aa_p.x = in_x_last;
+ aa_p.y = in_y_seg_start;
+ seg_size = in_y_seg_end - in_y_seg_start;
+
+ for(i = 0; i < seg_size; i++) {
+ lv_opa_t aa_opa = lv_draw_aa_get_opa(seg_size, i, opa);
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT1_X(aa_p) - 1, rb_origo.y + LV_CIRC_OCT1_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT3_X(aa_p) - i, lb_origo.y + LV_CIRC_OCT3_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT5_X(aa_p) + 1, lt_origo.y + LV_CIRC_OCT5_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT7_X(aa_p) + i, rt_origo.y + LV_CIRC_OCT7_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if(LV_CIRC_OCT1_X(aa_p) - 1 != LV_CIRC_OCT2_X(aa_p) + i) {
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rb_origo.x + LV_CIRC_OCT2_X(aa_p) + i, rb_origo.y + LV_CIRC_OCT2_Y(aa_p) - 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_BOTTOM) && (part & LV_BORDER_LEFT)) {
+ px_fp(lb_origo.x + LV_CIRC_OCT4_X(aa_p) + 1, lb_origo.y + LV_CIRC_OCT4_Y(aa_p) + i, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_LEFT)) {
+ px_fp(lt_origo.x + LV_CIRC_OCT6_X(aa_p) - i, lt_origo.y + LV_CIRC_OCT6_Y(aa_p) + 1, mask, style->body.border.color, aa_opa);
+ }
+
+ if((part & LV_BORDER_TOP) && (part & LV_BORDER_RIGHT)) {
+ px_fp(rt_origo.x + LV_CIRC_OCT8_X(aa_p) - 1, rt_origo.y + LV_CIRC_OCT8_Y(aa_p) - i, mask, style->body.border.color, aa_opa);
+ }
+ }
+ }
+
+#endif
+
+}
+
+#if USE_LV_SHADOW && LV_VDB_SIZE
+
+/**
+ * Draw a shadow
+ * @param rect pointer to rectangle object
+ * @param mask pointer to a mask area (from the design functions)
+ * @param opa_scale scale down all opacities by the factor
+ */
+static void lv_draw_shadow(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ /* If mask is in the middle of cords do not draw shadow*/
+ lv_coord_t radius = style->body.radius;
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+ lv_area_t area_tmp;
+
+ /*Check horizontally without radius*/
+ lv_area_copy(&area_tmp, coords);
+ area_tmp.x1 += radius;
+ area_tmp.x2 -= radius;
+ if(lv_area_is_in(mask, &area_tmp) != false) return;
+
+ /*Check vertically without radius*/
+ lv_area_copy(&area_tmp, coords);
+ area_tmp.y1 += radius;
+ area_tmp.y2 -= radius;
+ if(lv_area_is_in(mask, &area_tmp) != false) return;
+
+ if(style->body.shadow.type == LV_SHADOW_FULL) {
+ lv_draw_shadow_full(coords, mask, style, opa_scale);
+ } else if(style->body.shadow.type == LV_SHADOW_BOTTOM) {
+ lv_draw_shadow_bottom(coords, mask, style, opa_scale);
+ }
+}
+
+static void lv_draw_shadow_full(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+
+ /* KNOWN ISSUE
+ * The algorithm calculates the shadow only above the middle point of the radius (speaking about the left top corner).
+ * It causes an error because it doesn't consider how long the straight edge is which effects the value of bottom of the corner shadow.
+ * In addition the straight shadow is drawn from the middles point of the radius however
+ * the ends of the straight parts still should be effected by the corner shadow.
+ * It also causes an issue in opacity. A smaller radius means smaller average shadow opacity.
+ * The solution should be to start `line` from `- swidth` and handle if the straight part is short (or zero) and the value is taken from
+ * the other corner. `col` also should start from `- swidth`
+ */
+
+
+ lv_coord_t radius = style->body.radius;
+ lv_coord_t swidth = style->body.shadow.width;
+
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+
+ radius += LV_ANTIALIAS;
+
+#if LV_COMPILER_VLA_SUPPORTED
+ lv_coord_t curve_x[radius + swidth + 1]; /*Stores the 'x' coordinates of a quarter circle.*/
+#else
+# if LV_HOR_RES > LV_VER_RES
+ lv_coord_t curve_x[LV_HOR_RES];
+# else
+ lv_coord_t curve_x[LV_VER_RES];
+# endif
+#endif
+ memset(curve_x, 0, sizeof(curve_x));
+ lv_point_t circ;
+ lv_coord_t circ_tmp;
+ lv_circ_init(&circ, &circ_tmp, radius);
+ while(lv_circ_cont(&circ)) {
+ curve_x[LV_CIRC_OCT1_Y(circ)] = LV_CIRC_OCT1_X(circ);
+ curve_x[LV_CIRC_OCT2_Y(circ)] = LV_CIRC_OCT2_X(circ);
+ lv_circ_next(&circ, &circ_tmp);
+ }
+ int16_t line;
+
+ int16_t filter_width = 2 * swidth + 1;
+#if LV_COMPILER_VLA_SUPPORTED
+ uint32_t line_1d_blur[filter_width];
+#else
+# if LV_HOR_RES > LV_VER_RES
+ uint32_t line_1d_blur[LV_HOR_RES];
+# else
+ uint32_t line_1d_blur[LV_VER_RES];
+# endif
+#endif
+ /*1D Blur horizontally*/
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t) style->body.opa * opa_scale) >> 8;
+ for(line = 0; line < filter_width; line++) {
+ line_1d_blur[line] = (uint32_t)((uint32_t)(filter_width - line) * (opa * 2) << SHADOW_OPA_EXTRA_PRECISION) / (filter_width * filter_width);
+ }
+
+ uint16_t col;
+#if LV_COMPILER_VLA_SUPPORTED
+ lv_opa_t line_2d_blur[radius + swidth + 1];
+#else
+# if LV_HOR_RES > LV_VER_RES
+ lv_opa_t line_2d_blur[LV_HOR_RES];
+# else
+ lv_opa_t line_2d_blur[LV_VER_RES];
+# endif
+#endif
+
+ lv_point_t point_rt;
+ lv_point_t point_rb;
+ lv_point_t point_lt;
+ lv_point_t point_lb;
+ lv_point_t ofs_rb;
+ lv_point_t ofs_rt;
+ lv_point_t ofs_lb;
+ lv_point_t ofs_lt;
+ ofs_rb.x = coords->x2 - radius - LV_ANTIALIAS;
+ ofs_rb.y = coords->y2 - radius - LV_ANTIALIAS;
+
+ ofs_rt.x = coords->x2 - radius - LV_ANTIALIAS;
+ ofs_rt.y = coords->y1 + radius + LV_ANTIALIAS;
+
+ ofs_lb.x = coords->x1 + radius + LV_ANTIALIAS;
+ ofs_lb.y = coords->y2 - radius - LV_ANTIALIAS;
+
+ ofs_lt.x = coords->x1 + radius + LV_ANTIALIAS;
+ ofs_lt.y = coords->y1 + radius + LV_ANTIALIAS;
+ bool line_ready;
+ for(line = 0; line <= radius + swidth; line++) { /*Check all rows and make the 1D blur to 2D*/
+ line_ready = false;
+ for(col = 0; col <= radius + swidth; col++) { /*Check all pixels in a 1D blur line (from the origo to last shadow pixel (radius + swidth))*/
+
+ /*Sum the opacities from the lines above and below this 'row'*/
+ int16_t line_rel;
+ uint32_t px_opa_sum = 0;
+ for(line_rel = -swidth; line_rel <= swidth; line_rel ++) {
+ /*Get the relative x position of the 'line_rel' to 'line'*/
+ int16_t col_rel;
+ if(line + line_rel < 0) { /*Below the radius, here is the blur of the edge */
+ col_rel = radius - curve_x[line] - col;
+ } else if(line + line_rel > radius) { /*Above the radius, here won't be more 1D blur*/
+ break;
+ } else { /*Blur from the curve*/
+ col_rel = curve_x[line + line_rel] - curve_x[line] - col;
+ }
+
+ /*Add the value of the 1D blur on 'col_rel' position*/
+ if(col_rel < -swidth) { /*Outside of the blurred area. */
+ if(line_rel == -swidth) line_ready = true; /*If no data even on the very first line then it wont't be anything else in this line*/
+ break; /*Break anyway because only smaller 'col_rel' values will come */
+ } else if(col_rel > swidth) px_opa_sum += line_1d_blur[0]; /*Inside the not blurred area*/
+ else px_opa_sum += line_1d_blur[swidth - col_rel]; /*On the 1D blur (+ swidth to align to the center)*/
+ }
+
+ line_2d_blur[col] = px_opa_sum >> SHADOW_OPA_EXTRA_PRECISION;
+ if(line_ready) {
+ col++; /*To make this line to the last one ( drawing will go to '< col')*/
+ break;
+ }
+
+ }
+
+ /*Flush the line*/
+ point_rt.x = curve_x[line] + ofs_rt.x + 1;
+ point_rt.y = ofs_rt.y - line;
+
+ point_rb.x = curve_x[line] + ofs_rb.x + 1;
+ point_rb.y = ofs_rb.y + line;
+
+ point_lt.x = ofs_lt.x - curve_x[line] - 1;
+ point_lt.y = ofs_lt.y - line;
+
+ point_lb.x = ofs_lb.x - curve_x[line] - 1;
+ point_lb.y = ofs_lb.y + line;
+
+ uint16_t d;
+ for(d = 1; d < col; d++) {
+
+ if(point_lt.x < ofs_lt.x && point_lt.y < ofs_lt.y) {
+ px_fp(point_lt.x, point_lt.y, mask, style->body.shadow.color, line_2d_blur[d]);
+ }
+
+ if(point_lb.x < ofs_lb.x && point_lb.y > ofs_lb.y) {
+ px_fp(point_lb.x, point_lb.y, mask, style->body.shadow.color, line_2d_blur[d]);
+ }
+
+ if(point_rt.x > ofs_rt.x && point_rt.y < ofs_rt.y) {
+ px_fp(point_rt.x, point_rt.y, mask, style->body.shadow.color, line_2d_blur[d]);
+ }
+
+ if(point_rb.x > ofs_rb.x && point_rb.y > ofs_rb.y) {
+ px_fp(point_rb.x, point_rb.y, mask, style->body.shadow.color, line_2d_blur[d]);
+ }
+
+ point_rb.x++;
+ point_lb.x--;
+
+ point_rt.x++;
+ point_lt.x--;
+ }
+
+ /* Put the first line to the edges too.
+ * It is not correct because blur should be done below the corner too
+ * but is is simple, fast and gives a good enough result*/
+ if(line == 0) lv_draw_shadow_full_straight(coords, mask, style, line_2d_blur);
+ }
+}
+
+
+static void lv_draw_shadow_bottom(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
+{
+ lv_coord_t radius = style->body.radius;
+ lv_coord_t swidth = style->body.shadow.width;
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+ radius += LV_ANTIALIAS * SHADOW_BOTTOM_AA_EXTRA_RADIUS;
+ swidth += LV_ANTIALIAS;
+#if LV_COMPILER_VLA_SUPPORTED
+ lv_coord_t curve_x[radius + 1]; /*Stores the 'x' coordinates of a quarter circle.*/
+#else
+# if LV_HOR_RES > LV_VER_RES
+ lv_coord_t curve_x[LV_HOR_RES];
+# else
+ lv_coord_t curve_x[LV_VER_RES];
+# endif
+#endif
+ lv_point_t circ;
+ lv_coord_t circ_tmp;
+ lv_circ_init(&circ, &circ_tmp, radius);
+ while(lv_circ_cont(&circ)) {
+ curve_x[LV_CIRC_OCT1_Y(circ)] = LV_CIRC_OCT1_X(circ);
+ curve_x[LV_CIRC_OCT2_Y(circ)] = LV_CIRC_OCT2_X(circ);
+ lv_circ_next(&circ, &circ_tmp);
+ }
+
+ int16_t col;
+#if LV_COMPILER_VLA_SUPPORTED
+ lv_opa_t line_1d_blur[swidth];
+#else
+# if LV_HOR_RES > LV_VER_RES
+ lv_opa_t line_1d_blur[LV_HOR_RES];
+# else
+ lv_opa_t line_1d_blur[LV_VER_RES];
+# endif
+#endif
+
+ lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t) style->body.opa * opa_scale) >> 8;
+ for(col = 0; col < swidth; col++) {
+ line_1d_blur[col] = (uint32_t)((uint32_t)(swidth - col) * opa / 2) / (swidth);
+ }
+
+ lv_point_t point_l;
+ lv_point_t point_r;
+ lv_area_t area_mid;
+ lv_point_t ofs_l;
+ lv_point_t ofs_r;
+
+ ofs_l.x = coords->x1 + radius;
+ ofs_l.y = coords->y2 - radius + 1 - LV_ANTIALIAS;
+
+ ofs_r.x = coords->x2 - radius;
+ ofs_r.y = coords->y2 - radius + 1 - LV_ANTIALIAS;
+
+ for(col = 0; col <= radius; col++) {
+ point_l.x = ofs_l.x - col ;
+ point_l.y = ofs_l.y + curve_x[col];
+
+ point_r.x = ofs_r.x + col;
+ point_r.y = ofs_r.y + curve_x[col];
+
+ lv_opa_t px_opa;
+ int16_t diff = col == 0 ? 0 : curve_x[col - 1] - curve_x[col];
+ uint16_t d;
+ for(d = 0; d < swidth; d++) {
+ /*When stepping a pixel in y calculate the average with the pixel from the prev. column to make a blur */
+ if(diff == 0) {
+ px_opa = line_1d_blur[d];
+ } else {
+ px_opa = (uint16_t)((uint16_t)line_1d_blur[d] + line_1d_blur[d - diff]) >> 1;
+ }
+ px_fp(point_l.x, point_l.y, mask, style->body.shadow.color, px_opa);
+ point_l.y ++;
+
+ /*Don't overdraw the pixel on the middle*/
+ if(point_r.x > ofs_l.x) {
+ px_fp(point_r.x, point_r.y, mask, style->body.shadow.color, px_opa);
+ }
+ point_r.y ++;
+ }
+
+ }
+
+ area_mid.x1 = ofs_l.x + 1;
+ area_mid.y1 = ofs_l.y + radius;
+ area_mid.x2 = ofs_r.x - 1;
+ area_mid.y2 = area_mid.y1;
+
+ uint16_t d;
+ for(d = 0; d < swidth; d++) {
+ fill_fp(&area_mid, mask, style->body.shadow.color, line_1d_blur[d]);
+ area_mid.y1 ++;
+ area_mid.y2 ++;
+ }
+}
+
+static void lv_draw_shadow_full_straight(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, const lv_opa_t * map)
+{
+ lv_coord_t radius = style->body.radius;
+ lv_coord_t swidth = style->body.shadow.width;// + LV_ANTIALIAS;
+ lv_coord_t width = lv_area_get_width(coords);
+ lv_coord_t height = lv_area_get_height(coords);
+
+ radius = lv_draw_cont_radius_corr(radius, width, height);
+ radius += LV_ANTIALIAS;
+
+ lv_area_t right_area;
+ right_area.x1 = coords->x2 + 1 - LV_ANTIALIAS;
+ right_area.y1 = coords->y1 + radius + LV_ANTIALIAS;
+ right_area.x2 = right_area.x1;
+ right_area.y2 = coords->y2 - radius - LV_ANTIALIAS;
+
+ lv_area_t left_area;
+ left_area.x1 = coords->x1 - 1 + LV_ANTIALIAS;
+ left_area.y1 = coords->y1 + radius + LV_ANTIALIAS;
+ left_area.x2 = left_area.x1;
+ left_area.y2 = coords->y2 - radius - LV_ANTIALIAS;
+
+ lv_area_t top_area;
+ top_area.x1 = coords->x1 + radius + LV_ANTIALIAS;
+ top_area.y1 = coords->y1 - 1 + LV_ANTIALIAS;
+ top_area.x2 = coords->x2 - radius - LV_ANTIALIAS;
+ top_area.y2 = top_area.y1;
+
+ lv_area_t bottom_area;
+ bottom_area.x1 = coords->x1 + radius + LV_ANTIALIAS;
+ bottom_area.y1 = coords->y2 + 1 - LV_ANTIALIAS;
+ bottom_area.x2 = coords->x2 - radius - LV_ANTIALIAS;
+ bottom_area.y2 = bottom_area.y1;
+
+ lv_opa_t opa_act;
+ int16_t d;
+ for(d = 1 /*+ LV_ANTIALIAS*/; d <= swidth/* - LV_ANTIALIAS*/; d++) {
+ opa_act = map[d];
+
+ fill_fp(&right_area, mask, style->body.shadow.color, opa_act);
+ right_area.x1++;
+ right_area.x2++;
+
+ fill_fp(&left_area, mask, style->body.shadow.color, opa_act);
+ left_area.x1--;
+ left_area.x2--;
+
+ fill_fp(&top_area, mask, style->body.shadow.color, opa_act);
+ top_area.y1--;
+ top_area.y2--;
+
+ fill_fp(&bottom_area, mask, style->body.shadow.color, opa_act);
+ bottom_area.y1++;
+ bottom_area.y2++;
+ }
+}
+
+#endif
+
+
+static uint16_t lv_draw_cont_radius_corr(uint16_t r, lv_coord_t w, lv_coord_t h)
+{
+ if(r >= (w >> 1)) {
+ r = (w >> 1);
+ if(r != 0) r--;
+ }
+ if(r >= (h >> 1)) {
+ r = (h >> 1);
+ if(r != 0) r--;
+ }
+
+ if(r > 0) r -= LV_ANTIALIAS;
+
+ return r;
+}
+
+#if LV_ANTIALIAS
+
+/**
+ * Approximate the opacity for anti-aliasing.
+ * Used the first segment of a circle which is the longest and have the most non-linearity (cos)
+ * @param seg length of the line segment
+ * @param px_id index of pixel on the line segment
+ * @param line_opa opacity of the lien (it will be the max opacity)
+ * @return the desired opacity of the pixel
+ */
+static lv_opa_t antialias_get_opa_circ(lv_coord_t seg, lv_coord_t px_id, lv_opa_t opa)
+{
+ static const lv_opa_t opa_map[8] = {250, 242, 221, 196, 163, 122, 74, 18};
+
+ if(seg == 0) return LV_OPA_TRANSP;
+ else if(seg == 1) return LV_OPA_80;
+ else {
+
+ uint8_t id = (uint32_t)((uint32_t)px_id * (sizeof(opa_map) - 1)) / (seg - 1);
+ return (uint32_t)((uint32_t) opa_map[id] * opa) >> 8;
+
+ }
+
+}
+
+#endif
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_rect.h b/bdk/libs/lvgl/lv_draw/lv_draw_rect.h
new file mode 100644
index 0000000..933590c
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_rect.h
@@ -0,0 +1,48 @@
+/**
+ * @file lv_draw_rect.h
+ *
+ */
+
+#ifndef LV_DRAW_RECT_H
+#define LV_DRAW_RECT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Draw a rectangle
+ * @param coords the coordinates of the rectangle
+ * @param mask the rectangle will be drawn only in this mask
+ * @param style pointer to a style
+ * @param opa_scale scale down all opacities by the factor
+ */
+void lv_draw_rect(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_RECT_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_triangle.c b/bdk/libs/lvgl/lv_draw/lv_draw_triangle.c
new file mode 100644
index 0000000..84c9d3f
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_triangle.c
@@ -0,0 +1,168 @@
+/**
+ * @file lv_draw_triangle.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw_triangle.h"
+#include "../lv_misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static void point_swap(lv_point_t * p1, lv_point_t * p2);
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+#if USE_LV_TRIANGLE != 0
+/**
+ *
+ * @param points pointer to an array with 3 points
+ * @param mask the triangle will be drawn only in this mask
+ * @param color color of the triangle
+ */
+void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, lv_color_t color)
+{
+ lv_point_t tri[3];
+
+ memcpy(tri, points, sizeof(tri));
+
+ /*Sort the vertices according to their y coordinate (0: y max, 1: y mid, 2:y min)*/
+ if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
+ if(tri[2].y < tri[1].y) point_swap(&tri[2], &tri[1]);
+ if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
+
+ /*Return is the triangle is degenerated*/
+ if(tri[0].x == tri[1].x && tri[0].y == tri[1].y) return;
+ if(tri[1].x == tri[2].x && tri[1].y == tri[2].y) return;
+ if(tri[0].x == tri[2].x && tri[0].y == tri[2].y) return;
+
+ if(tri[0].x == tri[1].x && tri[1].x == tri[2].x) return;
+ if(tri[0].y == tri[1].y && tri[1].y == tri[2].y) return;
+
+ /*Draw the triangle*/
+ lv_point_t edge1;
+ lv_coord_t dx1 = LV_MATH_ABS(tri[0].x - tri[1].x);
+ lv_coord_t sx1 = tri[0].x < tri[1].x ? 1 : -1;
+ lv_coord_t dy1 = LV_MATH_ABS(tri[0].y - tri[1].y);
+ lv_coord_t sy1 = tri[0].y < tri[1].y ? 1 : -1;
+ lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
+ lv_coord_t err_tmp1;
+
+ lv_point_t edge2;
+ lv_coord_t dx2 = LV_MATH_ABS(tri[0].x - tri[2].x);
+ lv_coord_t sx2 = tri[0].x < tri[2].x ? 1 : -1;
+ lv_coord_t dy2 = LV_MATH_ABS(tri[0].y - tri[2].y);
+ lv_coord_t sy2 = tri[0].y < tri[2].y ? 1 : -1;
+ lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
+ lv_coord_t err_tmp2;
+
+ lv_coord_t y1_tmp;
+ lv_coord_t y2_tmp;
+
+ edge1.x = tri[0].x;
+ edge1.y = tri[0].y;
+ edge2.x = tri[0].x;
+ edge2.y = tri[0].y;
+ lv_area_t act_area;
+ lv_area_t draw_area;
+
+ while(1) {
+ act_area.x1 = edge1.x;
+ act_area.x2 = edge2.x ;
+ act_area.y1 = edge1.y;
+ act_area.y2 = edge2.y ;
+
+
+ draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
+ draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
+ draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
+ draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
+ draw_area.x2--; /*Do not draw most right pixel because it will be drawn by the adjacent triangle*/
+ fill_fp(&draw_area, mask, color, LV_OPA_50);
+
+ /*Calc. the next point of edge1*/
+ y1_tmp = edge1.y;
+ do {
+ if(edge1.x == tri[1].x && edge1.y == tri[1].y) {
+
+ dx1 = LV_MATH_ABS(tri[1].x - tri[2].x);
+ sx1 = tri[1].x < tri[2].x ? 1 : -1;
+ dy1 = LV_MATH_ABS(tri[1].y - tri[2].y);
+ sy1 = tri[1].y < tri[2].y ? 1 : -1;
+ err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
+ } else if(edge1.x == tri[2].x && edge1.y == tri[2].y) return;
+ err_tmp1 = err1;
+ if(err_tmp1 > -dx1) {
+ err1 -= dy1;
+ edge1.x += sx1;
+ }
+ if(err_tmp1 < dy1) {
+ err1 += dx1;
+ edge1.y += sy1;
+ }
+ } while(edge1.y == y1_tmp);
+
+ /*Calc. the next point of edge2*/
+ y2_tmp = edge2.y;
+ do {
+ if(edge2.x == tri[2].x && edge2.y == tri[2].y) return;
+ err_tmp2 = err2;
+ if(err_tmp2 > -dx2) {
+ err2 -= dy2;
+ edge2.x += sx2;
+ }
+ if(err_tmp2 < dy2) {
+ err2 += dx2;
+ edge2.y += sy2;
+ }
+ } while(edge2.y == y2_tmp);
+ }
+}
+#endif
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+
+#if USE_LV_TRIANGLE != 0
+/**
+ * Swap two points
+ * p1 pointer to the first point
+ * p2 pointer to the second point
+ */
+static void point_swap(lv_point_t * p1, lv_point_t * p2)
+{
+ lv_point_t tmp;
+ tmp.x = p1->x;
+ tmp.y = p1->y;
+
+ p1->x = p2->x;
+ p1->y = p2->y;
+
+ p2->x = tmp.x;
+ p2->y = tmp.y;
+
+}
+
+#endif
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_triangle.h b/bdk/libs/lvgl/lv_draw/lv_draw_triangle.h
new file mode 100644
index 0000000..c3c6208
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_triangle.h
@@ -0,0 +1,51 @@
+/**
+ * @file lv_draw_triangle.h
+ *
+ */
+
+#ifndef LV_DRAW_TRIANGLE_H
+#define LV_DRAW_TRIANGLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+/*Experimental use for 3D modeling*/
+#define USE_LV_TRIANGLE 1
+
+#if USE_LV_TRIANGLE != 0
+/**
+ *
+ * @param points pointer to an array with 3 points
+ * @param mask the triangle will be drawn only in this mask
+ * @param color color of the triangle
+ */
+void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, lv_color_t color);
+#endif
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_TRIANGLE_H*/
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.c b/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.c
new file mode 100644
index 0000000..ebe5f57
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.c
@@ -0,0 +1,691 @@
+/**
+ * @file lv_vdraw.c
+ *
+ */
+
+#include "lv_draw_vbasic.h"
+
+#include
+#include
+
+#include "../lv_hal/lv_hal_disp.h"
+#include "../lv_misc/lv_area.h"
+#include "../lv_misc/lv_font.h"
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_log.h"
+
+#if LV_VDB_SIZE != 0
+
+#include
+#include "../lv_core/lv_vdb.h"
+#include "lv_draw.h"
+
+/*********************
+ * INCLUDES
+ *********************/
+
+/*********************
+ * DEFINES
+ *********************/
+#define VFILL_HW_ACC_SIZE_LIMIT 50 /*Always fill < 50 px with 'sw_color_fill' because of the hw. init overhead*/
+
+#ifndef LV_ATTRIBUTE_MEM_ALIGN
+#define LV_ATTRIBUTE_MEM_ALIGN
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
+static void sw_color_fill(lv_area_t * mem_area, lv_color_t * mem, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa);
+
+#if LV_COLOR_SCREEN_TRANSP
+static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Put a pixel in the Virtual Display Buffer
+ * @param x pixel x coordinate
+ * @param y pixel y coordinate
+ * @param mask_p fill only on this mask (truncated to VDB area)
+ * @param color pixel color
+ * @param opa opacity of the area (0..255)
+ */
+void lv_vpx(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
+{
+ if(opa < LV_OPA_MIN) return;
+ if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
+
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ /*Pixel out of the mask*/
+ if(x < mask_p->x1 || x > mask_p->x2 ||
+ y < mask_p->y1 || y > mask_p->y2) {
+ return;
+ }
+
+ uint32_t vdb_width = lv_area_get_width(&vdb_p->area);
+
+ /*Make the coordinates relative to VDB*/
+ x -= vdb_p->area.x1;
+ y -= vdb_p->area.y1;
+
+ lv_disp_t * disp = lv_disp_get_active();
+ if(disp->driver.vdb_wr) {
+ disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, x, y, color, opa);
+ } else {
+ lv_color_t * vdb_px_p = vdb_p->buf + y * vdb_width + x;
+#if LV_COLOR_SCREEN_TRANSP == 0
+ if(opa == LV_OPA_COVER) {
+ *vdb_px_p = color;
+ } else {
+ *vdb_px_p = lv_color_mix(color, *vdb_px_p, opa);
+ }
+#else
+ *vdb_px_p = color_mix_2_alpha(*vdb_px_p, (*vdb_px_p).alpha, color, opa);
+#endif
+ }
+}
+
+
+/**
+ * Fill an area in the Virtual Display Buffer
+ * @param cords_p coordinates of the area to fill
+ * @param mask_p fill only o this mask (truncated to VDB area)
+ * @param color fill color
+ * @param opa opacity of the area (0..255)
+ */
+void lv_vfill(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ lv_color_t color, lv_opa_t opa)
+{
+ if(opa < LV_OPA_MIN) return;
+ if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
+
+ lv_area_t res_a;
+ bool union_ok;
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ /*Get the union of cord and mask*/
+ /* The mask is already truncated to the vdb size
+ * in 'lv_refr_area_with_vdb' function */
+ union_ok = lv_area_intersect(&res_a, cords_p, mask_p);
+
+ /*If there are common part of the three area then draw to the vdb*/
+ if(union_ok == false) return;
+
+ lv_area_t vdb_rel_a; /*Stores relative coordinates on vdb*/
+ vdb_rel_a.x1 = res_a.x1 - vdb_p->area.x1;
+ vdb_rel_a.y1 = res_a.y1 - vdb_p->area.y1;
+ vdb_rel_a.x2 = res_a.x2 - vdb_p->area.x1;
+ vdb_rel_a.y2 = res_a.y2 - vdb_p->area.y1;
+
+ lv_color_t * vdb_buf_tmp = vdb_p->buf;
+ uint32_t vdb_width = lv_area_get_width(&vdb_p->area);
+ /*Move the vdb_tmp to the first row*/
+ vdb_buf_tmp += vdb_width * vdb_rel_a.y1;
+
+
+#if USE_LV_GPU
+ static LV_ATTRIBUTE_MEM_ALIGN lv_color_t color_array_tmp[LV_HOR_RES]; /*Used by 'lv_disp_mem_blend'*/
+ static lv_coord_t last_width = -1;
+
+ lv_coord_t w = lv_area_get_width(&vdb_rel_a);
+ /*Don't use hw. acc. for every small fill (because of the init overhead)*/
+ if(w < VFILL_HW_ACC_SIZE_LIMIT) {
+ sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
+ }
+ /*Not opaque fill*/
+ else if(opa == LV_OPA_COVER) {
+ /*Use hw fill if present*/
+ if(lv_disp_is_mem_fill_supported()) {
+ lv_coord_t row;
+ for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
+ lv_disp_mem_fill(&vdb_buf_tmp[vdb_rel_a.x1], w, color);
+ vdb_buf_tmp += vdb_width;
+ }
+ }
+ /*Use hw blend if present and the area is not too small*/
+ else if(lv_area_get_height(&vdb_rel_a) > VFILL_HW_ACC_SIZE_LIMIT &&
+ lv_disp_is_mem_blend_supported()) {
+ /*Fill a one line sized buffer with a color and blend this later*/
+ if(color_array_tmp[0].full != color.full || last_width != w) {
+ uint16_t i;
+ for(i = 0; i < w; i++) {
+ color_array_tmp[i].full = color.full;
+ }
+ last_width = w;
+ }
+
+ /*Blend the filled line to every line VDB line-by-line*/
+ lv_coord_t row;
+ for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
+ lv_disp_mem_blend(&vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
+ vdb_buf_tmp += vdb_width;
+ }
+
+ }
+ /*Else use sw fill if no better option*/
+ else {
+ sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
+ }
+
+ }
+ /*Fill with opacity*/
+ else {
+ /*Use hw blend if present*/
+ if(lv_disp_is_mem_blend_supported()) {
+ if(color_array_tmp[0].full != color.full || last_width != w) {
+ uint16_t i;
+ for(i = 0; i < w; i++) {
+ color_array_tmp[i].full = color.full;
+ }
+
+ last_width = w;
+ }
+ lv_coord_t row;
+ for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
+ lv_disp_mem_blend(&vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
+ vdb_buf_tmp += vdb_width;
+ }
+
+ }
+ /*Use sw fill with opa if no better option*/
+ else {
+ sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
+ }
+
+ }
+#else
+ sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
+#endif
+}
+
+/**
+ * Draw a letter in the Virtual Display Buffer
+ * @param pos_p left-top coordinate of the latter
+ * @param mask_p the letter will be drawn only on this area (truncated to VDB area)
+ * @param font_p pointer to font
+ * @param letter a letter to draw
+ * @param color color of letter
+ * @param opa opacity of letter (0..255)
+ */
+void lv_vletter(const lv_point_t * pos_p, const lv_area_t * mask_p,
+ const lv_font_t * font_p, uint32_t letter,
+ lv_color_t color, lv_opa_t opa)
+{
+ const uint8_t bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
+ const uint8_t bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
+ const uint8_t bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
+ 68, 85, 102, 119,
+ 136, 153, 170, 187,
+ 204, 221, 238, 255
+ };
+ if(opa < LV_OPA_MIN) return;
+ if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
+
+ if(font_p == NULL) {
+ LV_LOG_WARN("Font: character's bitmap not found");
+ return;
+ }
+
+ lv_coord_t pos_x = pos_p->x;
+ lv_coord_t pos_y = pos_p->y;
+ uint8_t letter_w = lv_font_get_real_width(font_p, letter);
+ uint8_t letter_h = lv_font_get_height(font_p);
+ uint8_t bpp = lv_font_get_bpp(font_p, letter); /*Bit per pixel (1,2, 4 or 8)*/
+ const uint8_t * bpp_opa_table;
+ uint8_t mask_init;
+ uint8_t mask;
+
+ if(lv_font_is_monospace(font_p, letter)) {
+ pos_x += (lv_font_get_width(font_p, letter) - letter_w) / 2;
+ }
+
+
+ switch(bpp) {
+ case 1:
+ bpp_opa_table = bpp1_opa_table;
+ mask_init = 0x80;
+ break;
+ case 2:
+ bpp_opa_table = bpp2_opa_table;
+ mask_init = 0xC0;
+ break;
+ case 4:
+ bpp_opa_table = bpp4_opa_table;
+ mask_init = 0xF0;
+ break;
+ case 8:
+ bpp_opa_table = NULL;
+ mask_init = 0xFF;
+ break; /*No opa table, pixel value will be used directly*/
+ default:
+ return; /*Invalid bpp. Can't render the letter*/
+ }
+
+ const uint8_t * map_p = lv_font_get_bitmap(font_p, letter);
+
+ if(map_p == NULL) return;
+
+ /*If the letter is completely out of mask don't draw it */
+ if(pos_x + letter_w < mask_p->x1 || pos_x > mask_p->x2 ||
+ pos_y + letter_h < mask_p->y1 || pos_y > mask_p->y2) return;
+
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ lv_coord_t vdb_width = lv_area_get_width(&vdb_p->area);
+ lv_color_t * vdb_buf_tmp = vdb_p->buf;
+ lv_coord_t col, row;
+ uint8_t col_bit;
+ uint8_t col_byte_cnt;
+ uint8_t width_byte_scr = letter_w >> 3; /*Width in bytes (on the screen finally) (e.g. w = 11 -> 2 bytes wide)*/
+ if(letter_w & 0x7) width_byte_scr++;
+ uint8_t width_byte_bpp = (letter_w * bpp) >> 3; /*Letter width in byte. Real width in the font*/
+ if((letter_w * bpp) & 0x7) width_byte_bpp++;
+
+ /* Calculate the col/row start/end on the map*/
+ lv_coord_t col_start = pos_x >= mask_p->x1 ? 0 : mask_p->x1 - pos_x;
+ lv_coord_t col_end = pos_x + letter_w <= mask_p->x2 ? letter_w : mask_p->x2 - pos_x + 1;
+ lv_coord_t row_start = pos_y >= mask_p->y1 ? 0 : mask_p->y1 - pos_y;
+ lv_coord_t row_end = pos_y + letter_h <= mask_p->y2 ? letter_h : mask_p->y2 - pos_y + 1;
+
+ /*Set a pointer on VDB to the first pixel of the letter*/
+ vdb_buf_tmp += ((pos_y - vdb_p->area.y1) * vdb_width)
+ + pos_x - vdb_p->area.x1;
+
+ /*If the letter is partially out of mask the move there on VDB*/
+ vdb_buf_tmp += (row_start * vdb_width) + col_start;
+
+ /*Move on the map too*/
+ map_p += (row_start * width_byte_bpp) + ((col_start * bpp) >> 3);
+
+ lv_disp_t * disp = lv_disp_get_active();
+
+ uint8_t letter_px;
+ lv_opa_t px_opa;
+ for(row = row_start; row < row_end; row ++) {
+ col_byte_cnt = 0;
+ col_bit = (col_start * bpp) % 8;
+ mask = mask_init >> col_bit;
+ for(col = col_start; col < col_end; col ++) {
+ letter_px = (*map_p & mask) >> (8 - col_bit - bpp);
+ if(letter_px != 0) {
+ if(opa == LV_OPA_COVER) {
+ px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px];
+ } else {
+ px_opa = bpp == 8 ?
+ (uint16_t)((uint16_t)letter_px * opa) >> 8 :
+ (uint16_t)((uint16_t)bpp_opa_table[letter_px] * opa) >> 8;
+ }
+
+ if(disp->driver.vdb_wr) {
+ disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width,
+ (col + pos_x) - vdb_p->area.x1, (row + pos_y) - vdb_p->area.y1,
+ color, px_opa);
+ } else {
+#if LV_COLOR_SCREEN_TRANSP == 0
+ *vdb_buf_tmp = lv_color_mix(color, *vdb_buf_tmp, px_opa);
+#else
+ *vdb_buf_tmp = color_mix_2_alpha(*vdb_buf_tmp, (*vdb_buf_tmp).alpha, color, px_opa);
+#endif
+ }
+ }
+
+ vdb_buf_tmp++;
+
+ if(col_bit < 8 - bpp) {
+ col_bit += bpp;
+ mask = mask >> bpp;
+ } else {
+ col_bit = 0;
+ col_byte_cnt ++;
+ mask = mask_init;
+ map_p ++;
+ }
+ }
+
+ map_p += (width_byte_bpp) - col_byte_cnt;
+ vdb_buf_tmp += vdb_width - (col_end - col_start); /*Next row in VDB*/
+ }
+}
+
+/**
+ * Draw a color map to the display (image)
+ * @param cords_p coordinates the color map
+ * @param mask_p the map will drawn only on this area (truncated to VDB area)
+ * @param map_p pointer to a lv_color_t array
+ * @param opa opacity of the map
+ * @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
+ * @param alpha_byte true: extra alpha byte is inserted for every pixel
+ * @param recolor mix the pixels with this color
+ * @param recolor_opa the intense of recoloring
+ */
+void lv_vmap(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa)
+{
+
+ if(opa < LV_OPA_MIN) return;
+ if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
+
+ lv_area_t masked_a;
+ bool union_ok;
+ lv_vdb_t * vdb_p = lv_vdb_get();
+ if(!vdb_p) {
+ LV_LOG_WARN("Invalid VDB pointer");
+ return;
+ }
+
+ /*Get the union of map size and mask*/
+ /* The mask is already truncated to the vdb size
+ * in 'lv_refr_area_with_vdb' function */
+ union_ok = lv_area_intersect(&masked_a, cords_p, mask_p);
+
+ /*If there are common part of the three area then draw to the vdb*/
+ if(union_ok == false) return;
+
+ /*The pixel size in byte is different if an alpha byte is added too*/
+ uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
+
+ /*If the map starts OUT of the masked area then calc. the first pixel*/
+ lv_coord_t map_width = lv_area_get_width(cords_p);
+ if(cords_p->y1 < masked_a.y1) {
+ map_p += (uint32_t) map_width * ((masked_a.y1 - cords_p->y1)) * px_size_byte;
+ }
+ if(cords_p->x1 < masked_a.x1) {
+ map_p += (masked_a.x1 - cords_p->x1) * px_size_byte;
+ }
+
+ /*Stores coordinates relative to the current VDB*/
+ masked_a.x1 = masked_a.x1 - vdb_p->area.x1;
+ masked_a.y1 = masked_a.y1 - vdb_p->area.y1;
+ masked_a.x2 = masked_a.x2 - vdb_p->area.x1;
+ masked_a.y2 = masked_a.y2 - vdb_p->area.y1;
+
+ lv_coord_t vdb_width = lv_area_get_width(&vdb_p->area);
+ lv_color_t * vdb_buf_tmp = vdb_p->buf;
+ vdb_buf_tmp += (uint32_t) vdb_width * masked_a.y1; /*Move to the first row*/
+ vdb_buf_tmp += (uint32_t) masked_a.x1; /*Move to the first col*/
+
+ lv_coord_t row;
+ lv_coord_t map_useful_w = lv_area_get_width(&masked_a);
+
+ lv_disp_t * disp = lv_disp_get_active();
+
+ /*The simplest case just copy the pixels into the VDB*/
+ if(chroma_key == false && alpha_byte == false && opa == LV_OPA_COVER && recolor_opa == LV_OPA_TRANSP) {
+
+ /*Use the custom VDB write function is exists*/
+ if(disp->driver.vdb_wr) {
+ lv_coord_t col;
+ for(row = masked_a.y1; row <= masked_a.y2; row++) {
+ for(col = 0; col < map_useful_w; col++) {
+ lv_color_t px_color = *((lv_color_t *)&map_p[(uint32_t)col * px_size_byte]);
+ disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, col + masked_a.x1, row, px_color, opa);
+ }
+ map_p += map_width * px_size_byte; /*Next row on the map*/
+ }
+ }
+ /*Normal native VDB*/
+ else {
+ for(row = masked_a.y1; row <= masked_a.y2; row++) {
+#if USE_LV_GPU
+ if(lv_disp_is_mem_blend_supported() == false) {
+ sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
+ } else {
+ lv_disp_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
+ }
+#else
+ sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
+#endif
+ map_p += map_width * px_size_byte; /*Next row on the map*/
+ vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
+ }
+ }
+ }
+
+ /*In the other cases every pixel need to be checked one-by-one*/
+ else {
+ lv_color_t chroma_key_color = LV_COLOR_TRANSP;
+ lv_coord_t col;
+ lv_color_t last_img_px = LV_COLOR_BLACK;
+ lv_color_t recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
+ for(row = masked_a.y1; row <= masked_a.y2; row++) {
+ for(col = 0; col < map_useful_w; col++) {
+ lv_opa_t opa_result = opa;
+ uint8_t * px_color_p = (uint8_t *) &map_p[(uint32_t)col * px_size_byte];
+ lv_color_t px_color;
+
+ /*Calculate with the pixel level alpha*/
+ if(alpha_byte) {
+#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
+ px_color.full = px_color_p[0];
+#elif LV_COLOR_DEPTH == 16
+ /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
+ px_color.full = px_color_p[0] + (px_color_p[1] << 8);
+#elif LV_COLOR_DEPTH == 32
+ px_color = *((lv_color_t *)px_color_p);
+#endif
+ lv_opa_t px_opa = *(px_color_p + LV_IMG_PX_SIZE_ALPHA_BYTE - 1);
+ if(px_opa == LV_OPA_TRANSP) continue;
+ else if(px_opa != LV_OPA_COVER) opa_result = (uint32_t)((uint32_t)px_opa * opa_result) >> 8;
+ } else {
+ px_color = *((lv_color_t *)px_color_p);
+ }
+
+ /*Handle chroma key*/
+ if(chroma_key && px_color.full == chroma_key_color.full) continue;
+
+ /*Re-color the pixel if required*/
+ if(recolor_opa != LV_OPA_TRANSP) {
+ if(last_img_px.full != px_color.full) { /*Minor acceleration: calculate only for new colors (save the last)*/
+ last_img_px = px_color;
+ recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
+ }
+ /*Handle custom VDB write is present*/
+ if(disp->driver.vdb_wr) {
+ disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, col + masked_a.x1, row, recolored_px, opa_result);
+ }
+ /*Normal native VDB write*/
+ else {
+ if(opa_result == LV_OPA_COVER) vdb_buf_tmp[col].full = recolored_px.full;
+ else vdb_buf_tmp[col] = lv_color_mix(recolored_px, vdb_buf_tmp[col], opa_result);
+ }
+ } else {
+ /*Handle custom VDB write is present*/
+ if(disp->driver.vdb_wr) {
+ disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, col + masked_a.x1, row, px_color, opa_result);
+ }
+ /*Normal native VDB write*/
+ else {
+ if(opa_result == LV_OPA_COVER) vdb_buf_tmp[col] = px_color;
+ else {
+#if LV_COLOR_SCREEN_TRANSP == 0
+ vdb_buf_tmp[col] = lv_color_mix(px_color, vdb_buf_tmp[col], opa_result);
+#else
+ vdb_buf_tmp[col] = color_mix_2_alpha(vdb_buf_tmp[col], vdb_buf_tmp[col].alpha, px_color, opa_result);
+#endif
+ }
+ }
+ }
+ }
+
+ map_p += map_width * px_size_byte; /*Next row on the map*/
+ vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
+ }
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Blend pixels to destination memory using opacity
+ * @param dest a memory address. Copy 'src' here.
+ * @param src pointer to pixel map. Copy it to 'dest'.
+ * @param length number of pixels in 'src'
+ * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
+ */
+static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
+{
+ if(opa == LV_OPA_COVER) {
+ memcpy(dest, src, length * sizeof(lv_color_t));
+ } else {
+ uint32_t col;
+ for(col = 0; col < length; col++) {
+ dest[col] = lv_color_mix(src[col], dest[col], opa);
+ }
+ }
+}
+
+/**
+ *
+ * @param mem_area coordinates of 'mem' memory area
+ * @param mem a memory address. Considered to a rectangular window according to 'mem_area'
+ * @param fill_area coordinates of an area to fill. Relative to 'mem_area'.
+ * @param color fill color
+ * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
+ */
+static void sw_color_fill(lv_area_t * mem_area, lv_color_t * mem, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
+{
+ /*Set all row in vdb to the given color*/
+ lv_coord_t row;
+ lv_coord_t col;
+ lv_coord_t mem_width = lv_area_get_width(mem_area);
+
+ lv_disp_t * disp = lv_disp_get_active();
+ if(disp->driver.vdb_wr) {
+ for(col = fill_area->x1; col <= fill_area->x2; col++) {
+ for(row = fill_area->y1; row <= fill_area->y2; row++) {
+ disp->driver.vdb_wr((uint8_t *)mem, mem_width, col, row, color, opa);
+ }
+ }
+ } else {
+ mem += fill_area->y1 * mem_width; /*Go to the first row*/
+
+ /*Run simpler function without opacity*/
+ if(opa == LV_OPA_COVER) {
+
+ /*Fill the first row with 'color'*/
+ for(col = fill_area->x1; col <= fill_area->x2; col++) {
+ mem[col] = color;
+ }
+
+ /*Copy the first row to all other rows*/
+ lv_color_t * mem_first = &mem[fill_area->x1];
+ lv_coord_t copy_size = (fill_area->x2 - fill_area->x1 + 1) * sizeof(lv_color_t);
+ mem += mem_width;
+
+ for(row = fill_area->y1 + 1; row <= fill_area->y2; row++) {
+ memcpy(&mem[fill_area->x1], mem_first, copy_size);
+ mem += mem_width;
+ }
+ }
+ /*Calculate with alpha too*/
+ else {
+
+#if LV_COLOR_SCREEN_TRANSP == 0
+ lv_color_t bg_tmp = LV_COLOR_BLACK;
+ lv_color_t opa_tmp = lv_color_mix(color, bg_tmp, opa);
+#endif
+ for(row = fill_area->y1; row <= fill_area->y2; row++) {
+ for(col = fill_area->x1; col <= fill_area->x2; col++) {
+#if LV_COLOR_SCREEN_TRANSP == 0
+ /*If the bg color changed recalculate the result color*/
+ if(mem[col].full != bg_tmp.full) {
+ bg_tmp = mem[col];
+ opa_tmp = lv_color_mix(color, bg_tmp, opa);
+ }
+
+ mem[col] = opa_tmp;
+
+#else
+ mem[col] = color_mix_2_alpha(mem[col], mem[col].alpha, color, opa);
+#endif
+ }
+ mem += mem_width;
+ }
+ }
+ }
+}
+
+#if LV_COLOR_SCREEN_TRANSP
+
+/**
+ * Mix two colors. Both color can have alpha value. It requires ARGB888 colors.
+ * @param bg_color background color
+ * @param bg_opa alpha of the background color
+ * @param fg_color foreground color
+ * @param fg_opa alpha of the foreground color
+ * @return the mixed color. the alpha channel (color.alpha) contains the result alpha
+ */
+static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa)
+{
+ /* Pick the foreground if it's fully opaque or the Background is fully transparent*/
+ if(fg_opa == LV_OPA_COVER && bg_opa <= LV_OPA_MIN) {
+ fg_color.alpha = fg_opa;
+ return fg_color;
+ }
+ /*Transparent foreground: use the Background*/
+ else if(fg_opa <= LV_OPA_MIN) {
+ return bg_color;
+ }
+ /*Opaque background: use simple mix*/
+ else if(bg_opa >= LV_OPA_MAX) {
+ return lv_color_mix(fg_color, bg_color, fg_opa);
+ }
+ /*Both colors have alpha. Expensive calculation need to be applied*/
+ else {
+ /*Save the parameters and the result. If they will be asked again don't compute again*/
+ static lv_opa_t fg_opa_save = 0;
+ static lv_opa_t bg_opa_save = 0;
+ static lv_color_t c = {{0}};
+
+ if(fg_opa != fg_opa_save || bg_opa != bg_opa_save) {
+ fg_opa_save = fg_opa;
+ bg_opa_save = bg_opa;
+ /*Info: https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
+ lv_opa_t alpha_res = 255 - ((uint16_t)((uint16_t)(255 - fg_opa) * (255 - bg_opa)) >> 8);
+ if(alpha_res == 0) {
+ while(1);
+ }
+ lv_opa_t ratio = (uint16_t)((uint16_t) fg_opa * 255) / alpha_res;
+ c = lv_color_mix(fg_color, bg_color, ratio);
+ c.alpha = alpha_res;
+ }
+ return c;
+
+ }
+}
+#endif /*LV_COLOR_SCREEN_TRANSP*/
+
+#endif
diff --git a/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.h b/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.h
new file mode 100644
index 0000000..88838b7
--- /dev/null
+++ b/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.h
@@ -0,0 +1,89 @@
+/**
+ * @file lv_draw_vbasic.h
+ *
+ */
+
+#ifndef LV_DRAW_VBASIC_H
+#define LV_DRAW_VBASIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#if LV_VDB_SIZE != 0
+
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_area.h"
+#include "../lv_misc/lv_font.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+void lv_vpx(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa);
+/**
+ * Fill an area in the Virtual Display Buffer
+ * @param cords_p coordinates of the area to fill
+ * @param mask_p fill only o this mask
+ * @param color fill color
+ * @param opa opacity of the area (0..255)
+ */
+void lv_vfill(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ lv_color_t color, lv_opa_t opa);
+
+/**
+ * Draw a letter in the Virtual Display Buffer
+ * @param pos_p left-top coordinate of the latter
+ * @param mask_p the letter will be drawn only on this area
+ * @param font_p pointer to font
+ * @param letter a letter to draw
+ * @param color color of letter
+ * @param opa opacity of letter (0..255)
+ */
+void lv_vletter(const lv_point_t * pos_p, const lv_area_t * mask_p,
+ const lv_font_t * font_p, uint32_t letter,
+ lv_color_t color, lv_opa_t opa);
+
+/**
+ * Draw a color map to the display (image)
+ * @param cords_p coordinates the color map
+ * @param mask_p the map will drawn only on this area (truncated to VDB area)
+ * @param map_p pointer to a lv_color_t array
+ * @param opa opacity of the map
+ * @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
+ * @param alpha_byte true: extra alpha byte is inserted for every pixel
+ * @param recolor mix the pixels with this color
+ * @param recolor_opa the intense of recoloring
+ */
+void lv_vmap(const lv_area_t * cords_p, const lv_area_t * mask_p,
+ const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
+ lv_color_t recolor, lv_opa_t recolor_opa);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*LV_VDB_SIZE != 0*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_DRAW_RBASIC_H*/
diff --git a/bdk/libs/lvgl/lv_fonts/hekate_symbol_120.c b/bdk/libs/lvgl/lv_fonts/hekate_symbol_120.c
new file mode 100644
index 0000000..c97eda6
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/hekate_symbol_120.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "../lv_misc/lv_font.h"
+
+#include
+
+#if USE_HEKATE_SYMBOL_120 != 0 /*Can be enabled in lv_conf.h*/
+
+/***********************************************************************************
+ * hekate-symbols-huge.ttf 120 px Font in U+f002 () .. U+f007 () range with all bpp
+ * Sparse font with only these characters:
+***********************************************************************************/
+
+/*Store the glyph descriptions*/
+static const lv_font_glyph_dsc_t hekate_symbol_120_glyph_dsc[] =
+{
+#if USE_HEKATE_SYMBOL_120 == 8
+ {.w_px = 103, .glyph_index = 0}, /*Unicode: U+f002 ()*/
+ {.w_px = 103, .glyph_index = 12360}, /*Unicode: U+f003 ()*/
+ {.w_px = 103, .glyph_index = 24720}, /*Unicode: U+f005 ()*/
+ {.w_px = 103, .glyph_index = 37080}, /*Unicode: U+f007 ()*/
+
+#endif
+};
+
+lv_font_t hekate_symbol_120 =
+{
+ .unicode_first = LV_SYMBOL_GLYPH_FIRST, /*First Unicode letter in this font*/
+ .unicode_last = LV_SYMBOL_GLYPH_LAST, /*Last Unicode letter in this font*/
+ .h_px = 120, /*Font height in pixels*/
+ .glyph_bitmap = (const uint8_t *)(NYX_RES_ADDR + 0x36E00), /*Bitmap of glyphs*/
+ .glyph_dsc = hekate_symbol_120_glyph_dsc, /*Description of glyphs*/
+ .glyph_cnt = 4, /*Number of glyphs in the font*/
+ .unicode_list = NULL, /*List of unicode characters*/
+ .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/
+ .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/
+#if USE_HEKATE_SYMBOL_120 == 8
+ .bpp = 8, /*Bit per pixel*/
+#endif
+ .monospace = 0, /*Fix width (0: if not used)*/
+ .next_page = NULL, /*Pointer to a font extension*/
+};
+
+#endif /*USE_HEKATE_SYMBOL_100*/
diff --git a/bdk/libs/lvgl/lv_fonts/hekate_symbol_20.c b/bdk/libs/lvgl/lv_fonts/hekate_symbol_20.c
new file mode 100644
index 0000000..426bde8
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/hekate_symbol_20.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "../lv_misc/lv_font.h"
+
+#include
+
+#if USE_HEKATE_SYMBOL_20 != 0 /*Can be enabled in lv_conf.h*/
+
+/***********************************************************************************
+ * hekate-symbols.ttf 20 px Font in U+f000 () .. U+f2ee () range with all bpp
+ * Sparse font with only these characters:
+***********************************************************************************/
+
+/*Store the glyph descriptions*/
+static const lv_font_glyph_dsc_t hekate_symbol_20_glyph_dsc[] =
+{
+#if USE_HEKATE_SYMBOL_20 == 4
+ {.w_px = 5, .glyph_index = 0}, /*Unicode: U+f001 ()*/
+ {.w_px = 16, .glyph_index = 60}, /*Unicode: U+f008 ()*/
+ {.w_px = 20, .glyph_index = 220}, /*Unicode: U+f00b ()*/
+ {.w_px = 22, .glyph_index = 420}, /*Unicode: U+f00c ()*/
+ {.w_px = 16, .glyph_index = 640}, /*Unicode: U+f00d ()*/
+ {.w_px = 18, .glyph_index = 800}, /*Unicode: U+f011 ()*/
+ {.w_px = 18, .glyph_index = 980}, /*Unicode: U+f013 ()*/
+ {.w_px = 16, .glyph_index = 1160}, /*Unicode: U+f014 ()*/
+ {.w_px = 23, .glyph_index = 1320}, /*Unicode: U+f015 ()*/
+ {.w_px = 18, .glyph_index = 1560}, /*Unicode: U+f019 ()*/
+ {.w_px = 23, .glyph_index = 1740}, /*Unicode: U+f01c ()*/
+ {.w_px = 18, .glyph_index = 1980}, /*Unicode: U+f021 ()*/
+ {.w_px = 18, .glyph_index = 2160}, /*Unicode: U+f026 ()*/
+ {.w_px = 18, .glyph_index = 2340}, /*Unicode: U+f027 ()*/
+ {.w_px = 13, .glyph_index = 2520}, /*Unicode: U+f028 ()*/
+ {.w_px = 13, .glyph_index = 2660}, /*Unicode: U+f03e ()*/
+ {.w_px = 16, .glyph_index = 2800}, /*Unicode: U+f040 ()*/
+ {.w_px = 13, .glyph_index = 2960}, /*Unicode: U+f048 ()*/
+ {.w_px = 13, .glyph_index = 3100}, /*Unicode: U+f04b ()*/
+ {.w_px = 13, .glyph_index = 3240}, /*Unicode: U+f04c ()*/
+ {.w_px = 9, .glyph_index = 3380}, /*Unicode: U+f04d ()*/
+ {.w_px = 23, .glyph_index = 3480}, /*Unicode: U+f051 ()*/
+ {.w_px = 21, .glyph_index = 3720}, /*Unicode: U+f052 ()*/
+ {.w_px = 11, .glyph_index = 3940}, /*Unicode: U+f053 ()*/
+ {.w_px = 11, .glyph_index = 4060}, /*Unicode: U+f054 ()*/
+ {.w_px = 18, .glyph_index = 4180}, /*Unicode: U+f067 ()*/
+ {.w_px = 18, .glyph_index = 4360}, /*Unicode: U+f068 ()*/
+ {.w_px = 20, .glyph_index = 4540}, /*Unicode: U+f071 ()*/
+ {.w_px = 20, .glyph_index = 4740}, /*Unicode: U+f074 ()*/
+ {.w_px = 18, .glyph_index = 4940}, /*Unicode: U+f077 ()*/
+ {.w_px = 18, .glyph_index = 5120}, /*Unicode: U+f078 ()*/
+ {.w_px = 18, .glyph_index = 5300}, /*Unicode: U+f079 ()*/
+ {.w_px = 20, .glyph_index = 5480}, /*Unicode: U+f07b ()*/
+ {.w_px = 18, .glyph_index = 5680}, /*Unicode: U+f093 ()*/
+ {.w_px = 25, .glyph_index = 5860}, /*Unicode: U+f095 ()*/
+ {.w_px = 18, .glyph_index = 6120}, /*Unicode: U+f0c4 ()*/
+ {.w_px = 16, .glyph_index = 6300}, /*Unicode: U+f0c5 ()*/
+ {.w_px = 17, .glyph_index = 6460}, /*Unicode: U+f0c7 ()*/
+ {.w_px = 8, .glyph_index = 6640}, /*Unicode: U+f0e7 ()*/
+ {.w_px = 12, .glyph_index = 6720}, /*Unicode: U+f0f3 ()*/
+ {.w_px = 23, .glyph_index = 6840}, /*Unicode: U+f11c ()*/
+ {.w_px = 18, .glyph_index = 7080}, /*Unicode: U+f124 ()*/
+ {.w_px = 13, .glyph_index = 7260}, /*Unicode: U+f15b ()*/
+ {.w_px = 20, .glyph_index = 7400}, /*Unicode: U+f1eb ()*/
+ {.w_px = 26, .glyph_index = 7600}, /*Unicode: U+f240 ()*/
+ {.w_px = 26, .glyph_index = 7860}, /*Unicode: U+f241 ()*/
+ {.w_px = 26, .glyph_index = 8120}, /*Unicode: U+f242 ()*/
+ {.w_px = 26, .glyph_index = 8380}, /*Unicode: U+f243 ()*/
+ {.w_px = 26, .glyph_index = 8640}, /*Unicode: U+f244 ()*/
+ {.w_px = 20, .glyph_index = 8900}, /*Unicode: U+f293 ()*/
+
+#elif USE_HEKATE_SYMBOL_20 == 8
+ {.w_px = 5, .glyph_index = 0}, /*Unicode: U+f001 ()*/
+ {.w_px = 16, .glyph_index = 100}, /*Unicode: U+f008 ()*/
+ {.w_px = 20, .glyph_index = 420}, /*Unicode: U+f00b ()*/
+ {.w_px = 22, .glyph_index = 820}, /*Unicode: U+f00c ()*/
+ {.w_px = 16, .glyph_index = 1260}, /*Unicode: U+f00d ()*/
+ {.w_px = 18, .glyph_index = 1580}, /*Unicode: U+f011 ()*/
+ {.w_px = 18, .glyph_index = 1940}, /*Unicode: U+f013 ()*/
+ {.w_px = 16, .glyph_index = 2300}, /*Unicode: U+f014 ()*/
+ {.w_px = 23, .glyph_index = 2620}, /*Unicode: U+f015 ()*/
+ {.w_px = 18, .glyph_index = 3080}, /*Unicode: U+f019 ()*/
+ {.w_px = 23, .glyph_index = 3440}, /*Unicode: U+f01c ()*/
+ {.w_px = 18, .glyph_index = 3900}, /*Unicode: U+f021 ()*/
+ {.w_px = 18, .glyph_index = 4260}, /*Unicode: U+f026 ()*/
+ {.w_px = 18, .glyph_index = 4620}, /*Unicode: U+f027 ()*/
+ {.w_px = 13, .glyph_index = 4980}, /*Unicode: U+f028 ()*/
+ {.w_px = 13, .glyph_index = 5240}, /*Unicode: U+f03e ()*/
+ {.w_px = 16, .glyph_index = 5500}, /*Unicode: U+f040 ()*/
+ {.w_px = 13, .glyph_index = 5820}, /*Unicode: U+f048 ()*/
+ {.w_px = 13, .glyph_index = 6080}, /*Unicode: U+f04b ()*/
+ {.w_px = 13, .glyph_index = 6340}, /*Unicode: U+f04c ()*/
+ {.w_px = 9, .glyph_index = 6600}, /*Unicode: U+f04d ()*/
+ {.w_px = 23, .glyph_index = 6780}, /*Unicode: U+f051 ()*/
+ {.w_px = 21, .glyph_index = 7240}, /*Unicode: U+f052 ()*/
+ {.w_px = 11, .glyph_index = 7660}, /*Unicode: U+f053 ()*/
+ {.w_px = 11, .glyph_index = 7880}, /*Unicode: U+f054 ()*/
+ {.w_px = 18, .glyph_index = 8100}, /*Unicode: U+f067 ()*/
+ {.w_px = 18, .glyph_index = 8460}, /*Unicode: U+f068 ()*/
+ {.w_px = 20, .glyph_index = 8820}, /*Unicode: U+f071 ()*/
+ {.w_px = 20, .glyph_index = 9220}, /*Unicode: U+f074 ()*/
+ {.w_px = 18, .glyph_index = 9620}, /*Unicode: U+f077 ()*/
+ {.w_px = 18, .glyph_index = 9980}, /*Unicode: U+f078 ()*/
+ {.w_px = 18, .glyph_index = 10340}, /*Unicode: U+f079 ()*/
+ {.w_px = 20, .glyph_index = 10700}, /*Unicode: U+f07b ()*/
+ {.w_px = 18, .glyph_index = 11100}, /*Unicode: U+f093 ()*/
+ {.w_px = 25, .glyph_index = 11460}, /*Unicode: U+f095 ()*/
+ {.w_px = 18, .glyph_index = 11960}, /*Unicode: U+f0c4 ()*/
+ {.w_px = 16, .glyph_index = 12320}, /*Unicode: U+f0c5 ()*/
+ {.w_px = 17, .glyph_index = 12640}, /*Unicode: U+f0c7 ()*/
+ {.w_px = 8, .glyph_index = 12980}, /*Unicode: U+f0e7 ()*/
+ {.w_px = 12, .glyph_index = 13140}, /*Unicode: U+f0f3 ()*/
+ {.w_px = 23, .glyph_index = 13380}, /*Unicode: U+f11c ()*/
+ {.w_px = 18, .glyph_index = 13840}, /*Unicode: U+f124 ()*/
+ {.w_px = 13, .glyph_index = 14200}, /*Unicode: U+f15b ()*/
+ {.w_px = 20, .glyph_index = 14460}, /*Unicode: U+f1eb ()*/
+ {.w_px = 26, .glyph_index = 14860}, /*Unicode: U+f240 ()*/
+ {.w_px = 26, .glyph_index = 15380}, /*Unicode: U+f241 ()*/
+ {.w_px = 26, .glyph_index = 15900}, /*Unicode: U+f242 ()*/
+ {.w_px = 26, .glyph_index = 16420}, /*Unicode: U+f243 ()*/
+ {.w_px = 26, .glyph_index = 16940}, /*Unicode: U+f244 ()*/
+ {.w_px = 20, .glyph_index = 17460}, /*Unicode: U+f293 ()*/
+
+#endif
+};
+
+lv_font_t hekate_symbol_20 =
+{
+ .unicode_first = LV_SYMBOL_GLYPH_FIRST, /*First Unicode letter in this font*/
+ .unicode_last = LV_SYMBOL_GLYPH_LAST, /*Last Unicode letter in this font*/
+ .h_px = 20, /*Font height in pixels*/
+ //.glyph_bitmap = hekate_symbol_20_glyph_bitmap, /*Bitmap of glyphs*/
+ .glyph_bitmap = (const uint8_t *)(NYX_RES_ADDR + 0xFC00),
+ .glyph_dsc = hekate_symbol_20_glyph_dsc, /*Description of glyphs*/
+ .glyph_cnt = 50, /*Number of glyphs in the font*/
+ .unicode_list = NULL, /*List of unicode characters*/
+ .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/
+ .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/
+#if USE_HEKATE_SYMBOL_20 == 4
+ .bpp = 4, /*Bit per pixel*/
+#elif USE_HEKATE_SYMBOL_20 == 8
+ .bpp = 8, /*Bit per pixel*/
+#endif
+ .monospace = 0, /*Fix width (0: if not used)*/
+ .next_page = NULL, /*Pointer to a font extension*/
+};
+
+#endif /*USE_HEKATE_SYMBOL_20*/
diff --git a/bdk/libs/lvgl/lv_fonts/hekate_symbol_30.c b/bdk/libs/lvgl/lv_fonts/hekate_symbol_30.c
new file mode 100644
index 0000000..4e0da47
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/hekate_symbol_30.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "../lv_misc/lv_font.h"
+
+#include
+
+#if USE_HEKATE_SYMBOL_30 != 0 /*Can be enabled in lv_conf.h*/
+
+/***********************************************************************************
+ * hekate-symbols.ttf 30 px Font in U+f000 () .. U+f2ee () range with all bpp
+ * Sparse font with only these characters:
+***********************************************************************************/
+
+/*Store the glyph descriptions*/
+static const lv_font_glyph_dsc_t hekate_symbol_30_glyph_dsc[] =
+{
+#if USE_HEKATE_SYMBOL_30 == 4
+ {.w_px = 7, .glyph_index = 0}, /*Unicode: U+f001 ()*/
+ {.w_px = 25, .glyph_index = 120}, /*Unicode: U+f008 ()*/
+ {.w_px = 27, .glyph_index = 510}, /*Unicode: U+f00b ()*/
+ {.w_px = 31, .glyph_index = 930}, /*Unicode: U+f00c ()*/
+ {.w_px = 22, .glyph_index = 1410}, /*Unicode: U+f00d ()*/
+ {.w_px = 25, .glyph_index = 1740}, /*Unicode: U+f011 ()*/
+ {.w_px = 25, .glyph_index = 2130}, /*Unicode: U+f013 ()*/
+ {.w_px = 23, .glyph_index = 2520}, /*Unicode: U+f014 ()*/
+ {.w_px = 34, .glyph_index = 2880}, /*Unicode: U+f015 ()*/
+ {.w_px = 25, .glyph_index = 3390}, /*Unicode: U+f019 ()*/
+ {.w_px = 32, .glyph_index = 3780}, /*Unicode: U+f01c ()*/
+ {.w_px = 25, .glyph_index = 4260}, /*Unicode: U+f021 ()*/
+ {.w_px = 25, .glyph_index = 4650}, /*Unicode: U+f026 ()*/
+ {.w_px = 25, .glyph_index = 5040}, /*Unicode: U+f027 ()*/
+ {.w_px = 20, .glyph_index = 5430}, /*Unicode: U+f028 ()*/
+ {.w_px = 20, .glyph_index = 5730}, /*Unicode: U+f03e ()*/
+ {.w_px = 25, .glyph_index = 6030}, /*Unicode: U+f040 ()*/
+ {.w_px = 20, .glyph_index = 6420}, /*Unicode: U+f048 ()*/
+ {.w_px = 20, .glyph_index = 6720}, /*Unicode: U+f04b ()*/
+ {.w_px = 20, .glyph_index = 7020}, /*Unicode: U+f04c ()*/
+ {.w_px = 13, .glyph_index = 7320}, /*Unicode: U+f04d ()*/
+ {.w_px = 32, .glyph_index = 7530}, /*Unicode: U+f051 ()*/
+ {.w_px = 30, .glyph_index = 8010}, /*Unicode: U+f052 ()*/
+ {.w_px = 16, .glyph_index = 8460}, /*Unicode: U+f053 ()*/
+ {.w_px = 16, .glyph_index = 8700}, /*Unicode: U+f054 ()*/
+ {.w_px = 25, .glyph_index = 8940}, /*Unicode: U+f067 ()*/
+ {.w_px = 25, .glyph_index = 9330}, /*Unicode: U+f068 ()*/
+ {.w_px = 27, .glyph_index = 9720}, /*Unicode: U+f071 ()*/
+ {.w_px = 29, .glyph_index = 10140}, /*Unicode: U+f074 ()*/
+ {.w_px = 26, .glyph_index = 10590}, /*Unicode: U+f077 ()*/
+ {.w_px = 26, .glyph_index = 10980}, /*Unicode: U+f078 ()*/
+ {.w_px = 25, .glyph_index = 11370}, /*Unicode: U+f079 ()*/
+ {.w_px = 29, .glyph_index = 11760}, /*Unicode: U+f07b ()*/
+ {.w_px = 25, .glyph_index = 12210}, /*Unicode: U+f093 ()*/
+ {.w_px = 37, .glyph_index = 12600}, /*Unicode: U+f095 ()*/
+ {.w_px = 25, .glyph_index = 13170}, /*Unicode: U+f0c4 ()*/
+ {.w_px = 23, .glyph_index = 13560}, /*Unicode: U+f0c5 ()*/
+ {.w_px = 24, .glyph_index = 13920}, /*Unicode: U+f0c7 ()*/
+ {.w_px = 13, .glyph_index = 14280}, /*Unicode: U+f0e7 ()*/
+ {.w_px = 18, .glyph_index = 14490}, /*Unicode: U+f0f3 ()*/
+ {.w_px = 33, .glyph_index = 14760}, /*Unicode: U+f11c ()*/
+ {.w_px = 25, .glyph_index = 15270}, /*Unicode: U+f124 ()*/
+ {.w_px = 20, .glyph_index = 15660}, /*Unicode: U+f15b ()*/
+ {.w_px = 29, .glyph_index = 15960}, /*Unicode: U+f1eb ()*/
+ {.w_px = 38, .glyph_index = 16410}, /*Unicode: U+f240 ()*/
+ {.w_px = 38, .glyph_index = 16980}, /*Unicode: U+f241 ()*/
+ {.w_px = 38, .glyph_index = 17550}, /*Unicode: U+f242 ()*/
+ {.w_px = 38, .glyph_index = 18120}, /*Unicode: U+f243 ()*/
+ {.w_px = 38, .glyph_index = 18690}, /*Unicode: U+f244 ()*/
+ {.w_px = 29, .glyph_index = 19260}, /*Unicode: U+f293 ()*/
+
+#elif USE_HEKATE_SYMBOL_30 == 8
+ {.w_px = 7, .glyph_index = 0}, /*Unicode: U+f001 ()*/
+ {.w_px = 25, .glyph_index = 210}, /*Unicode: U+f008 ()*/
+ {.w_px = 27, .glyph_index = 960}, /*Unicode: U+f00b ()*/
+ {.w_px = 31, .glyph_index = 1770}, /*Unicode: U+f00c ()*/
+ {.w_px = 22, .glyph_index = 2700}, /*Unicode: U+f00d ()*/
+ {.w_px = 25, .glyph_index = 3360}, /*Unicode: U+f011 ()*/
+ {.w_px = 25, .glyph_index = 4110}, /*Unicode: U+f013 ()*/
+ {.w_px = 23, .glyph_index = 4860}, /*Unicode: U+f014 ()*/
+ {.w_px = 34, .glyph_index = 5550}, /*Unicode: U+f015 ()*/
+ {.w_px = 25, .glyph_index = 6570}, /*Unicode: U+f019 ()*/
+ {.w_px = 32, .glyph_index = 7320}, /*Unicode: U+f01c ()*/
+ {.w_px = 25, .glyph_index = 8280}, /*Unicode: U+f021 ()*/
+ {.w_px = 25, .glyph_index = 9030}, /*Unicode: U+f026 ()*/
+ {.w_px = 25, .glyph_index = 9780}, /*Unicode: U+f027 ()*/
+ {.w_px = 20, .glyph_index = 10530}, /*Unicode: U+f028 ()*/
+ {.w_px = 20, .glyph_index = 11130}, /*Unicode: U+f03e ()*/
+ {.w_px = 25, .glyph_index = 11730}, /*Unicode: U+f040 ()*/
+ {.w_px = 20, .glyph_index = 12480}, /*Unicode: U+f048 ()*/
+ {.w_px = 20, .glyph_index = 13080}, /*Unicode: U+f04b ()*/
+ {.w_px = 20, .glyph_index = 13680}, /*Unicode: U+f04c ()*/
+ {.w_px = 13, .glyph_index = 14280}, /*Unicode: U+f04d ()*/
+ {.w_px = 32, .glyph_index = 14670}, /*Unicode: U+f051 ()*/
+ {.w_px = 30, .glyph_index = 15630}, /*Unicode: U+f052 ()*/
+ {.w_px = 16, .glyph_index = 16530}, /*Unicode: U+f053 ()*/
+ {.w_px = 16, .glyph_index = 17010}, /*Unicode: U+f054 ()*/
+ {.w_px = 25, .glyph_index = 17490}, /*Unicode: U+f067 ()*/
+ {.w_px = 25, .glyph_index = 18240}, /*Unicode: U+f068 ()*/
+ {.w_px = 27, .glyph_index = 18990}, /*Unicode: U+f071 ()*/
+ {.w_px = 29, .glyph_index = 19800}, /*Unicode: U+f074 ()*/
+ {.w_px = 26, .glyph_index = 20670}, /*Unicode: U+f077 ()*/
+ {.w_px = 26, .glyph_index = 21450}, /*Unicode: U+f078 ()*/
+ {.w_px = 25, .glyph_index = 22230}, /*Unicode: U+f079 ()*/
+ {.w_px = 29, .glyph_index = 22980}, /*Unicode: U+f07b ()*/
+ {.w_px = 25, .glyph_index = 23850}, /*Unicode: U+f093 ()*/
+ {.w_px = 37, .glyph_index = 24600}, /*Unicode: U+f095 ()*/
+ {.w_px = 25, .glyph_index = 25710}, /*Unicode: U+f0c4 ()*/
+ {.w_px = 23, .glyph_index = 26460}, /*Unicode: U+f0c5 ()*/
+ {.w_px = 24, .glyph_index = 27150}, /*Unicode: U+f0c7 ()*/
+ {.w_px = 13, .glyph_index = 27870}, /*Unicode: U+f0e7 ()*/
+ {.w_px = 18, .glyph_index = 28260}, /*Unicode: U+f0f3 ()*/
+ {.w_px = 33, .glyph_index = 28800}, /*Unicode: U+f11c ()*/
+ {.w_px = 25, .glyph_index = 29790}, /*Unicode: U+f124 ()*/
+ {.w_px = 20, .glyph_index = 30540}, /*Unicode: U+f15b ()*/
+ {.w_px = 29, .glyph_index = 31140}, /*Unicode: U+f1eb ()*/
+ {.w_px = 38, .glyph_index = 32010}, /*Unicode: U+f240 ()*/
+ {.w_px = 38, .glyph_index = 33150}, /*Unicode: U+f241 ()*/
+ {.w_px = 38, .glyph_index = 34290}, /*Unicode: U+f242 ()*/
+ {.w_px = 38, .glyph_index = 35430}, /*Unicode: U+f243 ()*/
+ {.w_px = 38, .glyph_index = 36570}, /*Unicode: U+f244 ()*/
+ {.w_px = 29, .glyph_index = 37710}, /*Unicode: U+f293 ()*/
+
+#endif
+};
+
+lv_font_t hekate_symbol_30 =
+{
+ .unicode_first = LV_SYMBOL_GLYPH_FIRST, /*First Unicode letter in this font*/
+ .unicode_last = LV_SYMBOL_GLYPH_LAST, /*Last Unicode letter in this font*/
+ .h_px = 30, /*Font height in pixels*/
+ //.glyph_bitmap = hekate_symbol_30_glyph_bitmap, /*Bitmap of glyphs*/
+ .glyph_bitmap = (const uint8_t *)(NYX_RES_ADDR + 0x14200),
+ .glyph_dsc = hekate_symbol_30_glyph_dsc, /*Description of glyphs*/
+ .glyph_cnt = 50, /*Number of glyphs in the font*/
+ .unicode_list = NULL, /*List of unicode characters*/
+ .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/
+ .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/
+#if USE_HEKATE_SYMBOL_30 == 4
+ .bpp = 4, /*Bit per pixel*/
+ #elif USE_HEKATE_SYMBOL_30 == 8
+ .bpp = 8, /*Bit per pixel*/
+#endif
+ .monospace = 0, /*Fix width (0: if not used)*/
+ .next_page = NULL, /*Pointer to a font extension*/
+};
+
+#endif /*USE_HEKATE_SYMBOL_30*/
diff --git a/bdk/libs/lvgl/lv_fonts/interui_20.c b/bdk/libs/lvgl/lv_fonts/interui_20.c
new file mode 100644
index 0000000..5f50c6c
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/interui_20.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "../lv_misc/lv_font.h"
+
+#include
+
+#if USE_INTERUI_20 != 0 /*Can be enabled in lv_conf.h*/
+
+/***********************************************************************************
+ * Inter-UI-Regular-stretched.ttf 20 px Font in U+0020 ( ) .. U+007e (~) range with all bpp
+***********************************************************************************/
+
+/*Store the glyph descriptions*/
+static const lv_font_glyph_dsc_t interui_20_glyph_dsc[] =
+{
+#if USE_INTERUI_20 == 4
+ {.w_px = 6, .glyph_index = 0}, /*Unicode: U+0020 ( )*/
+ {.w_px = 3, .glyph_index = 60}, /*Unicode: U+0021 (!)*/
+ {.w_px = 5, .glyph_index = 100}, /*Unicode: U+0022 (")*/
+ {.w_px = 8, .glyph_index = 160}, /*Unicode: U+0023 (#)*/
+ {.w_px = 9, .glyph_index = 240}, /*Unicode: U+0024 ($)*/
+ {.w_px = 14, .glyph_index = 340}, /*Unicode: U+0025 (%)*/
+ {.w_px = 12, .glyph_index = 480}, /*Unicode: U+0026 (&)*/
+ {.w_px = 3, .glyph_index = 600}, /*Unicode: U+0027 (')*/
+ {.w_px = 5, .glyph_index = 640}, /*Unicode: U+0028 (()*/
+ {.w_px = 5, .glyph_index = 700}, /*Unicode: U+0029 ())*/
+ {.w_px = 7, .glyph_index = 760}, /*Unicode: U+002a (*)*/
+ {.w_px = 10, .glyph_index = 840}, /*Unicode: U+002b (+)*/
+ {.w_px = 3, .glyph_index = 940}, /*Unicode: U+002c (,)*/
+ {.w_px = 5, .glyph_index = 980}, /*Unicode: U+002d (-)*/
+ {.w_px = 3, .glyph_index = 1040}, /*Unicode: U+002e (.)*/
+ {.w_px = 8, .glyph_index = 1080}, /*Unicode: U+002f (/)*/
+ {.w_px = 10, .glyph_index = 1160}, /*Unicode: U+0030 (0)*/
+ {.w_px = 5, .glyph_index = 1260}, /*Unicode: U+0031 (1)*/
+ {.w_px = 9, .glyph_index = 1320}, /*Unicode: U+0032 (2)*/
+ {.w_px = 10, .glyph_index = 1420}, /*Unicode: U+0033 (3)*/
+ {.w_px = 9, .glyph_index = 1520}, /*Unicode: U+0034 (4)*/
+ {.w_px = 9, .glyph_index = 1620}, /*Unicode: U+0035 (5)*/
+ {.w_px = 10, .glyph_index = 1720}, /*Unicode: U+0036 (6)*/
+ {.w_px = 8, .glyph_index = 1820}, /*Unicode: U+0037 (7)*/
+ {.w_px = 10, .glyph_index = 1900}, /*Unicode: U+0038 (8)*/
+ {.w_px = 10, .glyph_index = 2000}, /*Unicode: U+0039 (9)*/
+ {.w_px = 3, .glyph_index = 2100}, /*Unicode: U+003a (:)*/
+ {.w_px = 3, .glyph_index = 2140}, /*Unicode: U+003b (;)*/
+ {.w_px = 12, .glyph_index = 2180}, /*Unicode: U+003c (<)*/
+ {.w_px = 10, .glyph_index = 2300}, /*Unicode: U+003d (=)*/
+ {.w_px = 12, .glyph_index = 2400}, /*Unicode: U+003e (>)*/
+ {.w_px = 8, .glyph_index = 2520}, /*Unicode: U+003f (?)*/
+ {.w_px = 14, .glyph_index = 2600}, /*Unicode: U+0040 (@)*/
+ {.w_px = 12, .glyph_index = 2740}, /*Unicode: U+0041 (A)*/
+ {.w_px = 10, .glyph_index = 2860}, /*Unicode: U+0042 (B)*/
+ {.w_px = 11, .glyph_index = 2960}, /*Unicode: U+0043 (C)*/
+ {.w_px = 12, .glyph_index = 3080}, /*Unicode: U+0044 (D)*/
+ {.w_px = 9, .glyph_index = 3200}, /*Unicode: U+0045 (E)*/
+ {.w_px = 8, .glyph_index = 3300}, /*Unicode: U+0046 (F)*/
+ {.w_px = 12, .glyph_index = 3380}, /*Unicode: U+0047 (G)*/
+ {.w_px = 10, .glyph_index = 3500}, /*Unicode: U+0048 (H)*/
+ {.w_px = 3, .glyph_index = 3600}, /*Unicode: U+0049 (I)*/
+ {.w_px = 7, .glyph_index = 3640}, /*Unicode: U+004a (J)*/
+ {.w_px = 10, .glyph_index = 3720}, /*Unicode: U+004b (K)*/
+ {.w_px = 7, .glyph_index = 3820}, /*Unicode: U+004c (L)*/
+ {.w_px = 15, .glyph_index = 3900}, /*Unicode: U+004d (M)*/
+ {.w_px = 12, .glyph_index = 4060}, /*Unicode: U+004e (N)*/
+ {.w_px = 14, .glyph_index = 4180}, /*Unicode: U+004f (O)*/
+ {.w_px = 9, .glyph_index = 4320}, /*Unicode: U+0050 (P)*/
+ {.w_px = 14, .glyph_index = 4420}, /*Unicode: U+0051 (Q)*/
+ {.w_px = 9, .glyph_index = 4560}, /*Unicode: U+0052 (R)*/
+ {.w_px = 9, .glyph_index = 4660}, /*Unicode: U+0053 (S)*/
+ {.w_px = 9, .glyph_index = 4760}, /*Unicode: U+0054 (T)*/
+ {.w_px = 12, .glyph_index = 4860}, /*Unicode: U+0055 (U)*/
+ {.w_px = 12, .glyph_index = 4980}, /*Unicode: U+0056 (V)*/
+ {.w_px = 18, .glyph_index = 5100}, /*Unicode: U+0057 (W)*/
+ {.w_px = 10, .glyph_index = 5280}, /*Unicode: U+0058 (X)*/
+ {.w_px = 10, .glyph_index = 5380}, /*Unicode: U+0059 (Y)*/
+ {.w_px = 10, .glyph_index = 5480}, /*Unicode: U+005a (Z)*/
+ {.w_px = 5, .glyph_index = 5580}, /*Unicode: U+005b ([)*/
+ {.w_px = 8, .glyph_index = 5640}, /*Unicode: U+005c (\)*/
+ {.w_px = 5, .glyph_index = 5720}, /*Unicode: U+005d (])*/
+ {.w_px = 9, .glyph_index = 5780}, /*Unicode: U+005e (^)*/
+ {.w_px = 8, .glyph_index = 5880}, /*Unicode: U+005f (_)*/
+ {.w_px = 4, .glyph_index = 5960}, /*Unicode: U+0060 (`)*/
+ {.w_px = 9, .glyph_index = 6000}, /*Unicode: U+0061 (a)*/
+ {.w_px = 10, .glyph_index = 6100}, /*Unicode: U+0062 (b)*/
+ {.w_px = 8, .glyph_index = 6200}, /*Unicode: U+0063 (c)*/
+ {.w_px = 10, .glyph_index = 6280}, /*Unicode: U+0064 (d)*/
+ {.w_px = 9, .glyph_index = 6380}, /*Unicode: U+0065 (e)*/
+ {.w_px = 4, .glyph_index = 6480}, /*Unicode: U+0066 (f)*/
+ {.w_px = 9, .glyph_index = 6520}, /*Unicode: U+0067 (g)*/
+ {.w_px = 9, .glyph_index = 6620}, /*Unicode: U+0068 (h)*/
+ {.w_px = 3, .glyph_index = 6720}, /*Unicode: U+0069 (i)*/
+ {.w_px = 5, .glyph_index = 6760}, /*Unicode: U+006a (j)*/
+ {.w_px = 8, .glyph_index = 6820}, /*Unicode: U+006b (k)*/
+ {.w_px = 4, .glyph_index = 6900}, /*Unicode: U+006c (l)*/
+ {.w_px = 13, .glyph_index = 6940}, /*Unicode: U+006d (m)*/
+ {.w_px = 9, .glyph_index = 7080}, /*Unicode: U+006e (n)*/
+ {.w_px = 10, .glyph_index = 7180}, /*Unicode: U+006f (o)*/
+ {.w_px = 10, .glyph_index = 7280}, /*Unicode: U+0070 (p)*/
+ {.w_px = 10, .glyph_index = 7380}, /*Unicode: U+0071 (q)*/
+ {.w_px = 5, .glyph_index = 7480}, /*Unicode: U+0072 (r)*/
+ {.w_px = 8, .glyph_index = 7540}, /*Unicode: U+0073 (s)*/
+ {.w_px = 5, .glyph_index = 7620}, /*Unicode: U+0074 (t)*/
+ {.w_px = 9, .glyph_index = 7680}, /*Unicode: U+0075 (u)*/
+ {.w_px = 8, .glyph_index = 7780}, /*Unicode: U+0076 (v)*/
+ {.w_px = 14, .glyph_index = 7860}, /*Unicode: U+0077 (w)*/
+ {.w_px = 8, .glyph_index = 8000}, /*Unicode: U+0078 (x)*/
+ {.w_px = 8, .glyph_index = 8080}, /*Unicode: U+0079 (y)*/
+ {.w_px = 7, .glyph_index = 8160}, /*Unicode: U+007a (z)*/
+ {.w_px = 4, .glyph_index = 8240}, /*Unicode: U+007b ({)*/
+ {.w_px = 3, .glyph_index = 8280}, /*Unicode: U+007c (|)*/
+ {.w_px = 4, .glyph_index = 8320}, /*Unicode: U+007d (})*/
+ {.w_px = 6, .glyph_index = 8360}, /*Unicode: U+007e (~)*/
+
+#elif USE_INTERUI_20 == 8
+ {.w_px = 6, .glyph_index = 0}, /*Unicode: U+0020 ( )*/
+ {.w_px = 3, .glyph_index = 120}, /*Unicode: U+0021 (!)*/
+ {.w_px = 5, .glyph_index = 180}, /*Unicode: U+0022 (")*/
+ {.w_px = 8, .glyph_index = 280}, /*Unicode: U+0023 (#)*/
+ {.w_px = 9, .glyph_index = 440}, /*Unicode: U+0024 ($)*/
+ {.w_px = 14, .glyph_index = 620}, /*Unicode: U+0025 (%)*/
+ {.w_px = 12, .glyph_index = 900}, /*Unicode: U+0026 (&)*/
+ {.w_px = 3, .glyph_index = 1140}, /*Unicode: U+0027 (')*/
+ {.w_px = 5, .glyph_index = 1200}, /*Unicode: U+0028 (()*/
+ {.w_px = 5, .glyph_index = 1300}, /*Unicode: U+0029 ())*/
+ {.w_px = 7, .glyph_index = 1400}, /*Unicode: U+002a (*)*/
+ {.w_px = 10, .glyph_index = 1540}, /*Unicode: U+002b (+)*/
+ {.w_px = 3, .glyph_index = 1740}, /*Unicode: U+002c (,)*/
+ {.w_px = 5, .glyph_index = 1800}, /*Unicode: U+002d (-)*/
+ {.w_px = 3, .glyph_index = 1900}, /*Unicode: U+002e (.)*/
+ {.w_px = 8, .glyph_index = 1960}, /*Unicode: U+002f (/)*/
+ {.w_px = 10, .glyph_index = 2120}, /*Unicode: U+0030 (0)*/
+ {.w_px = 5, .glyph_index = 2320}, /*Unicode: U+0031 (1)*/
+ {.w_px = 9, .glyph_index = 2420}, /*Unicode: U+0032 (2)*/
+ {.w_px = 10, .glyph_index = 2600}, /*Unicode: U+0033 (3)*/
+ {.w_px = 9, .glyph_index = 2800}, /*Unicode: U+0034 (4)*/
+ {.w_px = 9, .glyph_index = 2980}, /*Unicode: U+0035 (5)*/
+ {.w_px = 10, .glyph_index = 3160}, /*Unicode: U+0036 (6)*/
+ {.w_px = 8, .glyph_index = 3360}, /*Unicode: U+0037 (7)*/
+ {.w_px = 10, .glyph_index = 3520}, /*Unicode: U+0038 (8)*/
+ {.w_px = 10, .glyph_index = 3720}, /*Unicode: U+0039 (9)*/
+ {.w_px = 3, .glyph_index = 3920}, /*Unicode: U+003a (:)*/
+ {.w_px = 3, .glyph_index = 3980}, /*Unicode: U+003b (;)*/
+ {.w_px = 12, .glyph_index = 4040}, /*Unicode: U+003c (<)*/
+ {.w_px = 10, .glyph_index = 4280}, /*Unicode: U+003d (=)*/
+ {.w_px = 12, .glyph_index = 4480}, /*Unicode: U+003e (>)*/
+ {.w_px = 8, .glyph_index = 4720}, /*Unicode: U+003f (?)*/
+ {.w_px = 14, .glyph_index = 4880}, /*Unicode: U+0040 (@)*/
+ {.w_px = 12, .glyph_index = 5160}, /*Unicode: U+0041 (A)*/
+ {.w_px = 10, .glyph_index = 5400}, /*Unicode: U+0042 (B)*/
+ {.w_px = 11, .glyph_index = 5600}, /*Unicode: U+0043 (C)*/
+ {.w_px = 12, .glyph_index = 5820}, /*Unicode: U+0044 (D)*/
+ {.w_px = 9, .glyph_index = 6060}, /*Unicode: U+0045 (E)*/
+ {.w_px = 8, .glyph_index = 6240}, /*Unicode: U+0046 (F)*/
+ {.w_px = 12, .glyph_index = 6400}, /*Unicode: U+0047 (G)*/
+ {.w_px = 10, .glyph_index = 6640}, /*Unicode: U+0048 (H)*/
+ {.w_px = 3, .glyph_index = 6840}, /*Unicode: U+0049 (I)*/
+ {.w_px = 7, .glyph_index = 6900}, /*Unicode: U+004a (J)*/
+ {.w_px = 10, .glyph_index = 7040}, /*Unicode: U+004b (K)*/
+ {.w_px = 7, .glyph_index = 7240}, /*Unicode: U+004c (L)*/
+ {.w_px = 15, .glyph_index = 7380}, /*Unicode: U+004d (M)*/
+ {.w_px = 12, .glyph_index = 7680}, /*Unicode: U+004e (N)*/
+ {.w_px = 14, .glyph_index = 7920}, /*Unicode: U+004f (O)*/
+ {.w_px = 9, .glyph_index = 8200}, /*Unicode: U+0050 (P)*/
+ {.w_px = 14, .glyph_index = 8380}, /*Unicode: U+0051 (Q)*/
+ {.w_px = 9, .glyph_index = 8660}, /*Unicode: U+0052 (R)*/
+ {.w_px = 9, .glyph_index = 8840}, /*Unicode: U+0053 (S)*/
+ {.w_px = 9, .glyph_index = 9020}, /*Unicode: U+0054 (T)*/
+ {.w_px = 12, .glyph_index = 9200}, /*Unicode: U+0055 (U)*/
+ {.w_px = 12, .glyph_index = 9440}, /*Unicode: U+0056 (V)*/
+ {.w_px = 18, .glyph_index = 9680}, /*Unicode: U+0057 (W)*/
+ {.w_px = 10, .glyph_index = 10040}, /*Unicode: U+0058 (X)*/
+ {.w_px = 10, .glyph_index = 10240}, /*Unicode: U+0059 (Y)*/
+ {.w_px = 10, .glyph_index = 10440}, /*Unicode: U+005a (Z)*/
+ {.w_px = 5, .glyph_index = 10640}, /*Unicode: U+005b ([)*/
+ {.w_px = 8, .glyph_index = 10740}, /*Unicode: U+005c (\)*/
+ {.w_px = 5, .glyph_index = 10900}, /*Unicode: U+005d (])*/
+ {.w_px = 9, .glyph_index = 11000}, /*Unicode: U+005e (^)*/
+ {.w_px = 8, .glyph_index = 11180}, /*Unicode: U+005f (_)*/
+ {.w_px = 4, .glyph_index = 11340}, /*Unicode: U+0060 (`)*/
+ {.w_px = 9, .glyph_index = 11420}, /*Unicode: U+0061 (a)*/
+ {.w_px = 10, .glyph_index = 11600}, /*Unicode: U+0062 (b)*/
+ {.w_px = 8, .glyph_index = 11800}, /*Unicode: U+0063 (c)*/
+ {.w_px = 10, .glyph_index = 11960}, /*Unicode: U+0064 (d)*/
+ {.w_px = 9, .glyph_index = 12160}, /*Unicode: U+0065 (e)*/
+ {.w_px = 4, .glyph_index = 12340}, /*Unicode: U+0066 (f)*/
+ {.w_px = 9, .glyph_index = 12420}, /*Unicode: U+0067 (g)*/
+ {.w_px = 9, .glyph_index = 12600}, /*Unicode: U+0068 (h)*/
+ {.w_px = 3, .glyph_index = 12780}, /*Unicode: U+0069 (i)*/
+ {.w_px = 5, .glyph_index = 12840}, /*Unicode: U+006a (j)*/
+ {.w_px = 8, .glyph_index = 12940}, /*Unicode: U+006b (k)*/
+ {.w_px = 4, .glyph_index = 13100}, /*Unicode: U+006c (l)*/
+ {.w_px = 13, .glyph_index = 13180}, /*Unicode: U+006d (m)*/
+ {.w_px = 9, .glyph_index = 13440}, /*Unicode: U+006e (n)*/
+ {.w_px = 10, .glyph_index = 13620}, /*Unicode: U+006f (o)*/
+ {.w_px = 10, .glyph_index = 13820}, /*Unicode: U+0070 (p)*/
+ {.w_px = 10, .glyph_index = 14020}, /*Unicode: U+0071 (q)*/
+ {.w_px = 5, .glyph_index = 14220}, /*Unicode: U+0072 (r)*/
+ {.w_px = 8, .glyph_index = 14320}, /*Unicode: U+0073 (s)*/
+ {.w_px = 5, .glyph_index = 14480}, /*Unicode: U+0074 (t)*/
+ {.w_px = 9, .glyph_index = 14580}, /*Unicode: U+0075 (u)*/
+ {.w_px = 8, .glyph_index = 14760}, /*Unicode: U+0076 (v)*/
+ {.w_px = 14, .glyph_index = 14920}, /*Unicode: U+0077 (w)*/
+ {.w_px = 8, .glyph_index = 15200}, /*Unicode: U+0078 (x)*/
+ {.w_px = 8, .glyph_index = 15360}, /*Unicode: U+0079 (y)*/
+ {.w_px = 7, .glyph_index = 15520}, /*Unicode: U+007a (z)*/
+ {.w_px = 4, .glyph_index = 15660}, /*Unicode: U+007b ({)*/
+ {.w_px = 3, .glyph_index = 15740}, /*Unicode: U+007c (|)*/
+ {.w_px = 4, .glyph_index = 15800}, /*Unicode: U+007d (})*/
+ {.w_px = 6, .glyph_index = 15880}, /*Unicode: U+007e (~)*/
+
+#endif
+};
+
+lv_font_t interui_20 =
+{
+ .unicode_first = 32, /*First Unicode letter in this font*/
+ .unicode_last = 126, /*Last Unicode letter in this font*/
+ .h_px = 20, /*Font height in pixels*/
+ //.glyph_bitmap = interui_20_glyph_bitmap, /*Bitmap of glyphs*/
+ .glyph_bitmap = (const uint8_t *)(NYX_RES_ADDR + 0x3A00),
+ .glyph_dsc = interui_20_glyph_dsc, /*Description of glyphs*/
+ .glyph_cnt = 95, /*Number of glyphs in the font*/
+ .unicode_list = NULL, /*Every character in the font from 'unicode_first' to 'unicode_last'*/
+ .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/
+ .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/
+#if USE_INTERUI_20 == 4
+ .bpp = 4, /*Bit per pixel*/
+#elif USE_INTERUI_20 == 8
+ .bpp = 8, /*Bit per pixel*/
+#endif
+ .monospace = 0, /*Fix width (0: if not used)*/
+ .next_page = NULL, /*Pointer to a font extension*/
+};
+
+#endif /*USE_INTERUI_20*/
diff --git a/bdk/libs/lvgl/lv_fonts/interui_30.c b/bdk/libs/lvgl/lv_fonts/interui_30.c
new file mode 100644
index 0000000..238bcb1
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/interui_30.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "../lv_misc/lv_font.h"
+
+#include
+
+#if USE_INTERUI_30 != 0 /*Can be enabled in lv_conf.h*/
+
+/***********************************************************************************
+ * Inter-UI-Regular-stretched.ttf 30 px Font in U+0020 ( ) .. U+007e (~) range with all bpp
+***********************************************************************************/
+
+/*Store the glyph descriptions*/
+static const lv_font_glyph_dsc_t interui_30_glyph_dsc[] =
+{
+#if USE_INTERUI_30 == 4
+ {.w_px = 8, .glyph_index = 0}, /*Unicode: U+0020 ( )*/
+ {.w_px = 3, .glyph_index = 120}, /*Unicode: U+0021 (!)*/
+ {.w_px = 6, .glyph_index = 180}, /*Unicode: U+0022 (")*/
+ {.w_px = 13, .glyph_index = 270}, /*Unicode: U+0023 (#)*/
+ {.w_px = 12, .glyph_index = 480}, /*Unicode: U+0024 ($)*/
+ {.w_px = 18, .glyph_index = 660}, /*Unicode: U+0025 (%)*/
+ {.w_px = 16, .glyph_index = 930}, /*Unicode: U+0026 (&)*/
+ {.w_px = 3, .glyph_index = 1170}, /*Unicode: U+0027 (')*/
+ {.w_px = 6, .glyph_index = 1230}, /*Unicode: U+0028 (()*/
+ {.w_px = 7, .glyph_index = 1320}, /*Unicode: U+0029 ())*/
+ {.w_px = 9, .glyph_index = 1440}, /*Unicode: U+002a (*)*/
+ {.w_px = 16, .glyph_index = 1590}, /*Unicode: U+002b (+)*/
+ {.w_px = 3, .glyph_index = 1830}, /*Unicode: U+002c (,)*/
+ {.w_px = 8, .glyph_index = 1890}, /*Unicode: U+002d (-)*/
+ {.w_px = 3, .glyph_index = 2010}, /*Unicode: U+002e (.)*/
+ {.w_px = 11, .glyph_index = 2070}, /*Unicode: U+002f (/)*/
+ {.w_px = 13, .glyph_index = 2250}, /*Unicode: U+0030 (0)*/
+ {.w_px = 7, .glyph_index = 2460}, /*Unicode: U+0031 (1)*/
+ {.w_px = 13, .glyph_index = 2580}, /*Unicode: U+0032 (2)*/
+ {.w_px = 14, .glyph_index = 2790}, /*Unicode: U+0033 (3)*/
+ {.w_px = 13, .glyph_index = 3000}, /*Unicode: U+0034 (4)*/
+ {.w_px = 14, .glyph_index = 3210}, /*Unicode: U+0035 (5)*/
+ {.w_px = 13, .glyph_index = 3420}, /*Unicode: U+0036 (6)*/
+ {.w_px = 13, .glyph_index = 3630}, /*Unicode: U+0037 (7)*/
+ {.w_px = 13, .glyph_index = 3840}, /*Unicode: U+0038 (8)*/
+ {.w_px = 13, .glyph_index = 4050}, /*Unicode: U+0039 (9)*/
+ {.w_px = 3, .glyph_index = 4260}, /*Unicode: U+003a (:)*/
+ {.w_px = 3, .glyph_index = 4320}, /*Unicode: U+003b (;)*/
+ {.w_px = 18, .glyph_index = 4380}, /*Unicode: U+003c (<)*/
+ {.w_px = 16, .glyph_index = 4650}, /*Unicode: U+003d (=)*/
+ {.w_px = 18, .glyph_index = 4890}, /*Unicode: U+003e (>)*/
+ {.w_px = 12, .glyph_index = 5160}, /*Unicode: U+003f (?)*/
+ {.w_px = 18, .glyph_index = 5340}, /*Unicode: U+0040 (@)*/
+ {.w_px = 17, .glyph_index = 5610}, /*Unicode: U+0041 (A)*/
+ {.w_px = 13, .glyph_index = 5880}, /*Unicode: U+0042 (B)*/
+ {.w_px = 15, .glyph_index = 6090}, /*Unicode: U+0043 (C)*/
+ {.w_px = 17, .glyph_index = 6330}, /*Unicode: U+0044 (D)*/
+ {.w_px = 12, .glyph_index = 6600}, /*Unicode: U+0045 (E)*/
+ {.w_px = 11, .glyph_index = 6780}, /*Unicode: U+0046 (F)*/
+ {.w_px = 17, .glyph_index = 6960}, /*Unicode: U+0047 (G)*/
+ {.w_px = 14, .glyph_index = 7230}, /*Unicode: U+0048 (H)*/
+ {.w_px = 3, .glyph_index = 7440}, /*Unicode: U+0049 (I)*/
+ {.w_px = 11, .glyph_index = 7500}, /*Unicode: U+004a (J)*/
+ {.w_px = 14, .glyph_index = 7680}, /*Unicode: U+004b (K)*/
+ {.w_px = 9, .glyph_index = 7890}, /*Unicode: U+004c (L)*/
+ {.w_px = 23, .glyph_index = 8040}, /*Unicode: U+004d (M)*/
+ {.w_px = 16, .glyph_index = 8400}, /*Unicode: U+004e (N)*/
+ {.w_px = 19, .glyph_index = 8640}, /*Unicode: U+004f (O)*/
+ {.w_px = 11, .glyph_index = 8940}, /*Unicode: U+0050 (P)*/
+ {.w_px = 19, .glyph_index = 9120}, /*Unicode: U+0051 (Q)*/
+ {.w_px = 13, .glyph_index = 9420}, /*Unicode: U+0052 (R)*/
+ {.w_px = 12, .glyph_index = 9630}, /*Unicode: U+0053 (S)*/
+ {.w_px = 14, .glyph_index = 9810}, /*Unicode: U+0054 (T)*/
+ {.w_px = 16, .glyph_index = 10020}, /*Unicode: U+0055 (U)*/
+ {.w_px = 18, .glyph_index = 10260}, /*Unicode: U+0056 (V)*/
+ {.w_px = 27, .glyph_index = 10530}, /*Unicode: U+0057 (W)*/
+ {.w_px = 15, .glyph_index = 10950}, /*Unicode: U+0058 (X)*/
+ {.w_px = 15, .glyph_index = 11190}, /*Unicode: U+0059 (Y)*/
+ {.w_px = 15, .glyph_index = 11430}, /*Unicode: U+005a (Z)*/
+ {.w_px = 6, .glyph_index = 11670}, /*Unicode: U+005b ([)*/
+ {.w_px = 11, .glyph_index = 11760}, /*Unicode: U+005c (\)*/
+ {.w_px = 7, .glyph_index = 11940}, /*Unicode: U+005d (])*/
+ {.w_px = 13, .glyph_index = 12060}, /*Unicode: U+005e (^)*/
+ {.w_px = 12, .glyph_index = 12270}, /*Unicode: U+005f (_)*/
+ {.w_px = 6, .glyph_index = 12450}, /*Unicode: U+0060 (`)*/
+ {.w_px = 12, .glyph_index = 12540}, /*Unicode: U+0061 (a)*/
+ {.w_px = 13, .glyph_index = 12720}, /*Unicode: U+0062 (b)*/
+ {.w_px = 11, .glyph_index = 12930}, /*Unicode: U+0063 (c)*/
+ {.w_px = 13, .glyph_index = 13110}, /*Unicode: U+0064 (d)*/
+ {.w_px = 12, .glyph_index = 13320}, /*Unicode: U+0065 (e)*/
+ {.w_px = 6, .glyph_index = 13500}, /*Unicode: U+0066 (f)*/
+ {.w_px = 12, .glyph_index = 13590}, /*Unicode: U+0067 (g)*/
+ {.w_px = 11, .glyph_index = 13770}, /*Unicode: U+0068 (h)*/
+ {.w_px = 3, .glyph_index = 13950}, /*Unicode: U+0069 (i)*/
+ {.w_px = 7, .glyph_index = 14010}, /*Unicode: U+006a (j)*/
+ {.w_px = 12, .glyph_index = 14130}, /*Unicode: U+006b (k)*/
+ {.w_px = 4, .glyph_index = 14310}, /*Unicode: U+006c (l)*/
+ {.w_px = 19, .glyph_index = 14370}, /*Unicode: U+006d (m)*/
+ {.w_px = 11, .glyph_index = 14670}, /*Unicode: U+006e (n)*/
+ {.w_px = 14, .glyph_index = 14850}, /*Unicode: U+006f (o)*/
+ {.w_px = 13, .glyph_index = 15060}, /*Unicode: U+0070 (p)*/
+ {.w_px = 13, .glyph_index = 15270}, /*Unicode: U+0071 (q)*/
+ {.w_px = 7, .glyph_index = 15480}, /*Unicode: U+0072 (r)*/
+ {.w_px = 11, .glyph_index = 15600}, /*Unicode: U+0073 (s)*/
+ {.w_px = 8, .glyph_index = 15780}, /*Unicode: U+0074 (t)*/
+ {.w_px = 11, .glyph_index = 15900}, /*Unicode: U+0075 (u)*/
+ {.w_px = 12, .glyph_index = 16080}, /*Unicode: U+0076 (v)*/
+ {.w_px = 21, .glyph_index = 16260}, /*Unicode: U+0077 (w)*/
+ {.w_px = 13, .glyph_index = 16590}, /*Unicode: U+0078 (x)*/
+ {.w_px = 13, .glyph_index = 16800}, /*Unicode: U+0079 (y)*/
+ {.w_px = 10, .glyph_index = 17010}, /*Unicode: U+007a (z)*/
+ {.w_px = 6, .glyph_index = 17160}, /*Unicode: U+007b ({)*/
+ {.w_px = 3, .glyph_index = 17250}, /*Unicode: U+007c (|)*/
+ {.w_px = 5, .glyph_index = 17310}, /*Unicode: U+007d (})*/
+ {.w_px = 8, .glyph_index = 17400}, /*Unicode: U+007e (~)*/
+
+#elif USE_INTERUI_30 == 8
+ {.w_px = 8, .glyph_index = 0}, /*Unicode: U+0020 ( )*/
+ {.w_px = 3, .glyph_index = 240}, /*Unicode: U+0021 (!)*/
+ {.w_px = 6, .glyph_index = 330}, /*Unicode: U+0022 (")*/
+ {.w_px = 13, .glyph_index = 510}, /*Unicode: U+0023 (#)*/
+ {.w_px = 12, .glyph_index = 900}, /*Unicode: U+0024 ($)*/
+ {.w_px = 18, .glyph_index = 1260}, /*Unicode: U+0025 (%)*/
+ {.w_px = 16, .glyph_index = 1800}, /*Unicode: U+0026 (&)*/
+ {.w_px = 3, .glyph_index = 2280}, /*Unicode: U+0027 (')*/
+ {.w_px = 6, .glyph_index = 2370}, /*Unicode: U+0028 (()*/
+ {.w_px = 7, .glyph_index = 2550}, /*Unicode: U+0029 ())*/
+ {.w_px = 9, .glyph_index = 2760}, /*Unicode: U+002a (*)*/
+ {.w_px = 16, .glyph_index = 3030}, /*Unicode: U+002b (+)*/
+ {.w_px = 3, .glyph_index = 3510}, /*Unicode: U+002c (,)*/
+ {.w_px = 8, .glyph_index = 3600}, /*Unicode: U+002d (-)*/
+ {.w_px = 3, .glyph_index = 3840}, /*Unicode: U+002e (.)*/
+ {.w_px = 11, .glyph_index = 3930}, /*Unicode: U+002f (/)*/
+ {.w_px = 13, .glyph_index = 4260}, /*Unicode: U+0030 (0)*/
+ {.w_px = 7, .glyph_index = 4650}, /*Unicode: U+0031 (1)*/
+ {.w_px = 13, .glyph_index = 4860}, /*Unicode: U+0032 (2)*/
+ {.w_px = 14, .glyph_index = 5250}, /*Unicode: U+0033 (3)*/
+ {.w_px = 13, .glyph_index = 5670}, /*Unicode: U+0034 (4)*/
+ {.w_px = 14, .glyph_index = 6060}, /*Unicode: U+0035 (5)*/
+ {.w_px = 13, .glyph_index = 6480}, /*Unicode: U+0036 (6)*/
+ {.w_px = 13, .glyph_index = 6870}, /*Unicode: U+0037 (7)*/
+ {.w_px = 13, .glyph_index = 7260}, /*Unicode: U+0038 (8)*/
+ {.w_px = 13, .glyph_index = 7650}, /*Unicode: U+0039 (9)*/
+ {.w_px = 3, .glyph_index = 8040}, /*Unicode: U+003a (:)*/
+ {.w_px = 3, .glyph_index = 8130}, /*Unicode: U+003b (;)*/
+ {.w_px = 18, .glyph_index = 8220}, /*Unicode: U+003c (<)*/
+ {.w_px = 16, .glyph_index = 8760}, /*Unicode: U+003d (=)*/
+ {.w_px = 18, .glyph_index = 9240}, /*Unicode: U+003e (>)*/
+ {.w_px = 12, .glyph_index = 9780}, /*Unicode: U+003f (?)*/
+ {.w_px = 18, .glyph_index = 10140}, /*Unicode: U+0040 (@)*/
+ {.w_px = 17, .glyph_index = 10680}, /*Unicode: U+0041 (A)*/
+ {.w_px = 13, .glyph_index = 11190}, /*Unicode: U+0042 (B)*/
+ {.w_px = 15, .glyph_index = 11580}, /*Unicode: U+0043 (C)*/
+ {.w_px = 17, .glyph_index = 12030}, /*Unicode: U+0044 (D)*/
+ {.w_px = 12, .glyph_index = 12540}, /*Unicode: U+0045 (E)*/
+ {.w_px = 11, .glyph_index = 12900}, /*Unicode: U+0046 (F)*/
+ {.w_px = 17, .glyph_index = 13230}, /*Unicode: U+0047 (G)*/
+ {.w_px = 14, .glyph_index = 13740}, /*Unicode: U+0048 (H)*/
+ {.w_px = 3, .glyph_index = 14160}, /*Unicode: U+0049 (I)*/
+ {.w_px = 11, .glyph_index = 14250}, /*Unicode: U+004a (J)*/
+ {.w_px = 14, .glyph_index = 14580}, /*Unicode: U+004b (K)*/
+ {.w_px = 9, .glyph_index = 15000}, /*Unicode: U+004c (L)*/
+ {.w_px = 23, .glyph_index = 15270}, /*Unicode: U+004d (M)*/
+ {.w_px = 16, .glyph_index = 15960}, /*Unicode: U+004e (N)*/
+ {.w_px = 19, .glyph_index = 16440}, /*Unicode: U+004f (O)*/
+ {.w_px = 11, .glyph_index = 17010}, /*Unicode: U+0050 (P)*/
+ {.w_px = 19, .glyph_index = 17340}, /*Unicode: U+0051 (Q)*/
+ {.w_px = 13, .glyph_index = 17910}, /*Unicode: U+0052 (R)*/
+ {.w_px = 12, .glyph_index = 18300}, /*Unicode: U+0053 (S)*/
+ {.w_px = 14, .glyph_index = 18660}, /*Unicode: U+0054 (T)*/
+ {.w_px = 16, .glyph_index = 19080}, /*Unicode: U+0055 (U)*/
+ {.w_px = 18, .glyph_index = 19560}, /*Unicode: U+0056 (V)*/
+ {.w_px = 27, .glyph_index = 20100}, /*Unicode: U+0057 (W)*/
+ {.w_px = 15, .glyph_index = 20910}, /*Unicode: U+0058 (X)*/
+ {.w_px = 15, .glyph_index = 21360}, /*Unicode: U+0059 (Y)*/
+ {.w_px = 15, .glyph_index = 21810}, /*Unicode: U+005a (Z)*/
+ {.w_px = 6, .glyph_index = 22260}, /*Unicode: U+005b ([)*/
+ {.w_px = 11, .glyph_index = 22440}, /*Unicode: U+005c (\)*/
+ {.w_px = 7, .glyph_index = 22770}, /*Unicode: U+005d (])*/
+ {.w_px = 13, .glyph_index = 22980}, /*Unicode: U+005e (^)*/
+ {.w_px = 12, .glyph_index = 23370}, /*Unicode: U+005f (_)*/
+ {.w_px = 6, .glyph_index = 23730}, /*Unicode: U+0060 (`)*/
+ {.w_px = 12, .glyph_index = 23910}, /*Unicode: U+0061 (a)*/
+ {.w_px = 13, .glyph_index = 24270}, /*Unicode: U+0062 (b)*/
+ {.w_px = 11, .glyph_index = 24660}, /*Unicode: U+0063 (c)*/
+ {.w_px = 13, .glyph_index = 24990}, /*Unicode: U+0064 (d)*/
+ {.w_px = 12, .glyph_index = 25380}, /*Unicode: U+0065 (e)*/
+ {.w_px = 6, .glyph_index = 25740}, /*Unicode: U+0066 (f)*/
+ {.w_px = 12, .glyph_index = 25920}, /*Unicode: U+0067 (g)*/
+ {.w_px = 11, .glyph_index = 26280}, /*Unicode: U+0068 (h)*/
+ {.w_px = 3, .glyph_index = 26610}, /*Unicode: U+0069 (i)*/
+ {.w_px = 7, .glyph_index = 26700}, /*Unicode: U+006a (j)*/
+ {.w_px = 12, .glyph_index = 26910}, /*Unicode: U+006b (k)*/
+ {.w_px = 4, .glyph_index = 27270}, /*Unicode: U+006c (l)*/
+ {.w_px = 19, .glyph_index = 27390}, /*Unicode: U+006d (m)*/
+ {.w_px = 11, .glyph_index = 27960}, /*Unicode: U+006e (n)*/
+ {.w_px = 14, .glyph_index = 28290}, /*Unicode: U+006f (o)*/
+ {.w_px = 13, .glyph_index = 28710}, /*Unicode: U+0070 (p)*/
+ {.w_px = 13, .glyph_index = 29100}, /*Unicode: U+0071 (q)*/
+ {.w_px = 7, .glyph_index = 29490}, /*Unicode: U+0072 (r)*/
+ {.w_px = 11, .glyph_index = 29700}, /*Unicode: U+0073 (s)*/
+ {.w_px = 8, .glyph_index = 30030}, /*Unicode: U+0074 (t)*/
+ {.w_px = 11, .glyph_index = 30270}, /*Unicode: U+0075 (u)*/
+ {.w_px = 12, .glyph_index = 30600}, /*Unicode: U+0076 (v)*/
+ {.w_px = 21, .glyph_index = 30960}, /*Unicode: U+0077 (w)*/
+ {.w_px = 13, .glyph_index = 31590}, /*Unicode: U+0078 (x)*/
+ {.w_px = 13, .glyph_index = 31980}, /*Unicode: U+0079 (y)*/
+ {.w_px = 10, .glyph_index = 32370}, /*Unicode: U+007a (z)*/
+ {.w_px = 6, .glyph_index = 32670}, /*Unicode: U+007b ({)*/
+ {.w_px = 3, .glyph_index = 32850}, /*Unicode: U+007c (|)*/
+ {.w_px = 5, .glyph_index = 32940}, /*Unicode: U+007d (})*/
+ {.w_px = 8, .glyph_index = 33090}, /*Unicode: U+007e (~)*/
+
+#endif
+};
+
+lv_font_t interui_30 =
+{
+ .unicode_first = 32, /*First Unicode letter in this font*/
+ .unicode_last = 126, /*Last Unicode letter in this font*/
+ .h_px = 30, /*Font height in pixels*/
+ //.glyph_bitmap = interui_30_glyph_bitmap, /*Bitmap of glyphs*/
+ .glyph_bitmap = (const uint8_t *)(NYX_RES_ADDR + 0x7900),
+ .glyph_dsc = interui_30_glyph_dsc, /*Description of glyphs*/
+ .glyph_cnt = 95, /*Number of glyphs in the font*/
+ .unicode_list = NULL, /*Every character in the font from 'unicode_first' to 'unicode_last'*/
+ .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/
+ .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/
+#if USE_INTERUI_30 == 4
+ .bpp = 4, /*Bit per pixel*/
+#elif USE_INTERUI_30 == 8
+ .bpp = 8, /*Bit per pixel*/
+#endif
+ .monospace = 0, /*Fix width (0: if not used)*/
+ .next_page = NULL, /*Pointer to a font extension*/
+};
+
+#endif /*USE_INTERUI_30*/
diff --git a/bdk/libs/lvgl/lv_fonts/lv_font_builtin.c b/bdk/libs/lvgl/lv_fonts/lv_font_builtin.c
new file mode 100644
index 0000000..16cbaea
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/lv_font_builtin.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * @file lv_font_built_in.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_font_builtin.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the built-in fonts
+ */
+void lv_font_builtin_init(void)
+{
+ /*InterUI 20*/
+#if USE_INTERUI_20 != 0
+ lv_font_add(&interui_20, NULL);
+#endif
+
+ /*SYMBOL 20*/
+#if USE_HEKATE_SYMBOL_20 != 0
+#if USE_INTERUI_20 != 0
+ lv_font_add(&hekate_symbol_20, &interui_20);
+#else
+ lv_font_add(&hekate_symbol_20, NULL);
+#endif
+#endif
+
+ /*InterUI 30*/
+#if USE_INTERUI_30 != 0
+ lv_font_add(&interui_30, NULL);
+#endif
+
+ /*SYMBOL 30*/
+#if USE_HEKATE_SYMBOL_30 != 0
+#if USE_INTERUI_30 != 0
+ lv_font_add(&hekate_symbol_30, &interui_30);
+#else
+ lv_font_add(&hekate_symbol_30, NULL);
+#endif
+#endif
+
+ /*MONO 12*/
+#if USE_UBUNTU_MONO != 0
+ lv_font_add(&ubuntu_mono, NULL);
+#if USE_INTERUI_20 != 0
+ lv_font_add(&hekate_symbol_20, &ubuntu_mono);
+#endif
+#endif
+
+ /*Symbol 120*/
+#if USE_HEKATE_SYMBOL_120 != 0
+ lv_font_add(&hekate_symbol_120, NULL);
+#endif
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/bdk/libs/lvgl/lv_fonts/lv_font_builtin.h b/bdk/libs/lvgl/lv_fonts/lv_font_builtin.h
new file mode 100644
index 0000000..519471f
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/lv_font_builtin.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * @file lv_font_builtin.h
+ *
+ */
+
+#ifndef LV_FONT_BUILTIN_H
+#define LV_FONT_BUILTIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#include "../lv_misc/lv_font.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the built-in fonts
+ */
+void lv_font_builtin_init(void);
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * FONT DECLARATIONS
+ **********************/
+
+/*20 px */
+#if USE_INTERUI_20
+LV_FONT_DECLARE(interui_20);
+#endif
+
+#if USE_HEKATE_SYMBOL_20
+LV_FONT_DECLARE(hekate_symbol_20);
+#endif
+
+/*30 px */
+#if USE_INTERUI_30
+LV_FONT_DECLARE(interui_30);
+#endif
+
+#if USE_HEKATE_SYMBOL_30
+LV_FONT_DECLARE(hekate_symbol_30);
+#endif
+
+#if USE_UBUNTU_MONO
+LV_FONT_DECLARE(ubuntu_mono);
+#endif
+
+#if USE_HEKATE_SYMBOL_120
+LV_FONT_DECLARE(hekate_symbol_120);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_FONT_BUILTIN_H*/
diff --git a/bdk/libs/lvgl/lv_fonts/lv_fonts.mk b/bdk/libs/lvgl/lv_fonts/lv_fonts.mk
new file mode 100644
index 0000000..d288f8a
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/lv_fonts.mk
@@ -0,0 +1,14 @@
+CSRCS += lv_font_builtin.c
+CSRCS += hekate_symbol_10.c
+CSRCS += hekate_symbol_20.c
+CSRCS += hekate_symbol_30.c
+CSRCS += hekate_symbol_40.c
+CSRCS += interui_12.c
+CSRCS += interui_20.c
+CSRCS += interui_30.c
+CSRCS += interui_40.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_fonts
+VPATH += :$(LVGL_DIR)/lvgl/lv_fonts
+
+CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_fonts"
diff --git a/bdk/libs/lvgl/lv_fonts/ubuntu_mono.c b/bdk/libs/lvgl/lv_fonts/ubuntu_mono.c
new file mode 100644
index 0000000..4c988fc
--- /dev/null
+++ b/bdk/libs/lvgl/lv_fonts/ubuntu_mono.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "../lv_misc/lv_font.h"
+
+#include
+
+#if USE_UBUNTU_MONO != 0 /*Can be enabled in lv_conf.h*/
+
+/***********************************************************************************
+ * UbuntuMono-B.ttf 20 px Font in U+0020 ( ) .. U+007e (~) range with all bpp
+***********************************************************************************/
+
+/*Store the glyph descriptions*/
+static const lv_font_glyph_dsc_t ubuntu_mono_glyph_dsc[] =
+{
+#if USE_UBUNTU_MONO == 4
+ {.w_px = 6, .glyph_index = 0}, /*Unicode: U+0020 ( )*/
+ {.w_px = 2, .glyph_index = 60}, /*Unicode: U+0021 (!)*/
+ {.w_px = 5, .glyph_index = 80}, /*Unicode: U+0022 (")*/
+ {.w_px = 10, .glyph_index = 140}, /*Unicode: U+0023 (#)*/
+ {.w_px = 8, .glyph_index = 240}, /*Unicode: U+0024 ($)*/
+ {.w_px = 10, .glyph_index = 320}, /*Unicode: U+0025 (%)*/
+ {.w_px = 10, .glyph_index = 420}, /*Unicode: U+0026 (&)*/
+ {.w_px = 2, .glyph_index = 520}, /*Unicode: U+0027 (')*/
+ {.w_px = 6, .glyph_index = 540}, /*Unicode: U+0028 (()*/
+ {.w_px = 6, .glyph_index = 600}, /*Unicode: U+0029 ())*/
+ {.w_px = 9, .glyph_index = 660}, /*Unicode: U+002a (*)*/
+ {.w_px = 8, .glyph_index = 760}, /*Unicode: U+002b (+)*/
+ {.w_px = 4, .glyph_index = 840}, /*Unicode: U+002c (,)*/
+ {.w_px = 5, .glyph_index = 880}, /*Unicode: U+002d (-)*/
+ {.w_px = 4, .glyph_index = 940}, /*Unicode: U+002e (.)*/
+ {.w_px = 8, .glyph_index = 980}, /*Unicode: U+002f (/)*/
+ {.w_px = 8, .glyph_index = 1060}, /*Unicode: U+0030 (0)*/
+ {.w_px = 7, .glyph_index = 1140}, /*Unicode: U+0031 (1)*/
+ {.w_px = 8, .glyph_index = 1220}, /*Unicode: U+0032 (2)*/
+ {.w_px = 8, .glyph_index = 1300}, /*Unicode: U+0033 (3)*/
+ {.w_px = 8, .glyph_index = 1380}, /*Unicode: U+0034 (4)*/
+ {.w_px = 8, .glyph_index = 1460}, /*Unicode: U+0035 (5)*/
+ {.w_px = 8, .glyph_index = 1540}, /*Unicode: U+0036 (6)*/
+ {.w_px = 7, .glyph_index = 1620}, /*Unicode: U+0037 (7)*/
+ {.w_px = 8, .glyph_index = 1700}, /*Unicode: U+0038 (8)*/
+ {.w_px = 8, .glyph_index = 1780}, /*Unicode: U+0039 (9)*/
+ {.w_px = 4, .glyph_index = 1860}, /*Unicode: U+003a (:)*/
+ {.w_px = 4, .glyph_index = 1900}, /*Unicode: U+003b (;)*/
+ {.w_px = 9, .glyph_index = 1940}, /*Unicode: U+003c (<)*/
+ {.w_px = 8, .glyph_index = 2040}, /*Unicode: U+003d (=)*/
+ {.w_px = 8, .glyph_index = 2120}, /*Unicode: U+003e (>)*/
+ {.w_px = 8, .glyph_index = 2200}, /*Unicode: U+003f (?)*/
+ {.w_px = 9, .glyph_index = 2280}, /*Unicode: U+0040 (@)*/
+ {.w_px = 10, .glyph_index = 2380}, /*Unicode: U+0041 (A)*/
+ {.w_px = 8, .glyph_index = 2480}, /*Unicode: U+0042 (B)*/
+ {.w_px = 8, .glyph_index = 2560}, /*Unicode: U+0043 (C)*/
+ {.w_px = 8, .glyph_index = 2640}, /*Unicode: U+0044 (D)*/
+ {.w_px = 8, .glyph_index = 2720}, /*Unicode: U+0045 (E)*/
+ {.w_px = 8, .glyph_index = 2800}, /*Unicode: U+0046 (F)*/
+ {.w_px = 8, .glyph_index = 2880}, /*Unicode: U+0047 (G)*/
+ {.w_px = 8, .glyph_index = 2960}, /*Unicode: U+0048 (H)*/
+ {.w_px = 8, .glyph_index = 3040}, /*Unicode: U+0049 (I)*/
+ {.w_px = 8, .glyph_index = 3120}, /*Unicode: U+004a (J)*/
+ {.w_px = 9, .glyph_index = 3200}, /*Unicode: U+004b (K)*/
+ {.w_px = 8, .glyph_index = 3300}, /*Unicode: U+004c (L)*/
+ {.w_px = 9, .glyph_index = 3380}, /*Unicode: U+004d (M)*/
+ {.w_px = 8, .glyph_index = 3480}, /*Unicode: U+004e (N)*/
+ {.w_px = 8, .glyph_index = 3560}, /*Unicode: U+004f (O)*/
+ {.w_px = 8, .glyph_index = 3640}, /*Unicode: U+0050 (P)*/
+ {.w_px = 8, .glyph_index = 3720}, /*Unicode: U+0051 (Q)*/
+ {.w_px = 8, .glyph_index = 3800}, /*Unicode: U+0052 (R)*/
+ {.w_px = 8, .glyph_index = 3880}, /*Unicode: U+0053 (S)*/
+ {.w_px = 8, .glyph_index = 3960}, /*Unicode: U+0054 (T)*/
+ {.w_px = 8, .glyph_index = 4040}, /*Unicode: U+0055 (U)*/
+ {.w_px = 9, .glyph_index = 4120}, /*Unicode: U+0056 (V)*/
+ {.w_px = 9, .glyph_index = 4220}, /*Unicode: U+0057 (W)*/
+ {.w_px = 10, .glyph_index = 4320}, /*Unicode: U+0058 (X)*/
+ {.w_px = 10, .glyph_index = 4420}, /*Unicode: U+0059 (Y)*/
+ {.w_px = 8, .glyph_index = 4520}, /*Unicode: U+005a (Z)*/
+ {.w_px = 6, .glyph_index = 4600}, /*Unicode: U+005b ([)*/
+ {.w_px = 8, .glyph_index = 4660}, /*Unicode: U+005c (\)*/
+ {.w_px = 6, .glyph_index = 4740}, /*Unicode: U+005d (])*/
+ {.w_px = 10, .glyph_index = 4800}, /*Unicode: U+005e (^)*/
+ {.w_px = 10, .glyph_index = 4900}, /*Unicode: U+005f (_)*/
+ {.w_px = 4, .glyph_index = 5000}, /*Unicode: U+0060 (`)*/
+ {.w_px = 8, .glyph_index = 5040}, /*Unicode: U+0061 (a)*/
+ {.w_px = 8, .glyph_index = 5120}, /*Unicode: U+0062 (b)*/
+ {.w_px = 8, .glyph_index = 5200}, /*Unicode: U+0063 (c)*/
+ {.w_px = 8, .glyph_index = 5280}, /*Unicode: U+0064 (d)*/
+ {.w_px = 9, .glyph_index = 5360}, /*Unicode: U+0065 (e)*/
+ {.w_px = 9, .glyph_index = 5460}, /*Unicode: U+0066 (f)*/
+ {.w_px = 8, .glyph_index = 5560}, /*Unicode: U+0067 (g)*/
+ {.w_px = 8, .glyph_index = 5640}, /*Unicode: U+0068 (h)*/
+ {.w_px = 9, .glyph_index = 5720}, /*Unicode: U+0069 (i)*/
+ {.w_px = 7, .glyph_index = 5820}, /*Unicode: U+006a (j)*/
+ {.w_px = 9, .glyph_index = 5900}, /*Unicode: U+006b (k)*/
+ {.w_px = 8, .glyph_index = 6000}, /*Unicode: U+006c (l)*/
+ {.w_px = 8, .glyph_index = 6080}, /*Unicode: U+006d (m)*/
+ {.w_px = 8, .glyph_index = 6160}, /*Unicode: U+006e (n)*/
+ {.w_px = 8, .glyph_index = 6240}, /*Unicode: U+006f (o)*/
+ {.w_px = 8, .glyph_index = 6320}, /*Unicode: U+0070 (p)*/
+ {.w_px = 8, .glyph_index = 6400}, /*Unicode: U+0071 (q)*/
+ {.w_px = 7, .glyph_index = 6480}, /*Unicode: U+0072 (r)*/
+ {.w_px = 8, .glyph_index = 6560}, /*Unicode: U+0073 (s)*/
+ {.w_px = 8, .glyph_index = 6640}, /*Unicode: U+0074 (t)*/
+ {.w_px = 8, .glyph_index = 6720}, /*Unicode: U+0075 (u)*/
+ {.w_px = 9, .glyph_index = 6800}, /*Unicode: U+0076 (v)*/
+ {.w_px = 10, .glyph_index = 6900}, /*Unicode: U+0077 (w)*/
+ {.w_px = 10, .glyph_index = 7000}, /*Unicode: U+0078 (x)*/
+ {.w_px = 9, .glyph_index = 7100}, /*Unicode: U+0079 (y)*/
+ {.w_px = 8, .glyph_index = 7200}, /*Unicode: U+007a (z)*/
+ {.w_px = 7, .glyph_index = 7280}, /*Unicode: U+007b ({)*/
+ {.w_px = 2, .glyph_index = 7360}, /*Unicode: U+007c (|)*/
+ {.w_px = 8, .glyph_index = 7380}, /*Unicode: U+007d (})*/
+ {.w_px = 9, .glyph_index = 7460}, /*Unicode: U+007e (~)*/
+
+#elif USE_UBUNTU_MONO == 8
+ {.w_px = 6, .glyph_index = 0}, /*Unicode: U+0020 ( )*/
+ {.w_px = 2, .glyph_index = 120}, /*Unicode: U+0021 (!)*/
+ {.w_px = 5, .glyph_index = 160}, /*Unicode: U+0022 (")*/
+ {.w_px = 10, .glyph_index = 260}, /*Unicode: U+0023 (#)*/
+ {.w_px = 8, .glyph_index = 460}, /*Unicode: U+0024 ($)*/
+ {.w_px = 10, .glyph_index = 620}, /*Unicode: U+0025 (%)*/
+ {.w_px = 10, .glyph_index = 820}, /*Unicode: U+0026 (&)*/
+ {.w_px = 2, .glyph_index = 1020}, /*Unicode: U+0027 (')*/
+ {.w_px = 6, .glyph_index = 1060}, /*Unicode: U+0028 (()*/
+ {.w_px = 6, .glyph_index = 1180}, /*Unicode: U+0029 ())*/
+ {.w_px = 9, .glyph_index = 1300}, /*Unicode: U+002a (*)*/
+ {.w_px = 8, .glyph_index = 1480}, /*Unicode: U+002b (+)*/
+ {.w_px = 4, .glyph_index = 1640}, /*Unicode: U+002c (,)*/
+ {.w_px = 5, .glyph_index = 1720}, /*Unicode: U+002d (-)*/
+ {.w_px = 4, .glyph_index = 1820}, /*Unicode: U+002e (.)*/
+ {.w_px = 8, .glyph_index = 1900}, /*Unicode: U+002f (/)*/
+ {.w_px = 8, .glyph_index = 2060}, /*Unicode: U+0030 (0)*/
+ {.w_px = 7, .glyph_index = 2220}, /*Unicode: U+0031 (1)*/
+ {.w_px = 8, .glyph_index = 2360}, /*Unicode: U+0032 (2)*/
+ {.w_px = 8, .glyph_index = 2520}, /*Unicode: U+0033 (3)*/
+ {.w_px = 8, .glyph_index = 2680}, /*Unicode: U+0034 (4)*/
+ {.w_px = 8, .glyph_index = 2840}, /*Unicode: U+0035 (5)*/
+ {.w_px = 8, .glyph_index = 3000}, /*Unicode: U+0036 (6)*/
+ {.w_px = 7, .glyph_index = 3160}, /*Unicode: U+0037 (7)*/
+ {.w_px = 8, .glyph_index = 3300}, /*Unicode: U+0038 (8)*/
+ {.w_px = 8, .glyph_index = 3460}, /*Unicode: U+0039 (9)*/
+ {.w_px = 4, .glyph_index = 3620}, /*Unicode: U+003a (:)*/
+ {.w_px = 4, .glyph_index = 3700}, /*Unicode: U+003b (;)*/
+ {.w_px = 9, .glyph_index = 3780}, /*Unicode: U+003c (<)*/
+ {.w_px = 8, .glyph_index = 3960}, /*Unicode: U+003d (=)*/
+ {.w_px = 8, .glyph_index = 4120}, /*Unicode: U+003e (>)*/
+ {.w_px = 8, .glyph_index = 4280}, /*Unicode: U+003f (?)*/
+ {.w_px = 9, .glyph_index = 4440}, /*Unicode: U+0040 (@)*/
+ {.w_px = 10, .glyph_index = 4620}, /*Unicode: U+0041 (A)*/
+ {.w_px = 8, .glyph_index = 4820}, /*Unicode: U+0042 (B)*/
+ {.w_px = 8, .glyph_index = 4980}, /*Unicode: U+0043 (C)*/
+ {.w_px = 8, .glyph_index = 5140}, /*Unicode: U+0044 (D)*/
+ {.w_px = 8, .glyph_index = 5300}, /*Unicode: U+0045 (E)*/
+ {.w_px = 8, .glyph_index = 5460}, /*Unicode: U+0046 (F)*/
+ {.w_px = 8, .glyph_index = 5620}, /*Unicode: U+0047 (G)*/
+ {.w_px = 8, .glyph_index = 5780}, /*Unicode: U+0048 (H)*/
+ {.w_px = 8, .glyph_index = 5940}, /*Unicode: U+0049 (I)*/
+ {.w_px = 8, .glyph_index = 6100}, /*Unicode: U+004a (J)*/
+ {.w_px = 9, .glyph_index = 6260}, /*Unicode: U+004b (K)*/
+ {.w_px = 8, .glyph_index = 6440}, /*Unicode: U+004c (L)*/
+ {.w_px = 9, .glyph_index = 6600}, /*Unicode: U+004d (M)*/
+ {.w_px = 8, .glyph_index = 6780}, /*Unicode: U+004e (N)*/
+ {.w_px = 8, .glyph_index = 6940}, /*Unicode: U+004f (O)*/
+ {.w_px = 8, .glyph_index = 7100}, /*Unicode: U+0050 (P)*/
+ {.w_px = 8, .glyph_index = 7260}, /*Unicode: U+0051 (Q)*/
+ {.w_px = 8, .glyph_index = 7420}, /*Unicode: U+0052 (R)*/
+ {.w_px = 8, .glyph_index = 7580}, /*Unicode: U+0053 (S)*/
+ {.w_px = 8, .glyph_index = 7740}, /*Unicode: U+0054 (T)*/
+ {.w_px = 8, .glyph_index = 7900}, /*Unicode: U+0055 (U)*/
+ {.w_px = 9, .glyph_index = 8060}, /*Unicode: U+0056 (V)*/
+ {.w_px = 9, .glyph_index = 8240}, /*Unicode: U+0057 (W)*/
+ {.w_px = 10, .glyph_index = 8420}, /*Unicode: U+0058 (X)*/
+ {.w_px = 10, .glyph_index = 8620}, /*Unicode: U+0059 (Y)*/
+ {.w_px = 8, .glyph_index = 8820}, /*Unicode: U+005a (Z)*/
+ {.w_px = 6, .glyph_index = 8980}, /*Unicode: U+005b ([)*/
+ {.w_px = 8, .glyph_index = 9100}, /*Unicode: U+005c (\)*/
+ {.w_px = 6, .glyph_index = 9260}, /*Unicode: U+005d (])*/
+ {.w_px = 10, .glyph_index = 9380}, /*Unicode: U+005e (^)*/
+ {.w_px = 10, .glyph_index = 9580}, /*Unicode: U+005f (_)*/
+ {.w_px = 4, .glyph_index = 9780}, /*Unicode: U+0060 (`)*/
+ {.w_px = 8, .glyph_index = 9860}, /*Unicode: U+0061 (a)*/
+ {.w_px = 8, .glyph_index = 10020}, /*Unicode: U+0062 (b)*/
+ {.w_px = 8, .glyph_index = 10180}, /*Unicode: U+0063 (c)*/
+ {.w_px = 8, .glyph_index = 10340}, /*Unicode: U+0064 (d)*/
+ {.w_px = 9, .glyph_index = 10500}, /*Unicode: U+0065 (e)*/
+ {.w_px = 9, .glyph_index = 10680}, /*Unicode: U+0066 (f)*/
+ {.w_px = 8, .glyph_index = 10860}, /*Unicode: U+0067 (g)*/
+ {.w_px = 8, .glyph_index = 11020}, /*Unicode: U+0068 (h)*/
+ {.w_px = 9, .glyph_index = 11180}, /*Unicode: U+0069 (i)*/
+ {.w_px = 7, .glyph_index = 11360}, /*Unicode: U+006a (j)*/
+ {.w_px = 9, .glyph_index = 11500}, /*Unicode: U+006b (k)*/
+ {.w_px = 8, .glyph_index = 11680}, /*Unicode: U+006c (l)*/
+ {.w_px = 8, .glyph_index = 11840}, /*Unicode: U+006d (m)*/
+ {.w_px = 8, .glyph_index = 12000}, /*Unicode: U+006e (n)*/
+ {.w_px = 8, .glyph_index = 12160}, /*Unicode: U+006f (o)*/
+ {.w_px = 8, .glyph_index = 12320}, /*Unicode: U+0070 (p)*/
+ {.w_px = 8, .glyph_index = 12480}, /*Unicode: U+0071 (q)*/
+ {.w_px = 7, .glyph_index = 12640}, /*Unicode: U+0072 (r)*/
+ {.w_px = 8, .glyph_index = 12780}, /*Unicode: U+0073 (s)*/
+ {.w_px = 8, .glyph_index = 12940}, /*Unicode: U+0074 (t)*/
+ {.w_px = 8, .glyph_index = 13100}, /*Unicode: U+0075 (u)*/
+ {.w_px = 9, .glyph_index = 13260}, /*Unicode: U+0076 (v)*/
+ {.w_px = 10, .glyph_index = 13440}, /*Unicode: U+0077 (w)*/
+ {.w_px = 10, .glyph_index = 13640}, /*Unicode: U+0078 (x)*/
+ {.w_px = 9, .glyph_index = 13840}, /*Unicode: U+0079 (y)*/
+ {.w_px = 8, .glyph_index = 14020}, /*Unicode: U+007a (z)*/
+ {.w_px = 7, .glyph_index = 14180}, /*Unicode: U+007b ({)*/
+ {.w_px = 2, .glyph_index = 14320}, /*Unicode: U+007c (|)*/
+ {.w_px = 8, .glyph_index = 14360}, /*Unicode: U+007d (})*/
+ {.w_px = 9, .glyph_index = 14520}, /*Unicode: U+007e (~)*/
+
+#endif
+};
+
+lv_font_t ubuntu_mono =
+{
+ .unicode_first = 32, /*First Unicode letter in this font*/
+ .unicode_last = 126, /*Last Unicode letter in this font*/
+ .h_px = 20, /*Font height in pixels*/
+ //.glyph_bitmap = ubuntu_mono_glyph_bitmap, /*Bitmap of glyphs*/
+ .glyph_bitmap = (const uint8_t *)(NYX_RES_ADDR),
+ .glyph_dsc = ubuntu_mono_glyph_dsc, /*Description of glyphs*/
+ .glyph_cnt = 95, /*Number of glyphs in the font*/
+ .unicode_list = NULL, /*Every character in the font from 'unicode_first' to 'unicode_last'*/
+ .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/
+ .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/
+#if USE_UBUNTU_MONO == 4
+ .bpp = 4, /*Bit per pixel*/
+#elif USE_UBUNTU_MONO == 8
+ .bpp = 8, /*Bit per pixel*/
+#endif
+ .monospace = 10, /*Fix width (0: if not used)*/
+ .next_page = NULL, /*Pointer to a font extension*/
+};
+
+#endif /*USE_UBUNTU_MONO*/
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal.h b/bdk/libs/lvgl/lv_hal/lv_hal.h
new file mode 100644
index 0000000..5ab28f2
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal.h
@@ -0,0 +1,40 @@
+/**
+ * @file hal.h
+ *
+ */
+
+#ifndef HAL_H
+#define HAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_hal_disp.h"
+#include "lv_hal_indev.h"
+#include "lv_hal_tick.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal.mk b/bdk/libs/lvgl/lv_hal/lv_hal.mk
new file mode 100644
index 0000000..83f4bf1
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal.mk
@@ -0,0 +1,8 @@
+CSRCS += lv_hal_disp.c
+CSRCS += lv_hal_indev.c
+CSRCS += lv_hal_tick.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_hal
+VPATH += :$(LVGL_DIR)/lvgl/lv_hal
+
+CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_hal"
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal_disp.c b/bdk/libs/lvgl/lv_hal/lv_hal_disp.c
new file mode 100644
index 0000000..3be8b92
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal_disp.c
@@ -0,0 +1,242 @@
+
+/**
+ * @file hal_disp.c
+ *
+ * @description HAL layer for display driver
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include
+#include
+#include "../lv_hal/lv_hal_disp.h"
+#include "../lv_misc/lv_mem.h"
+#include "../lv_core/lv_obj.h"
+#include "../lv_misc/lv_gc.h"
+
+#if defined(LV_GC_INCLUDE)
+# include LV_GC_INCLUDE
+#endif /* LV_ENABLE_GC */
+
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_disp_t * active;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize a display driver with default values.
+ * It is used to surly have known values in the fields ant not memory junk.
+ * After it you can set the fields.
+ * @param driver pointer to driver variable to initialize
+ */
+void lv_disp_drv_init(lv_disp_drv_t * driver)
+{
+ driver->disp_fill = NULL;
+ driver->disp_map = NULL;
+ driver->disp_flush = NULL;
+
+#if USE_LV_GPU
+ driver->mem_blend = NULL;
+ driver->mem_fill = NULL;
+#endif
+
+#if LV_VDB_SIZE
+ driver->vdb_wr = NULL;
+#endif
+}
+
+/**
+ * Register an initialized display driver.
+ * Automatically set the first display as active.
+ * @param driver pointer to an initialized 'lv_disp_drv_t' variable (can be local variable)
+ * @return pointer to the new display or NULL on error
+ */
+lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
+{
+ lv_disp_t * node;
+
+ node = lv_mem_alloc(sizeof(lv_disp_t));
+ lv_mem_assert(node);
+ if(node == NULL) return NULL;
+
+ memcpy(&node->driver, driver, sizeof(lv_disp_drv_t));
+ node->next = NULL;
+
+ /* Set first display as active by default */
+ if(LV_GC_ROOT(_lv_disp_list) == NULL) {
+ LV_GC_ROOT(_lv_disp_list) = node;
+ active = node;
+ lv_obj_invalidate(lv_scr_act());
+ } else {
+ ((lv_disp_t*)LV_GC_ROOT(_lv_disp_list))->next = node;
+ }
+
+ return node;
+}
+
+
+/**
+ * Set the active display
+ * @param disp pointer to a display (return value of 'lv_disp_register')
+ */
+void lv_disp_set_active(lv_disp_t * disp)
+{
+ active = disp;
+ lv_obj_invalidate(lv_scr_act());
+}
+
+/**
+ * Get a pointer to the active display
+ * @return pointer to the active display
+ */
+lv_disp_t * lv_disp_get_active(void)
+{
+ return active;
+}
+
+/**
+ * Get the next display.
+ * @param disp pointer to the current display. NULL to initialize.
+ * @return the next display or NULL if no more. Give the first display when the parameter is NULL
+ */
+lv_disp_t * lv_disp_next(lv_disp_t * disp)
+{
+ if(disp == NULL) {
+ return LV_GC_ROOT(_lv_disp_list);
+ } else {
+ if(((lv_disp_t*)LV_GC_ROOT(_lv_disp_list))->next == NULL) return NULL;
+ else return ((lv_disp_t*)LV_GC_ROOT(_lv_disp_list))->next;
+ }
+}
+
+/**
+ * Write the content of the internal buffer (VDB) to the display
+ * @param x1 left coordinate of the rectangle
+ * @param x2 right coordinate of the rectangle
+ * @param y1 top coordinate of the rectangle
+ * @param y2 bottom coordinate of the rectangle
+ * @param color_p fill color
+ */
+void lv_disp_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color)
+{
+ if(active == NULL) return;
+ if(active->driver.disp_fill != NULL) active->driver.disp_fill(x1, y1, x2, y2, color);
+}
+
+/**
+ * Fill a rectangular area with a color on the active display
+ * @param x1 left coordinate of the rectangle
+ * @param x2 right coordinate of the rectangle
+ * @param y1 top coordinate of the rectangle
+ * @param y2 bottom coordinate of the rectangle
+ * @param color_p pointer to an array of colors
+ */
+void lv_disp_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t * color_p)
+{
+ if(active == NULL) return;
+ if(active->driver.disp_flush != NULL) {
+
+ LV_LOG_TRACE("disp flush started");
+ active->driver.disp_flush(x1, y1, x2, y2, color_p);
+ LV_LOG_TRACE("disp flush ready");
+
+ } else {
+ LV_LOG_WARN("disp flush function registered");
+ }
+}
+
+/**
+ * Put a color map to a rectangular area on the active display
+ * @param x1 left coordinate of the rectangle
+ * @param x2 right coordinate of the rectangle
+ * @param y1 top coordinate of the rectangle
+ * @param y2 bottom coordinate of the rectangle
+ * @param color_map pointer to an array of colors
+ */
+void lv_disp_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_map)
+{
+ if(active == NULL) return;
+ if(active->driver.disp_map != NULL) active->driver.disp_map(x1, y1, x2, y2, color_map);
+}
+
+#if USE_LV_GPU
+
+/**
+ * Blend pixels to a destination memory from a source memory
+ * In 'lv_disp_drv_t' 'mem_blend' is optional. (NULL if not available)
+ * @param dest a memory address. Blend 'src' here.
+ * @param src pointer to pixel map. Blend it to 'dest'.
+ * @param length number of pixels in 'src'
+ * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
+ */
+void lv_disp_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
+{
+ if(active == NULL) return;
+ if(active->driver.mem_blend != NULL) active->driver.mem_blend(dest, src, length, opa);
+}
+
+/**
+ * Fill a memory with a color (GPUs may support it)
+ * In 'lv_disp_drv_t' 'mem_fill' is optional. (NULL if not available)
+ * @param dest a memory address. Copy 'src' here.
+ * @param src pointer to pixel map. Copy it to 'dest'.
+ * @param length number of pixels in 'src'
+ * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
+ */
+void lv_disp_mem_fill(lv_color_t * dest, uint32_t length, lv_color_t color)
+{
+ if(active == NULL) return;
+ if(active->driver.mem_fill != NULL) active->driver.mem_fill(dest, length, color);
+}
+
+/**
+ * Shows if memory blending (by GPU) is supported or not
+ * @return false: 'mem_blend' is not supported in the driver; true: 'mem_blend' is supported in the driver
+ */
+bool lv_disp_is_mem_blend_supported(void)
+{
+ if(active == NULL) return false;
+ if(active->driver.mem_blend) return true;
+ else return false;
+}
+
+/**
+ * Shows if memory fill (by GPU) is supported or not
+ * @return false: 'mem_fill' is not supported in the drover; true: 'mem_fill' is supported in the driver
+ */
+bool lv_disp_is_mem_fill_supported(void)
+{
+ if(active == NULL) return false;
+ if(active->driver.mem_fill) return true;
+ else return false;
+}
+
+#endif
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal_disp.h b/bdk/libs/lvgl/lv_hal/lv_hal_disp.h
new file mode 100644
index 0000000..95b7ab5
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal_disp.h
@@ -0,0 +1,173 @@
+/**
+ * @file hal_disp.h
+ *
+ * @description Display Driver HAL interface header file
+ *
+ */
+
+#ifndef HAL_DISP_H
+#define HAL_DISP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include
+#include "lv_hal.h"
+#include "../lv_misc/lv_color.h"
+#include "../lv_misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**
+ * Display Driver structure to be registered by HAL
+ */
+typedef struct _disp_drv_t {
+ /*Write the internal buffer (VDB) to the display. 'lv_flush_ready()' has to be called when finished*/
+ void (*disp_flush)(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p);
+
+ /*Fill an area with a color on the display*/
+ void (*disp_fill)(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color);
+
+ /*Write pixel map (e.g. image) to the display*/
+ void (*disp_map)(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p);
+
+ /*Optional interface functions to use GPU*/
+#if USE_LV_GPU
+ /*Blend two memories using opacity (GPU only)*/
+ void (*mem_blend)(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
+
+ /*Fill a memory with a color (GPU only)*/
+ void (*mem_fill)(lv_color_t * dest, uint32_t length, lv_color_t color);
+#endif
+
+#if LV_VDB_SIZE
+ /*Optional: Set a pixel in a buffer according to the requirements of the display*/
+ void (*vdb_wr)(uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
+#endif
+} lv_disp_drv_t;
+
+typedef struct _disp_t {
+ lv_disp_drv_t driver;
+ struct _disp_t *next;
+} lv_disp_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize a display driver with default values.
+ * It is used to surly have known values in the fields ant not memory junk.
+ * After it you can set the fields.
+ * @param driver pointer to driver variable to initialize
+ */
+void lv_disp_drv_init(lv_disp_drv_t *driver);
+
+/**
+ * Register an initialized display driver.
+ * Automatically set the first display as active.
+ * @param driver pointer to an initialized 'lv_disp_drv_t' variable (can be local variable)
+ * @return pointer to the new display or NULL on error
+ */
+lv_disp_t * lv_disp_drv_register(lv_disp_drv_t *driver);
+
+/**
+ * Set the active display
+ * @param disp pointer to a display (return value of 'lv_disp_register')
+ */
+void lv_disp_set_active(lv_disp_t * disp);
+
+/**
+ * Get a pointer to the active display
+ * @return pointer to the active display
+ */
+lv_disp_t * lv_disp_get_active(void);
+
+/**
+ * Get the next display.
+ * @param disp pointer to the current display. NULL to initialize.
+ * @return the next display or NULL if no more. Give the first display when the parameter is NULL
+ */
+lv_disp_t * lv_disp_next(lv_disp_t * disp);
+
+/**
+ * Fill a rectangular area with a color on the active display
+ * @param x1 left coordinate of the rectangle
+ * @param x2 right coordinate of the rectangle
+ * @param y1 top coordinate of the rectangle
+ * @param y2 bottom coordinate of the rectangle
+ * @param color_p pointer to an array of colors
+ */
+void lv_disp_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t *color_p);
+
+/**
+ * Fill a rectangular area with a color on the active display
+ * @param x1 left coordinate of the rectangle
+ * @param x2 right coordinate of the rectangle
+ * @param y1 top coordinate of the rectangle
+ * @param y2 bottom coordinate of the rectangle
+ * @param color fill color
+ */
+void lv_disp_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color);
+
+/**
+ * Put a color map to a rectangular area on the active display
+ * @param x1 left coordinate of the rectangle
+ * @param x2 right coordinate of the rectangle
+ * @param y1 top coordinate of the rectangle
+ * @param y2 bottom coordinate of the rectangle
+ * @param color_map pointer to an array of colors
+ */
+void lv_disp_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_map);
+
+#if USE_LV_GPU
+/**
+ * Blend pixels to a destination memory from a source memory
+ * In 'lv_disp_drv_t' 'mem_blend' is optional. (NULL if not available)
+ * @param dest a memory address. Blend 'src' here.
+ * @param src pointer to pixel map. Blend it to 'dest'.
+ * @param length number of pixels in 'src'
+ * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
+ */
+void lv_disp_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
+
+/**
+ * Fill a memory with a color (GPUs may support it)
+ * In 'lv_disp_drv_t' 'mem_fill' is optional. (NULL if not available)
+ * @param dest a memory address. Copy 'src' here.
+ * @param src pointer to pixel map. Copy it to 'dest'.
+ * @param length number of pixels in 'src'
+ * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
+ */
+void lv_disp_mem_fill(lv_color_t * dest, uint32_t length, lv_color_t color);
+/**
+ * Shows if memory blending (by GPU) is supported or not
+ * @return false: 'mem_blend' is not supported in the driver; true: 'mem_blend' is supported in the driver
+ */
+bool lv_disp_is_mem_blend_supported(void);
+
+/**
+ * Shows if memory fill (by GPU) is supported or not
+ * @return false: 'mem_fill' is not supported in the drover; true: 'mem_fill' is supported in the driver
+ */
+bool lv_disp_is_mem_fill_supported(void);
+#endif
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal_indev.c b/bdk/libs/lvgl/lv_hal/lv_hal_indev.c
new file mode 100644
index 0000000..6083c3f
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal_indev.c
@@ -0,0 +1,135 @@
+/**
+ * @file hal_indev.c
+ *
+ * @description Input device HAL interface
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_hal/lv_hal_indev.h"
+#include "../lv_misc/lv_mem.h"
+#include "../lv_misc/lv_gc.h"
+
+#if defined(LV_GC_INCLUDE)
+# include LV_GC_INCLUDE
+#endif /* LV_ENABLE_GC */
+
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize an input device driver with default values.
+ * It is used to surly have known values in the fields ant not memory junk.
+ * After it you can set the fields.
+ * @param driver pointer to driver variable to initialize
+ */
+void lv_indev_drv_init(lv_indev_drv_t * driver)
+{
+ driver->read = NULL;
+ driver->type = LV_INDEV_TYPE_NONE;
+ driver->user_data = NULL;
+}
+
+/**
+ * Register an initialized input device driver.
+ * @param driver pointer to an initialized 'lv_indev_drv_t' variable (can be local variable)
+ * @return pointer to the new input device or NULL on error
+ */
+lv_indev_t * lv_indev_drv_register(lv_indev_drv_t * driver)
+{
+ lv_indev_t * node;
+
+ node = lv_mem_alloc(sizeof(lv_indev_t));
+ if(!node) return NULL;
+
+ memset(node, 0, sizeof(lv_indev_t));
+ memcpy(&node->driver, driver, sizeof(lv_indev_drv_t));
+
+ node->next = NULL;
+ node->proc.reset_query = 1;
+ node->cursor = NULL;
+ node->group = NULL;
+ node->btn_points = NULL;
+
+ if(LV_GC_ROOT(_lv_indev_list) == NULL) {
+ LV_GC_ROOT(_lv_indev_list) = node;
+ } else {
+ lv_indev_t * last = LV_GC_ROOT(_lv_indev_list);
+ while(last->next)
+ last = last->next;
+
+ last->next = node;
+ }
+
+ return node;
+}
+
+/**
+ * Get the next input device.
+ * @param indev pointer to the current input device. NULL to initialize.
+ * @return the next input devise or NULL if no more. Give the first input device when the parameter is NULL
+ */
+lv_indev_t * lv_indev_next(lv_indev_t * indev)
+{
+
+ if(indev == NULL) {
+ return LV_GC_ROOT(_lv_indev_list);
+ } else {
+ if(indev->next == NULL) return NULL;
+ else return indev->next;
+ }
+}
+
+/**
+ * Read data from an input device.
+ * @param indev pointer to an input device
+ * @param data input device will write its data here
+ * @return false: no more data; true: there more data to read (buffered)
+ */
+bool lv_indev_read(lv_indev_t * indev, lv_indev_data_t * data)
+{
+ bool cont = false;
+
+ memset(data, 0, sizeof(lv_indev_data_t));
+ data->state = LV_INDEV_STATE_REL;
+
+ if(indev->driver.read) {
+ data->user_data = indev->driver.user_data;
+
+ LV_LOG_TRACE("idnev read started");
+ cont = indev->driver.read(data);
+ LV_LOG_TRACE("idnev read finished");
+ } else {
+ LV_LOG_WARN("indev function registered");
+ }
+
+ return cont;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal_indev.h b/bdk/libs/lvgl/lv_hal/lv_hal_indev.h
new file mode 100644
index 0000000..2355baa
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal_indev.h
@@ -0,0 +1,166 @@
+/**
+ * @file hal_indev.h
+ *
+ * @description Input Device HAL interface layer header file
+ *
+ */
+
+#ifndef HAL_INDEV_H
+#define HAL_INDEV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include
+#include "lv_hal.h"
+#include
+#include "../lv_misc/lv_area.h"
+#include "../lv_core/lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/*Possible input device types*/
+enum {
+ LV_INDEV_TYPE_NONE, /*Show uninitialized state*/
+ LV_INDEV_TYPE_POINTER, /*Touch pad, mouse, external button*/
+ LV_INDEV_TYPE_KEYPAD, /*Keypad or keyboard*/
+ LV_INDEV_TYPE_BUTTON, /*External (hardware button) which is assinged to a specific point of the screen*/
+ LV_INDEV_TYPE_ENCODER, /*Encoder with only Left, Right turn and a Button*/
+};
+typedef uint8_t lv_hal_indev_type_t;
+
+/*States for input devices*/
+enum {
+ LV_INDEV_STATE_REL = 0,
+ LV_INDEV_STATE_PR
+};
+typedef uint8_t lv_indev_state_t;
+
+/*Data type when an input device is read */
+typedef struct {
+ union {
+ lv_point_t point; /*For LV_INDEV_TYPE_POINTER the currently pressed point*/
+ uint32_t key; /*For LV_INDEV_TYPE_KEYPAD the currently pressed key*/
+ uint32_t btn; /*For LV_INDEV_TYPE_BUTTON the currently pressed button*/
+ int16_t enc_diff; /*For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/
+ };
+ void *user_data; /*'lv_indev_drv_t.priv' for this driver*/
+ lv_indev_state_t state; /*LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/
+} lv_indev_data_t;
+
+/*Initialized by the user and registered by 'lv_indev_add()'*/
+typedef struct {
+ lv_hal_indev_type_t type; /*Input device type*/
+ bool (*read)(lv_indev_data_t *data); /*Function pointer to read data. Return 'true' if there is still data to be read (buffered)*/
+ void *user_data; /*Pointer to user defined data, passed in 'lv_indev_data_t' on read*/
+} lv_indev_drv_t;
+
+struct _lv_obj_t;
+
+/*Run time data of input devices*/
+typedef struct _lv_indev_proc_t {
+ lv_indev_state_t state;
+ union {
+ struct { /*Pointer and button data*/
+ lv_point_t act_point;
+ lv_point_t last_point;
+ lv_point_t vect;
+ lv_point_t drag_sum; /*Count the dragged pixels to check LV_INDEV_DRAG_LIMIT*/
+ struct _lv_obj_t * act_obj;
+ struct _lv_obj_t * last_obj;
+
+ /*Flags*/
+ uint8_t drag_range_out :1;
+ uint8_t drag_in_prog :1;
+ uint8_t wait_unil_release :1;
+ };
+ struct { /*Keypad data*/
+ lv_indev_state_t last_state;
+ uint32_t last_key;
+ };
+ };
+
+ uint32_t pr_timestamp; /*Pressed time stamp*/
+ uint32_t longpr_rep_timestamp; /*Long press repeat time stamp*/
+
+ /*Flags*/
+ uint8_t long_pr_sent :1;
+ uint8_t reset_query :1;
+ uint8_t disabled :1;
+} lv_indev_proc_t;
+
+struct _lv_indev_t;
+
+typedef void (*lv_indev_feedback_t)(struct _lv_indev_t *, lv_signal_t);
+
+struct _lv_obj_t;
+struct _lv_group_t;
+
+/*The main input device descriptor with driver, runtime data ('proc') and some additional information*/
+typedef struct _lv_indev_t {
+ lv_indev_drv_t driver;
+ lv_indev_proc_t proc;
+ lv_indev_feedback_t feedback;
+ uint32_t last_activity_time;
+ union {
+ struct _lv_obj_t *cursor; /*Cursor for LV_INPUT_TYPE_POINTER*/
+ struct _lv_group_t *group; /*Keypad destination group*/
+ const lv_point_t * btn_points; /*Array points assigned to the button ()screen will be pressed here by the buttons*/
+
+ };
+ struct _lv_indev_t *next;
+} lv_indev_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize an input device driver with default values.
+ * It is used to surly have known values in the fields ant not memory junk.
+ * After it you can set the fields.
+ * @param driver pointer to driver variable to initialize
+ */
+void lv_indev_drv_init(lv_indev_drv_t *driver);
+
+/**
+ * Register an initialized input device driver.
+ * @param driver pointer to an initialized 'lv_indev_drv_t' variable (can be local variable)
+ * @return pointer to the new input device or NULL on error
+ */
+lv_indev_t * lv_indev_drv_register(lv_indev_drv_t *driver);
+
+/**
+ * Get the next input device.
+ * @param indev pointer to the current input device. NULL to initialize.
+ * @return the next input devise or NULL if no more. Gives the first input device when the parameter is NULL
+ */
+lv_indev_t * lv_indev_next(lv_indev_t * indev);
+
+/**
+ * Read data from an input device.
+ * @param indev pointer to an input device
+ * @param data input device will write its data here
+ * @return false: no more data; true: there more data to read (buffered)
+ */
+bool lv_indev_read(lv_indev_t * indev, lv_indev_data_t *data);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal_tick.c b/bdk/libs/lvgl/lv_hal/lv_hal_tick.c
new file mode 100644
index 0000000..0310092
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal_tick.c
@@ -0,0 +1,100 @@
+/**
+ * @file systick.c
+ * Provide access to the system tick with 1 millisecond resolution
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#include "lv_hal_tick.h"
+#include
+
+#if LV_TICK_CUSTOM == 1
+#include LV_TICK_CUSTOM_INCLUDE
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static uint32_t sys_time = 0;
+static volatile uint8_t tick_irq_flag;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * You have to call this function periodically
+ * @param tick_period the call period of this function in milliseconds
+ */
+LV_ATTRIBUTE_TICK_INC void lv_tick_inc(uint32_t tick_period)
+{
+ tick_irq_flag = 0;
+ sys_time += tick_period;
+}
+
+/**
+ * Get the elapsed milliseconds since start up
+ * @return the elapsed milliseconds
+ */
+uint32_t lv_tick_get(void)
+{
+#if LV_TICK_CUSTOM == 0
+ uint32_t result;
+ do {
+ tick_irq_flag = 1;
+ result = sys_time;
+ } while(!tick_irq_flag); /*'lv_tick_inc()' clears this flag which can be in an interrupt. Continue until make a non interrupted cycle */
+
+ return result;
+#else
+ return LV_TICK_CUSTOM_SYS_TIME_EXPR;
+#endif
+}
+
+/**
+ * Get the elapsed milliseconds since a previous time stamp
+ * @param prev_tick a previous time stamp (return value of systick_get() )
+ * @return the elapsed milliseconds since 'prev_tick'
+ */
+uint32_t lv_tick_elaps(uint32_t prev_tick)
+{
+ uint32_t act_time = lv_tick_get();
+
+ /*If there is no overflow in sys_time simple subtract*/
+ if(act_time >= prev_tick) {
+ prev_tick = act_time - prev_tick;
+ } else {
+ prev_tick = UINT32_MAX - prev_tick + 1;
+ prev_tick += act_time;
+ }
+
+ return prev_tick;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
diff --git a/bdk/libs/lvgl/lv_hal/lv_hal_tick.h b/bdk/libs/lvgl/lv_hal/lv_hal_tick.h
new file mode 100644
index 0000000..af3d8ea
--- /dev/null
+++ b/bdk/libs/lvgl/lv_hal/lv_hal_tick.h
@@ -0,0 +1,65 @@
+/**
+ * @file lv_hal_tick.h
+ * Provide access to the system tick with 1 millisecond resolution
+ */
+
+#ifndef LV_HAL_TICK_H
+#define LV_HAL_TICK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+#include
+
+/*********************
+ * DEFINES
+ *********************/
+#ifndef LV_ATTRIBUTE_TICK_INC
+#define LV_ATTRIBUTE_TICK_INC
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * You have to call this function periodically
+ * @param tick_period the call period of this function in milliseconds
+ */
+LV_ATTRIBUTE_TICK_INC void lv_tick_inc(uint32_t tick_period);
+
+/**
+ * Get the elapsed milliseconds since start up
+ * @return the elapsed milliseconds
+ */
+uint32_t lv_tick_get(void);
+
+/**
+ * Get the elapsed milliseconds since a previous time stamp
+ * @param prev_tick a previous time stamp (return value of systick_get() )
+ * @return the elapsed milliseconds since 'prev_tick'
+ */
+uint32_t lv_tick_elaps(uint32_t prev_tick);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*LV_HAL_TICK_H*/
diff --git a/bdk/libs/lvgl/lv_misc/lv_anim.c b/bdk/libs/lvgl/lv_misc/lv_anim.c
new file mode 100644
index 0000000..578c556
--- /dev/null
+++ b/bdk/libs/lvgl/lv_misc/lv_anim.c
@@ -0,0 +1,445 @@
+/**
+ * @file anim.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_anim.h"
+
+#if USE_LV_ANIMATION
+#include
+#include
+#include "../lv_hal/lv_hal_tick.h"
+#include "lv_task.h"
+#include "lv_math.h"
+#include "lv_gc.h"
+
+#if defined(LV_GC_INCLUDE)
+# include LV_GC_INCLUDE
+#endif /* LV_ENABLE_GC */
+
+
+/*********************
+ * DEFINES
+ *********************/
+#define LV_ANIM_RESOLUTION 1024
+#define LV_ANIM_RES_SHIFT 10
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void anim_task(void * param);
+static bool anim_ready_handler(lv_anim_t * a);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static uint32_t last_task_run;
+static bool anim_list_changed;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Init. the animation module
+ */
+void lv_anim_init(void)
+{
+ lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t));
+ last_task_run = lv_tick_get();
+ lv_task_create(anim_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);
+}
+
+/**
+ * Create an animation
+ * @param anim_p an initialized 'anim_t' variable. Not required after call.
+ */
+void lv_anim_create(lv_anim_t * anim_p)
+{
+ LV_LOG_TRACE("animation create started")
+ /* Do not let two animations for the same 'var' with the same 'fp'*/
+ if(anim_p->fp != NULL) lv_anim_del(anim_p->var, anim_p->fp); /*fp == NULL would delete all animations of var*/
+
+ /*Add the new animation to the animation linked list*/
+ lv_anim_t * new_anim = lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll));
+ lv_mem_assert(new_anim);
+ if(new_anim == NULL) return;
+
+ /*Initialize the animation descriptor*/
+ anim_p->playback_now = 0;
+ memcpy(new_anim, anim_p, sizeof(lv_anim_t));
+
+ /*Set the start value*/
+ if(new_anim->fp != NULL) new_anim->fp(new_anim->var, new_anim->start);
+
+ /* Creating an animation changed the linked list.
+ * It's important if it happens in a ready callback. (see `anim_task`)*/
+ anim_list_changed = true;
+
+ LV_LOG_TRACE("animation created")
+}
+
+/**
+ * Delete an animation for a variable with a given animator function
+ * @param var pointer to variable
+ * @param fp a function pointer which is animating 'var',
+ * or NULL to delete all animations of 'var'
+ * @return true: at least 1 animation is deleted, false: no animation is deleted
+ */
+bool lv_anim_del(void * var, lv_anim_fp_t fp)
+{
+ lv_anim_t * a;
+ lv_anim_t * a_next;
+ bool del = false;
+ a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
+ while(a != NULL) {
+ /*'a' might be deleted, so get the next object while 'a' is valid*/
+ a_next = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
+
+ if(a->var == var && (a->fp == fp || fp == NULL)) {
+ lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);
+ lv_mem_free(a);
+ anim_list_changed = true; /*Read by `anim_task`. It need to know if a delete occurred in the linked list*/
+ del = true;
+ }
+
+ a = a_next;
+ }
+
+ return del;
+}
+
+/**
+ * Get the number of currently running animations
+ * @return the number of running animations
+ */
+uint16_t lv_anim_count_running(void)
+{
+ uint16_t cnt = 0;
+ lv_anim_t * a;
+ LL_READ(LV_GC_ROOT(_lv_anim_ll), a) cnt++;
+
+ return cnt++;
+}
+
+/**
+ * Calculate the time of an animation with a given speed and the start and end values
+ * @param speed speed of animation in unit/sec
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @return the required time [ms] for the animation with the given parameters
+ */
+uint16_t lv_anim_speed_to_time(uint16_t speed, int32_t start, int32_t end)
+{
+ int32_t d = LV_MATH_ABS((int32_t) start - end);
+ uint32_t time = (int32_t)((int32_t)(d * 1000) / speed);
+
+ if(time > UINT16_MAX) time = UINT16_MAX;
+
+ if(time == 0) {
+ time++;
+ }
+
+ return time;
+}
+
+/**
+ * Calculate the current value of an animation applying linear characteristic
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_linear(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint16_t step;
+ if(a->time == a->act_time) step = LV_ANIM_RESOLUTION; /*Use the last value if the time fully elapsed*/
+ else step = (a->act_time * LV_ANIM_RESOLUTION) / a->time;
+
+ /* Get the new value which will be proportional to `step`
+ * and the `start` and `end` values*/
+ int32_t new_value;
+ new_value = (int32_t) step * (a->end - a->start);
+ new_value = new_value >> LV_ANIM_RES_SHIFT;
+ new_value += a->start;
+
+ return new_value;
+}
+
+/**
+ * Calculate the current value of an animation slowing down the start phase
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_ease_in(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint32_t t;
+ if(a->time == a->act_time) t = 1024;
+ else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
+
+ int32_t step = lv_bezier3(t, 0, 1, 1, 1024);
+
+ int32_t new_value;
+ new_value = (int32_t) step * (a->end - a->start);
+ new_value = new_value >> 10;
+ new_value += a->start;
+
+
+ return new_value;
+}
+
+/**
+ * Calculate the current value of an animation slowing down the end phase
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_ease_out(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+
+ uint32_t t;
+ if(a->time == a->act_time) t = 1024;
+ else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
+
+ int32_t step = lv_bezier3(t, 0, 1023, 1023, 1024);
+
+ int32_t new_value;
+ new_value = (int32_t) step * (a->end - a->start);
+ new_value = new_value >> 10;
+ new_value += a->start;
+
+
+ return new_value;
+}
+
+/**
+ * Calculate the current value of an animation applying an "S" characteristic (cosine)
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_ease_in_out(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+
+ uint32_t t;
+ if(a->time == a->act_time) t = 1024;
+ else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
+
+ int32_t step = lv_bezier3(t, 0, 100, 924, 1024);
+
+ int32_t new_value;
+ new_value = (int32_t) step * (a->end - a->start);
+ new_value = new_value >> 10;
+ new_value += a->start;
+
+
+ return new_value;
+}
+
+/**
+ * Calculate the current value of an animation with overshoot at the end
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_overshoot(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+
+ uint32_t t;
+ if(a->time == a->act_time) t = 1024;
+ else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
+
+ int32_t step = lv_bezier3(t, 0, 600, 1300, 1024);
+
+ int32_t new_value;
+ new_value = (int32_t) step * (a->end - a->start);
+ new_value = new_value >> 10;
+ new_value += a->start;
+
+
+ return new_value;
+}
+
+/**
+ * Calculate the current value of an animation with 3 bounces
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_bounce(const lv_anim_t * a)
+{
+ /*Calculate the current step*/
+ uint32_t t;
+ if(a->time == a->act_time) t = 1024;
+ else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
+
+ int32_t diff = (a->end - a->start);
+
+ /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/
+
+ if(t < 408){
+ /*Go down*/
+ t = (t * 2500) >> 10; /*[0..1024] range*/
+ }
+ else if(t >= 408 && t < 614) {
+ /*First bounce back*/
+ t -= 408;
+ t = t * 5; /*to [0..1024] range*/
+ t = 1024 - t;
+ diff = diff / 6;
+ }
+ else if(t >= 614 && t < 819) {
+ /*Fall back*/
+ t -= 614;
+ t = t * 5; /*to [0..1024] range*/
+ diff = diff / 6;
+ }
+ else if(t >= 819 && t < 921) {
+ /*Second bounce back*/
+ t -= 819;
+ t = t * 10; /*to [0..1024] range*/
+ t = 1024 - t;
+ diff = diff / 16;
+ }
+ else if(t >= 921 && t <= 1024) {
+ /*Fall back*/
+ t -= 921;
+ t = t * 10; /*to [0..1024] range*/
+ diff = diff / 16;
+ }
+
+ if(t > 1024) t = 1024;
+
+ int32_t step = lv_bezier3(t, 1024, 1024, 800, 0);
+
+ int32_t new_value;
+
+ new_value = (int32_t) step * diff;
+ new_value = new_value >> 10;
+ new_value = a->end - new_value;
+
+
+ return new_value;
+}
+
+/**
+ * Calculate the current value of an animation applying step characteristic.
+ * (Set end value on the end of the animation)
+ * @param a pointer to an animation
+ * @return the current value to set
+ */
+int32_t lv_anim_path_step(const lv_anim_t * a)
+{
+ if(a->act_time >= a->time) return a->end;
+ else return a->start;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Periodically handle the animations.
+ * @param param unused
+ */
+static void anim_task(void * param)
+{
+ (void)param;
+
+ lv_anim_t * a;
+ LL_READ(LV_GC_ROOT(_lv_anim_ll), a) {
+ a->has_run = 0;
+ }
+
+ uint32_t elaps = lv_tick_elaps(last_task_run);
+ a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
+
+ while(a != NULL) {
+ /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete happened in `anim_ready_handler`
+ * which could make this linked list reading corrupt because the list is changed meanwhile
+ */
+ anim_list_changed = false;
+
+ if(!a->has_run) {
+ a->has_run = 1; /*The list readying might be reseted so need to know which anim has run already*/
+ a->act_time += elaps;
+ if(a->act_time >= 0) {
+ if(a->act_time > a->time) a->act_time = a->time;
+
+ int32_t new_value;
+ new_value = a->path(a);
+
+ if(a->fp != NULL) a->fp(a->var, new_value); /*Apply the calculated value*/
+
+ /*If the time is elapsed the animation is ready*/
+ if(a->act_time >= a->time) {
+ anim_ready_handler(a);
+ }
+ }
+ }
+
+ /* If the linked list changed due to anim. delete then it's not safe to continue
+ * the reading of the list from here -> start from the head*/
+ if(anim_list_changed) a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
+ else a = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
+ }
+
+ last_task_run = lv_tick_get();
+}
+
+/**
+ * Called when an animation is ready to do the necessary thinks
+ * e.g. repeat, play back, delete etc.
+ * @param a pointer to an animation descriptor
+ * @return true: animation delete occurred nnd the `LV_GC_ROOT(_lv_anim_ll)` has changed
+ * */
+static bool anim_ready_handler(lv_anim_t * a)
+{
+
+ /*Delete the animation if
+ * - no repeat and no play back (simple one shot animation)
+ * - no repeat, play back is enabled and play back is ready */
+ if((a->repeat == 0 && a->playback == 0) ||
+ (a->repeat == 0 && a->playback == 1 && a->playback_now == 1)) {
+ void (*cb)(void *) = a->end_cb;
+ void * p = a->var;
+ lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);
+ lv_mem_free(a);
+ anim_list_changed = true;
+
+ /* Call the callback function at the end*/
+ /* Check if an animation is deleted in the cb function
+ * if yes then the caller function has to know this*/
+ if(cb != NULL) cb(p);
+ }
+ /*If the animation is not deleted then restart it*/
+ else {
+ a->act_time = - a->repeat_pause; /*Restart the animation*/
+ /*Swap the start and end values in play back mode*/
+ if(a->playback != 0) {
+ /*If now turning back use the 'playback_pause*/
+ if(a->playback_now == 0) a->act_time = - a->playback_pause;
+
+ /*Toggle the play back state*/
+ a->playback_now = a->playback_now == 0 ? 1 : 0;
+ /*Swap the start and end values*/
+ int32_t tmp;
+ tmp = a->start;
+ a->start = a->end;
+ a->end = tmp;
+ }
+ }
+
+ return anim_list_changed;
+}
+#endif
diff --git a/bdk/libs/lvgl/lv_misc/lv_anim.h b/bdk/libs/lvgl/lv_misc/lv_anim.h
new file mode 100644
index 0000000..6625ae2
--- /dev/null
+++ b/bdk/libs/lvgl/lv_misc/lv_anim.h
@@ -0,0 +1,176 @@
+/**
+ * @file anim.h
+ *
+ */
+
+#ifndef ANIM_H
+#define ANIM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*********************
+ * INCLUDES
+ *********************/
+#ifdef LV_CONF_INCLUDE_SIMPLE
+#include "lv_conf.h"
+#else
+#include "../../lv_conf.h"
+#endif
+
+#if USE_LV_ANIMATION
+
+#include