summaryrefslogtreecommitdiff
path: root/fw/fe310
diff options
context:
space:
mode:
Diffstat (limited to 'fw/fe310')
-rw-r--r--fw/fe310/Makefile25
-rw-r--r--fw/fe310/bsp/default.lds153
-rw-r--r--fw/fe310/bsp/drivers/Makefile10
-rw-r--r--fw/fe310/bsp/drivers/plic_driver.c127
-rw-r--r--fw/fe310/bsp/drivers/plic_driver.h51
-rw-r--r--fw/fe310/bsp/drivers/prci_driver.c265
-rw-r--r--fw/fe310/bsp/drivers/prci_driver.h80
-rw-r--r--fw/fe310/bsp/gloss/Makefile17
-rw-r--r--fw/fe310/bsp/gloss/crt0.S246
-rw-r--r--fw/fe310/bsp/gloss/sys_access.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_chdir.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_chmod.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_chown.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_close.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_execve.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_exit.c7
-rw-r--r--fw/fe310/bsp/gloss/sys_faccessat.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_fork.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_fstat.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_fstatat.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_ftime.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_getcwd.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_getpid.c7
-rw-r--r--fw/fe310/bsp/gloss/sys_isatty.c7
-rw-r--r--fw/fe310/bsp/gloss/sys_kill.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_link.c7
-rw-r--r--fw/fe310/bsp/gloss/sys_lseek.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_lstat.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_open.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_openat.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_read.c23
-rw-r--r--fw/fe310/bsp/gloss/sys_sbrk.c38
-rw-r--r--fw/fe310/bsp/gloss/sys_stat.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_sysconf.c16
-rw-r--r--fw/fe310/bsp/gloss/sys_unlink.c8
-rw-r--r--fw/fe310/bsp/gloss/sys_utime.c9
-rw-r--r--fw/fe310/bsp/gloss/sys_wait.c7
-rw-r--r--fw/fe310/bsp/gloss/sys_write.c24
-rw-r--r--fw/fe310/bsp/include/encoding.h1313
-rw-r--r--fw/fe310/bsp/include/platform.h140
-rw-r--r--fw/fe310/bsp/include/sifive/bits.h36
-rw-r--r--fw/fe310/bsp/include/sifive/const.h18
-rw-r--r--fw/fe310/bsp/include/sifive/devices/aon.h89
-rw-r--r--fw/fe310/bsp/include/sifive/devices/clint.h14
-rw-r--r--fw/fe310/bsp/include/sifive/devices/gpio.h24
-rw-r--r--fw/fe310/bsp/include/sifive/devices/i2c.h34
-rw-r--r--fw/fe310/bsp/include/sifive/devices/otp.h23
-rw-r--r--fw/fe310/bsp/include/sifive/devices/plic.h31
-rw-r--r--fw/fe310/bsp/include/sifive/devices/prci.h56
-rw-r--r--fw/fe310/bsp/include/sifive/devices/pwm.h37
-rw-r--r--fw/fe310/bsp/include/sifive/devices/spi.h80
-rw-r--r--fw/fe310/bsp/include/sifive/devices/uart.h27
-rw-r--r--fw/fe310/bsp/metal/Makefile13
-rw-r--r--fw/fe310/bsp/metal/entry.S106
-rw-r--r--fw/fe310/bsp/metal/metal.c34
-rw-r--r--fw/fe310/bsp/openocd.cfg34
-rwxr-xr-xfw/fe310/bsp/upload47
-rw-r--r--fw/fe310/common.mk5
-rw-r--r--fw/fe310/crypto/Makefile17
-rw-r--r--fw/fe310/eos/LICENSE339
-rw-r--r--fw/fe310/eos/Makefile19
-rw-r--r--fw/fe310/eos/app/Makefile17
-rw-r--r--fw/fe310/eos/app/app_root.c88
-rw-r--r--fw/fe310/eos/app/app_root.h12
-rw-r--r--fw/fe310/eos/app/app_status.c17
-rw-r--r--fw/fe310/eos/app/app_status.h4
-rw-r--r--fw/fe310/eos/board.h42
-rw-r--r--fw/fe310/eos/cam.c138
-rw-r--r--fw/fe310/eos/cam.h88
-rw-r--r--fw/fe310/eos/cell.c51
-rw-r--r--fw/fe310/eos/cell.h51
-rw-r--r--fw/fe310/eos/eos.c52
-rw-r--r--fw/fe310/eos/eos.h12
-rw-r--r--fw/fe310/eos/eve/Makefile17
-rw-r--r--fw/fe310/eos/eve/clipb.c19
-rw-r--r--fw/fe310/eos/eve/clipb.h6
-rw-r--r--fw/fe310/eos/eve/eve.c476
-rw-r--r--fw/fe310/eos/eve/eve.h70
-rwxr-xr-xfw/fe310/eos/eve/eve_config.h39
-rwxr-xr-xfw/fe310/eos/eve/eve_def.h872
-rw-r--r--fw/fe310/eos/eve/eve_font.c59
-rw-r--r--fw/fe310/eos/eve/eve_font.h16
-rw-r--r--fw/fe310/eos/eve/eve_kbd.c162
-rw-r--r--fw/fe310/eos/eve/eve_kbd.h23
-rw-r--r--fw/fe310/eos/eve/eve_phy.c77
-rw-r--r--fw/fe310/eos/eve/eve_phy.h28
-rw-r--r--fw/fe310/eos/eve/eve_platform.c80
-rw-r--r--fw/fe310/eos/eve/eve_platform.h39
-rw-r--r--fw/fe310/eos/eve/eve_text.c209
-rw-r--r--fw/fe310/eos/eve/eve_text.h32
-rw-r--r--fw/fe310/eos/eve/eve_touch.c419
-rw-r--r--fw/fe310/eos/eve/eve_touch.h121
-rw-r--r--fw/fe310/eos/eve/eve_vtrack.c65
-rw-r--r--fw/fe310/eos/eve/eve_vtrack.h28
-rw-r--r--fw/fe310/eos/eve/screen/Makefile17
-rw-r--r--fw/fe310/eos/eve/screen/form.c165
-rw-r--r--fw/fe310/eos/eve/screen/form.h23
-rw-r--r--fw/fe310/eos/eve/screen/page.c349
-rw-r--r--fw/fe310/eos/eve/screen/page.h56
-rw-r--r--fw/fe310/eos/eve/screen/uievt.h14
-rw-r--r--fw/fe310/eos/eve/screen/view.c94
-rw-r--r--fw/fe310/eos/eve/screen/view.h42
-rw-r--r--fw/fe310/eos/eve/screen/window.c300
-rw-r--r--fw/fe310/eos/eve/screen/window.h60
-rw-r--r--fw/fe310/eos/eve/widget/Makefile17
-rw-r--r--fw/fe310/eos/eve/widget/freew.c54
-rw-r--r--fw/fe310/eos/eve/widget/freew.h25
-rw-r--r--fw/fe310/eos/eve/widget/label.c23
-rw-r--r--fw/fe310/eos/eve/widget/label.h10
-rw-r--r--fw/fe310/eos/eve/widget/pagew.c59
-rw-r--r--fw/fe310/eos/eve/widget/pagew.h20
-rw-r--r--fw/fe310/eos/eve/widget/selectw.c248
-rw-r--r--fw/fe310/eos/eve/widget/selectw.h30
-rw-r--r--fw/fe310/eos/eve/widget/spacerw.c36
-rw-r--r--fw/fe310/eos/eve/widget/spacerw.h14
-rw-r--r--fw/fe310/eos/eve/widget/strw.c446
-rw-r--r--fw/fe310/eos/eve/widget/strw.h43
-rw-r--r--fw/fe310/eos/eve/widget/textw.c545
-rw-r--r--fw/fe310/eos/eve/widget/textw.h46
-rw-r--r--fw/fe310/eos/eve/widget/widget.c77
-rw-r--r--fw/fe310/eos/eve/widget/widget.h31
-rw-r--r--fw/fe310/eos/eve/widget/widgets.h39
-rw-r--r--fw/fe310/eos/event.c155
-rw-r--r--fw/fe310/eos/event.h21
-rw-r--r--fw/fe310/eos/evt_def.h12
-rw-r--r--fw/fe310/eos/i2c.c99
-rw-r--r--fw/fe310/eos/i2c.h9
-rw-r--r--fw/fe310/eos/i2c/Makefile17
-rw-r--r--fw/fe310/eos/i2c/bq25895.c27
-rw-r--r--fw/fe310/eos/i2c/bq25895.h5
-rw-r--r--fw/fe310/eos/i2c/drv2605.h3
-rw-r--r--fw/fe310/eos/i2c/lsm9ds1.h4
-rw-r--r--fw/fe310/eos/i2c/ov2640.c861
-rw-r--r--fw/fe310/eos/i2c/ov2640.h8
-rw-r--r--fw/fe310/eos/i2c/ov2640_regs.h245
-rw-r--r--fw/fe310/eos/i2s.c408
-rw-r--r--fw/fe310/eos/i2s.h34
-rw-r--r--fw/fe310/eos/i2s_def.h5
-rw-r--r--fw/fe310/eos/i2s_priv.h8
-rw-r--r--fw/fe310/eos/interrupt.c72
-rw-r--r--fw/fe310/eos/interrupt.h13
-rw-r--r--fw/fe310/eos/irq_def.h11
-rw-r--r--fw/fe310/eos/msgq.c122
-rw-r--r--fw/fe310/eos/msgq.h32
-rw-r--r--fw/fe310/eos/msgq_priv.h10
-rw-r--r--fw/fe310/eos/net.c568
-rw-r--r--fw/fe310/eos/net.h39
-rw-r--r--fw/fe310/eos/power.c119
-rw-r--r--fw/fe310/eos/power.h23
-rw-r--r--fw/fe310/eos/sdc_crypto.c51
-rw-r--r--fw/fe310/eos/sdc_crypto.h18
-rw-r--r--fw/fe310/eos/sdcard.c541
-rw-r--r--fw/fe310/eos/sdcard.h22
-rw-r--r--fw/fe310/eos/sock.c103
-rw-r--r--fw/fe310/eos/sock.h26
-rw-r--r--fw/fe310/eos/spi.c338
-rw-r--r--fw/fe310/eos/spi.h28
-rw-r--r--fw/fe310/eos/spi_cfg.h35
-rw-r--r--fw/fe310/eos/spi_dev.c93
-rw-r--r--fw/fe310/eos/spi_dev.h21
-rw-r--r--fw/fe310/eos/spi_priv.h8
-rw-r--r--fw/fe310/eos/timer.c135
-rw-r--r--fw/fe310/eos/timer.h23
-rw-r--r--fw/fe310/eos/trap_entry.S517
-rw-r--r--fw/fe310/eos/uart.c92
-rw-r--r--fw/fe310/eos/uart.h18
-rw-r--r--fw/fe310/eos/unicode.c195
-rw-r--r--fw/fe310/eos/unicode.h19
-rw-r--r--fw/fe310/eos/wifi.c45
-rw-r--r--fw/fe310/eos/wifi.h13
-rw-r--r--fw/fe310/fsfat/Makefile17
-rw-r--r--fw/fe310/fsfat/diskio.c141
-rw-r--r--fw/fe310/fsfat/diskio.h77
-rw-r--r--fw/fe310/fsfat/ff.c6982
-rw-r--r--fw/fe310/fsfat/ff.h422
-rw-r--r--fw/fe310/fsfat/ffconf.h301
-rw-r--r--fw/fe310/test/Makefile24
-rw-r--r--fw/fe310/test/cam.c166
-rw-r--r--fw/fe310/test/cam.h2
-rw-r--r--fw/fe310/test/cell_dev.c24
-rw-r--r--fw/fe310/test/cell_dev.h1
-rw-r--r--fw/fe310/test/cell_pdp.c108
-rw-r--r--fw/fe310/test/cell_pdp.h4
-rw-r--r--fw/fe310/test/fs.c104
-rw-r--r--fw/fe310/test/fs.h3
-rw-r--r--fw/fe310/test/main.c93
-rw-r--r--fw/fe310/test/modem.c168
-rw-r--r--fw/fe310/test/modem.h2
-rw-r--r--fw/fe310/test/phone.c122
-rw-r--r--fw/fe310/test/phone.h9
-rw-r--r--fw/fe310/test/status.c81
-rw-r--r--fw/fe310/test/status.h2
-rw-r--r--fw/fe310/test/test.c62
-rw-r--r--fw/fe310/test/test.h2
-rw-r--r--fw/fe310/test/wifi.c119
-rw-r--r--fw/fe310/test/wifi.h4
196 files changed, 24437 insertions, 0 deletions
diff --git a/fw/fe310/Makefile b/fw/fe310/Makefile
new file mode 100644
index 0000000..96687c7
--- /dev/null
+++ b/fw/fe310/Makefile
@@ -0,0 +1,25 @@
+include common.mk
+
+CFLAGS += -I./include
+
+subdirs = bsp/drivers bsp/gloss bsp/metal eos eos/i2c eos/eve eos/eve/screen eos/eve/widget eos/app crypto fsfat
+
+all: libeos.a
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
+
+libeos.a:
+ for i in $(subdirs); do \
+ (cd $$i && $(MAKE)) || exit; \
+ done
+ rm -f $@
+ for i in $(subdirs); do \
+ $(AR) rcs $@ $$i/*.o; \
+ done
+
+clean:
+ for i in $(subdirs); do \
+ (cd $$i && $(MAKE) clean) || exit; \
+ done
+ rm -f *.o *.a
diff --git a/fw/fe310/bsp/default.lds b/fw/fe310/bsp/default.lds
new file mode 100644
index 0000000..cabcf01
--- /dev/null
+++ b/fw/fe310/bsp/default.lds
@@ -0,0 +1,153 @@
+OUTPUT_ARCH("riscv")
+
+ENTRY(_enter)
+
+MEMORY
+{
+ flash (rxai!w) : ORIGIN = 0x20000000, LENGTH = 0x6a120
+ itim (wx!rai) : ORIGIN = 0x8000000, LENGTH = 0x2000
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 0x4000
+}
+
+PHDRS
+{
+ flash PT_LOAD;
+ ram PT_LOAD;
+ ram_init PT_LOAD;
+ itim PT_LOAD;
+ itim_init PT_LOAD;
+}
+
+SECTIONS
+{
+
+ __stack_size = 0x1000;
+ PROVIDE(__metal_boot_hart = 0);
+ PROVIDE(__metal_chicken_bit = 0);
+
+ .init : {
+ KEEP (*(.text.metal.init.enter))
+ KEEP (*(.text.metal.init.*))
+ KEEP (*(SORT_NONE(.init)))
+ KEEP (*(.text.libgloss.start))
+ } >flash AT>flash :flash
+
+ .fini : {
+ KEEP (*(SORT_NONE(.fini)))
+ } >flash AT>flash :flash
+
+ .text : {
+ *(.text.unlikely .text.unlikely.*)
+ *(.text.startup .text.startup.*)
+ *(.text .text.*)
+ *(.gnu.linkonce.t.*)
+ } >flash AT>flash :flash
+
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+
+ .rodata : {
+ *(.rdata)
+ *(.rodata .rodata.*)
+ *(.gnu.linkonce.r.*)
+ . = ALIGN(8);
+ *(.srodata.cst16)
+ *(.srodata.cst8)
+ *(.srodata.cst4)
+ *(.srodata.cst2)
+ *(.srodata .srodata.*)
+ } >flash AT>flash :flash
+
+ . = ALIGN(8);
+
+ .preinit_array : {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ } >flash AT>flash :flash
+
+ .init_array : {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+ KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ } >flash AT>flash :flash
+
+ .fini_array : {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+ KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ } >flash AT>flash :flash
+
+ .ctors : {
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*crtbegin?.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ } >flash AT>flash :flash
+
+ .dtors : {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ } >flash AT>flash :flash
+
+ .itim : ALIGN(8) {
+ *(.itim .itim.*)
+ } >itim AT>flash :itim_init
+
+ PROVIDE( metal_segment_itim_source_start = LOADADDR(.itim) );
+ PROVIDE( metal_segment_itim_target_start = ADDR(.itim) );
+ PROVIDE( metal_segment_itim_target_end = ADDR(.itim) + SIZEOF(.itim) );
+
+ .data : ALIGN(8) {
+ *(.data .data.*)
+ *(.gnu.linkonce.d.*)
+ . = ALIGN(8);
+ PROVIDE( __global_pointer$ = . + 0x800 );
+ *(.sdata .sdata.* .sdata2.*)
+ *(.gnu.linkonce.s.*)
+ } >itim AT>flash :itim_init
+/* >ram AT>flash :ram_init */
+
+ PROVIDE( metal_segment_data_source_start = LOADADDR(.data) );
+ PROVIDE( metal_segment_data_target_start = ADDR(.data) );
+ PROVIDE( metal_segment_data_target_end = ADDR(.data) + SIZEOF(.data) );
+
+ .bss : ALIGN(8) {
+ *(.ibss .ibss.*)
+ *(.sbss*)
+ *(.gnu.linkonce.sb.*)
+ *(.bss .bss.*)
+ *(.gnu.linkonce.b.*)
+ *(COMMON)
+ } >itim AT>ram :itim
+/* >ram AT>ram :ram */
+
+ PROVIDE( metal_segment_bss_target_start = ADDR(.bss) );
+ PROVIDE( metal_segment_bss_target_end = ADDR(.bss) + SIZEOF(.bss) );
+
+ __heap_size = 0x4000 - __stack_size;
+ .heap : {
+ PROVIDE( metal_segment_heap_target_start = . );
+ . += __heap_size;
+ PROVIDE( metal_segment_heap_target_end = . );
+ PROVIDE( _heap_end = . );
+ } >ram AT>ram :ram
+
+ PROVIDE(__stack_size = __stack_size);
+
+ .stack : {
+ PROVIDE(metal_segment_stack_begin = .);
+ . += __stack_size;
+ PROVIDE(metal_segment_stack_end = .);
+ PROVIDE( _sp = . );
+ } >ram AT>ram :ram
+
+}
+
diff --git a/fw/fe310/bsp/drivers/Makefile b/fw/fe310/bsp/drivers/Makefile
new file mode 100644
index 0000000..14fed05
--- /dev/null
+++ b/fw/fe310/bsp/drivers/Makefile
@@ -0,0 +1,10 @@
+include ../../common.mk
+CFLAGS += -I../include -include sys/cdefs.h
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
+
+all: plic_driver.o prci_driver.o
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/bsp/drivers/plic_driver.c b/fw/fe310/bsp/drivers/plic_driver.c
new file mode 100644
index 0000000..27b9d2c
--- /dev/null
+++ b/fw/fe310/bsp/drivers/plic_driver.c
@@ -0,0 +1,127 @@
+// See LICENSE for license details.
+
+#include "sifive/devices/plic.h"
+#include "plic_driver.h"
+#include "platform.h"
+#include "encoding.h"
+#include <string.h>
+
+
+// Note that there are no assertions or bounds checking on these
+// parameter values.
+
+void volatile_memzero(uint8_t * base, unsigned int size)
+{
+ volatile uint8_t * ptr;
+ for (ptr = base; ptr < (base + size); ptr++){
+ *ptr = 0;
+ }
+}
+
+void PLIC_init (
+ plic_instance_t * this_plic,
+ uintptr_t base_addr,
+ uint32_t num_sources,
+ uint32_t num_priorities
+ )
+{
+
+ this_plic->base_addr = base_addr;
+ this_plic->num_sources = num_sources;
+ this_plic->num_priorities = num_priorities;
+
+ // Disable all interrupts (don't assume that these registers are reset).
+ unsigned long hart_id = read_csr(mhartid);
+ volatile_memzero((uint8_t*) (this_plic->base_addr +
+ PLIC_ENABLE_OFFSET +
+ (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET)),
+ (num_sources + 8) / 8);
+
+ // Set all priorities to 0 (equal priority -- don't assume that these are reset).
+ volatile_memzero ((uint8_t *)(this_plic->base_addr +
+ PLIC_PRIORITY_OFFSET),
+ (num_sources + 1) << PLIC_PRIORITY_SHIFT_PER_SOURCE);
+
+ // Set the threshold to 0.
+ volatile plic_threshold* threshold = (plic_threshold*)
+ (this_plic->base_addr +
+ PLIC_THRESHOLD_OFFSET +
+ (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET));
+
+ *threshold = 0;
+
+}
+
+void PLIC_set_threshold (plic_instance_t * this_plic,
+ plic_threshold threshold){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile plic_threshold* threshold_ptr = (plic_threshold*) (this_plic->base_addr +
+ PLIC_THRESHOLD_OFFSET +
+ (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET));
+
+ *threshold_ptr = threshold;
+
+}
+
+
+void PLIC_enable_interrupt (plic_instance_t * this_plic, plic_source source){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile uint8_t * current_ptr = (volatile uint8_t *)(this_plic->base_addr +
+ PLIC_ENABLE_OFFSET +
+ (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) +
+ (source >> 3));
+ uint8_t current = *current_ptr;
+ current = current | ( 1 << (source & 0x7));
+ *current_ptr = current;
+
+}
+
+void PLIC_disable_interrupt (plic_instance_t * this_plic, plic_source source){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile uint8_t * current_ptr = (volatile uint8_t *) (this_plic->base_addr +
+ PLIC_ENABLE_OFFSET +
+ (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) +
+ (source >> 3));
+ uint8_t current = *current_ptr;
+ current = current & ~(( 1 << (source & 0x7)));
+ *current_ptr = current;
+
+}
+
+void PLIC_set_priority (plic_instance_t * this_plic, plic_source source, plic_priority priority){
+
+ if (this_plic->num_priorities > 0) {
+ volatile plic_priority * priority_ptr = (volatile plic_priority *)
+ (this_plic->base_addr +
+ PLIC_PRIORITY_OFFSET +
+ (source << PLIC_PRIORITY_SHIFT_PER_SOURCE));
+ *priority_ptr = priority;
+ }
+}
+
+plic_source PLIC_claim_interrupt(plic_instance_t * this_plic){
+
+ unsigned long hart_id = read_csr(mhartid);
+
+ volatile plic_source * claim_addr = (volatile plic_source * )
+ (this_plic->base_addr +
+ PLIC_CLAIM_OFFSET +
+ (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET));
+
+ return *claim_addr;
+
+}
+
+void PLIC_complete_interrupt(plic_instance_t * this_plic, plic_source source){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile plic_source * claim_addr = (volatile plic_source *) (this_plic->base_addr +
+ PLIC_CLAIM_OFFSET +
+ (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET));
+ *claim_addr = source;
+
+}
+
diff --git a/fw/fe310/bsp/drivers/plic_driver.h b/fw/fe310/bsp/drivers/plic_driver.h
new file mode 100644
index 0000000..98b2bdd
--- /dev/null
+++ b/fw/fe310/bsp/drivers/plic_driver.h
@@ -0,0 +1,51 @@
+// See LICENSE file for licence details
+
+#ifndef _PLIC_DRIVER_H_
+#define _PLIC_DRIVER_H_
+
+
+__BEGIN_DECLS
+
+#include "platform.h"
+
+typedef struct __plic_instance_t
+{
+ uintptr_t base_addr;
+
+ uint32_t num_sources;
+ uint32_t num_priorities;
+
+} plic_instance_t;
+
+typedef uint32_t plic_source;
+typedef uint32_t plic_priority;
+typedef uint32_t plic_threshold;
+
+void PLIC_init (
+ plic_instance_t * this_plic,
+ uintptr_t base_addr,
+ uint32_t num_sources,
+ uint32_t num_priorities
+ );
+
+void PLIC_set_threshold (plic_instance_t * this_plic,
+ plic_threshold threshold);
+
+void PLIC_enable_interrupt (plic_instance_t * this_plic,
+ plic_source source);
+
+void PLIC_disable_interrupt (plic_instance_t * this_plic,
+ plic_source source);
+
+void PLIC_set_priority (plic_instance_t * this_plic,
+ plic_source source,
+ plic_priority priority);
+
+plic_source PLIC_claim_interrupt(plic_instance_t * this_plic);
+
+void PLIC_complete_interrupt(plic_instance_t * this_plic,
+ plic_source source);
+
+__END_DECLS
+
+#endif
diff --git a/fw/fe310/bsp/drivers/prci_driver.c b/fw/fe310/bsp/drivers/prci_driver.c
new file mode 100644
index 0000000..c2df2c9
--- /dev/null
+++ b/fw/fe310/bsp/drivers/prci_driver.c
@@ -0,0 +1,265 @@
+// See LICENSE file for license details
+
+#include "platform.h"
+
+#ifdef PRCI_CTRL_ADDR
+#include "prci_driver.h"
+#include <unistd.h>
+
+#define rdmcycle(x) { \
+ uint32_t lo, hi, hi2; \
+ __asm__ __volatile__ ("1:\n\t" \
+ "csrr %0, mcycleh\n\t" \
+ "csrr %1, mcycle\n\t" \
+ "csrr %2, mcycleh\n\t" \
+ "bne %0, %2, 1b\n\t" \
+ : "=r" (hi), "=r" (lo), "=r" (hi2)) ; \
+ *(x) = lo | ((uint64_t) hi << 32); \
+ }
+
+uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq)
+{
+
+ uint32_t start_mtime = CLINT_REG(CLINT_MTIME);
+ uint32_t end_mtime = start_mtime + mtime_ticks + 1;
+
+ // Make sure we won't get rollover.
+ while (end_mtime < start_mtime) {
+ start_mtime = CLINT_REG(CLINT_MTIME);
+ end_mtime = start_mtime + mtime_ticks + 1;
+ }
+
+ // Don't start measuring until mtime edge.
+ uint32_t tmp = start_mtime;
+ do {
+ start_mtime = CLINT_REG(CLINT_MTIME);
+ } while (start_mtime == tmp);
+
+ uint64_t start_mcycle;
+ rdmcycle(&start_mcycle);
+
+ while (CLINT_REG(CLINT_MTIME) < end_mtime) ;
+
+ uint64_t end_mcycle;
+ rdmcycle(&end_mcycle);
+ uint32_t difference = (uint32_t) (end_mcycle - start_mcycle);
+
+ uint64_t freq = ((uint64_t) difference * mtime_freq) / mtime_ticks;
+ return (uint32_t) freq & 0xFFFFFFFF;
+
+}
+
+unsigned long PRCI_get_cpu_freq()
+{
+ static uint32_t cpu_freq;
+
+ if (!cpu_freq) {
+ // warm up I$
+ PRCI_measure_mcycle_freq(3000, RTC_FREQ);
+ // measure for real
+ cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
+ }
+
+ return cpu_freq;
+}
+
+void PRCI_use_hfrosc(int div, int trim)
+{
+ // Make sure the HFROSC is running at its default setting
+ // It is OK to change this even if we are running off of it.
+
+ PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(div) | ROSC_TRIM(trim) | ROSC_EN(1));
+
+ while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0);
+
+ PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(1);
+}
+
+void PRCI_use_pll(int refsel, int bypass,
+ int r, int f, int q, int finaldiv,
+ int hfroscdiv, int hfrosctrim)
+{
+ // Ensure that we aren't running off the PLL before we mess with it.
+ if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) {
+ // Make sure the HFROSC is running at its default setting
+ PRCI_use_hfrosc(4, 16);
+ }
+
+ // Set PLL Source to be HFXOSC if desired.
+ uint32_t config_value = 0;
+
+ config_value |= PLL_REFSEL(refsel);
+
+ if (bypass) {
+ // Bypass
+ config_value |= PLL_BYPASS(1);
+
+ PRCI_REG(PRCI_PLLCFG) = config_value;
+
+ // If we don't have an HFXTAL, this doesn't really matter.
+ // Set our Final output divide to divide-by-1:
+ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0));
+ } else {
+
+ // To overclock, use the hfrosc
+ if (hfrosctrim >= 0 && hfroscdiv >= 0) {
+ PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
+ }
+
+ // Set DIV Settings for PLL
+
+ // (Legal values of f_REF are 6-48MHz)
+
+ // Set DIVR to divide-by-2 to get 8MHz frequency
+ // (legal values of f_R are 6-12 MHz)
+
+ config_value |= PLL_BYPASS(1);
+ config_value |= PLL_R(r);
+
+ // Set DIVF to get 512Mhz frequncy
+ // There is an implied multiply-by-2, 16Mhz.
+ // So need to write 32-1
+ // (legal values of f_F are 384-768 MHz)
+ config_value |= PLL_F(f);
+
+ // Set DIVQ to divide-by-2 to get 256 MHz frequency
+ // (legal values of f_Q are 50-400Mhz)
+ config_value |= PLL_Q(q);
+
+ // Set our Final output divide:
+ if (finaldiv < 0) {
+ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0));
+ } else {
+ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV(finaldiv));
+ }
+
+ PRCI_REG(PRCI_PLLCFG) = config_value;
+
+ // Un-Bypass the PLL.
+ PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1);
+
+ // Wait for PLL Lock
+ // Note that the Lock signal can be glitchy.
+ // Need to wait 100 us
+ // RTC is running at 32kHz.
+ // So wait 4 ticks of RTC.
+ uint32_t now = CLINT_REG(CLINT_MTIME);
+ while (CLINT_REG(CLINT_MTIME) - now < 4) ;
+
+ // Now it is safe to check for PLL Lock
+ while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0);
+
+ }
+
+ // Switch over to PLL Clock source
+ PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1);
+
+ // If we're running off HFXOSC, turn off the HFROSC to
+ // save power.
+ if (refsel) {
+ PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1);
+ }
+
+}
+
+void PRCI_use_default_clocks()
+{
+ // Turn off the LFROSC
+ AON_REG(AON_LFROSC) &= ~ROSC_EN(1);
+
+ // Use HFROSC
+ PRCI_use_hfrosc(4, 16);
+}
+
+void PRCI_use_hfxosc(uint32_t finaldiv)
+{
+
+ PRCI_use_pll(1, // Use HFXTAL
+ 1, // Bypass = 1
+ 0, // PLL settings don't matter
+ 0, // PLL settings don't matter
+ 0, // PLL settings don't matter
+ finaldiv,
+ -1,
+ -1);
+}
+
+// This is a generic function, which
+// doesn't span the entire range of HFROSC settings.
+// It only adjusts the trim, which can span a hundred MHz or so.
+// This function does not check the legality of the PLL settings
+// at all, and it is quite possible to configure invalid PLL settings
+// this way.
+// It returns the actual measured CPU frequency.
+
+uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target )
+{
+
+ uint32_t hfrosctrim = 0;
+ uint32_t hfroscdiv = 4;
+ uint32_t prev_trim = 0;
+
+ // In this function we use PLL settings which
+ // will give us a 32x multiplier from the output
+ // of the HFROSC source to the output of the
+ // PLL. We first measure our HFROSC to get the
+ // right trim, then finally use it as the PLL source.
+ // We should really check here that the f_cpu
+ // requested is something in the limit of the PLL. For
+ // now that is up to the user.
+
+ // This will undershoot for frequencies not divisible by 16.
+ uint32_t desired_hfrosc_freq = (f_cpu / 16);
+
+ PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
+
+ // Ignore the first run (for icache reasons)
+ uint32_t cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
+
+ cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
+ uint32_t prev_freq = cpu_freq;
+
+ while ((cpu_freq < desired_hfrosc_freq) && (hfrosctrim < 0x1F)){
+ prev_trim = hfrosctrim;
+ prev_freq = cpu_freq;
+ hfrosctrim ++;
+ PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
+ cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
+ }
+
+ // We couldn't go low enough
+ if (prev_freq > desired_hfrosc_freq) {
+ PRCI_use_pll(0, 0, 1, 31, 1, -1, hfroscdiv, prev_trim);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ return cpu_freq;
+ }
+
+ // We couldn't go high enough
+ if (cpu_freq < desired_hfrosc_freq) {
+ PRCI_use_pll(0, 0, 1, 31, 1, -1, hfroscdiv, prev_trim);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ return cpu_freq;
+ }
+
+ // Check for over/undershoot
+ switch(target) {
+ case(PRCI_FREQ_CLOSEST):
+ if ((desired_hfrosc_freq - prev_freq) < (cpu_freq - desired_hfrosc_freq)) {
+ PRCI_use_pll(0, 0, 1, 31, 1, -1, hfroscdiv, prev_trim);
+ } else {
+ PRCI_use_pll(0, 0, 1, 31, 1, -1, hfroscdiv, hfrosctrim);
+ }
+ break;
+ case(PRCI_FREQ_UNDERSHOOT):
+ PRCI_use_pll(0, 0, 1, 31, 1, -1, hfroscdiv, prev_trim);
+ break;
+ default:
+ PRCI_use_pll(0, 0, 1, 31, 1, -1, hfroscdiv, hfrosctrim);
+ }
+
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ return cpu_freq;
+
+}
+
+#endif
diff --git a/fw/fe310/bsp/drivers/prci_driver.h b/fw/fe310/bsp/drivers/prci_driver.h
new file mode 100644
index 0000000..14e1370
--- /dev/null
+++ b/fw/fe310/bsp/drivers/prci_driver.h
@@ -0,0 +1,80 @@
+// See LICENSE file for license details
+
+#ifndef _PRCI_DRIVER_H_
+#define _PRCI_DRIVER_H_
+
+__BEGIN_DECLS
+
+#include <unistd.h>
+
+typedef enum prci_freq_target {
+
+ PRCI_FREQ_OVERSHOOT,
+ PRCI_FREQ_CLOSEST,
+ PRCI_FREQ_UNDERSHOOT
+
+} PRCI_freq_target;
+
+/* Measure and return the approximate frequency of the
+ * CPU, as given by measuring the mcycle counter against
+ * the mtime ticks.
+ */
+uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq);
+unsigned long PRCI_get_cpu_freq();
+
+/* Safely switch over to the HFROSC using the given div
+ * and trim settings.
+ */
+void PRCI_use_hfrosc(int div, int trim);
+
+/* Safely switch over to the 16MHz HFXOSC,
+ * applying the finaldiv clock divider (1 is the lowest
+ * legal value).
+ */
+void PRCI_use_hfxosc(uint32_t finaldiv);
+
+/* Safely switch over to the PLL using the given
+ * settings.
+ *
+ * Note that not all combinations of the inputs are actually
+ * legal, and this function does not check for their
+ * legality ("safely" means that this function won't turn off
+ * or glitch the clock the CPU is actually running off, but
+ * doesn't protect against you making it too fast or slow.)
+ */
+
+void PRCI_use_pll(int refsel, int bypass,
+ int r, int f, int q, int finaldiv,
+ int hfroscdiv, int hfrosctrim);
+
+/* Use the default clocks configured at reset.
+ * This is ~16Mhz HFROSC and turns off the LFROSC
+ * (on the current FE310 Dev Platforms, an external LFROSC is
+ * used as it is more power efficient).
+ */
+void PRCI_use_default_clocks();
+
+/* This routine will adjust the HFROSC trim
+ * while using HFROSC as the clock source,
+ * measure the resulting frequency, then
+ * use it as the PLL clock source,
+ * in an attempt to get over, under, or close to the
+ * requested frequency. It returns the actual measured
+ * frequency.
+ *
+ * Note that the requested frequency must be within the
+ * range supported by the PLL so not all values are
+ * achievable with this function, and not all
+ * are guaranteed to actually work. The PLL
+ * is rated higher than the hardware.
+ *
+ * There is no check on the desired f_cpu frequency, it
+ * is up to the user to specify something reasonable.
+ */
+
+uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target);
+
+__END_DECLS
+
+#endif
+
diff --git a/fw/fe310/bsp/gloss/Makefile b/fw/fe310/bsp/gloss/Makefile
new file mode 100644
index 0000000..4376dce
--- /dev/null
+++ b/fw/fe310/bsp/gloss/Makefile
@@ -0,0 +1,17 @@
+include ../../common.mk
+CFLAGS += -I../include
+
+src = sys_access.c sys_chdir.c sys_chmod.c sys_chown.c sys_close.c sys_execve.c sys_exit.c sys_faccessat.c sys_fork.c sys_fstat.c sys_fstatat.c sys_ftime.c sys_getcwd.c sys_getpid.c \
+ sys_isatty.c sys_kill.c sys_link.c sys_lseek.c sys_lstat.c sys_open.c sys_openat.c sys_read.c sys_sbrk.c sys_stat.c sys_sysconf.c sys_unlink.c sys_utime.c sys_wait.c sys_write.c
+obj = crt0.o $(src:.c=.o)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/bsp/gloss/crt0.S b/fw/fe310/bsp/gloss/crt0.S
new file mode 100644
index 0000000..920ee4b
--- /dev/null
+++ b/fw/fe310/bsp/gloss/crt0.S
@@ -0,0 +1,246 @@
+/* Copyright (c) 2017-2018 SiFive Inc. All rights reserved.
+
+ This copyrighted material is made available to anyone wishing to use,
+ modify, copy, or redistribute it subject to the terms and conditions
+ of the FreeBSD License. This program is distributed in the hope that
+ it will be useful, but WITHOUT ANY WARRANTY expressed or implied,
+ including the implied warranties of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. A copy of this license is available at
+ http://www.opensource.org/licenses.
+*/
+
+/* crt0.S: Entry point for RISC-V METAL programs. */
+
+.section .text.libgloss.start
+.global _start
+.type _start, @function
+
+ /* _start is defined by the METAL to have been called with the following
+ * arguments:
+ * a0: the hart ID of the currently executing hart. Harts can start at
+ * any arbitrary point, it's the C library's job to ensure the code is
+ * safe.
+ * a1: a pointer to a description of the machine on which this code is
+ * currently executing. This is probably 0 on an embedded system
+ * because they tend to not be dynamically portable. As such, newlib
+ * ignores this argument.
+ * a2: a pointer to a function that must be run after the envirnoment has
+ * been initialized, but before user code can be expected to be run.
+ * If this is 0 then there is no function to be run. */
+_start:
+.cfi_startproc
+.cfi_undefined ra
+
+ /* This is a bit funky: it's not usually sane for _start to return, but in
+ * this case we actually want to in order to signal an error to the METAL. */
+ mv s0, ra
+
+ /* Before doing anything we must initialize the global pointer, as we cannot
+ * safely perform any access that may be relaxed without GP being set. This
+ * is done with relaxation disabled to avoid relaxing the address calculation
+ * to just "addi gp, gp, 0". */
+.option push
+.option norelax
+ la gp, __global_pointer$
+.option pop
+
+ /* The METAL is designed for a bare-metal environment and therefor is expected
+ * to define its own stack pointer. We also align the stack pointer here
+ * because the only RISC-V ABI that's currently defined mandates 16-byte
+ * stack alignment. */
+ la sp, _sp
+
+ /* Increment by hartid number of stack sizes */
+ li t0, 0
+ la t1, __stack_size
+1:
+ beq t0, a0, 1f
+ add sp, sp, t1
+ addi t0, t0, 1
+ j 1b
+1:
+ andi sp, sp, -16
+
+ /* If we're not hart 0, skip the initialization work */
+ la t0, __metal_boot_hart
+ bne a0, t0, _skip_init
+
+ /* Embedded systems frequently require relocating the data segment before C
+ * code can be run -- for example, the data segment may exist in flash upon
+ * boot and then need to get relocated into a non-persistant writable memory
+ * before C code can execute. If this is the case we do so here. This step
+ * is optional: if the METAL provides an environment in which this relocation
+ * is not necessary then it must simply set metal_segment_data_source_start to
+ * be equal to metal_segment_data_target_start. */
+ la t0, metal_segment_data_source_start
+ la t1, metal_segment_data_target_start
+ la t2, metal_segment_data_target_end
+
+ beq t0, t1, 2f
+ bge t1, t2, 2f
+
+1:
+#if __riscv_xlen == 32
+ lw a0, 0(t0)
+ addi t0, t0, 4
+ sw a0, 0(t1)
+ addi t1, t1, 4
+ blt t1, t2, 1b
+#else
+ ld a0, 0(t0)
+ addi t0, t0, 8
+ sd a0, 0(t1)
+ addi t1, t1, 8
+ blt t1, t2, 1b
+#endif
+2:
+
+ /* Copy the ITIM section */
+ la t0, metal_segment_itim_source_start
+ la t1, metal_segment_itim_target_start
+ la t2, metal_segment_itim_target_end
+
+ beq t0, t1, 2f
+ bge t1, t2, 2f
+
+1:
+#if __riscv_xlen == 32
+ lw a0, 0(t0)
+ addi t0, t0, 4
+ sw a0, 0(t1)
+ addi t1, t1, 4
+ blt t1, t2, 1b
+#else
+ ld a0, 0(t0)
+ addi t0, t0, 8
+ sd a0, 0(t1)
+ addi t1, t1, 8
+ blt t1, t2, 1b
+#endif
+2:
+
+ /* Fence all subsequent instruction fetches until after the ITIM writes
+ complete */
+ fence.i
+
+ /* Zero the BSS segment. */
+ la t1, metal_segment_bss_target_start
+ la t2, metal_segment_bss_target_end
+
+ bge t1, t2, 2f
+
+1:
+#if __riscv_xlen == 32
+ sw x0, 0(t1)
+ addi t1, t1, 4
+ blt t1, t2, 1b
+#else
+ sd x0, 0(t1)
+ addi t1, t1, 8
+ blt t1, t2, 1b
+#endif
+2:
+
+ /* At this point we're in an environment that can execute C code. The first
+ * thing to do is to make the callback to the parent environment if it's been
+ * requested to do so. */
+ beqz a2, 1f
+ jalr a2
+1:
+
+ /* The RISC-V port only uses new-style constructors and destructors. */
+ la a0, __libc_fini_array
+ call atexit
+ call __libc_init_array
+
+_skip_init:
+
+ /* Synchronize harts so that secondary harts wait until hart 0 finishes
+ initializing */
+ call __metal_synchronize_harts
+
+ /* Check RISC-V isa and enable FS bits if Floating Point architecture. */
+ csrr a5, misa
+ li a4, 0x10028
+ and a5, a5, a4
+ beqz a5, 1f
+ csrr a5, mstatus
+ lui a4, 0x2
+ or a5, a5, a4
+ csrw mstatus, a5
+ csrwi fcsr, 0
+1:
+
+ /* This is a C runtime, so main() is defined to have some arguments. Since
+ * there's nothing sane the METAL can pass we don't bother with that but
+ * instead just setup as close to a NOP as we can. */
+ li a0, 1 /* argc=1 */
+ la a1, argv /* argv = {"libgloss", NULL} */
+ la a2, envp /* envp = {NULL} */
+ call secondary_main
+
+ /* Call exit to handle libc's cleanup routines. Under normal contains this
+ * shouldn't even get called, but I'm still not using a tail call here
+ * because returning to the METAL is the right thing to do in pathological
+ * situations. */
+ call exit
+
+ /* And here's where we return. Again, it's a bit odd but the METAL defines
+ * this as a bad idea (ie, as opposed to leaving it undefined) and at this
+ * point it's really the only thing left to do. */
+ mv ra, s0
+ ret
+
+.cfi_endproc
+
+/* RISC-V systems always use __libc_{init,fini}_array, but for compatibility we
+ * define _{init,fini} to do nothing. */
+.global _init
+.type _init, @function
+.global _fini
+.type _fini, @function
+_init:
+_fini:
+ ret
+.size _init, .-_init
+.size _fini, .-_fini
+
+/* By default, secondary_main will cause secondary harts to spin forever.
+ * Users can redefine secondary_main themselves to run code on secondary harts */
+.weak secondary_main
+.global secondary_main
+.type secondary_main, @function
+
+secondary_main:
+ addi sp, sp, -16
+#if __riscv_xlen == 32
+ sw ra, 4(sp)
+#else
+ sd ra, 8(sp)
+#endif
+ csrr t0, mhartid
+ la t1, __metal_boot_hart
+ beq t0, t1, 2f
+1:
+ wfi
+ j 1b
+2:
+ call main
+#if __riscv_xlen == 32
+ lw ra, 4(sp)
+#else
+ ld ra, 8(sp)
+#endif
+ addi sp, sp, 16
+ ret
+
+/* This shim allows main() to be passed a set of arguments that can satisfy the
+ * requirements of the C API. */
+.section .rodata.libgloss.start
+argv:
+.dc.a name
+envp:
+.dc.a 0
+name:
+.asciz "libgloss"
+
diff --git a/fw/fe310/bsp/gloss/sys_access.c b/fw/fe310/bsp/gloss/sys_access.c
new file mode 100644
index 0000000..c0bc153
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_access.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_access(const char *file, int mode)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_chdir.c b/fw/fe310/bsp/gloss/sys_chdir.c
new file mode 100644
index 0000000..f33d26a
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_chdir.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_chdir(const char *path)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_chmod.c b/fw/fe310/bsp/gloss/sys_chmod.c
new file mode 100644
index 0000000..67412bf
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_chmod.c
@@ -0,0 +1,9 @@
+#include <errno.h>
+#include <sys/types.h>
+
+int
+_chmod(const char *path, mode_t mode)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_chown.c b/fw/fe310/bsp/gloss/sys_chown.c
new file mode 100644
index 0000000..302952e
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_chown.c
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <errno.h>
+
+int
+_chown(const char *path, uid_t owner, gid_t group)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_close.c b/fw/fe310/bsp/gloss/sys_close.c
new file mode 100644
index 0000000..26dd6a5
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_close.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_close(int file)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_execve.c b/fw/fe310/bsp/gloss/sys_execve.c
new file mode 100644
index 0000000..9ae9f7e
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_execve.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_execve(const char *name, char *const argv[], char *const env[])
+{
+ errno = ENOMEM;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_exit.c b/fw/fe310/bsp/gloss/sys_exit.c
new file mode 100644
index 0000000..1502b3e
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_exit.c
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+__attribute__ ((noreturn)) void
+_exit(int st) { while (1); }
+
+int
+atexit(void (*f)(void)) { return 0; }
diff --git a/fw/fe310/bsp/gloss/sys_faccessat.c b/fw/fe310/bsp/gloss/sys_faccessat.c
new file mode 100644
index 0000000..873d52c
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_faccessat.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_faccessat(int dirfd, const char *file, int mode, int flags)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_fork.c b/fw/fe310/bsp/gloss/sys_fork.c
new file mode 100644
index 0000000..64e6756
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_fork.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_fork()
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_fstat.c b/fw/fe310/bsp/gloss/sys_fstat.c
new file mode 100644
index 0000000..fedc289
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_fstat.c
@@ -0,0 +1,9 @@
+#include <errno.h>
+#include <sys/stat.h>
+
+int
+_fstat(int file, struct stat *st)
+{
+ errno = -ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_fstatat.c b/fw/fe310/bsp/gloss/sys_fstatat.c
new file mode 100644
index 0000000..f2f43bd
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_fstatat.c
@@ -0,0 +1,9 @@
+#include <errno.h>
+#include <sys/stat.h>
+
+int
+_fstatat(int dirfd, const char *file, struct stat *st, int flags)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_ftime.c b/fw/fe310/bsp/gloss/sys_ftime.c
new file mode 100644
index 0000000..65c1563
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_ftime.c
@@ -0,0 +1,9 @@
+#include <errno.h>
+#include <sys/timeb.h>
+
+int
+_ftime(struct timeb *tp)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_getcwd.c b/fw/fe310/bsp/gloss/sys_getcwd.c
new file mode 100644
index 0000000..82e8404
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_getcwd.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+char *
+_getcwd(char *buf, size_t size)
+{
+ errno = -ENOSYS;
+ return NULL;
+}
diff --git a/fw/fe310/bsp/gloss/sys_getpid.c b/fw/fe310/bsp/gloss/sys_getpid.c
new file mode 100644
index 0000000..589ad11
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_getpid.c
@@ -0,0 +1,7 @@
+#include <errno.h>
+
+int
+_getpid()
+{
+ return 1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_isatty.c b/fw/fe310/bsp/gloss/sys_isatty.c
new file mode 100644
index 0000000..70aec43
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_isatty.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int
+_isatty(int fd)
+{
+ return ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO) || (fd == STDERR_FILENO));
+}
diff --git a/fw/fe310/bsp/gloss/sys_kill.c b/fw/fe310/bsp/gloss/sys_kill.c
new file mode 100644
index 0000000..9003f26
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_kill.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_kill(int pid, int sig)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_link.c b/fw/fe310/bsp/gloss/sys_link.c
new file mode 100644
index 0000000..40d5912
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_link.c
@@ -0,0 +1,7 @@
+#include <errno.h>
+
+int _link(const char *old_name, const char *new_name)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_lseek.c b/fw/fe310/bsp/gloss/sys_lseek.c
new file mode 100644
index 0000000..d28a781
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_lseek.c
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <errno.h>
+
+off_t
+_lseek(int file, off_t ptr, int dir)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_lstat.c b/fw/fe310/bsp/gloss/sys_lstat.c
new file mode 100644
index 0000000..97a4585
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_lstat.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+#include <sys/stat.h>
+
+int _lstat(const char *file, struct stat *st)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_open.c b/fw/fe310/bsp/gloss/sys_open.c
new file mode 100644
index 0000000..a59f627
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_open.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_open(const char *name, int flags, int mode)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_openat.c b/fw/fe310/bsp/gloss/sys_openat.c
new file mode 100644
index 0000000..206de3b
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_openat.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_openat(int dirfd, const char *name, int flags, int mode)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_read.c b/fw/fe310/bsp/gloss/sys_read.c
new file mode 100644
index 0000000..1857fc6
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_read.c
@@ -0,0 +1,23 @@
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "platform.h"
+
+/* Read from a file. */
+ssize_t
+_read(int fd, void *ptr, size_t len)
+{
+ if (fd != STDIN_FILENO) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ char *current = ptr;
+ for (size_t i = 0; i < len; i++) {
+ volatile uint32_t r;
+ while ((r = UART0_REG(UART_REG_RXFIFO)) & 0x80000000);
+ current[i] = r & 0xFF;
+ }
+ return len;
+}
diff --git a/fw/fe310/bsp/gloss/sys_sbrk.c b/fw/fe310/bsp/gloss/sys_sbrk.c
new file mode 100644
index 0000000..ce10c90
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_sbrk.c
@@ -0,0 +1,38 @@
+#include <sys/types.h>
+
+/* brk is handled entirely within the C library. This limits METAL programs that
+ * use the C library to be disallowed from dynamically allocating memory
+ * without talking to the C library, but that sounds like a sane way to go
+ * about it. Note that there is no error checking anywhere in this file, users
+ * will simply get the relevant error when actually trying to use the memory
+ * that's been allocated. */
+extern char metal_segment_heap_target_start;
+extern char metal_segment_heap_target_end;
+static char *brk = &metal_segment_heap_target_start;
+
+int
+_brk(void *addr)
+{
+ brk = addr;
+ return 0;
+}
+
+char *
+_sbrk(ptrdiff_t incr)
+{
+ char *old = brk;
+
+ /* If __heap_size == 0, we can't allocate memory on the heap */
+ if(&metal_segment_heap_target_start == &metal_segment_heap_target_end) {
+ return (void *)-1;
+ }
+
+ /* Don't move the break past the end of the heap */
+ if ((brk + incr) <= &metal_segment_heap_target_end) {
+ brk += incr;
+ } else {
+ return (void *)-1;
+ }
+
+ return old;
+}
diff --git a/fw/fe310/bsp/gloss/sys_stat.c b/fw/fe310/bsp/gloss/sys_stat.c
new file mode 100644
index 0000000..3c2e419
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_stat.c
@@ -0,0 +1,9 @@
+#include <errno.h>
+#include <sys/stat.h>
+
+int
+_stat(const char *file, struct stat *st)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_sysconf.c b/fw/fe310/bsp/gloss/sys_sysconf.c
new file mode 100644
index 0000000..452a252
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_sysconf.c
@@ -0,0 +1,16 @@
+#include <unistd.h>
+#include <time.h>
+
+/* Get configurable system variables. */
+
+long
+_sysconf(int name)
+{
+ switch (name)
+ {
+ case _SC_CLK_TCK:
+ return CLOCKS_PER_SEC;
+ }
+
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_unlink.c b/fw/fe310/bsp/gloss/sys_unlink.c
new file mode 100644
index 0000000..b369d20
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_unlink.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+
+int
+_unlink(const char *name)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_utime.c b/fw/fe310/bsp/gloss/sys_utime.c
new file mode 100644
index 0000000..33d557a
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_utime.c
@@ -0,0 +1,9 @@
+#include <errno.h>
+struct utimbuf;
+
+int
+_utime(const char *path, const struct utimbuf *times)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_wait.c b/fw/fe310/bsp/gloss/sys_wait.c
new file mode 100644
index 0000000..9d459f1
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_wait.c
@@ -0,0 +1,7 @@
+#include <errno.h>
+
+int _wait(int *status)
+{
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/fw/fe310/bsp/gloss/sys_write.c b/fw/fe310/bsp/gloss/sys_write.c
new file mode 100644
index 0000000..458f9a4
--- /dev/null
+++ b/fw/fe310/bsp/gloss/sys_write.c
@@ -0,0 +1,24 @@
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "platform.h"
+
+#define PUTC(c) { while (UART0_REG(UART_REG_TXFIFO) & 0x80000000); UART0_REG(UART_REG_TXFIFO) = (c); }
+
+/* Write to a file. */
+ssize_t
+_write(int fd, const void *ptr, size_t len)
+{
+ if ((fd != STDOUT_FILENO) && (fd != STDERR_FILENO)) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ const char *current = ptr;
+ for (size_t i = 0; i < len; i++) {
+ if (current[i] == '\n') PUTC('\r');
+ PUTC(current[i]);
+ }
+ return len;
+}
diff --git a/fw/fe310/bsp/include/encoding.h b/fw/fe310/bsp/include/encoding.h
new file mode 100644
index 0000000..35e0f9f
--- /dev/null
+++ b/fw/fe310/bsp/include/encoding.h
@@ -0,0 +1,1313 @@
+// See LICENSE for license details.
+
+#ifndef RISCV_CSR_ENCODING_H
+#define RISCV_CSR_ENCODING_H
+
+#define MSTATUS_UIE 0x00000001
+#define MSTATUS_SIE 0x00000002
+#define MSTATUS_HIE 0x00000004
+#define MSTATUS_MIE 0x00000008
+#define MSTATUS_UPIE 0x00000010
+#define MSTATUS_SPIE 0x00000020
+#define MSTATUS_HPIE 0x00000040
+#define MSTATUS_MPIE 0x00000080
+#define MSTATUS_SPP 0x00000100
+#define MSTATUS_HPP 0x00000600
+#define MSTATUS_MPP 0x00001800
+#define MSTATUS_FS 0x00006000
+#define MSTATUS_XS 0x00018000
+#define MSTATUS_MPRV 0x00020000
+#define MSTATUS_PUM 0x00040000
+#define MSTATUS_MXR 0x00080000
+#define MSTATUS_VM 0x1F000000
+#define MSTATUS32_SD 0x80000000
+#define MSTATUS64_SD 0x8000000000000000
+
+#define SSTATUS_UIE 0x00000001
+#define SSTATUS_SIE 0x00000002
+#define SSTATUS_UPIE 0x00000010
+#define SSTATUS_SPIE 0x00000020
+#define SSTATUS_SPP 0x00000100
+#define SSTATUS_FS 0x00006000
+#define SSTATUS_XS 0x00018000
+#define SSTATUS_PUM 0x00040000
+#define SSTATUS32_SD 0x80000000
+#define SSTATUS64_SD 0x8000000000000000
+
+#define DCSR_XDEBUGVER (3U<<30)
+#define DCSR_NDRESET (1<<29)
+#define DCSR_FULLRESET (1<<28)
+#define DCSR_EBREAKM (1<<15)
+#define DCSR_EBREAKH (1<<14)
+#define DCSR_EBREAKS (1<<13)
+#define DCSR_EBREAKU (1<<12)
+#define DCSR_STOPCYCLE (1<<10)
+#define DCSR_STOPTIME (1<<9)
+#define DCSR_CAUSE (7<<6)
+#define DCSR_DEBUGINT (1<<5)
+#define DCSR_HALT (1<<3)
+#define DCSR_STEP (1<<2)
+#define DCSR_PRV (3<<0)
+
+#define DCSR_CAUSE_NONE 0
+#define DCSR_CAUSE_SWBP 1
+#define DCSR_CAUSE_HWBP 2
+#define DCSR_CAUSE_DEBUGINT 3
+#define DCSR_CAUSE_STEP 4
+#define DCSR_CAUSE_HALT 5
+
+#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4))
+#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5))
+#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11))
+
+#define MCONTROL_SELECT (1<<19)
+#define MCONTROL_TIMING (1<<18)
+#define MCONTROL_ACTION (0x3f<<12)
+#define MCONTROL_CHAIN (1<<11)
+#define MCONTROL_MATCH (0xf<<7)
+#define MCONTROL_M (1<<6)
+#define MCONTROL_H (1<<5)
+#define MCONTROL_S (1<<4)
+#define MCONTROL_U (1<<3)
+#define MCONTROL_EXECUTE (1<<2)
+#define MCONTROL_STORE (1<<1)
+#define MCONTROL_LOAD (1<<0)
+
+#define MCONTROL_TYPE_NONE 0
+#define MCONTROL_TYPE_MATCH 2
+
+#define MCONTROL_ACTION_DEBUG_EXCEPTION 0
+#define MCONTROL_ACTION_DEBUG_MODE 1
+#define MCONTROL_ACTION_TRACE_START 2
+#define MCONTROL_ACTION_TRACE_STOP 3
+#define MCONTROL_ACTION_TRACE_EMIT 4
+
+#define MCONTROL_MATCH_EQUAL 0
+#define MCONTROL_MATCH_NAPOT 1
+#define MCONTROL_MATCH_GE 2
+#define MCONTROL_MATCH_LT 3
+#define MCONTROL_MATCH_MASK_LOW 4
+#define MCONTROL_MATCH_MASK_HIGH 5
+
+#define MIP_SSIP (1 << IRQ_S_SOFT)
+#define MIP_HSIP (1 << IRQ_H_SOFT)
+#define MIP_MSIP (1 << IRQ_M_SOFT)
+#define MIP_STIP (1 << IRQ_S_TIMER)
+#define MIP_HTIP (1 << IRQ_H_TIMER)
+#define MIP_MTIP (1 << IRQ_M_TIMER)
+#define MIP_SEIP (1 << IRQ_S_EXT)
+#define MIP_HEIP (1 << IRQ_H_EXT)
+#define MIP_MEIP (1 << IRQ_M_EXT)
+
+#define SIP_SSIP MIP_SSIP
+#define SIP_STIP MIP_STIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+#define VM_MBARE 0
+#define VM_MBB 1
+#define VM_MBBID 2
+#define VM_SV32 8
+#define VM_SV39 9
+#define VM_SV48 10
+
+#define IRQ_S_SOFT 1
+#define IRQ_H_SOFT 2
+#define IRQ_M_SOFT 3
+#define IRQ_S_TIMER 5
+#define IRQ_H_TIMER 6
+#define IRQ_M_TIMER 7
+#define IRQ_S_EXT 9
+#define IRQ_H_EXT 10
+#define IRQ_M_EXT 11
+#define IRQ_COP 12
+#define IRQ_HOST 13
+
+#define DEFAULT_RSTVEC 0x00001000
+#define DEFAULT_NMIVEC 0x00001004
+#define DEFAULT_MTVEC 0x00001010
+#define CONFIG_STRING_ADDR 0x0000100C
+#define EXT_IO_BASE 0x40000000
+#define DRAM_BASE 0x80000000
+
+// page table entry (PTE) fields
+#define PTE_V 0x001 // Valid
+#define PTE_R 0x002 // Read
+#define PTE_W 0x004 // Write
+#define PTE_X 0x008 // Execute
+#define PTE_U 0x010 // User
+#define PTE_G 0x020 // Global
+#define PTE_A 0x040 // Accessed
+#define PTE_D 0x080 // Dirty
+#define PTE_SOFT 0x300 // Reserved for Software
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+
+#ifdef __riscv
+
+#ifdef __riscv64
+# define MSTATUS_SD MSTATUS64_SD
+# define SSTATUS_SD SSTATUS64_SD
+# define RISCV_PGLEVEL_BITS 9
+#else
+# define MSTATUS_SD MSTATUS32_SD
+# define SSTATUS_SD SSTATUS32_SD
+# define RISCV_PGLEVEL_BITS 10
+#endif
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE (1 << RISCV_PGSHIFT)
+
+#ifndef __ASSEMBLER__
+
+#ifdef __GNUC__
+
+#define read_csr(reg) ({ unsigned long __tmp; \
+ asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
+ __tmp; })
+
+#define write_csr(reg, val) ({ \
+ if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
+ asm volatile ("csrw " #reg ", %0" :: "i"(val)); \
+ else \
+ asm volatile ("csrw " #reg ", %0" :: "r"(val)); })
+
+#define swap_csr(reg, val) ({ unsigned long __tmp; \
+ if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
+ asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "i"(val)); \
+ else \
+ asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "r"(val)); \
+ __tmp; })
+
+#define set_csr(reg, bit) ({ unsigned long __tmp; \
+ if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
+ asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \
+ else \
+ asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \
+ __tmp; })
+
+#define clear_csr(reg, bit) ({ unsigned long __tmp; \
+ if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
+ asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \
+ else \
+ asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \
+ __tmp; })
+
+#define rdtime() read_csr(time)
+#define rdcycle() read_csr(cycle)
+#define rdinstret() read_csr(instret)
+
+#endif
+
+#endif
+
+#endif
+
+#endif
+/* Automatically generated by parse-opcodes */
+#ifndef RISCV_ENCODING_H
+#define RISCV_ENCODING_H
+#define MATCH_BEQ 0x63
+#define MASK_BEQ 0x707f
+#define MATCH_BNE 0x1063
+#define MASK_BNE 0x707f
+#define MATCH_BLT 0x4063
+#define MASK_BLT 0x707f
+#define MATCH_BGE 0x5063
+#define MASK_BGE 0x707f
+#define MATCH_BLTU 0x6063
+#define MASK_BLTU 0x707f
+#define MATCH_BGEU 0x7063
+#define MASK_BGEU 0x707f
+#define MATCH_JALR 0x67
+#define MASK_JALR 0x707f
+#define MATCH_JAL 0x6f
+#define MASK_JAL 0x7f
+#define MATCH_LUI 0x37
+#define MASK_LUI 0x7f
+#define MATCH_AUIPC 0x17
+#define MASK_AUIPC 0x7f
+#define MATCH_ADDI 0x13
+#define MASK_ADDI 0x707f
+#define MATCH_SLLI 0x1013
+#define MASK_SLLI 0xfc00707f
+#define MATCH_SLTI 0x2013
+#define MASK_SLTI 0x707f
+#define MATCH_SLTIU 0x3013
+#define MASK_SLTIU 0x707f
+#define MATCH_XORI 0x4013
+#define MASK_XORI 0x707f
+#define MATCH_SRLI 0x5013
+#define MASK_SRLI 0xfc00707f
+#define MATCH_SRAI 0x40005013
+#define MASK_SRAI 0xfc00707f
+#define MATCH_ORI 0x6013
+#define MASK_ORI 0x707f
+#define MATCH_ANDI 0x7013
+#define MASK_ANDI 0x707f
+#define MATCH_ADD 0x33
+#define MASK_ADD 0xfe00707f
+#define MATCH_SUB 0x40000033
+#define MASK_SUB 0xfe00707f
+#define MATCH_SLL 0x1033
+#define MASK_SLL 0xfe00707f
+#define MATCH_SLT 0x2033
+#define MASK_SLT 0xfe00707f
+#define MATCH_SLTU 0x3033
+#define MASK_SLTU 0xfe00707f
+#define MATCH_XOR 0x4033
+#define MASK_XOR 0xfe00707f
+#define MATCH_SRL 0x5033
+#define MASK_SRL 0xfe00707f
+#define MATCH_SRA 0x40005033
+#define MASK_SRA 0xfe00707f
+#define MATCH_OR 0x6033
+#define MASK_OR 0xfe00707f
+#define MATCH_AND 0x7033
+#define MASK_AND 0xfe00707f
+#define MATCH_ADDIW 0x1b
+#define MASK_ADDIW 0x707f
+#define MATCH_SLLIW 0x101b
+#define MASK_SLLIW 0xfe00707f
+#define MATCH_SRLIW 0x501b
+#define MASK_SRLIW 0xfe00707f
+#define MATCH_SRAIW 0x4000501b
+#define MASK_SRAIW 0xfe00707f
+#define MATCH_ADDW 0x3b
+#define MASK_ADDW 0xfe00707f
+#define MATCH_SUBW 0x4000003b
+#define MASK_SUBW 0xfe00707f
+#define MATCH_SLLW 0x103b
+#define MASK_SLLW 0xfe00707f
+#define MATCH_SRLW 0x503b
+#define MASK_SRLW 0xfe00707f
+#define MATCH_SRAW 0x4000503b
+#define MASK_SRAW 0xfe00707f
+#define MATCH_LB 0x3
+#define MASK_LB 0x707f
+#define MATCH_LH 0x1003
+#define MASK_LH 0x707f
+#define MATCH_LW 0x2003
+#define MASK_LW 0x707f
+#define MATCH_LD 0x3003
+#define MASK_LD 0x707f
+#define MATCH_LBU 0x4003
+#define MASK_LBU 0x707f
+#define MATCH_LHU 0x5003
+#define MASK_LHU 0x707f
+#define MATCH_LWU 0x6003
+#define MASK_LWU 0x707f
+#define MATCH_SB 0x23
+#define MASK_SB 0x707f
+#define MATCH_SH 0x1023
+#define MASK_SH 0x707f
+#define MATCH_SW 0x2023
+#define MASK_SW 0x707f
+#define MATCH_SD 0x3023
+#define MASK_SD 0x707f
+#define MATCH_FENCE 0xf
+#define MASK_FENCE 0x707f
+#define MATCH_FENCE_I 0x100f
+#define MASK_FENCE_I 0x707f
+#define MATCH_MUL 0x2000033
+#define MASK_MUL 0xfe00707f
+#define MATCH_MULH 0x2001033
+#define MASK_MULH 0xfe00707f
+#define MATCH_MULHSU 0x2002033
+#define MASK_MULHSU 0xfe00707f
+#define MATCH_MULHU 0x2003033
+#define MASK_MULHU 0xfe00707f
+#define MATCH_DIV 0x2004033
+#define MASK_DIV 0xfe00707f
+#define MATCH_DIVU 0x2005033
+#define MASK_DIVU 0xfe00707f
+#define MATCH_REM 0x2006033
+#define MASK_REM 0xfe00707f
+#define MATCH_REMU 0x2007033
+#define MASK_REMU 0xfe00707f
+#define MATCH_MULW 0x200003b
+#define MASK_MULW 0xfe00707f
+#define MATCH_DIVW 0x200403b
+#define MASK_DIVW 0xfe00707f
+#define MATCH_DIVUW 0x200503b
+#define MASK_DIVUW 0xfe00707f
+#define MATCH_REMW 0x200603b
+#define MASK_REMW 0xfe00707f
+#define MATCH_REMUW 0x200703b
+#define MASK_REMUW 0xfe00707f
+#define MATCH_AMOADD_W 0x202f
+#define MASK_AMOADD_W 0xf800707f
+#define MATCH_AMOXOR_W 0x2000202f
+#define MASK_AMOXOR_W 0xf800707f
+#define MATCH_AMOOR_W 0x4000202f
+#define MASK_AMOOR_W 0xf800707f
+#define MATCH_AMOAND_W 0x6000202f
+#define MASK_AMOAND_W 0xf800707f
+#define MATCH_AMOMIN_W 0x8000202f
+#define MASK_AMOMIN_W 0xf800707f
+#define MATCH_AMOMAX_W 0xa000202f
+#define MASK_AMOMAX_W 0xf800707f
+#define MATCH_AMOMINU_W 0xc000202f
+#define MASK_AMOMINU_W 0xf800707f
+#define MATCH_AMOMAXU_W 0xe000202f
+#define MASK_AMOMAXU_W 0xf800707f
+#define MATCH_AMOSWAP_W 0x800202f
+#define MASK_AMOSWAP_W 0xf800707f
+#define MATCH_LR_W 0x1000202f
+#define MASK_LR_W 0xf9f0707f
+#define MATCH_SC_W 0x1800202f
+#define MASK_SC_W 0xf800707f
+#define MATCH_AMOADD_D 0x302f
+#define MASK_AMOADD_D 0xf800707f
+#define MATCH_AMOXOR_D 0x2000302f
+#define MASK_AMOXOR_D 0xf800707f
+#define MATCH_AMOOR_D 0x4000302f
+#define MASK_AMOOR_D 0xf800707f
+#define MATCH_AMOAND_D 0x6000302f
+#define MASK_AMOAND_D 0xf800707f
+#define MATCH_AMOMIN_D 0x8000302f
+#define MASK_AMOMIN_D 0xf800707f
+#define MATCH_AMOMAX_D 0xa000302f
+#define MASK_AMOMAX_D 0xf800707f
+#define MATCH_AMOMINU_D 0xc000302f
+#define MASK_AMOMINU_D 0xf800707f
+#define MATCH_AMOMAXU_D 0xe000302f
+#define MASK_AMOMAXU_D 0xf800707f
+#define MATCH_AMOSWAP_D 0x800302f
+#define MASK_AMOSWAP_D 0xf800707f
+#define MATCH_LR_D 0x1000302f
+#define MASK_LR_D 0xf9f0707f
+#define MATCH_SC_D 0x1800302f
+#define MASK_SC_D 0xf800707f
+#define MATCH_ECALL 0x73
+#define MASK_ECALL 0xffffffff
+#define MATCH_EBREAK 0x100073
+#define MASK_EBREAK 0xffffffff
+#define MATCH_URET 0x200073
+#define MASK_URET 0xffffffff
+#define MATCH_SRET 0x10200073
+#define MASK_SRET 0xffffffff
+#define MATCH_HRET 0x20200073
+#define MASK_HRET 0xffffffff
+#define MATCH_MRET 0x30200073
+#define MASK_MRET 0xffffffff
+#define MATCH_DRET 0x7b200073
+#define MASK_DRET 0xffffffff
+#define MATCH_SFENCE_VM 0x10400073
+#define MASK_SFENCE_VM 0xfff07fff
+#define MATCH_WFI 0x10500073
+#define MASK_WFI 0xffffffff
+#define MATCH_CSRRW 0x1073
+#define MASK_CSRRW 0x707f
+#define MATCH_CSRRS 0x2073
+#define MASK_CSRRS 0x707f
+#define MATCH_CSRRC 0x3073
+#define MASK_CSRRC 0x707f
+#define MATCH_CSRRWI 0x5073
+#define MASK_CSRRWI 0x707f
+#define MATCH_CSRRSI 0x6073
+#define MASK_CSRRSI 0x707f
+#define MATCH_CSRRCI 0x7073
+#define MASK_CSRRCI 0x707f
+#define MATCH_FADD_S 0x53
+#define MASK_FADD_S 0xfe00007f
+#define MATCH_FSUB_S 0x8000053
+#define MASK_FSUB_S 0xfe00007f
+#define MATCH_FMUL_S 0x10000053
+#define MASK_FMUL_S 0xfe00007f
+#define MATCH_FDIV_S 0x18000053
+#define MASK_FDIV_S 0xfe00007f
+#define MATCH_FSGNJ_S 0x20000053
+#define MASK_FSGNJ_S 0xfe00707f
+#define MATCH_FSGNJN_S 0x20001053
+#define MASK_FSGNJN_S 0xfe00707f
+#define MATCH_FSGNJX_S 0x20002053
+#define MASK_FSGNJX_S 0xfe00707f
+#define MATCH_FMIN_S 0x28000053
+#define MASK_FMIN_S 0xfe00707f
+#define MATCH_FMAX_S 0x28001053
+#define MASK_FMAX_S 0xfe00707f
+#define MATCH_FSQRT_S 0x58000053
+#define MASK_FSQRT_S 0xfff0007f
+#define MATCH_FADD_D 0x2000053
+#define MASK_FADD_D 0xfe00007f
+#define MATCH_FSUB_D 0xa000053
+#define MASK_FSUB_D 0xfe00007f
+#define MATCH_FMUL_D 0x12000053
+#define MASK_FMUL_D 0xfe00007f
+#define MATCH_FDIV_D 0x1a000053
+#define MASK_FDIV_D 0xfe00007f
+#define MATCH_FSGNJ_D 0x22000053
+#define MASK_FSGNJ_D 0xfe00707f
+#define MATCH_FSGNJN_D 0x22001053
+#define MASK_FSGNJN_D 0xfe00707f
+#define MATCH_FSGNJX_D 0x22002053
+#define MASK_FSGNJX_D 0xfe00707f
+#define MATCH_FMIN_D 0x2a000053
+#define MASK_FMIN_D 0xfe00707f
+#define MATCH_FMAX_D 0x2a001053
+#define MASK_FMAX_D 0xfe00707f
+#define MATCH_FCVT_S_D 0x40100053
+#define MASK_FCVT_S_D 0xfff0007f
+#define MATCH_FCVT_D_S 0x42000053
+#define MASK_FCVT_D_S 0xfff0007f
+#define MATCH_FSQRT_D 0x5a000053
+#define MASK_FSQRT_D 0xfff0007f
+#define MATCH_FLE_S 0xa0000053
+#define MASK_FLE_S 0xfe00707f
+#define MATCH_FLT_S 0xa0001053
+#define MASK_FLT_S 0xfe00707f
+#define MATCH_FEQ_S 0xa0002053
+#define MASK_FEQ_S 0xfe00707f
+#define MATCH_FLE_D 0xa2000053
+#define MASK_FLE_D 0xfe00707f
+#define MATCH_FLT_D 0xa2001053
+#define MASK_FLT_D 0xfe00707f
+#define MATCH_FEQ_D 0xa2002053
+#define MASK_FEQ_D 0xfe00707f
+#define MATCH_FCVT_W_S 0xc0000053
+#define MASK_FCVT_W_S 0xfff0007f
+#define MATCH_FCVT_WU_S 0xc0100053
+#define MASK_FCVT_WU_S 0xfff0007f
+#define MATCH_FCVT_L_S 0xc0200053
+#define MASK_FCVT_L_S 0xfff0007f
+#define MATCH_FCVT_LU_S 0xc0300053
+#define MASK_FCVT_LU_S 0xfff0007f
+#define MATCH_FMV_X_S 0xe0000053
+#define MASK_FMV_X_S 0xfff0707f
+#define MATCH_FCLASS_S 0xe0001053
+#define MASK_FCLASS_S 0xfff0707f
+#define MATCH_FCVT_W_D 0xc2000053
+#define MASK_FCVT_W_D 0xfff0007f
+#define MATCH_FCVT_WU_D 0xc2100053
+#define MASK_FCVT_WU_D 0xfff0007f
+#define MATCH_FCVT_L_D 0xc2200053
+#define MASK_FCVT_L_D 0xfff0007f
+#define MATCH_FCVT_LU_D 0xc2300053
+#define MASK_FCVT_LU_D 0xfff0007f
+#define MATCH_FMV_X_D 0xe2000053
+#define MASK_FMV_X_D 0xfff0707f
+#define MATCH_FCLASS_D 0xe2001053
+#define MASK_FCLASS_D 0xfff0707f
+#define MATCH_FCVT_S_W 0xd0000053
+#define MASK_FCVT_S_W 0xfff0007f
+#define MATCH_FCVT_S_WU 0xd0100053
+#define MASK_FCVT_S_WU 0xfff0007f
+#define MATCH_FCVT_S_L 0xd0200053
+#define MASK_FCVT_S_L 0xfff0007f
+#define MATCH_FCVT_S_LU 0xd0300053
+#define MASK_FCVT_S_LU 0xfff0007f
+#define MATCH_FMV_S_X 0xf0000053
+#define MASK_FMV_S_X 0xfff0707f
+#define MATCH_FCVT_D_W 0xd2000053
+#define MASK_FCVT_D_W 0xfff0007f
+#define MATCH_FCVT_D_WU 0xd2100053
+#define MASK_FCVT_D_WU 0xfff0007f
+#define MATCH_FCVT_D_L 0xd2200053
+#define MASK_FCVT_D_L 0xfff0007f
+#define MATCH_FCVT_D_LU 0xd2300053
+#define MASK_FCVT_D_LU 0xfff0007f
+#define MATCH_FMV_D_X 0xf2000053
+#define MASK_FMV_D_X 0xfff0707f
+#define MATCH_FLW 0x2007
+#define MASK_FLW 0x707f
+#define MATCH_FLD 0x3007
+#define MASK_FLD 0x707f
+#define MATCH_FSW 0x2027
+#define MASK_FSW 0x707f
+#define MATCH_FSD 0x3027
+#define MASK_FSD 0x707f
+#define MATCH_FMADD_S 0x43
+#define MASK_FMADD_S 0x600007f
+#define MATCH_FMSUB_S 0x47
+#define MASK_FMSUB_S 0x600007f
+#define MATCH_FNMSUB_S 0x4b
+#define MASK_FNMSUB_S 0x600007f
+#define MATCH_FNMADD_S 0x4f
+#define MASK_FNMADD_S 0x600007f
+#define MATCH_FMADD_D 0x2000043
+#define MASK_FMADD_D 0x600007f
+#define MATCH_FMSUB_D 0x2000047
+#define MASK_FMSUB_D 0x600007f
+#define MATCH_FNMSUB_D 0x200004b
+#define MASK_FNMSUB_D 0x600007f
+#define MATCH_FNMADD_D 0x200004f
+#define MASK_FNMADD_D 0x600007f
+#define MATCH_C_NOP 0x1
+#define MASK_C_NOP 0xffff
+#define MATCH_C_ADDI16SP 0x6101
+#define MASK_C_ADDI16SP 0xef83
+#define MATCH_C_JR 0x8002
+#define MASK_C_JR 0xf07f
+#define MATCH_C_JALR 0x9002
+#define MASK_C_JALR 0xf07f
+#define MATCH_C_EBREAK 0x9002
+#define MASK_C_EBREAK 0xffff
+#define MATCH_C_LD 0x6000
+#define MASK_C_LD 0xe003
+#define MATCH_C_SD 0xe000
+#define MASK_C_SD 0xe003
+#define MATCH_C_ADDIW 0x2001
+#define MASK_C_ADDIW 0xe003
+#define MATCH_C_LDSP 0x6002
+#define MASK_C_LDSP 0xe003
+#define MATCH_C_SDSP 0xe002
+#define MASK_C_SDSP 0xe003
+#define MATCH_C_ADDI4SPN 0x0
+#define MASK_C_ADDI4SPN 0xe003
+#define MATCH_C_FLD 0x2000
+#define MASK_C_FLD 0xe003
+#define MATCH_C_LW 0x4000
+#define MASK_C_LW 0xe003
+#define MATCH_C_FLW 0x6000
+#define MASK_C_FLW 0xe003
+#define MATCH_C_FSD 0xa000
+#define MASK_C_FSD 0xe003
+#define MATCH_C_SW 0xc000
+#define MASK_C_SW 0xe003
+#define MATCH_C_FSW 0xe000
+#define MASK_C_FSW 0xe003
+#define MATCH_C_ADDI 0x1
+#define MASK_C_ADDI 0xe003
+#define MATCH_C_JAL 0x2001
+#define MASK_C_JAL 0xe003
+#define MATCH_C_LI 0x4001
+#define MASK_C_LI 0xe003
+#define MATCH_C_LUI 0x6001
+#define MASK_C_LUI 0xe003
+#define MATCH_C_SRLI 0x8001
+#define MASK_C_SRLI 0xec03
+#define MATCH_C_SRAI 0x8401
+#define MASK_C_SRAI 0xec03
+#define MATCH_C_ANDI 0x8801
+#define MASK_C_ANDI 0xec03
+#define MATCH_C_SUB 0x8c01
+#define MASK_C_SUB 0xfc63
+#define MATCH_C_XOR 0x8c21
+#define MASK_C_XOR 0xfc63
+#define MATCH_C_OR 0x8c41
+#define MASK_C_OR 0xfc63
+#define MATCH_C_AND 0x8c61
+#define MASK_C_AND 0xfc63
+#define MATCH_C_SUBW 0x9c01
+#define MASK_C_SUBW 0xfc63
+#define MATCH_C_ADDW 0x9c21
+#define MASK_C_ADDW 0xfc63
+#define MATCH_C_J 0xa001
+#define MASK_C_J 0xe003
+#define MATCH_C_BEQZ 0xc001
+#define MASK_C_BEQZ 0xe003
+#define MATCH_C_BNEZ 0xe001
+#define MASK_C_BNEZ 0xe003
+#define MATCH_C_SLLI 0x2
+#define MASK_C_SLLI 0xe003
+#define MATCH_C_FLDSP 0x2002
+#define MASK_C_FLDSP 0xe003
+#define MATCH_C_LWSP 0x4002
+#define MASK_C_LWSP 0xe003
+#define MATCH_C_FLWSP 0x6002
+#define MASK_C_FLWSP 0xe003
+#define MATCH_C_MV 0x8002
+#define MASK_C_MV 0xf003
+#define MATCH_C_ADD 0x9002
+#define MASK_C_ADD 0xf003
+#define MATCH_C_FSDSP 0xa002
+#define MASK_C_FSDSP 0xe003
+#define MATCH_C_SWSP 0xc002
+#define MASK_C_SWSP 0xe003
+#define MATCH_C_FSWSP 0xe002
+#define MASK_C_FSWSP 0xe003
+#define MATCH_CUSTOM0 0xb
+#define MASK_CUSTOM0 0x707f
+#define MATCH_CUSTOM0_RS1 0x200b
+#define MASK_CUSTOM0_RS1 0x707f
+#define MATCH_CUSTOM0_RS1_RS2 0x300b
+#define MASK_CUSTOM0_RS1_RS2 0x707f
+#define MATCH_CUSTOM0_RD 0x400b
+#define MASK_CUSTOM0_RD 0x707f
+#define MATCH_CUSTOM0_RD_RS1 0x600b
+#define MASK_CUSTOM0_RD_RS1 0x707f
+#define MATCH_CUSTOM0_RD_RS1_RS2 0x700b
+#define MASK_CUSTOM0_RD_RS1_RS2 0x707f
+#define MATCH_CUSTOM1 0x2b
+#define MASK_CUSTOM1 0x707f
+#define MATCH_CUSTOM1_RS1 0x202b
+#define MASK_CUSTOM1_RS1 0x707f
+#define MATCH_CUSTOM1_RS1_RS2 0x302b
+#define MASK_CUSTOM1_RS1_RS2 0x707f
+#define MATCH_CUSTOM1_RD 0x402b
+#define MASK_CUSTOM1_RD 0x707f
+#define MATCH_CUSTOM1_RD_RS1 0x602b
+#define MASK_CUSTOM1_RD_RS1 0x707f
+#define MATCH_CUSTOM1_RD_RS1_RS2 0x702b
+#define MASK_CUSTOM1_RD_RS1_RS2 0x707f
+#define MATCH_CUSTOM2 0x5b
+#define MASK_CUSTOM2 0x707f
+#define MATCH_CUSTOM2_RS1 0x205b
+#define MASK_CUSTOM2_RS1 0x707f
+#define MATCH_CUSTOM2_RS1_RS2 0x305b
+#define MASK_CUSTOM2_RS1_RS2 0x707f
+#define MATCH_CUSTOM2_RD 0x405b
+#define MASK_CUSTOM2_RD 0x707f
+#define MATCH_CUSTOM2_RD_RS1 0x605b
+#define MASK_CUSTOM2_RD_RS1 0x707f
+#define MATCH_CUSTOM2_RD_RS1_RS2 0x705b
+#define MASK_CUSTOM2_RD_RS1_RS2 0x707f
+#define MATCH_CUSTOM3 0x7b
+#define MASK_CUSTOM3 0x707f
+#define MATCH_CUSTOM3_RS1 0x207b
+#define MASK_CUSTOM3_RS1 0x707f
+#define MATCH_CUSTOM3_RS1_RS2 0x307b
+#define MASK_CUSTOM3_RS1_RS2 0x707f
+#define MATCH_CUSTOM3_RD 0x407b
+#define MASK_CUSTOM3_RD 0x707f
+#define MATCH_CUSTOM3_RD_RS1 0x607b
+#define MASK_CUSTOM3_RD_RS1 0x707f
+#define MATCH_CUSTOM3_RD_RS1_RS2 0x707b
+#define MASK_CUSTOM3_RD_RS1_RS2 0x707f
+#define CSR_FFLAGS 0x1
+#define CSR_FRM 0x2
+#define CSR_FCSR 0x3
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_HPMCOUNTER3 0xc03
+#define CSR_HPMCOUNTER4 0xc04
+#define CSR_HPMCOUNTER5 0xc05
+#define CSR_HPMCOUNTER6 0xc06
+#define CSR_HPMCOUNTER7 0xc07
+#define CSR_HPMCOUNTER8 0xc08
+#define CSR_HPMCOUNTER9 0xc09
+#define CSR_HPMCOUNTER10 0xc0a
+#define CSR_HPMCOUNTER11 0xc0b
+#define CSR_HPMCOUNTER12 0xc0c
+#define CSR_HPMCOUNTER13 0xc0d
+#define CSR_HPMCOUNTER14 0xc0e
+#define CSR_HPMCOUNTER15 0xc0f
+#define CSR_HPMCOUNTER16 0xc10
+#define CSR_HPMCOUNTER17 0xc11
+#define CSR_HPMCOUNTER18 0xc12
+#define CSR_HPMCOUNTER19 0xc13
+#define CSR_HPMCOUNTER20 0xc14
+#define CSR_HPMCOUNTER21 0xc15
+#define CSR_HPMCOUNTER22 0xc16
+#define CSR_HPMCOUNTER23 0xc17
+#define CSR_HPMCOUNTER24 0xc18
+#define CSR_HPMCOUNTER25 0xc19
+#define CSR_HPMCOUNTER26 0xc1a
+#define CSR_HPMCOUNTER27 0xc1b
+#define CSR_HPMCOUNTER28 0xc1c
+#define CSR_HPMCOUNTER29 0xc1d
+#define CSR_HPMCOUNTER30 0xc1e
+#define CSR_HPMCOUNTER31 0xc1f
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_SBADADDR 0x143
+#define CSR_SIP 0x144
+#define CSR_SPTBR 0x180
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MEDELEG 0x302
+#define CSR_MIDELEG 0x303
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MBADADDR 0x343
+#define CSR_MIP 0x344
+#define CSR_TSELECT 0x7a0
+#define CSR_TDATA1 0x7a1
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA3 0x7a3
+#define CSR_DCSR 0x7b0
+#define CSR_DPC 0x7b1
+#define CSR_DSCRATCH 0x7b2
+#define CSR_MCYCLE 0xb00
+#define CSR_MINSTRET 0xb02
+#define CSR_MHPMCOUNTER3 0xb03
+#define CSR_MHPMCOUNTER4 0xb04
+#define CSR_MHPMCOUNTER5 0xb05
+#define CSR_MHPMCOUNTER6 0xb06
+#define CSR_MHPMCOUNTER7 0xb07
+#define CSR_MHPMCOUNTER8 0xb08
+#define CSR_MHPMCOUNTER9 0xb09
+#define CSR_MHPMCOUNTER10 0xb0a
+#define CSR_MHPMCOUNTER11 0xb0b
+#define CSR_MHPMCOUNTER12 0xb0c
+#define CSR_MHPMCOUNTER13 0xb0d
+#define CSR_MHPMCOUNTER14 0xb0e
+#define CSR_MHPMCOUNTER15 0xb0f
+#define CSR_MHPMCOUNTER16 0xb10
+#define CSR_MHPMCOUNTER17 0xb11
+#define CSR_MHPMCOUNTER18 0xb12
+#define CSR_MHPMCOUNTER19 0xb13
+#define CSR_MHPMCOUNTER20 0xb14
+#define CSR_MHPMCOUNTER21 0xb15
+#define CSR_MHPMCOUNTER22 0xb16
+#define CSR_MHPMCOUNTER23 0xb17
+#define CSR_MHPMCOUNTER24 0xb18
+#define CSR_MHPMCOUNTER25 0xb19
+#define CSR_MHPMCOUNTER26 0xb1a
+#define CSR_MHPMCOUNTER27 0xb1b
+#define CSR_MHPMCOUNTER28 0xb1c
+#define CSR_MHPMCOUNTER29 0xb1d
+#define CSR_MHPMCOUNTER30 0xb1e
+#define CSR_MHPMCOUNTER31 0xb1f
+#define CSR_MUCOUNTEREN 0x320
+#define CSR_MSCOUNTEREN 0x321
+#define CSR_MHPMEVENT3 0x323
+#define CSR_MHPMEVENT4 0x324
+#define CSR_MHPMEVENT5 0x325
+#define CSR_MHPMEVENT6 0x326
+#define CSR_MHPMEVENT7 0x327
+#define CSR_MHPMEVENT8 0x328
+#define CSR_MHPMEVENT9 0x329
+#define CSR_MHPMEVENT10 0x32a
+#define CSR_MHPMEVENT11 0x32b
+#define CSR_MHPMEVENT12 0x32c
+#define CSR_MHPMEVENT13 0x32d
+#define CSR_MHPMEVENT14 0x32e
+#define CSR_MHPMEVENT15 0x32f
+#define CSR_MHPMEVENT16 0x330
+#define CSR_MHPMEVENT17 0x331
+#define CSR_MHPMEVENT18 0x332
+#define CSR_MHPMEVENT19 0x333
+#define CSR_MHPMEVENT20 0x334
+#define CSR_MHPMEVENT21 0x335
+#define CSR_MHPMEVENT22 0x336
+#define CSR_MHPMEVENT23 0x337
+#define CSR_MHPMEVENT24 0x338
+#define CSR_MHPMEVENT25 0x339
+#define CSR_MHPMEVENT26 0x33a
+#define CSR_MHPMEVENT27 0x33b
+#define CSR_MHPMEVENT28 0x33c
+#define CSR_MHPMEVENT29 0x33d
+#define CSR_MHPMEVENT30 0x33e
+#define CSR_MHPMEVENT31 0x33f
+#define CSR_MVENDORID 0xf11
+#define CSR_MARCHID 0xf12
+#define CSR_MIMPID 0xf13
+#define CSR_MHARTID 0xf14
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_HPMCOUNTER3H 0xc83
+#define CSR_HPMCOUNTER4H 0xc84
+#define CSR_HPMCOUNTER5H 0xc85
+#define CSR_HPMCOUNTER6H 0xc86
+#define CSR_HPMCOUNTER7H 0xc87
+#define CSR_HPMCOUNTER8H 0xc88
+#define CSR_HPMCOUNTER9H 0xc89
+#define CSR_HPMCOUNTER10H 0xc8a
+#define CSR_HPMCOUNTER11H 0xc8b
+#define CSR_HPMCOUNTER12H 0xc8c
+#define CSR_HPMCOUNTER13H 0xc8d
+#define CSR_HPMCOUNTER14H 0xc8e
+#define CSR_HPMCOUNTER15H 0xc8f
+#define CSR_HPMCOUNTER16H 0xc90
+#define CSR_HPMCOUNTER17H 0xc91
+#define CSR_HPMCOUNTER18H 0xc92
+#define CSR_HPMCOUNTER19H 0xc93
+#define CSR_HPMCOUNTER20H 0xc94
+#define CSR_HPMCOUNTER21H 0xc95
+#define CSR_HPMCOUNTER22H 0xc96
+#define CSR_HPMCOUNTER23H 0xc97
+#define CSR_HPMCOUNTER24H 0xc98
+#define CSR_HPMCOUNTER25H 0xc99
+#define CSR_HPMCOUNTER26H 0xc9a
+#define CSR_HPMCOUNTER27H 0xc9b
+#define CSR_HPMCOUNTER28H 0xc9c
+#define CSR_HPMCOUNTER29H 0xc9d
+#define CSR_HPMCOUNTER30H 0xc9e
+#define CSR_HPMCOUNTER31H 0xc9f
+#define CSR_MCYCLEH 0xb80
+#define CSR_MINSTRETH 0xb82
+#define CSR_MHPMCOUNTER3H 0xb83
+#define CSR_MHPMCOUNTER4H 0xb84
+#define CSR_MHPMCOUNTER5H 0xb85
+#define CSR_MHPMCOUNTER6H 0xb86
+#define CSR_MHPMCOUNTER7H 0xb87
+#define CSR_MHPMCOUNTER8H 0xb88
+#define CSR_MHPMCOUNTER9H 0xb89
+#define CSR_MHPMCOUNTER10H 0xb8a
+#define CSR_MHPMCOUNTER11H 0xb8b
+#define CSR_MHPMCOUNTER12H 0xb8c
+#define CSR_MHPMCOUNTER13H 0xb8d
+#define CSR_MHPMCOUNTER14H 0xb8e
+#define CSR_MHPMCOUNTER15H 0xb8f
+#define CSR_MHPMCOUNTER16H 0xb90
+#define CSR_MHPMCOUNTER17H 0xb91
+#define CSR_MHPMCOUNTER18H 0xb92
+#define CSR_MHPMCOUNTER19H 0xb93
+#define CSR_MHPMCOUNTER20H 0xb94
+#define CSR_MHPMCOUNTER21H 0xb95
+#define CSR_MHPMCOUNTER22H 0xb96
+#define CSR_MHPMCOUNTER23H 0xb97
+#define CSR_MHPMCOUNTER24H 0xb98
+#define CSR_MHPMCOUNTER25H 0xb99
+#define CSR_MHPMCOUNTER26H 0xb9a
+#define CSR_MHPMCOUNTER27H 0xb9b
+#define CSR_MHPMCOUNTER28H 0xb9c
+#define CSR_MHPMCOUNTER29H 0xb9d
+#define CSR_MHPMCOUNTER30H 0xb9e
+#define CSR_MHPMCOUNTER31H 0xb9f
+#define CAUSE_MISALIGNED_FETCH 0x0
+#define CAUSE_FAULT_FETCH 0x1
+#define CAUSE_ILLEGAL_INSTRUCTION 0x2
+#define CAUSE_BREAKPOINT 0x3
+#define CAUSE_MISALIGNED_LOAD 0x4
+#define CAUSE_FAULT_LOAD 0x5
+#define CAUSE_MISALIGNED_STORE 0x6
+#define CAUSE_FAULT_STORE 0x7
+#define CAUSE_USER_ECALL 0x8
+#define CAUSE_SUPERVISOR_ECALL 0x9
+#define CAUSE_HYPERVISOR_ECALL 0xa
+#define CAUSE_MACHINE_ECALL 0xb
+#endif
+#ifdef DECLARE_INSN
+DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ)
+DECLARE_INSN(bne, MATCH_BNE, MASK_BNE)
+DECLARE_INSN(blt, MATCH_BLT, MASK_BLT)
+DECLARE_INSN(bge, MATCH_BGE, MASK_BGE)
+DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU)
+DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU)
+DECLARE_INSN(jalr, MATCH_JALR, MASK_JALR)
+DECLARE_INSN(jal, MATCH_JAL, MASK_JAL)
+DECLARE_INSN(lui, MATCH_LUI, MASK_LUI)
+DECLARE_INSN(auipc, MATCH_AUIPC, MASK_AUIPC)
+DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI)
+DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI)
+DECLARE_INSN(slti, MATCH_SLTI, MASK_SLTI)
+DECLARE_INSN(sltiu, MATCH_SLTIU, MASK_SLTIU)
+DECLARE_INSN(xori, MATCH_XORI, MASK_XORI)
+DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI)
+DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI)
+DECLARE_INSN(ori, MATCH_ORI, MASK_ORI)
+DECLARE_INSN(andi, MATCH_ANDI, MASK_ANDI)
+DECLARE_INSN(add, MATCH_ADD, MASK_ADD)
+DECLARE_INSN(sub, MATCH_SUB, MASK_SUB)
+DECLARE_INSN(sll, MATCH_SLL, MASK_SLL)
+DECLARE_INSN(slt, MATCH_SLT, MASK_SLT)
+DECLARE_INSN(sltu, MATCH_SLTU, MASK_SLTU)
+DECLARE_INSN(xor, MATCH_XOR, MASK_XOR)
+DECLARE_INSN(srl, MATCH_SRL, MASK_SRL)
+DECLARE_INSN(sra, MATCH_SRA, MASK_SRA)
+DECLARE_INSN(or, MATCH_OR, MASK_OR)
+DECLARE_INSN(and, MATCH_AND, MASK_AND)
+DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW)
+DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW)
+DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW)
+DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW)
+DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW)
+DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW)
+DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW)
+DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW)
+DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW)
+DECLARE_INSN(lb, MATCH_LB, MASK_LB)
+DECLARE_INSN(lh, MATCH_LH, MASK_LH)
+DECLARE_INSN(lw, MATCH_LW, MASK_LW)
+DECLARE_INSN(ld, MATCH_LD, MASK_LD)
+DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU)
+DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU)
+DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU)
+DECLARE_INSN(sb, MATCH_SB, MASK_SB)
+DECLARE_INSN(sh, MATCH_SH, MASK_SH)
+DECLARE_INSN(sw, MATCH_SW, MASK_SW)
+DECLARE_INSN(sd, MATCH_SD, MASK_SD)
+DECLARE_INSN(fence, MATCH_FENCE, MASK_FENCE)
+DECLARE_INSN(fence_i, MATCH_FENCE_I, MASK_FENCE_I)
+DECLARE_INSN(mul, MATCH_MUL, MASK_MUL)
+DECLARE_INSN(mulh, MATCH_MULH, MASK_MULH)
+DECLARE_INSN(mulhsu, MATCH_MULHSU, MASK_MULHSU)
+DECLARE_INSN(mulhu, MATCH_MULHU, MASK_MULHU)
+DECLARE_INSN(div, MATCH_DIV, MASK_DIV)
+DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU)
+DECLARE_INSN(rem, MATCH_REM, MASK_REM)
+DECLARE_INSN(remu, MATCH_REMU, MASK_REMU)
+DECLARE_INSN(mulw, MATCH_MULW, MASK_MULW)
+DECLARE_INSN(divw, MATCH_DIVW, MASK_DIVW)
+DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW)
+DECLARE_INSN(remw, MATCH_REMW, MASK_REMW)
+DECLARE_INSN(remuw, MATCH_REMUW, MASK_REMUW)
+DECLARE_INSN(amoadd_w, MATCH_AMOADD_W, MASK_AMOADD_W)
+DECLARE_INSN(amoxor_w, MATCH_AMOXOR_W, MASK_AMOXOR_W)
+DECLARE_INSN(amoor_w, MATCH_AMOOR_W, MASK_AMOOR_W)
+DECLARE_INSN(amoand_w, MATCH_AMOAND_W, MASK_AMOAND_W)
+DECLARE_INSN(amomin_w, MATCH_AMOMIN_W, MASK_AMOMIN_W)
+DECLARE_INSN(amomax_w, MATCH_AMOMAX_W, MASK_AMOMAX_W)
+DECLARE_INSN(amominu_w, MATCH_AMOMINU_W, MASK_AMOMINU_W)
+DECLARE_INSN(amomaxu_w, MATCH_AMOMAXU_W, MASK_AMOMAXU_W)
+DECLARE_INSN(amoswap_w, MATCH_AMOSWAP_W, MASK_AMOSWAP_W)
+DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W)
+DECLARE_INSN(sc_w, MATCH_SC_W, MASK_SC_W)
+DECLARE_INSN(amoadd_d, MATCH_AMOADD_D, MASK_AMOADD_D)
+DECLARE_INSN(amoxor_d, MATCH_AMOXOR_D, MASK_AMOXOR_D)
+DECLARE_INSN(amoor_d, MATCH_AMOOR_D, MASK_AMOOR_D)
+DECLARE_INSN(amoand_d, MATCH_AMOAND_D, MASK_AMOAND_D)
+DECLARE_INSN(amomin_d, MATCH_AMOMIN_D, MASK_AMOMIN_D)
+DECLARE_INSN(amomax_d, MATCH_AMOMAX_D, MASK_AMOMAX_D)
+DECLARE_INSN(amominu_d, MATCH_AMOMINU_D, MASK_AMOMINU_D)
+DECLARE_INSN(amomaxu_d, MATCH_AMOMAXU_D, MASK_AMOMAXU_D)
+DECLARE_INSN(amoswap_d, MATCH_AMOSWAP_D, MASK_AMOSWAP_D)
+DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D)
+DECLARE_INSN(sc_d, MATCH_SC_D, MASK_SC_D)
+DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL)
+DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK)
+DECLARE_INSN(uret, MATCH_URET, MASK_URET)
+DECLARE_INSN(sret, MATCH_SRET, MASK_SRET)
+DECLARE_INSN(hret, MATCH_HRET, MASK_HRET)
+DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
+DECLARE_INSN(dret, MATCH_DRET, MASK_DRET)
+DECLARE_INSN(sfence_vm, MATCH_SFENCE_VM, MASK_SFENCE_VM)
+DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI)
+DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW)
+DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS)
+DECLARE_INSN(csrrc, MATCH_CSRRC, MASK_CSRRC)
+DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI)
+DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI)
+DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI)
+DECLARE_INSN(fadd_s, MATCH_FADD_S, MASK_FADD_S)
+DECLARE_INSN(fsub_s, MATCH_FSUB_S, MASK_FSUB_S)
+DECLARE_INSN(fmul_s, MATCH_FMUL_S, MASK_FMUL_S)
+DECLARE_INSN(fdiv_s, MATCH_FDIV_S, MASK_FDIV_S)
+DECLARE_INSN(fsgnj_s, MATCH_FSGNJ_S, MASK_FSGNJ_S)
+DECLARE_INSN(fsgnjn_s, MATCH_FSGNJN_S, MASK_FSGNJN_S)
+DECLARE_INSN(fsgnjx_s, MATCH_FSGNJX_S, MASK_FSGNJX_S)
+DECLARE_INSN(fmin_s, MATCH_FMIN_S, MASK_FMIN_S)
+DECLARE_INSN(fmax_s, MATCH_FMAX_S, MASK_FMAX_S)
+DECLARE_INSN(fsqrt_s, MATCH_FSQRT_S, MASK_FSQRT_S)
+DECLARE_INSN(fadd_d, MATCH_FADD_D, MASK_FADD_D)
+DECLARE_INSN(fsub_d, MATCH_FSUB_D, MASK_FSUB_D)
+DECLARE_INSN(fmul_d, MATCH_FMUL_D, MASK_FMUL_D)
+DECLARE_INSN(fdiv_d, MATCH_FDIV_D, MASK_FDIV_D)
+DECLARE_INSN(fsgnj_d, MATCH_FSGNJ_D, MASK_FSGNJ_D)
+DECLARE_INSN(fsgnjn_d, MATCH_FSGNJN_D, MASK_FSGNJN_D)
+DECLARE_INSN(fsgnjx_d, MATCH_FSGNJX_D, MASK_FSGNJX_D)
+DECLARE_INSN(fmin_d, MATCH_FMIN_D, MASK_FMIN_D)
+DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D)
+DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D)
+DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S)
+DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D)
+DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S)
+DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S)
+DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S)
+DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D)
+DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D)
+DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D)
+DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S)
+DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S)
+DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S)
+DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S)
+DECLARE_INSN(fmv_x_s, MATCH_FMV_X_S, MASK_FMV_X_S)
+DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S)
+DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D)
+DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D)
+DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D)
+DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D)
+DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D)
+DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D)
+DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W)
+DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU)
+DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L)
+DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU)
+DECLARE_INSN(fmv_s_x, MATCH_FMV_S_X, MASK_FMV_S_X)
+DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W)
+DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU)
+DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L)
+DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU)
+DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X)
+DECLARE_INSN(flw, MATCH_FLW, MASK_FLW)
+DECLARE_INSN(fld, MATCH_FLD, MASK_FLD)
+DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW)
+DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD)
+DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S)
+DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S)
+DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S)
+DECLARE_INSN(fnmadd_s, MATCH_FNMADD_S, MASK_FNMADD_S)
+DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D)
+DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D)
+DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D)
+DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D)
+DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP)
+DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP)
+DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR)
+DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR)
+DECLARE_INSN(c_ebreak, MATCH_C_EBREAK, MASK_C_EBREAK)
+DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD)
+DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
+DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW)
+DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
+DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
+DECLARE_INSN(c_addi4spn, MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN)
+DECLARE_INSN(c_fld, MATCH_C_FLD, MASK_C_FLD)
+DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW)
+DECLARE_INSN(c_flw, MATCH_C_FLW, MASK_C_FLW)
+DECLARE_INSN(c_fsd, MATCH_C_FSD, MASK_C_FSD)
+DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW)
+DECLARE_INSN(c_fsw, MATCH_C_FSW, MASK_C_FSW)
+DECLARE_INSN(c_addi, MATCH_C_ADDI, MASK_C_ADDI)
+DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL)
+DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI)
+DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI)
+DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI)
+DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI)
+DECLARE_INSN(c_andi, MATCH_C_ANDI, MASK_C_ANDI)
+DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB)
+DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR)
+DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR)
+DECLARE_INSN(c_and, MATCH_C_AND, MASK_C_AND)
+DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW)
+DECLARE_INSN(c_addw, MATCH_C_ADDW, MASK_C_ADDW)
+DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J)
+DECLARE_INSN(c_beqz, MATCH_C_BEQZ, MASK_C_BEQZ)
+DECLARE_INSN(c_bnez, MATCH_C_BNEZ, MASK_C_BNEZ)
+DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI)
+DECLARE_INSN(c_fldsp, MATCH_C_FLDSP, MASK_C_FLDSP)
+DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP)
+DECLARE_INSN(c_flwsp, MATCH_C_FLWSP, MASK_C_FLWSP)
+DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV)
+DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD)
+DECLARE_INSN(c_fsdsp, MATCH_C_FSDSP, MASK_C_FSDSP)
+DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP)
+DECLARE_INSN(c_fswsp, MATCH_C_FSWSP, MASK_C_FSWSP)
+DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0)
+DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1)
+DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2)
+DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD)
+DECLARE_INSN(custom0_rd_rs1, MATCH_CUSTOM0_RD_RS1, MASK_CUSTOM0_RD_RS1)
+DECLARE_INSN(custom0_rd_rs1_rs2, MATCH_CUSTOM0_RD_RS1_RS2, MASK_CUSTOM0_RD_RS1_RS2)
+DECLARE_INSN(custom1, MATCH_CUSTOM1, MASK_CUSTOM1)
+DECLARE_INSN(custom1_rs1, MATCH_CUSTOM1_RS1, MASK_CUSTOM1_RS1)
+DECLARE_INSN(custom1_rs1_rs2, MATCH_CUSTOM1_RS1_RS2, MASK_CUSTOM1_RS1_RS2)
+DECLARE_INSN(custom1_rd, MATCH_CUSTOM1_RD, MASK_CUSTOM1_RD)
+DECLARE_INSN(custom1_rd_rs1, MATCH_CUSTOM1_RD_RS1, MASK_CUSTOM1_RD_RS1)
+DECLARE_INSN(custom1_rd_rs1_rs2, MATCH_CUSTOM1_RD_RS1_RS2, MASK_CUSTOM1_RD_RS1_RS2)
+DECLARE_INSN(custom2, MATCH_CUSTOM2, MASK_CUSTOM2)
+DECLARE_INSN(custom2_rs1, MATCH_CUSTOM2_RS1, MASK_CUSTOM2_RS1)
+DECLARE_INSN(custom2_rs1_rs2, MATCH_CUSTOM2_RS1_RS2, MASK_CUSTOM2_RS1_RS2)
+DECLARE_INSN(custom2_rd, MATCH_CUSTOM2_RD, MASK_CUSTOM2_RD)
+DECLARE_INSN(custom2_rd_rs1, MATCH_CUSTOM2_RD_RS1, MASK_CUSTOM2_RD_RS1)
+DECLARE_INSN(custom2_rd_rs1_rs2, MATCH_CUSTOM2_RD_RS1_RS2, MASK_CUSTOM2_RD_RS1_RS2)
+DECLARE_INSN(custom3, MATCH_CUSTOM3, MASK_CUSTOM3)
+DECLARE_INSN(custom3_rs1, MATCH_CUSTOM3_RS1, MASK_CUSTOM3_RS1)
+DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2)
+DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD)
+DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1)
+DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2)
+#endif
+#ifdef DECLARE_CSR
+DECLARE_CSR(fflags, CSR_FFLAGS)
+DECLARE_CSR(frm, CSR_FRM)
+DECLARE_CSR(fcsr, CSR_FCSR)
+DECLARE_CSR(cycle, CSR_CYCLE)
+DECLARE_CSR(time, CSR_TIME)
+DECLARE_CSR(instret, CSR_INSTRET)
+DECLARE_CSR(hpmcounter3, CSR_HPMCOUNTER3)
+DECLARE_CSR(hpmcounter4, CSR_HPMCOUNTER4)
+DECLARE_CSR(hpmcounter5, CSR_HPMCOUNTER5)
+DECLARE_CSR(hpmcounter6, CSR_HPMCOUNTER6)
+DECLARE_CSR(hpmcounter7, CSR_HPMCOUNTER7)
+DECLARE_CSR(hpmcounter8, CSR_HPMCOUNTER8)
+DECLARE_CSR(hpmcounter9, CSR_HPMCOUNTER9)
+DECLARE_CSR(hpmcounter10, CSR_HPMCOUNTER10)
+DECLARE_CSR(hpmcounter11, CSR_HPMCOUNTER11)
+DECLARE_CSR(hpmcounter12, CSR_HPMCOUNTER12)
+DECLARE_CSR(hpmcounter13, CSR_HPMCOUNTER13)
+DECLARE_CSR(hpmcounter14, CSR_HPMCOUNTER14)
+DECLARE_CSR(hpmcounter15, CSR_HPMCOUNTER15)
+DECLARE_CSR(hpmcounter16, CSR_HPMCOUNTER16)
+DECLARE_CSR(hpmcounter17, CSR_HPMCOUNTER17)
+DECLARE_CSR(hpmcounter18, CSR_HPMCOUNTER18)
+DECLARE_CSR(hpmcounter19, CSR_HPMCOUNTER19)
+DECLARE_CSR(hpmcounter20, CSR_HPMCOUNTER20)
+DECLARE_CSR(hpmcounter21, CSR_HPMCOUNTER21)
+DECLARE_CSR(hpmcounter22, CSR_HPMCOUNTER22)
+DECLARE_CSR(hpmcounter23, CSR_HPMCOUNTER23)
+DECLARE_CSR(hpmcounter24, CSR_HPMCOUNTER24)
+DECLARE_CSR(hpmcounter25, CSR_HPMCOUNTER25)
+DECLARE_CSR(hpmcounter26, CSR_HPMCOUNTER26)
+DECLARE_CSR(hpmcounter27, CSR_HPMCOUNTER27)
+DECLARE_CSR(hpmcounter28, CSR_HPMCOUNTER28)
+DECLARE_CSR(hpmcounter29, CSR_HPMCOUNTER29)
+DECLARE_CSR(hpmcounter30, CSR_HPMCOUNTER30)
+DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31)
+DECLARE_CSR(sstatus, CSR_SSTATUS)
+DECLARE_CSR(sie, CSR_SIE)
+DECLARE_CSR(stvec, CSR_STVEC)
+DECLARE_CSR(sscratch, CSR_SSCRATCH)
+DECLARE_CSR(sepc, CSR_SEPC)
+DECLARE_CSR(scause, CSR_SCAUSE)
+DECLARE_CSR(sbadaddr, CSR_SBADADDR)
+DECLARE_CSR(sip, CSR_SIP)
+DECLARE_CSR(sptbr, CSR_SPTBR)
+DECLARE_CSR(mstatus, CSR_MSTATUS)
+DECLARE_CSR(misa, CSR_MISA)
+DECLARE_CSR(medeleg, CSR_MEDELEG)
+DECLARE_CSR(mideleg, CSR_MIDELEG)
+DECLARE_CSR(mie, CSR_MIE)
+DECLARE_CSR(mtvec, CSR_MTVEC)
+DECLARE_CSR(mscratch, CSR_MSCRATCH)
+DECLARE_CSR(mepc, CSR_MEPC)
+DECLARE_CSR(mcause, CSR_MCAUSE)
+DECLARE_CSR(mbadaddr, CSR_MBADADDR)
+DECLARE_CSR(mip, CSR_MIP)
+DECLARE_CSR(tselect, CSR_TSELECT)
+DECLARE_CSR(tdata1, CSR_TDATA1)
+DECLARE_CSR(tdata2, CSR_TDATA2)
+DECLARE_CSR(tdata3, CSR_TDATA3)
+DECLARE_CSR(dcsr, CSR_DCSR)
+DECLARE_CSR(dpc, CSR_DPC)
+DECLARE_CSR(dscratch, CSR_DSCRATCH)
+DECLARE_CSR(mcycle, CSR_MCYCLE)
+DECLARE_CSR(minstret, CSR_MINSTRET)
+DECLARE_CSR(mhpmcounter3, CSR_MHPMCOUNTER3)
+DECLARE_CSR(mhpmcounter4, CSR_MHPMCOUNTER4)
+DECLARE_CSR(mhpmcounter5, CSR_MHPMCOUNTER5)
+DECLARE_CSR(mhpmcounter6, CSR_MHPMCOUNTER6)
+DECLARE_CSR(mhpmcounter7, CSR_MHPMCOUNTER7)
+DECLARE_CSR(mhpmcounter8, CSR_MHPMCOUNTER8)
+DECLARE_CSR(mhpmcounter9, CSR_MHPMCOUNTER9)
+DECLARE_CSR(mhpmcounter10, CSR_MHPMCOUNTER10)
+DECLARE_CSR(mhpmcounter11, CSR_MHPMCOUNTER11)
+DECLARE_CSR(mhpmcounter12, CSR_MHPMCOUNTER12)
+DECLARE_CSR(mhpmcounter13, CSR_MHPMCOUNTER13)
+DECLARE_CSR(mhpmcounter14, CSR_MHPMCOUNTER14)
+DECLARE_CSR(mhpmcounter15, CSR_MHPMCOUNTER15)
+DECLARE_CSR(mhpmcounter16, CSR_MHPMCOUNTER16)
+DECLARE_CSR(mhpmcounter17, CSR_MHPMCOUNTER17)
+DECLARE_CSR(mhpmcounter18, CSR_MHPMCOUNTER18)
+DECLARE_CSR(mhpmcounter19, CSR_MHPMCOUNTER19)
+DECLARE_CSR(mhpmcounter20, CSR_MHPMCOUNTER20)
+DECLARE_CSR(mhpmcounter21, CSR_MHPMCOUNTER21)
+DECLARE_CSR(mhpmcounter22, CSR_MHPMCOUNTER22)
+DECLARE_CSR(mhpmcounter23, CSR_MHPMCOUNTER23)
+DECLARE_CSR(mhpmcounter24, CSR_MHPMCOUNTER24)
+DECLARE_CSR(mhpmcounter25, CSR_MHPMCOUNTER25)
+DECLARE_CSR(mhpmcounter26, CSR_MHPMCOUNTER26)
+DECLARE_CSR(mhpmcounter27, CSR_MHPMCOUNTER27)
+DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28)
+DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29)
+DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30)
+DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31)
+DECLARE_CSR(mucounteren, CSR_MUCOUNTEREN)
+DECLARE_CSR(mscounteren, CSR_MSCOUNTEREN)
+DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3)
+DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4)
+DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5)
+DECLARE_CSR(mhpmevent6, CSR_MHPMEVENT6)
+DECLARE_CSR(mhpmevent7, CSR_MHPMEVENT7)
+DECLARE_CSR(mhpmevent8, CSR_MHPMEVENT8)
+DECLARE_CSR(mhpmevent9, CSR_MHPMEVENT9)
+DECLARE_CSR(mhpmevent10, CSR_MHPMEVENT10)
+DECLARE_CSR(mhpmevent11, CSR_MHPMEVENT11)
+DECLARE_CSR(mhpmevent12, CSR_MHPMEVENT12)
+DECLARE_CSR(mhpmevent13, CSR_MHPMEVENT13)
+DECLARE_CSR(mhpmevent14, CSR_MHPMEVENT14)
+DECLARE_CSR(mhpmevent15, CSR_MHPMEVENT15)
+DECLARE_CSR(mhpmevent16, CSR_MHPMEVENT16)
+DECLARE_CSR(mhpmevent17, CSR_MHPMEVENT17)
+DECLARE_CSR(mhpmevent18, CSR_MHPMEVENT18)
+DECLARE_CSR(mhpmevent19, CSR_MHPMEVENT19)
+DECLARE_CSR(mhpmevent20, CSR_MHPMEVENT20)
+DECLARE_CSR(mhpmevent21, CSR_MHPMEVENT21)
+DECLARE_CSR(mhpmevent22, CSR_MHPMEVENT22)
+DECLARE_CSR(mhpmevent23, CSR_MHPMEVENT23)
+DECLARE_CSR(mhpmevent24, CSR_MHPMEVENT24)
+DECLARE_CSR(mhpmevent25, CSR_MHPMEVENT25)
+DECLARE_CSR(mhpmevent26, CSR_MHPMEVENT26)
+DECLARE_CSR(mhpmevent27, CSR_MHPMEVENT27)
+DECLARE_CSR(mhpmevent28, CSR_MHPMEVENT28)
+DECLARE_CSR(mhpmevent29, CSR_MHPMEVENT29)
+DECLARE_CSR(mhpmevent30, CSR_MHPMEVENT30)
+DECLARE_CSR(mhpmevent31, CSR_MHPMEVENT31)
+DECLARE_CSR(mvendorid, CSR_MVENDORID)
+DECLARE_CSR(marchid, CSR_MARCHID)
+DECLARE_CSR(mimpid, CSR_MIMPID)
+DECLARE_CSR(mhartid, CSR_MHARTID)
+DECLARE_CSR(cycleh, CSR_CYCLEH)
+DECLARE_CSR(timeh, CSR_TIMEH)
+DECLARE_CSR(instreth, CSR_INSTRETH)
+DECLARE_CSR(hpmcounter3h, CSR_HPMCOUNTER3H)
+DECLARE_CSR(hpmcounter4h, CSR_HPMCOUNTER4H)
+DECLARE_CSR(hpmcounter5h, CSR_HPMCOUNTER5H)
+DECLARE_CSR(hpmcounter6h, CSR_HPMCOUNTER6H)
+DECLARE_CSR(hpmcounter7h, CSR_HPMCOUNTER7H)
+DECLARE_CSR(hpmcounter8h, CSR_HPMCOUNTER8H)
+DECLARE_CSR(hpmcounter9h, CSR_HPMCOUNTER9H)
+DECLARE_CSR(hpmcounter10h, CSR_HPMCOUNTER10H)
+DECLARE_CSR(hpmcounter11h, CSR_HPMCOUNTER11H)
+DECLARE_CSR(hpmcounter12h, CSR_HPMCOUNTER12H)
+DECLARE_CSR(hpmcounter13h, CSR_HPMCOUNTER13H)
+DECLARE_CSR(hpmcounter14h, CSR_HPMCOUNTER14H)
+DECLARE_CSR(hpmcounter15h, CSR_HPMCOUNTER15H)
+DECLARE_CSR(hpmcounter16h, CSR_HPMCOUNTER16H)
+DECLARE_CSR(hpmcounter17h, CSR_HPMCOUNTER17H)
+DECLARE_CSR(hpmcounter18h, CSR_HPMCOUNTER18H)
+DECLARE_CSR(hpmcounter19h, CSR_HPMCOUNTER19H)
+DECLARE_CSR(hpmcounter20h, CSR_HPMCOUNTER20H)
+DECLARE_CSR(hpmcounter21h, CSR_HPMCOUNTER21H)
+DECLARE_CSR(hpmcounter22h, CSR_HPMCOUNTER22H)
+DECLARE_CSR(hpmcounter23h, CSR_HPMCOUNTER23H)
+DECLARE_CSR(hpmcounter24h, CSR_HPMCOUNTER24H)
+DECLARE_CSR(hpmcounter25h, CSR_HPMCOUNTER25H)
+DECLARE_CSR(hpmcounter26h, CSR_HPMCOUNTER26H)
+DECLARE_CSR(hpmcounter27h, CSR_HPMCOUNTER27H)
+DECLARE_CSR(hpmcounter28h, CSR_HPMCOUNTER28H)
+DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H)
+DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H)
+DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H)
+DECLARE_CSR(mcycleh, CSR_MCYCLEH)
+DECLARE_CSR(minstreth, CSR_MINSTRETH)
+DECLARE_CSR(mhpmcounter3h, CSR_MHPMCOUNTER3H)
+DECLARE_CSR(mhpmcounter4h, CSR_MHPMCOUNTER4H)
+DECLARE_CSR(mhpmcounter5h, CSR_MHPMCOUNTER5H)
+DECLARE_CSR(mhpmcounter6h, CSR_MHPMCOUNTER6H)
+DECLARE_CSR(mhpmcounter7h, CSR_MHPMCOUNTER7H)
+DECLARE_CSR(mhpmcounter8h, CSR_MHPMCOUNTER8H)
+DECLARE_CSR(mhpmcounter9h, CSR_MHPMCOUNTER9H)
+DECLARE_CSR(mhpmcounter10h, CSR_MHPMCOUNTER10H)
+DECLARE_CSR(mhpmcounter11h, CSR_MHPMCOUNTER11H)
+DECLARE_CSR(mhpmcounter12h, CSR_MHPMCOUNTER12H)
+DECLARE_CSR(mhpmcounter13h, CSR_MHPMCOUNTER13H)
+DECLARE_CSR(mhpmcounter14h, CSR_MHPMCOUNTER14H)
+DECLARE_CSR(mhpmcounter15h, CSR_MHPMCOUNTER15H)
+DECLARE_CSR(mhpmcounter16h, CSR_MHPMCOUNTER16H)
+DECLARE_CSR(mhpmcounter17h, CSR_MHPMCOUNTER17H)
+DECLARE_CSR(mhpmcounter18h, CSR_MHPMCOUNTER18H)
+DECLARE_CSR(mhpmcounter19h, CSR_MHPMCOUNTER19H)
+DECLARE_CSR(mhpmcounter20h, CSR_MHPMCOUNTER20H)
+DECLARE_CSR(mhpmcounter21h, CSR_MHPMCOUNTER21H)
+DECLARE_CSR(mhpmcounter22h, CSR_MHPMCOUNTER22H)
+DECLARE_CSR(mhpmcounter23h, CSR_MHPMCOUNTER23H)
+DECLARE_CSR(mhpmcounter24h, CSR_MHPMCOUNTER24H)
+DECLARE_CSR(mhpmcounter25h, CSR_MHPMCOUNTER25H)
+DECLARE_CSR(mhpmcounter26h, CSR_MHPMCOUNTER26H)
+DECLARE_CSR(mhpmcounter27h, CSR_MHPMCOUNTER27H)
+DECLARE_CSR(mhpmcounter28h, CSR_MHPMCOUNTER28H)
+DECLARE_CSR(mhpmcounter29h, CSR_MHPMCOUNTER29H)
+DECLARE_CSR(mhpmcounter30h, CSR_MHPMCOUNTER30H)
+DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H)
+#endif
+#ifdef DECLARE_CAUSE
+DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH)
+DECLARE_CAUSE("fault fetch", CAUSE_FAULT_FETCH)
+DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION)
+DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT)
+DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD)
+DECLARE_CAUSE("fault load", CAUSE_FAULT_LOAD)
+DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE)
+DECLARE_CAUSE("fault store", CAUSE_FAULT_STORE)
+DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL)
+DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL)
+DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL)
+DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL)
+#endif
diff --git a/fw/fe310/bsp/include/platform.h b/fw/fe310/bsp/include/platform.h
new file mode 100644
index 0000000..46ce61c
--- /dev/null
+++ b/fw/fe310/bsp/include/platform.h
@@ -0,0 +1,140 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_PLATFORM_H
+#define _SIFIVE_PLATFORM_H
+
+// Some things missing from the official encoding.h
+#define MCAUSE_INT 0x80000000
+#define MCAUSE_CAUSE 0x7FFFFFFF
+
+#include <stdint.h>
+
+#include "sifive/const.h"
+#include "sifive/devices/aon.h"
+#include "sifive/devices/clint.h"
+#include "sifive/devices/gpio.h"
+#include "sifive/devices/otp.h"
+#include "sifive/devices/plic.h"
+#include "sifive/devices/prci.h"
+#include "sifive/devices/pwm.h"
+#include "sifive/devices/spi.h"
+#include "sifive/devices/i2c.h"
+#include "sifive/devices/uart.h"
+
+/****************************************************************************
+ * Platform definitions
+ *****************************************************************************/
+
+// Memory map
+#define MASKROM_MEM_ADDR _AC(0x00001000,UL)
+#define TRAPVEC_TABLE_CTRL_ADDR _AC(0x00001010,UL)
+#define OTP_MEM_ADDR _AC(0x00020000,UL)
+#define CLINT_CTRL_ADDR _AC(0x02000000,UL)
+#define PLIC_CTRL_ADDR _AC(0x0C000000,UL)
+#define AON_CTRL_ADDR _AC(0x10000000,UL)
+#define PRCI_CTRL_ADDR _AC(0x10008000,UL)
+#define OTP_CTRL_ADDR _AC(0x10010000,UL)
+#define GPIO_CTRL_ADDR _AC(0x10012000,UL)
+#define UART0_CTRL_ADDR _AC(0x10013000,UL)
+#define SPI0_CTRL_ADDR _AC(0x10014000,UL)
+#define PWM0_CTRL_ADDR _AC(0x10015000,UL)
+#define I2C0_CTRL_ADDR _AC(0x10016000,UL)
+#define UART1_CTRL_ADDR _AC(0x10023000,UL)
+#define SPI1_CTRL_ADDR _AC(0x10024000,UL)
+#define PWM1_CTRL_ADDR _AC(0x10025000,UL)
+#define SPI2_CTRL_ADDR _AC(0x10034000,UL)
+#define PWM2_CTRL_ADDR _AC(0x10035000,UL)
+#define SPI0_MEM_ADDR _AC(0x20000000,UL)
+#define MEM_CTRL_ADDR _AC(0x80000000,UL)
+
+// IOF masks
+#define IOF0_SPI1_MASK _AC(0x000007FC,UL)
+#define SPI11_NUM_SS (4)
+#define IOF_SPI1_SS0 (2u)
+#define IOF_SPI1_SS1 (8u)
+#define IOF_SPI1_SS2 (9u)
+#define IOF_SPI1_SS3 (10u)
+#define IOF_SPI1_MOSI (3u)
+#define IOF_SPI1_MISO (4u)
+#define IOF_SPI1_SCK (5u)
+#define IOF_SPI1_DQ0 (3u)
+#define IOF_SPI1_DQ1 (4u)
+#define IOF_SPI1_DQ2 (6u)
+#define IOF_SPI1_DQ3 (7u)
+
+#define IOF0_SPI2_MASK _AC(0xFC000000,UL)
+#define SPI2_NUM_SS (1)
+#define IOF_SPI2_SS0 (26u)
+#define IOF_SPI2_MOSI (27u)
+#define IOF_SPI2_MISO (28u)
+#define IOF_SPI2_SCK (29u)
+#define IOF_SPI2_DQ0 (27u)
+#define IOF_SPI2_DQ1 (28u)
+#define IOF_SPI2_DQ2 (30u)
+#define IOF_SPI2_DQ3 (31u)
+
+#define IOF0_I2C0_MASK _AC(0x00003000,UL)
+#define IOF_I2C0_SCL (13u)
+#define IOF_I2C0_SDA (12u)
+
+#define IOF0_UART0_MASK _AC(0x00030000,UL)
+#define IOF_UART0_RX (16u)
+#define IOF_UART0_TX (17u)
+
+#define IOF0_UART1_MASK _AC(0x00840000,UL)
+#define IOF_UART1_RX (23u)
+#define IOF_UART1_TX (18u)
+
+#define IOF1_PWM0_MASK _AC(0x0000000F,UL)
+#define IOF1_PWM1_MASK _AC(0x00780000,UL)
+#define IOF1_PWM2_MASK _AC(0x00003C00,UL)
+
+// Interrupt numbers
+#define INT_RESERVED 0
+#define INT_WDOGCMP 1
+#define INT_RTCCMP 2
+#define INT_UART0_BASE 3
+#define INT_UART1_BASE 4
+#define INT_SPI0_BASE 5
+#define INT_SPI1_BASE 6
+#define INT_SPI2_BASE 7
+#define INT_GPIO_BASE 8
+#define INT_PWM0_BASE 40
+#define INT_PWM1_BASE 44
+#define INT_PWM2_BASE 48
+#define INT_I2C0_BASE 52
+
+// Helper functions
+#define _REG8(p, i) (*(volatile uint8_t *) ((p) + (i)))
+#define _REG16(p, i) (*(volatile uint16_t *) ((p) + (i)))
+#define _REG32(p, i) (*(volatile uint32_t *) ((p) + (i)))
+#define _REG8P(p, i) ((volatile uint8_t *) ((p) + (i)))
+#define _REG16P(p, i) ((volatile uint16_t *) ((p) + (i)))
+#define _REG32P(p, i) ((volatile uint32_t *) ((p) + (i)))
+#define AON_REG(offset) _REG32(AON_CTRL_ADDR, offset)
+#define CLINT_REG(offset) _REG32(CLINT_CTRL_ADDR, offset)
+#define GPIO_REG(offset) _REG32(GPIO_CTRL_ADDR, offset)
+#define OTP_REG(offset) _REG32(OTP_CTRL_ADDR, offset)
+#define PLIC_REG(offset) _REG32(PLIC_CTRL_ADDR, offset)
+#define PRCI_REG(offset) _REG32(PRCI_CTRL_ADDR, offset)
+#define PWM0_REG(offset) _REG32(PWM0_CTRL_ADDR, offset)
+#define PWM1_REG(offset) _REG32(PWM1_CTRL_ADDR, offset)
+#define PWM2_REG(offset) _REG32(PWM2_CTRL_ADDR, offset)
+#define SPI0_REG(offset) _REG32(SPI0_CTRL_ADDR, offset)
+#define SPI1_REG(offset) _REG32(SPI1_CTRL_ADDR, offset)
+#define SPI2_REG(offset) _REG32(SPI2_CTRL_ADDR, offset)
+#define I2C0_REG(offset) _REG32(I2C0_CTRL_ADDR, offset)
+#define I2C0_REGB(offset) _REG8(I2C0_CTRL_ADDR, offset)
+#define UART0_REG(offset) _REG32(UART0_CTRL_ADDR, offset)
+#define UART1_REG(offset) _REG32(UART1_CTRL_ADDR, offset)
+
+// Misc
+
+#define PLIC_NUM_INTERRUPTS 52
+#define PLIC_NUM_PRIORITIES 7
+
+#define RTC_FREQ 32768
+
+void metal_init(void);
+
+#endif /* _SIFIVE_PLATFORM_H */
diff --git a/fw/fe310/bsp/include/sifive/bits.h b/fw/fe310/bsp/include/sifive/bits.h
new file mode 100644
index 0000000..bfe656f
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/bits.h
@@ -0,0 +1,36 @@
+// See LICENSE for license details.
+#ifndef _RISCV_BITS_H
+#define _RISCV_BITS_H
+
+#define likely(x) __builtin_expect((x), 1)
+#define unlikely(x) __builtin_expect((x), 0)
+
+#define ROUNDUP(a, b) ((((a)-1)/(b)+1)*(b))
+#define ROUNDDOWN(a, b) ((a)/(b)*(b))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define CLAMP(a, lo, hi) MIN(MAX(a, lo), hi)
+
+#define EXTRACT_FIELD(val, which) (((val) & (which)) / ((which) & ~((which)-1)))
+#define INSERT_FIELD(val, which, fieldval) (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1))))
+
+#define STR(x) XSTR(x)
+#define XSTR(x) #x
+
+#if __riscv_xlen == 64
+# define SLL32 sllw
+# define STORE sd
+# define LOAD ld
+# define LWU lwu
+# define LOG_REGBYTES 3
+#else
+# define SLL32 sll
+# define STORE sw
+# define LOAD lw
+# define LWU lw
+# define LOG_REGBYTES 2
+#endif
+#define REGBYTES (1 << LOG_REGBYTES)
+
+#endif
diff --git a/fw/fe310/bsp/include/sifive/const.h b/fw/fe310/bsp/include/sifive/const.h
new file mode 100644
index 0000000..8dcffbb
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/const.h
@@ -0,0 +1,18 @@
+// See LICENSE for license details.
+/* Derived from <linux/const.h> */
+
+#ifndef _SIFIVE_CONST_H
+#define _SIFIVE_CONST_H
+
+#ifdef __ASSEMBLER__
+#define _AC(X,Y) X
+#define _AT(T,X) X
+#else
+#define _AC(X,Y) (X##Y)
+#define _AT(T,X) ((T)(X))
+#endif /* !__ASSEMBLER__*/
+
+#define _BITUL(x) (_AC(1,UL) << (x))
+#define _BITULL(x) (_AC(1,ULL) << (x))
+
+#endif /* _SIFIVE_CONST_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/aon.h b/fw/fe310/bsp/include/sifive/devices/aon.h
new file mode 100644
index 0000000..4a3332c
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/aon.h
@@ -0,0 +1,89 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_AON_H
+#define _SIFIVE_AON_H
+
+/* Register offsets */
+
+#define AON_WDOGCFG 0x000
+#define AON_WDOGCOUNT 0x008
+#define AON_WDOGS 0x010
+#define AON_WDOGFEED 0x018
+#define AON_WDOGKEY 0x01C
+#define AON_WDOGCMP 0x020
+
+#define AON_RTCCFG 0x040
+#define AON_RTCLO 0x048
+#define AON_RTCHI 0x04C
+#define AON_RTCS 0x050
+#define AON_RTCCMP 0x060
+
+#define AON_BACKUP0 0x080
+#define AON_BACKUP1 0x084
+#define AON_BACKUP2 0x088
+#define AON_BACKUP3 0x08C
+#define AON_BACKUP4 0x090
+#define AON_BACKUP5 0x094
+#define AON_BACKUP6 0x098
+#define AON_BACKUP7 0x09C
+#define AON_BACKUP8 0x0A0
+#define AON_BACKUP9 0x0A4
+#define AON_BACKUP10 0x0A8
+#define AON_BACKUP11 0x0AC
+#define AON_BACKUP12 0x0B0
+#define AON_BACKUP13 0x0B4
+#define AON_BACKUP14 0x0B8
+#define AON_BACKUP15 0x0BC
+
+#define AON_PMUWAKEUPI0 0x100
+#define AON_PMUWAKEUPI1 0x104
+#define AON_PMUWAKEUPI2 0x108
+#define AON_PMUWAKEUPI3 0x10C
+#define AON_PMUWAKEUPI4 0x110
+#define AON_PMUWAKEUPI5 0x114
+#define AON_PMUWAKEUPI6 0x118
+#define AON_PMUWAKEUPI7 0x11C
+#define AON_PMUSLEEPI0 0x120
+#define AON_PMUSLEEPI1 0x124
+#define AON_PMUSLEEPI2 0x128
+#define AON_PMUSLEEPI3 0x12C
+#define AON_PMUSLEEPI4 0x130
+#define AON_PMUSLEEPI5 0x134
+#define AON_PMUSLEEPI6 0x138
+#define AON_PMUSLEEPI7 0x13C
+#define AON_PMUIE 0x140
+#define AON_PMUCAUSE 0x144
+#define AON_PMUSLEEP 0x148
+#define AON_PMUKEY 0x14C
+
+#define AON_LFROSC 0x070
+
+/* Constants */
+
+#define AON_WDOGKEY_VALUE 0x51F15E
+#define AON_WDOGFEED_VALUE 0xD09F00D
+
+#define AON_WDOGCFG_SCALE 0x0000000F
+#define AON_WDOGCFG_RSTEN 0x00000100
+#define AON_WDOGCFG_ZEROCMP 0x00000200
+#define AON_WDOGCFG_ENALWAYS 0x00001000
+#define AON_WDOGCFG_ENCOREAWAKE 0x00002000
+#define AON_WDOGCFG_CMPIP 0x10000000
+
+#define AON_RTCCFG_SCALE 0x0000000F
+#define AON_RTCCFG_ENALWAYS 0x00001000
+#define AON_RTCCFG_CMPIP 0x10000000
+
+#define AON_WAKEUPCAUSE_RESET 0x00
+#define AON_WAKEUPCAUSE_RTC 0x01
+#define AON_WAKEUPCAUSE_DWAKEUP 0x02
+#define AON_WAKEUPCAUSE_AWAKEUP 0x03
+
+#define AON_RESETCAUSE_POWERON 0x0000
+#define AON_RESETCAUSE_EXTERNAL 0x0100
+#define AON_RESETCAUSE_WATCHDOG 0x0200
+
+#define AON_PMUCAUSE_WAKEUPCAUSE 0x00FF
+#define AON_PMUCAUSE_RESETCAUSE 0xFF00
+
+#endif /* _SIFIVE_AON_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/clint.h b/fw/fe310/bsp/include/sifive/devices/clint.h
new file mode 100644
index 0000000..617b657
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/clint.h
@@ -0,0 +1,14 @@
+// See LICENSE for license details
+
+#ifndef _SIFIVE_CLINT_H
+#define _SIFIVE_CLINT_H
+
+
+#define CLINT_MSIP 0x0000
+#define CLINT_MSIP_size 0x4
+#define CLINT_MTIMECMP 0x4000
+#define CLINT_MTIMECMP_size 0x8
+#define CLINT_MTIME 0xBFF8
+#define CLINT_MTIME_size 0x8
+
+#endif /* _SIFIVE_CLINT_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/gpio.h b/fw/fe310/bsp/include/sifive/devices/gpio.h
new file mode 100644
index 0000000..835ac83
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/gpio.h
@@ -0,0 +1,24 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_GPIO_H
+#define _SIFIVE_GPIO_H
+
+#define GPIO_INPUT_VAL (0x00)
+#define GPIO_INPUT_EN (0x04)
+#define GPIO_OUTPUT_EN (0x08)
+#define GPIO_OUTPUT_VAL (0x0C)
+#define GPIO_PULLUP_EN (0x10)
+#define GPIO_DRIVE (0x14)
+#define GPIO_RISE_IE (0x18)
+#define GPIO_RISE_IP (0x1C)
+#define GPIO_FALL_IE (0x20)
+#define GPIO_FALL_IP (0x24)
+#define GPIO_HIGH_IE (0x28)
+#define GPIO_HIGH_IP (0x2C)
+#define GPIO_LOW_IE (0x30)
+#define GPIO_LOW_IP (0x34)
+#define GPIO_IOF_EN (0x38)
+#define GPIO_IOF_SEL (0x3C)
+#define GPIO_OUTPUT_XOR (0x40)
+
+#endif /* _SIFIVE_GPIO_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/i2c.h b/fw/fe310/bsp/include/sifive/devices/i2c.h
new file mode 100644
index 0000000..900e238
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/i2c.h
@@ -0,0 +1,34 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_I2C_H
+#define _SIFIVE_I2C_H
+
+/* Register offsets */
+
+#define I2C_PRESCALE_LOW 0x00
+#define I2C_PRESCALE_HIGH 0x04
+#define I2C_CONTROL 0x08
+#define I2C_TRANSMIT 0x0c
+#define I2C_RECEIVE 0x0c
+#define I2C_COMMAND 0x10
+#define I2C_STATUS 0x10
+
+/* Constants */
+
+#define I2C_CONTROL_EN (1UL << 7)
+#define I2C_CONTROL_IE (1UL << 6)
+#define I2C_WRITE (0UL << 0)
+#define I2C_READ (1UL << 0)
+#define I2C_CMD_START (1UL << 7)
+#define I2C_CMD_STOP (1UL << 6)
+#define I2C_CMD_READ (1UL << 5)
+#define I2C_CMD_WRITE (1UL << 4)
+#define I2C_CMD_ACK (1UL << 3)
+#define I2C_CMD_IACK (1UL << 0)
+#define I2C_STATUS_RXACK (1UL << 7)
+#define I2C_STATUS_BUSY (1UL << 6)
+#define I2C_STATUS_AL (1UL << 5)
+#define I2C_STATUS_TIP (1UL << 1)
+#define I2C_STATUS_IP (1UL << 0)
+
+#endif /* _SIFIVE_I2C_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/otp.h b/fw/fe310/bsp/include/sifive/devices/otp.h
new file mode 100644
index 0000000..93833e2
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/otp.h
@@ -0,0 +1,23 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_OTP_H
+#define _SIFIVE_OTP_H
+
+/* Register offsets */
+
+#define OTP_LOCK 0x00
+#define OTP_CK 0x04
+#define OTP_OE 0x08
+#define OTP_SEL 0x0C
+#define OTP_WE 0x10
+#define OTP_MR 0x14
+#define OTP_MRR 0x18
+#define OTP_MPP 0x1C
+#define OTP_VRREN 0x20
+#define OTP_VPPEN 0x24
+#define OTP_A 0x28
+#define OTP_D 0x2C
+#define OTP_Q 0x30
+#define OTP_READ_TIMINGS 0x34
+
+#endif
diff --git a/fw/fe310/bsp/include/sifive/devices/plic.h b/fw/fe310/bsp/include/sifive/devices/plic.h
new file mode 100644
index 0000000..e1ca5d6
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/plic.h
@@ -0,0 +1,31 @@
+// See LICENSE for license details.
+
+#ifndef PLIC_H
+#define PLIC_H
+
+#include <sifive/const.h>
+
+// 32 bits per source
+#define PLIC_PRIORITY_OFFSET _AC(0x0000,UL)
+#define PLIC_PRIORITY_SHIFT_PER_SOURCE 2
+// 1 bit per source (1 address)
+#define PLIC_PENDING_OFFSET _AC(0x1000,UL)
+#define PLIC_PENDING_SHIFT_PER_SOURCE 0
+
+//0x80 per target
+#define PLIC_ENABLE_OFFSET _AC(0x2000,UL)
+#define PLIC_ENABLE_SHIFT_PER_TARGET 7
+
+
+#define PLIC_THRESHOLD_OFFSET _AC(0x200000,UL)
+#define PLIC_CLAIM_OFFSET _AC(0x200004,UL)
+#define PLIC_THRESHOLD_SHIFT_PER_TARGET 12
+#define PLIC_CLAIM_SHIFT_PER_TARGET 12
+
+#define PLIC_MAX_SOURCE 1023
+#define PLIC_SOURCE_MASK 0x3FF
+
+#define PLIC_MAX_TARGET 15871
+#define PLIC_TARGET_MASK 0x3FFF
+
+#endif /* PLIC_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/prci.h b/fw/fe310/bsp/include/sifive/devices/prci.h
new file mode 100644
index 0000000..d46da08
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/prci.h
@@ -0,0 +1,56 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_PRCI_H
+#define _SIFIVE_PRCI_H
+
+/* Register offsets */
+
+#define PRCI_HFROSCCFG (0x0000)
+#define PRCI_HFXOSCCFG (0x0004)
+#define PRCI_PLLCFG (0x0008)
+#define PRCI_PLLDIV (0x000C)
+#define PRCI_PROCMONCFG (0x00F0)
+
+/* Fields */
+#define ROSC_DIV(x) (((x) & 0x2F) << 0 )
+#define ROSC_TRIM(x) (((x) & 0x1F) << 16)
+#define ROSC_EN(x) (((x) & 0x1 ) << 30)
+#define ROSC_RDY(x) (((x) & 0x1 ) << 31)
+
+#define XOSC_EN(x) (((x) & 0x1) << 30)
+#define XOSC_RDY(x) (((x) & 0x1) << 31)
+
+#define PLL_R(x) (((x) & 0x7) << 0)
+// single reserved bit for F LSB.
+#define PLL_F(x) (((x) & 0x3F) << 4)
+#define PLL_Q(x) (((x) & 0x3) << 10)
+#define PLL_SEL(x) (((x) & 0x1) << 16)
+#define PLL_REFSEL(x) (((x) & 0x1) << 17)
+#define PLL_BYPASS(x) (((x) & 0x1) << 18)
+#define PLL_LOCK(x) (((x) & 0x1) << 31)
+
+#define PLL_R_default 0x1
+#define PLL_F_default 0x1F
+#define PLL_Q_default 0x3
+
+#define PLL_REFSEL_HFROSC 0x0
+#define PLL_REFSEL_HFXOSC 0x1
+
+#define PLL_SEL_HFROSC 0x0
+#define PLL_SEL_PLL 0x1
+
+#define PLL_FINAL_DIV(x) (((x) & 0x3F) << 0)
+#define PLL_FINAL_DIV_BY_1(x) (((x) & 0x1 ) << 8)
+
+#define PROCMON_DIV(x) (((x) & 0x1F) << 0)
+#define PROCMON_TRIM(x) (((x) & 0x1F) << 8)
+#define PROCMON_EN(x) (((x) & 0x1) << 16)
+#define PROCMON_SEL(x) (((x) & 0x3) << 24)
+#define PROCMON_NT_EN(x) (((x) & 0x1) << 28)
+
+#define PROCMON_SEL_HFCLK 0
+#define PROCMON_SEL_HFXOSCIN 1
+#define PROCMON_SEL_PLLOUTDIV 2
+#define PROCMON_SEL_PROCMON 3
+
+#endif // _SIFIVE_PRCI_H
diff --git a/fw/fe310/bsp/include/sifive/devices/pwm.h b/fw/fe310/bsp/include/sifive/devices/pwm.h
new file mode 100644
index 0000000..067889a
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/pwm.h
@@ -0,0 +1,37 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_PWM_H
+#define _SIFIVE_PWM_H
+
+/* Register offsets */
+
+#define PWM_CFG 0x00
+#define PWM_COUNT 0x08
+#define PWM_S 0x10
+#define PWM_CMP0 0x20
+#define PWM_CMP1 0x24
+#define PWM_CMP2 0x28
+#define PWM_CMP3 0x2C
+
+/* Constants */
+
+#define PWM_CFG_SCALE 0x0000000F
+#define PWM_CFG_STICKY 0x00000100
+#define PWM_CFG_ZEROCMP 0x00000200
+#define PWM_CFG_DEGLITCH 0x00000400
+#define PWM_CFG_ENALWAYS 0x00001000
+#define PWM_CFG_ONESHOT 0x00002000
+#define PWM_CFG_CMP0CENTER 0x00010000
+#define PWM_CFG_CMP1CENTER 0x00020000
+#define PWM_CFG_CMP2CENTER 0x00040000
+#define PWM_CFG_CMP3CENTER 0x00080000
+#define PWM_CFG_CMP0GANG 0x01000000
+#define PWM_CFG_CMP1GANG 0x02000000
+#define PWM_CFG_CMP2GANG 0x04000000
+#define PWM_CFG_CMP3GANG 0x08000000
+#define PWM_CFG_CMP0IP 0x10000000
+#define PWM_CFG_CMP1IP 0x20000000
+#define PWM_CFG_CMP2IP 0x40000000
+#define PWM_CFG_CMP3IP 0x80000000
+
+#endif /* _SIFIVE_PWM_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/spi.h b/fw/fe310/bsp/include/sifive/devices/spi.h
new file mode 100644
index 0000000..47b4693
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/spi.h
@@ -0,0 +1,80 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_SPI_H
+#define _SIFIVE_SPI_H
+
+/* Register offsets */
+
+#define SPI_REG_SCKDIV 0x00
+#define SPI_REG_SCKMODE 0x04
+#define SPI_REG_CSID 0x10
+#define SPI_REG_CSDEF 0x14
+#define SPI_REG_CSMODE 0x18
+
+#define SPI_REG_DCSSCK 0x28
+#define SPI_REG_DSCKCS 0x2a
+#define SPI_REG_DINTERCS 0x2c
+#define SPI_REG_DINTERXFR 0x2e
+
+#define SPI_REG_FMT 0x40
+#define SPI_REG_TXFIFO 0x48
+#define SPI_REG_RXFIFO 0x4c
+#define SPI_REG_TXCTRL 0x50
+#define SPI_REG_RXCTRL 0x54
+
+#define SPI_REG_FCTRL 0x60
+#define SPI_REG_FFMT 0x64
+
+#define SPI_REG_IE 0x70
+#define SPI_REG_IP 0x74
+
+/* Fields */
+
+#define SPI_SCK_PHA 0x1
+#define SPI_SCK_POL 0x2
+
+#define SPI_FMT_PROTO(x) ((x) & 0x3)
+#define SPI_FMT_ENDIAN(x) (((x) & 0x1) << 2)
+#define SPI_FMT_DIR(x) (((x) & 0x1) << 3)
+#define SPI_FMT_LEN(x) (((x) & 0xf) << 16)
+
+/* TXCTRL register */
+#define SPI_TXWM(x) ((x) & 0xffff)
+/* RXCTRL register */
+#define SPI_RXWM(x) ((x) & 0xffff)
+
+#define SPI_IP_TXWM 0x1
+#define SPI_IP_RXWM 0x2
+
+#define SPI_FCTRL_EN 0x1
+
+#define SPI_INSN_CMD_EN 0x1
+#define SPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1)
+#define SPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4)
+#define SPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8)
+#define SPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10)
+#define SPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12)
+#define SPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16)
+#define SPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24)
+
+#define SPI_TXFIFO_FULL (1 << 31)
+#define SPI_RXFIFO_EMPTY (1 << 31)
+
+/* Values */
+
+#define SPI_CSMODE_AUTO 0
+#define SPI_CSMODE_HOLD 2
+#define SPI_CSMODE_OFF 3
+
+#define SPI_DIR_RX 0
+#define SPI_DIR_TX 1
+
+#define SPI_PROTO_S 0
+#define SPI_PROTO_D 1
+#define SPI_PROTO_Q 2
+
+#define SPI_ENDIAN_MSB 0
+#define SPI_ENDIAN_LSB 1
+
+
+#endif /* _SIFIVE_SPI_H */
diff --git a/fw/fe310/bsp/include/sifive/devices/uart.h b/fw/fe310/bsp/include/sifive/devices/uart.h
new file mode 100644
index 0000000..71bea6f
--- /dev/null
+++ b/fw/fe310/bsp/include/sifive/devices/uart.h
@@ -0,0 +1,27 @@
+// See LICENSE for license details.
+
+#ifndef _SIFIVE_UART_H
+#define _SIFIVE_UART_H
+
+/* Register offsets */
+#define UART_REG_TXFIFO 0x00
+#define UART_REG_RXFIFO 0x04
+#define UART_REG_TXCTRL 0x08
+#define UART_REG_RXCTRL 0x0c
+#define UART_REG_IE 0x10
+#define UART_REG_IP 0x14
+#define UART_REG_DIV 0x18
+
+/* TXCTRL register */
+#define UART_TXEN 0x1
+#define UART_TXWM(x) (((x) & 0xffff) << 16)
+
+/* RXCTRL register */
+#define UART_RXEN 0x1
+#define UART_RXWM(x) (((x) & 0xffff) << 16)
+
+/* IP register */
+#define UART_IP_TXWM 0x1
+#define UART_IP_RXWM 0x2
+
+#endif /* _SIFIVE_UART_H */
diff --git a/fw/fe310/bsp/metal/Makefile b/fw/fe310/bsp/metal/Makefile
new file mode 100644
index 0000000..32acfd2
--- /dev/null
+++ b/fw/fe310/bsp/metal/Makefile
@@ -0,0 +1,13 @@
+include ../../common.mk
+CFLAGS += -I../include -I../drivers
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: entry.o metal.o
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/bsp/metal/entry.S b/fw/fe310/bsp/metal/entry.S
new file mode 100644
index 0000000..97da3fd
--- /dev/null
+++ b/fw/fe310/bsp/metal/entry.S
@@ -0,0 +1,106 @@
+/* Copyright 2018 SiFive, Inc */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* This code executes before _start, which is contained inside the C library.
+ * In embedded systems we want to ensure that _enter, which contains the first
+ * code to be executed, can be loaded at a specific address. To enable this
+ * feature we provide the '.text.metal.init.enter' section, which is
+ * defined to have the first address being where execution should start. */
+.section .text.metal.init.enter
+.global _enter
+_enter:
+ .cfi_startproc
+
+ /* Inform the debugger that there is nowhere to backtrace past _enter. */
+ .cfi_undefined ra
+
+ /* The absolute first thing that must happen is configuring the global
+ * pointer register, which must be done with relaxation disabled because
+ * it's not valid to obtain the address of any symbol without GP
+ * configured. The C environment might go ahead and do this again, but
+ * that's safe as it's a fixed register. */
+.option push
+.option norelax
+ la gp, __global_pointer$
+.option pop
+
+ /* Set up a simple trap vector to catch anything that goes wrong early in
+ * the boot process. */
+ la t0, early_trap_vector
+ csrw mtvec, t0
+ /* enable chicken bit if core is bullet series*/
+ la t0, __metal_chicken_bit
+ beqz t0, 1f
+ csrwi 0x7C1, 0
+1:
+
+ /* There may be pre-initialization routines inside the MBI code that run in
+ * C, so here we set up a C environment. First we set up a stack pointer,
+ * which is left as a weak reference in order to allow initialization
+ * routines that do not need a stack to be set up to transparently be
+ * called. */
+ .weak __metal_stack_pointer
+ la sp, __metal_stack_pointer
+
+ /* Check for an initialization routine and call it if one exists, otherwise
+ * just skip over the call entirely. Note that __metal_initialize isn't
+ * actually a full C function, as it doesn't end up with the .bss or .data
+ * segments having been initialized. This is done to avoid putting a
+ * burden on systems that can be initialized without having a C environment
+ * set up. */
+ .weak __metal_before_start
+ la ra, __metal_before_start
+ beqz ra, 1f
+ jalr ra
+1:
+
+ /* At this point we can enter the C runtime's startup file. The arguments
+ * to this function are designed to match those provided to the SEE, just
+ * so we don't have to write another ABI. */
+ csrr a0, mhartid
+ li a1, 0
+ li a2, 0
+ call _start
+
+ /* If we've made it back here then there's probably something wrong. We
+ * allow the METAL to register a handler here. */
+ .weak __metal_after_main
+ la ra, __metal_after_main
+ beqz ra, 1f
+ jalr ra
+1:
+
+ /* If that handler returns then there's not a whole lot we can do. Just
+ * try to make some noise. */
+ la t0, 1f
+ csrw mtvec, t0
+1:
+ lw t1, 0(x0)
+ j 1b
+
+ .cfi_endproc
+
+/* For sanity's sake we set up an early trap vector that just does nothing. If
+ * you end up here then there's a bug in the early boot code somewhere. */
+.section .text.metal.init.trapvec
+.align 2
+early_trap_vector:
+ .cfi_startproc
+ csrr t0, mcause
+ csrr t1, mepc
+ csrr t2, mtval
+ j early_trap_vector
+ .cfi_endproc
+
+/* The GCC port might not emit a __register_frame_info symbol, which eventually
+ * results in a weak undefined reference that eventually causes crash when it
+ * is dereference early in boot. We really shouldn't need to put this here,
+ * but to deal with what I think is probably a bug in the linker script I'm
+ * going to leave this in for now. At least it's fairly cheap :) */
+.weak __register_frame_info
+.global __register_frame_info
+.section .text.metal.init.__register_frame_info
+__register_frame_info:
+ .cfi_startproc
+ ret
+ .cfi_endproc
diff --git a/fw/fe310/bsp/metal/metal.c b/fw/fe310/bsp/metal/metal.c
new file mode 100644
index 0000000..00849b9
--- /dev/null
+++ b/fw/fe310/bsp/metal/metal.c
@@ -0,0 +1,34 @@
+#include <sys/cdefs.h>
+
+#include "encoding.h"
+#include "platform.h"
+#include "prci_driver.h"
+
+extern void eos_trap_entry();
+
+static void uart_init(size_t baud_rate) {
+ GPIO_REG(GPIO_IOF_SEL) &= ~IOF0_UART0_MASK;
+ GPIO_REG(GPIO_IOF_EN) |= IOF0_UART0_MASK;
+ UART0_REG(UART_REG_DIV) = PRCI_get_cpu_freq() / baud_rate - 1;
+ UART0_REG(UART_REG_TXCTRL) |= UART_TXEN;
+ UART0_REG(UART_REG_RXCTRL) |= UART_RXEN;
+}
+
+__attribute__((constructor))
+void metal_init(void) {
+ SPI0_REG(SPI_REG_SCKDIV) = 8;
+
+ PRCI_use_default_clocks();
+ PRCI_use_pll(PLL_REFSEL_HFXOSC, 0, 1, 31, 1, -1, -1, -1);
+ uart_init(115200);
+
+ write_csr(mtvec, &eos_trap_entry);
+ if (read_csr(misa) & (1 << ('F' - 'A'))) { // if F extension is present
+ write_csr(mstatus, MSTATUS_FS); // allow FPU instructions without trapping
+ write_csr(fcsr, 0); // initialize rounding mode, undefined at reset
+ }
+}
+
+__attribute__((section(".init")))
+void __metal_synchronize_harts() {
+}
diff --git a/fw/fe310/bsp/openocd.cfg b/fw/fe310/bsp/openocd.cfg
new file mode 100644
index 0000000..1205cbd
--- /dev/null
+++ b/fw/fe310/bsp/openocd.cfg
@@ -0,0 +1,34 @@
+#write_config_file
+# JTAG adapter setup
+adapter_khz 10000
+
+interface ftdi
+ftdi_device_desc "FT2232H-56Q MiniModule"
+ftdi_vid_pid 0x0403 0x6010
+
+ftdi_layout_init 0x0008 0x001b
+ftdi_layout_signal nSRST -oe 0x0020 -data 0x0020
+
+#Reset Stretcher logic on FE310 is ~1 second long
+#This doesn't apply if you use
+# ftdi_set_signal, but still good to document
+#adapter_nsrst_delay 1500
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000913
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+
+flash bank spi0 fespi 0x20000000 0 0 0 $_TARGETNAME.0 0x10014000
+init
+if {[ info exists pulse_srst]} {
+ ftdi_set_signal nSRST 0
+ ftdi_set_signal nSRST z
+sleep 1500
+}
+halt
+
+# flash protect 0 64 last off
+echo "Ready for Remote Connections"
diff --git a/fw/fe310/bsp/upload b/fw/fe310/bsp/upload
new file mode 100755
index 0000000..a52fc0a
--- /dev/null
+++ b/fw/fe310/bsp/upload
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -e
+set -o pipefail
+
+unset elf
+unset hex
+unset cfg
+unset openocd
+unset jlink
+unset gdb
+while [[ "$1" != "" ]]
+do
+ case "$1"
+ in
+ --elf) elf="$2"; shift 2;;
+ --hex) hex="$2"; shift 2;;
+ --openocd) openocd="$2"; shift 2;;
+ --openocd-config) cfg="$2"; shift 2;;
+ --jlink) jlink="$2"; shift 2;;
+ --gdb) gdb="$2"; shift 2;;
+ *) echo "$0: Unknown argument $1"; exit 1;;
+ esac
+done
+
+if [ "$elf" == "" -a "$hex" == "" ]
+then
+ echo "$0: --elf or --hex is required" >&2
+ exit 1
+fi
+
+export GDB_PORT=3333
+
+if [ "$jlink" != "" ]
+then
+
+echo -e "loadfile $hex\nrnh\nexit" | $jlink -device FE310 -if JTAG -speed 4000 -jtagconf -1,-1 -autoconnect 1
+
+else
+
+$openocd -f $cfg &
+
+$gdb $elf --batch -ex "set remotetimeout 240" -ex "target extended-remote localhost:${GDB_PORT}" -ex "monitor reset halt" -ex "monitor flash protect 0 64 last off" -ex "load" -ex "monitor resume" -ex "monitor shutdown" -ex "quit"
+
+kill %1
+
+fi
diff --git a/fw/fe310/common.mk b/fw/fe310/common.mk
new file mode 100644
index 0000000..9fddf67
--- /dev/null
+++ b/fw/fe310/common.mk
@@ -0,0 +1,5 @@
+CC = $(RISCV_HOME)/bin/riscv64-unknown-elf-gcc
+AR = $(RISCV_HOME)/bin/riscv64-unknown-elf-ar
+RANLIB = $(RISCV_HOME)/bin/riscv64-unknown-elf-ranlib
+
+CFLAGS = -march=rv32imac -mabi=ilp32 -mcmodel=medlow -ffunction-sections -fdata-sections --specs=nano.specs -O3
diff --git a/fw/fe310/crypto/Makefile b/fw/fe310/crypto/Makefile
new file mode 100644
index 0000000..2230d6a
--- /dev/null
+++ b/fw/fe310/crypto/Makefile
@@ -0,0 +1,17 @@
+include ../common.mk
+
+CRYPTO_DIR = ../../../crypto
+
+obj = $(CRYPTO_DIR)/aes/aes.o $(CRYPTO_DIR)/blowfish/blowfish.o $(CRYPTO_DIR)/sha/sha1.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o $(obj)
diff --git a/fw/fe310/eos/LICENSE b/fw/fe310/eos/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/fw/fe310/eos/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/fw/fe310/eos/Makefile b/fw/fe310/eos/Makefile
new file mode 100644
index 0000000..abdcbac
--- /dev/null
+++ b/fw/fe310/eos/Makefile
@@ -0,0 +1,19 @@
+include ../common.mk
+
+CRYPTO_DIR = ../../../crypto
+
+CFLAGS += -I. -I../bsp/include -I../bsp/drivers -I$(CRYPTO_DIR)
+
+obj = trap_entry.o eos.o msgq.o event.o interrupt.o timer.o power.o i2s.o i2c.o uart.o spi.o spi_dev.o sdcard.o sdc_crypto.o cam.o net.o wifi.o cell.o sock.o unicode.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/eos/app/Makefile b/fw/fe310/eos/app/Makefile
new file mode 100644
index 0000000..e0eee28
--- /dev/null
+++ b/fw/fe310/eos/app/Makefile
@@ -0,0 +1,17 @@
+include ../../common.mk
+
+CFLAGS += -I..
+
+obj = app_root.o app_status.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/eos/app/app_root.c b/fw/fe310/eos/app/app_root.c
new file mode 100644
index 0000000..1b46ea8
--- /dev/null
+++ b/fw/fe310/eos/app/app_root.c
@@ -0,0 +1,88 @@
+#include <stdlib.h>
+
+#include "net.h"
+
+#include "eve/eve.h"
+#include "eve/eve_kbd.h"
+#include "eve/eve_font.h"
+
+#include "eve/screen/window.h"
+#include "eve/screen/page.h"
+#include "eve/screen/form.h"
+
+#include "eve/widget/widgets.h"
+
+#include "app_status.h"
+#include "app_root.h"
+
+#define KBD_X 0
+#define KBD_Y 575
+#define KBD_W 480
+#define KBD_H 225
+
+static EVEKbd kbd;
+static EVEFont font;
+static EVEWindowRoot win_root;
+static EVEWindowKbd win_kbd;
+static EVEWindow win_status;
+static EVEWindow win_main;
+static EVEView view_status;
+static EVEViewStack view_stack;
+
+EVEWindowRoot *app_root(void) {
+ return &win_root;
+}
+
+void app_root_refresh(void) {
+ eve_spi_start();
+ eve_window_root_draw(app_root());
+ eve_spi_stop();
+}
+
+void app_root_init(eve_view_constructor_t home_page) {
+ EVERect g;
+
+ eve_spi_start();
+
+ eve_brightness(0x40);
+
+ eve_font_init(&font, APP_FONT_HANDLE);
+
+ g.x = 0;
+ g.y = 0;
+ g.w = APP_SCREEN_W;
+ g.h = APP_SCREEN_H;
+ eve_window_init_root(&win_root, &g, "root", &font);
+
+ g.x = KBD_X;
+ g.y = KBD_Y;
+ g.w = KBD_W;
+ g.h = KBD_H;
+ eve_kbd_init(&kbd, &g, win_root.mem_next, &win_root.mem_next);
+ eve_window_init_kbd(&win_kbd, &g, &win_root, "kbd", &kbd);
+
+ g.x = 0;
+ g.y = 0;
+ g.w = APP_SCREEN_W;
+ g.h = APP_STATUS_H;
+ eve_window_init(&win_status, &g, (EVEWindow *)&win_root, "status");
+ eve_view_init(&view_status, &win_status, app_status_draw, app_status_touch, NULL, NULL);
+
+ g.x = 0;
+ g.y = APP_STATUS_H;
+ g.w = APP_SCREEN_W;
+ g.h = APP_SCREEN_H - APP_STATUS_H;
+ eve_window_init(&win_main, &g, (EVEWindow *)&win_root, "main");
+
+ eve_view_stack_init(&view_stack);
+ eve_view_create(&win_main, &view_stack, home_page);
+
+ eve_window_append(&win_status);
+ eve_window_append(&win_main);
+
+ eve_window_root_draw(&win_root);
+
+ eve_spi_stop();
+
+ eos_net_acquire_for_evt(EOS_EVT_UI | EVE_ETYPE_INTR, 1);
+}
diff --git a/fw/fe310/eos/app/app_root.h b/fw/fe310/eos/app/app_root.h
new file mode 100644
index 0000000..7a44565
--- /dev/null
+++ b/fw/fe310/eos/app/app_root.h
@@ -0,0 +1,12 @@
+#include <stdint.h>
+
+#define APP_SCREEN_W 480
+#define APP_SCREEN_H 800
+#define APP_STATUS_H 60
+
+#define APP_FONT_HANDLE 31
+
+EVEWindowRoot *app_root(void);
+void app_root_refresh(void);
+
+void app_root_init(eve_view_constructor_t home_page);
diff --git a/fw/fe310/eos/app/app_status.c b/fw/fe310/eos/app/app_status.c
new file mode 100644
index 0000000..4ab0a97
--- /dev/null
+++ b/fw/fe310/eos/app/app_status.c
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+#include "eve/eve.h"
+#include "eve/eve_kbd.h"
+#include "eve/eve_font.h"
+
+#include "eve/screen/window.h"
+
+#include "app_status.h"
+
+uint8_t app_status_draw(EVEView *view, uint8_t tag0) {
+ return eve_view_clear(view, tag0, 0);
+}
+
+int app_status_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ return 0;
+}
diff --git a/fw/fe310/eos/app/app_status.h b/fw/fe310/eos/app/app_status.h
new file mode 100644
index 0000000..9b2ac66
--- /dev/null
+++ b/fw/fe310/eos/app/app_status.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+int app_status_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0);
+uint8_t app_status_draw(EVEView *view, uint8_t tag0);
diff --git a/fw/fe310/eos/board.h b/fw/fe310/eos/board.h
new file mode 100644
index 0000000..752808a
--- /dev/null
+++ b/fw/fe310/eos/board.h
@@ -0,0 +1,42 @@
+#define SPI_DIV_NET 16
+#define SPI_DIV_EVE 16
+#define SPI_DIV_SDC 1024
+#define SPI_DIV_CAM 24
+
+#define SPI_CSID_NET 3
+#define SPI_CSID_EVE 2
+#define SPI_CSID_SDC SPI_CSID_NONE
+#define SPI_CSID_CAM SPI_CSID_NONE
+
+#define SPI_CSPIN_NET SPI_CSPIN_NONE
+#define SPI_CSPIN_EVE SPI_CSPIN_NONE
+#define SPI_CSPIN_SDC 2
+#define SPI_CSPIN_CAM 23
+
+#define SPI_IOF_MASK_CS (((uint32_t)1 << IOF_SPI1_SS2) | ((uint32_t)1 << IOF_SPI1_SS3))
+
+#define NET_PIN_RTS 20
+#define NET_PIN_CTS 22
+
+#define I2S_PIN_CK 1 /* PWM 0.1 */
+#define I2S_PIN_CK_SW 21 /* PWM 1.2 */
+#define I2S_PIN_CK_SR 18
+#define I2S_PIN_WS_MIC 19 /* PWM 1.1 */
+#define I2S_PIN_WS_SPK 11 /* PWM 2.1 */
+#define I2S_PIN_SD_IN 13
+#define I2S_PIN_SD_OUT 12
+
+#define I2S_IRQ_WS_ID (INT_PWM2_BASE + 0)
+#define I2S_IRQ_SD_ID (INT_PWM2_BASE + 3)
+
+#define I2S_CTRL_ADDR_CK PWM0_CTRL_ADDR
+#define I2S_CTRL_ADDR_WS_MIC PWM1_CTRL_ADDR
+#define I2S_CTRL_ADDR_WS_SPK PWM2_CTRL_ADDR
+
+#define I2S_IDLE_CYCLES 8
+
+#define EVE_GPIO_DIR 0x0f
+
+#define EVE_GPIO_CAM 0
+#define EVE_GPIO_EXT 1
+#define EVE_GPIO_MOTOR 3
diff --git a/fw/fe310/eos/cam.c b/fw/fe310/eos/cam.c
new file mode 100644
index 0000000..43293af
--- /dev/null
+++ b/fw/fe310/eos/cam.c
@@ -0,0 +1,138 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "eos.h"
+#include "spi.h"
+#include "cam.h"
+
+const int _eos_cam_resolution[][2] = {
+ {0, 0 },
+ // C/SIF Resolutions
+ {88, 72 }, /* QQCIF */
+ {176, 144 }, /* QCIF */
+ {352, 288 }, /* CIF */
+ {88, 60 }, /* QQSIF */
+ {176, 120 }, /* QSIF */
+ {352, 240 }, /* SIF */
+ // VGA Resolutions
+ {40, 30 }, /* QQQQVGA */
+ {80, 60 }, /* QQQVGA */
+ {160, 120 }, /* QQVGA */
+ {320, 240 }, /* QVGA */
+ {640, 480 }, /* VGA */
+ {60, 40 }, /* HQQQVGA */
+ {120, 80 }, /* HQQVGA */
+ {240, 160 }, /* HQVGA */
+ // FFT Resolutions
+ {64, 32 }, /* 64x32 */
+ {64, 64 }, /* 64x64 */
+ {128, 64 }, /* 128x64 */
+ {128, 128 }, /* 128x128 */
+ {320, 320 }, /* 128x128 */
+ // Other
+ {128, 160 }, /* LCD */
+ {128, 160 }, /* QQVGA2 */
+ {720, 480 }, /* WVGA */
+ {752, 480 }, /* WVGA2 */
+ {800, 600 }, /* SVGA */
+ {1024, 768 }, /* XGA */
+ {1280, 1024}, /* SXGA */
+ {1600, 1200}, /* UXGA */
+ {1280, 720 }, /* HD */
+ {1920, 1080}, /* FHD */
+ {2560, 1440}, /* QHD */
+ {2048, 1536}, /* QXGA */
+ {2560, 1600}, /* WQXGA */
+ {2592, 1944}, /* WQXGA2 */
+};
+
+#define CAM_REG_CAPTURE_CTRL 0x01
+#define CAM_REG_FIFO_CTRL 0x04
+
+#define CAM_REG_GPIO_DIR 0x05
+#define CAM_REG_GPIO_WR 0x06
+#define CAM_REG_GPIO_RD 0x45
+
+#define CAM_REG_STATUS 0x41
+
+#define CAM_REG_VER 0x40
+
+#define CAM_REG_READ_BURST 0x3c
+#define CAM_REG_READ_BYTE 0x3d
+
+#define CAM_REG_FIFO_SIZE1 0x42
+#define CAM_REG_FIFO_SIZE2 0x43
+#define CAM_REG_FIFO_SIZE3 0x44
+
+#define CAM_VAL_FIFO_CTRL_CLEAR 0x01
+#define CAM_VAL_FIFO_CTRL_START 0x02
+#define CAM_VAL_FIFO_CTRL_RSTWR 0x10
+#define CAM_VAL_FIFO_CTRL_RSTRD 0x20
+
+#define CAM_VAL_STATUS_VSYNC 0x01
+#define CAM_VAL_STATUS_SHUTTER 0x02
+#define CAM_VAL_STATUS_CPTDONE 0x08
+
+#define CAM_VAL_GPIO_RST 0x01
+#define CAM_VAL_GPIO_PWRDN 0x02
+#define CAM_VAL_GPIO_PWREN 0x04
+
+static uint8_t reg_read(uint8_t addr) {
+ uint8_t ret;
+
+ eos_spi_cs_set();
+ eos_spi_xchg8(addr, 0);
+ ret = eos_spi_xchg8(0, 0);
+ eos_spi_cs_clear();
+
+ return ret;
+}
+
+static void reg_write(uint8_t addr, uint8_t val) {
+ eos_spi_cs_set();
+ eos_spi_xchg8(addr | 0x80, 0);
+ eos_spi_xchg8(val, 0);
+ eos_spi_cs_clear();
+}
+
+void eos_cam_capture(void) {
+ reg_write(CAM_REG_FIFO_CTRL, CAM_VAL_FIFO_CTRL_START);
+}
+
+int eos_cam_capture_done(void) {
+ return !!(reg_read(CAM_REG_STATUS) & CAM_VAL_STATUS_CPTDONE);
+}
+
+void eos_cam_capture_wait(void) {
+ int done = 0;
+
+ while (!done) {
+ done = eos_cam_capture_done();
+ }
+}
+
+uint32_t eos_cam_fbuf_size(void) {
+ uint32_t ret;
+
+ ret = reg_read(CAM_REG_FIFO_SIZE1);
+ ret |= reg_read(CAM_REG_FIFO_SIZE2) << 8;
+ ret |= (reg_read(CAM_REG_FIFO_SIZE3) & 0x7f) << 16;
+ return ret;
+}
+
+void eos_cam_fbuf_read(uint8_t *buffer, uint16_t sz, int first) {
+ int i;
+
+ eos_spi_cs_set();
+ eos_spi_xchg8(CAM_REG_READ_BURST, 0);
+ if (first) eos_spi_xchg8(0, 0);
+
+ for (i=0; i<sz; i++) {
+ buffer[i] = eos_spi_xchg8(0, 0);
+ }
+ eos_spi_cs_clear();
+}
+
+void eos_cam_fbuf_done(void) {
+ reg_write(CAM_REG_FIFO_CTRL, CAM_VAL_FIFO_CTRL_CLEAR);
+}
diff --git a/fw/fe310/eos/cam.h b/fw/fe310/eos/cam.h
new file mode 100644
index 0000000..f61757b
--- /dev/null
+++ b/fw/fe310/eos/cam.h
@@ -0,0 +1,88 @@
+#include <stdint.h>
+
+typedef enum {
+ PIXFORMAT_INVALID = 0,
+ PIXFORMAT_BINARY, // 1BPP/BINARY
+ PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
+ PIXFORMAT_RGB565, // 2BPP/RGB565
+ PIXFORMAT_YUV422, // 2BPP/YUV422
+ PIXFORMAT_BAYER, // 1BPP/RAW
+ PIXFORMAT_JPEG, // JPEG/COMPRESSED
+} pixformat_t;
+
+typedef enum {
+ FRAMESIZE_INVALID = 0,
+ // C/SIF Resolutions
+ FRAMESIZE_QQCIF, // 88x72
+ FRAMESIZE_QCIF, // 176x144
+ FRAMESIZE_CIF, // 352x288
+ FRAMESIZE_QQSIF, // 88x60
+ FRAMESIZE_QSIF, // 176x120
+ FRAMESIZE_SIF, // 352x240
+ // VGA Resolutions
+ FRAMESIZE_QQQQVGA, // 40x30
+ FRAMESIZE_QQQVGA, // 80x60
+ FRAMESIZE_QQVGA, // 160x120
+ FRAMESIZE_QVGA, // 320x240
+ FRAMESIZE_VGA, // 640x480
+ FRAMESIZE_HQQQVGA, // 60x40
+ FRAMESIZE_HQQVGA, // 120x80
+ FRAMESIZE_HQVGA, // 240x160
+ // FFT Resolutions
+ FRAMESIZE_64X32, // 64x32
+ FRAMESIZE_64X64, // 64x64
+ FRAMESIZE_128X64, // 128x64
+ FRAMESIZE_128X128, // 128x128
+ FRAMESIZE_320X320, // 320x320
+ // Other
+ FRAMESIZE_LCD, // 128x160
+ FRAMESIZE_QQVGA2, // 128x160
+ FRAMESIZE_WVGA, // 720x480
+ FRAMESIZE_WVGA2, // 752x480
+ FRAMESIZE_SVGA, // 800x600
+ FRAMESIZE_XGA, // 1024x768
+ FRAMESIZE_SXGA, // 1280x1024
+ FRAMESIZE_UXGA, // 1600x1200
+ FRAMESIZE_HD, // 1280x720
+ FRAMESIZE_FHD, // 1920x1080
+ FRAMESIZE_QHD, // 2560x1440
+ FRAMESIZE_QXGA, // 2048x1536
+ FRAMESIZE_WQXGA, // 2560x1600
+ FRAMESIZE_WQXGA2, // 2592x1944
+} framesize_t;
+
+typedef enum {
+ GAINCEILING_2X,
+ GAINCEILING_4X,
+ GAINCEILING_8X,
+ GAINCEILING_16X,
+ GAINCEILING_32X,
+ GAINCEILING_64X,
+ GAINCEILING_128X,
+} gainceiling_t;
+
+typedef enum {
+ SDE_NORMAL,
+ SDE_NEGATIVE,
+} sde_t;
+
+extern const int _eos_cam_resolution[][2];
+
+#define IM_LOG2_2(x) (((x) & 0x2ULL) ? ( 2 ) : 1) // NO ({ ... }) !
+#define IM_LOG2_4(x) (((x) & 0xCULL) ? ( 2 + IM_LOG2_2((x) >> 2)) : IM_LOG2_2(x)) // NO ({ ... }) !
+#define IM_LOG2_8(x) (((x) & 0xF0ULL) ? ( 4 + IM_LOG2_4((x) >> 4)) : IM_LOG2_4(x)) // NO ({ ... }) !
+#define IM_LOG2_16(x) (((x) & 0xFF00ULL) ? ( 8 + IM_LOG2_8((x) >> 8)) : IM_LOG2_8(x)) // NO ({ ... }) !
+#define IM_LOG2_32(x) (((x) & 0xFFFF0000ULL) ? (16 + IM_LOG2_16((x) >> 16)) : IM_LOG2_16(x)) // NO ({ ... }) !
+#define IM_LOG2(x) (((x) & 0xFFFFFFFF00000000ULL) ? (32 + IM_LOG2_32((x) >> 32)) : IM_LOG2_32(x)) // NO ({ ... }) !
+
+#define IM_MAX(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
+#define IM_MIN(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
+#define IM_DIV(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _b ? (_a / _b) : 0; })
+#define IM_MOD(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _b ? (_a % _b) : 0; })
+
+void eos_cam_capture(void);
+int eos_cam_capture_done(void);
+void eos_cam_capture_wait(void);
+uint32_t eos_cam_fbuf_size(void);
+void eos_cam_fbuf_read(uint8_t *buffer, uint16_t sz, int first);
+void eos_cam_fbuf_done(void);
diff --git a/fw/fe310/eos/cell.c b/fw/fe310/eos/cell.c
new file mode 100644
index 0000000..2886041
--- /dev/null
+++ b/fw/fe310/eos/cell.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "eos.h"
+#include "event.h"
+#include "net.h"
+
+#include "cell.h"
+
+static eos_evt_handler_t evt_handler[EOS_CELL_MAX_MTYPE];
+
+static void cell_handle_msg(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char mtype;
+ unsigned char idx;
+
+ if ((buffer == NULL) || (len < 1)) {
+ eos_net_bad_handler(type, buffer, len);
+ return;
+ }
+
+ mtype = buffer[0];
+ idx = (mtype & EOS_CELL_MTYPE_MASK) >> 4;
+ if ((idx < EOS_CELL_MAX_MTYPE) && evt_handler[idx]) {
+ evt_handler[idx](mtype & ~EOS_CELL_MTYPE_MASK, buffer, len);
+ } else {
+ eos_net_bad_handler(type, buffer, len);
+ }
+}
+
+void eos_cell_init(void) {
+ int i;
+
+ for (i=0; i<EOS_CELL_MAX_MTYPE; i++) {
+ evt_handler[i] = NULL;
+ }
+ eos_net_set_handler(EOS_NET_MTYPE_CELL, cell_handle_msg);
+}
+
+void eos_cell_set_handler(unsigned char mtype, eos_evt_handler_t handler) {
+ unsigned char idx = (mtype & EOS_CELL_MTYPE_MASK) >> 4;
+
+ if (idx < EOS_CELL_MAX_MTYPE) evt_handler[idx] = handler;
+}
+
+eos_evt_handler_t eos_cell_get_handler(unsigned char mtype) {
+ unsigned char idx = (mtype & EOS_CELL_MTYPE_MASK) >> 4;
+
+ if (idx < EOS_CELL_MAX_MTYPE) return evt_handler[idx];
+ return NULL;
+}
diff --git a/fw/fe310/eos/cell.h b/fw/fe310/eos/cell.h
new file mode 100644
index 0000000..0fc2052
--- /dev/null
+++ b/fw/fe310/eos/cell.h
@@ -0,0 +1,51 @@
+#include <stdint.h>
+#include "event.h"
+
+#define EOS_CELL_MTYPE_DEV 0x10
+#define EOS_CELL_MTYPE_VOICE 0x20
+#define EOS_CELL_MTYPE_SMS 0x30
+#define EOS_CELL_MTYPE_CBS 0x40
+#define EOS_CELL_MTYPE_USSD 0x50
+#define EOS_CELL_MTYPE_PDP 0x60
+
+#define EOS_CELL_MTYPE_MASK 0xf0
+#define EOS_CELL_MAX_MTYPE 8
+
+/* EOS_CELL_MTYPE_DEV subtypes */
+#define EOS_CELL_MTYPE_READY 1
+#define EOS_CELL_MTYPE_UART_DATA 2
+#define EOS_CELL_MTYPE_UART_TAKE 3
+#define EOS_CELL_MTYPE_UART_GIVE 4
+#define EOS_CELL_MTYPE_RESET 5
+
+#define EOS_CELL_MTYPE_VOICE_PCM 1
+#define EOS_CELL_MTYPE_VOICE_DIAL 2
+#define EOS_CELL_MTYPE_VOICE_RING 3
+#define EOS_CELL_MTYPE_VOICE_ANSWER 4
+#define EOS_CELL_MTYPE_VOICE_HANGUP 5
+#define EOS_CELL_MTYPE_VOICE_BEGIN 6
+#define EOS_CELL_MTYPE_VOICE_END 7
+#define EOS_CELL_MTYPE_VOICE_MISS 8
+#define EOS_CELL_MTYPE_VOICE_BUSY 9
+#define EOS_CELL_MTYPE_VOICE_ERR 10
+
+#define EOS_CELL_MTYPE_SMS_LIST 1
+#define EOS_CELL_MTYPE_SMS_SEND 2
+#define EOS_CELL_MTYPE_SMS_MSG_NEW 3
+#define EOS_CELL_MTYPE_SMS_MSG_ITEM 4
+
+#define EOS_CELL_MTYPE_USSD_REQUEST 1
+#define EOS_CELL_MTYPE_USSD_REPLY 2
+#define EOS_CELL_MTYPE_USSD_CANCEL 3
+
+#define EOS_CELL_MTYPE_PDP_CONFIG 1
+#define EOS_CELL_MTYPE_PDP_CONNECT 2
+#define EOS_CELL_MTYPE_PDP_DISCONNECT 3
+
+#define EOS_CELL_SMS_ADDRTYPE_INTL 1
+#define EOS_CELL_SMS_ADDRTYPE_ALPHA 2
+#define EOS_CELL_SMS_ADDRTYPE_OTHER 3
+
+void eos_cell_init(void);
+void eos_cell_set_handler(unsigned char mtype, eos_evt_handler_t handler);
+eos_evt_handler_t eos_cell_get_handler(unsigned char mtype); \ No newline at end of file
diff --git a/fw/fe310/eos/eos.c b/fw/fe310/eos/eos.c
new file mode 100644
index 0000000..e2ad3d1
--- /dev/null
+++ b/fw/fe310/eos/eos.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+
+#include "event.h"
+#include "interrupt.h"
+#include "timer.h"
+#include "power.h"
+#include "i2s.h"
+#include "uart.h"
+#include "spi.h"
+#include "spi_dev.h"
+#include "sdcard.h"
+#include "net.h"
+#include "wifi.h"
+#include "cell.h"
+#include "sock.h"
+#include "i2c/bq25895.h"
+#include "eve/eve.h"
+
+#include "board.h"
+
+#include "eos.h"
+
+void eos_init(void) {
+ uint8_t wakeup_cause = eos_power_wakeup_cause();
+ uint32_t touch_matrix[6] = {0xfa46,0xfffffcf6,0x422fe,0xffffff38,0x10002,0xf3cb0};
+ int touch_calibrate = 0;
+
+ printf("WAKE:%d\n", wakeup_cause);
+
+ eos_evtq_init();
+ eos_intr_init();
+ eos_timer_init();
+ eos_i2s_init();
+ eos_uart_init();
+ eos_spi_init();
+ eos_spi_dev_init();
+ eos_sdc_init();
+ eos_net_init();
+ eos_power_init();
+ eos_wifi_init();
+ eos_cell_init();
+ eos_sock_init();
+ eos_bq25895_init();
+
+ eos_net_start(wakeup_cause);
+
+ eve_init(wakeup_cause == EOS_PWR_WAKE_RST, touch_calibrate, touch_matrix, EVE_GPIO_DIR);
+ if (touch_calibrate) {
+ printf("TOUCH MATRIX:\n");
+ printf("uint32_t touch_matrix[6] = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x}\n", touch_matrix[0], touch_matrix[1], touch_matrix[2], touch_matrix[3], touch_matrix[4], touch_matrix[5]);
+ }
+}
diff --git a/fw/fe310/eos/eos.h b/fw/fe310/eos/eos.h
new file mode 100644
index 0000000..2a295f1
--- /dev/null
+++ b/fw/fe310/eos/eos.h
@@ -0,0 +1,12 @@
+#define EOS_OK 0
+#define EOS_ERR -1
+#define EOS_ERR_TIMEOUT -2
+#define EOS_ERR_BUSY -3
+
+#define EOS_ERR_FULL -10
+#define EOS_ERR_EMPTY -11
+#define EOS_ERR_NOTFOUND -12
+
+#define EOS_ERR_NET -20
+
+void eos_init(void);
diff --git a/fw/fe310/eos/eve/Makefile b/fw/fe310/eos/eve/Makefile
new file mode 100644
index 0000000..e55ef7a
--- /dev/null
+++ b/fw/fe310/eos/eve/Makefile
@@ -0,0 +1,17 @@
+include ../../common.mk
+
+CFLAGS += -I.. -I../../bsp/include
+
+obj = eve.o eve_platform.o eve_touch.o eve_phy.o eve_vtrack.o eve_font.o eve_kbd.o eve_text.o clipb.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/eos/eve/clipb.c b/fw/fe310/eos/eve/clipb.c
new file mode 100644
index 0000000..974b631
--- /dev/null
+++ b/fw/fe310/eos/eve/clipb.c
@@ -0,0 +1,19 @@
+#include <string.h>
+
+#include "eve.h"
+#include "clipb.h"
+
+static uint8_t clipb[EVE_CLIPB_SIZE_BUF];
+
+int eve_clipb_push(uint8_t *str, uint16_t len) {
+ if (len >= EVE_CLIPB_SIZE_BUF) return EVE_ERR;
+
+ memcpy(clipb, str, len);
+ clipb[len] = '\0';
+
+ return EVE_OK;
+}
+
+uint8_t *eve_clipb_get(void) {
+ return clipb;
+} \ No newline at end of file
diff --git a/fw/fe310/eos/eve/clipb.h b/fw/fe310/eos/eve/clipb.h
new file mode 100644
index 0000000..2d6fae6
--- /dev/null
+++ b/fw/fe310/eos/eve/clipb.h
@@ -0,0 +1,6 @@
+#include <stdint.h>
+
+#define EVE_CLIPB_SIZE_BUF 256
+
+int eve_clipb_push(uint8_t *str, uint16_t len);
+uint8_t *eve_clipb_get(void); \ No newline at end of file
diff --git a/fw/fe310/eos/eve/eve.c b/fw/fe310/eos/eve/eve.c
new file mode 100644
index 0000000..d625fec
--- /dev/null
+++ b/fw/fe310/eos/eve/eve.c
@@ -0,0 +1,476 @@
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "eve.h"
+
+#define EVE_MEM_WRITE 0x800000
+#define EVE_SPI_FLAGS(b) ( (b) ? (EVE_SPI_FLAG_TX | EVE_SPI_FLAG_BSWAP) : EVE_SPI_FLAG_BSWAP )
+
+static char cmd_burst;
+static uint16_t cmd_offset;
+
+static char dl_burst;
+static uint32_t dl_addr;
+
+static uint8_t brigtness;
+static uint8_t power_state;
+
+void eve_command(uint8_t command, uint8_t parameter) {
+ eve_spi_cs_set();
+ eve_spi_xchg24(((uint32_t)command << 16) | ((uint32_t)parameter << 8), 0);
+ eve_spi_cs_clear();
+}
+
+uint8_t eve_read8(uint32_t addr) {
+ uint8_t r;
+ eve_spi_cs_set();
+ eve_spi_xchg32(addr << 8, 0);
+ r = eve_spi_xchg8(0, EVE_SPI_FLAG_BSWAP);
+ eve_spi_cs_clear();
+
+ return r;
+}
+
+uint16_t eve_read16(uint32_t addr) {
+ uint16_t r;
+ eve_spi_cs_set();
+ eve_spi_xchg32(addr << 8, 0);
+ r = eve_spi_xchg16(0, EVE_SPI_FLAG_BSWAP);
+ eve_spi_cs_clear();
+
+ return r;
+}
+
+uint32_t eve_read32(uint32_t addr) {
+ uint32_t r;
+ eve_spi_cs_set();
+ eve_spi_xchg32(addr << 8, 0);
+ r = eve_spi_xchg32(0, EVE_SPI_FLAG_BSWAP);
+ eve_spi_cs_clear();
+
+ return r;
+}
+
+void eve_write8(uint32_t addr, uint8_t data) {
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, 0);
+ eve_spi_xchg8(data, EVE_SPI_FLAG_BSWAP);
+ eve_spi_cs_clear();
+}
+
+void eve_write16(uint32_t addr, uint16_t data) {
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, 0);
+ eve_spi_xchg16(data, EVE_SPI_FLAG_BSWAP);
+ eve_spi_cs_clear();
+}
+
+void eve_write32(uint32_t addr, uint32_t data) {
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, 0);
+ eve_spi_xchg32(data, EVE_SPI_FLAG_BSWAP);
+ eve_spi_cs_clear();
+}
+
+void eve_readb(uint32_t addr, uint8_t *buf, size_t size) {
+ int i;
+
+ eve_spi_cs_set();
+ eve_spi_xchg32(addr << 8, 0);
+ for (i=0; i<size; i++) {
+ buf[i] = eve_spi_xchg8(0, 0);
+ }
+ eve_spi_cs_clear();
+}
+
+void eve_writeb(uint32_t addr, uint8_t *buf, size_t size) {
+ int i;
+
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, 0);
+ for (i=0; i<size; i++) {
+ eve_spi_xchg8(buf[i], 0);
+ }
+ eve_spi_cs_clear();
+}
+
+static void dl_inc(uint32_t i) {
+ dl_addr += i;
+}
+
+void eve_dl_start(uint32_t addr, char burst) {
+ dl_addr = addr;
+ dl_burst = burst;
+ if (burst) {
+ eve_spi_lock();
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, EVE_SPI_FLAG_TX);
+ }
+}
+
+void eve_dl_write(uint32_t dl) {
+ if (dl_burst) {
+ eve_spi_xchg32(dl, EVE_SPI_FLAGS(dl_burst));
+ } else {
+ eve_write32(dl_addr, dl);
+ }
+ dl_inc(4);
+}
+
+void eve_dl_end(void) {
+ if (dl_burst) {
+ eve_spi_flush();
+ eve_spi_cs_clear();
+ eve_spi_unlock();
+ dl_burst = 0;
+ }
+}
+
+void eve_dl_swap(void) {
+ eve_write8(REG_DLSWAP, EVE_DLSWAP_FRAME);
+}
+
+uint32_t eve_dl_get_addr(void) {
+ return dl_addr;
+}
+
+static void cmd_inc(uint16_t i) {
+ cmd_offset += i;
+ cmd_offset &= 0x0fff;
+}
+
+static void cmd_begin(uint32_t command, uint8_t flags) {
+ if (!cmd_burst) {
+ uint32_t addr = EVE_RAM_CMD + cmd_offset;
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, 0);
+ }
+ eve_spi_xchg32(command, flags);
+ cmd_inc(4);
+}
+
+static void cmd_end(void) {
+ if (!cmd_burst) eve_spi_cs_clear();
+}
+
+static void cmd_string(const char *str, uint8_t flags) {
+ int i = 0;
+
+ while (str[i] != 0) {
+ eve_spi_xchg8(str[i], flags);
+ i++;
+ }
+ eve_spi_xchg8(0, flags);
+ i++;
+ cmd_inc(i);
+}
+
+static void cmd_buffer(const char *buf, int size, uint8_t flags) {
+ int i = 0;
+
+ for (i=0; i<size; i++) {
+ eve_spi_xchg8(buf[i], flags);
+ }
+ cmd_inc(size);
+}
+
+static void cmd_padding(uint8_t flags) {
+ int i = cmd_offset & 3; /* equivalent to cmd_offset % 4 */
+
+ if (i) {
+ i = 4 - i; /* 3, 2 or 1 */
+ cmd_inc(i);
+
+ while (i > 0) {
+ eve_spi_xchg8(0, flags);
+ i--;
+ }
+ }
+}
+
+void eve_cmd(uint32_t cmd, const char *fmt, ...) {
+ uint8_t flags = EVE_SPI_FLAGS(cmd_burst);
+ va_list argv;
+ uint16_t *p;
+ int i = 0;
+ int cont = 0;
+
+ va_start(argv, fmt);
+ cmd_begin(cmd, flags);
+ while (fmt[i]) {
+ switch (fmt[i]) {
+ case 'b':
+ eve_spi_xchg8(va_arg(argv, int), flags);
+ cmd_inc(1);
+ break;
+ case 'h':
+ eve_spi_xchg16(va_arg(argv, int), flags);
+ cmd_inc(2);
+ break;
+ case 'w':
+ eve_spi_xchg32(va_arg(argv, int), flags);
+ cmd_inc(4);
+ break;
+ case '&':
+ p = va_arg(argv, uint16_t *);
+ *p = cmd_offset;
+ eve_spi_xchg32(0, flags);
+ cmd_inc(4);
+ break;
+ case 's':
+ cmd_string(va_arg(argv, const char *), flags);
+ break;
+ case 'p':
+ cmd_buffer(va_arg(argv, const char *), va_arg(argv, int), flags);
+ break;
+ case '+':
+ cont = 1;
+ break;
+ }
+ if (cont) break;
+ i++;
+ }
+ va_end(argv);
+ if (!cont) eve_cmd_end();
+}
+
+void eve_cmd_write(uint8_t *buffer, size_t size) {
+ cmd_buffer(buffer, size, EVE_SPI_FLAGS(cmd_burst));
+}
+
+void eve_cmd_end(void) {
+ cmd_padding(EVE_SPI_FLAGS(cmd_burst));
+ cmd_end();
+}
+
+uint32_t eve_cmd_result(uint16_t offset) {
+ return eve_read32(EVE_RAM_CMD + offset);
+}
+
+void eve_cmd_dl(uint32_t dl) {
+ cmd_begin(dl, EVE_SPI_FLAGS(cmd_burst));
+ cmd_end();
+}
+
+int eve_cmd_done(void) {
+ uint16_t r = eve_read16(REG_CMD_READ);
+ if (r == 0xfff) {
+ cmd_offset = 0;
+ eve_write8(REG_CPURESET, 1);
+ eve_write16(REG_CMD_READ, 0);
+ eve_write16(REG_CMD_WRITE, 0);
+ eve_write16(REG_CMD_DL, 0);
+ eve_write8(REG_CPURESET, 0);
+ return -1;
+ }
+ return (r == cmd_offset);
+}
+
+int eve_cmd_exec(int w) {
+ eve_write16(REG_CMD_WRITE, cmd_offset);
+ if (w) {
+ int r;
+ do {
+ r = eve_cmd_done();
+ } while (!r);
+ if (r < 0) return EVE_ERR;
+ }
+ return EVE_OK;
+}
+
+void eve_cmd_burst_start(void) {
+ uint32_t addr = EVE_RAM_CMD + cmd_offset;
+ eve_spi_lock();
+ eve_spi_cs_set();
+ eve_spi_xchg24(addr | EVE_MEM_WRITE, EVE_SPI_FLAG_TX);
+ cmd_burst = 1;
+}
+
+void eve_cmd_burst_end(void) {
+ eve_spi_flush();
+ eve_spi_cs_clear();
+ eve_spi_unlock();
+ cmd_burst = 0;
+}
+
+int eve_gpio_get(int gpio) {
+ uint16_t reg = eve_read16(REG_GPIOX);
+ return !!(reg & (1 << gpio));
+}
+
+void eve_gpio_set(int gpio, int val) {
+ uint16_t reg = eve_read16(REG_GPIOX);
+ uint16_t reg_val = (1 << gpio);
+ if (val) {
+ reg |= reg_val;
+ } else {
+ reg &= ~reg_val;
+ }
+ eve_write16(REG_GPIOX, reg);
+}
+
+uint8_t eve_gpio_get_dir(void) {
+ uint16_t reg = eve_read16(REG_GPIOX_DIR);
+ return reg & 0x000f;
+}
+
+void eve_gpio_set_dir(uint8_t dir) {
+ uint16_t reg = eve_read16(REG_GPIOX_DIR);
+ reg &= 0xfff0;
+ reg |= dir & 0x0f;
+ eve_write16(REG_GPIOX_DIR, reg);
+}
+
+void eve_active(void) {
+ eve_command(EVE_ACTIVE, 0);
+ if (power_state == EVE_PSTATE_SLEEP) eve_time_sleep(40);
+
+ eve_write8(REG_PWM_DUTY, brigtness);
+ if (power_state != EVE_PSTATE_SLEEP) return;
+
+ eve_write8(REG_TOUCH_MODE, EVE_TMODE_CONTINUOUS);
+ eve_write8(REG_CTOUCH_EXTENDED, 0x00);
+
+ power_state = EVE_PSTATE_ACTIVE;
+}
+
+void eve_standby(void) {
+ if (power_state != EVE_PSTATE_ACTIVE) return;
+
+ brigtness = eve_read8(REG_PWM_DUTY);
+ eve_command(EVE_STANDBY, 0);
+
+ power_state = EVE_PSTATE_STANDBY;
+}
+
+void eve_sleep(void) {
+ if (power_state != EVE_PSTATE_ACTIVE) return;
+
+ brigtness = eve_read8(REG_PWM_DUTY);
+ eve_write8(REG_PWM_DUTY, 0x0);
+
+ eve_write8(REG_CTOUCH_EXTENDED, 0x01);
+ eve_write8(REG_TOUCH_MODE, EVE_TMODE_OFF);
+
+ eve_time_sleep(500);
+ eve_command(EVE_SLEEP, 0);
+
+ power_state = EVE_PSTATE_SLEEP;
+}
+
+void eve_brightness(uint8_t b) {
+ eve_write8(REG_PWM_DUTY, b);
+}
+
+static int _init(int touch_calibrate, uint32_t *touch_matrix, uint8_t gpio_dir) {
+ uint8_t chipid = 0;
+ uint16_t timeout = 0;
+
+ eve_command(EVE_RST_PULSE, 0);
+ eve_command(EVE_CLKEXT, 0);
+ eve_command(EVE_ACTIVE, 0); /* start EVE */
+
+ while (chipid != 0x7C) { /* if chipid is not 0x7c, continue to read it until it is, EVE needs a moment for it's power on self-test and configuration */
+ eve_time_sleep(1);
+ chipid = eve_read8(REG_ID);
+ timeout++;
+ if (timeout > 400) return EVE_ERR;
+ }
+
+ eve_write8(REG_PWM_DUTY, 0);
+ eve_write16(REG_GPIOX_DIR, 0x8000 | (gpio_dir & 0x0f));
+ eve_write16(REG_GPIOX, 0);
+
+ /* Initialize Display */
+ eve_write16(REG_HCYCLE, EVE_HCYCLE); /* total number of clocks per line, incl front/back porch */
+ eve_write16(REG_HOFFSET, EVE_HOFFSET); /* start of active line */
+ eve_write16(REG_HSYNC0, EVE_HSYNC0); /* start of horizontal sync pulse */
+ eve_write16(REG_HSYNC1, EVE_HSYNC1); /* end of horizontal sync pulse */
+ eve_write16(REG_VCYCLE, EVE_VCYCLE); /* total number of lines per screen, including pre/post */
+ eve_write16(REG_VOFFSET, EVE_VOFFSET); /* start of active screen */
+ eve_write16(REG_VSYNC0, EVE_VSYNC0); /* start of vertical sync pulse */
+ eve_write16(REG_VSYNC1, EVE_VSYNC1); /* end of vertical sync pulse */
+ eve_write8(REG_SWIZZLE, EVE_SWIZZLE); /* FT8xx output to LCD - pin order */
+ eve_write8(REG_PCLK_POL, EVE_PCLKPOL); /* LCD data is clocked in on this PCLK edge */
+ eve_write8(REG_CSPREAD, EVE_CSPREAD); /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */
+ eve_write16(REG_HSIZE, EVE_HSIZE); /* active display width */
+ eve_write16(REG_VSIZE, EVE_VSIZE); /* active display height */
+
+ /* do not set PCLK yet - wait for just after the first display list */
+
+ /* disable Audio */
+ eve_write16(REG_SOUND, 0x0000); /* set synthesizer to silence */
+ eve_write8(REG_VOL_SOUND, 0x00); /* turn synthesizer volume off */
+ eve_write8(REG_VOL_PB, 0x00); /* turn recorded audio volume off */
+
+ /* write a basic display-list to get things started */
+ eve_dl_start(EVE_RAM_DL, 0);
+ eve_dl_write(CLEAR_COLOR_RGB(0,0,0));
+ eve_dl_write(CLEAR(1,1,1));
+ eve_dl_write(DISPLAY());
+ eve_dl_swap();
+
+ /* nothing is being displayed yet... the pixel clock is still 0x00 */
+ eve_write16(REG_GPIOX, 0x8000); /* enable the DISP signal to the LCD panel, it is set to output in REG_GPIOX_DIR by default */
+ eve_write8(REG_PCLK, EVE_PCLK); /* now start clocking data to the LCD panel */
+
+ /* configure Touch */
+ eve_write16(REG_TOUCH_CONFIG, 0xb81); /* enable touch low power mode: 0xb81 - default: 0x381 */
+ eve_write8(REG_TOUCH_MODE, EVE_TMODE_CONTINUOUS); /* enable touch */
+ eve_write16(REG_TOUCH_RZTHRESH, EVE_TOUCH_RZTHRESH); /* eliminate any false touches */
+
+ if (touch_calibrate) {
+ eve_write8(REG_PWM_DUTY, 0x40);
+ eve_cmd_dl(CMD_DLSTART);
+ eve_cmd_dl(CLEAR_COLOR_RGB(0,0,0));
+ eve_cmd_dl(CLEAR(1,1,1));
+ eve_cmd(CMD_TEXT, "hhhhs", EVE_HSIZE/2, EVE_VSIZE/2, 27, EVE_OPT_CENTER, "Please tap on the dot.");
+ eve_cmd(CMD_CALIBRATE, "w", 0);
+ eve_cmd_dl(DISPLAY());
+ eve_cmd_dl(CMD_SWAP);
+ eve_cmd_exec(1);
+ eve_write8(REG_PWM_DUTY, 0);
+
+ touch_matrix[0] = eve_read32(REG_TOUCH_TRANSFORM_A);
+ touch_matrix[1] = eve_read32(REG_TOUCH_TRANSFORM_B);
+ touch_matrix[2] = eve_read32(REG_TOUCH_TRANSFORM_C);
+ touch_matrix[3] = eve_read32(REG_TOUCH_TRANSFORM_D);
+ touch_matrix[4] = eve_read32(REG_TOUCH_TRANSFORM_E);
+ touch_matrix[5] = eve_read32(REG_TOUCH_TRANSFORM_F);
+ } else {
+ eve_write32(REG_TOUCH_TRANSFORM_A, touch_matrix[0]);
+ eve_write32(REG_TOUCH_TRANSFORM_B, touch_matrix[1]);
+ eve_write32(REG_TOUCH_TRANSFORM_C, touch_matrix[2]);
+ eve_write32(REG_TOUCH_TRANSFORM_D, touch_matrix[3]);
+ eve_write32(REG_TOUCH_TRANSFORM_E, touch_matrix[4]);
+ eve_write32(REG_TOUCH_TRANSFORM_F, touch_matrix[5]);
+ }
+
+ eve_write8(REG_CTOUCH_EXTENDED, 0x00);
+
+ eve_cmd(CMD_SETROTATE, "w", 2);
+ eve_cmd_exec(1);
+
+ return EVE_OK;
+}
+
+int eve_init(int pwr_on, int touch_calibrate, uint32_t *touch_matrix, uint8_t gpio_dir) {
+ eve_spi_start();
+
+ pwr_on = 1; // override this for now
+
+ if (pwr_on) {
+ int rv = _init(touch_calibrate, touch_matrix, gpio_dir);
+ if (rv) return rv;
+ } else {
+ power_state = EVE_PSTATE_SLEEP;
+ eve_active();
+ }
+
+ eve_touch_init();
+ eve_platform_init();
+
+ eve_spi_stop();
+
+ return EVE_OK;
+}
diff --git a/fw/fe310/eos/eve/eve.h b/fw/fe310/eos/eve/eve.h
new file mode 100644
index 0000000..2f26edf
--- /dev/null
+++ b/fw/fe310/eos/eve/eve.h
@@ -0,0 +1,70 @@
+#include <stdint.h>
+
+#include "eve_def.h"
+#include "eve_touch.h"
+#include "eve_phy.h"
+#include "eve_vtrack.h"
+#include "eve_platform.h"
+
+#define EVE_OK 0
+#define EVE_ERR -1
+
+#define EVE_ERR_FULL -10
+#define EVE_ERR_EMPTY -11
+
+#define EVE_ERR_NOMEM -100
+
+#define EVE_PSTATE_ACTIVE 0
+#define EVE_PSTATE_STANDBY 1
+#define EVE_PSTATE_SLEEP 3
+
+#define COLOR_RGBC(c) ((4UL<<24)|((c)&16777215UL))
+#define CLEAR_COLOR_RGBC(c) ((2UL<<24)|((c)&16777215UL))
+
+typedef struct EVERect {
+ int16_t x;
+ int16_t y;
+ uint16_t w;
+ uint16_t h;
+} EVERect;
+
+void eve_command(uint8_t command, uint8_t parameter);
+
+uint8_t eve_read8(uint32_t addr);
+uint16_t eve_read16(uint32_t addr);
+uint32_t eve_read32(uint32_t addr);
+void eve_write8(uint32_t addr, uint8_t data);
+void eve_write16(uint32_t addr, uint16_t data);
+void eve_write32(uint32_t addr, uint32_t data);
+
+void eve_readb(uint32_t addr, uint8_t *buf, size_t size);
+void eve_writeb(uint32_t addr, uint8_t *buf, size_t size);
+
+void eve_dl_start(uint32_t addr, char burst);
+void eve_dl_write(uint32_t dl);
+void eve_dl_end(void);
+void eve_dl_swap(void);
+uint32_t eve_dl_get_addr(void);
+
+void eve_cmd(uint32_t cmd, const char *fmt, ...);
+void eve_cmd_write(uint8_t *buffer, size_t size);
+void eve_cmd_end(void);
+uint32_t eve_cmd_result(uint16_t offset);
+void eve_cmd_dl(uint32_t dl);
+int eve_cmd_done(void);
+int eve_cmd_exec(int w);
+void eve_cmd_burst_start(void);
+void eve_cmd_burst_end(void);
+
+int eve_gpio_get(int gpio);
+void eve_gpio_set(int gpio, int val);
+uint8_t eve_gpio_get_dir(void);
+void eve_gpio_set_dir(uint8_t dir);
+
+void eve_active(void);
+void eve_standby(void);
+void eve_sleep(void);
+void eve_wake(void);
+void eve_brightness(uint8_t b);
+
+int eve_init(int pwr_on, int touch_calibrate, uint32_t *touch_matrix, uint8_t gpio_dir);
diff --git a/fw/fe310/eos/eve/eve_config.h b/fw/fe310/eos/eve/eve_config.h
new file mode 100755
index 0000000..882ea89
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_config.h
@@ -0,0 +1,39 @@
+#ifndef EVE_CONFIG_H_
+#define EVE_CONFIG_H_
+
+/* my display */
+
+#define EVE_TH 1200L
+#define EVE_THD 800L
+#define EVE_THF 210L
+#define EVE_THP 20L
+#define EVE_THB 46L
+
+#define EVE_TV 650L
+#define EVE_TVD 480L
+#define EVE_TVF 22L
+#define EVE_TVP 12L
+#define EVE_TVB 23L
+
+
+#define EVE_HSIZE (EVE_THD) /* Thd Length of visible part of line (in PCLKs) - display width */
+#define EVE_HSYNC0 (EVE_THF) /* Thf Horizontal Front Porch */
+#define EVE_HSYNC1 (EVE_THF + EVE_THP) /* Thf + Thp Horizontal Front Porch plus Hsync Pulse width */
+#define EVE_HOFFSET (EVE_THF + EVE_THP + EVE_THB) /* Thf + Thp + Thb Length of non-visible part of line (in PCLK cycles) */
+#define EVE_HCYCLE (EVE_TH) /* Th Total length of line (visible and non-visible) (in PCLKs) */
+
+#define EVE_VSIZE (EVE_TVD) /* Tvd Number of visible lines (in lines) - display height */
+#define EVE_VSYNC0 (EVE_TVF) /* Tvf Vertical Front Porch */
+#define EVE_VSYNC1 (EVE_TVF + EVE_TVP) /* Tvf + Tvp Vertical Front Porch plus Vsync Pulse width */
+#define EVE_VOFFSET (EVE_TVF + EVE_TVP + EVE_TVB) /* Tvf + Tvp + Tvb Number of non-visible lines (in lines) */
+#define EVE_VCYCLE (EVE_TV) /* Tv Total number of lines (visible and non-visible) (in lines) */
+
+#define EVE_PCLKPOL (1L) /* PCLK polarity (0 = rising edge, 1 = falling edge) */
+#define EVE_SWIZZLE (0L) /* Defines the arrangement of the RGB pins of the FT800 */
+#define EVE_PCLK (1L) /* 60MHz / REG_PCLK = PCLK frequency - 30 MHz */
+#define EVE_CSPREAD (0L) /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */
+#define EVE_TOUCH_RZTHRESH (1200L) /* touch-sensitivity */
+#define EVE_HAS_CRYSTAL
+#define FT81X_ENABLE
+
+#endif /* EVE_CONFIG_H */
diff --git a/fw/fe310/eos/eve/eve_def.h b/fw/fe310/eos/eve/eve_def.h
new file mode 100755
index 0000000..25e4927
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_def.h
@@ -0,0 +1,872 @@
+/*
+@file EVE.h
+@brief Contains FT80x/FT81x/BT81x API definitions
+@version 4.0
+@date 2019-11-17
+@author Rudolph Riedel
+
+@section LICENSE
+
+MIT License
+
+Copyright (c) 2016-2019 Rudolph Riedel
+
+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.
+
+@section History
+
+2.1
+- changes to this header
+
+2.2
+- commented out "#define DISPLAY() ((0UL<<24))" as it collides with a define in the Arduino IDE - the whole section of "macros" needs a rework...
+
+3.0
+- renamed from FT800.h to FT8.h
+- changed FT_ prefixes to FT8_
+- switched to standard-C compliant comment-style
+- changed FT81x register definitions from decimal to hex
+- verified all FT81x register definitions
+- moved FT81x registers marked as "reserved" to an #if 0 block
+
+3.1
+- moved several undocumented commands to an #if 0 block
+
+3.2
+- moved CMD_CRC to the block of undocumented commands as well
+
+3.3
+- changed macros BITMAP_LAYOUT_H and BITMAP_SIZE_H as submitted to github by "jventerprises"
+ These macros provide the extended bits for bitmaps bigger than 511 pixels, FTDIs original implementation that I used and never touched takes the arguments
+ as already processed and not the original values.
+ Note the one example in "FT81x Series Programmers Guide" Version 1.1 for displaying a 800x480 sized bitmap:
+ dl(BITMAP_SIZE_H(1, 0));
+ dl(BITMAP_SIZE(NEAREST, BORDER, BORDER, 288, 480));
+ Now you can use it like this:
+ EVE_cmd_dl(BITMAP_SIZE_H(800, 480));
+ EVE_cmd_dl(BITMAP_SIZE(NEAREST, BORDER, BORDER, 800, 480));
+
+4.0
+- renamed from FT8.h to EVE.h
+- renamed EVE_81X_ENABLE to FT81X_ENABLE
+- changed FT8_ prefixes to EVE_
+- rearranged things a bit with FT80x specific includes moved to the end and a "#if defined (BT81X_ENABLE)" block on top of the chip-specific includes
+- started to add specific BT81x defines
+- minor maintenance
+- changed OPT_FLASH to EVE_OPT_FLASH and OPT_FORMAT to EVE_OPT_FORMAT for consistency
+- added EVE_OPT_FILL which has been left out of the documentation for the BT81x so far
+- added a few BT81x specific macros
+- added a few FT81x/BT81x specific host commands
+- removed the preceding underscore from the include guard define to avoid potential undefined behavior
+- removed a bunch of defines for FT80x that I never implemented for FT81x
+
+*/
+
+#include "eve_config.h"
+
+#ifndef EVE_H_
+#define EVE_H_
+
+
+#define DL_CLEAR 0x26000000UL /* requires OR'd arguments */
+#define DL_CLEAR_RGB 0x02000000UL /* requires OR'd arguments */
+#define DL_COLOR_RGB 0x04000000UL /* requires OR'd arguments */
+#define DL_POINT_SIZE 0x0D000000UL /* requires OR'd arguments */
+#define DL_END 0x21000000UL
+#define DL_BEGIN 0x1F000000UL /* requires OR'd arguments */
+#define DL_DISPLAY 0x00000000UL
+
+#define CLR_COL 0x4
+#define CLR_STN 0x2
+#define CLR_TAG 0x1
+
+
+/* Host commands */
+#define EVE_ACTIVE 0x00 /* place FT8xx in active state */
+#define EVE_STANDBY 0x41 /* place FT8xx in Standby (clk running) */
+#define EVE_SLEEP 0x42 /* place FT8xx in Sleep (clk off) */
+#define EVE_PWRDOWN 0x50 /* place FT8xx in Power Down (core off) */
+#define EVE_CLKEXT 0x44 /* select external clock source */
+#define EVE_CLKINT 0x48 /* select internal clock source */
+#define EVE_CORERST 0x68 /* reset core - all registers default and processors reset */
+#define EVE_CLK48M 0x62 /* select 48MHz PLL output */
+#define EVE_CLK36M 0x61 /* select 36MHz PLL output */
+
+
+/* defines used for graphics commands */
+#define EVE_NEVER 0UL
+#define EVE_LESS 1UL
+#define EVE_LEQUAL 2UL
+#define EVE_GREATER 3UL
+#define EVE_GEQUAL 4UL
+#define EVE_EQUAL 5UL
+#define EVE_NOTEQUAL 6UL
+#define EVE_ALWAYS 7UL
+
+
+/* Bitmap formats */
+#define EVE_ARGB1555 0UL
+#define EVE_L1 1UL
+#define EVE_L4 2UL
+#define EVE_L8 3UL
+#define EVE_RGB332 4UL
+#define EVE_ARGB2 5UL
+#define EVE_ARGB4 6UL
+#define EVE_RGB565 7UL
+#define EVE_PALETTED 8UL
+#define EVE_TEXT8X8 9UL
+#define EVE_TEXTVGA 10UL
+#define EVE_BARGRAPH 11UL
+
+
+/* Bitmap filter types */
+#define EVE_NEAREST 0UL
+#define EVE_BILINEAR 1UL
+
+
+/* Bitmap wrap types */
+#define EVE_BORDER 0UL
+#define EVE_REPEAT 1UL
+
+
+/* Stencil defines */
+#define EVE_KEEP 1UL
+#define EVE_REPLACE 2UL
+#define EVE_INCR 3UL
+#define EVE_DECR 4UL
+#define EVE_INVERT 5UL
+
+
+/* Graphics display list swap defines */
+#define EVE_DLSWAP_DONE 0UL
+#define EVE_DLSWAP_LINE 1UL
+#define EVE_DLSWAP_FRAME 2UL
+
+
+/* Interrupt bits */
+#define EVE_INT_SWAP 0x01
+#define EVE_INT_TOUCH 0x02
+#define EVE_INT_TAG 0x04
+#define EVE_INT_SOUND 0x08
+#define EVE_INT_PLAYBACK 0x10
+#define EVE_INT_CMDEMPTY 0x20
+#define EVE_INT_CMDFLAG 0x40
+#define EVE_INT_CONVCOMPLETE 0x80
+
+
+/* Touch mode */
+#define EVE_TMODE_OFF 0
+#define EVE_TMODE_ONESHOT 1
+#define EVE_TMODE_FRAME 2
+#define EVE_TMODE_CONTINUOUS 3
+
+
+/* Alpha blending */
+#define EVE_ZERO 0UL
+#define EVE_ONE 1UL
+#define EVE_SRC_ALPHA 2UL
+#define EVE_DST_ALPHA 3UL
+#define EVE_ONE_MINUS_SRC_ALPHA 4UL
+#define EVE_ONE_MINUS_DST_ALPHA 5UL
+
+
+/* Graphics primitives */
+#define EVE_BITMAPS 1UL
+#define EVE_POINTS 2UL
+#define EVE_LINES 3UL
+#define EVE_LINE_STRIP 4UL
+#define EVE_EDGE_STRIP_R 5UL
+#define EVE_EDGE_STRIP_L 6UL
+#define EVE_EDGE_STRIP_A 7UL
+#define EVE_EDGE_STRIP_B 8UL
+#define EVE_RECTS 9UL
+
+
+/* Widget command */
+#define EVE_OPT_MONO 1
+#define EVE_OPT_NODL 2
+#define EVE_OPT_FLAT 256
+#define EVE_OPT_CENTERX 512
+#define EVE_OPT_CENTERY 1024
+#define EVE_OPT_CENTER (EVE_OPT_CENTERX | EVE_OPT_CENTERY)
+#define EVE_OPT_NOBACK 4096
+#define EVE_OPT_NOTICKS 8192
+#define EVE_OPT_NOHM 16384
+#define EVE_OPT_NOPOINTER 16384
+#define EVE_OPT_NOSECS 32768
+#define EVE_OPT_NOHANDS 49152
+#define EVE_OPT_RIGHTX 2048
+#define EVE_OPT_SIGNED 256
+
+
+/* Defines related to inbuilt font */
+#define EVE_NUMCHAR_PERFONT (128L) /* number of font characters per bitmap handle */
+#define EVE_FONT_TABLE_SIZE (148L) /* size of the font table - utilized for loopup by the graphics engine */
+#define EVE_FONT_TABLE_POINTER (0xFFFFCUL) /* pointer to the inbuilt font tables starting from bitmap handle 16 */
+
+
+/* Audio sample type defines */
+#define EVE_LINEAR_SAMPLES 0UL /* 8bit signed samples */
+#define EVE_ULAW_SAMPLES 1UL /* 8bit ulaw samples */
+#define EVE_ADPCM_SAMPLES 2UL /* 4bit ima adpcm samples */
+
+
+/* Synthesized sound */
+#define EVE_SILENCE 0x00
+#define EVE_SQUAREWAVE 0x01
+#define EVE_SINEWAVE 0x02
+#define EVE_SAWTOOTH 0x03
+#define EVE_TRIANGLE 0x04
+#define EVE_BEEPING 0x05
+#define EVE_ALARM 0x06
+#define EVE_WARBLE 0x07
+#define EVE_CAROUSEL 0x08
+#define EVE_PIPS(n) (0x0F + (n))
+#define EVE_HARP 0x40
+#define EVE_XYLOPHONE 0x41
+#define EVE_TUBA 0x42
+#define EVE_GLOCKENSPIEL 0x43
+#define EVE_ORGAN 0x44
+#define EVE_TRUMPET 0x45
+#define EVE_PIANO 0x46
+#define EVE_CHIMES 0x47
+#define EVE_MUSICBOX 0x48
+#define EVE_BELL 0x49
+#define EVE_CLICK 0x50
+#define EVE_SWITCH 0x51
+#define EVE_COWBELL 0x52
+#define EVE_NOTCH 0x53
+#define EVE_HIHAT 0x54
+#define EVE_KICKDRUM 0x55
+#define EVE_POP 0x56
+#define EVE_CLACK 0x57
+#define EVE_CHACK 0x58
+#define EVE_MUTE 0x60
+#define EVE_UNMUTE 0x61
+
+
+/* Synthesized sound frequencies, midi note */
+#define EVE_MIDI_A0 21
+#define EVE_MIDI_A_0 22
+#define EVE_MIDI_B0 23
+#define EVE_MIDI_C1 24
+#define EVE_MIDI_C_1 25
+#define EVE_MIDI_D1 26
+#define EVE_MIDI_D_1 27
+#define EVE_MIDI_E1 28
+#define EVE_MIDI_F1 29
+#define EVE_MIDI_F_1 30
+#define EVE_MIDI_G1 31
+#define EVE_MIDI_G_1 32
+#define EVE_MIDI_A1 33
+#define EVE_MIDI_A_1 34
+#define EVE_MIDI_B1 35
+#define EVE_MIDI_C2 36
+#define EVE_MIDI_C_2 37
+#define EVE_MIDI_D2 38
+#define EVE_MIDI_D_2 39
+#define EVE_MIDI_E2 40
+#define EVE_MIDI_F2 41
+#define EVE_MIDI_F_2 42
+#define EVE_MIDI_G2 43
+#define EVE_MIDI_G_2 44
+#define EVE_MIDI_A2 45
+#define EVE_MIDI_A_2 46
+#define EVE_MIDI_B2 47
+#define EVE_MIDI_C3 48
+#define EVE_MIDI_C_3 49
+#define EVE_MIDI_D3 50
+#define EVE_MIDI_D_3 51
+#define EVE_MIDI_E3 52
+#define EVE_MIDI_F3 53
+#define EVE_MIDI_F_3 54
+#define EVE_MIDI_G3 55
+#define EVE_MIDI_G_3 56
+#define EVE_MIDI_A3 57
+#define EVE_MIDI_A_3 58
+#define EVE_MIDI_B3 59
+#define EVE_MIDI_C4 60
+#define EVE_MIDI_C_4 61
+#define EVE_MIDI_D4 62
+#define EVE_MIDI_D_4 63
+#define EVE_MIDI_E4 64
+#define EVE_MIDI_F4 65
+#define EVE_MIDI_F_4 66
+#define EVE_MIDI_G4 67
+#define EVE_MIDI_G_4 68
+#define EVE_MIDI_A4 69
+#define EVE_MIDI_A_4 70
+#define EVE_MIDI_B4 71
+#define EVE_MIDI_C5 72
+#define EVE_MIDI_C_5 73
+#define EVE_MIDI_D5 74
+#define EVE_MIDI_D_5 75
+#define EVE_MIDI_E5 76
+#define EVE_MIDI_F5 77
+#define EVE_MIDI_F_5 78
+#define EVE_MIDI_G5 79
+#define EVE_MIDI_G_5 80
+#define EVE_MIDI_A5 81
+#define EVE_MIDI_A_5 82
+#define EVE_MIDI_B5 83
+#define EVE_MIDI_C6 84
+#define EVE_MIDI_C_6 85
+#define EVE_MIDI_D6 86
+#define EVE_MIDI_D_6 87
+#define EVE_MIDI_E6 88
+#define EVE_MIDI_F6 89
+#define EVE_MIDI_F_6 90
+#define EVE_MIDI_G6 91
+#define EVE_MIDI_G_6 92
+#define EVE_MIDI_A6 93
+#define EVE_MIDI_A_6 94
+#define EVE_MIDI_B6 95
+#define EVE_MIDI_C7 96
+#define EVE_MIDI_C_7 97
+#define EVE_MIDI_D7 98
+#define EVE_MIDI_D_7 99
+#define EVE_MIDI_E7 100
+#define EVE_MIDI_F7 101
+#define EVE_MIDI_F_7 102
+#define EVE_MIDI_G7 103
+#define EVE_MIDI_G_7 104
+#define EVE_MIDI_A7 105
+#define EVE_MIDI_A_7 106
+#define EVE_MIDI_B7 107
+#define EVE_MIDI_C8 108
+
+
+/* GPIO bits */
+#define EVE_GPIO0 0
+#define EVE_GPIO1 1 /* default gpio pin for audio shutdown, 1 - enable, 0 - disable */
+#define EVE_GPIO7 7 /* default gpio pin for display enable, 1 - enable, 0 - disable */
+
+
+/* Display rotation */
+#define EVE_DISPLAY_0 0 /* 0 degrees rotation */
+#define EVE_DISPLAY_180 1 /* 180 degrees rotation */
+
+
+/* commands common to EVE/EVE2/EVE3 */
+#define CMD_APPEND 0xFFFFFF1E
+#define CMD_BGCOLOR 0xFFFFFF09
+#define CMD_BUTTON 0xFFFFFF0D
+#define CMD_CALIBRATE 0xFFFFFF15
+#define CMD_CLOCK 0xFFFFFF14
+#define CMD_COLDSTART 0xFFFFFF32
+#define CMD_DIAL 0xFFFFFF2D
+#define CMD_DLSTART 0xFFFFFF00
+#define CMD_FGCOLOR 0xFFFFFF0A
+#define CMD_GAUGE 0xFFFFFF13
+#define CMD_GETMATRIX 0xFFFFFF33
+#define CMD_GETPROPS 0xFFFFFF25
+#define CMD_GETPTR 0xFFFFFF23
+#define CMD_GRADCOLOR 0xFFFFFF34
+#define CMD_GRADIENT 0xFFFFFF0B
+#define CMD_INFLATE 0xFFFFFF22
+#define CMD_INTERRUPT 0xFFFFFF02
+#define CMD_KEYS 0xFFFFFF0E
+#define CMD_LOADIDENTITY 0xFFFFFF26
+#define CMD_LOADIMAGE 0xFFFFFF24
+#define CMD_LOGO 0xFFFFFF31
+#define CMD_MEMCPY 0xFFFFFF1D
+#define CMD_MEMCRC 0xFFFFFF18
+#define CMD_MEMSET 0xFFFFFF1B
+#define CMD_MEMWRITE 0xFFFFFF1A
+#define CMD_MEMZERO 0xFFFFFF1C
+#define CMD_NUMBER 0xFFFFFF2E
+#define CMD_PROGRESS 0xFFFFFF0F
+#define CMD_REGREAD 0xFFFFFF19
+#define CMD_ROTATE 0xFFFFFF29
+#define CMD_SCALE 0xFFFFFF28
+#define CMD_SCREENSAVER 0xFFFFFF2F
+#define CMD_SCROLLBAR 0xFFFFFF11
+#define CMD_SETFONT 0xFFFFFF2B
+#define CMD_SETMATRIX 0xFFFFFF2A
+#define CMD_SKETCH 0xFFFFFF30
+#define CMD_SLIDER 0xFFFFFF10
+#define CMD_SNAPSHOT 0xFFFFFF1F
+#define CMD_SPINNER 0xFFFFFF16
+#define CMD_STOP 0xFFFFFF17
+#define CMD_SWAP 0xFFFFFF01
+#define CMD_TEXT 0xFFFFFF0C
+#define CMD_TOGGLE 0xFFFFFF12
+#define CMD_TRACK 0xFFFFFF2C
+#define CMD_TRANSLATE 0xFFFFFF27
+
+
+/* the following are undocumented commands that therefore should not be used */
+#if 0
+#define CMD_CRC 0xFFFFFF03
+#define CMD_HAMMERAUX 0xFFFFFF04
+#define CMD_MARCH 0xFFFFFF05
+#define CMD_IDCT 0xFFFFFF06
+#define CMD_EXECUTE 0xFFFFFF07
+#define CMD_GETPOINT 0xFFFFFF08
+#define CMD_TOUCH_TRANSFORM 0xFFFFFF20
+#endif
+
+
+/* FT8xx graphics engine specific macros useful for static display list generation */
+#define ALPHA_FUNC(func,ref) ((9UL<<24)|(((func)&7UL)<<8)|(((ref)&255UL)<<0))
+#define BEGIN(prim) ((31UL<<24)|(((prim)&15UL)<<0))
+#define BITMAP_HANDLE(handle) ((5UL<<24)|(((handle)&31UL)<<0))
+#define BITMAP_LAYOUT(format,linestride,height) ((7UL<<24)|(((format)&31UL)<<19)|(((linestride)&1023UL)<<9)|(((height)&511UL)<<0))
+#define BITMAP_SIZE(filter,wrapx,wrapy,width,height) ((8UL<<24)|(((filter)&1UL)<<20)|(((wrapx)&1UL)<<19)|(((wrapy)&1UL)<<18)|(((width)&511UL)<<9)|(((height)&511UL)<<0))
+#define BITMAP_TRANSFORM_A(a) ((21UL<<24)|(((a)&131071UL)<<0))
+#define BITMAP_TRANSFORM_B(b) ((22UL<<24)|(((b)&131071UL)<<0))
+#define BITMAP_TRANSFORM_C(c) ((23UL<<24)|(((c)&16777215UL)<<0))
+#define BITMAP_TRANSFORM_D(d) ((24UL<<24)|(((d)&131071UL)<<0))
+#define BITMAP_TRANSFORM_E(e) ((25UL<<24)|(((e)&131071UL)<<0))
+#define BITMAP_TRANSFORM_F(f) ((26UL<<24)|(((f)&16777215UL)<<0))
+#define BLEND_FUNC(src,dst) ((11UL<<24)|(((src)&7UL)<<3)|(((dst)&7UL)<<0))
+#define CALL(dest) ((29UL<<24)|(((dest)&65535UL)<<0))
+#define CELL(cell) ((6UL<<24)|(((cell)&127UL)<<0))
+#define CLEAR(c,s,t) ((38UL<<24)|(((c)&1UL)<<2)|(((s)&1UL)<<1)|(((t)&1UL)<<0))
+#define CLEAR_COLOR_A(alpha) ((15UL<<24)|(((alpha)&255UL)<<0))
+#define CLEAR_COLOR_RGB(red,green,blue) ((2UL<<24)|(((red)&255UL)<<16)|(((green)&255UL)<<8)|(((blue)&255UL)<<0))
+#define CLEAR_STENCIL(s) ((17UL<<24)|(((s)&255UL)<<0))
+#define CLEAR_TAG(s) ((18UL<<24)|(((s)&255UL)<<0))
+#define COLOR_A(alpha) ((16UL<<24)|(((alpha)&255UL)<<0))
+#define COLOR_MASK(r,g,b,a) ((32UL<<24)|(((r)&1UL)<<3)|(((g)&1UL)<<2)|(((b)&1UL)<<1)|(((a)&1UL)<<0))
+#define COLOR_RGB(red,green,blue) ((4UL<<24)|(((red)&255UL)<<16)|(((green)&255UL)<<8)|(((blue)&255UL)<<0))
+#define DISPLAY() ((0UL<<24))
+#define END() ((33UL<<24))
+#define JUMP(dest) ((30UL<<24)|(((dest)&65535UL)<<0))
+#define LINE_WIDTH(width) ((14UL<<24)|(((width)&4095UL)<<0))
+#define MACRO(m) ((37UL<<24)|(((m)&1UL)<<0))
+#define POINT_SIZE(size) ((13UL<<24)|(((size)&8191UL)<<0))
+#define RESTORE_CONTEXT() ((35UL<<24))
+#define RETURN() ((36UL<<24))
+#define SAVE_CONTEXT() ((34UL<<24))
+#define STENCIL_FUNC(func,ref,mask) ((10UL<<24)|(((func)&7UL)<<16)|(((ref)&255UL)<<8)|(((mask)&255UL)<<0))
+#define STENCIL_MASK(mask) ((19UL<<24)|(((mask)&255UL)<<0))
+#define STENCIL_OP(sfail,spass) ((12UL<<24)|(((sfail)&7UL)<<3)|(((spass)&7UL)<<0))
+#define TAG(s) ((3UL<<24)|(((s)&255UL)<<0))
+#define TAG_MASK(mask) ((20UL<<24)|(((mask)&1UL)<<0))
+#define VERTEX2F(x,y) ((1UL<<30)|(((x)&32767UL)<<15)|(((y)&32767UL)<<0))
+#define VERTEX2II(x,y,handle,cell) ((2UL<<30)|(((x)&511UL)<<21)|(((y)&511UL)<<12)|(((handle)&31UL)<<7)|(((cell)&127UL)<<0))
+
+
+/* ----------------- BT81x exclusive definitions -----------------*/
+#if defined (BT81X_ENABLE)
+
+#define EVE_GLFORMAT 31UL /* used with BITMAP_LAYOUT to indicate bitmap-format is specified by BITMAP_EXT_FORMAT */
+
+#define DL_BITMAP_EXT_FORMAT 0x2E000000 /* requires OR'd arguments */
+
+/* extended Bitmap formats */
+#define EVE_COMPRESSED_RGBA_ASTC_4x4_KHR 37808UL
+#define EVE_COMPRESSED_RGBA_ASTC_5x4_KHR 37809UL
+#define EVE_COMPRESSED_RGBA_ASTC_5x5_KHR 37810UL
+#define EVE_COMPRESSED_RGBA_ASTC_6x5_KHR 37811UL
+#define EVE_COMPRESSED_RGBA_ASTC_6x6_KHR 37812UL
+#define EVE_COMPRESSED_RGBA_ASTC_8x5_KHR 37813UL
+#define EVE_COMPRESSED_RGBA_ASTC_8x6_KHR 37814UL
+#define EVE_COMPRESSED_RGBA_ASTC_8x8_KHR 37815UL
+#define EVE_COMPRESSED_RGBA_ASTC_10x5_KHR 37816UL
+#define EVE_COMPRESSED_RGBA_ASTC_10x6_KHR 37817UL
+#define EVE_COMPRESSED_RGBA_ASTC_10x8_KHR 37818UL
+#define EVE_COMPRESSED_RGBA_ASTC_10x10_KHR 37819UL
+#define EVE_COMPRESSED_RGBA_ASTC_12x10_KHR 37820UL
+#define EVE_COMPRESSED_RGBA_ASTC_12x12_KHR 37821UL
+
+
+#define EVE_RAM_ERR_REPORT 0x309800UL /* max 128 bytes null terminated string */
+#define EVE_RAM_FLASH 0x800000UL
+#define EVE_RAM_FLASH_POSTBLOB 0x801000UL
+
+#define EVE_OPT_FLASH 64UL
+#define EVE_OPT_FORMAT 4096UL
+#define EVE_OPT_FILL 8192UL
+
+
+/* additional commands for BT81x */
+#define CMD_BITMAP_TRANSFORM 0xFFFFFF21
+#define CMD_SYNC 0xFFFFFF42 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_SYNC) */
+#define CMD_FLASHERASE 0xFFFFFF44 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHERASE) */
+#define CMD_FLASHWRITE 0xFFFFFF45
+#define CMD_FLASHREAD 0xFFFFFF46
+#define CMD_FLASHUPDATE 0xFFFFFF47
+#define CMD_FLASHDETACH 0xFFFFFF48 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHDETACH) */
+#define CMD_FLASHATTACH 0xFFFFFF49 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHATTACH) */
+#define CMD_FLASHFAST 0xFFFFFF4A
+#define CMD_FLASHSPIDESEL 0xFFFFFF4B /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHSPIDESEL) */
+#define CMD_FLASHSPITX 0xFFFFFF4C
+#define CMD_FLASHSPIRX 0xFFFFFF4D
+#define CMD_FLASHSOURCE 0xFFFFFF4E
+#define CMD_CLEARCACHE 0xFFFFFF4F /* does not need a dedicated function, just use EVE_cmd_dl(CMD_CLEARCACHE) */
+#define CMD_INFLATE2 0xFFFFFF50
+#define CMD_ROTATEAROUND 0xFFFFFF51
+#define CMD_RESETFONTS 0xFFFFFF52 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_RESETFONTS) */
+#define CMD_ANIMSTART 0xFFFFFF53
+#define CMD_ANIMSTOP 0xFFFFFF54
+#define CMD_ANIMXY 0xFFFFFF55
+#define CMD_ANIMDRAW 0xFFFFFF56
+#define CMD_GRADIENTA 0xFFFFFF57
+#define CMD_FILLWIDTH 0xFFFFFF58
+#define CMD_APPENDF 0xFFFFFF59
+#define CMD_ANIMFRAME 0xFFFFFF5A
+#define CMD_VIDEOSTARTF 0xFFFFFF5F /* does not need a dedicated function, just use EVE_cmd_dl(CMD_VIDEOSTARTF) */
+
+#if 0
+/* some undocumented commands for BT81x */
+#define CMD_NOP 0xFFFFFF5B
+#define CMD_SHA1 0xFFFFFF5C
+#define CMD_HMAC 0xFFFFFF5D
+#define CMD_LAST_ 0xFFFFFF5E
+
+#endif
+
+
+/* additional registers for BT81x */
+#define REG_ADAPTIVE_FRAMERATE 0x30257cUL
+#define REG_PLAYBACK_PAUSE 0x3025ecUL
+#define REG_FLASH_STATUS 0x3025f0UL
+#define REG_FLASH_SIZE 0x309024UL
+#define REG_PLAY_CONTROL 0x30914eUL
+#define REG_COPRO_PATCH_DTR 0x309162UL
+
+
+/* BT81x graphics engine specific macros */
+#define BITMAP_EXT_FORMAT(format) ((46UL<<24)|(((format)&65535UL)<<0))
+#define BITMAP_SWIZZLE(r,g,b,a) ((47UL<<24)|(((r)&7UL)<<9)|(((g)&7UL)<<6)|(((b)&7UL)<<3)|(((a)&7UL)<<0))
+#define BITMAP_SOURCE2(flash_or_ram, addr) ((1UL<<24)|((flash_or_ram) << 23)|(((addr)&8388607UL)<<0))
+#define INT_FRR() ((48UL<<24))
+
+#undef BITMAP_TRANSFORM_A
+#undef BITMAP_TRANSFORM_B
+#undef BITMAP_TRANSFORM_D
+#undef BITMAP_TRANSFORM_E
+
+#define BITMAP_TRANSFORM_A_EXT(p,v) ((21UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0))
+#define BITMAP_TRANSFORM_B_EXT(p,v) ((22UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0))
+#define BITMAP_TRANSFORM_D_EXT(p,v) ((24UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0))
+#define BITMAP_TRANSFORM_E_EXT(p,v) ((25UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0))
+
+#define BITMAP_TRANSFORM_A(a) BITMAP_TRANSFORM_A_EXT(0,a)
+#define BITMAP_TRANSFORM_B(b) BITMAP_TRANSFORM_B_EXT(0,b)
+#define BITMAP_TRANSFORM_D(d) BITMAP_TRANSFORM_D_EXT(0,d)
+#define BITMAP_TRANSFORM_E(e) BITMAP_TRANSFORM_E_EXT(0,e)
+
+#endif
+
+/* ----------------- FT81x / BT81x exclusive definitions -----------------*/
+#if defined (FT81X_ENABLE)
+
+
+/* Host commands */
+#define EVE_CLKSEL 0x61 /* configure system clock */
+#define EVE_RST_PULSE 0x68 /* reset core - all registers default and processors reset */
+#define EVE_PINDRIVE 0x70 /* setup drive strength for various pins */
+#define EVE_PIN_PD_STATE 0x71 /* setup how pins behave during power down */
+
+
+/* Memory definitions */
+#define EVE_RAM_G 0x000000UL
+#define EVE_ROM_CHIPID 0x0C0000UL
+#define EVE_ROM_FONT 0x1E0000UL
+#define EVE_ROM_FONT_ADDR 0x2FFFFCUL
+#define EVE_RAM_DL 0x300000UL
+#define EVE_RAM_REG 0x302000UL
+#define EVE_RAM_CMD 0x308000UL
+
+
+/* Memory buffer sizes */
+#define EVE_RAM_G_SIZE 1024*1024L
+#define EVE_CMDFIFO_SIZE 4*1024L
+#define EVE_RAM_DL_SIZE 8*1024L
+
+
+/* various additional defines for FT81x */
+#define EVE_ADC_DIFFERENTIAL 1UL
+#define EVE_ADC_SINGLE_ENDED 0UL
+
+#define EVE_INT_G8 18UL
+#define EVE_INT_L8C 12UL
+#define EVE_INT_VGA 13UL
+
+#define EVE_OPT_MEDIAFIFO 16UL
+#define EVE_OPT_FULLSCREEN 8UL
+#define EVE_OPT_NOTEAR 4UL
+#define EVE_OPT_SOUND 32UL
+
+#define EVE_PALETTED565 14UL
+#define EVE_PALETTED4444 15UL
+#define EVE_PALETTED8 16UL
+#define EVE_L2 17UL
+
+
+/* additional commands for FT81x */
+#define CMD_MEDIAFIFO 0xFFFFFF39
+#define CMD_PLAYVIDEO 0xFFFFFF3A
+#define CMD_ROMFONT 0xFFFFFF3F
+#define CMD_SETBASE 0xFFFFFF38
+#define CMD_SETBITMAP 0xFFFFFF43
+#define CMD_SETFONT2 0xFFFFFF3B
+#define CMD_SETROTATE 0xFFFFFF36
+#define CMD_SETSCRATCH 0xFFFFFF3C
+#define CMD_SNAPSHOT2 0xFFFFFF37
+#define CMD_VIDEOFRAME 0xFFFFFF41
+#define CMD_VIDEOSTART 0xFFFFFF40
+
+
+/* the following are undocumented commands that therefore should not be used */
+#if 0
+#define CMD_CSKETCH 0xFFFFFF35
+#define CMD_INT_RAMSHARED 0xFFFFFF3D
+#define CMD_INT_SWLOADIMAGE 0xFFFFFF3E
+#endif
+
+
+/* Register definitions */
+#define REG_ANA_COMP 0x302184UL /* only listed in datasheet */
+#define REG_BIST_EN 0x302174UL /* only listed in datasheet */
+#define REG_CLOCK 0x302008UL
+#define REG_CMDB_SPACE 0x302574UL
+#define REG_CMDB_WRITE 0x302578UL
+#define REG_CMD_DL 0x302100UL
+#define REG_CMD_READ 0x3020f8UL
+#define REG_CMD_WRITE 0x3020fcUL
+#define REG_CPURESET 0x302020UL
+#define REG_CSPREAD 0x302068UL
+#define REG_CTOUCH_EXTENDED 0x302108UL
+#define REG_CTOUCH_TOUCH0_XY 0x302124UL /* only listed in datasheet */
+#define REG_CTOUCH_TOUCH4_X 0x30216cUL
+#define REG_CTOUCH_TOUCH4_Y 0x302120UL
+#define REG_CTOUCH_TOUCH1_XY 0x30211cUL
+#define REG_CTOUCH_TOUCH2_XY 0x30218cUL
+#define REG_CTOUCH_TOUCH3_XY 0x302190UL
+#define REG_TOUCH_CONFIG 0x302168UL
+#define REG_DATESTAMP 0x302564UL /* only listed in datasheet */
+#define REG_DITHER 0x302060UL
+#define REG_DLSWAP 0x302054UL
+#define REG_FRAMES 0x302004UL
+#define REG_FREQUENCY 0x30200cUL
+#define REG_GPIO 0x302094UL
+#define REG_GPIOX 0x30209cUL
+#define REG_GPIOX_DIR 0x302098UL
+#define REG_GPIO_DIR 0x302090UL
+#define REG_HCYCLE 0x30202cUL
+#define REG_HOFFSET 0x302030UL
+#define REG_HSIZE 0x302034UL
+#define REG_HSYNC0 0x302038UL
+#define REG_HSYNC1 0x30203cUL
+#define REG_ID 0x302000UL
+#define REG_INT_EN 0x3020acUL
+#define REG_INT_FLAGS 0x3020a8UL
+#define REG_INT_MASK 0x3020b0UL
+#define REG_MACRO_0 0x3020d8UL
+#define REG_MACRO_1 0x3020dcUL
+#define REG_MEDIAFIFO_READ 0x309014UL /* only listed in programmers guide */
+#define REG_MEDIAFIFO_WRITE 0x309018UL /* only listed in programmers guide */
+#define REG_OUTBITS 0x30205cUL
+#define REG_PCLK 0x302070UL
+#define REG_PCLK_POL 0x30206cUL
+#define REG_PLAY 0x30208cUL
+#define REG_PLAYBACK_FORMAT 0x3020c4UL
+#define REG_PLAYBACK_FREQ 0x3020c0UL
+#define REG_PLAYBACK_LENGTH 0x3020b8UL
+#define REG_PLAYBACK_LOOP 0x3020c8UL
+#define REG_PLAYBACK_PLAY 0x3020ccUL
+#define REG_PLAYBACK_READPTR 0x3020bcUL
+#define REG_PLAYBACK_START 0x3020b4UL
+#define REG_PWM_DUTY 0x3020d4UL
+#define REG_PWM_HZ 0x3020d0UL
+#define REG_RENDERMODE 0x302010UL /* only listed in datasheet */
+#define REG_ROTATE 0x302058UL
+#define REG_SNAPFORMAT 0x30201cUL /* only listed in datasheet */
+#define REG_SNAPSHOT 0x302018UL /* only listed in datasheet */
+#define REG_SNAPY 0x302014UL /* only listed in datasheet */
+#define REG_SOUND 0x302088UL
+#define REG_SPI_WIDTH 0x302188UL /* listed with false offset in programmers guide V1.1 */
+#define REG_SWIZZLE 0x302064UL
+#define REG_TAG 0x30207cUL
+#define REG_TAG_X 0x302074UL
+#define REG_TAG_Y 0x302078UL
+#define REG_TAP_CRC 0x302024UL /* only listed in datasheet */
+#define REG_TAP_MASK 0x302028UL /* only listed in datasheet */
+#define REG_TOUCH_ADC_MODE 0x302108UL
+#define REG_TOUCH_CHARGE 0x30210cUL
+#define REG_TOUCH_DIRECT_XY 0x30218cUL
+#define REG_TOUCH_DIRECT_Z1Z2 0x302190UL
+#define REG_TOUCH_MODE 0x302104UL
+#define REG_TOUCH_OVERSAMPLE 0x302114UL
+#define REG_TOUCH_RAW_XY 0x30211cUL
+#define REG_TOUCH_RZ 0x302120UL
+#define REG_TOUCH_RZTHRESH 0x302118UL
+#define REG_TOUCH_SCREEN_XY 0x302124UL
+#define REG_TOUCH_SETTLE 0x302110UL
+#define REG_TOUCH_TAG 0x30212cUL
+#define REG_TOUCH_TAG1 0x302134UL /* only listed in datasheet */
+#define REG_TOUCH_TAG1_XY 0x302130UL /* only listed in datasheet */
+#define REG_TOUCH_TAG2 0x30213cUL /* only listed in datasheet */
+#define REG_TOUCH_TAG2_XY 0x302138UL /* only listed in datasheet */
+#define REG_TOUCH_TAG3 0x302144UL /* only listed in datasheet */
+#define REG_TOUCH_TAG3_XY 0x302140UL /* only listed in datasheet */
+#define REG_TOUCH_TAG4 0x30214cUL /* only listed in datasheet */
+#define REG_TOUCH_TAG4_XY 0x302148UL /* only listed in datasheet */
+#define REG_TOUCH_TAG_XY 0x302128UL
+#define REG_TOUCH_TRANSFORM_A 0x302150UL
+#define REG_TOUCH_TRANSFORM_B 0x302154UL
+#define REG_TOUCH_TRANSFORM_C 0x302158UL
+#define REG_TOUCH_TRANSFORM_D 0x30215cUL
+#define REG_TOUCH_TRANSFORM_E 0x302160UL
+#define REG_TOUCH_TRANSFORM_F 0x302164UL
+#define REG_TRACKER 0x309000UL /* only listed in programmers guide */
+#define REG_TRACKER_1 0x309004UL /* only listed in programmers guide */
+#define REG_TRACKER_2 0x309008UL /* only listed in programmers guide */
+#define REG_TRACKER_3 0x30900cUL /* only listed in programmers guide */
+#define REG_TRACKER_4 0x309010UL /* only listed in programmers guide */
+#define REG_TRIM 0x302180UL
+#define REG_VCYCLE 0x302040UL
+#define REG_VOFFSET 0x302044UL
+#define REG_VOL_PB 0x302080UL
+#define REG_VOL_SOUND 0x302084UL
+#define REG_VSIZE 0x302048UL
+#define REG_VSYNC0 0x30204cUL
+#define REG_VSYNC1 0x302050UL
+
+#if 0
+#define REG_BUSYBITS 0x3020e8UL /* only listed as "reserved" in datasheet */
+#define REG_CRC 0x302178UL /* only listed as "reserved" in datasheet */
+#define REG_SPI_EARLY_TX 0x30217cUL /* only listed as "reserved" in datasheet */
+#define REG_ROMSUB_SEL 0x3020f0UL /* only listed as "reserved" in datasheet */
+#define REG_TOUCH_FAULT 0x302170UL /* only listed as "reserved" in datasheet */
+#endif
+
+
+/* FT81x graphics engine specific macros useful for static display list generation */
+
+/* beware, these are different to FTDIs implementation as these take the original values as parameters and not only the upper bits */
+#define BITMAP_LAYOUT_H(linestride,height) ((40UL<<24)|((((linestride&0xC00)>>10)&3UL)<<2)|((((height&0x600)>>9)&3UL)<<0))
+#define BITMAP_SIZE_H(width,height) ((41UL<<24)|((((width&0x600)>>9)&3UL)<<2)|((((height&0x600)>>9)&3UL)<<0))
+
+#define BITMAP_SOURCE(addr) ((1UL<<24)|(((addr)&4194303UL)<<0))
+#define NOP() ((45UL<<24))
+#define PALETTE_SOURCE(addr) ((42UL<<24)|(((addr)&4194303UL)<<0))
+#define SCISSOR_SIZE(width,height) ((28UL<<24)|(((width)&4095UL)<<12)|(((height)&4095UL)<<0))
+#define SCISSOR_XY(x,y) ((27UL<<24)|(((x)&2047UL)<<11)|(((y)&2047UL)<<0))
+#define VERTEX_FORMAT(frac) ((39UL<<24)|(((frac)&7UL)<<0))
+#define VERTEX_TRANSLATE_X(x) ((43UL<<24)|(((x)&131071UL)<<0))
+#define VERTEX_TRANSLATE_Y(y) ((44UL<<24)|(((y)&131071UL)<<0))
+
+
+
+/* ----------------- FT80x exclusive definitions -----------------*/
+#else
+
+/* Memory definitions */
+#define EVE_RAM_G 0x000000UL
+#define EVE_ROM_CHIPID 0x0C0000UL
+#define EVE_ROM_FONT 0x0BB23CUL
+#define EVE_ROM_FONT_ADDR 0x0FFFFCUL
+#define EVE_RAM_DL 0x100000UL
+#define EVE_RAM_PAL 0x102000UL
+#define EVE_RAM_CMD 0x108000UL
+#define EVE_RAM_SCREENSHOT 0x1C2000UL
+
+
+/* Memory buffer sizes */
+#define EVE_RAM_G_SIZE 256*1024L
+#define EVE_CMDFIFO_SIZE 4*1024L
+#define EVE_RAM_DL_SIZE 8*1024L
+#define EVE_RAM_PAL_SIZE 1*1024L
+
+
+/* Register definitions */
+#define REG_ID 0x102400UL
+#define REG_FRAMES 0x102404UL
+#define REG_CLOCK 0x102408UL
+#define REG_FREQUENCY 0x10240CUL
+#define REG_SCREENSHOT_EN 0x102410UL
+#define REG_SCREENSHOT_Y 0x102414UL
+#define REG_SCREENSHOT_START 0x102418UL
+#define REG_CPURESET 0x10241CUL
+#define REG_TAP_CRC 0x102420UL
+#define REG_TAP_MASK 0x102424UL
+#define REG_HCYCLE 0x102428UL
+#define REG_HOFFSET 0x10242CUL
+#define REG_HSIZE 0x102430UL
+#define REG_HSYNC0 0x102434UL
+#define REG_HSYNC1 0x102438UL
+#define REG_VCYCLE 0x10243CUL
+#define REG_VOFFSET 0x102440UL
+#define REG_VSIZE 0x102444UL
+#define REG_VSYNC0 0x102448UL
+#define REG_VSYNC1 0x10244CUL
+#define REG_DLSWAP 0x102450UL
+#define REG_ROTATE 0x102454UL
+#define REG_OUTBITS 0x102458UL
+#define REG_DITHER 0x10245CUL
+#define REG_SWIZZLE 0x102460UL
+#define REG_CSPREAD 0x102464UL
+#define REG_PCLK_POL 0x102468UL
+#define REG_PCLK 0x10246CUL
+#define REG_TAG_X 0x102470UL
+#define REG_TAG_Y 0x102474UL
+#define REG_TAG 0x102478UL
+#define REG_VOL_PB 0x10247CUL
+#define REG_VOL_SOUND 0x102480UL
+#define REG_SOUND 0x102484UL
+#define REG_PLAY 0x102488UL
+#define REG_GPIO_DIR 0x10248CUL
+#define REG_GPIO 0x102490UL
+#define REG_INT_FLAGS 0x102498UL
+#define REG_INT_EN 0x10249CUL
+#define REG_INT_MASK 0x1024A0UL
+#define REG_PLAYBACK_START 0x1024A4UL
+#define REG_PLAYBACK_LENGTH 0x1024A8UL
+#define REG_PLAYBACK_READPTR 0x1024ACUL
+#define REG_PLAYBACK_FREQ 0x1024B0UL
+#define REG_PLAYBACK_FORMAT 0x1024B4UL
+#define REG_PLAYBACK_LOOP 0x1024B8UL
+#define REG_PLAYBACK_PLAY 0x1024BCUL
+#define REG_PWM_HZ 0x1024C0UL
+#define REG_PWM_DUTY 0x1024C4UL
+#define REG_MACRO_0 0x1024C8UL
+#define REG_MACRO_1 0x1024CCUL
+#define REG_SCREENSHOT_BUSY 0x1024D8UL
+#define REG_CMD_READ 0x1024E4UL
+#define REG_CMD_WRITE 0x1024E8UL
+#define REG_CMD_DL 0x1024ECUL
+#define REG_TOUCH_MODE 0x1024F0UL
+#define REG_TOUCH_ADC_MODE 0x1024F4UL
+#define REG_TOUCH_CHARGE 0x1024F8UL
+#define REG_TOUCH_SETTLE 0x1024FCUL
+#define REG_TOUCH_OVERSAMPLE 0x102500UL
+#define REG_TOUCH_RZTHRESH 0x102504UL
+#define REG_TOUCH_RAW_XY 0x102508UL
+#define REG_TOUCH_RZ 0x10250CUL
+#define REG_TOUCH_SCREEN_XY 0x102510UL
+#define REG_TOUCH_TAG_XY 0x102514UL
+#define REG_TOUCH_TAG 0x102518UL
+#define REG_TOUCH_TRANSFORM_A 0x10251CUL
+#define REG_TOUCH_TRANSFORM_B 0x102520UL
+#define REG_TOUCH_TRANSFORM_C 0x102524UL
+#define REG_TOUCH_TRANSFORM_D 0x102528UL
+#define REG_TOUCH_TRANSFORM_E 0x10252CUL
+#define REG_TOUCH_TRANSFORM_F 0x102530UL
+#define REG_SCREENSHOT_READ 0x102554UL
+#define REG_TRIM 0x10256CUL
+#define REG_TOUCH_DIRECT_XY 0x102574UL
+#define REG_TOUCH_DIRECT_Z1Z2 0x102578UL
+#define REG_TRACKER 0x109000UL
+
+/* FT80x graphics engine specific macros useful for static display list generation */
+#define BITMAP_SOURCE(addr) ((1UL<<24)|(((addr)&1048575UL)<<0))
+#define SCISSOR_SIZE(width,height) ((28UL<<24)|(((width)&1023UL)<<10)|(((height)&1023UL)<<0))
+#define SCISSOR_XY(x,y) ((27UL<<24)|(((x)&511UL)<<9)|(((y)&511UL)<<0))
+
+#endif
+
+#endif /* EVE_H_ */
diff --git a/fw/fe310/eos/eve/eve_font.c b/fw/fe310/eos/eve/eve_font.c
new file mode 100644
index 0000000..e1e3aee
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_font.c
@@ -0,0 +1,59 @@
+#include <stdlib.h>
+
+#include "eve.h"
+#include "eve_font.h"
+
+void eve_font_init(EVEFont *font, uint8_t font_id) {
+ uint32_t p;
+
+ p = eve_read32(EVE_ROM_FONT_ADDR);
+ p += (148 * (font_id - 16));
+ font->id = font_id;
+ font->w = eve_read32(p + 136);
+ font->h = eve_read32(p + 140);
+ eve_readb(p, font->w_ch, 128);
+}
+
+uint8_t eve_font_ch_w(EVEFont *font, utf32_t ch) {
+ if (ch < 128) return font->w_ch[ch];
+ return 0;
+}
+
+uint16_t eve_font_str_w(EVEFont *font, utf8_t *str) {
+ uint16_t r = 0;
+ utf32_t ch;
+ uint8_t ch_w;
+ uint8_t ch_l;
+
+ if (str == NULL) return 0;
+
+ while (*str) {
+ ch_l = utf8_dec(str, &ch);
+ ch_w = eve_font_ch_w(font, ch);
+ r += ch_w;
+ str += ch_l;
+ }
+
+ return r;
+}
+
+uint16_t eve_font_buf_w(EVEFont *font, utf8_t *buf, uint16_t buf_len) {
+ int i = 0;
+ uint16_t r = 0;
+ utf32_t ch;
+ uint8_t ch_w;
+ uint8_t ch_l;
+
+ while (i < buf_len) {
+ ch_l = utf8_dec(buf + i, &ch);
+ ch_w = eve_font_ch_w(font, ch);
+ r += ch_w;
+ i += ch_l;
+ }
+
+ return r;
+}
+
+uint8_t eve_font_h(EVEFont *font) {
+ return font->h;
+}
diff --git a/fw/fe310/eos/eve/eve_font.h b/fw/fe310/eos/eve/eve_font.h
new file mode 100644
index 0000000..87ef41f
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_font.h
@@ -0,0 +1,16 @@
+#include <stdint.h>
+
+#include "unicode.h"
+
+typedef struct EVEFont {
+ uint8_t id;
+ uint8_t w;
+ uint8_t h;
+ uint8_t w_ch[128];
+} EVEFont;
+
+void eve_font_init(EVEFont *font, uint8_t font_id);
+uint8_t eve_font_ch_w(EVEFont *font, utf32_t ch);
+uint16_t eve_font_str_w(EVEFont *font, utf8_t *str);
+uint16_t eve_font_buf_w(EVEFont *font, utf8_t *buf, uint16_t buf_len);
+uint8_t eve_font_h(EVEFont *font);
diff --git a/fw/fe310/eos/eve/eve_kbd.c b/fw/fe310/eos/eve/eve_kbd.c
new file mode 100644
index 0000000..c8c1362
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_kbd.c
@@ -0,0 +1,162 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+
+#define KEY_SPACERX 3
+#define KEY_SPACERY 5
+#define KEY_FONT 29
+#define MOD_FONT 21
+
+#define KEY_BS 0x08
+#define KEY_RET 0x0a
+
+#define FLAG_SHIFT 0x01
+#define FLAG_CTRL 0x02
+#define FLAG_FN 0x04
+
+#define TAG_SHIFT 0x11
+#define TAG_CTRL 0x12
+#define TAG_FN 0x13
+
+void eve_kbd_init(EVEKbd *kbd, EVERect *g, uint32_t mem_addr, uint32_t *mem_next) {
+ uint16_t mem_size;
+
+ kbd->g = *g;
+ kbd->mem_addr = mem_addr;
+ kbd->key_modifier = 0;
+ kbd->key_modifier_sticky = 0;
+ kbd->key_modifier_lock = 0;
+ kbd->key_count = 0;
+ kbd->key_down = 0;
+ kbd->putc = NULL;
+ kbd->param = NULL;
+
+ kbd->key_down = 0xff;
+ eve_write16(REG_CMD_DL, 0);
+ eve_kbd_draw(kbd);
+ eve_cmd_exec(1);
+ mem_size = eve_read16(REG_CMD_DL);
+ eve_cmd(CMD_MEMCPY, "www", mem_addr, EVE_RAM_DL, mem_size);
+ eve_cmd_exec(1);
+ kbd->key_down = 0;
+ kbd->mem_size = mem_size;
+
+ *mem_next = kbd->mem_addr + kbd->mem_size;
+}
+
+void eve_kbd_close(EVEKbd *kbd) {
+ kbd->key_modifier = 0;
+ kbd->key_modifier_sticky = 0;
+ kbd->key_modifier_lock = 0;
+ kbd->key_count = 0;
+ kbd->key_down = 0;
+ kbd->putc = NULL;
+ kbd->param = NULL;
+}
+
+void eve_kbd_set_handler(EVEKbd *kbd, eve_kbd_input_handler_t putc, void *param) {
+ kbd->putc = putc;
+ kbd->param = param;
+}
+
+int eve_kbd_touch(EVEKbd *kbd, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ int ret;
+
+ evt = eve_touch_evt(touch, evt, tag0, 1, 126);
+ if (touch && evt) {
+ int8_t touch_idx = eve_touch_get_idx(touch);
+
+ if (evt & EVE_TOUCH_ETYPE_TAG) {
+ uint8_t _tag = touch->tag;
+
+ if (_tag >= TAG_SHIFT && _tag <= TAG_FN) {
+ if (touch_idx == 0) {
+ uint8_t f = (1 << (_tag - TAG_SHIFT));
+
+ kbd->key_modifier = f;
+ kbd->key_modifier_sticky &= f;
+ kbd->key_modifier_lock &= f;
+ if (kbd->key_modifier_lock & f) {
+ kbd->key_modifier_lock &= ~f;
+ } else if (kbd->key_modifier_sticky & f) {
+ kbd->key_modifier_sticky &= ~f;
+ kbd->key_modifier_lock |= f;
+ } else {
+ kbd->key_modifier_sticky |= f;
+ }
+ }
+ } else {
+ kbd->key_count++;
+ kbd->key_down = _tag;
+ if (kbd->putc) {
+ int c = _tag;
+
+ if ((kbd->key_modifier & FLAG_CTRL) && (_tag >= '?') && (_tag <= '_')) c = (_tag - '@') & 0x7f;
+ kbd->putc(kbd->param, c);
+ }
+ }
+ }
+ if (evt & EVE_TOUCH_ETYPE_TAG_UP) {
+ uint8_t _tag = touch->tag_up;
+
+ if (_tag >= TAG_SHIFT && _tag <= TAG_FN) {
+ if (touch_idx == 0) {
+ uint8_t f = (1 << (_tag - TAG_SHIFT));
+
+ if (!((kbd->key_modifier_lock | kbd->key_modifier_sticky) & f)) {
+ kbd->key_modifier &= ~f;
+ }
+ }
+ } else {
+ if (kbd->key_count) kbd->key_count--;
+ if (!kbd->key_count) kbd->key_down = 0;
+ if (kbd->key_modifier_sticky) {
+ if (touch_idx == 0) kbd->key_modifier &= ~kbd->key_modifier_sticky;
+ kbd->key_modifier_sticky = 0;
+ }
+ }
+ }
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+uint8_t eve_kbd_draw(EVEKbd *kbd) {
+ if (kbd->key_down || kbd->key_modifier) {
+ int x = kbd->g.x;
+ int y = kbd->g.y;
+ int w = kbd->g.w;
+ int row_h = kbd->g.h / 5;
+ int key_w = (w - 9 * KEY_SPACERX) / 10 + 1;
+ int mod_w = key_w + key_w / 2;
+ int key_h = row_h - KEY_SPACERY;
+
+ eve_cmd_dl(SAVE_CONTEXT());
+ eve_cmd(CMD_KEYS, "hhhhhhs", x, y + row_h * 0, w, key_h, KEY_FONT, kbd->key_down, kbd->key_modifier & (FLAG_FN | FLAG_SHIFT) ? "!@#$%^&*()" : (kbd->key_modifier & FLAG_CTRL ? " @[\\]^_? " : "1234567890"));
+ eve_cmd(CMD_KEYS, "hhhhhhs", x, y + row_h * 1, w, key_h, KEY_FONT, kbd->key_down, kbd->key_modifier & FLAG_FN ? "-_=+[]{}\\|" : kbd->key_modifier & (FLAG_SHIFT | FLAG_CTRL) ? "QWERTYUIOP" : "qwertyuiop");
+ eve_cmd(CMD_KEYS, "hhhhhhs", x + key_w / 2, y + row_h * 2, w - key_w, key_h, KEY_FONT, kbd->key_down, kbd->key_modifier & FLAG_FN ? "`~ ;:'\"" : kbd->key_modifier & (FLAG_SHIFT | FLAG_CTRL) ? "ASDFGHJKL" : "asdfghjkl");
+ eve_cmd(CMD_KEYS, "hhhhhhs", x + mod_w + KEY_SPACERX, y + row_h * 3, w - 2 * (mod_w + KEY_SPACERX), key_h, KEY_FONT, kbd->key_down, kbd->key_modifier & FLAG_FN ? " ,.<>/?" : kbd->key_modifier & (FLAG_SHIFT | FLAG_CTRL) ? "ZXCVBNM" : "zxcvbnm");
+ eve_cmd_dl(TAG(TAG_SHIFT));
+ eve_cmd(CMD_BUTTON, "hhhhhhs", x, y + row_h * 3, mod_w, key_h, MOD_FONT, kbd->key_modifier & FLAG_SHIFT ? EVE_OPT_FLAT : 0, "shift");
+ eve_cmd_dl(TAG(KEY_BS));
+ eve_cmd(CMD_BUTTON, "hhhhhhs", x + w - mod_w, y + row_h * 3, mod_w, key_h, MOD_FONT, kbd->key_down == KEY_BS ? EVE_OPT_FLAT : 0, "del");
+ eve_cmd_dl(TAG(TAG_FN));
+ eve_cmd(CMD_BUTTON, "hhhhhhs", x, y + row_h * 4, mod_w, key_h, MOD_FONT, kbd->key_modifier & FLAG_FN ? EVE_OPT_FLAT : 0, "fn");
+ eve_cmd_dl(TAG(TAG_CTRL));
+ eve_cmd(CMD_BUTTON, "hhhhhhs", x + mod_w + KEY_SPACERX, y + row_h * 4, mod_w, key_h, MOD_FONT, kbd->key_modifier & FLAG_CTRL ? EVE_OPT_FLAT : 0, "ctrl");
+ eve_cmd_dl(TAG(' '));
+ eve_cmd(CMD_BUTTON, "hhhhhhs", x + 2 * (mod_w + KEY_SPACERX), y + row_h * 4, w - 3 * (mod_w + KEY_SPACERX), key_h, MOD_FONT, kbd->key_down == ' ' ? EVE_OPT_FLAT : 0, "");
+ eve_cmd_dl(TAG(KEY_RET));
+ eve_cmd(CMD_BUTTON, "hhhhhhs", x + w - mod_w, y + row_h * 4, mod_w, key_h, MOD_FONT, kbd->key_down == KEY_RET ? EVE_OPT_FLAT : 0, "ret");
+ eve_cmd_dl(RESTORE_CONTEXT());
+ } else {
+ eve_cmd(CMD_APPEND, "ww", kbd->mem_addr, kbd->mem_size);
+ }
+
+ return 0x80;
+}
diff --git a/fw/fe310/eos/eve/eve_kbd.h b/fw/fe310/eos/eve/eve_kbd.h
new file mode 100644
index 0000000..b4f9874
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_kbd.h
@@ -0,0 +1,23 @@
+#include <stdint.h>
+
+typedef void (*eve_kbd_input_handler_t) (void *, int);
+
+typedef struct EVEKbd {
+ EVERect g;
+ uint32_t mem_addr;
+ uint16_t mem_size;
+ uint8_t key_count;
+ uint8_t key_down;
+ uint8_t key_modifier;
+ uint8_t key_modifier_sticky;
+ uint8_t key_modifier_lock;
+ char active;
+ eve_kbd_input_handler_t putc;
+ void *param;
+} EVEKbd;
+
+void eve_kbd_init(EVEKbd *kbd, EVERect *g, uint32_t mem_addr, uint32_t *mem_next);
+void eve_kbd_close(EVEKbd *kbd);
+void eve_kbd_set_handler(EVEKbd *kbd, eve_kbd_input_handler_t putc, void *param);
+int eve_kbd_touch(EVEKbd *kbd, EVETouch *touch, uint16_t evt, uint8_t tag0);
+uint8_t eve_kbd_draw(EVEKbd *kbd);
diff --git a/fw/fe310/eos/eve/eve_phy.c b/fw/fe310/eos/eve/eve_phy.c
new file mode 100644
index 0000000..1e255fe
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_phy.c
@@ -0,0 +1,77 @@
+#include <stdlib.h>
+#include <math.h>
+
+#include "eve_platform.h"
+#include "eve_phy.h"
+
+/* Constant accelerator */
+void eve_phy_acc_init(EVEPhyAcc *param, int a) {
+ param->a = a;
+}
+
+void eve_phy_acc_start(EVEPhyAcc *param, int x0, int y0, int v0x, int v0y) {
+ double v0 = sqrt(v0x * v0x + v0y * v0y);
+
+ param->x0 = x0;
+ param->y0 = y0;
+ param->v0x = v0x;
+ param->v0y = v0y;
+ param->k = 2 * v0 / param->a * EVE_RTC_FREQ;
+}
+
+int eve_phy_acc_tick(EVEPhyAcc *param, int dt, int *x, int *y) {
+ int k = param->k;
+ int x0 = param->x0;
+ int y0 = param->y0;
+ int v0x = param->v0x;
+ int v0y = param->v0y;
+ int more = 1;
+
+ if ((k < 0) && (dt >= -k / 2)) {
+ dt = -k / 2;
+ more = 0;
+ }
+ if (x) *x = x0 + (v0x * dt + v0x * dt / k * dt) / (int)(EVE_RTC_FREQ);
+ if (y) *y = y0 + (v0y * dt + v0y * dt / k * dt) / (int)(EVE_RTC_FREQ);
+
+ return more;
+}
+
+/* Linear harmonic oscillator */
+void eve_phy_lho_init(EVEPhyLHO *param, int x, int y, uint32_t T, double d, uint32_t t_max) {
+ double f0 = 2 * M_PI / (T * EVE_RTC_FREQ / 1000);
+
+ if (d < 0) d = 0;
+ if (d > 1) d = 1;
+ param->x = x;
+ param->y = y;
+ param->f = f0 * sqrt(1 - d * d);
+ param->a = -d * f0;
+ param->t_max = t_max * EVE_RTC_FREQ / 1000;
+}
+
+int eve_phy_lho_start(EVEPhyLHO *param, int x0, int y0) {
+ param->x0 = x0;
+ param->y0 = y0;
+}
+
+int eve_phy_lho_tick(EVEPhyLHO *param, int dt, int *x, int *y) {
+ int ax = param->x0 - param->x;
+ int ay = param->y0 - param->y;
+ int more = 1;
+
+ if (param->t_max && (dt >= param->t_max)) {
+ dt = param->t_max;
+ more = 0;
+ }
+ if (param->a) {
+ double e = exp(param->a * dt);
+ ax = ax * e;
+ ay = ay * e;
+ if ((ax == 0) && (ay == 0)) more = 0;
+ }
+ if (x) *x = param->x + ax * cos(param->f * dt);
+ if (y) *y = param->y + ay * cos(param->f * dt);
+
+ return more;
+}
diff --git a/fw/fe310/eos/eve/eve_phy.h b/fw/fe310/eos/eve/eve_phy.h
new file mode 100644
index 0000000..1be5fd0
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_phy.h
@@ -0,0 +1,28 @@
+#include <stdint.h>
+
+typedef struct EVEPhyAcc {
+ int a;
+ int k;
+ int x0;
+ int y0;
+ int v0x;
+ int v0y;
+} EVEPhyAcc;
+
+void eve_phy_acc_init(EVEPhyAcc *param, int a);
+void eve_phy_acc_start(EVEPhyAcc *param, int x0, int y0, int v0x, int v0y);
+int eve_phy_acc_tick(EVEPhyAcc *param, int dt, int *x, int *y);
+
+typedef struct EVEPhyLHO {
+ int x;
+ int y;
+ double f;
+ double a;
+ uint32_t t_max;
+ int x0;
+ int y0;
+} EVEPhyLHO;
+
+void eve_phy_lho_init(EVEPhyLHO *param, int x, int y, uint32_t T, double d, uint32_t t_max);
+int eve_phy_lho_start(EVEPhyLHO *param, int x0, int y0);
+int eve_phy_lho_tick(EVEPhyLHO *param, int dt, int *x, int *y); \ No newline at end of file
diff --git a/fw/fe310/eos/eve/eve_platform.c b/fw/fe310/eos/eve/eve_platform.c
new file mode 100644
index 0000000..6bc87bb
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_platform.c
@@ -0,0 +1,80 @@
+#include <stdlib.h>
+
+#include "platform.h"
+
+#include "eos.h"
+#include "interrupt.h"
+#include "event.h"
+
+#include "eve.h"
+#include "eve_platform.h"
+
+
+static void handle_time(unsigned char type) {
+ eve_handle_time();
+}
+
+static void handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ eve_handle_touch();
+
+ GPIO_REG(GPIO_LOW_IP) = (1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_LOW_IE) |= (1 << EVE_PIN_INTR);
+}
+
+static void handle_intr(void) {
+ GPIO_REG(GPIO_LOW_IE) &= ~(1 << EVE_PIN_INTR);
+ eos_evtq_push_isr(EOS_EVT_UI | EVE_ETYPE_INTR, NULL, 0);
+ return;
+}
+
+void eve_time_sleep(uint32_t ms) {
+ eos_time_sleep(ms);
+}
+
+void eve_timer_set(uint32_t ms) {
+ eos_timer_set(ms, EOS_TIMER_ETYPE_UI);
+}
+
+void eve_timer_clear(void) {
+ eos_timer_clear(EOS_TIMER_ETYPE_UI);
+}
+
+uint64_t eve_time_get_tick(void) {
+ return eos_time_get_tick();
+}
+
+void eve_platform_init(void) {
+ eos_evtq_set_handler(EOS_EVT_UI, handle_evt);
+ eos_timer_set_handler(EOS_TIMER_ETYPE_UI, handle_time);
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << EVE_PIN_INTR);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << EVE_PIN_INTR);
+
+ GPIO_REG(GPIO_LOW_IE) |= (1 << EVE_PIN_INTR);
+ eos_intr_set(INT_GPIO_BASE + EVE_PIN_INTR, IRQ_PRIORITY_UI, handle_intr);
+
+ eos_spi_set_div(EOS_SPI_DEV_EVE, 4);
+}
+
+void eve_spi_start(void) {
+ eos_spi_select(EOS_SPI_DEV_EVE);
+}
+
+void eve_spi_stop(void) {
+ eos_spi_deselect();
+}
+
+#include <stdio.h>
+
+void *eve_malloc(size_t size) {
+ void *p = malloc(size);
+ printf("MALLOC:%p %d\n", p, size);
+ return p;
+}
+
+void eve_free(void *p) {
+ printf("FREE:%p\n", p);
+ free(p);
+}
diff --git a/fw/fe310/eos/eve/eve_platform.h b/fw/fe310/eos/eve/eve_platform.h
new file mode 100644
index 0000000..7975fbc
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_platform.h
@@ -0,0 +1,39 @@
+#include <stdint.h>
+
+#include "spi.h"
+#include "spi_dev.h"
+#include "timer.h"
+
+#define EVE_ETYPE_INTR 1
+#define EVE_PIN_INTR 0
+
+#define EVE_RTC_FREQ EOS_TIMER_RTC_FREQ
+
+#define EVE_SPI_FLAG_BSWAP EOS_SPI_FLAG_BSWAP
+#define EVE_SPI_FLAG_TX EOS_SPI_FLAG_TX
+
+void *eve_malloc(size_t);
+void eve_free(void *);
+
+//#define eve_malloc malloc
+//#define eve_free free
+
+void eve_spi_start(void);
+void eve_spi_stop(void);
+
+#define eve_spi_cs_set eos_spi_cs_set
+#define eve_spi_cs_clear eos_spi_cs_clear
+#define eve_spi_flush eos_spi_flush
+#define eve_spi_xchg8 eos_spi_xchg8
+#define eve_spi_xchg16 eos_spi_xchg16
+#define eve_spi_xchg24 eos_spi_xchg24
+#define eve_spi_xchg32 eos_spi_xchg32
+#define eve_spi_lock eos_spi_lock
+#define eve_spi_unlock eos_spi_unlock
+
+void eve_time_sleep(uint32_t ms);
+void eve_timer_set(uint32_t ms);
+void eve_timer_clear(void);
+uint64_t eve_time_get_tick(void);
+
+void eve_platform_init(void);
diff --git a/fw/fe310/eos/eve/eve_text.c b/fw/fe310/eos/eve/eve_text.c
new file mode 100644
index 0000000..b52678d
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_text.c
@@ -0,0 +1,209 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_text.h"
+
+#define TEXT_ATTR 0x0A
+#define TEXT_CRSR 0x02DB
+
+#define LINE_IDX_ADD(l1,l2,s) (((((l1) + (l2)) % (s)) + (s)) % (s))
+#define LINE_IDX_SUB(l1,l2,s) (((((l1) - (l2)) % (s)) + (s)) % (s))
+
+#define LINE_IDX_LTE(l1,l2,s,h) (LINE_IDX_SUB(l2,l1,s) <= (s) - (h))
+#define LINE_IDX_DIFF(l1,l2,s) ((l1) > (l2) ? (l1) - (l2) : (s) + (l1) - (l2))
+
+static void scroll1(EVEText *box) {
+ box->line0 = (box->line0 + 1) % box->line_size;
+ eve_cmd(CMD_MEMSET, "www", box->mem_addr + box->ch_idx, 0x0, box->w * 2);
+ eve_cmd_exec(1);
+ eve_text_update(box);
+ box->dirty = 1;
+}
+
+void eve_text_init(EVEText *box, EVERect *g, uint16_t w, uint16_t h, uint16_t line_size, uint32_t mem_addr, uint32_t *mem_next) {
+ double scale_x, scale_y;
+
+ if (g->w == 0) {
+ g->w = w * 8;
+ }
+ if (g->h == 0) {
+ g->h = h * 16;
+ }
+
+ scale_x = (double)g->w / (w * 8);
+ scale_y = (double)g->h / (h * 16);
+ box->g = *g;
+ box->w = w;
+ box->h = h;
+ box->tag = EVE_NOTAG;
+ box->transform_a = 256 / scale_x;
+ box->transform_e = 256 / scale_y;
+ box->ch_w = scale_x * 8;
+ box->ch_h = scale_y * 16;
+ box->dl_size = 14;
+ box->mem_addr = mem_addr;
+ box->line_size = line_size;
+ box->line0 = 0;
+ box->line_top = -1;
+ box->line_top0 = -1;
+ box->ch_idx = 0;
+ if (box->transform_a != 256) {
+ box->dl_size += 1;
+ }
+ if (box->transform_e != 256) {
+ box->dl_size += 1;
+ }
+
+ eve_cmd(CMD_MEMSET, "www", mem_addr, 0x0, box->w * 2 * box->line_size);
+ eve_cmd_exec(1);
+
+ eve_text_update(box);
+ *mem_next = box->mem_addr + box->w * 2 * box->line_size + box->dl_size * 4;
+}
+
+void eve_text_update(EVEText *box) {
+ int text_h1;
+ int text_h2;
+ int line_top;
+
+ line_top = box->line_top >= 0 ? box->line_top : box->line0;
+
+ if (line_top + box->h > box->line_size) {
+ text_h1 = box->line_size - line_top;
+ text_h2 = box->h - text_h1;
+ } else {
+ text_h1 = box->h;
+ text_h2 = 0;
+ }
+
+ eve_dl_start(box->mem_addr + box->w * 2 * box->line_size, 1);
+ eve_dl_write(BEGIN(EVE_BITMAPS));
+ eve_dl_write(VERTEX_FORMAT(0));
+ eve_dl_write(BITMAP_HANDLE(15));
+ if (box->transform_a != 256) {
+ eve_dl_write(BITMAP_TRANSFORM_A(box->transform_a));
+ }
+ if (box->transform_e != 256) {
+ eve_dl_write(BITMAP_TRANSFORM_E(box->transform_e));
+ }
+ eve_dl_write(BITMAP_SOURCE(box->mem_addr + line_top * box->w * 2));
+ eve_dl_write(BITMAP_LAYOUT(EVE_TEXTVGA, box->w * 2, text_h1));
+ eve_dl_write(BITMAP_SIZE(EVE_NEAREST, EVE_BORDER, EVE_BORDER, box->w * box->ch_w, text_h1 * box->ch_h));
+ eve_dl_write(BITMAP_SIZE_H(box->w * box->ch_w, text_h1 * box->ch_h));
+ eve_dl_write(VERTEX2F(box->g.x, box->g.y));
+
+ if (text_h2) {
+ eve_dl_write(BITMAP_SOURCE(box->mem_addr));
+ eve_dl_write(BITMAP_LAYOUT(EVE_TEXTVGA, box->w * 2, text_h2));
+ eve_dl_write(BITMAP_SIZE(EVE_NEAREST, EVE_BORDER, EVE_BORDER, box->w * box->ch_w, text_h2 * box->ch_h));
+ eve_dl_write(BITMAP_SIZE_H(box->w * box->ch_w, text_h2 * box->ch_h));
+ eve_dl_write(VERTEX2F(box->g.x, box->g.y + text_h1 * box->ch_h));
+ } else {
+ eve_dl_write(NOP());
+ eve_dl_write(NOP());
+ eve_dl_write(NOP());
+ eve_dl_write(NOP());
+ eve_dl_write(NOP());
+ }
+
+ eve_dl_write(END());
+ eve_dl_end();
+}
+
+void eve_text_scroll0(EVEText *box) {
+ if (box->line_top >= 0) {
+ box->line_top = -1;
+ box->line_top0 = -1;
+ eve_text_update(box);
+ }
+}
+
+int eve_text_touch(EVEText *box, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ evt = eve_touch_evt(touch, evt, tag0, box->tag, 1);
+ if (touch && evt) {
+ if ((evt & EVE_TOUCH_ETYPE_TRACK_START) && (box->line_top < 0)) {
+ box->line_top = box->line0;
+ box->line_top0 = box->line0;
+ }
+ if ((evt & EVE_TOUCH_ETYPE_TRACK) && (box->line_top0 >=0)) {
+ int line = LINE_IDX_ADD(box->line_top0, (touch->y0 - touch->y) / box->ch_h, box->line_size);
+ if (LINE_IDX_LTE(line, box->line0, box->line_size, box->h)) {
+ box->line_top = line;
+ eve_text_update(box);
+ }
+ }
+ if (evt & EVE_TOUCH_ETYPE_TRACK_STOP) {
+ box->line_top0 = box->line_top;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+uint8_t eve_text_draw(EVEText *box, uint8_t tag) {
+ eve_cmd_dl(SAVE_CONTEXT());
+ box->tag = tag;
+ if (tag != EVE_NOTAG) {
+ eve_cmd_dl(TAG(tag));
+ eve_touch_set_opt(tag, EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_EXT_Y);
+ tag++;
+ }
+ eve_cmd(CMD_APPEND, "ww", box->mem_addr + box->w * 2 * box->line_size, box->dl_size * 4);
+ eve_cmd_dl(RESTORE_CONTEXT());
+
+ return tag;
+}
+
+void eve_text_putc(EVEText *box, int c) {
+ int line_c, line_n;
+
+ switch (c) {
+ case '\b':
+ eve_text_backspace(box);
+ break;
+ case '\r':
+ case '\n':
+ eve_text_newline(box);
+ break;
+ default:
+ line_c = box->ch_idx / 2 / box->w;
+
+ eve_write16(box->mem_addr + box->ch_idx, 0x0200 | (c & 0xff));
+ box->ch_idx = (box->ch_idx + 2) % (box->line_size * box->w * 2);
+ eve_write16(box->mem_addr + box->ch_idx, TEXT_CRSR);
+
+ line_n = box->ch_idx / 2 / box->w;
+ if ((line_c != line_n) && (LINE_IDX_DIFF(line_n, box->line0, box->line_size) == box->h)) scroll1(box);
+ break;
+ }
+}
+
+void eve_text_backspace(EVEText *box) {
+ uint16_t c;
+
+ eve_write16(box->mem_addr + box->ch_idx, 0);
+ box->ch_idx = (box->ch_idx - 2) % (box->line_size * box->w * 2);
+ if (eve_read16(box->mem_addr + box->ch_idx) == 0) {
+ box->ch_idx = (box->ch_idx + 2) % (box->line_size * box->w * 2);
+ }
+ eve_write16(box->mem_addr + box->ch_idx, TEXT_CRSR);
+}
+
+void eve_text_newline(EVEText *box) {
+ int line = (box->ch_idx / 2 / box->w + 1) % box->line_size;
+
+ eve_write16(box->mem_addr + box->ch_idx, 0);
+ box->ch_idx = line * box->w * 2;
+ if (LINE_IDX_DIFF(line, box->line0, box->line_size) == box->h) scroll1(box);
+ eve_write16(box->mem_addr + box->ch_idx, TEXT_CRSR);
+}
+
+void eve_text_puts(EVEText *box, char *s) {
+ while (*s) {
+ eve_text_putc(box, *s);
+ s++;
+ }
+}
diff --git a/fw/fe310/eos/eve/eve_text.h b/fw/fe310/eos/eve/eve_text.h
new file mode 100644
index 0000000..3b282c9
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_text.h
@@ -0,0 +1,32 @@
+#include <stdint.h>
+
+typedef struct EVEText {
+ EVERect g;
+ uint16_t w;
+ uint16_t h;
+ uint32_t mem_addr;
+ uint16_t line_size;
+ uint16_t line0;
+ int line_top;
+ int line_top0;
+ uint16_t ch_idx;
+ uint16_t transform_a;
+ uint16_t transform_e;
+ uint8_t tag;
+ uint8_t ch_h;
+ uint8_t ch_w;
+ uint8_t dl_size;
+ char dirty;
+} EVEText;
+
+void eve_text_init(EVEText *box, EVERect *g, uint16_t w, uint16_t h, uint16_t line_size, uint32_t mem_addr, uint32_t *mem_next);
+void eve_text_update(EVEText *box);
+void eve_text_scroll0(EVEText *box);
+
+int eve_text_touch(EVEText *box, EVETouch *touch, uint16_t evt, uint8_t tag0);
+uint8_t eve_text_draw(EVEText *box, uint8_t tag);
+
+void eve_text_putc(EVEText *box, int c);
+void eve_text_backspace(EVEText *box);
+void eve_text_newline(EVEText *box);
+void eve_text_puts(EVEText *box, char *s);
diff --git a/fw/fe310/eos/eve/eve_touch.c b/fw/fe310/eos/eve/eve_touch.c
new file mode 100644
index 0000000..e75cf4e
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_touch.c
@@ -0,0 +1,419 @@
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "eve.h"
+
+static int touch_intr_mask = EVE_INT_TAG | EVE_INT_TOUCH;
+static int touch_multi;
+static uint8_t touch_tag0;
+
+static EVETouch touch_obj[EVE_MAX_TOUCH];
+static EVETouchTimer touch_timer;
+
+static eve_touch_handler_t touch_handler;
+static void *touch_handler_param;
+static uint8_t touch_tag_opt[256];
+
+static const uint32_t _reg_touch[] = {
+ REG_CTOUCH_TOUCH0_XY,
+ REG_CTOUCH_TOUCH1_XY,
+ REG_CTOUCH_TOUCH2_XY,
+ REG_CTOUCH_TOUCH3_XY
+};
+
+static const uint32_t _reg_tag[] = {
+ REG_TOUCH_TAG,
+ REG_TOUCH_TAG1,
+ REG_TOUCH_TAG2,
+ REG_TOUCH_TAG3,
+ REG_TOUCH_TAG4
+};
+
+static const uint32_t _reg_track[] = {
+ REG_TRACKER,
+ REG_TRACKER_1,
+ REG_TRACKER_2,
+ REG_TRACKER_3,
+ REG_TRACKER_4
+};
+
+void eve_handle_touch(void) {
+ int i;
+ char touch_ex = 0;
+ char int_ccomplete = 0;
+ uint8_t flags;
+
+ eve_spi_start();
+
+ flags = eve_read8(REG_INT_FLAGS) & touch_intr_mask;
+ if (!touch_multi && (flags & EVE_INT_TOUCH)) touch_multi = 1;
+ for (i=0; i<EVE_MAX_TOUCH; i++) {
+ uint8_t touch_tag;
+ uint32_t touch_xy;
+ uint64_t now = 0;
+ uint16_t touch_evt = 0;
+ EVETouch *touch = &touch_obj[i];
+
+ touch_xy = i < 4 ? eve_read32(_reg_touch[i]) : (((uint32_t)eve_read16(REG_CTOUCH_TOUCH4_X) << 16) | eve_read16(REG_CTOUCH_TOUCH4_Y));
+
+ if (touch_xy != EVE_NOTOUCH) {
+ int16_t touch_x = touch_xy >> 16;
+ int16_t touch_y = touch_xy & 0xffff;
+ now = eve_time_get_tick();
+ if (touch->eevt & EVE_TOUCH_EETYPE_NOTOUCH) {
+ uint16_t _evt = 0;
+ uint16_t _eevt = 0;
+ uint16_t _ttevt = eve_touch_timer_get_evt(touch);
+
+ if (_ttevt) {
+ touch->eevt &= ~EVE_TOUCH_EETYPE_NOTOUCH;
+
+ if (_ttevt & EVE_TOUCH_ETYPE_TAP2) {
+ int dx = touch_x - touch->x0;
+ int dy = touch_y - touch->y0;
+
+ dx = dx < 0 ? -dx : dx;
+ dy = dy < 0 ? -dy : dy;
+ if ((dx > EVE_TOUCH_THRESHOLD_X) || (dy > EVE_TOUCH_THRESHOLD_Y)) {
+ touch_evt |= EVE_TOUCH_ETYPE_TAP1;
+ } else {
+ _evt |= EVE_TOUCH_ETYPE_TAP2;
+ _eevt |= EVE_TOUCH_EETYPE_TAP2;
+ }
+ }
+ if (_ttevt & EVE_TOUCH_ETYPE_TRACK) {
+ EVEVTrack *vtrack = eve_vtrack_get();
+
+ _eevt |= EVE_TOUCH_EETYPE_TRACK_ABORT;
+ touch_evt |= (EVE_TOUCH_ETYPE_TRACK_STOP | EVE_TOUCH_ETYPE_TRACK_ABORT);
+ if (vtrack->stop) vtrack->stop(touch, vtrack->param);
+ }
+ if (_ttevt & EVE_TOUCH_ETYPE_TIMER) {
+ _eevt |= EVE_TOUCH_EETYPE_TIMER_ABORT;
+ touch_evt |= EVE_TOUCH_ETYPE_TIMER_ABORT;
+ }
+
+ eve_touch_timer_clear(touch);
+ if (touch_handler && touch_evt) {
+ touch_handler(touch_timer.touch, touch_evt, touch_timer.tag0, touch_handler_param);
+ }
+ }
+ touch_evt = EVE_TOUCH_ETYPE_POINT | _evt;
+ touch->eevt = _eevt;
+ touch->tag0 = 0;
+ touch->tag = 0;
+ touch->tag_up = 0;
+ touch->tracker.tag = 0;
+ touch->tracker.track = 0;
+ touch->tracker.val = 0;
+ touch->t = 0;
+ touch->vx = 0;
+ touch->vy = 0;
+ touch->x0 = touch_x;
+ touch->y0 = touch_y;
+ } else if (touch->t) {
+ int dt = now - touch->t;
+ int vx = ((int)touch_x - touch->x) * (int)(EVE_RTC_FREQ) / dt;
+ int vy = ((int)touch_y - touch->y) * (int)(EVE_RTC_FREQ) / dt;
+ touch->vx = touch->vx ? (vx + touch->vx * EVE_TOUCH_TRAVG) / (EVE_TOUCH_TRAVG + 1) : vx;
+ touch->vy = touch->vy ? (vy + touch->vy * EVE_TOUCH_TRAVG) / (EVE_TOUCH_TRAVG + 1) : vy;
+ touch->t = now;
+ }
+ touch->x = touch_x;
+ touch->y = touch_y;
+ if (touch_multi || (flags & EVE_INT_TAG)) {
+ touch_tag = eve_read8(_reg_tag[i]);
+ } else {
+ touch_tag = touch->tag;
+ }
+ touch_ex = 1;
+ } else {
+ touch_tag = 0;
+ if (!(touch->eevt & EVE_TOUCH_EETYPE_NOTOUCH)) {
+ uint16_t _ttevt = eve_touch_timer_get_evt(touch);
+
+ touch_evt = EVE_TOUCH_ETYPE_POINT_UP;
+ touch->eevt |= EVE_TOUCH_EETYPE_NOTOUCH;
+ if (_ttevt & EVE_TOUCH_ETYPE_LPRESS) {
+ eve_touch_timer_set_evt(touch, _ttevt & ~EVE_TOUCH_ETYPE_LPRESS);
+ }
+ if (touch->tracker.tag && touch->tracker.track) {
+ uint8_t opt = touch_tag_opt[touch->tracker.tag];
+ char track_ext = ((opt & EVE_TOUCH_OPT_TRACK_EXT_X) && (touch->eevt & EVE_TOUCH_EETYPE_TRACK_X)) ||
+ ((opt & EVE_TOUCH_OPT_TRACK_EXT_Y) && (touch->eevt & EVE_TOUCH_EETYPE_TRACK_Y));
+ if (!eve_touch_timer_get_evt(NULL) && track_ext) {
+ EVEVTrack *vtrack = eve_vtrack_get();
+
+ eve_touch_timer_set(touch, EVE_TOUCH_ETYPE_TRACK, touch_tag0, EVE_TOUCH_TIMEOUT_TRACK);
+ if (vtrack->start) vtrack->start(touch, vtrack->param);
+ } else {
+ touch_evt |= EVE_TOUCH_ETYPE_TRACK_STOP;
+ }
+ }
+ }
+ }
+ if (touch_tag != touch->tag) {
+ if (touch_tag) {
+ if (!touch_tag0) touch_tag0 = touch_tag;
+ if (!touch->tag0) {
+ touch->tag0 = touch_tag;
+ if (touch_tag_opt[touch_tag] & (EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_REG)) {
+ touch->tracker.tag = touch_tag;
+ }
+ if (touch->tracker.tag && !(touch_tag_opt[touch->tracker.tag] & EVE_TOUCH_OPT_TRACK_XY)) {
+ touch_evt |= EVE_TOUCH_ETYPE_TRACK_START;
+ touch->tracker.track = 1;
+ touch->t = now;
+ }
+ if (!eve_touch_timer_get_evt(NULL) && (touch_tag_opt[touch_tag] & (EVE_TOUCH_OPT_LPRESS | EVE_TOUCH_OPT_TAP2))) {
+ uint16_t _evt = 0;
+
+ if (touch_tag_opt[touch_tag] & EVE_TOUCH_OPT_LPRESS) _evt |= EVE_TOUCH_ETYPE_LPRESS;
+ if (touch_tag_opt[touch_tag] & EVE_TOUCH_OPT_TAP2) _evt |= EVE_TOUCH_ETYPE_TAP2;
+ eve_touch_timer_set(touch, _evt, touch_tag0, EVE_TOUCH_TIMEOUT_TAP);
+ }
+ }
+ }
+ touch->tag_up = touch->tag;
+ if (touch->tag_up) touch_evt |= EVE_TOUCH_ETYPE_TAG_UP;
+ touch->tag = touch_tag;
+ if (touch->tag) touch_evt |= EVE_TOUCH_ETYPE_TAG;
+ }
+ if (touch_xy != EVE_NOTOUCH) {
+ uint16_t _ttevt = eve_touch_timer_get_evt(touch);
+ int _track = touch->tracker.tag && !touch->tracker.track;
+ int _timer = _ttevt & (EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TAP2);
+ if (_track || _timer) {
+ int dx = touch->x - touch->x0;
+ int dy = touch->y - touch->y0;
+ dx = dx < 0 ? -dx : dx;
+ dy = dy < 0 ? -dy : dy;
+ if (_track) {
+ if ((dx > EVE_TOUCH_THRESHOLD_X) && !(touch_tag_opt[touch->tracker.tag] & EVE_TOUCH_OPT_TRACK_X)) {
+ touch->tracker.tag = 0;
+ }
+ if ((dy > EVE_TOUCH_THRESHOLD_Y) && !(touch_tag_opt[touch->tracker.tag] & EVE_TOUCH_OPT_TRACK_Y)) {
+ touch->tracker.tag = 0;
+ }
+ if (touch->tracker.tag && ((dx > EVE_TOUCH_THRESHOLD_X) || (dy > EVE_TOUCH_THRESHOLD_Y))) {
+ if (dx > EVE_TOUCH_THRESHOLD_X) {
+ touch->eevt |= touch->x > touch->x0 ? EVE_TOUCH_EETYPE_TRACK_RIGHT : EVE_TOUCH_EETYPE_TRACK_LEFT;
+ }
+ if (dy > EVE_TOUCH_THRESHOLD_Y) {
+ touch->eevt |= touch->y > touch->y0 ? EVE_TOUCH_EETYPE_TRACK_DOWN : EVE_TOUCH_EETYPE_TRACK_UP;
+ }
+ touch_evt |= EVE_TOUCH_ETYPE_TRACK_START;
+ touch->tracker.track = 1;
+ touch->t = now;
+ }
+ }
+ if (_timer && ((dx > EVE_TOUCH_THRESHOLD_X) || (dy > EVE_TOUCH_THRESHOLD_Y))) {
+ eve_touch_timer_set_evt(touch, _ttevt & ~(EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TAP2));
+ _timer = 0;
+ }
+ }
+ if (touch->tracker.tag && touch->tracker.track) {
+ if (touch_tag_opt[touch->tracker.tag] & EVE_TOUCH_OPT_TRACK) touch_evt |= EVE_TOUCH_ETYPE_TRACK;
+ if (touch_tag_opt[touch->tracker.tag] & EVE_TOUCH_OPT_TRACK_REG) touch_evt |= EVE_TOUCH_ETYPE_TRACK_REG;
+ }
+ if (touch_evt & EVE_TOUCH_ETYPE_TRACK_REG) {
+ uint32_t touch_track = eve_read32(_reg_track[i]);
+ if (touch->tracker.tag == (touch_track & 0xff)) {
+ touch->tracker.val = touch_track >> 16;
+ } else {
+ touch_evt &= ~EVE_TOUCH_ETYPE_TRACK_REG;
+ }
+ }
+ if (touch->tracker.tag || _timer) int_ccomplete = 1;
+ }
+ if (touch_handler && touch_evt) {
+ touch_handler(touch, touch_evt, touch_tag0, touch_handler_param);
+ }
+ if (!touch_multi) break;
+ }
+
+ if (!touch_ex) {
+ touch_tag0 = 0;
+ touch_multi = 0;
+ }
+
+ if (touch_multi) int_ccomplete = 1;
+
+ if (int_ccomplete && !(touch_intr_mask & EVE_INT_CONVCOMPLETE)) {
+ touch_intr_mask |= EVE_INT_CONVCOMPLETE;
+ eve_write8(REG_INT_MASK, touch_intr_mask);
+ }
+ if (!int_ccomplete && (touch_intr_mask & EVE_INT_CONVCOMPLETE)) {
+ touch_intr_mask &= ~EVE_INT_CONVCOMPLETE;
+ eve_write8(REG_INT_MASK, touch_intr_mask);
+ }
+
+ eve_spi_stop();
+}
+
+void eve_handle_time(void) {
+ EVETouch *touch = touch_timer.touch;
+
+ if (touch_timer.evt) {
+ int more = 0;
+ uint16_t touch_evt = 0;
+
+ eve_spi_start();
+
+ if (touch_timer.evt & EVE_TOUCH_ETYPE_LPRESS) {
+ touch_evt |= EVE_TOUCH_ETYPE_LPRESS;
+ if (touch) touch->eevt |= EVE_TOUCH_EETYPE_LPRESS;
+ }
+ if (touch_timer.evt & EVE_TOUCH_ETYPE_TAP2) {
+ touch_evt |= EVE_TOUCH_ETYPE_TAP1;
+ }
+ if (touch_timer.evt & EVE_TOUCH_ETYPE_TRACK) {
+ EVEVTrack *vtrack = eve_vtrack_get();
+
+ if (vtrack->tick) {
+ touch_evt |= EVE_TOUCH_ETYPE_TRACK;
+ more = vtrack->tick(touch, vtrack->param);
+ }
+ if (!more) {
+ touch_evt |= EVE_TOUCH_ETYPE_TRACK_STOP;
+ if (vtrack->stop) vtrack->stop(touch, vtrack->param);
+ }
+ }
+ if (touch_timer.evt & EVE_TOUCH_ETYPE_TIMER) {
+ touch_evt |= EVE_TOUCH_ETYPE_TIMER;
+ more = 1;
+ }
+
+ if (more) {
+ eve_timer_set(touch_timer.to);
+ } else {
+ touch_timer.evt = 0;
+ }
+
+ if (touch_handler && touch_evt) {
+ touch_handler(touch, touch_evt, touch_timer.tag0, touch_handler_param);
+ }
+
+ eve_spi_stop();
+ }
+}
+
+void eve_touch_init(void) {
+ int i;
+
+ eve_vtrack_init();
+
+ for (i=0; i<EVE_MAX_TOUCH; i++) {
+ EVETouch *touch = &touch_obj[i];
+ touch->eevt |= EVE_TOUCH_EETYPE_NOTOUCH;
+ }
+
+ eve_write8(REG_INT_MASK, touch_intr_mask);
+ eve_write8(REG_INT_EN, 0x01);
+ while(eve_read8(REG_INT_FLAGS));
+}
+
+void eve_touch_set_handler(eve_touch_handler_t handler, void *param) {
+ touch_handler = handler;
+ touch_handler_param = param;
+}
+
+EVETouch *eve_touch_get(int i) {
+ return &touch_obj[i];
+}
+
+int8_t eve_touch_get_idx(EVETouch *touch) {
+ if (touch == NULL) return -1;
+ return touch - touch_obj;
+}
+
+uint16_t eve_touch_evt(EVETouch *touch, uint16_t evt, uint8_t tag0, uint8_t tag_min, uint8_t tag_n) {
+ int tag_max;
+ uint8_t _tag;
+ uint16_t _evt;
+
+ if (tag_min == EVE_NOTAG) return 0;
+
+ tag_max = tag_min + tag_n;
+ if ((tag0 < tag_min) || (tag0 >= tag_max)) return 0;
+
+ _evt = evt & (EVE_TOUCH_ETYPE_TIMER_MASK | EVE_TOUCH_ETYPE_USR_MASK);
+ if (touch == NULL) return _evt;
+
+ _evt |= evt & EVE_TOUCH_ETYPE_POINT_MASK;
+ if (evt & EVE_TOUCH_ETYPE_TAG) {
+ _tag = touch->tag;
+ if ((_tag >= tag_min) && (_tag < tag_max)) _evt |= EVE_TOUCH_ETYPE_TAG;
+ }
+ if (evt & EVE_TOUCH_ETYPE_TAG_UP) {
+ _tag = touch->tag_up;
+ if ((_tag >= tag_min) && (_tag < tag_max)) _evt |= EVE_TOUCH_ETYPE_TAG_UP;
+ }
+ if (evt & EVE_TOUCH_ETYPE_TRACK_MASK) {
+ _tag = touch->tracker.tag;
+ if ((_tag >= tag_min) && (_tag < tag_max)) _evt |= evt & EVE_TOUCH_ETYPE_TRACK_MASK;
+ }
+ if (evt & (EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TAP1 | EVE_TOUCH_ETYPE_TAP2)) {
+ _tag = touch->tag0;
+ if ((_tag >= tag_min) && (_tag < tag_max)) _evt |= evt & (EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TAP1 | EVE_TOUCH_ETYPE_TAP2);
+ }
+
+ return _evt;
+}
+
+void eve_touch_set_opt(uint8_t tag, uint8_t opt) {
+ touch_tag_opt[tag] = opt;
+}
+
+uint8_t eve_touch_get_opt(uint8_t tag) {
+ return touch_tag_opt[tag];
+}
+
+void eve_touch_clear_opt(void) {
+ memset(touch_tag_opt, 0, sizeof(touch_tag_opt));
+}
+
+void eve_touch_timer_set(EVETouch *touch, uint16_t evt, uint8_t tag0, uint32_t to) {
+ touch_timer.touch = touch;
+ touch_timer.evt = evt;
+ touch_timer.tag0 = tag0;
+ touch_timer.to = to;
+ eve_timer_set(to);
+}
+
+void eve_touch_timer_clear(EVETouch *touch) {
+ eve_touch_timer_set_evt(touch, 0);
+}
+
+uint16_t eve_touch_timer_get_evt(EVETouch *touch) {
+ uint16_t ret = 0;
+
+ if ((touch == NULL) || (touch_timer.touch == touch)) {
+ ret = touch_timer.evt;
+ } else if (touch_timer.touch == NULL) {
+ ret = touch_timer.evt & EVE_TOUCH_ETYPE_TIMER;
+ }
+ return ret;
+}
+
+void eve_touch_timer_set_evt(EVETouch *touch, uint16_t evt) {
+ if (touch == touch_timer.touch) {
+ touch_timer.evt = evt;
+ } else if (touch_timer.touch == NULL) {
+ touch_timer.evt = evt & EVE_TOUCH_ETYPE_TIMER;
+ }
+ if (!touch_timer.evt) eve_timer_clear();
+}
+
+void eve_touch_timer_start(uint8_t tag0, uint32_t to) {
+ eve_touch_timer_set(NULL, EVE_TOUCH_ETYPE_TIMER, tag0, to);
+}
+
+void eve_touch_timer_stop(void) {
+ eve_touch_timer_clear(NULL);
+}
+
+EVETouchTimer *eve_touch_timer_get(void) {
+ return &touch_timer;
+}
diff --git a/fw/fe310/eos/eve/eve_touch.h b/fw/fe310/eos/eve/eve_touch.h
new file mode 100644
index 0000000..1bb77ba
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_touch.h
@@ -0,0 +1,121 @@
+#include <stdint.h>
+
+#define EVE_TOUCH_TIMEOUT_TAP 1000
+#define EVE_TOUCH_TIMEOUT_TRACK 20
+
+#define EVE_TOUCH_THRESHOLD_X 5
+#define EVE_TOUCH_THRESHOLD_Y 5
+#define EVE_TOUCH_TRAVG 3
+
+#define EVE_NOTAG 0
+#define EVE_NOTOUCH 0x80008000
+#define EVE_MAX_TOUCH 5
+
+/* events */
+#define EVE_TOUCH_ETYPE_TAG 0x0001
+#define EVE_TOUCH_ETYPE_TAG_UP 0x0002
+#define EVE_TOUCH_ETYPE_POINT 0x0004
+#define EVE_TOUCH_ETYPE_POINT_UP 0x0008
+#define EVE_TOUCH_ETYPE_TRACK 0x0010
+#define EVE_TOUCH_ETYPE_TRACK_START 0x0020
+#define EVE_TOUCH_ETYPE_TRACK_STOP 0x0040
+#define EVE_TOUCH_ETYPE_TRACK_ABORT 0x0080
+#define EVE_TOUCH_ETYPE_TIMER 0x0100
+#define EVE_TOUCH_ETYPE_TIMER_ABORT 0x0200
+#define EVE_TOUCH_ETYPE_TRACK_REG 0x0400
+#define EVE_TOUCH_ETYPE_LPRESS 0x0800
+#define EVE_TOUCH_ETYPE_TAP1 0x1000
+#define EVE_TOUCH_ETYPE_TAP2 0x2000
+#define EVE_TOUCH_ETYPE_USR 0x4000
+#define EVE_TOUCH_ETYPE_USR1 0x8000
+
+#define EVE_TOUCH_ETYPE_TRACK_MASK (EVE_TOUCH_ETYPE_TRACK | EVE_TOUCH_ETYPE_TRACK_START | EVE_TOUCH_ETYPE_TRACK_STOP | EVE_TOUCH_ETYPE_TRACK_ABORT | EVE_TOUCH_ETYPE_TRACK_REG)
+#define EVE_TOUCH_ETYPE_TIMER_MASK (EVE_TOUCH_ETYPE_TIMER | EVE_TOUCH_ETYPE_TIMER_ABORT)
+#define EVE_TOUCH_ETYPE_POINT_MASK (EVE_TOUCH_ETYPE_POINT | EVE_TOUCH_ETYPE_POINT_UP)
+#define EVE_TOUCH_ETYPE_USR_MASK (EVE_TOUCH_ETYPE_USR | EVE_TOUCH_ETYPE_USR1)
+
+/* extended events */
+#define EVE_TOUCH_EETYPE_NOTOUCH 0x0001
+#define EVE_TOUCH_EETYPE_LPRESS 0x0002
+#define EVE_TOUCH_EETYPE_TAP2 0x0004
+
+#define EVE_TOUCH_EETYPE_TRACK_LEFT 0x0010
+#define EVE_TOUCH_EETYPE_TRACK_RIGHT 0x0020
+#define EVE_TOUCH_EETYPE_TRACK_UP 0x0040
+#define EVE_TOUCH_EETYPE_TRACK_DOWN 0x0080
+
+#define EVE_TOUCH_EETYPE_TRACK_ABORT 0x0100
+#define EVE_TOUCH_EETYPE_TIMER_ABORT 0x0200
+
+#define EVE_TOUCH_EETYPE_USR 0x1000
+#define EVE_TOUCH_EETYPE_USR1 0x2000
+#define EVE_TOUCH_EETYPE_USR2 0x4000
+#define EVE_TOUCH_EETYPE_USR3 0x8000
+
+#define EVE_TOUCH_EETYPE_TRACK_X (EVE_TOUCH_EETYPE_TRACK_LEFT | EVE_TOUCH_EETYPE_TRACK_RIGHT)
+#define EVE_TOUCH_EETYPE_TRACK_Y (EVE_TOUCH_EETYPE_TRACK_UP | EVE_TOUCH_EETYPE_TRACK_DOWN)
+#define EVE_TOUCH_EETYPE_TRACK_XY (EVE_TOUCH_EETYPE_TRACK_X | EVE_TOUCH_EETYPE_TRACK_Y)
+#define EVE_TOUCH_EETYPE_ABORT (EVE_TOUCH_EETYPE_TRACK_ABORT | EVE_TOUCH_EETYPE_TIMER_ABORT)
+
+/* tag options */
+#define EVE_TOUCH_OPT_TRACK 0x01
+#define EVE_TOUCH_OPT_TRACK_REG 0x02
+#define EVE_TOUCH_OPT_TRACK_X 0x04
+#define EVE_TOUCH_OPT_TRACK_Y 0x08
+#define EVE_TOUCH_OPT_TRACK_EXT_X 0x10
+#define EVE_TOUCH_OPT_TRACK_EXT_Y 0x20
+#define EVE_TOUCH_OPT_LPRESS 0x40
+#define EVE_TOUCH_OPT_TAP2 0x80
+
+#define EVE_TOUCH_OPT_TRACK_XY (EVE_TOUCH_OPT_TRACK_X | EVE_TOUCH_OPT_TRACK_Y)
+#define EVE_TOUCH_OPT_TRACK_EXT_XY (EVE_TOUCH_OPT_TRACK_EXT_X | EVE_TOUCH_OPT_TRACK_EXT_Y)
+
+typedef struct EVETouch {
+ int x;
+ int y;
+ int vx;
+ int vy;
+ int x0;
+ int y0;
+ uint64_t t;
+ uint16_t eevt;
+ uint8_t tag0;
+ uint8_t tag;
+ uint8_t tag_up;
+ struct {
+ uint8_t tag;
+ uint8_t track;
+ uint16_t val;
+ } tracker;
+} EVETouch;
+
+typedef struct EVETouchTimer {
+ EVETouch *touch;
+ uint32_t to;
+ uint16_t evt;
+ uint8_t tag0;
+} EVETouchTimer;
+
+typedef void (*eve_touch_handler_t) (EVETouch *, uint16_t, uint8_t, void *);
+
+void eve_handle_touch(void);
+void eve_handle_time(void);
+
+void eve_touch_init(void);
+void eve_touch_set_handler(eve_touch_handler_t handler, void *handler_param);
+EVETouch *eve_touch_get(int i);
+int8_t eve_touch_get_idx(EVETouch *touch);
+uint16_t eve_touch_evt(EVETouch *touch, uint16_t evt, uint8_t tag0, uint8_t tag_min, uint8_t tag_n);
+
+void eve_touch_set_opt(uint8_t tag, uint8_t opt);
+uint8_t eve_touch_get_opt(uint8_t tag);
+void eve_touch_clear_opt(void);
+
+void eve_touch_timer_set(EVETouch *touch, uint16_t evt, uint8_t tag0, uint32_t to);
+void eve_touch_timer_clear(EVETouch *touch);
+uint16_t eve_touch_timer_get_evt(EVETouch *touch);
+void eve_touch_timer_set_evt(EVETouch *touch, uint16_t evt);
+void eve_touch_timer_start(uint8_t tag0, uint32_t to);
+void eve_touch_timer_stop(void);
+
+EVETouchTimer *eve_touch_timer_get(void);
diff --git a/fw/fe310/eos/eve/eve_vtrack.c b/fw/fe310/eos/eve/eve_vtrack.c
new file mode 100644
index 0000000..b9f28af
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_vtrack.c
@@ -0,0 +1,65 @@
+#include <stdlib.h>
+#include <math.h>
+
+#include "eve.h"
+
+static EVEVTrack vtrack;
+static EVEPhyAcc vtrack_acc;
+
+void eve_vtrack_init(void) {
+ eve_phy_acc_init(&vtrack_acc, -EVE_VTRACK_ACC_A);
+ eve_vtrack_reset();
+}
+
+EVEVTrack *eve_vtrack_get(void) {
+ return &vtrack;
+}
+
+void eve_vtrack_set(eve_vtrack_start_t start, eve_vtrack_tick_t tick, eve_vtrack_stop_t stop, void *param) {
+ vtrack.start = start;
+ vtrack.tick = tick;
+ vtrack.stop = stop;
+ vtrack.param = param;
+}
+
+void eve_vtrack_reset(void) {
+ eve_vtrack_set(eve_vtrack_acc_start, eve_vtrack_acc_tick, NULL, &vtrack_acc);
+}
+
+void eve_vtrack_start(EVETouch *touch, uint8_t tag0, uint32_t to) {
+ eve_touch_timer_set(touch, EVE_TOUCH_ETYPE_TRACK, tag0, to);
+ if (vtrack.start) vtrack.start(touch, vtrack.param);
+}
+
+void eve_vtrack_stop(EVETouch *touch) {
+ eve_touch_timer_clear(touch);
+ eve_vtrack_reset();
+}
+
+void eve_vtrack_acc_start(EVETouch *touch, void *p) {
+ EVEPhyAcc *param = (EVEPhyAcc *)p;
+ eve_phy_acc_start(param, touch->x, touch->y, touch->vx, touch->vy);
+}
+
+int eve_vtrack_acc_tick(EVETouch *touch, void *p) {
+ EVEPhyAcc *param = (EVEPhyAcc *)p;
+
+ return eve_phy_acc_tick(param, eve_time_get_tick() - touch->t, &touch->x, &touch->y);
+}
+
+void eve_vtrack_lho_start(EVETouch *touch, void *p) {
+ EVEPhyLHO *param = (EVEPhyLHO *)p;
+
+ eve_phy_lho_start(param, touch->x, touch->y);
+}
+
+int eve_vtrack_lho_tick(EVETouch *touch, void *p) {
+ EVEPhyLHO *param = (EVEPhyLHO *)p;
+
+ return eve_phy_lho_tick(param, eve_time_get_tick() - touch->t, &touch->x, &touch->y);
+}
+
+void eve_vtrack_lho_stop(EVETouch *touch, void *p) {
+ eve_vtrack_reset();
+}
+
diff --git a/fw/fe310/eos/eve/eve_vtrack.h b/fw/fe310/eos/eve/eve_vtrack.h
new file mode 100644
index 0000000..b75f673
--- /dev/null
+++ b/fw/fe310/eos/eve/eve_vtrack.h
@@ -0,0 +1,28 @@
+#include <stdint.h>
+
+#define EVE_VTRACK_ACC_A 1000
+
+typedef void (*eve_vtrack_start_t) (EVETouch *, void *);
+typedef int (*eve_vtrack_tick_t) (EVETouch *, void *);
+typedef void (*eve_vtrack_stop_t) (EVETouch *, void *);
+
+typedef struct EVEVTrack {
+ eve_vtrack_start_t start;
+ eve_vtrack_tick_t tick;
+ eve_vtrack_stop_t stop;
+ void *param;
+} EVEVTrack;
+
+void eve_vtrack_init(void);
+EVEVTrack *eve_vtrack_get(void);
+void eve_vtrack_set(eve_vtrack_start_t start, eve_vtrack_tick_t tick, eve_vtrack_stop_t stop, void *param);
+void eve_vtrack_reset(void);
+void eve_vtrack_start(EVETouch *touch, uint8_t tag0, uint32_t to);
+void eve_vtrack_stop(EVETouch *touch);
+
+void eve_vtrack_acc_start(EVETouch *touch, void *p);
+int eve_vtrack_acc_tick(EVETouch *touch, void *p);
+
+void eve_vtrack_lho_start(EVETouch *touch, void *p);
+int eve_vtrack_lho_tick(EVETouch *touch, void *p);
+void eve_vtrack_lho_stop(EVETouch *touch, void *p);
diff --git a/fw/fe310/eos/eve/screen/Makefile b/fw/fe310/eos/eve/screen/Makefile
new file mode 100644
index 0000000..cc4a81d
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/Makefile
@@ -0,0 +1,17 @@
+include ../../../common.mk
+
+CFLAGS += -I.. -I../..
+
+obj = window.o view.o page.o form.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/eos/eve/screen/form.c b/fw/fe310/eos/eve/screen/form.c
new file mode 100644
index 0000000..8555158
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/form.c
@@ -0,0 +1,165 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "window.h"
+#include "page.h"
+#include "form.h"
+
+#include "widget/widgets.h"
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static void form_update_g(EVEForm *form, EVEWidget *_widget) {
+ EVEPage *page = &form->p;
+ EVEWidget *widget = page->widget;
+ int i;
+ uint16_t w = 0;
+ uint16_t h = 0;
+ uint16_t l_h = 0;
+
+ for (i=0; i<page->widget_size; i++) {
+ if (widget->label) {
+ h += l_h;
+ w = widget->label->g.w;
+ l_h = widget->label->g.h;
+ widget->label->g.x = 0;
+ widget->label->g.y = h;
+ }
+ if (w + widget->g.w > form->p.v.window->g.w) {
+ h += l_h;
+ w = 0;
+ l_h = 0;
+ }
+ widget->g.x = w;
+ widget->g.y = h;
+
+ w += widget->g.w;
+ l_h = MAX(l_h, widget->g.h);
+
+ widget = eve_widget_next(widget);
+ }
+ page->g.w = page->v.window->g.w;
+ page->g.h = h + l_h;
+}
+
+static void widgets_destroy(EVEWidget *widget, uint16_t widget_size) {
+ int i;
+
+ for (i=0; i<widget_size; i++) {
+ if (widget->label) eve_free(widget->label);
+ eve_widget_destroy(widget);
+ widget = eve_widget_next(widget);
+ }
+}
+
+EVEForm *eve_form_create(EVEWindow *window, EVEViewStack *stack, EVEWidgetSpec spec[], uint16_t spec_size, eve_form_uievt_t uievt, eve_form_action_t action, eve_form_destructor_t destructor) {
+ EVEWidget *widgets;
+ EVEWidget *widget;
+ EVELabel *label;
+ EVEForm *form;
+ int w_size = 0;
+ int i, r;
+
+ for (i=0; i<spec_size; i++) {
+ w_size += eve_widget_size(spec[i].widget.type);
+ }
+ form = eve_malloc(sizeof(EVEForm));
+ if (form == NULL) {
+ return NULL;
+ }
+ if (uievt == NULL) uievt = eve_form_uievt;
+ if (destructor == NULL) destructor = eve_form_destroy;
+ eve_form_init(form, window, stack, NULL, 0, uievt, action, destructor);
+
+ widgets = eve_malloc(w_size);
+ if (widgets == NULL) {
+ eve_free(form);
+ return NULL;
+ }
+
+ widget = widgets;
+ for (i=0; i<spec_size; i++) {
+ r = eve_widget_create(widget, spec[i].widget.type, &spec[i].widget.g, (EVEPage *)form, &spec[i].widget.spec);
+ if (r) {
+ widgets_destroy(widgets, i);
+ eve_free(widgets);
+ eve_free(form);
+ return NULL;
+ }
+ if (spec[i].label.title) {
+ EVEFont *font = spec[i].label.font ? spec[i].label.font : eve_window_font(window);
+ label = eve_malloc(sizeof(EVELabel));
+ if (label == NULL) {
+ eve_widget_destroy(widget);
+ widgets_destroy(widgets, i);
+ eve_free(widgets);
+ eve_free(form);
+ return NULL;
+ }
+ eve_label_init(label, &spec[i].label.g, font, spec[i].label.title);
+ eve_widget_set_label(widget, label);
+ if (label->g.w == 0) label->g.w = eve_font_str_w(font, label->title);
+ }
+ if (widget->label && (widget->label->g.w == 0)) eve_font_str_w(label->font, label->title) + EVE_FORM_LABEL_MARGIN;
+ if (widget->g.w == 0) widget->g.w = window->g.w - (widget->label ? widget->label->g.w : 0);
+ widget = eve_widget_next(widget);
+ }
+ eve_form_update(form, widgets, spec_size);
+
+ return form;
+}
+
+void eve_form_init(EVEForm *form, EVEWindow *window, EVEViewStack *stack, EVEWidget *widget, uint16_t widget_size, eve_form_uievt_t uievt, eve_form_action_t action, eve_form_destructor_t destructor) {
+ memset(form, 0, sizeof(EVEForm));
+ eve_page_init(&form->p, window, stack, NULL, 0, EVE_PAGE_OPT_SCROLL_Y | EVE_PAGE_OPT_SCROLL_BACK | EVE_PAGE_OPT_TRACK_EXT_Y, eve_page_draw, eve_page_touch, (eve_view_uievt_t)uievt, (eve_page_destructor_t)destructor);
+ form->action = action;
+ eve_form_update(form, widget, widget_size);
+}
+
+void eve_form_update(EVEForm *form, EVEWidget *widget, uint16_t widget_size) {
+ eve_page_update((EVEPage *)form, widget, widget_size);
+ form_update_g(form, NULL);
+}
+
+void eve_form_destroy(EVEForm *form) {
+ widgets_destroy(form->p.widget, form->p.widget_size);
+ eve_free(form->p.widget);
+ eve_free(form);
+}
+
+int eve_form_uievt(EVEForm *form, uint16_t evt, void *param) {
+ switch (evt) {
+ case EVE_UIEVT_WIDGET_UPDATE_G:
+ form_update_g(form, (EVEWidget *)param);
+ break;
+
+ case EVE_UIEVT_PAGE_SCROLL_START:
+ break;
+
+ case EVE_UIEVT_PAGE_SCROLL_STOP:
+ break;
+
+ case EVE_UIEVT_PAGE_TRACK_START:
+ break;
+
+ case EVE_UIEVT_PAGE_TRACK_STOP: {
+ EVEUIEvtTouch *touch_p = (EVEUIEvtTouch *)param;
+ if (touch_p->evt & EVE_TOUCH_ETYPE_TRACK_STOP) {
+ if (touch_p->touch->eevt & EVE_TOUCH_EETYPE_TRACK_RIGHT) {
+ eve_page_close((EVEPage *)form);
+ return 1;
+ }
+ if (touch_p->touch->eevt & EVE_TOUCH_EETYPE_TRACK_LEFT) {
+ if (form->action) form->action(form);
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/fw/fe310/eos/eve/screen/form.h b/fw/fe310/eos/eve/screen/form.h
new file mode 100644
index 0000000..272b6ed
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/form.h
@@ -0,0 +1,23 @@
+#include <stdint.h>
+
+#define EVE_FORM_LABEL_MARGIN 10
+
+struct EVEWidget;
+struct EVEWidgetSpec;
+struct EVEForm;
+
+typedef int (*eve_form_uievt_t) (struct EVEForm *, uint16_t, void *);
+typedef void (*eve_form_action_t) (struct EVEForm *);
+typedef void (*eve_form_destructor_t) (struct EVEForm *);
+
+typedef struct EVEForm {
+ EVEPage p;
+ eve_form_action_t action;
+} EVEForm;
+
+EVEForm *eve_form_create(EVEWindow *window, EVEViewStack *stack, struct EVEWidgetSpec *spec, uint16_t spec_size, eve_form_uievt_t uievt, eve_form_action_t action, eve_form_destructor_t destructor);
+void eve_form_init(EVEForm *form, EVEWindow *window, EVEViewStack *stack, struct EVEWidget *widget, uint16_t widget_size, eve_form_uievt_t uievt, eve_form_action_t action, eve_form_destructor_t destructor);
+void eve_form_update(EVEForm *form, struct EVEWidget *widget, uint16_t widget_size);
+void eve_form_destroy(EVEForm *form);
+
+int eve_form_uievt(EVEForm *form, uint16_t evt, void *param);
diff --git a/fw/fe310/eos/eve/screen/page.c b/fw/fe310/eos/eve/screen/page.c
new file mode 100644
index 0000000..65dd534
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/page.c
@@ -0,0 +1,349 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "window.h"
+#include "page.h"
+
+#include "widget/label.h"
+#include "widget/widget.h"
+
+#define PAGE_TMODE_NONE 0
+#define PAGE_TMODE_TRACK 1
+#define PAGE_TMODE_SCROLL 2
+
+void eve_page_init(EVEPage *page, EVEWindow *window, EVEViewStack *stack, EVEWidget *widget, uint16_t widget_size, uint8_t opt, eve_view_draw_t draw, eve_view_touch_t touch, eve_view_uievt_t uievt, eve_page_destructor_t destructor) {
+ memset(page, 0, sizeof(EVEPage));
+ eve_view_init(&page->v, window, draw, touch, uievt, NULL);
+ page->stack = stack;
+ page->opt = opt;
+ page->destructor = destructor;
+ eve_page_update(page, widget, widget_size);
+}
+
+void eve_page_update(EVEPage *page, EVEWidget *widget, uint16_t widget_size) {
+ page->widget = widget;
+ page->widget_size = widget_size;
+}
+
+void eve_page_open(EVEPage *parent, eve_view_constructor_t constructor) {
+ EVEWindow *window = parent->v.window;
+ EVEViewStack *stack = parent->stack;
+ eve_page_destructor_t destructor = parent->destructor;
+
+ if (destructor) destructor(parent);
+ eve_view_create(window, stack, constructor);
+}
+
+void eve_page_close(EVEPage *page) {
+ EVEWindow *window = page->v.window;
+ EVEViewStack *stack = page->stack;
+ eve_page_destructor_t destructor = page->destructor;
+
+ if (stack->level <= 1) return;
+
+ if (page->lho_t0) {
+ page->lho_t0 = 0;
+ eve_touch_timer_stop();
+ }
+ if (eve_window_scroll(window->root, NULL) == window) {
+ eve_window_scroll_stop(window);
+ }
+
+ if (destructor) destructor(page);
+ eve_window_kbd_detach(window);
+ eve_view_destroy(window, stack);
+}
+
+/* Screen to page coordinates */
+int16_t eve_page_x(EVEPage *page, int16_t x) {
+ return x + page->g.x - page->v.window->g.x;
+}
+
+int16_t eve_page_y(EVEPage *page, int16_t y) {
+ return y + page->g.y - page->v.window->g.y;
+}
+
+/* Page to window coordinates */
+int16_t eve_page_win_x(EVEPage *page, int16_t x) {
+ return x - page->g.x;
+}
+
+int16_t eve_page_win_y(EVEPage *page, int16_t y) {
+ return y - page->g.y;
+}
+
+/* Page to screen coordinates */
+int16_t eve_page_scr_x(EVEPage *page, int16_t x) {
+ return eve_page_win_x(page, x) + page->v.window->g.x;
+}
+
+int16_t eve_page_scr_y(EVEPage *page, int16_t y) {
+ return eve_page_win_y(page, y) + page->v.window->g.y;
+}
+
+int eve_page_rect_visible(EVEPage *page, EVERect *g) {
+ uint16_t w = page->v.window->g.w;
+ uint16_t h = page->v.window->g.h;
+
+ if (((g->x + g->w) >= page->g.x) && ((g->y + g->h) >= page->g.y) && (g->x <= (page->g.x + w)) && (g->y <= (page->g.y + h))) return 1;
+ return 0;
+}
+
+void eve_page_focus(EVEPage *page, EVERect *rect) {
+ if (rect) {
+ EVERect g;
+
+ eve_window_visible_g(page->v.window, &g);
+ g.x -= page->v.window->g.x;
+ g.y -= page->v.window->g.y;
+
+ if (rect->x < page->g.x + g.x) {
+ page->g.x = rect->x - g.x;
+ }
+ if (rect->y < page->g.y + g.y) {
+ page->g.y = rect->y - g.y;
+ }
+ if ((rect->x + rect->w) > (page->g.x + g.x + g.w)) {
+ page->g.x = (rect->x + rect->w) - (g.x + g.w);
+ }
+ if ((rect->y + rect->h) > (page->g.y + g.y + g.h)) {
+ page->g.y = (rect->y + rect->h) - (g.y + g.h);
+ }
+ }
+}
+
+void eve_page_focus_widget(EVEPage *page, EVEWidget *widget, EVERect *rect) {
+ if (page->widget_f != widget) {
+ EVEWindow *window = page->v.window;
+ EVEWidget *widget_f = page->widget_f;
+
+ if (widget_f && widget_f->putc) {
+ widget_f->putc(widget_f, EVE_PAGE_KBDCH_CLOSE);
+ if (!(widget && widget->putc)) eve_window_kbd_detach(window);
+ }
+ if (widget && widget->putc) {
+ EVEKbd *kbd = eve_window_kbd(window);
+
+ if (kbd) eve_kbd_set_handler(kbd, widget->putc, widget);
+ if (!(widget_f && widget_f->putc)) eve_window_kbd_attach(window);
+ }
+ page->widget_f = widget;
+ }
+ if (rect) eve_page_focus(page, rect);
+}
+
+EVEWidget *eve_page_focus_widget_get(EVEPage *page) {
+ return page->widget_f;
+}
+
+EVEWidget *eve_page_widget(EVEPage *page, uint16_t idx) {
+ EVEWidget *w = page->widget;
+ int i;
+
+ if (idx >= page->widget_size) return NULL;
+
+ for (i=0; i<idx; i++) {
+ w = eve_widget_next(w);
+ }
+ return w;
+}
+
+static int page_touch(EVEPage *page, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ EVEView *view = &page->v;
+ EVEWindow *window = view->window;
+ int scroll, scroll_x, scroll_y;
+ int ret = 0;
+
+ scroll_x = page->opt & (EVE_PAGE_OPT_SCROLL_X | EVE_PAGE_OPT_SCROLL_XY);
+ if (scroll_x && touch) scroll_x = touch->eevt & ((page->opt & EVE_PAGE_OPT_SCROLL_XY) ? EVE_TOUCH_EETYPE_TRACK_XY : EVE_TOUCH_EETYPE_TRACK_X);
+
+ scroll_y = page->opt & (EVE_PAGE_OPT_SCROLL_Y | EVE_PAGE_OPT_SCROLL_XY);
+ if (scroll_y && touch) scroll_y = touch->eevt & ((page->opt & EVE_PAGE_OPT_SCROLL_XY) ? EVE_TOUCH_EETYPE_TRACK_XY : EVE_TOUCH_EETYPE_TRACK_Y);
+
+ scroll = scroll_x || scroll_y;
+
+ if ((evt & EVE_TOUCH_ETYPE_POINT_UP) && !(touch->eevt & (EVE_TOUCH_EETYPE_TRACK_XY | EVE_TOUCH_EETYPE_ABORT))) {
+ int _ret = 0;
+
+ if (page->widget_f) eve_page_focus_widget(page, NULL, NULL);
+ _ret = eve_view_uievt_pusht(view, EVE_UIEVT_PAGE_TOUCH, touch, evt, tag0);
+ if (_ret) return _ret;
+ ret = 1;
+ }
+
+ /* Scroll start */
+ if ((evt & EVE_TOUCH_ETYPE_TRACK_START) && !(touch->eevt & EVE_TOUCH_EETYPE_ABORT)) {
+ int _ret = 0;
+
+ if (scroll) {
+ page->track_mode = PAGE_TMODE_SCROLL;
+ eve_window_scroll_start(window, touch->tracker.tag);
+ _ret = eve_view_uievt_pusht(view, EVE_UIEVT_PAGE_SCROLL_START, touch, evt, tag0);
+ } else {
+ page->track_mode = PAGE_TMODE_TRACK;
+ _ret = eve_view_uievt_pusht(view, EVE_UIEVT_PAGE_TRACK_START, touch, evt, tag0);
+ }
+ if (_ret) return _ret;
+ ret = 1;
+ }
+
+ /* Scroll stop */
+ if (((evt & EVE_TOUCH_ETYPE_TRACK_STOP) && !(evt & EVE_TOUCH_ETYPE_TRACK_ABORT)) ||
+ ((evt & EVE_TOUCH_ETYPE_POINT_UP) && (touch->eevt & EVE_TOUCH_EETYPE_ABORT) && !(touch->eevt & EVE_TOUCH_EETYPE_TRACK_XY))) {
+ if ((page->track_mode == PAGE_TMODE_SCROLL) && (page->opt & EVE_PAGE_OPT_SCROLL_BACK)) {
+ int wmax_x, wmax_y;
+ int lho_x, lho_y;
+ EVERect vg;
+
+ eve_window_visible_g(page->v.window, &vg);
+ wmax_x = page->g.w > vg.w ? page->g.w - vg.w : 0;
+ wmax_y = page->g.h > vg.h ? page->g.h - vg.h : 0;
+ lho_x = page->g.x < 0 ? 0 : wmax_x;
+ lho_y = page->g.y < 0 ? 0 : wmax_y;
+ if ((page->g.x < 0) || (page->g.x > wmax_x) ||
+ (page->g.y < 0) || (page->g.y > wmax_y)) {
+ EVEPhyLHO *lho = &page->lho;
+ uint8_t _tag;
+
+ eve_window_scroll(window->root, &_tag);
+
+ eve_phy_lho_init(lho, lho_x, lho_y, 1000, 0.5, 0);
+ eve_phy_lho_start(lho, page->g.x, page->g.y);
+ page->lho_t0 = eve_time_get_tick();
+ eve_touch_timer_start(_tag, 20);
+ }
+ }
+
+ if (!page->lho_t0) {
+ int _ret = 0;
+
+ if (page->track_mode == PAGE_TMODE_SCROLL) {
+ page->track_mode = PAGE_TMODE_NONE;
+ eve_window_scroll_stop(window);
+ _ret = eve_view_uievt_pusht(view, EVE_UIEVT_PAGE_SCROLL_STOP, touch, evt, tag0);
+ } else if (!(touch->eevt & EVE_TOUCH_EETYPE_ABORT)) {
+ page->track_mode = PAGE_TMODE_NONE;
+ _ret = eve_view_uievt_pusht(view, EVE_UIEVT_PAGE_TRACK_STOP, touch, evt, tag0);
+ }
+ if (_ret) return _ret;
+ ret = 1;
+ }
+ }
+
+ if (page->track_mode == PAGE_TMODE_SCROLL) {
+ if (evt & EVE_TOUCH_ETYPE_TRACK_START) {
+ if (scroll_x) {
+ page->x0 = page->g.x;
+ }
+ if (scroll_y) {
+ page->y0 = page->g.y;
+ }
+ }
+ if (evt & EVE_TOUCH_ETYPE_TRACK) {
+ if (scroll_x) {
+ page->g.x = page->x0 + touch->x0 - touch->x;
+ }
+ if (scroll_y) {
+ page->g.y = page->y0 + touch->y0 - touch->y;
+ }
+ ret = 1;
+ }
+ if ((evt & EVE_TOUCH_ETYPE_TIMER) && (page->opt & EVE_PAGE_OPT_SCROLL_BACK)) {
+ EVEPhyLHO *lho = &page->lho;
+ int x, y, more;
+
+ more = eve_phy_lho_tick(lho, eve_time_get_tick() - page->lho_t0, scroll_x ? &x : NULL, scroll_y ? &y : NULL);
+ if (scroll_x) page->g.x = x;
+ if (scroll_y) page->g.y = y;
+ if (!more) {
+ int _ret = 0;
+
+ page->lho_t0 = 0;
+ eve_touch_timer_stop();
+ page->track_mode = PAGE_TMODE_NONE;
+ eve_window_scroll_stop(window);
+ _ret = eve_view_uievt_pusht(view, EVE_UIEVT_PAGE_SCROLL_STOP, touch, evt, tag0);
+ if (_ret) return _ret;
+ }
+ ret = 1;
+ }
+ if (evt & EVE_TOUCH_EETYPE_TIMER_ABORT) {
+ page->lho_t0 = 0;
+ }
+ }
+
+ return ret;
+}
+
+uint8_t eve_page_draw(EVEView *view, uint8_t tag0) {
+ EVEPage *page = (EVEPage *)view;
+ EVEWidget *widget = page->widget;
+ int i;
+ uint8_t tagN = tag0;
+ uint8_t tag_opt;
+
+ tag_opt = EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_XY;
+ if (page->opt & EVE_PAGE_OPT_TRACK_EXT_X) tag_opt |= EVE_TOUCH_OPT_TRACK_EXT_X;
+ if (page->opt & EVE_PAGE_OPT_TRACK_EXT_Y) tag_opt |= EVE_TOUCH_OPT_TRACK_EXT_Y;
+
+ tag0 = eve_view_clear(view, tag0, tag_opt);
+ tagN = tag0;
+ eve_cmd_dl(SAVE_CONTEXT());
+ eve_cmd_dl(VERTEX_FORMAT(0));
+ eve_cmd_dl(VERTEX_TRANSLATE_X(eve_page_scr_x(page, 0) * 16));
+ eve_cmd_dl(VERTEX_TRANSLATE_Y(eve_page_scr_y(page, 0) * 16));
+ for (i=0; i<page->widget_size; i++) {
+ if (widget->label && eve_page_rect_visible(page, &widget->label->g)) {
+ eve_cmd_dl(TAG_MASK(0));
+ eve_label_draw(widget->label);
+ eve_cmd_dl(TAG_MASK(1));
+ }
+ if (eve_page_rect_visible(page, &widget->g)) {
+ tagN = widget->draw(widget, tagN);
+ }
+ widget = eve_widget_next(widget);
+ }
+ eve_cmd_dl(RESTORE_CONTEXT());
+
+ for (i=tag0; i<tagN; i++) {
+ if (i != EVE_NOTAG) eve_touch_set_opt(i, eve_touch_get_opt(i) | tag_opt);
+ }
+
+ return tagN;
+}
+
+int eve_page_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ EVEPage *page = (EVEPage *)view;
+ EVEWidget *widget = page->widget;
+ int8_t touch_idx = eve_touch_get_idx(touch);
+ uint16_t _evt;
+ int i, ret;
+
+ if (touch_idx > 0) return 0;
+
+ _evt = eve_touch_evt(touch, evt, tag0, page->v.tag, 1);
+ if (_evt) {
+ ret = page_touch(page, touch, _evt, tag0);
+ if (ret) return 1;
+ }
+ for (i=0; i<page->widget_size; i++) {
+ if (eve_page_rect_visible(page, &widget->g)) {
+ _evt = eve_touch_evt(touch, evt, tag0, widget->tag0, widget->tagN - widget->tag0);
+ if (_evt) {
+ if (page->track_mode == PAGE_TMODE_NONE) {
+ ret = widget->touch(widget, touch, _evt);
+ if (ret) return 1;
+ }
+ ret = page_touch(page, touch, _evt, tag0);
+ if (ret) return 1;
+ }
+ }
+ widget = eve_widget_next(widget);
+ }
+
+ return 0;
+}
diff --git a/fw/fe310/eos/eve/screen/page.h b/fw/fe310/eos/eve/screen/page.h
new file mode 100644
index 0000000..26c33c5
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/page.h
@@ -0,0 +1,56 @@
+#include <stdint.h>
+
+#define EVE_PAGE_KBDCH_CLOSE 0x1a
+
+#define EVE_PAGE_OPT_SCROLL_X 0x01
+#define EVE_PAGE_OPT_SCROLL_Y 0x02
+#define EVE_PAGE_OPT_SCROLL_BACK 0x04
+#define EVE_PAGE_OPT_SCROLL_XY 0x08
+#define EVE_PAGE_OPT_TRACK_EXT_X 0x10
+#define EVE_PAGE_OPT_TRACK_EXT_Y 0x20
+#define EVE_PAGE_OPT_TRACK_EXT_XY (EVE_PAGE_OPT_TRACK_EXT_X | EVE_PAGE_OPT_TRACK_EXT_Y)
+
+struct EVEWidget;
+struct EVEPage;
+
+typedef void (*eve_page_destructor_t) (struct EVEPage *);
+
+typedef struct EVEPage {
+ EVEView v;
+ EVERect g;
+ int16_t x0;
+ int16_t y0;
+ EVEViewStack *stack;
+ eve_page_destructor_t destructor;
+ struct EVEWidget *widget;
+ uint16_t widget_size;
+ struct EVEWidget *widget_f;
+ EVEPhyLHO lho;
+ uint64_t lho_t0;
+ uint8_t track_mode;
+ uint8_t opt;
+} EVEPage;
+
+void eve_page_init(EVEPage *page, EVEWindow *window, EVEViewStack *stack, struct EVEWidget *widget, uint16_t widget_size, uint8_t opt,eve_view_draw_t draw, eve_view_touch_t touch, eve_view_uievt_t uievt, eve_page_destructor_t destructor);
+void eve_page_update(EVEPage *page, struct EVEWidget *widget, uint16_t widget_size);
+void eve_page_open(EVEPage *parent, eve_view_constructor_t constructor);
+void eve_page_close(EVEPage *page);
+
+/* Screen to page coordinates */
+int16_t eve_page_x(EVEPage *page, int16_t x);
+int16_t eve_page_y(EVEPage *page, int16_t y);
+/* Page to window coordinates */
+int16_t eve_page_win_x(EVEPage *page, int16_t x);
+int16_t eve_page_win_y(EVEPage *page, int16_t y);
+/* Page to screen coordinates */
+int16_t eve_page_scr_x(EVEPage *page, int16_t x);
+int16_t eve_page_scr_y(EVEPage *page, int16_t y);
+int eve_page_rect_visible(EVEPage *page, EVERect *g);
+
+void eve_page_focus(EVEPage *page, EVERect *rect);
+void eve_page_focus_widget(EVEPage *page, struct EVEWidget *widget, EVERect *rect);
+struct EVEWidget *eve_page_focus_widget_get(EVEPage *page);
+struct EVEWidget *eve_page_widget(EVEPage *page, uint16_t idx);
+
+uint8_t eve_page_draw(EVEView *view, uint8_t tag0);
+int eve_page_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0);
diff --git a/fw/fe310/eos/eve/screen/uievt.h b/fw/fe310/eos/eve/screen/uievt.h
new file mode 100644
index 0000000..98f0d95
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/uievt.h
@@ -0,0 +1,14 @@
+#define EVE_UIEVT_WIN_UPDATE_G 1
+#define EVE_UIEVT_PAGE_UPDATE_G 2
+#define EVE_UIEVT_PAGE_TOUCH 3
+#define EVE_UIEVT_PAGE_SCROLL_START 4
+#define EVE_UIEVT_PAGE_SCROLL_STOP 5
+#define EVE_UIEVT_PAGE_TRACK_START 6
+#define EVE_UIEVT_PAGE_TRACK_STOP 7
+#define EVE_UIEVT_WIDGET_UPDATE_G 8
+
+typedef struct EVEUIEvtTouch {
+ EVETouch *touch;
+ uint16_t evt;
+ uint8_t tag0;
+} EVEUIEvtTouch;
diff --git a/fw/fe310/eos/eve/screen/view.c b/fw/fe310/eos/eve/screen/view.c
new file mode 100644
index 0000000..ac9aef4
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/view.c
@@ -0,0 +1,94 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "window.h"
+
+void eve_view_init(EVEView *view, EVEWindow *window, eve_view_draw_t draw, eve_view_touch_t touch, eve_view_uievt_t uievt, void *param) {
+ view->draw = draw;
+ view->touch = touch;
+ view->uievt = uievt;
+ view->param = param;
+ view->window = window;
+ view->color_bg = 0x000000;
+ view->color_fg = 0xffffff;
+ window->view = view;
+}
+
+void eve_view_set_color_bg(EVEView *view, uint8_t r, uint8_t g, uint8_t b) {
+ view->color_bg = (r << 16) | (g << 8) | b;
+}
+
+void eve_view_set_color_fg(EVEView *view, uint8_t r, uint8_t g, uint8_t b) {
+ view->color_fg = (r << 16) | (g << 8) | b;
+}
+
+uint8_t eve_view_clear(EVEView *view, uint8_t tag0, uint8_t tag_opt) {
+ EVEWindow *win_scroll = NULL;
+ EVEWindow *window = view->window;
+ uint8_t _tag;
+
+ win_scroll = eve_window_scroll(window->root, &_tag);
+ eve_cmd_dl(CLEAR_COLOR_RGBC(view->color_bg));
+ eve_cmd_dl(COLOR_RGBC(view->color_fg));
+ if (win_scroll == window) {
+ view->tag = _tag;
+ eve_touch_set_opt(view->tag, tag_opt);
+ eve_cmd_dl(TAG(view->tag));
+ eve_cmd_dl(CLEAR_TAG(view->tag));
+ } else if (win_scroll) {
+ view->tag = EVE_NOTAG;
+ eve_cmd_dl(TAG(view->tag));
+ eve_cmd_dl(CLEAR_TAG(view->tag));
+ } else {
+ view->tag = tag0;
+ if (tag0 != EVE_NOTAG) {
+ eve_touch_set_opt(tag0, tag_opt);
+ eve_cmd_dl(CLEAR_TAG(tag0));
+ tag0++;
+ }
+ }
+ eve_cmd_dl(CLEAR(1,1,1));
+ return tag0;
+}
+
+void eve_view_stack_init(EVEViewStack *stack) {
+ memset(stack, 0, sizeof(EVEViewStack));
+}
+
+void eve_view_create(EVEWindow *window, EVEViewStack *stack, eve_view_constructor_t constructor) {
+ if (stack->level < EVE_VIEW_SIZE_STACK - 1) {
+ stack->constructor[stack->level] = constructor;
+ stack->level++;
+ constructor(window, stack);
+ }
+}
+
+void eve_view_destroy(EVEWindow *window, EVEViewStack *stack) {
+ if (stack->level > 1) {
+ eve_view_constructor_t constructor;
+
+ stack->level--;
+ constructor = stack->constructor[stack->level - 1];
+ constructor(window, stack);
+ }
+}
+
+void eve_view_uievt_push(EVEView *view, uint16_t evt, void *param) {
+ if (view->uievt) view->uievt(view, evt, param);
+}
+
+int eve_view_uievt_pusht(EVEView *view, uint16_t evt, EVETouch *touch, uint16_t t_evt, uint8_t tag0) {
+ if (view->uievt) {
+ EVEUIEvtTouch param;
+
+ param.touch = touch;
+ param.evt = t_evt;
+ param.tag0 = tag0;
+ view->uievt(view, evt, &param);
+ }
+ return 0;
+}
diff --git a/fw/fe310/eos/eve/screen/view.h b/fw/fe310/eos/eve/screen/view.h
new file mode 100644
index 0000000..65999d7
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/view.h
@@ -0,0 +1,42 @@
+#include <stdint.h>
+
+#include "uievt.h"
+
+#define EVE_VIEW_SIZE_STACK 16
+
+struct EVEView;
+struct EVEViewStack;
+struct EVEWindow;
+
+typedef uint8_t (*eve_view_draw_t) (struct EVEView *, uint8_t);
+typedef int (*eve_view_touch_t) (struct EVEView *, EVETouch *, uint16_t, uint8_t);
+typedef int (*eve_view_uievt_t) (struct EVEView *, uint16_t, void *);
+typedef void (*eve_view_constructor_t) (struct EVEWindow *window, struct EVEViewStack *);
+
+typedef struct EVEView {
+ eve_view_draw_t draw;
+ eve_view_touch_t touch;
+ eve_view_uievt_t uievt;
+ struct EVEWindow *window;
+ void *param;
+ uint32_t color_bg;
+ uint32_t color_fg;
+ uint8_t tag;
+} EVEView;
+
+typedef struct EVEViewStack {
+ eve_view_constructor_t constructor[EVE_VIEW_SIZE_STACK];
+ uint8_t level;
+} EVEViewStack;
+
+void eve_view_init(EVEView *view, struct EVEWindow *window, eve_view_draw_t draw, eve_view_touch_t touch, eve_view_uievt_t uievt, void *param);
+void eve_view_set_color_bg(EVEView *view, uint8_t r, uint8_t g, uint8_t b);
+void eve_view_set_color_fg(EVEView *view, uint8_t r, uint8_t g, uint8_t b);
+uint8_t eve_view_clear(EVEView *view, uint8_t tag0, uint8_t tag_opt);
+
+void eve_view_stack_init(EVEViewStack *stack);
+void eve_view_create(struct EVEWindow *window, EVEViewStack *stack, eve_view_constructor_t constructor);
+void eve_view_destroy(struct EVEWindow *window, EVEViewStack *stack);
+
+void eve_view_uievt_push(EVEView *view, uint16_t evt, void *param);
+int eve_view_uievt_pusht(EVEView *view, uint16_t evt, EVETouch *touch, uint16_t t_evt, uint8_t tag0);
diff --git a/fw/fe310/eos/eve/screen/window.c b/fw/fe310/eos/eve/screen/window.c
new file mode 100644
index 0000000..c259b19
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/window.c
@@ -0,0 +1,300 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "window.h"
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+
+void eve_window_init(EVEWindow *window, EVERect *g, EVEWindow *parent, char *name) {
+ memset(window, 0, sizeof(EVEWindow));
+
+ if (g) window->g = *g;
+ window->root = parent ? parent->root : NULL;
+ window->parent = parent;
+ window->name = name;
+}
+
+void eve_window_init_root(EVEWindowRoot *root, EVERect *g, char *name, EVEFont *font) {
+ EVEWindow *_window = &root->w;
+
+ eve_window_init(_window, g, NULL, name);
+ _window->root = root;
+ root->mem_next = EVE_RAM_G;
+ root->font = font;
+ root->win_kbd = NULL;
+ root->tag0 = EVE_NOTAG;
+ eve_touch_set_handler(eve_window_root_touch, root);
+}
+
+static uint8_t kbd_draw(EVEView *view, uint8_t tag0) {
+ EVEKbd *kbd = view->param;
+
+ tag0 = eve_view_clear(view, tag0, 0);
+
+ eve_kbd_draw(kbd);
+ return tag0;
+}
+
+static int kbd_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ EVEKbd *kbd = view->param;
+
+ return eve_kbd_touch(kbd, touch, evt, tag0);
+}
+
+void eve_window_init_kbd(EVEWindowKbd *win_kbd, EVERect *g, EVEWindowRoot *root, char *name, EVEKbd *kbd) {
+ EVEWindow *_window = &win_kbd->w;
+
+ eve_window_init(_window, g, NULL, name);
+ _window->root = root;
+ win_kbd->kbd = kbd;
+ root->win_kbd = win_kbd;
+ eve_view_init(&win_kbd->v, _window, kbd_draw, kbd_touch, NULL, kbd);
+}
+
+void eve_window_set_parent(EVEWindow *window, EVEWindow *parent) {
+ window->parent = parent;
+ window->root = parent->root;
+}
+
+int eve_window_visible(EVEWindow *window) {
+ if (window->g.x >= window->root->w.g.w) return 0;
+ if (window->g.y >= window->root->w.g.h) return 0;
+ if ((window->g.x + window->g.w) <= 0) return 0;
+ if ((window->g.y + window->g.h) <= 0) return 0;
+ return 1;
+}
+
+static void window_visible_g(EVEWindow *w, EVERect *g) {
+ while (w) {
+ if (eve_window_visible(w)) {
+ if (w->g.x > g->x) g->w = MIN(g->w, w->g.x - g->x);
+ if (w->g.y > g->y) g->h = MIN(g->h, w->g.y - g->y);
+ if (w->g.x + w->g.w < g->x + g->w) {
+ uint16_t x0 = g->w - MIN(g->w, (g->x + g->w) - (w->g.x + w->g.w));
+ g->x += x0;
+ g->w -= x0;
+ }
+ if (w->g.y + w->g.h < g->y + g->h) {
+ uint16_t y0 = g->h - MIN(g->h, (g->y + g->h) - (w->g.y + w->g.h));
+ g->y += y0;
+ g->h -= y0;
+ }
+ }
+ if (w->child_head) window_visible_g(w->child_head, g);
+ w = w->next;
+ }
+}
+
+void eve_window_visible_g(EVEWindow *window, EVERect *g) {
+ *g = window->g;
+ if (window->child_head) window_visible_g(window->child_head, g);
+ window_visible_g(window->next, g);
+}
+
+void eve_window_append(EVEWindow *window) {
+ EVEWindow *parent = window->parent;
+
+ window->prev = parent->child_tail;
+ window->next = NULL;
+ if (parent->child_tail) {
+ parent->child_tail->next = window;
+ } else {
+ parent->child_head = window;
+ }
+ parent->child_tail = window;
+}
+
+void eve_window_insert_above(EVEWindow *window, EVEWindow *win_prev) {
+ EVEWindow *parent = window->parent;
+
+ window->prev = win_prev;
+ window->next = win_prev->next;
+
+ if (window->next) {
+ window->next->prev = window;
+ } else {
+ parent->child_tail = window;
+ }
+ win_prev->next = window;
+}
+
+void eve_window_insert_below(EVEWindow *window, EVEWindow *win_next) {
+ EVEWindow *parent = window->parent;
+
+ window->prev = win_next->prev;
+ window->next = win_next;
+
+ win_next->prev = window;
+ if (window->prev) {
+ window->prev->next = window;
+ } else {
+ parent->child_head = window;
+ }
+}
+
+void eve_window_remove(EVEWindow *window) {
+ EVEWindow *parent = window->parent;
+
+ if (window->prev) {
+ window->prev->next = window->next;
+ } else {
+ parent->child_head = window->next;
+ }
+ if (window->next) {
+ window->next->prev = window->prev;
+ } else {
+ parent->child_tail = window->prev;
+ }
+ window->parent = NULL;
+ window->prev = NULL;
+ window->next = NULL;
+}
+
+EVEWindow *eve_window_search(EVEWindow *window, char *name) {
+ while (window) {
+ if (window->name && (strcmp(name, window->name) == 0)) return window;
+ if (window->child_head) {
+ EVEWindow *ret = eve_window_search(window->child_head, name);
+ if (ret) return ret;
+ }
+ window = window->next;
+ }
+
+ return NULL;
+}
+
+uint8_t eve_window_draw(EVEWindow *window, uint8_t tag0) {
+ while (window) {
+ if (eve_window_visible(window) && window->view) {
+ int16_t s_x = window->g.x;
+ int16_t s_y = window->g.y;
+ uint16_t s_w = window->g.w;
+ uint16_t s_h = window->g.h;
+
+ if (s_x < 0) {
+ s_w += s_x;
+ s_x = 0;
+ }
+ if (s_y < 0) {
+ s_h += s_y;
+ s_y = 0;
+ }
+ if (s_x + s_w > window->root->w.g.w) s_w = window->root->w.g.w - s_x;
+ if (s_y + s_h > window->root->w.g.h) s_h = window->root->w.g.h - s_y;
+ eve_cmd_dl(SCISSOR_XY(s_x, s_y));
+ eve_cmd_dl(SCISSOR_SIZE(s_w, s_h));
+ tag0 = window->view->draw(window->view, tag0);
+ }
+ if (window->child_head) tag0 = eve_window_draw(window->child_head, tag0);
+ window = window->next;
+ }
+
+ return tag0;
+}
+
+int eve_window_touch(EVEWindow *window, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ int ret = 0;
+
+ while (window) {
+ if (window->child_tail) {
+ ret = eve_window_touch(window->child_tail, touch, evt, tag0);
+ if (ret) return 1;
+ }
+ if (eve_window_visible(window) && window->view) {
+ ret = window->view->touch(window->view, touch, evt, tag0);
+ if (ret) return 1;
+ }
+ window = window->prev;
+ }
+
+ return 0;
+}
+
+void eve_window_root_draw(EVEWindowRoot *root) {
+ uint8_t tag0 = 0x80;
+ int rv;
+
+ eve_cmd_burst_start();
+ eve_cmd_dl(CMD_DLSTART);
+
+ if (root->tag0 != EVE_NOTAG) tag0 = EVE_NOTAG;
+ eve_window_draw(&root->w, tag0);
+
+ eve_cmd_dl(DISPLAY());
+ eve_cmd_dl(CMD_SWAP);
+ eve_cmd_burst_end();
+ rv = eve_cmd_exec(1);
+ if (rv) printf("EVE EXEC ERR\n");
+}
+
+void eve_window_root_touch(EVETouch *touch, uint16_t evt, uint8_t tag0, void *win) {
+ EVEWindowRoot *root = (EVEWindowRoot *)win;
+ int ret;
+
+ if (root->tag0 != EVE_NOTAG) tag0 = root->tag0;
+ ret = eve_window_touch(&root->w, touch, evt, tag0);
+ if (ret) {
+ eve_touch_clear_opt();
+ eve_window_root_draw(root);
+ }
+}
+
+EVEKbd *eve_window_kbd(EVEWindow *window) {
+ EVEWindowRoot *root = window->root;
+ EVEWindowKbd *win_kbd = root->win_kbd;
+
+ if (win_kbd) return win_kbd->kbd;
+ return NULL;
+}
+
+void eve_window_kbd_attach(EVEWindow *window) {
+ EVEWindowRoot *root = window->root;
+ EVEWindowKbd *win_kbd = root->win_kbd;
+ EVEKbd *kbd = win_kbd ? win_kbd->kbd : NULL;
+
+ if (kbd) {
+ eve_window_set_parent(&win_kbd->w, window);
+ eve_window_append(&win_kbd->w);
+ }
+}
+
+void eve_window_kbd_detach(EVEWindow *window) {
+ EVEWindowRoot *root = window->root;
+ EVEWindowKbd *win_kbd = root->win_kbd;
+ EVEKbd *kbd = win_kbd ? win_kbd->kbd : NULL;
+
+ if (kbd && win_kbd->w.parent) {
+ eve_window_remove(&win_kbd->w);
+ eve_kbd_close(kbd);
+ }
+}
+
+EVEFont *eve_window_font(EVEWindow *window) {
+ EVEWindowRoot *root = window->root;
+
+ return root->font;
+}
+
+EVEWindow *eve_window_scroll(EVEWindowRoot *root, uint8_t *tag) {
+ if (tag) *tag = root->tag0;
+ return root->win_scroll;
+}
+
+void eve_window_scroll_start(EVEWindow *window, uint8_t tag) {
+ EVEWindowRoot *root = window->root;
+
+ root->win_scroll = window;
+ root->tag0 = tag;
+}
+
+void eve_window_scroll_stop(EVEWindow *window) {
+ EVEWindowRoot *root = window->root;
+
+ root->win_scroll = NULL;
+ root->tag0 = EVE_NOTAG;
+}
diff --git a/fw/fe310/eos/eve/screen/window.h b/fw/fe310/eos/eve/screen/window.h
new file mode 100644
index 0000000..27465c4
--- /dev/null
+++ b/fw/fe310/eos/eve/screen/window.h
@@ -0,0 +1,60 @@
+#include <stdint.h>
+
+#include "view.h"
+
+struct EVEWindowRoot;
+
+typedef struct EVEWindow {
+ EVERect g;
+ char *name;
+ EVEView *view;
+ struct EVEWindowRoot *root;
+ struct EVEWindow *parent;
+ struct EVEWindow *next;
+ struct EVEWindow *prev;
+ struct EVEWindow *child_head;
+ struct EVEWindow *child_tail;
+} EVEWindow;
+
+typedef struct EVEWindowKbd {
+ EVEWindow w;
+ EVEView v;
+ EVEKbd *kbd;
+} EVEWindowKbd;
+
+typedef struct EVEWindowRoot {
+ EVEWindow w;
+ uint32_t mem_next;
+ EVEFont *font;
+ EVEWindowKbd *win_kbd;
+ EVEWindow *win_scroll;
+ uint8_t tag0;
+} EVEWindowRoot;
+
+void eve_window_init(EVEWindow *window, EVERect *g, EVEWindow *parent, char *name);
+void eve_window_init_root(EVEWindowRoot *root, EVERect *g, char *name, EVEFont *font);
+void eve_window_init_kbd(EVEWindowKbd *win_kbd, EVERect *g, EVEWindowRoot *root, char *name, EVEKbd *kbd);
+void eve_window_set_parent(EVEWindow *window, EVEWindow *parent);
+
+int eve_window_visible(EVEWindow *window);
+void eve_window_visible_g(EVEWindow *window, EVERect *g);
+
+void eve_window_append(EVEWindow *window);
+void eve_window_insert_above(EVEWindow *window, EVEWindow *win_prev);
+void eve_window_insert_below(EVEWindow *window, EVEWindow *win_next);
+void eve_window_remove(EVEWindow *window);
+EVEWindow *eve_window_search(EVEWindow *window, char *name);
+
+uint8_t eve_window_draw(EVEWindow *window, uint8_t tag0);
+int eve_window_touch(EVEWindow *window, EVETouch *touch, uint16_t evt, uint8_t tag0);
+void eve_window_root_draw(EVEWindowRoot *root);
+void eve_window_root_touch(EVETouch *touch, uint16_t evt, uint8_t tag0, void *win);
+
+EVEKbd *eve_window_kbd(EVEWindow *window);
+void eve_window_kbd_attach(EVEWindow *window);
+void eve_window_kbd_detach(EVEWindow *window);
+EVEFont *eve_window_font(EVEWindow *window);
+
+EVEWindow *eve_window_scroll(EVEWindowRoot *root, uint8_t *tag);
+void eve_window_scroll_start(EVEWindow *window, uint8_t tag);
+void eve_window_scroll_stop(EVEWindow *window);
diff --git a/fw/fe310/eos/eve/widget/Makefile b/fw/fe310/eos/eve/widget/Makefile
new file mode 100644
index 0000000..95af16d
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/Makefile
@@ -0,0 +1,17 @@
+include ../../../common.mk
+
+CFLAGS += -I.. -I../..
+
+obj = label.o widget.o freew.o spacerw.o pagew.o strw.o textw.o selectw.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/eos/eve/widget/freew.c b/fw/fe310/eos/eve/widget/freew.c
new file mode 100644
index 0000000..86c2686
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/freew.c
@@ -0,0 +1,54 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+#include "widget.h"
+#include "freew.h"
+
+int eve_freew_create(EVEFreeWidget *widget, EVERect *g, EVEPage *page, EVEFreeSpec *spec) {
+ eve_freew_init(widget, g, page, spec->draw, spec->touch, spec->putc);
+
+ return EVE_OK;
+}
+
+void eve_freew_init(EVEFreeWidget *widget, EVERect *g, EVEPage *page, eve_freew_draw_t draw, eve_freew_touch_t touch, eve_kbd_input_handler_t putc) {
+ EVEWidget *_widget = &widget->w;
+
+ memset(widget, 0, sizeof(EVEFreeWidget));
+ eve_widget_init(_widget, EVE_WIDGET_TYPE_FREE, g, page, eve_freew_draw, eve_freew_touch, putc);
+ widget->_draw = draw;
+ widget->_touch = touch;
+ widget->w.putc = putc;
+}
+
+void eve_freew_tag(EVEFreeWidget *widget) {
+ EVEWidget *_widget = &widget->w;
+
+ if (_widget->tagN != EVE_NOTAG) {
+ eve_cmd_dl(TAG(_widget->tagN));
+ _widget->tagN++;
+ }
+}
+
+uint8_t eve_freew_draw(EVEWidget *_widget, uint8_t tag0) {
+ EVEFreeWidget *widget = (EVEFreeWidget *)_widget;
+
+ _widget->tag0 = tag0;
+ _widget->tagN = tag0;
+ widget->_draw(widget);
+
+ return _widget->tagN;
+}
+
+int eve_freew_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) {
+ EVEFreeWidget *widget = (EVEFreeWidget *)_widget;
+
+ return widget->_touch(widget, touch, evt);
+}
diff --git a/fw/fe310/eos/eve/widget/freew.h b/fw/fe310/eos/eve/widget/freew.h
new file mode 100644
index 0000000..7eda8c1
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/freew.h
@@ -0,0 +1,25 @@
+#include <stdint.h>
+
+struct EVEFreeWidget;
+
+typedef void (*eve_freew_draw_t) (struct EVEFreeWidget *);
+typedef int (*eve_freew_touch_t) (struct EVEFreeWidget *, EVETouch *, uint16_t);
+
+typedef struct EVEFreeWidget {
+ EVEWidget w;
+ eve_freew_draw_t _draw;
+ eve_freew_touch_t _touch;
+} EVEFreeWidget;
+
+typedef struct EVEFreeSpec {
+ eve_freew_draw_t draw;
+ eve_freew_touch_t touch;
+ eve_kbd_input_handler_t putc;
+} EVEFreeSpec;
+
+int eve_freew_create(EVEFreeWidget *widget, EVERect *g, EVEPage *page, EVEFreeSpec *spec);
+void eve_freew_init(EVEFreeWidget *widget, EVERect *g, EVEPage *page, eve_freew_draw_t draw, eve_freew_touch_t touch, eve_kbd_input_handler_t putc);
+
+void eve_freew_tag(EVEFreeWidget *widget);
+uint8_t eve_freew_draw(EVEWidget *_widget, uint8_t tag0);
+int eve_freew_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt);
diff --git a/fw/fe310/eos/eve/widget/label.c b/fw/fe310/eos/eve/widget/label.c
new file mode 100644
index 0000000..f2d2e8e
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/label.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+
+void eve_label_init(EVELabel *label, EVERect *g, EVEFont *font, char *title) {
+ memset(label, 0, sizeof(EVELabel));
+ if (g) label->g = *g;
+ label->font = font;
+ label->title = title;
+ if (label->g.h == 0) label->g.h = eve_font_h(font);
+}
+
+void eve_label_draw(EVELabel *label) {
+ eve_cmd(CMD_TEXT, "hhhhs", label->g.x, label->g.y, label->font->id, 0, label->title);
+} \ No newline at end of file
diff --git a/fw/fe310/eos/eve/widget/label.h b/fw/fe310/eos/eve/widget/label.h
new file mode 100644
index 0000000..659c057
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/label.h
@@ -0,0 +1,10 @@
+#include <stdint.h>
+
+typedef struct EVELabel {
+ EVERect g;
+ EVEFont *font;
+ char *title;
+} EVELabel;
+
+void eve_label_init(EVELabel *label, EVERect *g, EVEFont *font, char *title);
+void eve_label_draw(EVELabel *label); \ No newline at end of file
diff --git a/fw/fe310/eos/eve/widget/pagew.c b/fw/fe310/eos/eve/widget/pagew.c
new file mode 100644
index 0000000..bd7819f
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/pagew.c
@@ -0,0 +1,59 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+#include "widget.h"
+#include "pagew.h"
+
+int eve_pagew_create(EVEPageWidget *widget, EVERect *g, EVEPage *page, EVEPageSpec *spec) {
+ EVEFont *font = spec->font ? spec->font : eve_window_font(page->v.window);
+
+ eve_pagew_init(widget, g, page, font, spec->title, spec->constructor);
+
+ return EVE_OK;
+}
+
+void eve_pagew_init(EVEPageWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, char *title, eve_view_constructor_t constructor) {
+ EVEWidget *_widget = &widget->w;
+
+ memset(widget, 0, sizeof(EVEPageWidget));
+ eve_widget_init(_widget, EVE_WIDGET_TYPE_PAGE, g, page, eve_pagew_draw, eve_pagew_touch, NULL);
+ widget->font = font;
+ widget->title = title;
+ widget->constructor = constructor;
+ if (_widget->g.h == 0) _widget->g.h = eve_font_h(widget->font);
+}
+
+uint8_t eve_pagew_draw(EVEWidget *_widget, uint8_t tag0) {
+ EVEPageWidget *widget = (EVEPageWidget *)_widget;
+
+ _widget->tag0 = tag0;
+ if (tag0 != EVE_NOTAG) {
+ eve_cmd_dl(TAG(tag0));
+ tag0++;
+ }
+ _widget->tagN = tag0;
+
+ eve_cmd(CMD_TEXT, "hhhhs", _widget->g.x, _widget->g.y, widget->font->id, 0, widget->title);
+
+ return _widget->tagN;
+}
+
+int eve_pagew_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) {
+ EVEPage *parent = _widget->page;
+ EVEPageWidget *widget = (EVEPageWidget *)_widget;
+
+ if (evt & EVE_TOUCH_ETYPE_TAG_UP) {
+ eve_page_open(parent, widget->constructor);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/fw/fe310/eos/eve/widget/pagew.h b/fw/fe310/eos/eve/widget/pagew.h
new file mode 100644
index 0000000..76ac0d2
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/pagew.h
@@ -0,0 +1,20 @@
+#include <stdint.h>
+
+typedef struct EVEPageWidget {
+ EVEWidget w;
+ EVEFont *font;
+ char *title;
+ eve_view_constructor_t constructor;
+} EVEPageWidget;
+
+typedef struct EVEPageSpec {
+ EVEFont *font;
+ char *title;
+ eve_view_constructor_t constructor;
+} EVEPageSpec;
+
+int eve_pagew_create(EVEPageWidget *widget, EVERect *g, EVEPage *page, EVEPageSpec *spec);
+void eve_pagew_init(EVEPageWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, char *title, eve_view_constructor_t constructor);
+
+uint8_t eve_pagew_draw(EVEWidget *_widget, uint8_t tag0);
+int eve_pagew_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt);
diff --git a/fw/fe310/eos/eve/widget/selectw.c b/fw/fe310/eos/eve/widget/selectw.c
new file mode 100644
index 0000000..46ed3d1
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/selectw.c
@@ -0,0 +1,248 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+#include "widget.h"
+#include "selectw.h"
+
+#define SELECTW_NOSELECT 0xffffffff
+
+#define DIVC(x,y) ((x) / (y) + ((x) % (y) != 0))
+
+static int selectw_verify(utf8_t *option, uint16_t option_size) {
+ int o_len;
+ uint16_t o_curr;
+ int rv;
+
+ o_curr = 0;
+ while (o_curr < option_size) {
+ rv = utf8_verify(option + o_curr, option_size - o_curr, &o_len);
+ if (rv) return EVE_ERR;
+ if (o_len == 0) return EVE_OK;
+ o_curr += o_len + 1;
+ }
+
+ return EVE_OK;
+}
+
+static int selectw_count(EVESelectWidget *widget) {
+ int o_len;
+ int o_curr;
+ int i;
+
+ o_curr = 0;
+ i = 0;
+ do {
+ o_len = strnlen(widget->option + o_curr, widget->option_size - o_curr);
+ if (o_len == widget->option_size - o_curr) return i;
+ if (o_len) {
+ o_curr += o_len + 1;
+ i++;
+ }
+ } while (o_len);
+
+ return i;
+}
+
+static void selectw_update_sz(EVESelectWidget *widget, int uievt) {
+ EVEWidget *_widget = &widget->w;
+ EVEPage *page = _widget->page;
+
+ _widget->g.h = widget->option_count * widget->font->h;
+ if (uievt) eve_view_uievt_push(&page->v, EVE_UIEVT_WIDGET_UPDATE_G, _widget);
+}
+
+int eve_selectw_create(EVESelectWidget *widget, EVERect *g, EVEPage *page, EVESelectSpec *spec) {
+ EVEFont *font = spec->font ? spec->font : eve_window_font(page->v.window);
+ utf8_t *option;
+
+ option = eve_malloc(spec->option_size);
+ if (option == NULL) {
+ return EVE_ERR_NOMEM;
+ }
+ memset(option, 0, spec->option_size);
+
+ eve_selectw_init(widget, g, page, font, option, spec->option_size, spec->multi);
+
+ return EVE_OK;
+}
+
+void eve_selectw_init(EVESelectWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *option, uint16_t option_size, uint8_t multi) {
+ EVEWidget *_widget = &widget->w;
+ int rv;
+
+ memset(widget, 0, sizeof(EVESelectWidget));
+ eve_widget_init(_widget, EVE_WIDGET_TYPE_SELECT, g, page, eve_selectw_draw, eve_selectw_touch, NULL);
+ widget->font = font;
+ rv = selectw_verify(option, option_size);
+ if (rv == EVE_OK) {
+ widget->option = option;
+ widget->option_size = option_size;
+ widget->option_count = selectw_count(widget);
+ selectw_update_sz(widget, 0);
+ }
+ widget->multi = multi;
+ widget->select = widget->multi ? 0 : SELECTW_NOSELECT;
+}
+
+void eve_selectw_destroy(EVESelectWidget *widget) {
+ eve_free(widget->option);
+}
+
+uint8_t eve_selectw_draw(EVEWidget *_widget, uint8_t tag0) {
+ EVEPage *page = _widget->page;
+ EVESelectWidget *widget = (EVESelectWidget *)_widget;
+ int o_len;
+ int o_curr;
+ int i, s;
+ int16_t x1, x2, y1, y2;
+ uint16_t new_h;
+
+ int line0, lineN;
+
+ line0 = -((int)eve_page_win_y(page, _widget->g.y)) / widget->font->h;
+ lineN = DIVC((-((int)eve_page_win_y(page, _widget->g.y)) + page->v.window->g.h), widget->font->h);
+ if (line0 < 0) line0 = 0;
+ if (lineN < 0) lineN = 0;
+ if (line0 > widget->option_count) line0 = widget->option_count;
+ if (lineN > widget->option_count) lineN = widget->option_count;
+
+ _widget->tag0 = tag0;
+ _widget->tagN = tag0;
+ widget->line0 = line0;
+
+ o_curr = 0;
+ i = 0;
+ do {
+ o_len = strnlen(widget->option + o_curr, widget->option_size - o_curr);
+ if (!o_len || (o_len == widget->option_size - o_curr)) break;
+ if ((i >= line0) && (i < lineN)) {
+ if (_widget->tagN != EVE_NOTAG) {
+ eve_cmd_dl(TAG(_widget->tagN));
+ _widget->tagN++;
+ }
+ s = widget->multi ? widget->select & (0x1 << i) : widget->select == i;
+ x1 = _widget->g.x;
+ x2 = x1 + _widget->g.w;
+ y1 = _widget->g.y + i * widget->font->h;
+ y2 = y1 + widget->font->h;
+ eve_cmd_dl(BEGIN(EVE_RECTS));
+ if (!s) eve_cmd_dl(COLOR_MASK(0 ,0 ,0 ,0));
+ eve_cmd_dl(VERTEX2F(x1, y1));
+ eve_cmd_dl(VERTEX2F(x2, y2));
+ if (!s) {
+ eve_cmd_dl(COLOR_MASK(1 ,1 ,1 ,1));
+ eve_cmd_dl(BEGIN(EVE_LINES));
+ eve_cmd_dl(VERTEX2F(x1, y2));
+ eve_cmd_dl(VERTEX2F(x2, y2));
+ }
+ eve_cmd_dl(END());
+ if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_bg));
+ eve_cmd(CMD_TEXT, "hhhhs", x1, y1, widget->font->id, 0, widget->option + o_curr);
+ if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_fg));
+ }
+
+ o_curr += o_len + 1;
+ i++;
+ } while (o_len);
+
+ return _widget->tagN;
+}
+
+int eve_selectw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) {
+ EVESelectWidget *widget = (EVESelectWidget *)_widget;
+
+ if (evt & EVE_TOUCH_ETYPE_TAG_UP) {
+ int i = touch->tag0 - _widget->tag0 + widget->line0;
+ if (widget->multi) {
+ uint32_t f = (0x1 << i);
+
+ if (widget->select & f) {
+ widget->select &= ~f;
+ } else {
+ widget->select |= f;
+ }
+ } else {
+ if (widget->select == i) {
+ widget->select = SELECTW_NOSELECT;
+ } else {
+ widget->select = i;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+utf8_t *eve_selectw_option_get(EVESelectWidget *widget, int idx) {
+ int o_len;
+ int o_curr;
+ int i;
+
+ o_curr = 0;
+ i = 0;
+ do {
+ o_len = strnlen(widget->option + o_curr, widget->option_size - o_curr);
+ if (o_len == widget->option_size - o_curr) return NULL;
+ if (o_len && (i == idx)) return widget->option + o_curr;
+ o_curr += o_len + 1;
+ i++;
+ } while (o_len);
+
+ return NULL;
+}
+
+utf8_t *eve_selectw_option_get_select(EVESelectWidget *widget) {
+ return eve_selectw_option_get(widget, widget->select);
+}
+
+int eve_selectw_option_add(EVESelectWidget *widget, utf8_t *option) {
+ int o_len;
+ int o_curr;
+ int rv, i;
+
+ rv = utf8_verify(option, strlen(option) + 1, NULL);
+ if (rv) return EVE_ERR;
+
+ o_curr = 0;
+ i = 0;
+ do {
+ o_len = strnlen(widget->option + o_curr, widget->option_size - o_curr);
+ if (o_len == widget->option_size - o_curr) return EVE_ERR_FULL;
+ if (o_len) {
+ o_curr += o_len + 1;
+ i++;
+ }
+ } while (o_len);
+
+ if (o_curr + strlen(option) + 1 > widget->option_size) return EVE_ERR_FULL;
+ strcpy(widget->option + o_curr, option);
+
+ widget->option_count = i + 1;
+ selectw_update_sz(widget, 1);
+
+ return EVE_OK;
+}
+
+int eve_selectw_option_set(EVESelectWidget *widget, utf8_t *option, uint16_t option_size) {
+ int rv, i;
+
+ rv = selectw_verify(option, option_size);
+ if (rv) return rv;
+ if (option_size > widget->option_size) return EVE_ERR_FULL;
+
+ memcpy(widget->option, option, option_size);
+ memset(widget->option + option_size, 0, widget->option_size - option_size);
+
+ widget->option_count = selectw_count(widget);
+ selectw_update_sz(widget, 1);
+
+ return EVE_OK;
+}
diff --git a/fw/fe310/eos/eve/widget/selectw.h b/fw/fe310/eos/eve/widget/selectw.h
new file mode 100644
index 0000000..939e362
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/selectw.h
@@ -0,0 +1,30 @@
+#include <stdint.h>
+
+typedef struct EVESelectWidget {
+ EVEWidget w;
+ EVEFont *font;
+ utf8_t *option;
+ uint16_t option_size;
+ uint16_t option_count;
+ uint32_t select;
+ uint16_t line0;
+ uint8_t multi;
+} EVESelectWidget;
+
+typedef struct EVESelectSpec {
+ EVEFont *font;
+ uint16_t option_size;
+ uint8_t multi;
+} EVESelectSpec;
+
+int eve_selectw_create(EVESelectWidget *widget, EVERect *g, EVEPage *page, EVESelectSpec *spec);
+void eve_selectw_init(EVESelectWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *option, uint16_t option_size, uint8_t multi);
+void eve_selectw_destroy(EVESelectWidget *widget);
+
+uint8_t eve_selectw_draw(EVEWidget *_widget, uint8_t tag0);
+int eve_selectw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt);
+
+utf8_t *eve_selectw_option_get(EVESelectWidget *widget, int idx);
+utf8_t *eve_selectw_option_get_select(EVESelectWidget *widget);
+int eve_selectw_option_add(EVESelectWidget *widget, utf8_t *option);
+int eve_selectw_option_set(EVESelectWidget *widget, utf8_t *option, uint16_t option_size);
diff --git a/fw/fe310/eos/eve/widget/spacerw.c b/fw/fe310/eos/eve/widget/spacerw.c
new file mode 100644
index 0000000..d9f9104
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/spacerw.c
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+#include "widget.h"
+#include "spacerw.h"
+
+int eve_spacerw_create(EVESpacerWidget *widget, EVERect *g, EVEPage *page, EVESpacerSpec *spec) {
+ eve_spacerw_init(widget, g, page);
+
+ return EVE_OK;
+}
+
+void eve_spacerw_init(EVESpacerWidget *widget, EVERect *g, EVEPage *page) {
+ EVEWidget *_widget = &widget->w;
+
+ memset(widget, 0, sizeof(EVESpacerWidget));
+ eve_widget_init(_widget, EVE_WIDGET_TYPE_SPACER, g, page, eve_spacerw_draw, eve_spacerw_touch, NULL);
+}
+
+uint8_t eve_spacerw_draw(EVEWidget *_widget, uint8_t tag0) {
+ _widget->tag0 = tag0;
+ _widget->tagN = tag0;
+ return _widget->tagN;
+}
+
+int eve_spacerw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) {
+ return 0;
+}
diff --git a/fw/fe310/eos/eve/widget/spacerw.h b/fw/fe310/eos/eve/widget/spacerw.h
new file mode 100644
index 0000000..9dafa8b
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/spacerw.h
@@ -0,0 +1,14 @@
+#include <stdint.h>
+
+typedef struct EVESpacerWidget {
+ EVEWidget w;
+} EVESpacerWidget;
+
+typedef struct EVESpacerSpec {
+} EVESpacerSpec;
+
+int eve_spacerw_create(EVESpacerWidget *widget, EVERect *g, EVEPage *page, EVESpacerSpec *spec);
+void eve_spacerw_init(EVESpacerWidget *widget, EVERect *g, EVEPage *page);
+
+uint8_t eve_spacerw_draw(EVEWidget *_widget, uint8_t tag0);
+int eve_spacerw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt);
diff --git a/fw/fe310/eos/eve/widget/strw.c b/fw/fe310/eos/eve/widget/strw.c
new file mode 100644
index 0000000..e78cf46
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/strw.c
@@ -0,0 +1,446 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "clipb.h"
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+#include "widget.h"
+#include "strw.h"
+
+#define STRW_TOUCH_OPT EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_X | EVE_TOUCH_OPT_TRACK_EXT_X | EVE_TOUCH_OPT_LPRESS
+
+#define STRW_TMODE_NONE 0
+#define STRW_TMODE_CRSR 1
+#define STRW_TMODE_TXT 2
+
+#define CH_BS 0x08
+#define CH_DEL 0x7f
+
+#define CH_CTRLX 0x18
+#define CH_CTRLC 0x03
+#define CH_CTRLV 0x16
+
+#define CHAR_VALID_INPUT(c) ((c >= 0x20) && (c < 0x7f))
+
+int eve_strw_create(EVEStrWidget *widget, EVERect *g, EVEPage *page, EVEStrSpec *spec) {
+ EVEFont *font = spec->font ? spec->font : eve_window_font(page->v.window);
+ utf8_t *str;
+ uint16_t *line;
+
+ str = eve_malloc(spec->str_size);
+ if (str == NULL) return EVE_ERR_NOMEM;
+ str[0] = '\0';
+
+ eve_strw_init(widget, g, page, font, str, spec->str_size);
+
+ return EVE_OK;
+}
+
+void eve_strw_init(EVEStrWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *str, uint16_t str_size) {
+ EVEWidget *_widget = &widget->w;
+ int rv, str_len;
+
+ memset(widget, 0, sizeof(EVEStrWidget));
+ eve_widget_init(_widget, EVE_WIDGET_TYPE_STR, g, page, eve_strw_draw, eve_strw_touch, eve_strw_putc);
+ widget->font = font;
+ rv = utf8_verify(str, str_size, &str_len);
+ if (rv != UTF_OK) {
+ if (str_len >= str_size) str_len = 0;
+ str[str_len] = '\0';
+ }
+ widget->str = str;
+ widget->str_size = str_size;
+ widget->str_len = str_len;
+ widget->str_g.w = eve_font_str_w(widget->font, str);
+ if (_widget->g.h == 0) _widget->g.h = eve_font_h(widget->font);
+}
+
+int eve_strw_update(EVEStrWidget *widget) {
+ int rv, str_len;
+
+ rv = utf8_verify(widget->str, widget->str_size, &str_len);
+ if (rv != UTF_OK) {
+ if (str_len >= widget->str_size) str_len = 0;
+ widget->str[str_len] = '\0';
+ }
+ widget->str_len = str_len;
+ return (rv == UTF_OK) ? EVE_OK : EVE_ERR;
+}
+
+void eve_strw_destroy(EVEStrWidget *widget) {
+ eve_free(widget->str);
+}
+
+static void set_focus(EVEStrWidget *widget) {
+ EVEWidget *_widget = &widget->w;
+ EVERect focus;
+
+ focus.x = _widget->g.x;
+ focus.y = _widget->g.y;
+ focus.w = _widget->g.w;
+ focus.h = 2 * widget->font->h;
+ eve_widget_focus(_widget, &focus);
+}
+
+static EVEStrCursor *cursor_prox(EVEStrWidget *widget, EVEStrCursor *cursor, EVETouch *touch, short *dx) {
+ EVEWidget *_widget = &widget->w;
+ EVEPage *page = _widget->page;
+ int x = eve_page_x(page, touch->x0) - _widget->g.x + widget->str_g.x;
+ int _dx;
+
+ *dx = cursor->x - x;
+ _dx = *dx < 0 ? -(*dx) : *dx;
+
+ if (_dx <= widget->font->w) return cursor;
+ return NULL;
+}
+
+static void draw_string(EVEStrWidget *widget, uint16_t ch, uint16_t len, uint16_t x1, uint16_t x2, char s) {
+ int16_t x;
+ EVEWidget *_widget = &widget->w;
+ EVEPage *page = _widget->page;
+
+ x = _widget->g.x - widget->str_g.x;
+ if (x1 != x2) {
+ eve_cmd_dl(BEGIN(EVE_RECTS));
+ if (!s) eve_cmd_dl(COLOR_MASK(0 ,0 ,0 ,0));
+ eve_cmd_dl(VERTEX2F(x + x1, _widget->g.y));
+ eve_cmd_dl(VERTEX2F(x + x2, _widget->g.y + widget->font->h));
+ if (!s) {
+ eve_cmd_dl(COLOR_MASK(1 ,1 ,1 ,1));
+ eve_cmd_dl(BEGIN(EVE_LINES));
+ eve_cmd_dl(VERTEX2F(x + x1, _widget->g.y + widget->font->h));
+ eve_cmd_dl(VERTEX2F(x + x2, _widget->g.y + widget->font->h));
+ }
+ eve_cmd_dl(END());
+ if (len) {
+ if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_bg));
+ eve_cmd(CMD_TEXT, "hhhhpb", x + x1, _widget->g.y, widget->font->id, 0, widget->str + ch, len, 0);
+ if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_fg));
+ }
+ }
+}
+
+static void draw_cursor(EVEStrWidget *widget, EVEStrCursor *cursor) {
+ uint16_t x, y;
+ EVEWidget *_widget = &widget->w;
+
+ x = _widget->g.x - widget->str_g.x + cursor->x;
+ y = _widget->g.y;
+ eve_cmd_dl(BEGIN(EVE_LINES));
+ eve_cmd_dl(VERTEX2F(x, y));
+ eve_cmd_dl(VERTEX2F(x, y + widget->font->h));
+ eve_cmd_dl(END());
+}
+
+uint8_t eve_strw_draw(EVEWidget *_widget, uint8_t tag0) {
+ EVEStrWidget *widget = (EVEStrWidget *)_widget;
+ char cut = widget->str_g.x || (widget->str_g.w > _widget->g.w);
+
+ _widget->tag0 = tag0;
+ if (tag0 != EVE_NOTAG) {
+ eve_cmd_dl(TAG(tag0));
+ eve_touch_set_opt(tag0, STRW_TOUCH_OPT);
+ tag0++;
+ }
+ _widget->tagN = tag0;
+
+ if (cut) {
+ EVEPage *page = _widget->page;
+ EVEWindow *window = page->v.window;
+ int16_t x = eve_page_scr_x(page, _widget->g.x);
+ int16_t y = eve_page_scr_y(page, _widget->g.y);
+ uint16_t w = _widget->g.w;
+ uint16_t h = _widget->g.h;
+ int16_t win_x1 = window->g.x;
+ int16_t win_y1 = window->g.y;
+ int16_t win_x2 = win_x1 + window->g.w;
+ int16_t win_y2 = win_y1 + window->g.h;
+
+ if (win_x1 < 0) win_x1 = 0;
+ if (win_y1 < 0) win_y1 = 0;
+ if (win_x2 > window->root->w.g.w) win_x2 = window->root->w.g.w;
+ if (win_y2 > window->root->w.g.h) win_y2 = window->root->w.g.h;
+ if (x < win_x1) {
+ w += x - win_x1;
+ x = win_x1;
+ }
+ if (y < win_y1) {
+ h += y - win_y1;
+ y = win_y1;
+ }
+ if (x + w > win_x2) w = win_x2 - x;
+ if (y + h > win_y2) h = win_y2 - y;
+
+ eve_cmd_dl(SAVE_CONTEXT());
+ eve_cmd_dl(SCISSOR_XY(x, y));
+ eve_cmd_dl(SCISSOR_SIZE(w, h));
+ }
+
+ if (widget->cursor2.on) {
+ EVEStrCursor *c1, *c2;
+ int l1, l2, l3;
+
+ if (widget->cursor1.ch <= widget->cursor2.ch) {
+ c1 = &widget->cursor1;
+ c2 = &widget->cursor2;
+ } else {
+ c1 = &widget->cursor2;
+ c2 = &widget->cursor1;
+ }
+
+ l1 = c1->ch;
+ l2 = c2->ch - c1->ch;
+ l3 = widget->str_len - c2->ch;
+ draw_string(widget, 0, l1, 0, c1->x, 0);
+ draw_string(widget, c1->ch, l2, c1->x, c2->x, 1);
+ draw_string(widget, c2->ch, l3, c2->x, widget->str_g.x + _widget->g.w, 0);
+ } else {
+ if (widget->cursor1.on) draw_cursor(widget, &widget->cursor1);
+ draw_string(widget, 0, widget->str_len, 0, widget->str_g.x + _widget->g.w, 0);
+ }
+
+ if (cut) {
+ eve_cmd_dl(RESTORE_CONTEXT());
+ }
+
+ return _widget->tagN;
+}
+
+int eve_strw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) {
+ EVEPage *page = _widget->page;
+ EVEStrWidget *widget = (EVEStrWidget *)_widget;
+ EVEStrCursor *t_cursor = NULL;
+ short dx;
+ int ret = 0;
+
+ if (evt & (EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TRACK_START)) {
+ if (widget->cursor2.on) {
+ t_cursor = cursor_prox(widget, &widget->cursor2, touch, &dx);
+ }
+ if ((t_cursor == NULL) && widget->cursor1.on) {
+ t_cursor = cursor_prox(widget, &widget->cursor1, touch, &dx);
+ }
+ if (evt & EVE_TOUCH_ETYPE_TRACK_START) {
+ if (t_cursor) {
+ widget->track.mode = STRW_TMODE_CRSR;
+ widget->track.cursor = t_cursor;
+ widget->track.dx = dx;
+ } else if (touch->eevt & EVE_TOUCH_EETYPE_TRACK_X) {
+ widget->track.mode = STRW_TMODE_TXT;
+ }
+ }
+ }
+
+ if (widget->track.mode) {
+ int x, w1;
+
+ if (evt & EVE_TOUCH_ETYPE_TRACK) {
+ switch (widget->track.mode) {
+ case STRW_TMODE_TXT:
+ if (evt & EVE_TOUCH_ETYPE_TRACK_START) {
+ widget->str_g.x0 = widget->str_g.x;
+ }
+ x = widget->str_g.x0 + touch->x0 - touch->x;
+ w1 = _widget->g.w - widget->font->w;
+ if (x > widget->str_g.w - w1) x = widget->str_g.w - w1;
+ if (x < 0) x = 0;
+ widget->str_g.x = x;
+ break;
+
+ case STRW_TMODE_CRSR:
+ eve_strw_cursor_set(widget, widget->track.cursor, eve_page_x(page, touch->x) + widget->track.dx);
+ break;
+ }
+ }
+ ret = 1;
+ } else {
+ if (evt & EVE_TOUCH_ETYPE_LPRESS) {
+ if (widget->cursor2.on) {
+ // copy
+ } else if (widget->cursor1.on) {
+ if (t_cursor) {
+ // paste
+ } else {
+ eve_strw_cursor_set(widget, &widget->cursor2, eve_page_x(page, touch->x));
+ }
+ } else {
+ // select
+ }
+ ret = 1;
+ }
+ if ((evt & EVE_TOUCH_ETYPE_POINT_UP) && !(touch->eevt & (EVE_TOUCH_EETYPE_TRACK_XY | EVE_TOUCH_EETYPE_ABORT | EVE_TOUCH_EETYPE_LPRESS))) {
+ eve_strw_cursor_set(widget, &widget->cursor1, eve_page_x(page, touch->x0));
+ if (widget->cursor2.on) eve_strw_cursor_clear(widget, &widget->cursor2);
+ set_focus(widget);
+ ret = 1;
+ }
+ }
+
+ if (evt & EVE_TOUCH_ETYPE_TRACK_STOP) {
+ widget->track.mode = STRW_TMODE_NONE;
+ widget->track.cursor = NULL;
+ widget->track.dx = 0;
+ }
+
+ return ret;
+}
+
+void eve_strw_putc(void *w, int c) {
+ EVEStrWidget *widget = (EVEStrWidget *)w;
+ EVEWidget *_widget = &widget->w;
+ EVEStrCursor *cursor1 = &widget->cursor1;
+ EVEStrCursor *cursor2 = &widget->cursor2;
+ utf8_t *str;
+ utf8_t *clipb = NULL;
+ int w0 = widget->font->w;
+ int w1 = _widget->g.w - widget->font->w;
+ int ins_c = 0, del_c = 0;
+ int ins_w = 0, del_w = 0;
+
+
+ if (c == EVE_PAGE_KBDCH_CLOSE) {
+ if (cursor1->on) eve_strw_cursor_clear(widget, cursor1);
+ if (cursor2->on) eve_strw_cursor_clear(widget, cursor2);
+ return;
+ }
+
+ if (!cursor1->on) return;
+
+ if (!cursor2->on && ((c == CH_BS) || (c == CH_DEL))) {
+ utf32_t uc;
+
+ str = widget->str + cursor1->ch;
+ switch (c) {
+ case CH_BS:
+ if (cursor1->ch > 0) {
+ del_c = -utf8_seek(str, -1, &uc);
+ del_w = eve_font_ch_w(widget->font, uc);
+ memmove(str - del_c, str, widget->str_len - cursor1->ch + 1);
+ widget->str_len -= del_c;
+ widget->str_g.w -= del_w;
+ cursor1->ch -= del_c;
+ cursor1->x -= del_w;
+ }
+ break;
+
+ case CH_DEL:
+ if (cursor1->ch < widget->str_len) {
+ del_c = utf8_dec(str, &uc);
+ del_w = eve_font_ch_w(widget->font, uc);
+ memmove(str, str + del_c, widget->str_len - cursor1->ch - del_c + 1);
+ widget->str_len -= del_c;
+ widget->str_g.w -= del_w;
+ }
+ break;
+ }
+ if (widget->str_g.w - widget->str_g.x < w1) {
+ widget->str_g.x -= del_w;
+ if (widget->str_g.x < 0) widget->str_g.x = 0;
+ }
+ } else {
+ EVEStrCursor *c1 = cursor1;
+ EVEStrCursor *c2 = cursor1;
+ utf8_t utf8_buf[4];
+
+ if (cursor2->on) {
+ if (cursor1->ch <= cursor2->ch) {
+ c2 = cursor2;
+ } else {
+ c1 = cursor2;
+ }
+ del_c = c2->ch - c1->ch;
+ del_w = eve_font_buf_w(widget->font, str, del_c);
+ if ((c == CH_CTRLX) || (c == CH_CTRLC)) {
+ eve_clipb_push(str, del_c);
+ if (c == CH_CTRLC) return;
+ }
+ }
+
+ str = widget->str + c1->ch;
+ if (CHAR_VALID_INPUT(c)) {
+ ins_c = utf8_enc(c, utf8_buf);
+ ins_w = eve_font_ch_w(widget->font, c);
+ } else if (c == CH_CTRLV) {
+ int rv, clipb_len = 0;
+
+ clipb = eve_clipb_get();
+ if (clipb) {
+ rv = utf8_verify(clipb, EVE_CLIPB_SIZE_BUF, &clipb_len);
+ if (rv != UTF_OK) {
+ clipb = NULL;
+ clipb_len = 0;
+ }
+ }
+ ins_c = clipb_len;
+ ins_w = eve_font_str_w(widget->font, clipb);
+ }
+ if (widget->str_len + ins_c >= widget->str_size + del_c) {
+ ins_c = 0;
+ ins_w = 0;
+ }
+ if (ins_c != del_c) memmove(str + ins_c, str + del_c, widget->str_len - c2->ch + 1);
+ if (ins_c) {
+ if (c == CH_CTRLV) {
+ memcpy(str, clipb, ins_c);
+ } else if (ins_c > 1) {
+ memcpy(str, utf8_buf, ins_c);
+ } else {
+ *str = utf8_buf[0];
+ }
+ c1->ch += ins_c;
+ c1->x += ins_w;
+ }
+ widget->str_len += ins_c - del_c;
+ widget->str_g.w += ins_w - del_w;
+ if (c1 == cursor2) widget->cursor1 = widget->cursor2;
+ if (cursor2->on) eve_strw_cursor_clear(widget, cursor2);
+ }
+
+ if (cursor1->x - widget->str_g.x < w0) widget->str_g.x = cursor1->x > w0 ? cursor1->x - w0 : 0;
+ if (cursor1->x - widget->str_g.x > w1) widget->str_g.x = cursor1->x - w1;
+}
+
+void eve_strw_cursor_set(EVEStrWidget *widget, EVEStrCursor *cursor, int16_t x) {
+ int i;
+ int16_t _x, _d;
+ utf32_t ch;
+ uint8_t ch_w;
+ uint8_t ch_l;
+ EVEWidget *_widget = &widget->w;
+
+ x = x - _widget->g.x + widget->str_g.x;
+
+ _x = 0;
+ _d = x;
+ i = 0;
+ while (i < widget->str_len) {
+ ch_l = utf8_dec(widget->str + i, &ch);
+ ch_w = eve_font_ch_w(widget->font, ch);
+ _x += ch_w;
+ i += ch_l;
+ if (_x >= x) {
+ if (_x - x > _d) {
+ _x -= ch_w;
+ i -= ch_l;
+ }
+ break;
+ } else {
+ _d = x - _x;
+ }
+ }
+ cursor->x = _x;
+ cursor->ch = i;
+ cursor->on = 1;
+}
+
+void eve_strw_cursor_clear(EVEStrWidget *widget, EVEStrCursor *cursor) {
+ cursor->on = 0;
+}
diff --git a/fw/fe310/eos/eve/widget/strw.h b/fw/fe310/eos/eve/widget/strw.h
new file mode 100644
index 0000000..44e5e2d
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/strw.h
@@ -0,0 +1,43 @@
+#include <stdint.h>
+
+typedef struct EVEStrCursor {
+ char on;
+ uint16_t x;
+ uint16_t ch;
+} EVEStrCursor;
+
+typedef struct EVEStrWidget {
+ EVEWidget w;
+ EVEFont *font;
+ utf8_t *str;
+ uint16_t str_size;
+ uint16_t str_len;
+ struct {
+ int16_t x;
+ int16_t x0;
+ uint16_t w;
+ } str_g;
+ EVEStrCursor cursor1;
+ EVEStrCursor cursor2;
+ struct {
+ EVEStrCursor *cursor;
+ short dx;
+ char mode;
+ } track;
+} EVEStrWidget;
+
+typedef struct EVEStrSpec {
+ EVEFont *font;
+ uint16_t str_size;
+} EVEStrSpec;
+
+int eve_strw_create(EVEStrWidget *widget, EVERect *g, EVEPage *page, EVEStrSpec *spec);
+void eve_strw_init(EVEStrWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *str, uint16_t str_size);
+void eve_strw_destroy(EVEStrWidget *widget);
+int eve_strw_update(EVEStrWidget *widget);
+
+uint8_t eve_strw_draw(EVEWidget *_widget, uint8_t tag0);
+int eve_strw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt);
+void eve_strw_putc(void *_page, int c);
+void eve_strw_cursor_set(EVEStrWidget *widget, EVEStrCursor *cursor, int16_t x);
+void eve_strw_cursor_clear(EVEStrWidget *widget, EVEStrCursor *cursor); \ No newline at end of file
diff --git a/fw/fe310/eos/eve/widget/textw.c b/fw/fe310/eos/eve/widget/textw.c
new file mode 100644
index 0000000..e994c0e
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/textw.c
@@ -0,0 +1,545 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "clipb.h"
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "label.h"
+#include "widget.h"
+#include "textw.h"
+
+#define TEXTW_TOUCH_OPT EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_XY | EVE_TOUCH_OPT_TRACK_EXT_XY | EVE_TOUCH_OPT_LPRESS
+
+#define CH_BS 0x08
+#define CH_DEL 0x7f
+
+#define CH_CTRLX 0x18
+#define CH_CTRLC 0x03
+#define CH_CTRLV 0x16
+
+#define LINE_LEN(w,l) ((l) ? (w->line[l] ? w->line[l] - w->line[(l) - 1] - 1 : 0) : w->line[l])
+#define LINE_START(w,l) ((l) ? w->line[(l) - 1] + 1 : 0)
+#define LINE_END(w,l) (w->line[l])
+#define LINE_EMPTY 0xffff
+
+#define CHAR_VALID_INPUT(c) (((c >= 0x20) && (c < 0x7f)) || (c == '\n'))
+
+#define DIVC(x,y) ((x) / (y) + ((x) % (y) != 0))
+
+int eve_textw_create(EVETextWidget *widget, EVERect *g, EVEPage *page, EVETextSpec *spec) {
+ EVEFont *font = spec->font ? spec->font : eve_window_font(page->v.window);
+ utf8_t *text;
+ uint16_t *line;
+
+ text = eve_malloc(spec->text_size);
+ if (text == NULL) {
+ return EVE_ERR_NOMEM;
+ }
+ text[0] = '\0';
+ line = eve_malloc(sizeof(uint16_t) * spec->line_size);
+ if (line == NULL) {
+ free(text);
+ return EVE_ERR_NOMEM;
+ }
+
+ eve_textw_init(widget, g, page, font, text, spec->text_size, line, spec->line_size);
+
+ return EVE_OK;
+}
+
+void eve_textw_init(EVETextWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *text, uint16_t text_size, uint16_t *line, uint16_t line_size) {
+ EVEWidget *_widget = &widget->w;
+ int rv, text_len;
+
+ memset(widget, 0, sizeof(EVETextWidget));
+ eve_widget_init(_widget, EVE_WIDGET_TYPE_TEXT, g, page, eve_textw_draw, eve_textw_touch, eve_textw_putc);
+ widget->font = font;
+ rv = utf8_verify(text, text_size, &text_len);
+ if (rv != UTF_OK) {
+ if (text_len >= text_size) text_len = 0;
+ text[text_len] = '\0';
+ }
+ widget->text = text;
+ widget->text_size = text_size;
+ widget->text_len = text_len;
+ widget->line = line;
+ widget->line_size = line_size;
+ memset(widget->line, 0xff, line_size * sizeof(uint16_t));
+ eve_textw_text_update(widget, 0, 0);
+}
+
+void eve_textw_destroy(EVETextWidget *widget) {
+ eve_free(widget->line);
+ eve_free(widget->text);
+}
+
+static void set_focus(EVETextWidget *widget, EVETextCursor *cursor) {
+ EVEWidget *_widget = &widget->w;
+ EVERect focus;
+
+ focus.x = _widget->g.x;
+ focus.y = _widget->g.y + cursor->line * widget->font->h;
+ focus.w = _widget->g.w;
+ focus.h = 2 * widget->font->h;
+ eve_widget_focus(_widget, &focus);
+}
+
+static EVETextCursor *cursor_prox(EVETextWidget *widget, EVETextCursor *cursor, EVETouch *touch, short *dx, short *dl) {
+ EVEWidget *_widget = &widget->w;
+ EVEPage *page = _widget->page;
+ int x = eve_page_x(page, touch->x0) - _widget->g.x;
+ int l = (int)touch->tag0 - _widget->tag0 + widget->line0;
+ int _dx, _dl;
+
+ *dx = cursor->x - x;
+ *dl = cursor->line - l;
+
+ _dx = *dx < 0 ? -(*dx) : *dx;
+ _dl = *dl < 0 ? -(*dl) : *dl;
+
+ if ((_dx <= widget->font->h) && (_dl <= 1)) return cursor;
+ return NULL;
+}
+
+static void draw_line(EVETextWidget *widget, uint16_t l, uint16_t ch, uint16_t len, uint16_t x1, uint16_t x2, char s) {
+ EVEWidget *_widget = &widget->w;
+ EVEPage *page = _widget->page;
+
+ if (x1 != x2) {
+ eve_cmd_dl(BEGIN(EVE_RECTS));
+ if (!s) eve_cmd_dl(COLOR_MASK(0 ,0 ,0 ,0));
+ eve_cmd_dl(VERTEX2F(_widget->g.x + x1, _widget->g.y + l * widget->font->h));
+ eve_cmd_dl(VERTEX2F(_widget->g.x + x2, _widget->g.y + (l + 1) * widget->font->h));
+ if (!s) {
+ eve_cmd_dl(COLOR_MASK(1 ,1 ,1 ,1));
+ eve_cmd_dl(BEGIN(EVE_LINES));
+ eve_cmd_dl(VERTEX2F(_widget->g.x + x1, _widget->g.y + (l + 1) * widget->font->h));
+ eve_cmd_dl(VERTEX2F(_widget->g.x + x2, _widget->g.y + (l + 1) * widget->font->h));
+ }
+ eve_cmd_dl(END());
+ if (len) {
+ if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_bg));
+ eve_cmd(CMD_TEXT, "hhhhpb", _widget->g.x + x1, _widget->g.y + l * widget->font->h, widget->font->id, 0, widget->text + ch, len, 0);
+ if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_fg));
+ }
+ }
+}
+
+static void draw_cursor(EVETextWidget *widget, EVETextCursor *cursor) {
+ uint16_t x, y;
+ EVEWidget *_widget = &widget->w;
+
+ x = _widget->g.x + cursor->x;
+ y = _widget->g.y + cursor->line * widget->font->h;
+ eve_cmd_dl(BEGIN(EVE_LINES));
+ eve_cmd_dl(VERTEX2F(x, y));
+ eve_cmd_dl(VERTEX2F(x, y + widget->font->h));
+ eve_cmd_dl(END());
+}
+
+uint8_t eve_textw_draw(EVEWidget *_widget, uint8_t tag0) {
+ EVEPage *page = _widget->page;
+ EVETextWidget *widget = (EVETextWidget *)_widget;
+ int line0, lineN;
+ int _line0, _lineN;
+ char lineNvisible;
+
+ _line0 = line0 = -((int)eve_page_win_y(page, _widget->g.y)) / widget->font->h;
+ _lineN = lineN = DIVC((-((int)eve_page_win_y(page, _widget->g.y)) + page->v.window->g.h), widget->font->h);
+ if (line0 < 0) line0 = 0;
+ if (lineN < 0) lineN = 0;
+ if (line0 > widget->line_len) line0 = widget->line_len;
+ if (lineN > widget->line_len) lineN = widget->line_len;
+ lineNvisible = (lineN >= _line0) && (lineN < _lineN);
+
+ if (lineNvisible || (line0 < lineN)) {
+ int i;
+ char s = 0;
+ EVETextCursor *c1, *c2;
+
+ widget->line0 = line0;
+ _widget->tag0 = tag0;
+ _widget->tagN = tag0;
+
+ if (widget->cursor2.on) {
+ if (widget->cursor1.ch <= widget->cursor2.ch) {
+ c1 = &widget->cursor1;
+ c2 = &widget->cursor2;
+ } else {
+ c1 = &widget->cursor2;
+ c2 = &widget->cursor1;
+ }
+ } else {
+ c1 = NULL;
+ c2 = NULL;
+ }
+
+ for (i=line0; i<lineN; i++) {
+ if (_widget->tagN != EVE_NOTAG) {
+ eve_cmd_dl(TAG(_widget->tagN));
+ eve_touch_set_opt(_widget->tagN, TEXTW_TOUCH_OPT);
+ _widget->tagN++;
+ }
+ if (!s && c1 && (c1->line == i)) {
+ int l1, l2, l3;
+
+ l1 = c1->ch - LINE_START(widget, i);
+ if (c2->line == i) {
+ l2 = c2->ch - c1->ch;
+ l3 = LINE_START(widget, i) + LINE_LEN(widget, i) - c2->ch;
+ } else {
+ l2 = LINE_START(widget, i) + LINE_LEN(widget, i) - c1->ch;
+ l3 = 0;
+ s = 1;
+ }
+ draw_line(widget, i, LINE_START(widget, i), l1, 0, c1->x, 0);
+ draw_line(widget, i, c1->ch, l2, c1->x, s ? _widget->g.w : c2->x, 1);
+ if (!s) {
+ draw_line(widget, i, c2->ch, l3, c2->x, _widget->g.w, 0);
+ c1 = NULL;
+ c2 = NULL;
+ }
+ } else if (s && (c2->line == i)) {
+ int l1 = c2->ch - LINE_START(widget, i);
+ int l2 = LINE_START(widget, i) + LINE_LEN(widget, i) - c2->ch;
+
+ draw_line(widget, i, LINE_START(widget, i), l1, 0, c2->x, 1);
+ draw_line(widget, i, c2->ch, l2, c2->x, _widget->g.w, 0);
+ c1 = NULL;
+ c2 = NULL;
+ s = 0;
+ } else {
+ if (widget->cursor1.on && (widget->cursor1.line == i)) draw_cursor(widget, &widget->cursor1);
+ draw_line(widget, i, LINE_START(widget, i), LINE_LEN(widget, i), 0, _widget->g.w, s);
+ }
+ }
+ if (lineNvisible) {
+ if (_widget->tagN != EVE_NOTAG) {
+ eve_cmd_dl(TAG(_widget->tagN));
+ eve_touch_set_opt(_widget->tagN, TEXTW_TOUCH_OPT);
+ _widget->tagN++;
+ }
+ draw_line(widget, lineN, 0, 0, 0, _widget->g.w, 0);
+ }
+ } else {
+ widget->line0 = 0;
+ _widget->tag0 = EVE_NOTAG;
+ _widget->tagN = EVE_NOTAG;
+ }
+
+ return _widget->tagN;
+}
+
+int eve_textw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) {
+ EVEPage *page = _widget->page;
+ EVETextWidget *widget = (EVETextWidget *)_widget;
+ EVETextCursor *t_cursor = NULL;
+ short dx, dl;
+ int ret = 0;
+
+ if (evt & (EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TRACK_START)) {
+ if (widget->cursor2.on) {
+ t_cursor = cursor_prox(widget, &widget->cursor2, touch, &dx, &dl);
+ }
+ if ((t_cursor == NULL) && widget->cursor1.on) {
+ t_cursor = cursor_prox(widget, &widget->cursor1, touch, &dx, &dl);
+ }
+ if (t_cursor && (evt & EVE_TOUCH_ETYPE_TRACK_START)) {
+ widget->track.cursor = t_cursor;
+ widget->track.dx = dx;
+ widget->track.dl = dl;
+ }
+ }
+
+ if (widget->track.cursor) {
+ if (evt & EVE_TOUCH_ETYPE_TRACK) eve_textw_cursor_set(widget, widget->track.cursor, touch->tag + widget->track.dl, eve_page_x(page, touch->x) + widget->track.dx);
+ ret = 1;
+ } else {
+ if (evt & EVE_TOUCH_ETYPE_LPRESS) {
+ if (widget->cursor2.on) {
+ // copy
+ } else if (widget->cursor1.on) {
+ if (t_cursor && (dl == 0)) {
+ // paste
+ } else {
+ eve_textw_cursor_set(widget, &widget->cursor2, touch->tag, eve_page_x(page, touch->x));
+ }
+ } else {
+ // select
+ }
+ ret = 1;
+ }
+ if ((evt & EVE_TOUCH_ETYPE_POINT_UP) && !(touch->eevt & (EVE_TOUCH_EETYPE_TRACK_XY | EVE_TOUCH_EETYPE_ABORT | EVE_TOUCH_EETYPE_LPRESS))) {
+ eve_textw_cursor_set(widget, &widget->cursor1, touch->tag_up, eve_page_x(page, touch->x0));
+ if (widget->cursor2.on) eve_textw_cursor_clear(widget, &widget->cursor2);
+ set_focus(widget, &widget->cursor1);
+ ret = 1;
+ }
+ }
+
+ if (evt & EVE_TOUCH_ETYPE_TRACK_STOP) {
+ widget->track.cursor = NULL;
+ widget->track.dx = 0;
+ widget->track.dl = 0;
+ }
+
+ return ret;
+}
+
+void eve_textw_putc(void *w, int c) {
+ EVETextWidget *widget = (EVETextWidget *)w;
+ EVEWidget *_widget = &widget->w;
+ EVETextCursor *cursor1 = &widget->cursor1;
+ EVETextCursor *cursor2 = &widget->cursor2;
+ utf8_t *text;
+ utf8_t *clipb = NULL;
+ int i, r;
+ int ins_c = 0, del_c = 0;
+ int ch_w = 0;
+
+ if (c == EVE_PAGE_KBDCH_CLOSE) {
+ if (cursor1->on) eve_textw_cursor_clear(widget, cursor1);
+ if (cursor2->on) eve_textw_cursor_clear(widget, cursor2);
+ return;
+ }
+
+ if (!cursor1->on) return;
+
+ if (!cursor2->on && ((c == CH_BS) || (c == CH_DEL))) {
+ utf32_t uc;
+
+ text = widget->text + cursor1->ch;
+ switch (c) {
+ case CH_BS:
+ if (cursor1->ch > 0) {
+ del_c = -utf8_seek(text, -1, &uc);
+ ch_w = eve_font_ch_w(widget->font, uc);
+ memmove(text - del_c, text, widget->text_len - cursor1->ch + 1);
+ cursor1->ch -= del_c;
+ }
+ break;
+
+ case CH_DEL:
+ if (cursor1->ch < widget->text_len) {
+ del_c = utf8_dec(text, &uc);
+ ch_w = eve_font_ch_w(widget->font, uc);
+ memmove(text, text + del_c, widget->text_len - cursor1->ch - del_c + 1);
+ }
+ break;
+ }
+ } else {
+ EVETextCursor *c1 = cursor1;
+ EVETextCursor *c2 = cursor1;
+ utf8_t utf8_buf[4];
+
+ if (cursor2->on) {
+ if (cursor1->ch <= cursor2->ch) {
+ c2 = cursor2;
+ } else {
+ c1 = cursor2;
+ }
+ del_c = c2->ch - c1->ch;
+ if ((c == CH_CTRLX) || (c == CH_CTRLC)) {
+ eve_clipb_push(text, del_c);
+ if (c == CH_CTRLC) return;
+ }
+ }
+
+ text = widget->text + c1->ch;
+ if (CHAR_VALID_INPUT(c)) {
+ ins_c = utf8_enc(c, utf8_buf);
+ ch_w = eve_font_ch_w(widget->font, c);
+ } else if (c == CH_CTRLV) {
+ int rv, clipb_len = 0;
+
+ clipb = eve_clipb_get();
+ if (clipb) {
+ rv = utf8_verify(clipb, EVE_CLIPB_SIZE_BUF, &clipb_len);
+ if (rv != UTF_OK) {
+ clipb = NULL;
+ clipb_len = 0;
+ }
+ }
+ ins_c = clipb_len;
+ ch_w = eve_font_str_w(widget->font, clipb);
+ }
+ if (widget->text_len + ins_c >= widget->text_size + del_c) {
+ ins_c = 0;
+ ch_w = 0;
+ }
+ if (ins_c != del_c) memmove(text + ins_c, text + del_c, widget->text_len - c2->ch + 1);
+ if (ins_c) {
+ if (c == CH_CTRLV) {
+ memcpy(text, clipb, ins_c);
+ } else if (ins_c > 1) {
+ memcpy(text, utf8_buf, ins_c);
+ } else {
+ *text = utf8_buf[0];
+ }
+ c1->ch += ins_c;
+ }
+ if (c1 == cursor2) widget->cursor1 = widget->cursor2;
+ if (cursor2->on) eve_textw_cursor_clear(widget, cursor2);
+ }
+
+ if ((ins_c == 0) && (del_c == 0)) return;
+
+ widget->text_len += ins_c - del_c;
+ for (i=cursor1->line; i<widget->line_len; i++) {
+ widget->line[i] += ins_c - del_c;
+ }
+
+ r = cursor1->line;
+ if (cursor1->line) r = eve_textw_text_update(widget, cursor1->line - 1, 1);
+ if ((cursor1->line == 0) || (r == cursor1->line - 1)) r = eve_textw_text_update(widget, cursor1->line, 1);
+
+ if (cursor1->line && (cursor1->ch < LINE_START(widget, cursor1->line))) {
+ cursor1->line--;
+ eve_textw_cursor_update(widget, cursor1);
+ set_focus(widget, cursor1);
+ } else if (cursor1->ch > LINE_END(widget, cursor1->line)) {
+ while (cursor1->ch > LINE_END(widget, cursor1->line)) cursor1->line++;
+ eve_textw_cursor_update(widget, cursor1);
+ set_focus(widget, cursor1);
+ } else {
+ cursor1->x += ch_w;
+ }
+}
+
+uint16_t eve_textw_text_update(EVETextWidget *widget, uint16_t line, int uievt) {
+ int i;
+ utf32_t ch;
+ uint8_t ch_w;
+ uint8_t ch_l;
+ uint16_t word_w, line_w, line_b;
+ uint16_t new_h;
+ EVEWidget *_widget = &widget->w;
+ EVEPage *page = _widget->page;
+
+ word_w = 0;
+ line_w = 0;
+ line_b = LINE_EMPTY;
+
+ i = LINE_START(widget, line);
+ while (i < widget->text_size) {
+ ch_l = utf8_dec(widget->text + i, &ch);
+ if (!CHAR_VALID_INPUT(ch) && ch) {
+ ch = 0;
+ widget->text[i] = '\0';
+ widget->text_len = i;
+ widget->line[line] = LINE_EMPTY;
+ }
+
+ ch_w = eve_font_ch_w(widget->font, ch);
+ if (ch <= 0x20) {
+ if ((ch == '\n') || (ch == '\0')) {
+ if (widget->line[line] == i) return line;
+ widget->line[line] = i;
+ line++;
+ if ((ch == '\0') || (line == widget->line_size)) break;
+ word_w = 0;
+ line_w = 0;
+ line_b = LINE_EMPTY;
+ } else if (ch == ' ') {
+ word_w = 0;
+ line_w += ch_w;
+ line_b = i;
+ }
+ } else {
+ word_w += ch_w;
+ line_w += ch_w;
+ }
+ if ((line_w > _widget->g.w) && (line_b != LINE_EMPTY)) {
+ if (widget->line[line] == line_b) return line;
+ widget->line[line] = line_b;
+ line++;
+ if (line == widget->line_size) {
+ i = line_b;
+ break;
+ }
+ line_w = word_w;
+ line_b = LINE_EMPTY;
+ }
+ i += ch_l;
+ }
+
+ for (i=line; i<widget->line_size; i++) {
+ widget->line[i] = LINE_EMPTY;
+ }
+
+ if (uievt && (widget->line_len != line)) {
+ eve_view_uievt_push(&page->v, EVE_UIEVT_WIDGET_UPDATE_G, _widget);
+ }
+ widget->line_len = line;
+ _widget->g.h = (widget->line_len + 1) * widget->font->h;
+
+ return line;
+}
+
+void eve_textw_cursor_update(EVETextWidget *widget, EVETextCursor *cursor) {
+ int i = LINE_START(widget, cursor->line);
+ uint16_t x = 0;
+ utf32_t ch;
+ uint8_t ch_l;
+ EVEWidget *_widget = &widget->w;
+
+ while ((i < cursor->ch) && (i < LINE_END(widget, cursor->line))) {
+ ch_l = utf8_dec(widget->text + i, &ch);
+ x += eve_font_ch_w(widget->font, ch);
+ i += ch_l;
+ }
+ cursor->x = x;
+}
+
+void eve_textw_cursor_set(EVETextWidget *widget, EVETextCursor *cursor, uint8_t tag, int16_t x) {
+ int i;
+ int16_t _x, _d;
+ uint16_t c_line = LINE_EMPTY;
+ utf32_t ch;
+ uint8_t ch_w;
+ uint8_t ch_l;
+ EVEWidget *_widget = &widget->w;
+
+ if ((tag >= _widget->tag0) && ((_widget->tagN == EVE_NOTAG) || (tag < _widget->tagN))) c_line = tag - _widget->tag0 + widget->line0;
+ if (c_line < widget->line_len) {
+ cursor->line = c_line;
+ } else if (c_line == widget->line_len) {
+ cursor->line = c_line - 1;
+ } else if (!cursor->on) {
+ return;
+ }
+
+ x -= _widget->g.x;
+
+ _x = 0;
+ _d = x;
+ i = LINE_START(widget, cursor->line);
+ while (i < LINE_END(widget, cursor->line)) {
+ ch_l = utf8_dec(widget->text + i, &ch);
+ ch_w = eve_font_ch_w(widget->font, ch);
+ _x += ch_w;
+ i += ch_l;
+ if (_x >= x) {
+ if (_x - x > _d) {
+ _x -= ch_w;
+ i -= ch_l;
+ }
+ break;
+ } else {
+ _d = x - _x;
+ }
+ }
+ cursor->x = _x;
+ cursor->ch = i;
+ cursor->on = 1;
+}
+
+void eve_textw_cursor_clear(EVETextWidget *widget, EVETextCursor *cursor) {
+ cursor->on = 0;
+}
diff --git a/fw/fe310/eos/eve/widget/textw.h b/fw/fe310/eos/eve/widget/textw.h
new file mode 100644
index 0000000..570bba8
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/textw.h
@@ -0,0 +1,46 @@
+#include <stdint.h>
+
+typedef struct EVETextCursor {
+ char on;
+ uint16_t x;
+ uint16_t line;
+ uint16_t ch;
+} EVETextCursor;
+
+typedef struct EVETextWidget {
+ EVEWidget w;
+ EVEFont *font;
+ utf8_t *text;
+ uint16_t text_size;
+ uint16_t text_len;
+ uint16_t *line;
+ uint16_t line_size;
+ uint16_t line_len;
+ EVETextCursor cursor1;
+ EVETextCursor cursor2;
+ uint16_t line0;
+ struct {
+ EVETextCursor *cursor;
+ short dx;
+ short dl;
+ } track;
+} EVETextWidget;
+
+typedef struct EVETextSpec {
+ EVEFont *font;
+ uint16_t text_size;
+ uint16_t line_size;
+} EVETextSpec;
+
+int eve_textw_create(EVETextWidget *widget, EVERect *g, EVEPage *page, EVETextSpec *spec);
+void eve_textw_init(EVETextWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *text, uint16_t text_size, uint16_t *line, uint16_t line_size);
+void eve_textw_destroy(EVETextWidget *widget);
+
+uint8_t eve_textw_draw(EVEWidget *_widget, uint8_t tag0);
+int eve_textw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt);
+void eve_textw_putc(void *_w, int c);
+
+uint16_t eve_textw_text_update(EVETextWidget *widget, uint16_t line, int uievt);
+void eve_textw_cursor_update(EVETextWidget *widget, EVETextCursor *cursor);
+void eve_textw_cursor_set(EVETextWidget *widget, EVETextCursor *cursor, uint8_t tag, int16_t x);
+void eve_textw_cursor_clear(EVETextWidget *widget, EVETextCursor *cursor);
diff --git a/fw/fe310/eos/eve/widget/widget.c b/fw/fe310/eos/eve/widget/widget.c
new file mode 100644
index 0000000..96d2ddf
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/widget.c
@@ -0,0 +1,77 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "eve.h"
+#include "eve_kbd.h"
+#include "eve_font.h"
+
+#include "screen/window.h"
+#include "screen/page.h"
+
+#include "widgets.h"
+
+static const size_t _widget_size[] = {
+ 0,
+ sizeof(EVEFreeWidget),
+ sizeof(EVESpacerWidget),
+ sizeof(EVEPageWidget),
+ sizeof(EVEStrWidget),
+ sizeof(EVETextWidget),
+ sizeof(EVESelectWidget),
+};
+
+static const eve_widget_create_t _widget_create[] = {
+ NULL,
+ (eve_widget_create_t)eve_freew_create,
+ (eve_widget_create_t)eve_spacerw_create,
+ (eve_widget_create_t)eve_pagew_create,
+ (eve_widget_create_t)eve_strw_create,
+ (eve_widget_create_t)eve_textw_create,
+ (eve_widget_create_t)eve_selectw_create,
+};
+
+static const eve_widget_destroy_t _widget_destroy[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ (eve_widget_destroy_t)eve_strw_destroy,
+ (eve_widget_destroy_t)eve_textw_destroy,
+ (eve_widget_destroy_t)eve_selectw_destroy,
+};
+
+void eve_widget_init(EVEWidget *widget, uint8_t type, EVERect *g, EVEPage *page, eve_widget_draw_t draw, eve_widget_touch_t touch, eve_kbd_input_handler_t putc) {
+ if (g) widget->g = *g;
+ widget->page = page;
+ widget->draw = draw;
+ widget->touch = touch;
+ widget->putc = putc;
+ widget->type = type;
+}
+
+size_t eve_widget_size(uint8_t type) {
+ return _widget_size[type];
+}
+
+void eve_widget_set_label(EVEWidget *widget, EVELabel *label) {
+ widget->label = label;
+}
+
+EVEWidget *eve_widget_next(EVEWidget *widget) {
+ char *_w = (char *)widget;
+ return (EVEWidget *)(_w + _widget_size[widget->type]);
+}
+
+int eve_widget_create(EVEWidget *widget, uint8_t type, EVERect *g, EVEPage *page, EVEWidgetSpecT *spec) {
+ return _widget_create[type](widget, g, page, spec);
+}
+
+void eve_widget_destroy(EVEWidget *widget) {
+ if (_widget_destroy[widget->type]) _widget_destroy[widget->type](widget);
+}
+
+void eve_widget_focus(EVEWidget *widget, EVERect *rect) {
+ EVEPage *page = widget->page;
+
+ eve_page_focus_widget(page, widget, rect);
+}
diff --git a/fw/fe310/eos/eve/widget/widget.h b/fw/fe310/eos/eve/widget/widget.h
new file mode 100644
index 0000000..f081b4c
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/widget.h
@@ -0,0 +1,31 @@
+#include <stdint.h>
+
+#define EVE_WIDGET_TYPE_FREE 1
+#define EVE_WIDGET_TYPE_SPACER 2
+#define EVE_WIDGET_TYPE_PAGE 3
+#define EVE_WIDGET_TYPE_STR 4
+#define EVE_WIDGET_TYPE_TEXT 5
+#define EVE_WIDGET_TYPE_SELECT 6
+
+struct EVEWidget;
+
+typedef uint8_t (*eve_widget_draw_t) (struct EVEWidget *, uint8_t);
+typedef int (*eve_widget_touch_t) (struct EVEWidget *, EVETouch *, uint16_t);
+
+typedef struct EVEWidget {
+ EVERect g;
+ EVEPage *page;
+ eve_widget_draw_t draw;
+ eve_widget_touch_t touch;
+ eve_kbd_input_handler_t putc;
+ EVELabel *label;
+ uint8_t type;
+ uint8_t tag0;
+ uint8_t tagN;
+} EVEWidget;
+
+void eve_widget_init(EVEWidget *widget, uint8_t type, EVERect *g, EVEPage *page, eve_widget_draw_t draw, eve_widget_touch_t touch, eve_kbd_input_handler_t putc);
+size_t eve_widget_size(uint8_t type);
+void eve_widget_set_label(EVEWidget *widget, EVELabel *label);
+EVEWidget *eve_widget_next(EVEWidget *widget);
+void eve_widget_focus(EVEWidget *widget, EVERect *rect); \ No newline at end of file
diff --git a/fw/fe310/eos/eve/widget/widgets.h b/fw/fe310/eos/eve/widget/widgets.h
new file mode 100644
index 0000000..2f8d3ca
--- /dev/null
+++ b/fw/fe310/eos/eve/widget/widgets.h
@@ -0,0 +1,39 @@
+#include "label.h"
+#include "widget.h"
+
+#include "freew.h"
+#include "spacerw.h"
+#include "pagew.h"
+#include "strw.h"
+#include "textw.h"
+#include "selectw.h"
+
+typedef union EVEWidgetSpecT {
+ EVEFreeSpec free;
+ EVESpacerSpec spacer;
+ EVEPageSpec page;
+ EVEStrSpec str;
+ EVETextSpec text;
+ EVESelectSpec select;
+} EVEWidgetSpecT;
+
+typedef struct EVELabelSpec {
+ EVERect g;
+ EVEFont *font;
+ char *title;
+} APPLabelSpec;
+
+typedef struct EVEWidgetSpec {
+ APPLabelSpec label;
+ struct {
+ EVERect g;
+ EVEWidgetSpecT spec;
+ uint8_t type;
+ } widget;
+} EVEWidgetSpec;
+
+typedef int (*eve_widget_create_t) (EVEWidget *, EVERect *, EVEPage *, EVEWidgetSpecT *);
+typedef void (*eve_widget_destroy_t) (EVEWidget *);
+
+int eve_widget_create(EVEWidget *widget, uint8_t type, EVERect *g, EVEPage *page, EVEWidgetSpecT *spec);
+void eve_widget_destroy(EVEWidget *widget);
diff --git a/fw/fe310/eos/event.c b/fw/fe310/eos/event.c
new file mode 100644
index 0000000..e0a185f
--- /dev/null
+++ b/fw/fe310/eos/event.c
@@ -0,0 +1,155 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "msgq.h"
+#include "event.h"
+
+EOSMsgQ _eos_event_q;
+static EOSMsgItem event_q_array[EOS_EVT_SIZE_Q];
+
+static eos_evt_handler_t evt_handler[EOS_EVT_MAX_EVT + 1];
+
+static void evtq_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+
+ if (idx && (idx <= EOS_EVT_MAX_EVT)) {
+ evt_handler[idx](type, buffer, len);
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+void eos_evtq_init(void) {
+ int i;
+
+ evt_handler[0] = evtq_handler;
+ for (i=0; i<EOS_EVT_MAX_EVT; i++) {
+ evt_handler[i + 1] = eos_evtq_bad_handler;
+ }
+ eos_msgq_init(&_eos_event_q, event_q_array, EOS_EVT_SIZE_Q);
+}
+
+int eos_evtq_push(unsigned char type, unsigned char *buffer, uint16_t len) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ int ret = eos_msgq_push(&_eos_event_q, type, buffer, len);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+int eos_evtq_push_isr(unsigned char type, unsigned char *buffer, uint16_t len) {
+ return eos_msgq_push(&_eos_event_q, type, buffer, len);
+}
+
+void eos_evtq_pop(unsigned char *type, unsigned char **buffer, uint16_t *len) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ eos_msgq_pop(&_eos_event_q, type, buffer, len);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_evtq_pop_isr(unsigned char *type, unsigned char **buffer, uint16_t *len) {
+ eos_msgq_pop(&_eos_event_q, type, buffer, len);
+}
+
+int eos_evtq_get(unsigned char type, unsigned char **buffer, uint16_t *len) {
+ int rv = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ rv = eos_msgq_find(&_eos_event_q, type, NULL, 0, buffer, len);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+int eos_evtq_find(unsigned char type, unsigned char *selector, uint16_t sel_len, unsigned char **buffer, uint16_t *len) {
+ int rv = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ rv = eos_msgq_find(&_eos_event_q, type, selector, sel_len, buffer, len);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_evtq_wait(unsigned char type, unsigned char *selector, uint16_t sel_len, unsigned char **buffer, uint16_t *len) {
+ int rv = 0;
+
+ while(!rv) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ rv = eos_msgq_find(&_eos_event_q, type, selector, sel_len, buffer, len);
+ if (!rv) {
+ unsigned char _type;
+ unsigned char *_buffer;
+ uint16_t _len;
+
+ eos_msgq_pop(&_eos_event_q, &_type, &_buffer, &_len);
+ if (_type) {
+ set_csr(mstatus, MSTATUS_MIE);
+ evt_handler[0](_type, _buffer, _len);
+ } else {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ } else {
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ }
+}
+
+void eos_evtq_flush(void) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ eos_evtq_flush_isr();
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_evtq_flush_isr(void) {
+ unsigned char type;
+ unsigned char *buffer;
+ uint16_t len;
+
+ do {
+ eos_msgq_pop(&_eos_event_q, &type, &buffer, &len);
+ if (type) {
+ set_csr(mstatus, MSTATUS_MIE);
+ evt_handler[0](type, buffer, len);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ } while (type);
+}
+
+void eos_evtq_loop(void) {
+ unsigned char type;
+ unsigned char *buffer;
+ uint16_t len;
+ int foo = 1;
+
+ while(foo) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ eos_msgq_pop(&_eos_event_q, &type, &buffer, &len);
+ if (type) {
+ set_csr(mstatus, MSTATUS_MIE);
+ evt_handler[0](type, buffer, len);
+ } else {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ }
+}
+
+void eos_evtq_bad_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ printf("evt bad handler:0x%x\n", type);
+}
+
+void eos_evtq_set_handler(unsigned char type, eos_evt_handler_t handler) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+
+ if (handler == NULL) handler = eos_evtq_bad_handler;
+ if (idx <= EOS_EVT_MAX_EVT) evt_handler[idx] = handler;
+}
+
+eos_evt_handler_t eos_evtq_get_handler(unsigned char type) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+
+ if (idx <= EOS_EVT_MAX_EVT) return evt_handler[idx];
+ return NULL;
+}
diff --git a/fw/fe310/eos/event.h b/fw/fe310/eos/event.h
new file mode 100644
index 0000000..c9edd03
--- /dev/null
+++ b/fw/fe310/eos/event.h
@@ -0,0 +1,21 @@
+#include <stdint.h>
+
+#include "evt_def.h"
+
+typedef void (*eos_evt_handler_t) (unsigned char, unsigned char *, uint16_t);
+
+void eos_evtq_init(void);
+int eos_evtq_push(unsigned char type, unsigned char *buffer, uint16_t len);
+int eos_evtq_push_isr(unsigned char type, unsigned char *buffer, uint16_t len);
+void eos_evtq_pop(unsigned char *type, unsigned char **buffer, uint16_t *len);
+void eos_evtq_pop_isr(unsigned char *type, unsigned char **buffer, uint16_t *len);
+int eos_evtq_get(unsigned char type, unsigned char **buffer, uint16_t *len);
+int eos_evtq_find(unsigned char type, unsigned char *selector, uint16_t sel_len, unsigned char **buffer, uint16_t *len);
+void eos_evtq_wait(unsigned char type, unsigned char *selector, uint16_t sel_len, unsigned char **buffer, uint16_t *len);
+void eos_evtq_flush(void);
+void eos_evtq_flush_isr(void);
+void eos_evtq_loop(void);
+
+void eos_evtq_bad_handler(unsigned char type, unsigned char *buffer, uint16_t len);
+void eos_evtq_set_handler(unsigned char type, eos_evt_handler_t handler);
+eos_evt_handler_t eos_evtq_get_handler(unsigned char type);
diff --git a/fw/fe310/eos/evt_def.h b/fw/fe310/eos/evt_def.h
new file mode 100644
index 0000000..16c4255
--- /dev/null
+++ b/fw/fe310/eos/evt_def.h
@@ -0,0 +1,12 @@
+#define EOS_EVT_TIMER 0x10
+#define EOS_EVT_I2S 0x20
+#define EOS_EVT_NET 0x30
+#define EOS_EVT_SPI 0x40
+#define EOS_EVT_UART 0x50
+#define EOS_EVT_UI 0x60
+#define EOS_EVT_USER 0x80
+
+#define EOS_EVT_MAX_EVT 8
+#define EOS_EVT_MASK 0xF0
+
+#define EOS_EVT_SIZE_Q 4
diff --git a/fw/fe310/eos/i2c.c b/fw/fe310/eos/i2c.c
new file mode 100644
index 0000000..122a1b2
--- /dev/null
+++ b/fw/fe310/eos/i2c.c
@@ -0,0 +1,99 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+#include "prci_driver.h"
+
+#include "eos.h"
+#include "i2c.h"
+
+void eos_i2c_start(uint32_t baud_rate) {
+ eos_i2c_set_baud_rate(baud_rate);
+ GPIO_REG(GPIO_IOF_SEL) &= ~IOF0_I2C0_MASK;
+ GPIO_REG(GPIO_IOF_EN) |= IOF0_I2C0_MASK;
+}
+
+void eos_i2c_stop(void) {
+ GPIO_REG(GPIO_IOF_EN) &= ~IOF0_I2C0_MASK;
+}
+
+void eos_i2c_set_baud_rate(uint32_t baud_rate) {
+ unsigned long clock_rate = PRCI_get_cpu_freq();
+ uint16_t prescaler = (clock_rate / (baud_rate * 5)) - 1;
+
+ I2C0_REGB(I2C_CONTROL) &= ~I2C_CONTROL_EN;
+ I2C0_REGB(I2C_PRESCALE_LOW) = prescaler & 0xFF;
+ I2C0_REGB(I2C_PRESCALE_HIGH) = (prescaler >> 8) & 0xFF;
+ I2C0_REGB(I2C_CONTROL) |= I2C_CONTROL_EN;
+}
+
+
+static int i2c_read(uint8_t cmd) {
+ I2C0_REGB(I2C_COMMAND) = I2C_CMD_READ | cmd;
+ while (I2C0_REGB(I2C_STATUS) & I2C_STATUS_TIP);
+
+ return I2C0_REGB(I2C_RECEIVE);
+}
+
+static int i2c_write(uint8_t cmd, uint8_t b) {
+ I2C0_REGB(I2C_TRANSMIT) = b;
+ I2C0_REGB(I2C_COMMAND) = I2C_CMD_WRITE | cmd;
+ while (I2C0_REGB(I2C_STATUS) & I2C_STATUS_TIP);
+
+ if (I2C0_REGB(I2C_STATUS) & I2C_STATUS_RXACK) return EOS_ERR;
+ return EOS_OK;
+}
+
+static int i2c_addr(uint8_t addr, uint8_t rw_flag) {
+ return i2c_write(I2C_CMD_START, ((addr & 0x7F) << 1) | (rw_flag & 0x1));
+}
+
+int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *buffer, uint16_t len) {
+ int rv;
+ int i;
+
+ rv = i2c_addr(addr, I2C_WRITE);
+ if (rv) return rv;
+
+ rv = i2c_write(0, reg);
+ if (rv) return rv;
+
+ rv = i2c_addr(addr, I2C_READ);
+ if (rv) return rv;
+
+ for (i=0; i<len; i++) {
+ rv = i2c_read(i == (len - 1) ? (I2C_CMD_ACK | I2C_CMD_STOP) : 0); /* Set NACK to end read, and generate STOP condition */
+ if (rv < 0) return rv;
+
+ buffer[i] = (uint8_t)rv;
+ }
+
+ return EOS_OK;
+}
+
+int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *buffer, uint16_t len) {
+ int rv;
+ int i;
+
+ rv = i2c_addr(addr, I2C_WRITE);
+ if (rv) return rv;
+
+ rv = i2c_write(0, reg);
+ if (rv) return rv;
+
+ for (i=0; i<len; i++) {
+ rv = i2c_write(i == (len - 1) ? I2C_CMD_STOP : 0, buffer[i]);
+ if (rv) return rv;
+ }
+
+ return EOS_OK;
+}
+
+int eos_i2c_read8(uint8_t addr, uint8_t reg, uint8_t *data) {
+ return eos_i2c_read(addr, reg, data, 1);
+}
+
+int eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data) {
+ return eos_i2c_write(addr, reg, &data, 1);
+}
diff --git a/fw/fe310/eos/i2c.h b/fw/fe310/eos/i2c.h
new file mode 100644
index 0000000..9692ea0
--- /dev/null
+++ b/fw/fe310/eos/i2c.h
@@ -0,0 +1,9 @@
+#include <stdint.h>
+
+void eos_i2c_start(uint32_t baud_rate);
+void eos_i2c_stop(void);
+void eos_i2c_set_baud_rate(uint32_t baud_rate);
+int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *buffer, uint16_t len);
+int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *buffer, uint16_t len);
+int eos_i2c_read8(uint8_t addr, uint8_t reg, uint8_t *data);
+int eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/fw/fe310/eos/i2c/Makefile b/fw/fe310/eos/i2c/Makefile
new file mode 100644
index 0000000..a210898
--- /dev/null
+++ b/fw/fe310/eos/i2c/Makefile
@@ -0,0 +1,17 @@
+include ../../common.mk
+
+CFLAGS += -I..
+
+obj = bq25895.o ov2640.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/eos/i2c/bq25895.c b/fw/fe310/eos/i2c/bq25895.c
new file mode 100644
index 0000000..934b5e7
--- /dev/null
+++ b/fw/fe310/eos/i2c/bq25895.c
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "eos.h"
+#include "i2c.h"
+#include "i2c/bq25895.h"
+
+void eos_bq25895_init(void) {
+ uint8_t data = 0;
+ int i, ret = EOS_OK;
+
+ eos_i2c_start(100000);
+ ret = eos_i2c_write8(BQ25895_ADDR, 0x07, 0x8d); // disable watchdog
+ if (ret) printf("I2C ERROR 0x07\n");
+ ret = eos_i2c_write8(BQ25895_ADDR, 0x00, 0x28); // 2.1A input current
+ if (ret) printf("I2C ERROR 0x00\n");
+ ret = eos_i2c_write8(BQ25895_ADDR, 0x03, 0x1e); // sysmin 3.7, disable otg
+ if (ret) printf("I2C ERROR 0x03\n");
+
+ printf("BQ25895:\n");
+ for (i=0; i<0x15; i++) {
+ ret = eos_i2c_read8(BQ25895_ADDR, i, &data);
+ if (!ret) printf("REG%02x: %02x\n", i, data);
+ }
+ eos_i2c_stop();
+}
diff --git a/fw/fe310/eos/i2c/bq25895.h b/fw/fe310/eos/i2c/bq25895.h
new file mode 100644
index 0000000..f61a46f
--- /dev/null
+++ b/fw/fe310/eos/i2c/bq25895.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+#define BQ25895_ADDR 0x6A
+
+void eos_bq25895_init(void);
diff --git a/fw/fe310/eos/i2c/drv2605.h b/fw/fe310/eos/i2c/drv2605.h
new file mode 100644
index 0000000..fe90a9b
--- /dev/null
+++ b/fw/fe310/eos/i2c/drv2605.h
@@ -0,0 +1,3 @@
+#include <stdint.h>
+
+#define DRV2605_ADDR 0x5A
diff --git a/fw/fe310/eos/i2c/lsm9ds1.h b/fw/fe310/eos/i2c/lsm9ds1.h
new file mode 100644
index 0000000..92220e7
--- /dev/null
+++ b/fw/fe310/eos/i2c/lsm9ds1.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+#define LSM9DS1_ADDR_AG 0x1E
+#define LSM9DS1_ADDR_M 0x6B
diff --git a/fw/fe310/eos/i2c/ov2640.c b/fw/fe310/eos/i2c/ov2640.c
new file mode 100644
index 0000000..1a29463
--- /dev/null
+++ b/fw/fe310/eos/i2c/ov2640.c
@@ -0,0 +1,861 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "eos.h"
+#include "timer.h"
+#include "cam.h"
+
+#include "i2c.h"
+#include "ov2640_regs.h"
+#include "ov2640.h"
+
+#define XCLK_FREQ 24000000
+//#define XCLK_FREQ 12000000
+
+#define CIF_WIDTH (400)
+#define CIF_HEIGHT (296)
+
+#define SVGA_WIDTH (800)
+#define SVGA_HEIGHT (600)
+
+#define UXGA_WIDTH (1600)
+#define UXGA_HEIGHT (1200)
+
+static const uint8_t default_regs[][2] = {
+
+// From Linux Driver.
+
+ {BANK_SEL, BANK_SEL_DSP},
+ {0x2c, 0xff},
+ {0x2e, 0xdf},
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {0x3c, 0x32},
+// {CLKRC, CLKRC_DOUBLE | 0x02},
+ {CLKRC, 0x01},
+ {COM2, COM2_OUT_DRIVE_3x},
+ {REG04, REG04_SET(REG04_HFLIP_IMG | REG04_VFLIP_IMG | REG04_VREF_EN | REG04_HREF_EN)},
+ {COM8, COM8_SET(COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN)},
+ {COM9, COM9_AGC_SET(COM9_AGC_GAIN_8x)},
+ {0x2c, 0x0c},
+ {0x33, 0x78},
+ {0x3a, 0x33},
+ {0x3b, 0xfb},
+ {0x3e, 0x00},
+ {0x43, 0x11},
+ {0x16, 0x10},
+ {0x39, 0x02},
+ {0x35, 0x88},
+ {0x22, 0x0a},
+ {0x37, 0x40},
+ {0x23, 0x00},
+ {ARCOM2, 0xa0},
+ {0x06, 0x02},
+ {0x06, 0x88},
+ {0x07, 0xc0},
+ {0x0d, 0xb7},
+ {0x0e, 0x01},
+ {0x4c, 0x00},
+ {0x4a, 0x81},
+ {0x21, 0x99},
+ {AEW, 0x40},
+ {AEB, 0x38},
+ {VV, VV_AGC_TH_SET(0x08, 0x02)},
+ {0x5c, 0x00},
+ {0x63, 0x00},
+ {FLL, 0x22},
+ {COM3, COM3_BAND_SET(COM3_BAND_AUTO)},
+ {REG5D, 0x55},
+ {REG5E, 0x7d},
+ {REG5F, 0x7d},
+ {REG60, 0x55},
+ {HISTO_LOW, 0x70},
+ {HISTO_HIGH, 0x80},
+ {0x7c, 0x05},
+ {0x20, 0x80},
+ {0x28, 0x30},
+ {0x6c, 0x00},
+ {0x6d, 0x80},
+ {0x6e, 0x00},
+ {0x70, 0x02},
+ {0x71, 0x94},
+ {0x73, 0xc1},
+ {0x3d, 0x34},
+ {COM7, COM7_RES_UXGA | COM7_ZOOM_EN},
+ {0x5a, 0x57},
+ {COM25, 0x00},
+ {BD50, 0xbb},
+ {BD60, 0x9c},
+ {BANK_SEL, BANK_SEL_DSP},
+ {0xe5, 0x7f},
+ {MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL},
+ {0x41, 0x24},
+ {RESET, RESET_JPEG | RESET_DVP},
+ {0x76, 0xff},
+ {0x33, 0xa0},
+ {0x42, 0x20},
+ {0x43, 0x18},
+ {0x4c, 0x00},
+ {CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10},
+ {0x88, 0x3f},
+ {0xd7, 0x03},
+ {0xd9, 0x10},
+ {R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x2},
+ {0xc8, 0x08},
+ {0xc9, 0x80},
+ {BPADDR, 0x00},
+ {BPDATA, 0x00},
+ {BPADDR, 0x03},
+ {BPDATA, 0x48},
+ {BPDATA, 0x48},
+ {BPADDR, 0x08},
+ {BPDATA, 0x20},
+ {BPDATA, 0x10},
+ {BPDATA, 0x0e},
+ {0x90, 0x00},
+ {0x91, 0x0e},
+ {0x91, 0x1a},
+ {0x91, 0x31},
+ {0x91, 0x5a},
+ {0x91, 0x69},
+ {0x91, 0x75},
+ {0x91, 0x7e},
+ {0x91, 0x88},
+ {0x91, 0x8f},
+ {0x91, 0x96},
+ {0x91, 0xa3},
+ {0x91, 0xaf},
+ {0x91, 0xc4},
+ {0x91, 0xd7},
+ {0x91, 0xe8},
+ {0x91, 0x20},
+ {0x92, 0x00},
+ {0x93, 0x06},
+ {0x93, 0xe3},
+ {0x93, 0x03},
+ {0x93, 0x03},
+ {0x93, 0x00},
+ {0x93, 0x02},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x93, 0x00},
+ {0x96, 0x00},
+ {0x97, 0x08},
+ {0x97, 0x19},
+ {0x97, 0x02},
+ {0x97, 0x0c},
+ {0x97, 0x24},
+ {0x97, 0x30},
+ {0x97, 0x28},
+ {0x97, 0x26},
+ {0x97, 0x02},
+ {0x97, 0x98},
+ {0x97, 0x80},
+ {0x97, 0x00},
+ {0x97, 0x00},
+ {0xa4, 0x00},
+ {0xa8, 0x00},
+ {0xc5, 0x11},
+ {0xc6, 0x51},
+ {0xbf, 0x80},
+ {0xc7, 0x10}, /* simple AWB */
+ {0xb6, 0x66},
+ {0xb8, 0xA5},
+ {0xb7, 0x64},
+ {0xb9, 0x7C},
+ {0xb3, 0xaf},
+ {0xb4, 0x97},
+ {0xb5, 0xFF},
+ {0xb0, 0xC5},
+ {0xb1, 0x94},
+ {0xb2, 0x0f},
+ {0xc4, 0x5c},
+ {0xa6, 0x00},
+ {0xa7, 0x20},
+ {0xa7, 0xd8},
+ {0xa7, 0x1b},
+ {0xa7, 0x31},
+ {0xa7, 0x00},
+ {0xa7, 0x18},
+ {0xa7, 0x20},
+ {0xa7, 0xd8},
+ {0xa7, 0x19},
+ {0xa7, 0x31},
+ {0xa7, 0x00},
+ {0xa7, 0x18},
+ {0xa7, 0x20},
+ {0xa7, 0xd8},
+ {0xa7, 0x19},
+ {0xa7, 0x31},
+ {0xa7, 0x00},
+ {0xa7, 0x18},
+ {0x7f, 0x00},
+ {0xe5, 0x1f},
+ {0xe1, 0x77},
+ {0xdd, 0x7f},
+ {CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN},
+
+// OpenMV Custom.
+
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {0x0f, 0x4b},
+ {COM1, 0x8f},
+
+// End.
+
+ {0xff, 0xff},
+};
+
+// Looks really bad.
+//static const uint8_t cif_regs[][2] = {
+// {BANK_SEL, BANK_SEL_SENSOR},
+// {COM7, COM7_RES_CIF},
+// {COM1, 0x06 | 0x80},
+// {HSTART, 0x11},
+// {HSTOP, 0x43},
+// {VSTART, 0x01}, // 0x01 fixes issue with garbage pixels in the image...
+// {VSTOP, 0x97},
+// {REG32, 0x09},
+// {BANK_SEL, BANK_SEL_DSP},
+// {RESET, RESET_DVP},
+// {SIZEL, SIZEL_HSIZE8_11_SET(CIF_WIDTH) | SIZEL_HSIZE8_SET(CIF_WIDTH) | SIZEL_VSIZE8_SET(CIF_HEIGHT)},
+// {HSIZE8, HSIZE8_SET(CIF_WIDTH)},
+// {VSIZE8, VSIZE8_SET(CIF_HEIGHT)},
+// {CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN},
+// {0, 0},
+//};
+
+static const uint8_t svga_regs[][2] = {
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {COM7, COM7_RES_SVGA},
+ {COM1, 0x0A | 0x80},
+ {HSTART, 0x11},
+ {HSTOP, 0x43},
+ {VSTART, 0x01}, // 0x01 fixes issue with garbage pixels in the image...
+ {VSTOP, 0x97},
+ {REG32, 0x09},
+ {BANK_SEL, BANK_SEL_DSP},
+ {RESET, RESET_DVP},
+ {SIZEL, SIZEL_HSIZE8_11_SET(SVGA_WIDTH) | SIZEL_HSIZE8_SET(SVGA_WIDTH) | SIZEL_VSIZE8_SET(SVGA_HEIGHT)},
+ {HSIZE8, HSIZE8_SET(SVGA_WIDTH)},
+ {VSIZE8, VSIZE8_SET(SVGA_HEIGHT)},
+ {CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t uxga_regs[][2] = {
+ {BANK_SEL, BANK_SEL_SENSOR},
+ {COM7, COM7_RES_UXGA},
+ {COM1, 0x0F | 0x80},
+ {HSTART, 0x11},
+ {HSTOP, 0x75},
+ {VSTART, 0x01},
+ {VSTOP, 0x97},
+ {REG32, 0x36},
+ {BANK_SEL, BANK_SEL_DSP},
+ {RESET, RESET_DVP},
+ {SIZEL, SIZEL_HSIZE8_11_SET(UXGA_WIDTH) | SIZEL_HSIZE8_SET(UXGA_WIDTH) | SIZEL_VSIZE8_SET(UXGA_HEIGHT)},
+ {HSIZE8, HSIZE8_SET(UXGA_WIDTH)},
+ {VSIZE8, VSIZE8_SET(UXGA_HEIGHT)},
+ {CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t yuv422_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_YUV422},
+ {0xd7, 0x03},
+ {0x33, 0xa0},
+ {0xe5, 0x1f},
+ {0xe1, 0x67},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t rgb565_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_RGB565},
+ {0xd7, 0x03},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t bayer_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_RAW10},
+ {0xd7, 0x03},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+static const uint8_t jpeg_regs[][2] = {
+ {BANK_SEL, BANK_SEL_DSP},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {IMAGE_MODE, IMAGE_MODE_JPEG_EN},
+ {0xd7, 0x03},
+ {RESET, 0x00},
+ {R_BYPASS, R_BYPASS_DSP_EN},
+ {0xff, 0xff},
+};
+
+#define NUM_BRIGHTNESS_LEVELS (5)
+static const uint8_t brightness_regs[NUM_BRIGHTNESS_LEVELS + 1][5] = {
+ {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA},
+ {0x00, 0x04, 0x09, 0x00, 0x00}, /* -2 */
+ {0x00, 0x04, 0x09, 0x10, 0x00}, /* -1 */
+ {0x00, 0x04, 0x09, 0x20, 0x00}, /* 0 */
+ {0x00, 0x04, 0x09, 0x30, 0x00}, /* +1 */
+ {0x00, 0x04, 0x09, 0x40, 0x00}, /* +2 */
+};
+
+#define NUM_CONTRAST_LEVELS (5)
+static const uint8_t contrast_regs[NUM_CONTRAST_LEVELS + 1][7] = {
+ {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA, BPDATA, BPDATA},
+ {0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06}, /* -2 */
+ {0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06}, /* -1 */
+ {0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06}, /* 0 */
+ {0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06}, /* +1 */
+ {0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06}, /* +2 */
+};
+
+#define NUM_SATURATION_LEVELS (5)
+static const uint8_t saturation_regs[NUM_SATURATION_LEVELS + 1][5] = {
+ {BPADDR, BPDATA, BPADDR, BPDATA, BPDATA},
+ {0x00, 0x02, 0x03, 0x28, 0x28}, /* -2 */
+ {0x00, 0x02, 0x03, 0x38, 0x38}, /* -1 */
+ {0x00, 0x02, 0x03, 0x48, 0x48}, /* 0 */
+ {0x00, 0x02, 0x03, 0x58, 0x58}, /* +1 */
+ {0x00, 0x02, 0x03, 0x68, 0x68}, /* +2 */
+};
+
+static int reg_read(int8_t reg, uint8_t *data) {
+ return eos_i2c_read8(OV2640_ADDR, reg, data);
+}
+
+static int reg_write(uint8_t reg, uint8_t data) {
+ return eos_i2c_write8(OV2640_ADDR, reg, data);
+}
+
+static int regarr_write(const uint8_t (*regs)[2]) {
+ int i, rv;
+
+ i = 0;
+ rv = EOS_OK;
+
+ while ((regs[i][0] != 0xff) || (regs[i][1] != 0xff)) {
+ if (!rv) rv = reg_write(regs[i][0], regs[i][1]);
+ i++;
+ }
+
+ return rv;
+}
+
+int eos_ov2640_init(void) {
+ int rv;
+
+ // Reset all registers
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_write(COM7, COM7_SRST);
+ if (rv) return rv;
+
+ // Delay 5 ms
+ eos_time_sleep(5);
+
+ // Write default regsiters
+ rv = regarr_write(default_regs);
+ if (rv) return rv;
+
+ // Delay 300 ms
+ eos_time_sleep(300);
+
+ return EOS_OK;
+}
+
+int eos_ov2640_sleep(int enable) {
+ uint8_t reg;
+ int rv;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM2, &reg);
+ if (rv) return rv;
+
+ if (enable) {
+ reg |= COM2_STDBY;
+ } else {
+ reg &= ~COM2_STDBY;
+ }
+
+ // Write back register
+ return reg_write(COM2, reg);
+}
+
+int eos_ov2640_set_pixfmt(pixformat_t fmt) {
+ const uint8_t (*regs)[2];
+
+ switch (fmt) {
+ case PIXFORMAT_RGB565:
+ regs = rgb565_regs;
+ break;
+ case PIXFORMAT_YUV422:
+ case PIXFORMAT_GRAYSCALE:
+ regs = yuv422_regs;
+ break;
+ case PIXFORMAT_BAYER:
+ regs = bayer_regs;
+ break;
+ case PIXFORMAT_JPEG:
+ regs = jpeg_regs;
+ break;
+ default:
+ return EOS_ERR;
+ }
+
+ return regarr_write(regs);
+}
+
+int eos_ov2640_set_framesize(framesize_t framesize) {
+ const uint8_t (*regs)[2];
+ uint16_t sensor_w = 0;
+ uint16_t sensor_h = 0;
+ uint16_t w = _eos_cam_resolution[framesize][0];
+ uint16_t h = _eos_cam_resolution[framesize][1];
+ int rv;
+
+ if ((w % 4) || (h % 4) || (w > UXGA_WIDTH) || (h > UXGA_HEIGHT)) { // w/h must be divisble by 4
+ return EOS_ERR;
+ }
+
+ // Looks really bad.
+ /* if ((w <= CIF_WIDTH) && (h <= CIF_HEIGHT)) {
+ regs = cif_regs;
+ sensor_w = CIF_WIDTH;
+ sensor_h = CIF_HEIGHT;
+ } else */ if ((w <= SVGA_WIDTH) && (h <= SVGA_HEIGHT)) {
+ regs = svga_regs;
+ sensor_w = SVGA_WIDTH;
+ sensor_h = SVGA_HEIGHT;
+ } else {
+ regs = uxga_regs;
+ sensor_w = UXGA_WIDTH;
+ sensor_h = UXGA_HEIGHT;
+ }
+
+ // Write setup regsiters
+ rv = regarr_write(regs);
+ if (rv) return rv;
+
+ uint64_t tmp_div = IM_MIN(sensor_w / w, sensor_h / h);
+ uint16_t log_div = IM_MIN(IM_LOG2(tmp_div) - 1, 3);
+ uint16_t div = 1 << log_div;
+ uint16_t w_mul = w * div;
+ uint16_t h_mul = h * div;
+ uint16_t x_off = (sensor_w - w_mul) / 2;
+ uint16_t y_off = (sensor_h - h_mul) / 2;
+
+ rv = EOS_OK;
+ if (!rv) rv = reg_write(CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(log_div) | CTRLI_H_DIV_SET(log_div));
+ if (!rv) rv = reg_write(HSIZE, HSIZE_SET(w_mul));
+ if (!rv) rv = reg_write(VSIZE, VSIZE_SET(h_mul));
+ if (!rv) rv = reg_write(XOFFL, XOFFL_SET(x_off));
+ if (!rv) rv = reg_write(YOFFL, YOFFL_SET(y_off));
+ if (!rv) rv = reg_write(VHYX, VHYX_HSIZE_SET(w_mul) | VHYX_VSIZE_SET(h_mul) | VHYX_XOFF_SET(x_off) | VHYX_YOFF_SET(y_off));
+ if (!rv) rv = reg_write(TEST, TEST_HSIZE_SET(w_mul));
+ if (!rv) rv = reg_write(ZMOW, ZMOW_OUTW_SET(w));
+ if (!rv) rv = reg_write(ZMOH, ZMOH_OUTH_SET(h));
+ if (!rv) rv = reg_write(ZMHH, ZMHH_OUTW_SET(w) | ZMHH_OUTH_SET(h));
+ if (!rv) rv = reg_write(R_DVP_SP, div);
+ if (!rv) rv = reg_write(RESET, 0x00);
+
+ return rv;
+}
+
+int eos_ov2640_set_contrast(int level) {
+ int rv = EOS_OK;
+
+ level += (NUM_CONTRAST_LEVELS / 2) + 1;
+ if (level <= 0 || level > NUM_CONTRAST_LEVELS) {
+ return EOS_ERR;
+ }
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write contrast registers */
+ for (int i=0; i<sizeof(contrast_regs[0])/sizeof(contrast_regs[0][0]); i++) {
+ if (!rv) rv = reg_write(contrast_regs[0][i], contrast_regs[level][i]);
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_brightness(int level) {
+ int rv = EOS_OK;
+
+ level += (NUM_BRIGHTNESS_LEVELS / 2) + 1;
+ if (level <= 0 || level > NUM_BRIGHTNESS_LEVELS) {
+ return EOS_ERR;
+ }
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write brightness registers */
+ for (int i=0; i<sizeof(brightness_regs[0])/sizeof(brightness_regs[0][0]); i++) {
+ if (!rv) rv = reg_write(brightness_regs[0][i], brightness_regs[level][i]);
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_saturation(int level) {
+ int rv = EOS_OK;
+
+ level += (NUM_SATURATION_LEVELS / 2) + 1;
+ if (level <= 0 || level > NUM_SATURATION_LEVELS) {
+ return EOS_ERR;
+ }
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write saturation registers */
+ for (int i=0; i<sizeof(saturation_regs[0])/sizeof(saturation_regs[0][0]); i++) {
+ if (!rv) rv = reg_write(saturation_regs[0][i], saturation_regs[level][i]);
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_gainceiling(gainceiling_t gainceiling) {
+ int rv = EOS_OK;
+
+ /* Switch to SENSOR register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+
+ /* Write gain ceiling register */
+ if (!rv) rv = reg_write(COM9, COM9_AGC_SET(gainceiling));
+
+ return rv;
+}
+
+int eos_ov2640_set_quality(int qs) {
+ int rv = EOS_OK;
+
+ /* Switch to DSP register bank */
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+
+ /* Write QS register */
+ if (!rv) rv = reg_write(QS, qs);
+
+ return rv;
+}
+
+int eos_ov2640_set_colorbar(int enable) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM7, &reg);
+ if (rv) return rv;
+
+ if (enable) {
+ reg |= COM7_COLOR_BAR;
+ } else {
+ reg &= ~COM7_COLOR_BAR;
+ }
+
+ return reg_write(COM7, reg);
+}
+
+int eos_ov2640_set_auto_gain(int enable, float gain_db, float gain_db_ceiling) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ rv = reg_write(COM8, (reg & (~COM8_AGC_EN)) | (enable ? COM8_AGC_EN : 0));
+ if (rv) return rv;
+
+ rv = EOS_OK;
+ if (enable && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) {
+ float gain_ceiling = IM_MAX(IM_MIN(expf((gain_db_ceiling / 20.0) * logf(10.0)), 128.0), 2.0);
+
+ if (!rv) rv = reg_read(COM9, &reg);
+ if (!rv) rv = reg_write(COM9, (reg & 0x1F) | (((int)ceilf(log2f(gain_ceiling)) - 1) << 5));
+ }
+
+ if (!enable && (!isnanf(gain_db)) && (!isinff(gain_db))) {
+ float gain = IM_MAX(IM_MIN(expf((gain_db / 20.0) * logf(10.0)), 32.0), 1.0);
+
+ int gain_temp = roundf(log2f(IM_MAX(gain / 2.0, 1.0)));
+ int gain_hi = 0xF >> (4 - gain_temp);
+ int gain_lo = IM_MIN(roundf(((gain / (1 << gain_temp)) - 1.0) * 16.0), 15);
+
+ if (!rv) rv = reg_write(GAIN, (gain_hi << 4) | (gain_lo << 0));
+ }
+
+ return rv;
+}
+
+int eos_ov2640_get_gain_db(float *gain_db) {
+ int rv = EOS_OK;
+ uint8_t reg, gain;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AGC_EN) {
+ // rv = reg_write(COM8, reg & (~COM8_AGC_EN));
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ rv = reg_read(GAIN, &gain);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AGC_EN) {
+ // rv = reg_write(COM8, reg | COM8_AGC_EN);
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ int hi_gain = 1 << (((gain >> 7) & 1) + ((gain >> 6) & 1) + ((gain >> 5) & 1) + ((gain >> 4) & 1));
+ float lo_gain = 1.0 + (((gain >> 0) & 0xF) / 16.0);
+ *gain_db = 20.0 * (logf(hi_gain * lo_gain) / logf(10.0));
+
+ return EOS_OK;
+}
+
+int eos_ov2640_set_auto_exposure(int enable, int exposure_us) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ rv = reg_write(COM8, COM8_SET_AEC(reg, (enable != 0)));
+ if (rv) return rv;
+
+ if (!enable && (exposure_us >= 0)) {
+ rv = reg_read(COM7, &reg);
+ if (rv) return rv;
+
+ int t_line = 0;
+
+ if (COM7_GET_RES(reg) == COM7_RES_UXGA) t_line = 1600 + 322;
+ if (COM7_GET_RES(reg) == COM7_RES_SVGA) t_line = 800 + 390;
+ if (COM7_GET_RES(reg) == COM7_RES_CIF) t_line = 400 + 195;
+
+ rv = reg_read(CLKRC, &reg);
+ if (rv) return rv;
+
+ int pll_mult = ((reg & CLKRC_DOUBLE) ? 2 : 1) * 3;
+ int clk_rc = (reg & CLKRC_DIVIDER_MASK) + 2;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (rv) return rv;
+
+ rv = reg_read(IMAGE_MODE, &reg);
+ if (rv) return rv;
+
+ int t_pclk = 0;
+
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_YUV422) t_pclk = 2;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RAW10) t_pclk = 1;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RGB565) t_pclk = 2;
+
+ int exposure = IM_MAX(IM_MIN(((exposure_us*(((XCLK_FREQ/clk_rc)*pll_mult)/1000000))/t_pclk)/t_line,0xFFFF),0x0000);
+
+ if (!rv) rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+
+ if (!rv) rv = reg_read(REG04, &reg);
+ if (!rv) rv = reg_write(REG04, (reg & 0xFC) | ((exposure >> 0) & 0x3));
+
+ if (!rv) rv = reg_read(AEC, &reg);
+ if (!rv) rv = reg_write(AEC, (reg & 0x00) | ((exposure >> 2) & 0xFF));
+
+ if (!rv) rv = reg_read(REG45, &reg);
+ if (!rv) rv = reg_write(REG45, (reg & 0xC0) | ((exposure >> 10) & 0x3F));
+ }
+
+ return rv;
+}
+
+int eos_ov2640_get_exposure_us(int *exposure_us) {
+ int rv = EOS_OK;
+ uint8_t reg, aec_10, aec_92, aec_1510;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(COM8, &reg);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AEC_EN) {
+ // rv = reg_write(COM8, reg & (~COM8_AEC_EN));
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ rv = reg_read(REG04, &aec_10);
+ if (rv) return rv;
+
+ rv = reg_read(AEC, &aec_92);
+ if (rv) return rv;
+
+ rv = reg_read(REG45, &aec_1510);
+ if (rv) return rv;
+
+ // DISABLED
+ // if (reg & COM8_AEC_EN) {
+ // rv = reg_write(COM8, reg | COM8_AEC_EN);
+ // if (rv) return rv;
+ // }
+ // DISABLED
+
+ rv = reg_read(COM7, &reg);
+ if (rv) return rv;
+
+ int t_line = 0;
+
+ if (COM7_GET_RES(reg) == COM7_RES_UXGA) t_line = 1600 + 322;
+ if (COM7_GET_RES(reg) == COM7_RES_SVGA) t_line = 800 + 390;
+ if (COM7_GET_RES(reg) == COM7_RES_CIF) t_line = 400 + 195;
+
+ rv = reg_read(CLKRC, &reg);
+ if (rv) return rv;
+
+ int pll_mult = ((reg & CLKRC_DOUBLE) ? 2 : 1) * 3;
+ int clk_rc = (reg & CLKRC_DIVIDER_MASK) + 2;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (rv) return rv;
+
+ rv = reg_read(IMAGE_MODE, &reg);
+ if (rv) return rv;
+
+ int t_pclk = 0;
+
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_YUV422) t_pclk = 2;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RAW10) t_pclk = 1;
+ if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RGB565) t_pclk = 2;
+
+ uint16_t exposure = ((aec_1510 & 0x3F) << 10) + ((aec_92 & 0xFF) << 2) + ((aec_10 & 0x3) << 0);
+ *exposure_us = (exposure*t_line*t_pclk)/(((XCLK_FREQ/clk_rc)*pll_mult)/1000000);
+
+ return EOS_OK;
+}
+
+int eos_ov2640_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (rv) return rv;
+
+ rv = reg_read(CTRL1, &reg);
+ if (rv) return rv;
+
+ rv = reg_write(CTRL1, (reg & (~CTRL1_AWB)) | (enable ? CTRL1_AWB : 0));
+ if (rv) return rv;
+
+ if (!enable && (!isnanf(r_gain_db)) && (!isnanf(g_gain_db)) && (!isnanf(b_gain_db))
+ && (!isinff(r_gain_db)) && (!isinff(g_gain_db)) && (!isinff(b_gain_db))) {
+ }
+
+ return rv;
+}
+
+int eos_ov2640_set_hmirror(int enable) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(REG04, &reg);
+ if (rv) return rv;
+
+ if (!enable) { // Already mirrored.
+ reg |= REG04_HFLIP_IMG;
+ } else {
+ reg &= ~REG04_HFLIP_IMG;
+ }
+
+ return reg_write(REG04, reg);
+}
+
+int eos_ov2640_set_vflip(int enable) {
+ int rv = EOS_OK;
+ uint8_t reg;
+
+ rv = reg_write(BANK_SEL, BANK_SEL_SENSOR);
+ if (rv) return rv;
+
+ rv = reg_read(REG04, &reg);
+ if (rv) return rv;
+
+ if (!enable) { // Already flipped.
+ reg |= REG04_VFLIP_IMG | REG04_VREF_EN;
+ } else {
+ reg &= ~(REG04_VFLIP_IMG | REG04_VREF_EN);
+ }
+
+ return reg_write(REG04, reg);
+}
+
+int eos_ov2640_set_effect(sde_t sde) {
+ int rv = EOS_OK;
+
+ switch (sde) {
+ case SDE_NEGATIVE:
+ if (!rv) rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (!rv) rv = reg_write(BPADDR, 0x00);
+ if (!rv) rv = reg_write(BPDATA, 0x40);
+ if (!rv) rv = reg_write(BPADDR, 0x05);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ break;
+ case SDE_NORMAL:
+ if (!rv) rv = reg_write(BANK_SEL, BANK_SEL_DSP);
+ if (!rv) rv = reg_write(BPADDR, 0x00);
+ if (!rv) rv = reg_write(BPDATA, 0x00);
+ if (!rv) rv = reg_write(BPADDR, 0x05);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ if (!rv) rv = reg_write(BPDATA, 0x80);
+ break;
+ default:
+ return EOS_ERR;
+ }
+
+ return rv;
+}
diff --git a/fw/fe310/eos/i2c/ov2640.h b/fw/fe310/eos/i2c/ov2640.h
new file mode 100644
index 0000000..3d08c2a
--- /dev/null
+++ b/fw/fe310/eos/i2c/ov2640.h
@@ -0,0 +1,8 @@
+#include <stdint.h>
+
+#define OV2640_ADDR 0x30
+
+int eos_ov2640_init(void);
+int eos_ov2640_sleep(int enable);
+int eos_ov2640_set_pixfmt(pixformat_t fmt);
+int eos_ov2640_set_framesize(framesize_t framesize);
diff --git a/fw/fe310/eos/i2c/ov2640_regs.h b/fw/fe310/eos/i2c/ov2640_regs.h
new file mode 100644
index 0000000..deb7521
--- /dev/null
+++ b/fw/fe310/eos/i2c/ov2640_regs.h
@@ -0,0 +1,245 @@
+/*
+ * This file is part of the OpenMV project.
+ *
+ * Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
+ * Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
+ *
+ * This work is licensed under the MIT license, see the file LICENSE for details.
+ *
+ * OV2640 register definitions.
+ */
+#ifndef __REG_REGS_H__
+#define __REG_REGS_H__
+
+/* DSP register bank FF=0x00*/
+
+#define QS 0x44
+#define HSIZE 0x51
+#define VSIZE 0x52
+#define XOFFL 0x53
+#define YOFFL 0x54
+#define VHYX 0x55
+#define DPRP 0x56
+#define TEST 0x57
+#define ZMOW 0x5A
+#define ZMOH 0x5B
+#define ZMHH 0x5C
+#define BPADDR 0x7C
+#define BPDATA 0x7D
+#define SIZEL 0x8C
+#define HSIZE8 0xC0
+#define VSIZE8 0xC1
+#define CTRL1 0xC3
+#define MS_SP 0xF0
+#define SS_ID 0xF7
+#define SS_CTRL 0xF7
+#define MC_AL 0xFA
+#define MC_AH 0xFB
+#define MC_D 0xFC
+#define P_CMD 0xFD
+#define P_STATUS 0xFE
+
+#define CTRLI 0x50
+#define CTRLI_LP_DP 0x80
+#define CTRLI_ROUND 0x40
+
+#define CTRL0 0xC2
+#define CTRL0_AEC_EN 0x80
+#define CTRL0_AEC_SEL 0x40
+#define CTRL0_STAT_SEL 0x20
+#define CTRL0_VFIRST 0x10
+#define CTRL0_YUV422 0x08
+#define CTRL0_YUV_EN 0x04
+#define CTRL0_RGB_EN 0x02
+#define CTRL0_RAW_EN 0x01
+
+#define CTRL2 0x86
+#define CTRL2_DCW_EN 0x20
+#define CTRL2_SDE_EN 0x10
+#define CTRL2_UV_ADJ_EN 0x08
+#define CTRL2_UV_AVG_EN 0x04
+#define CTRL2_CMX_EN 0x01
+
+#define CTRL3 0x87
+#define CTRL3_BPC_EN 0x80
+#define CTRL3_WPC_EN 0x40
+#define R_DVP_SP 0xD3
+#define R_DVP_SP_AUTO_MODE 0x80
+
+#define R_BYPASS 0x05
+#define R_BYPASS_DSP_EN 0x00
+#define R_BYPASS_DSP_BYPAS 0x01
+
+#define IMAGE_MODE 0xDA
+#define IMAGE_MODE_Y8_DVP_EN 0x40
+#define IMAGE_MODE_JPEG_EN 0x10
+#define IMAGE_MODE_YUV422 0x00
+#define IMAGE_MODE_RAW10 0x04
+#define IMAGE_MODE_RGB565 0x09
+#define IMAGE_MODE_HREF_VSYNC 0x02
+#define IMAGE_MODE_LBYTE_FIRST 0x01
+#define IMAGE_MODE_GET_FMT(x) ((x)&0xC)
+
+#define RESET 0xE0
+#define RESET_MICROC 0x40
+#define RESET_SCCB 0x20
+#define RESET_JPEG 0x10
+#define RESET_DVP 0x04
+#define RESET_IPU 0x02
+#define RESET_CIF 0x01
+
+#define MC_BIST 0xF9
+#define MC_BIST_RESET 0x80
+#define MC_BIST_BOOT_ROM_SEL 0x40
+#define MC_BIST_12KB_SEL 0x20
+#define MC_BIST_12KB_MASK 0x30
+#define MC_BIST_512KB_SEL 0x08
+#define MC_BIST_512KB_MASK 0x0C
+#define MC_BIST_BUSY_BIT_R 0x02
+#define MC_BIST_MC_RES_ONE_SH_W 0x02
+#define MC_BIST_LAUNCH 0x01
+
+#define BANK_SEL 0xFF
+#define BANK_SEL_DSP 0x00
+#define BANK_SEL_SENSOR 0x01
+
+/* Sensor register bank FF=0x01*/
+
+#define GAIN 0x00
+#define COM1 0x03
+#define REG_PID 0x0A
+#define REG_VER 0x0B
+#define COM4 0x0D
+#define AEC 0x10
+
+#define CLKRC 0x11
+#define CLKRC_DOUBLE 0x80
+#define CLKRC_DIVIDER_MASK 0x3F
+
+#define COM10 0x15
+#define HSTART 0x17
+#define HSTOP 0x18
+#define VSTART 0x19
+#define VSTOP 0x1A
+#define MIDH 0x1C
+#define MIDL 0x1D
+#define AEW 0x24
+#define AEB 0x25
+#define REG2A 0x2A
+#define FRARL 0x2B
+#define ADDVSL 0x2D
+#define ADDVSH 0x2E
+#define YAVG 0x2F
+#define HSDY 0x30
+#define HEDY 0x31
+#define ARCOM2 0x34
+#define REG45 0x45
+#define FLL 0x46
+#define FLH 0x47
+#define COM19 0x48
+#define ZOOMS 0x49
+#define COM22 0x4B
+#define COM25 0x4E
+#define BD50 0x4F
+#define BD60 0x50
+#define REG5D 0x5D
+#define REG5E 0x5E
+#define REG5F 0x5F
+#define REG60 0x60
+#define HISTO_LOW 0x61
+#define HISTO_HIGH 0x62
+
+#define REG04 0x04
+#define REG04_DEFAULT 0x28
+#define REG04_HFLIP_IMG 0x80
+#define REG04_VFLIP_IMG 0x40
+#define REG04_VREF_EN 0x10
+#define REG04_HREF_EN 0x08
+#define REG04_SET(x) (REG04_DEFAULT|x)
+
+#define REG08 0x08
+#define COM2 0x09
+#define COM2_STDBY 0x10
+#define COM2_OUT_DRIVE_1x 0x00
+#define COM2_OUT_DRIVE_2x 0x01
+#define COM2_OUT_DRIVE_3x 0x02
+#define COM2_OUT_DRIVE_4x 0x03
+
+#define COM3 0x0C
+#define COM3_DEFAULT 0x38
+#define COM3_BAND_50Hz 0x04
+#define COM3_BAND_60Hz 0x00
+#define COM3_BAND_AUTO 0x02
+#define COM3_BAND_SET(x) (COM3_DEFAULT|x)
+
+#define COM7 0x12
+#define COM7_SRST 0x80
+#define COM7_RES_UXGA 0x00 /* UXGA */
+#define COM7_RES_SVGA 0x40 /* SVGA */
+#define COM7_RES_CIF 0x20 /* CIF */
+#define COM7_ZOOM_EN 0x04 /* Enable Zoom */
+#define COM7_COLOR_BAR 0x02 /* Enable Color Bar Test */
+#define COM7_GET_RES(x) ((x)&0x70)
+
+#define COM8 0x13
+#define COM8_DEFAULT 0xC0
+#define COM8_BNDF_EN 0x20 /* Enable Banding filter */
+#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
+#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
+#define COM8_SET(x) (COM8_DEFAULT|x)
+#define COM8_SET_AEC(r,x) (((r)&0xFE)|((x)&1))
+
+#define COM9 0x14 /* AGC gain ceiling */
+#define COM9_DEFAULT 0x08
+#define COM9_AGC_GAIN_2x 0x00 /* AGC: 2x */
+#define COM9_AGC_GAIN_4x 0x01 /* AGC: 4x */
+#define COM9_AGC_GAIN_8x 0x02 /* AGC: 8x */
+#define COM9_AGC_GAIN_16x 0x03 /* AGC: 16x */
+#define COM9_AGC_GAIN_32x 0x04 /* AGC: 32x */
+#define COM9_AGC_GAIN_64x 0x05 /* AGC: 64x */
+#define COM9_AGC_GAIN_128x 0x06 /* AGC: 128x */
+#define COM9_AGC_SET(x) (COM9_DEFAULT|(x<<5))
+
+#define CTRL1_AWB 0x08 /* Enable AWB */
+
+#define VV 0x26
+#define VV_AGC_TH_SET(h,l) ((h<<4)|(l&0x0F))
+
+#define REG32 0x32
+#define REG32_UXGA 0x36
+#define REG32_SVGA 0x09
+#define REG32_CIF 0x00
+
+#define VAL_SET(x, mask, rshift, lshift) ((((x) >> rshift) & mask) << lshift)
+
+#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
+#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
+
+#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
+#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
+#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
+
+#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+
+#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+
+#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+
+#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
+#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
+#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
+#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
+
+#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
+
+#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
+
+#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
+#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
+#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
+
+#endif //__REG_REGS_H__
diff --git a/fw/fe310/eos/i2s.c b/fw/fe310/eos/i2s.c
new file mode 100644
index 0000000..b4fd277
--- /dev/null
+++ b/fw/fe310/eos/i2s.c
@@ -0,0 +1,408 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+#include "prci_driver.h"
+
+#include "eos.h"
+#include "interrupt.h"
+#include "event.h"
+
+#include "board.h"
+
+#include "i2s.h"
+#include "i2s_priv.h"
+
+#define I2S_REG_CK(o) _REG32(I2S_CTRL_ADDR_CK, o)
+#define I2S_REG_WS_MIC(o) _REG32(I2S_CTRL_ADDR_WS_MIC, o)
+#define I2S_REG_WS_SPK(o) _REG32(I2S_CTRL_ADDR_WS_SPK, o)
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+#define EOS_ABUF_IDX_MASK(IDX, SIZE) ((IDX) & ((SIZE) - 1))
+
+EOSABuf _eos_i2s_mic_buf;
+EOSABuf _eos_i2s_spk_buf;
+uint32_t _eos_i2s_fmt = 0;
+uint32_t _eos_i2s_mic_wm = 0;
+uint32_t _eos_i2s_spk_wm = 0;
+uint32_t _eos_i2s_mic_evt_enable = 0;
+uint32_t _eos_i2s_spk_evt_enable = 0;
+
+static eos_i2s_handler_t i2s_spk_handler = NULL;
+static eos_i2s_handler_t i2s_mic_handler = NULL;
+static uint32_t i2s_clk_period;
+static uint8_t i2s_mic_volume = 0; /* 0 - 8 */
+static uint8_t i2s_spk_volume = 16; /* 0 - 16 */
+
+static void _abuf_init(EOSABuf *buf, uint8_t *array, uint16_t size) {
+ buf->idx_r = 0;
+ buf->idx_w = 0;
+ buf->size = size;
+ buf->array = array;
+}
+
+static int _abuf_push8(EOSABuf *buf, uint8_t sample) {
+ if ((uint16_t)(buf->idx_w - buf->idx_r) == buf->size) return EOS_ERR_FULL;
+
+ buf->array[EOS_ABUF_IDX_MASK(buf->idx_w, buf->size)] = sample;
+ buf->idx_w++;
+ return EOS_OK;
+}
+
+static int _abuf_push16(EOSABuf *buf, uint16_t sample) {
+ if ((uint16_t)(buf->idx_w - buf->idx_r) == buf->size) return EOS_ERR_FULL;
+
+ buf->array[EOS_ABUF_IDX_MASK(buf->idx_w, buf->size)] = sample >> 8;
+ buf->array[EOS_ABUF_IDX_MASK(buf->idx_w + 1, buf->size)] = sample & 0xFF;
+ buf->idx_w += 2;
+ return EOS_OK;
+}
+
+static int _abuf_pop8(EOSABuf *buf, uint8_t *sample) {
+ if (buf->idx_r == buf->idx_w) {
+ return EOS_ERR_EMPTY;
+ } else {
+ *sample = buf->array[EOS_ABUF_IDX_MASK(buf->idx_r, buf->size)];
+ buf->idx_r++;
+ return EOS_OK;
+ }
+}
+
+static int _abuf_pop16(EOSABuf *buf, uint16_t *sample) {
+ if (buf->idx_r == buf->idx_w) {
+ return EOS_ERR_EMPTY;
+ } else {
+ *sample = buf->array[EOS_ABUF_IDX_MASK(buf->idx_r, buf->size)] << 8;
+ *sample |= buf->array[EOS_ABUF_IDX_MASK(buf->idx_r + 1, buf->size)];
+ buf->idx_r += 2;
+ return EOS_OK;
+ }
+}
+
+
+static void _abuf_flush(EOSABuf *buf) {
+ buf->idx_r = 0;
+ buf->idx_w = 0;
+}
+
+static uint16_t _abuf_len(EOSABuf *buf) {
+ return buf->idx_w - buf->idx_r;
+}
+
+static void i2s_handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ switch(type & ~EOS_EVT_MASK) {
+ case EOS_I2S_ETYPE_MIC:
+ if (i2s_mic_handler) i2s_mic_handler(type);
+ clear_csr(mstatus, MSTATUS_MIE);
+ _eos_i2s_mic_evt_enable = 1;
+ set_csr(mstatus, MSTATUS_MIE);
+ break;
+ case EOS_I2S_ETYPE_SPK:
+ if (i2s_spk_handler) i2s_spk_handler(type);
+ clear_csr(mstatus, MSTATUS_MIE);
+ _eos_i2s_spk_evt_enable = 1;
+ set_csr(mstatus, MSTATUS_MIE);
+ break;
+ default:
+ eos_evtq_bad_handler(type, buffer, len);
+ break;
+ }
+}
+
+static void _mic_vol_set(uint8_t vol) {
+ I2S_REG_WS_MIC(PWM_CMP2) = i2s_clk_period * (vol + 1);
+ I2S_REG_WS_MIC(PWM_CMP3) = I2S_REG_WS_MIC(PWM_CMP2) + i2s_clk_period * 16;
+}
+
+static void _spk_vol_set(uint8_t vol) {
+ int spk_cmp = vol + i2s_mic_volume - 16;
+
+ if (spk_cmp <= 0) {
+ I2S_REG_WS_SPK(PWM_CMP1) = i2s_clk_period * (32 + spk_cmp);
+ I2S_REG_WS_SPK(PWM_CMP2) = i2s_clk_period * (64 + spk_cmp);
+ I2S_REG_WS_SPK(PWM_CMP3) = i2s_clk_period * 33;
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_WS_SPK);
+ } else {
+ I2S_REG_WS_SPK(PWM_CMP1) = i2s_clk_period * spk_cmp;
+ I2S_REG_WS_SPK(PWM_CMP2) = i2s_clk_period * (32 + spk_cmp);
+ I2S_REG_WS_SPK(PWM_CMP3) = i2s_clk_period * (33 + spk_cmp);
+ GPIO_REG(GPIO_OUTPUT_XOR) |= (1 << I2S_PIN_WS_SPK);
+ }
+}
+
+extern void _eos_i2s_start_pwm(void);
+
+void eos_i2s_init(void) {
+ eos_evtq_set_handler(EOS_EVT_I2S, i2s_handle_evt);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_CK);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << I2S_PIN_CK);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_CK);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_CK);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_CK_SW);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_WS_MIC);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << I2S_PIN_WS_MIC);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_WS_MIC);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_WS_MIC);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_WS_SPK);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << I2S_PIN_WS_SPK);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_WS_SPK);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_WS_SPK);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_CK_SR);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << I2S_PIN_CK_SR);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_CK_SR);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_CK_SR);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_SD_IN);
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << I2S_PIN_SD_IN);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_SD_IN);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_SD_IN);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_SD_OUT);
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << I2S_PIN_SD_OUT);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << I2S_PIN_SD_OUT);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_SD_OUT);
+
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_OUTPUT_VAL) &= ~((1 << I2S_PIN_CK) | (1 << I2S_PIN_CK_SR) | (1 << I2S_PIN_WS_MIC) | (1 << I2S_PIN_WS_SPK));
+}
+
+void eos_i2s_start(uint32_t sample_rate, unsigned char fmt) {
+ i2s_clk_period = ((PRCI_get_cpu_freq() / (sample_rate * 64)) & ~I2S_PWM_SCALE_CK_MASK) + 1;
+
+ I2S_REG_CK(PWM_CMP0) = i2s_clk_period >> I2S_PWM_SCALE_CK;
+ I2S_REG_CK(PWM_CMP1) = I2S_REG_CK(PWM_CMP0) / 2;
+ I2S_REG_CK(PWM_CMP2) = 0;
+ I2S_REG_CK(PWM_CMP3) = 0;
+
+ I2S_REG_WS_MIC(PWM_CMP0) = i2s_clk_period * 64 - 1;
+ I2S_REG_WS_MIC(PWM_CMP1) = i2s_clk_period * 32;
+ _mic_vol_set(i2s_mic_volume);
+
+ I2S_REG_WS_SPK(PWM_CMP0) = i2s_clk_period * 64 - 1;
+ _spk_vol_set(i2s_spk_volume);
+
+ I2S_REG_CK(PWM_COUNT) = 0;
+ I2S_REG_WS_MIC(PWM_COUNT) = 0;
+ I2S_REG_WS_SPK(PWM_COUNT) = i2s_clk_period / 2;
+
+ _eos_i2s_fmt = fmt;
+ _eos_i2s_mic_evt_enable = 1;
+ _eos_i2s_spk_evt_enable = 1;
+
+ eos_intr_set_priority(I2S_IRQ_WS_ID, IRQ_PRIORITY_I2S_WS);
+ eos_intr_set_priority(I2S_IRQ_SD_ID, 0);
+ eos_intr_enable(I2S_IRQ_WS_ID);
+ eos_intr_enable(I2S_IRQ_SD_ID);
+
+ _eos_i2s_start_pwm();
+ /*
+ I2S_REG_CK(PWM_CFG) = PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | I2S_PWM_SCALE_CK;
+ I2S_REG_WS_MIC(PWM_CFG) = PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | PWM_CFG_CMP2GANG;
+ I2S_REG_WS_SPK(PWM_CFG) = PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | PWM_CFG_CMP1GANG;
+ */
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << I2S_PIN_SD_IN);
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << I2S_PIN_CK_SR);
+
+ GPIO_REG(GPIO_IOF_SEL) |= (1 << I2S_PIN_CK);
+ GPIO_REG(GPIO_IOF_EN) |= (1 << I2S_PIN_CK);
+
+ GPIO_REG(GPIO_IOF_SEL) |= (1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_IOF_EN) |= (1 << I2S_PIN_CK_SW);
+
+ GPIO_REG(GPIO_IOF_SEL) |= (1 << I2S_PIN_WS_MIC);
+ GPIO_REG(GPIO_IOF_EN) |= (1 << I2S_PIN_WS_MIC);
+
+ GPIO_REG(GPIO_IOF_SEL) |= (1 << I2S_PIN_WS_SPK);
+ GPIO_REG(GPIO_IOF_EN) |= (1 << I2S_PIN_WS_SPK);
+}
+
+void eos_i2s_stop(void) {
+ I2S_REG_CK(PWM_CFG) = 0;
+ I2S_REG_WS_MIC(PWM_CFG) = 0;
+ I2S_REG_WS_SPK(PWM_CFG) = 0;
+ I2S_REG_CK(PWM_COUNT) = 0;
+ I2S_REG_WS_MIC(PWM_COUNT) = 0;
+ I2S_REG_WS_SPK(PWM_COUNT) = 0;
+
+ _eos_i2s_mic_evt_enable = 0;
+ _eos_i2s_spk_evt_enable = 0;
+ eos_intr_set_priority(I2S_IRQ_WS_ID, 0);
+ eos_intr_set_priority(I2S_IRQ_SD_ID, 0);
+ eos_intr_disable(I2S_IRQ_WS_ID);
+ eos_intr_disable(I2S_IRQ_SD_ID);
+
+ GPIO_REG(GPIO_IOF_EN) &= ~(1 << I2S_PIN_CK);
+ GPIO_REG(GPIO_IOF_SEL) &= ~(1 << I2S_PIN_CK);
+
+ GPIO_REG(GPIO_IOF_EN) &= ~(1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_IOF_SEL) &= ~(1 << I2S_PIN_CK_SW);
+
+ GPIO_REG(GPIO_IOF_EN) &= ~(1 << I2S_PIN_WS_MIC);
+ GPIO_REG(GPIO_IOF_SEL) &= ~(1 << I2S_PIN_WS_MIC);
+
+ GPIO_REG(GPIO_IOF_EN) &= ~(1 << I2S_PIN_WS_SPK);
+ GPIO_REG(GPIO_IOF_SEL) &= ~(1 << I2S_PIN_WS_SPK);
+
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << I2S_PIN_SD_IN);
+
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << I2S_PIN_CK_SW);
+ GPIO_REG(GPIO_OUTPUT_VAL) &= ~((1 << I2S_PIN_CK) | (1 << I2S_PIN_CK_SR) | (1 << I2S_PIN_WS_MIC) | (1 << I2S_PIN_WS_SPK));
+}
+
+void eos_i2s_mic_init(uint8_t *mic_arr, uint16_t mic_arr_size) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ _abuf_init(&_eos_i2s_mic_buf, mic_arr, mic_arr_size);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_i2s_mic_set_handler(eos_i2s_handler_t wm_handler) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ i2s_mic_handler = wm_handler;
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_i2s_mic_set_wm(uint16_t wm) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ _eos_i2s_mic_wm = wm;
+ set_csr(mstatus, MSTATUS_MIE);
+
+}
+
+uint16_t eos_i2s_mic_len(void) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ uint16_t ret = _abuf_len(&_eos_i2s_mic_buf);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+uint16_t eos_i2s_mic_read(uint8_t *sample, uint16_t ssize) {
+ uint16_t i;
+ uint16_t _ssize = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ _ssize = MIN(ssize, _abuf_len(&_eos_i2s_mic_buf));
+ set_csr(mstatus, MSTATUS_MIE);
+
+ for (i=0; i<_ssize; i++) {
+ sample[i] = _eos_i2s_mic_buf.array[EOS_ABUF_IDX_MASK(_eos_i2s_mic_buf.idx_r + i, _eos_i2s_mic_buf.size)];
+ }
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ _eos_i2s_mic_buf.idx_r += _ssize;
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return _ssize;
+}
+
+int eos_i2s_mic_pop8(uint8_t *sample) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ int ret = _abuf_pop8(&_eos_i2s_mic_buf, sample);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+int eos_i2s_mic_pop16(uint16_t *sample) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ int ret = _abuf_pop16(&_eos_i2s_mic_buf, sample);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+int eos_i2s_mic_vol_get(void) {
+ return i2s_mic_volume;
+}
+
+void eos_i2s_mic_vol_set(int vol) {
+ if ((vol < 0) || (vol > 8)) return;
+
+ i2s_mic_volume = vol;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ _mic_vol_set(vol);
+ _spk_vol_set(i2s_spk_volume);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_i2s_spk_init(uint8_t *spk_arr, uint16_t spk_arr_size) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ _abuf_init(&_eos_i2s_spk_buf, spk_arr, spk_arr_size);
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_i2s_spk_set_handler(eos_i2s_handler_t wm_handler) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ i2s_spk_handler = wm_handler;
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_i2s_spk_set_wm(uint16_t wm) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ _eos_i2s_spk_wm = wm;
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+uint16_t eos_i2s_spk_len(void) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ uint16_t ret = _abuf_len(&_eos_i2s_spk_buf);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+uint16_t eos_i2s_spk_write(uint8_t *sample, uint16_t ssize) {
+ uint16_t i;
+ uint16_t _ssize = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ _ssize = MIN(ssize, _eos_i2s_spk_buf.size - _abuf_len(&_eos_i2s_spk_buf));
+ set_csr(mstatus, MSTATUS_MIE);
+
+ for (i=0; i<_ssize; i++) {
+ _eos_i2s_spk_buf.array[EOS_ABUF_IDX_MASK(_eos_i2s_spk_buf.idx_w + i, _eos_i2s_spk_buf.size)] = sample[i];
+ }
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ _eos_i2s_spk_buf.idx_w += _ssize;
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return _ssize;
+}
+
+int eos_i2s_spk_push8(uint8_t sample) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ int ret = _abuf_push8(&_eos_i2s_spk_buf, sample);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+int eos_i2s_spk_push16(uint16_t sample) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ int ret = _abuf_push16(&_eos_i2s_spk_buf, sample);
+ set_csr(mstatus, MSTATUS_MIE);
+ return ret;
+}
+
+int eos_i2s_spk_vol_get(void) {
+ return i2s_spk_volume;
+}
+
+void eos_i2s_spk_vol_set(int vol) {
+ if ((vol < 0) || (vol > 16)) return;
+
+ i2s_spk_volume = vol;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ _spk_vol_set(vol);
+ set_csr(mstatus, MSTATUS_MIE);
+}
diff --git a/fw/fe310/eos/i2s.h b/fw/fe310/eos/i2s.h
new file mode 100644
index 0000000..47dc3e2
--- /dev/null
+++ b/fw/fe310/eos/i2s.h
@@ -0,0 +1,34 @@
+#include <stdint.h>
+
+#include "i2s_def.h"
+
+typedef struct EOSABuf {
+ uint16_t idx_r;
+ uint16_t idx_w;
+ uint16_t size;
+ uint8_t *array;
+} EOSABuf;
+
+typedef void (*eos_i2s_handler_t) (unsigned char);
+
+void eos_i2s_init(void);
+void eos_i2s_start(uint32_t sample_rate, unsigned char fmt);
+void eos_i2s_stop(void);
+void eos_i2s_mic_init(uint8_t *mic_arr, uint16_t mic_arr_size);
+void eos_i2s_mic_set_handler(eos_i2s_handler_t wm_handler);
+void eos_i2s_mic_set_wm(uint16_t wm);
+uint16_t eos_i2s_mic_len(void);
+uint16_t eos_i2s_mic_read(uint8_t *sample, uint16_t ssize);
+int eos_i2s_mic_pop8(uint8_t *sample);
+int eos_i2s_mic_pop16(uint16_t *sample);
+int eos_i2s_mic_vol_get(void);
+void eos_i2s_mic_vol_set(int vol);
+void eos_i2s_spk_init(uint8_t *mic_arr, uint16_t mic_arr_size);
+void eos_i2s_spk_set_handler(eos_i2s_handler_t wm_handler);
+void eos_i2s_spk_set_wm(uint16_t wm);
+uint16_t eos_i2s_spk_len(void);
+uint16_t eos_i2s_spk_write(uint8_t *sample, uint16_t ssize);
+int eos_i2s_spk_push8(uint8_t sample);
+int eos_i2s_spk_push16(uint16_t sample);
+int eos_i2s_spk_vol_get(void);
+void eos_i2s_spk_vol_set(int vol);
diff --git a/fw/fe310/eos/i2s_def.h b/fw/fe310/eos/i2s_def.h
new file mode 100644
index 0000000..1af70bb
--- /dev/null
+++ b/fw/fe310/eos/i2s_def.h
@@ -0,0 +1,5 @@
+#define EOS_I2S_FMT_ALAW 0
+#define EOS_I2S_FMT_PCM16 1
+
+#define EOS_I2S_ETYPE_MIC 1
+#define EOS_I2S_ETYPE_SPK 2
diff --git a/fw/fe310/eos/i2s_priv.h b/fw/fe310/eos/i2s_priv.h
new file mode 100644
index 0000000..25237c3
--- /dev/null
+++ b/fw/fe310/eos/i2s_priv.h
@@ -0,0 +1,8 @@
+#define I2S_PWM_SCALE_CK 2
+#define I2S_PWM_SCALE_CK_MASK 0x0003
+
+/* asm */
+#define I2S_ABUF_OFF_IDXR 0
+#define I2S_ABUF_OFF_IDXW 2
+#define I2S_ABUF_OFF_SIZE 4
+#define I2S_ABUF_OFF_ARRAY 8
diff --git a/fw/fe310/eos/interrupt.c b/fw/fe310/eos/interrupt.c
new file mode 100644
index 0000000..820d1fa
--- /dev/null
+++ b/fw/fe310/eos/interrupt.c
@@ -0,0 +1,72 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "encoding.h"
+#include "platform.h"
+#include "plic_driver.h"
+
+#include "interrupt.h"
+
+// Global Instance data for the PLIC
+// for use by the PLIC Driver.
+static plic_instance_t plic;
+
+static eos_intr_handler_t ext_interrupt_handler[PLIC_NUM_INTERRUPTS];
+
+uintptr_t eos_intr_handle(uintptr_t int_num) {
+ if ((int_num >=1) && (int_num <= PLIC_NUM_INTERRUPTS) && (ext_interrupt_handler[int_num-1])) {
+ ext_interrupt_handler[int_num-1]();
+ } else {
+ printf("error:%d\n", int_num);
+ exit(int_num);
+ }
+ return int_num;
+}
+
+void eos_intr_init(void) {
+ for (int i = 0; i < PLIC_NUM_INTERRUPTS; i++){
+ ext_interrupt_handler[i] = NULL;
+ }
+
+ /**************************************************************************
+ * Set up the PLIC
+ **************************************************************************/
+ PLIC_init(&plic,
+ PLIC_CTRL_ADDR,
+ PLIC_NUM_INTERRUPTS,
+ PLIC_NUM_PRIORITIES);
+
+ // Enable Global (PLIC) interrupts.
+ set_csr(mie, MIP_MEIP);
+
+ // Enable all interrupts
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_intr_set(uint8_t int_num, uint8_t priority, eos_intr_handler_t handler) {
+ ext_interrupt_handler[int_num-1] = handler;
+ PLIC_set_priority(&plic, int_num, priority);
+ PLIC_enable_interrupt(&plic, int_num);
+}
+
+void eos_intr_set_handler(uint8_t int_num, eos_intr_handler_t handler) {
+ ext_interrupt_handler[int_num-1] = handler;
+}
+
+void eos_intr_set_priority(uint8_t int_num, uint8_t priority) {
+ PLIC_set_priority(&plic, int_num, priority);
+}
+
+void eos_intr_enable(uint8_t int_num) {
+ PLIC_enable_interrupt(&plic, int_num);
+}
+
+void eos_intr_disable(uint8_t int_num) {
+ PLIC_disable_interrupt(&plic, int_num);
+}
+
+void eos_intr_mask(uint8_t priority) {
+ PLIC_set_threshold(&plic, priority);
+}
diff --git a/fw/fe310/eos/interrupt.h b/fw/fe310/eos/interrupt.h
new file mode 100644
index 0000000..feaf277
--- /dev/null
+++ b/fw/fe310/eos/interrupt.h
@@ -0,0 +1,13 @@
+#include <stdint.h>
+
+#include "irq_def.h"
+
+typedef void (*eos_intr_handler_t) (void);
+
+void eos_intr_init(void);
+void eos_intr_set(uint8_t int_num, uint8_t priority, eos_intr_handler_t handler);
+void eos_intr_set_handler(uint8_t int_num, eos_intr_handler_t handler);
+void eos_intr_set_priority(uint8_t int_num, uint8_t priority);
+void eos_intr_enable(uint8_t int_num);
+void eos_intr_disable(uint8_t int_num);
+void eos_intr_mask(uint8_t priority); \ No newline at end of file
diff --git a/fw/fe310/eos/irq_def.h b/fw/fe310/eos/irq_def.h
new file mode 100644
index 0000000..5d9fb1e
--- /dev/null
+++ b/fw/fe310/eos/irq_def.h
@@ -0,0 +1,11 @@
+#define IRQ_PRIORITY_I2S_SD 7
+#define IRQ_PRIORITY_I2S_WS 6
+
+#define IRQ_PRIORITY_SPI_XCHG 5
+
+#define IRQ_PRIORITY_NET_CTS 4
+#define IRQ_PRIORITY_NET_RTS 4
+
+#define IRQ_PRIORITY_UART 1
+
+#define IRQ_PRIORITY_UI 5
diff --git a/fw/fe310/eos/msgq.c b/fw/fe310/eos/msgq.c
new file mode 100644
index 0000000..a483a58
--- /dev/null
+++ b/fw/fe310/eos/msgq.c
@@ -0,0 +1,122 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "eos.h"
+#include "msgq.h"
+
+#define IDX_MASK(IDX, SIZE) ((IDX) & ((SIZE) - 1))
+#define IDX_HALF ((uint8_t)1 << (sizeof(uint8_t) * 8 - 1))
+#define IDX_LT(a,b) ((uint8_t)((uint8_t)(a) - (uint8_t)(b)) > IDX_HALF)
+#define IDX_LTE(a,b) ((uint8_t)((uint8_t)(b) - (uint8_t)(a)) < IDX_HALF)
+
+void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size) {
+ msgq->idx_r = 0;
+ msgq->idx_w = 0;
+ msgq->size = size;
+ msgq->array = array;
+}
+
+int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len) {
+ if ((uint8_t)(msgq->idx_w - msgq->idx_r) == msgq->size) return EOS_ERR_FULL;
+
+ uint8_t idx = IDX_MASK(msgq->idx_w, msgq->size);
+ msgq->array[idx].type = type;
+ msgq->array[idx].buffer = buffer;
+ msgq->array[idx].len = len;
+ msgq->idx_w++;
+ return EOS_OK;
+}
+
+void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len) {
+ if (msgq->idx_r == msgq->idx_w) {
+ *type = 0;
+ *buffer = NULL;
+ *len = 0;
+ } else {
+ uint8_t idx = IDX_MASK(msgq->idx_r, msgq->size);
+ *type = msgq->array[idx].type;
+ *buffer = msgq->array[idx].buffer;
+ *len = msgq->array[idx].len;
+ msgq->idx_r++;
+ }
+}
+
+int eos_msgq_find(EOSMsgQ *msgq, unsigned char type, unsigned char *selector, uint16_t sel_len, unsigned char **buffer, uint16_t *len) {
+ uint8_t i, j, idx;
+ unsigned char *_buffer;
+ uint16_t _len;
+
+ if (msgq->idx_r == msgq->idx_w) {
+ if (buffer && len) {
+ *buffer = NULL;
+ *len = 0;
+ }
+ return 0;
+ }
+
+ idx = IDX_MASK(msgq->idx_r, msgq->size);
+ if (type == msgq->array[idx].type) {
+ _buffer = msgq->array[idx].buffer;
+ _len = msgq->array[idx].len;
+ if ((selector == NULL) || (sel_len == 0) || ((sel_len <= _len) && (memcmp(selector, _buffer, sel_len) == 0))) {
+ msgq->idx_r++;
+ if (buffer && len) {
+ *buffer = _buffer;
+ *len = _len;
+ }
+ return 1;
+ }
+ }
+ for (i = msgq->idx_r + 1; IDX_LT(i, msgq->idx_w); i++) {
+ idx = IDX_MASK(i, msgq->size);
+ if (type== msgq->array[idx].type) {
+ _buffer = msgq->array[idx].buffer;
+ _len = msgq->array[idx].len;
+ if ((selector == NULL) || (sel_len == 0) || ((sel_len <= _len) && (memcmp(selector, _buffer, sel_len) == 0))) {
+ for (j = i + 1; IDX_LT(j, msgq->idx_w); j++) {
+ msgq->array[IDX_MASK(j - 1, msgq->size)] = msgq->array[IDX_MASK(j, msgq->size)];
+ }
+ msgq->idx_w--;
+ if (buffer && len) {
+ *buffer = _buffer;
+ *len = _len;
+ }
+ return 1;
+ }
+ }
+ }
+ if (buffer && len) {
+ *buffer = NULL;
+ *len = 0;
+ }
+ return 0;
+}
+
+uint8_t eos_msgq_len(EOSMsgQ *msgq) {
+ return (uint8_t)(msgq->idx_w - msgq->idx_r);
+}
+
+void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size) {
+ bufq->idx_r = 0;
+ bufq->idx_w = 0;
+ bufq->size = size;
+ bufq->array = array;
+}
+
+int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer) {
+ if ((uint8_t)(bufq->idx_w - bufq->idx_r) == bufq->size) return EOS_ERR_FULL;
+
+ bufq->array[IDX_MASK(bufq->idx_w++, bufq->size)] = buffer;
+ return EOS_OK;
+}
+
+unsigned char *eos_bufq_pop(EOSBufQ *bufq) {
+ if (bufq->idx_r == bufq->idx_w) return NULL;
+
+ return bufq->array[IDX_MASK(bufq->idx_r++, bufq->size)];
+}
+
+uint8_t eos_bufq_len(EOSBufQ *bufq) {
+ return (uint8_t)(bufq->idx_w - bufq->idx_r);
+}
diff --git a/fw/fe310/eos/msgq.h b/fw/fe310/eos/msgq.h
new file mode 100644
index 0000000..7e3b5e5
--- /dev/null
+++ b/fw/fe310/eos/msgq.h
@@ -0,0 +1,32 @@
+#include <stdint.h>
+
+typedef struct EOSMsgItem {
+ unsigned char type;
+ unsigned char *buffer;
+ uint16_t len;
+} EOSMsgItem;
+
+typedef struct EOSMsgQ {
+ uint8_t idx_r;
+ uint8_t idx_w;
+ uint8_t size;
+ EOSMsgItem *array;
+} EOSMsgQ;
+
+void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size);
+int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len);
+void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len);
+int eos_msgq_find(EOSMsgQ *msgq, unsigned char type, unsigned char *selector, uint16_t sel_len, unsigned char **buffer, uint16_t *len);
+uint8_t eos_msgq_len(EOSMsgQ *msgq);
+
+typedef struct EOSBufQ {
+ uint8_t idx_r;
+ uint8_t idx_w;
+ uint8_t size;
+ unsigned char **array;
+} EOSBufQ;
+
+void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size);
+int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer);
+unsigned char *eos_bufq_pop(EOSBufQ *bufq);
+uint8_t eos_bufq_len(EOSBufQ *bufq);
diff --git a/fw/fe310/eos/msgq_priv.h b/fw/fe310/eos/msgq_priv.h
new file mode 100644
index 0000000..2ad5fc5
--- /dev/null
+++ b/fw/fe310/eos/msgq_priv.h
@@ -0,0 +1,10 @@
+/* asm */
+#define MSGQ_OFF_IDXR 0
+#define MSGQ_OFF_IDXW 1
+#define MSGQ_OFF_SIZE 2
+#define MSGQ_OFF_ARRAY 4
+
+#define MSGQ_ITEM_OFF_TYPE 0
+#define MSGQ_ITEM_OFF_BUF 4
+#define MSGQ_ITEM_OFF_SIZE 8
+#define MSGQ_ITEM_SIZE 12
diff --git a/fw/fe310/eos/net.c b/fw/fe310/eos/net.c
new file mode 100644
index 0000000..d42686d
--- /dev/null
+++ b/fw/fe310/eos/net.c
@@ -0,0 +1,568 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "interrupt.h"
+#include "event.h"
+#include "timer.h"
+#include "power.h"
+
+#include "board.h"
+
+#include "spi.h"
+#include "spi_priv.h"
+#include "spi_dev.h"
+
+#include "net.h"
+
+#define NET_SIZE_HDR 3
+#define NET_STATE_FLAG_RUN 0x01
+#define NET_STATE_FLAG_INIT 0x02
+#define NET_STATE_FLAG_XCHG 0x04
+#define NET_STATE_FLAG_ONEW 0x10
+#define NET_STATE_FLAG_REPW 0x20
+#define NET_STATE_FLAG_RTS 0x40
+#define NET_STATE_FLAG_CTS 0x80
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static EOSBufQ net_buf_q;
+static unsigned char *net_bufq_array[EOS_NET_SIZE_BUFQ];
+static unsigned char net_bufq_buffer[EOS_NET_SIZE_BUFQ][EOS_NET_SIZE_BUF];
+
+static EOSMsgQ net_send_q;
+static EOSMsgItem net_sndq_array[EOS_NET_SIZE_BUFQ];
+
+static volatile uint8_t net_state_flags = 0;
+static unsigned char net_state_type = 0;
+static uint32_t net_state_len_tx = 0;
+static uint32_t net_state_len_rx = 0;
+unsigned char *net_state_buf = NULL;
+
+static uint8_t net_state_next_cnt = 0;
+static unsigned char *net_state_next_buf = NULL;
+
+static eos_evt_handler_t net_handler[EOS_NET_MAX_MTYPE];
+static uint16_t net_wrapper_acq[EOS_EVT_MAX_EVT];
+static uint16_t net_flags_acq[EOS_EVT_MAX_EVT];
+
+static int net_xchg_sleep(void) {
+ int i;
+ int rv = EOS_OK;
+ volatile uint32_t x = 0;
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ SPI1_REG(SPI_REG_TXFIFO) = 0xFF;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ if (x & 0xFF) rv = EOS_ERR_BUSY;
+
+ for (i=0; i<7; i++) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ }
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+
+ return rv;
+}
+
+static void net_xchg_wake(void) {
+ int i;
+ volatile uint32_t x = 0;
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ for (i=0; i<8; i++) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ }
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+static void net_xchg_reset(void) {
+ volatile uint32_t x = 0;
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+
+ SPI1_REG(SPI_REG_TXFIFO) = 0;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+}
+
+static void net_xchg_start(unsigned char type, unsigned char *buffer, uint16_t len) {
+ net_state_flags &= ~NET_STATE_FLAG_CTS;
+ net_state_flags |= (NET_STATE_FLAG_INIT | NET_STATE_FLAG_XCHG);
+
+ if (net_state_next_cnt && (net_state_next_buf == NULL)) type |= EOS_NET_MTYPE_FLAG_ONEW;
+ if (type & EOS_NET_MTYPE_FLAG_ONEW) net_state_flags |= NET_STATE_FLAG_ONEW;
+ if (type & EOS_NET_MTYPE_FLAG_REPW) net_state_flags |= NET_STATE_FLAG_REPW;
+
+ net_state_type = type;
+ net_state_len_tx = len;
+ net_state_len_rx = 0;
+ net_state_buf = buffer;
+
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+ SPI1_REG(SPI_REG_TXFIFO) = type;
+ SPI1_REG(SPI_REG_TXFIFO) = (len >> 8) & 0xFF;
+ SPI1_REG(SPI_REG_TXFIFO) = (len & 0xFF);
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(2);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_RXWM;
+}
+
+static int net_xchg_next(unsigned char *_buffer) {
+ unsigned char type;
+ unsigned char *buffer = NULL;
+ uint16_t len;
+ int ret = _buffer ? 1 : 0;
+
+ eos_msgq_pop(&net_send_q, &type, &buffer, &len);
+ if (type) {
+ net_xchg_start(type, buffer, len);
+ } else if (net_state_flags & NET_STATE_FLAG_RTS) {
+ if (_buffer) {
+ buffer = _buffer;
+ ret = 0;
+ } else {
+ buffer = eos_bufq_pop(&net_buf_q);
+ }
+ if (buffer) net_xchg_start(0, buffer, 0);
+ }
+
+ return ret;
+}
+
+static void net_handle_xchg(void) {
+ volatile uint32_t r1, r2, r3;
+ uint32_t len;
+
+ if (net_state_flags & NET_STATE_FLAG_INIT) {
+ net_state_flags &= ~NET_STATE_FLAG_INIT;
+
+ r1 = SPI1_REG(SPI_REG_RXFIFO);
+ r2 = SPI1_REG(SPI_REG_RXFIFO);
+ r3 = SPI1_REG(SPI_REG_RXFIFO);
+
+ if (net_state_flags & (NET_STATE_FLAG_ONEW | NET_STATE_FLAG_REPW)) {
+ r1 = 0;
+ r2 = 0;
+ r3 = 0;
+ }
+
+ net_state_type = (r1 & 0xFF);
+ net_state_len_rx = (r2 & 0xFF) << 8;
+ net_state_len_rx |= (r3 & 0xFF);
+ len = MAX(net_state_len_tx, net_state_len_rx);
+
+ if (len > EOS_NET_MTU) {
+ net_state_flags &= ~NET_STATE_FLAG_XCHG;
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ SPI1_REG(SPI_REG_IE) = 0x0;
+ return;
+ }
+
+ // esp32 dma workaraund
+ if (len < 8 - NET_SIZE_HDR) {
+ len = 8 - NET_SIZE_HDR;
+ } else if ((len + NET_SIZE_HDR) % 4 != 0) {
+ len = ((len + NET_SIZE_HDR)/4 + 1) * 4 - NET_SIZE_HDR;
+ }
+
+ _eos_spi_xchg_init(net_state_buf, len, 0);
+ SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(SPI_SIZE_WM);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_TXWM;
+ return;
+ }
+
+ eos_spi_handle_xchg();
+ if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_AUTO) { // exchange done
+ if (net_state_flags & NET_STATE_FLAG_REPW) {
+ net_state_flags &= ~NET_STATE_FLAG_REPW;
+ } else {
+ if (net_state_type) {
+ int r = eos_evtq_push_isr(EOS_EVT_NET | net_state_type, net_state_buf, net_state_len_rx);
+ if (r) eos_bufq_push(&net_buf_q, net_state_buf);
+ } else if (((net_state_flags & NET_STATE_FLAG_ONEW) || net_state_next_cnt) && (net_state_next_buf == NULL)) {
+ net_state_next_buf = net_state_buf;
+ net_state_flags &= ~NET_STATE_FLAG_ONEW;
+ } else {
+ eos_bufq_push(&net_buf_q, net_state_buf);
+ }
+ }
+ net_state_flags &= ~NET_STATE_FLAG_XCHG;
+ }
+}
+
+static void net_handle_cts(void) {
+ GPIO_REG(GPIO_RISE_IP) = (1 << NET_PIN_CTS);
+ net_state_flags |= NET_STATE_FLAG_CTS;
+
+ if (net_state_flags & NET_STATE_FLAG_RUN) {
+ net_xchg_next(NULL);
+ }
+}
+
+static void net_handle_rts(void) {
+ uint32_t rts_offset = (1 << NET_PIN_RTS);
+ if (GPIO_REG(GPIO_RISE_IP) & rts_offset) {
+ GPIO_REG(GPIO_RISE_IP) = rts_offset;
+ net_state_flags |= NET_STATE_FLAG_RTS;
+ if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) {
+ net_xchg_reset();
+ }
+ } else if (GPIO_REG(GPIO_FALL_IP) & rts_offset) {
+ GPIO_REG(GPIO_FALL_IP) = rts_offset;
+ net_state_flags &= ~NET_STATE_FLAG_RTS;
+ }
+}
+
+static void net_handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & ~EOS_EVT_MASK) - 1;
+
+ if (idx < EOS_NET_MAX_MTYPE) {
+ net_handler[idx](type, buffer, len);
+ } else {
+ eos_net_bad_handler(type, buffer, len);
+ }
+}
+
+static int net_acquire(unsigned char reserved) {
+ int ret = 0;
+
+ if (reserved) {
+ while (!ret) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_next_buf) {
+ ret = 1;
+ net_state_next_cnt--;
+ } else {
+ asm volatile ("wfi");
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ } else {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_next_buf == NULL) net_state_next_buf = eos_bufq_pop(&net_buf_q);
+ ret = (net_state_next_buf != NULL);
+ if (!ret) net_state_next_cnt++;
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ return ret;
+}
+
+static void evt_handler_wrapper(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char idx, uint16_t flag) {
+ int ok;
+
+ ok = net_acquire(net_wrapper_acq[idx] & flag);
+ if (ok) {
+ eos_evtq_get_handler(type)(type, buffer, len);
+ eos_net_release();
+ net_wrapper_acq[idx] &= ~flag;
+ } else {
+ net_wrapper_acq[idx] |= flag;
+ eos_evtq_push(type, buffer, len);
+ }
+}
+
+static void evt_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+
+ if (idx && (idx <= EOS_EVT_MAX_EVT)) {
+ uint16_t flag = (uint16_t)1 << (type & ~EOS_EVT_MASK);
+
+ idx--;
+ if (flag & net_flags_acq[idx]) {
+ evt_handler_wrapper(type, buffer, len, idx, flag);
+ } else {
+ eos_evtq_get_handler(type)(type, buffer, len);
+ }
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+static void net_pause(void) {
+ net_state_flags &= ~NET_STATE_FLAG_RUN;
+}
+
+static void net_resume(void) {
+ net_state_flags |= NET_STATE_FLAG_RUN;
+ if (net_state_flags & NET_STATE_FLAG_CTS) {
+ net_xchg_next(NULL);
+ }
+}
+
+void eos_net_init(void) {
+ int i;
+
+ eos_msgq_init(&net_send_q, net_sndq_array, EOS_NET_SIZE_BUFQ);
+ eos_bufq_init(&net_buf_q, net_bufq_array, EOS_NET_SIZE_BUFQ);
+ for (i=0; i<EOS_NET_SIZE_BUFQ; i++) {
+ eos_bufq_push(&net_buf_q, net_bufq_buffer[i]);
+ }
+
+ for (i=0; i<EOS_NET_MAX_MTYPE; i++) {
+ net_handler[i] = eos_net_bad_handler;
+ }
+ eos_evtq_set_handler(0, evt_handler);
+ eos_evtq_set_handler(EOS_EVT_NET, net_handle_evt);
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << NET_PIN_CTS);
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << NET_PIN_CTS);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << NET_PIN_CTS);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << NET_PIN_CTS);
+
+ GPIO_REG(GPIO_RISE_IE) |= (1 << NET_PIN_CTS);
+ eos_intr_set(INT_GPIO_BASE + NET_PIN_CTS, IRQ_PRIORITY_NET_CTS, net_handle_cts);
+
+ GPIO_REG(GPIO_INPUT_EN) |= (1 << NET_PIN_RTS);
+ GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << NET_PIN_RTS);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << NET_PIN_RTS);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << NET_PIN_RTS);
+
+ GPIO_REG(GPIO_RISE_IE) |= (1 << NET_PIN_RTS);
+ GPIO_REG(GPIO_FALL_IE) |= (1 << NET_PIN_RTS);
+ eos_intr_set(INT_GPIO_BASE + NET_PIN_RTS, IRQ_PRIORITY_NET_RTS, net_handle_rts);
+
+ /* set initial state */
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (GPIO_REG(GPIO_INPUT_VAL) & (1 << NET_PIN_CTS)) net_state_flags |= NET_STATE_FLAG_CTS;
+ if (GPIO_REG(GPIO_INPUT_VAL) & (1 << NET_PIN_RTS)) net_state_flags |= NET_STATE_FLAG_RTS;
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_net_start(uint8_t wakeup_cause) {
+ eos_intr_set_handler(INT_SPI1_BASE, net_handle_xchg);
+ SPI1_REG(SPI_REG_SCKDIV) = eos_spi_div(EOS_SPI_DEV_NET);
+ SPI1_REG(SPI_REG_CSID) = eos_spi_csid(EOS_SPI_DEV_NET);
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (wakeup_cause) {
+ if (wakeup_cause != EOS_PWR_WAKE_BTN) {
+ net_xchg_wake();
+ }
+ if (!(net_state_flags & NET_STATE_FLAG_CTS)) {
+ while (!(GPIO_REG(GPIO_RISE_IP) & (1 << NET_PIN_CTS))) {
+ asm volatile ("wfi");
+ }
+ GPIO_REG(GPIO_RISE_IP) = (1 << NET_PIN_CTS);
+ }
+ net_xchg_reset();
+ }
+ net_resume();
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+void eos_net_stop(void) {
+ uint8_t done = 0;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_flags & NET_STATE_FLAG_RUN) {
+ net_state_flags &= ~NET_STATE_FLAG_RUN;
+ done = !(net_state_flags & NET_STATE_FLAG_XCHG);
+ } else {
+ done = 1;
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+
+ while (!done) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ done = !(net_state_flags & NET_STATE_FLAG_XCHG);
+ if (!done) asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+}
+
+int eos_net_sleep(uint32_t timeout) {
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ uint64_t then_ms = timeout + *mtime * 1000 / EOS_TIMER_RTC_FREQ;
+ uint8_t done = 0;
+ int rv = EOS_OK;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (!(net_state_flags & NET_STATE_FLAG_RUN)) rv = EOS_ERR;
+ set_csr(mstatus, MSTATUS_MIE);
+
+ if (rv) return rv;
+
+ do {
+ if (*mtime * 1000 / EOS_TIMER_RTC_FREQ > then_ms) return EOS_ERR_TIMEOUT;
+ clear_csr(mstatus, MSTATUS_MIE);
+ eos_evtq_flush_isr();
+ done = (eos_msgq_len(&net_send_q) == 0);
+ done = done && (!(net_state_flags & NET_STATE_FLAG_RTS) && (net_state_flags & NET_STATE_FLAG_CTS));
+ if (done) done = (net_xchg_sleep() == EOS_OK);
+ if (!done) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ } while (!done);
+
+ while (!(GPIO_REG(GPIO_RISE_IP) & (1 << NET_PIN_CTS))) {
+ if (*mtime * 1000 / EOS_TIMER_RTC_FREQ > then_ms) {
+ rv = EOS_ERR_TIMEOUT;
+ break;
+ }
+ asm volatile ("wfi");
+ }
+
+ if (!rv) {
+ GPIO_REG(GPIO_RISE_IP) = (1 << NET_PIN_CTS);
+ net_state_flags &= ~NET_STATE_FLAG_RUN;
+ }
+
+ set_csr(mstatus, MSTATUS_MIE);
+
+ return rv;
+}
+
+void eos_net_bad_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ eos_evtq_bad_handler(type, buffer, len);
+ if (buffer) eos_net_free(buffer, 0);
+}
+
+void eos_net_set_handler(unsigned char mtype, eos_evt_handler_t handler) {
+ if (handler == NULL) handler = eos_net_bad_handler;
+ if (mtype && (mtype <= EOS_NET_MAX_MTYPE)) net_handler[mtype - 1] = handler;
+}
+
+void eos_net_acquire_for_evt(unsigned char type, char acq) {
+ unsigned char idx = (type & EOS_EVT_MASK) >> 4;
+ uint16_t flag = type & ~EOS_EVT_MASK ? (uint16_t)1 << (type & ~EOS_EVT_MASK) : 0xFFFF;
+
+ if (idx && (idx <= EOS_EVT_MAX_EVT)) {
+ idx--;
+ net_flags_acq[idx] &= ~flag;
+ if (acq) net_flags_acq[idx] |= flag;
+ }
+}
+
+void eos_net_acquire(void) {
+ unsigned char acq = net_acquire(0);
+ if (!acq) net_acquire(1);
+}
+
+void eos_net_release(void) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (!net_state_next_cnt && net_state_next_buf) {
+ eos_bufq_push(&net_buf_q, net_state_next_buf);
+ net_state_next_buf = NULL;
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+unsigned char *eos_net_alloc(void) {
+ unsigned char *ret = NULL;
+
+ while (!ret) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ if (net_state_next_buf) {
+ ret = net_state_next_buf;
+ net_state_next_buf = NULL;
+ } else {
+ asm volatile ("wfi");
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+
+ return ret;
+}
+
+void eos_net_free(unsigned char *buffer, unsigned char more) {
+ uint8_t do_release = 1;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if ((more || net_state_next_cnt) && (net_state_next_buf == NULL)) {
+ net_state_next_buf = buffer;
+ } else {
+ if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) {
+ do_release = net_xchg_next(buffer);
+ }
+ if (do_release) {
+ eos_bufq_push(&net_buf_q, buffer);
+ }
+ }
+ set_csr(mstatus, MSTATUS_MIE);
+}
+
+static int net_xchg(unsigned char *type, unsigned char *buffer, uint16_t *len) {
+ int rv = EOS_OK;
+ int _sync = 0;
+ unsigned char _type = *type;
+ uint16_t _len = *len;
+ uint8_t spi_dev = EOS_SPI_DEV_NET;
+
+ clear_csr(mstatus, MSTATUS_MIE);
+ if ((_type & EOS_NET_MTYPE_FLAG_REPW) || ((_type & EOS_NET_MTYPE_FLAG_ONEW) && !(net_state_flags & NET_STATE_FLAG_RUN))) _sync = 1;
+
+ if (!(net_state_flags & NET_STATE_FLAG_RUN) && _sync) {
+ int _rv;
+
+ set_csr(mstatus, MSTATUS_MIE);
+ spi_dev = eos_spi_dev();
+ _rv = eos_spi_deselect();
+ if (_rv) return _rv;
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+
+ if (_sync) {
+ net_pause();
+ while (!(net_state_flags & NET_STATE_FLAG_CTS)) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ net_xchg_start(_type, buffer, _len);
+ if (_type & EOS_NET_MTYPE_FLAG_REPW) {
+ while (!(net_state_flags & NET_STATE_FLAG_CTS)) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ net_xchg_start(0, buffer, 0);
+ while (net_state_flags & NET_STATE_FLAG_XCHG) {
+ asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ clear_csr(mstatus, MSTATUS_MIE);
+ }
+ *type = net_state_type;
+ *len = net_state_len_rx;
+ }
+ net_resume();
+ } else {
+ if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) {
+ net_xchg_start(_type, buffer, _len);
+ } else {
+ rv = eos_msgq_push(&net_send_q, _type, buffer, _len);
+ if (rv) eos_bufq_push(&net_buf_q, buffer);
+ }
+ }
+
+ set_csr(mstatus, MSTATUS_MIE);
+ if (spi_dev != EOS_SPI_DEV_NET) eos_spi_select(spi_dev);
+
+ return rv;
+}
+
+int eos_net_send(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char more) {
+ if (more) type |= EOS_NET_MTYPE_FLAG_ONEW;
+ return net_xchg(&type, buffer, &len);
+}
+
+int eos_net_xchg(unsigned char *type, unsigned char *buffer, uint16_t *len) {
+ *type |= EOS_NET_MTYPE_FLAG_REPW;
+ return net_xchg(type, buffer, len);
+}
diff --git a/fw/fe310/eos/net.h b/fw/fe310/eos/net.h
new file mode 100644
index 0000000..574d179
--- /dev/null
+++ b/fw/fe310/eos/net.h
@@ -0,0 +1,39 @@
+#include <stdint.h>
+#include "event.h"
+
+/* common */
+#define EOS_NET_MTU 1500
+#define EOS_NET_SIZE_BUF (EOS_NET_MTU + 4)
+
+#define EOS_NET_MTYPE_SOCK 1
+#define EOS_NET_MTYPE_POWER 4
+
+#define EOS_NET_MTYPE_WIFI 5
+#define EOS_NET_MTYPE_CELL 6
+#define EOS_NET_MTYPE_SIP 7
+#define EOS_NET_MTYPE_APP 8
+
+#define EOS_NET_MAX_MTYPE 8
+
+#define EOS_NET_MTYPE_FLAG_ONEW 0x40
+#define EOS_NET_MTYPE_FLAG_REPW 0x80
+#define EOS_NET_MTYPE_FLAG_MASK 0xc0
+
+/* fe310 specific */
+#define EOS_NET_SIZE_BUFQ 2
+
+void eos_net_init(void);
+void eos_net_start(uint8_t wakeup_cause);
+void eos_net_stop(void);
+int eos_net_sleep(uint32_t timeout);
+
+void eos_net_bad_handler(unsigned char type, unsigned char *buffer, uint16_t len);
+void eos_net_set_handler(unsigned char type, eos_evt_handler_t handler);
+void eos_net_acquire_for_evt(unsigned char type, char acq);
+
+void eos_net_acquire(void);
+void eos_net_release(void);
+unsigned char *eos_net_alloc(void);
+void eos_net_free(unsigned char *buffer, unsigned char more);
+int eos_net_send(unsigned char type, unsigned char *buffer, uint16_t len, unsigned char more);
+int eos_net_xchg(unsigned char *type, unsigned char *buffer, uint16_t *len);
diff --git a/fw/fe310/eos/power.c b/fw/fe310/eos/power.c
new file mode 100644
index 0000000..5f44a94
--- /dev/null
+++ b/fw/fe310/eos/power.c
@@ -0,0 +1,119 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "event.h"
+#include "timer.h"
+#include "spi.h"
+#include "spi_dev.h"
+#include "net.h"
+#include "eve/eve.h"
+
+#include "power.h"
+
+#define PWR_RTC_SCALE 15
+#define PWR_RTC_SFREQ (EOS_TIMER_RTC_FREQ >> PWR_RTC_SCALE)
+
+static eos_evt_handler_t evt_handler[EOS_PWR_MAX_MTYPE];
+static unsigned char power_btn_down;
+
+static void power_handle_msg(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char mtype;
+
+ if ((buffer == NULL) || (len < 1)) {
+ eos_net_bad_handler(type, buffer, len);
+ return;
+ }
+
+ mtype = buffer[0];
+ if ((mtype < EOS_PWR_MAX_MTYPE) && evt_handler[mtype]) {
+ evt_handler[mtype](mtype, buffer, len);
+ } else {
+ eos_net_bad_handler(type, buffer, len);
+ }
+}
+
+static void power_handle_btn(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char level = buffer[1];
+
+ eos_net_free(buffer, 0);
+ if (!level) {
+ power_btn_down = 1;
+ return;
+ }
+ if (!power_btn_down) return;
+
+ eos_power_sleep();
+}
+
+void eos_power_init(void) {
+ int i;
+
+ for (i=0; i<EOS_PWR_MAX_MTYPE; i++) {
+ evt_handler[i] = NULL;
+ }
+ eos_net_set_handler(EOS_NET_MTYPE_POWER, power_handle_msg);
+ eos_power_set_handler(EOS_PWR_MTYPE_BUTTON, power_handle_btn);
+
+ AON_REG(AON_PMUKEY) = 0x51F15E;
+ AON_REG(AON_PMUIE) = 0x5;
+
+ AON_REG(AON_RTCCMP) = 0xFFFFFFFF;
+ AON_REG(AON_RTCCFG) = PWR_RTC_SCALE;
+ AON_REG(AON_RTCHI) = 0;
+ AON_REG(AON_RTCLO) = 0;
+}
+
+uint8_t eos_power_wakeup_cause(void) {
+ return AON_REG(AON_PMUCAUSE) & 0xff;
+}
+
+uint8_t eos_power_reset_cause(void) {
+ return (AON_REG(AON_PMUCAUSE) >> 8) & 0xff;
+}
+
+void eos_power_sleep(void) {
+ eos_spi_select(EOS_SPI_DEV_EVE);
+ eve_sleep();
+ eos_spi_deselect();
+ eos_net_sleep(1000);
+
+ AON_REG(AON_PMUKEY) = 0x51F15E;
+ AON_REG(AON_PMUSLEEP) = 1;
+}
+
+void eos_power_wake_at(uint32_t msec) {
+ uint32_t pmuie;
+
+ AON_REG(AON_RTCCFG) |= AON_RTCCFG_ENALWAYS;
+ AON_REG(AON_RTCCMP) = msec * PWR_RTC_SFREQ / 1000;
+
+ pmuie = AON_REG(AON_PMUIE) | 0x2;
+ AON_REG(AON_PMUKEY) = 0x51F15E;
+ AON_REG(AON_PMUIE) = pmuie;
+}
+
+void eos_power_wake_disable(void) {
+ uint32_t pmuie;
+
+ AON_REG(AON_RTCCMP) = 0xFFFFFFFF;
+ AON_REG(AON_RTCCFG) &= ~AON_RTCCFG_ENALWAYS;
+ AON_REG(AON_RTCHI) = 0;
+ AON_REG(AON_RTCLO) = 0;
+
+ pmuie = AON_REG(AON_PMUIE) & ~0x2;
+ AON_REG(AON_PMUKEY) = 0x51F15E;
+ AON_REG(AON_PMUIE) = pmuie;
+}
+
+void eos_power_set_handler(unsigned char mtype, eos_evt_handler_t handler) {
+ if (mtype < EOS_PWR_MAX_MTYPE) evt_handler[mtype] = handler;
+}
+
+eos_evt_handler_t eos_power_get_handler(unsigned char mtype) {
+ if (mtype < EOS_PWR_MAX_MTYPE) return evt_handler[mtype];
+ return NULL;
+}
diff --git a/fw/fe310/eos/power.h b/fw/fe310/eos/power.h
new file mode 100644
index 0000000..3eee817
--- /dev/null
+++ b/fw/fe310/eos/power.h
@@ -0,0 +1,23 @@
+#include <stdint.h>
+#include "event.h"
+
+#define EOS_PWR_MTYPE_BUTTON 1
+
+#define EOS_PWR_MAX_MTYPE 2
+
+#define EOS_PWR_WAKE_RST 0
+#define EOS_PWR_WAKE_RTC 1
+#define EOS_PWR_WAKE_BTN 2
+
+#define EOS_PWR_RST_PWRON 0
+#define EOS_PWR_RST_EXT 1
+#define EOS_PWR_RST_WDOG 2
+
+void eos_power_init(void);
+uint8_t eos_power_wakeup_cause(void);
+uint8_t eos_power_reset_cause(void);
+void eos_power_sleep(void);
+void eos_power_wake_at(uint32_t msec);
+void eos_power_wake_disable(void);
+void eos_power_set_handler(unsigned char mtype, eos_evt_handler_t handler);
+eos_evt_handler_t eos_power_get_handler(unsigned char mtype); \ No newline at end of file
diff --git a/fw/fe310/eos/sdc_crypto.c b/fw/fe310/eos/sdc_crypto.c
new file mode 100644
index 0000000..c094468
--- /dev/null
+++ b/fw/fe310/eos/sdc_crypto.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "sha/sha1.h"
+#include "sdc_crypto.h"
+
+#define SDC_CRYPTO_KEY_SIZE 16
+#define SDC_CRYPTO_BLK_SIZE 16
+#define SDC_CRYPTO_HASH_SIZE 20
+
+EOSSDCCrypto *sdc_crypto;
+
+void eos_sdcc_init(EOSSDCCrypto *crypto, uint8_t *key, void *ctx, eve_sdcc_init_t init, eve_sdcc_crypt_t enc, eve_sdcc_crypt_t dec, void *ctx_essiv, eve_sdcc_init_t init_essiv, eve_sdcc_essiv_t enc_essiv) {
+ char key_essiv[SDC_CRYPTO_HASH_SIZE];
+
+ sdc_crypto = crypto;
+
+ memset(key_essiv, 0, SDC_CRYPTO_HASH_SIZE);
+ init(ctx, key);
+ sdc_crypto->ctx = ctx;
+ sdc_crypto->enc = enc;
+ sdc_crypto->dec = dec;
+
+ SHA1(key_essiv, key, SDC_CRYPTO_KEY_SIZE);
+ init_essiv(ctx_essiv, key_essiv);
+ sdc_crypto->ctx_essiv = ctx_essiv;
+ sdc_crypto->enc_essiv = enc_essiv;
+}
+
+void eos_sdcc_encrypt(uint32_t sect, uint8_t *buffer) {
+ uint8_t iv[SDC_CRYPTO_BLK_SIZE];
+
+ if (sdc_crypto == NULL) return;
+
+ memset(iv, 0, SDC_CRYPTO_BLK_SIZE);
+ memcpy(iv, &sect, sizeof(sect));
+ sdc_crypto->enc_essiv(sdc_crypto->ctx_essiv, iv);
+ sdc_crypto->enc(sdc_crypto->ctx, iv, buffer, 512);
+}
+
+void eos_sdcc_decrypt(uint32_t sect, uint8_t *buffer) {
+ uint8_t iv[SDC_CRYPTO_BLK_SIZE];
+
+ if (sdc_crypto == NULL) return;
+
+ memset(iv, 0, SDC_CRYPTO_BLK_SIZE);
+ memcpy(iv, &sect, sizeof(sect));
+ sdc_crypto->enc_essiv(sdc_crypto->ctx_essiv, iv);
+ sdc_crypto->dec(sdc_crypto->ctx, iv, buffer, 512);
+} \ No newline at end of file
diff --git a/fw/fe310/eos/sdc_crypto.h b/fw/fe310/eos/sdc_crypto.h
new file mode 100644
index 0000000..015bf8a
--- /dev/null
+++ b/fw/fe310/eos/sdc_crypto.h
@@ -0,0 +1,18 @@
+#include <stddef.h>
+#include <stdint.h>
+
+typedef void (*eve_sdcc_init_t) (void *, uint8_t *);
+typedef void (*eve_sdcc_crypt_t) (void *, uint8_t *, uint8_t *, size_t);
+typedef void (*eve_sdcc_essiv_t) (void *, uint8_t *);
+
+typedef struct EOSSDCCrypto {
+ void *ctx;
+ eve_sdcc_crypt_t enc;
+ eve_sdcc_crypt_t dec;
+ void *ctx_essiv;
+ eve_sdcc_essiv_t enc_essiv;
+} EOSSDCCrypto;
+
+void eos_sdcc_init(EOSSDCCrypto *crypto, uint8_t *key, void *ctx, eve_sdcc_init_t init, eve_sdcc_crypt_t enc, eve_sdcc_crypt_t dec, void *ctx_essiv, eve_sdcc_init_t init_essiv, eve_sdcc_essiv_t enc_essiv);
+void eos_sdcc_encrypt(uint32_t sect, uint8_t *buffer);
+void eos_sdcc_decrypt(uint32_t sect, uint8_t *buffer); \ No newline at end of file
diff --git a/fw/fe310/eos/sdcard.c b/fw/fe310/eos/sdcard.c
new file mode 100644
index 0000000..970ab17
--- /dev/null
+++ b/fw/fe310/eos/sdcard.c
@@ -0,0 +1,541 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "eos.h"
+#include "timer.h"
+
+#include "spi.h"
+#include "spi_dev.h"
+
+#include "sdc_crypto.h"
+#include "sdcard.h"
+
+#define SDC_TIMEOUT_CMD 500
+#define SDC_TIMEOUT_READ 200
+#define SDC_TIMEOUT_WRITE 500
+
+#define SDC_POLY_CRC7 0x09
+#define SDC_POLY_CRC16 0x1021
+
+#define SDC_CMD_FLAG_CRC 0x01
+#define SDC_CMD_FLAG_NOCS 0x02
+#define SDC_CMD_FLAG_NOWAIT 0x04
+#define SDC_CMD_FLAG_RSTUFF 0x08
+
+#define SDC_TOKEN_START_BLK 0xfe
+#define SDC_TOKEN_START_BLKM 0xfc
+#define SDC_TOKEN_STOP_TRAN 0xfd
+
+#define SDC_DRESP_MASK 0x1f
+#define SDC_DRESP_ACCEPT 0x05
+#define SDC_DRESP_ERR_CRC 0x0b
+#define SDC_DRESP_ERR_WRITE 0x0d
+
+#define SDC_R1_READY 0x00
+
+#define SDC_R1_IDLE_STATE 0x01
+#define SDC_R1_ERASE_RESET 0x02
+#define SDC_R1_ILLEGAL_CMD 0x04
+#define SDC_R1_ERR_CMD_CRC 0x08
+#define SDC_R1_ERR_ERASE_SEQ 0x10
+#define SDC_R1_ERR_ADDR 0x20
+#define SDC_R1_ERR_PARAM 0x40
+
+#define SDC_NCR 10
+
+#define SDC_ERR(rv) ((rv < 0) ? rv : EOS_ERR)
+
+/* CMD */
+#define GO_IDLE_STATE 0
+#define SEND_OP_COND 1
+#define SEND_IF_COND 8
+#define SEND_CSD 9
+#define STOP_TRANSMISSION 12
+#define SET_BLOCKLEN 16
+#define READ_SINGLE_BLOCK 17
+#define READ_MULTIPLE_BLOCK 18
+#define WRITE_BLOCK 24
+#define WRITE_MULTIPLE_BLOCK 25
+#define ERASE_WR_BLK_START 32
+#define ERASE_WR_BLK_END 33
+#define ERASE 38
+#define APP_CMD 55
+#define READ_OCR 58
+
+/* ACMD */
+#define SD_STATUS 13
+#define SET_WR_BLK_ERASE_COUNT 23
+#define SD_APP_OP_COND 41
+
+static uint8_t sdc_type = 0;
+
+static uint8_t sdc_crc7(uint8_t crc, uint8_t b) {
+ int i;
+
+ for (i=8; i--; b<<=1) {
+ crc <<= 1;
+ if ((b ^ crc) & 0x80) crc ^= SDC_POLY_CRC7;
+ }
+ return crc & 0x7f;
+}
+
+static uint16_t sdc_crc16(uint16_t crc, uint8_t b) {
+ int i;
+
+ crc = crc ^ ((uint16_t)b << 8);
+ for (i=8; i--;) {
+ if (crc & 0x8000) {
+ crc = (crc << 1) ^ SDC_POLY_CRC16;
+ } else {
+ crc <<= 1;
+ }
+ }
+ return crc;
+}
+
+static uint32_t sdc_nto(uint64_t start, uint32_t timeout) {
+ uint32_t d = eos_time_since(start);
+ return (d > timeout) ? 0 : timeout - d;
+}
+
+static uint8_t sdc_xchg8(uint8_t data) {
+ return eos_spi_xchg8(data, 0);
+}
+
+static uint16_t sdc_xchg16(uint16_t data) {
+ return eos_spi_xchg16(data, 0);
+}
+
+static uint32_t sdc_xchg32(uint32_t data) {
+ return eos_spi_xchg32(data, 0);
+}
+
+static void sdc_buf_send(unsigned char *buffer, uint16_t len) {
+ int i;
+
+ for (i=0; i<len; i++) {
+ sdc_xchg8(buffer[i]);
+ }
+}
+
+static void sdc_buf_recv(unsigned char *buffer, uint16_t len) {
+ int i;
+
+ for (i=0; i<len; i++) {
+ buffer[i] = sdc_xchg8(0xff);
+ }
+}
+
+static void sdc_select(void) {
+ eos_spi_cs_set();
+ eos_spi_xchg8(0xff, 0);
+}
+
+static void sdc_deselect(void) {
+ eos_spi_cs_clear();
+ eos_spi_xchg8(0xff, 0);
+}
+
+static int sdc_xchg_cmd(uint8_t cmd, uint32_t arg, uint8_t flags) {
+ int i;
+ uint8_t ret;
+ uint8_t crc = 0x7f;
+
+ cmd |= 0x40;
+ if (flags & SDC_CMD_FLAG_CRC) {
+ crc = sdc_crc7(0, cmd);
+ crc = sdc_crc7(crc, arg >> 24);
+ crc = sdc_crc7(crc, arg >> 16);
+ crc = sdc_crc7(crc, arg >> 8);
+ crc = sdc_crc7(crc, arg);
+ }
+ crc = (crc << 1) | 0x01;
+ sdc_xchg8(cmd);
+ sdc_xchg32(arg);
+ sdc_xchg8(crc);
+ if (flags & SDC_CMD_FLAG_RSTUFF) sdc_xchg8(0xff);
+
+ i = SDC_NCR;
+ do {
+ ret = sdc_xchg8(0xff);
+ } while ((ret & 0x80) && --i);
+ if (ret & 0x80) return EOS_ERR_BUSY;
+
+ return ret;
+}
+
+static int sdc_ready(uint32_t timeout) {
+ uint8_t d = 0;
+ uint64_t start;
+
+ if (timeout == 0) return EOS_ERR_BUSY;
+ start = eos_time_get_tick();
+ do {
+ if (eos_time_since(start) > timeout) break;
+ d = sdc_xchg8(0xff);
+ } while (d != 0xff);
+ if (d != 0xff) return EOS_ERR_BUSY;
+
+ return EOS_OK;
+}
+
+static int sdc_block_read(uint8_t *buffer, uint16_t len, uint32_t timeout) {
+ uint8_t token = 0xff;
+ uint64_t start;
+
+ if (timeout == 0) return EOS_ERR_BUSY;
+ start = eos_time_get_tick();
+ do {
+ if (eos_time_since(start) > timeout) break;
+ token = sdc_xchg8(0xff);
+ } while (token == 0xff);
+ if (token == 0xff) return EOS_ERR_BUSY;
+ if (token != SDC_TOKEN_START_BLK) return EOS_ERR;
+
+ sdc_buf_recv(buffer, len);
+ sdc_xchg16(0xffff); /* dummy CRC */
+
+ return EOS_OK;
+}
+
+static int sdc_block_write(uint8_t token, uint8_t *buffer, uint16_t len, uint32_t timeout) {
+ uint8_t d;
+ int rv;
+
+ rv = sdc_ready(timeout);
+ if (rv) return rv;
+
+ sdc_xchg8(token);
+ if (buffer && len) {
+ sdc_buf_send(buffer, len);
+ sdc_xchg16(0xffff); /* dummy CRC */
+
+ d = sdc_xchg8(0xff); /* Response */
+ if ((d & SDC_DRESP_MASK) != SDC_DRESP_ACCEPT) return EOS_ERR;
+ }
+
+ return EOS_OK;
+}
+
+static int sdc_cmd(uint8_t cmd, uint32_t arg, uint8_t flags, uint32_t timeout) {
+ int do_cs = !(flags & SDC_CMD_FLAG_NOCS);
+ int do_wait = !(flags & SDC_CMD_FLAG_NOWAIT);
+ int rv = EOS_OK;
+
+ if (do_cs) sdc_select();
+ if (do_wait) rv = sdc_ready(timeout);
+ if (rv) {
+ if (do_cs) sdc_deselect();
+ return rv;
+ }
+ rv = sdc_xchg_cmd(cmd, arg, flags);
+ if (do_cs) sdc_deselect();
+ return rv;
+}
+
+static int sdc_acmd(uint8_t cmd, uint32_t arg, uint8_t flags, uint32_t timeout) {
+ int rv;
+ uint64_t start;
+
+ start = eos_time_get_tick();
+ rv = sdc_cmd(APP_CMD, 0, flags, timeout);
+ if (rv & ~SDC_R1_IDLE_STATE) return rv;
+
+ if (flags & SDC_CMD_FLAG_NOCS) {
+ sdc_deselect();
+ sdc_select();
+ }
+ return sdc_cmd(cmd, arg, flags, sdc_nto(start, timeout));
+}
+
+static int sdc_init(uint32_t timeout) {
+ int rv, i;
+ uint8_t _type;
+ uint8_t ocr[4];
+ uint64_t start;
+ start = eos_time_get_tick();
+
+ eos_time_sleep(100);
+ for (i=10; i--;) sdc_xchg8(0xff); /* 80 dummy cycles */
+
+ rv = sdc_cmd(GO_IDLE_STATE, 0, SDC_CMD_FLAG_CRC, SDC_TIMEOUT_CMD);
+ if (rv != SDC_R1_IDLE_STATE) return SDC_ERR(rv);
+
+ sdc_select();
+ rv = sdc_cmd(SEND_IF_COND, 0x1aa, SDC_CMD_FLAG_CRC | SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout));
+ switch (rv) {
+ case SDC_R1_IDLE_STATE:
+ for (i=0; i<4; i++) { /* R7 response */
+ ocr[i] = sdc_xchg8(0xff);
+ }
+ sdc_deselect();
+ if (ocr[2] == 0x01 && ocr[3] == 0xaa) { /* Check voltage range: 2.7-3.6V */
+ do {
+ rv = sdc_acmd(SD_APP_OP_COND, 0x40000000, 0, sdc_nto(start, timeout));
+ } while (rv == SDC_R1_IDLE_STATE);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ sdc_select();
+ rv = sdc_cmd(READ_OCR, 0, SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout));
+ if (rv == SDC_R1_READY) {
+ for (i=0; i<4; i++) { /* R7 response */
+ ocr[i] = sdc_xchg8(0xff);
+ }
+ sdc_deselect();
+ } else {
+ sdc_deselect();
+ return SDC_ERR(rv);
+ }
+
+ _type = EOS_SDC_TYPE_SDC2;
+ if (ocr[0] & 0xc0) _type |= EOS_SDC_CAP_BLK;
+ }
+ break;
+
+ case SDC_R1_IDLE_STATE | SDC_R1_ILLEGAL_CMD:
+ sdc_deselect();
+ rv = sdc_acmd(SD_APP_OP_COND, 0, 0, sdc_nto(start, timeout));
+ switch (rv) {
+ case SDC_R1_IDLE_STATE:
+ do {
+ rv = sdc_acmd(SD_APP_OP_COND, 0, 0, sdc_nto(start, timeout));
+ } while (rv == SDC_R1_IDLE_STATE);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ case SDC_R1_READY:
+ _type = EOS_SDC_TYPE_SDC1;
+ break;
+
+ default:
+ do {
+ rv = sdc_cmd(SEND_OP_COND, 0, 0, sdc_nto(start, timeout));
+ } while (rv == SDC_R1_IDLE_STATE);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ _type = EOS_SDC_TYPE_MMC;
+ break;
+
+ }
+ break;
+
+ default:
+ sdc_deselect();
+ return SDC_ERR(rv);
+ }
+
+ if (!(_type & EOS_SDC_CAP_BLK)) {
+ rv = sdc_cmd(SET_BLOCKLEN, 512, 0, sdc_nto(start, timeout));
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ }
+
+ if (_type & EOS_SDC_TYPE_SDC) {
+ uint8_t csd[16];
+
+ sdc_select();
+ rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, sdc_nto(start, timeout));
+ if (rv == SDC_R1_READY) {
+ rv = sdc_block_read(csd, 16, sdc_nto(start, timeout));
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ if (rv) return rv;
+
+ for (i=0; i<16; i++) {
+ printf("%.2x ", csd[i]);
+ }
+ printf("\n");
+ if (csd[10] & 0x40) _type |= EOS_SDC_CAP_ERASE_EN;
+ }
+
+ eos_spi_set_div(EOS_SPI_DEV_SDC, 5);
+ sdc_type = _type;
+ return EOS_OK;
+}
+
+void eos_sdc_init(void) {
+ int rv;
+
+ eos_spi_select(EOS_SPI_DEV_SDC);
+ rv = sdc_init(5000);
+ if (rv) {
+ printf("SDC ERROR\n");
+ } else {
+ printf("SDC OK:%x\n", sdc_type);
+ }
+ eos_spi_deselect();
+}
+
+uint8_t eos_sdc_type(void) {
+ return sdc_type & EOS_SDC_TYPE_MASK;
+}
+
+uint8_t eos_sdc_cap(void) {
+ return sdc_type & EOS_SDC_CAP_MASK;
+}
+
+int eos_sdc_get_sect_count(uint32_t timeout, uint32_t *sectors) {
+ int rv;
+ uint8_t csd[16];
+ uint64_t start = eos_time_get_tick();
+
+ sdc_select();
+ rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, timeout);
+ if (rv == SDC_R1_READY) {
+ rv = sdc_block_read(csd, 16, sdc_nto(start, timeout));
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ if (rv) return rv;
+
+ if ((csd[0] >> 6) == 1) { /* SDCv2 */
+ *sectors = (csd[9] + (csd[8] << 8) + ((csd[7] & 0x3f) << 16) + 1) << 10;
+ } else { /* SDCv1 or MMC*/
+ uint8_t n = (csd[5] & 0x0f) + (csd[10] >> 7) + ((csd[9] & 0x03) << 1) + 2;
+ *sectors = (csd[8] >> 6) + (csd[7] << 2) + ((csd[6] & 0x03) << 10) + 1;
+ *sectors = *sectors << (n - 9);
+ }
+
+ return EOS_OK;
+}
+
+int eos_sdc_get_blk_size(uint32_t timeout, uint32_t *size) {
+ int rv;
+ uint8_t rbl[64]; /* SD Status or CSD register */
+ uint64_t start = eos_time_get_tick();
+
+ sdc_select();
+ if (sdc_type & EOS_SDC_TYPE_SDC2) {
+ rv = sdc_acmd(SD_STATUS, 0, SDC_CMD_FLAG_NOCS, timeout);
+ sdc_xchg8(0xff); /* R2 response */
+ } else {
+ rv = sdc_cmd(SEND_CSD, 0, SDC_CMD_FLAG_NOCS, timeout);
+ }
+ if (rv == SDC_R1_READY) {
+ rv = sdc_block_read(rbl, (sdc_type & EOS_SDC_TYPE_SDC2) ? 64 : 16, sdc_nto(start, timeout));
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ if (rv) return rv;
+
+ if (sdc_type & EOS_SDC_TYPE_SDC2) {
+ *size = 16UL << (rbl[10] >> 4);
+ } else if (sdc_type & EOS_SDC_TYPE_SDC1) {
+ *size = (((rbl[10] & 0x3f) << 1) + (rbl[11] >> 7) + 1) << ((rbl[13] >> 6) - 1);
+ } else {
+ *size = (((rbl[10] & 0x7c) >> 2) + 1) * (((rbl[11] & 0x03) << 3) + (rbl[11] >> 5) + 1);
+ }
+
+ return EOS_OK;
+}
+
+int eos_sdc_sync(uint32_t timeout) {
+ int rv;
+
+ sdc_select();
+ rv = sdc_ready(timeout);
+ sdc_deselect();
+
+ return rv;
+}
+
+int eos_sdc_erase(uint32_t blk_start, uint32_t blk_end, uint32_t timeout) {
+ int rv;
+ uint64_t start;
+
+ if (!(sdc_type & EOS_SDC_TYPE_SDC)) return EOS_ERR;
+ if (!(sdc_type & EOS_SDC_CAP_ERASE_EN)) return EOS_ERR;
+ if (!(sdc_type & EOS_SDC_CAP_BLK)) {
+ blk_start *= 512;
+ blk_end *= 512;
+ }
+
+ start = eos_time_get_tick();
+ rv = sdc_cmd(ERASE_WR_BLK_START, blk_start, 0, timeout);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ rv = sdc_cmd(ERASE_WR_BLK_END, blk_end, 0, sdc_nto(start, timeout));
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ rv = sdc_cmd(ERASE, 0, 0, sdc_nto(start, timeout));
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+
+ return eos_sdc_sync(sdc_nto(start, timeout));
+}
+
+int eos_sdc_sect_read(uint32_t sect, unsigned int count, uint8_t *buffer) {
+ int rv;
+ uint64_t start;
+ uint8_t cmd = ((count == 1) ? READ_SINGLE_BLOCK : READ_MULTIPLE_BLOCK);
+
+ if (!(sdc_type & EOS_SDC_CAP_BLK)) sect *= 512;
+ start = eos_time_get_tick();
+
+ sdc_select();
+ rv = sdc_cmd(cmd, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD);
+ if (rv == SDC_R1_READY) {
+ int _rv = SDC_R1_READY;
+
+ while (count) {
+ rv = sdc_block_read(buffer, 512, SDC_TIMEOUT_READ);
+ if (rv) break;
+ eos_sdcc_decrypt(sect, buffer);
+ buffer += 512;
+ count--;
+ }
+ if (cmd == READ_MULTIPLE_BLOCK) _rv = sdc_cmd(STOP_TRANSMISSION, 0, SDC_CMD_FLAG_NOCS | SDC_CMD_FLAG_NOWAIT | SDC_CMD_FLAG_RSTUFF, 0);
+ if (!rv && (_rv != SDC_R1_READY)) rv = SDC_ERR(_rv);
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+
+ return rv;
+}
+
+int eos_sdc_sect_write(uint32_t sect, unsigned int count, uint8_t *buffer) {
+ int rv;
+ uint64_t start;
+
+ if (!(sdc_type & EOS_SDC_CAP_BLK)) sect *= 512;
+ start = eos_time_get_tick();
+
+ if (count == 1) {
+ sdc_select();
+ rv = sdc_cmd(WRITE_BLOCK, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD);
+ if (rv == SDC_R1_READY) {
+ eos_sdcc_encrypt(sect, buffer);
+ rv = sdc_block_write(SDC_TOKEN_START_BLK, buffer, 512, SDC_TIMEOUT_WRITE);
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ } else {
+ if (sdc_type & EOS_SDC_TYPE_SDC) {
+ rv = sdc_acmd(SET_WR_BLK_ERASE_COUNT, count, 0, SDC_TIMEOUT_CMD);
+ if (rv != SDC_R1_READY) return SDC_ERR(rv);
+ }
+
+ sdc_select();
+ rv = sdc_cmd(WRITE_MULTIPLE_BLOCK, sect, SDC_CMD_FLAG_NOCS, SDC_TIMEOUT_CMD);
+ if (rv == SDC_R1_READY) {
+ int _rv;
+
+ while (count) {
+ eos_sdcc_encrypt(sect, buffer);
+ rv = sdc_block_write(SDC_TOKEN_START_BLKM, buffer, 512, SDC_TIMEOUT_WRITE);
+ if (rv) break;
+ buffer += 512;
+ count--;
+ }
+ _rv = sdc_block_write(SDC_TOKEN_STOP_TRAN, NULL, 0, SDC_TIMEOUT_WRITE);
+ if (!rv && (_rv != SDC_R1_READY)) rv = SDC_ERR(_rv);
+ } else {
+ rv = SDC_ERR(rv);
+ }
+ sdc_deselect();
+ }
+
+ return rv;
+}
diff --git a/fw/fe310/eos/sdcard.h b/fw/fe310/eos/sdcard.h
new file mode 100644
index 0000000..0234f65
--- /dev/null
+++ b/fw/fe310/eos/sdcard.h
@@ -0,0 +1,22 @@
+#include <stdint.h>
+
+#define EOS_SDC_TYPE_MMC 0x01
+
+#define EOS_SDC_TYPE_SDC1 0x04
+#define EOS_SDC_TYPE_SDC2 0x08
+#define EOS_SDC_TYPE_SDC 0x0c
+#define EOS_SDC_TYPE_MASK 0x0f
+
+#define EOS_SDC_CAP_BLK 0x10
+#define EOS_SDC_CAP_ERASE_EN 0x20
+#define EOS_SDC_CAP_MASK 0xf0
+
+void eos_sdc_init(void);
+uint8_t eos_sdc_type(void);
+uint8_t eos_sdc_cap(void);
+int eos_sdc_get_sect_count(uint32_t timeout, uint32_t *sectors);
+int eos_sdc_get_blk_size(uint32_t timeout, uint32_t *size);
+int eos_sdc_sync(uint32_t timeout);
+int eos_sdc_erase(uint32_t blk_start, uint32_t blk_end, uint32_t timeout);
+int eos_sdc_sect_read(uint32_t sect, unsigned int count, uint8_t *buffer);
+int eos_sdc_sect_write(uint32_t sect, unsigned int count, uint8_t *buffer);
diff --git a/fw/fe310/eos/sock.c b/fw/fe310/eos/sock.c
new file mode 100644
index 0000000..a2e30e3
--- /dev/null
+++ b/fw/fe310/eos/sock.c
@@ -0,0 +1,103 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "eos.h"
+#include "event.h"
+#include "net.h"
+
+#include "sock.h"
+
+static eos_evt_handler_t evt_handler[EOS_SOCK_MAX_SOCK];
+
+static void sock_handle_msg(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char sock;
+
+ if ((buffer == NULL) || (len < 2)) {
+ eos_net_bad_handler(type, buffer, len);
+ return;
+ }
+
+ sock = buffer[1];
+ if ((sock == 0) || (sock > EOS_SOCK_MAX_SOCK) || (evt_handler[sock - 1] == NULL)) {
+ eos_net_bad_handler(type, buffer, len);
+ return;
+ }
+
+ switch(buffer[0]) {
+ case EOS_SOCK_MTYPE_PKT:
+ evt_handler[sock - 1](type, buffer, len);
+ break;
+ default:
+ eos_net_bad_handler(type, buffer, len);
+ break;
+ }
+}
+
+void eos_sock_init(void) {
+ int i;
+
+ for (i=0; i<EOS_SOCK_MAX_SOCK; i++) {
+ evt_handler[i] = NULL;
+ }
+ eos_net_set_handler(EOS_NET_MTYPE_SOCK, sock_handle_msg);
+}
+
+void eos_sock_set_handler(unsigned char sock, eos_evt_handler_t handler) {
+ if (sock && (sock <= EOS_SOCK_MAX_SOCK)) evt_handler[sock - 1] = handler;
+}
+
+eos_evt_handler_t eos_sock_get_handler(unsigned char sock) {
+ if (sock && (sock <= EOS_SOCK_MAX_SOCK)) return evt_handler[sock - 1];
+ return NULL;
+}
+
+int eos_sock_open_udp(eos_evt_handler_t handler) {
+ unsigned char type = EOS_SOCK_MTYPE_OPEN_DGRAM;
+ unsigned char *buffer = eos_net_alloc();
+ uint16_t buf_size;
+ int rv, sock;
+
+ buffer[0] = type;
+ rv = eos_net_send(EOS_NET_MTYPE_SOCK, buffer, 1, 0);
+ if (rv) return rv;
+
+ eos_evtq_wait(EOS_NET_MTYPE_SOCK, &type, 1, &buffer, &buf_size);
+ if (buf_size < 2) {
+ eos_net_free(buffer, 0);
+ return EOS_ERR_NET;
+ }
+
+ sock = buffer[1];
+ eos_net_free(buffer, 1);
+
+ if (sock == 0) return EOS_ERR_NET;
+
+ eos_sock_set_handler(sock, handler);
+ return sock;
+}
+
+void eos_sock_close(unsigned char sock) {
+ unsigned char *buffer = eos_net_alloc();
+ buffer[0] = EOS_SOCK_MTYPE_CLOSE;
+ buffer[1] = sock;
+ eos_net_send(EOS_NET_MTYPE_SOCK, buffer, 2, 1);
+ eos_sock_set_handler(sock, NULL);
+}
+
+int eos_sock_sendto(unsigned char sock, unsigned char *buffer, uint16_t size, unsigned char more, EOSNetAddr *addr) {
+ unsigned char type = EOS_NET_MTYPE_SOCK;
+
+ buffer[0] = EOS_SOCK_MTYPE_PKT;
+ buffer[1] = sock;
+ memcpy(buffer+2, addr->host, sizeof(addr->host));
+ memcpy(buffer+2+sizeof(addr->host), &addr->port, sizeof(addr->port));
+ return eos_net_send(type, buffer, size, more);
+}
+
+void eos_sock_getfrom(unsigned char *buffer, EOSNetAddr *addr) {
+ memcpy(addr->host, buffer+2, sizeof(addr->host));
+ memcpy(&addr->port, buffer+2+sizeof(addr->host), sizeof(addr->port));
+}
+
+
diff --git a/fw/fe310/eos/sock.h b/fw/fe310/eos/sock.h
new file mode 100644
index 0000000..7461473
--- /dev/null
+++ b/fw/fe310/eos/sock.h
@@ -0,0 +1,26 @@
+#include <stdint.h>
+#include "event.h"
+
+#define EOS_SOCK_MTYPE_PKT 0
+#define EOS_SOCK_MTYPE_OPEN_DGRAM 1
+#define EOS_SOCK_MTYPE_CLOSE 127
+
+#define EOS_SOCK_MAX_SOCK 2
+
+#define EOS_SOCK_SIZE_UDP_HDR 8
+
+#define EOS_IPv4_ADDR_SIZE 4
+
+typedef struct EOSNetAddr {
+ unsigned char host[EOS_IPv4_ADDR_SIZE];
+ uint16_t port;
+} EOSNetAddr;
+
+void eos_sock_init(void);
+void eos_sock_set_handler(unsigned char sock, eos_evt_handler_t handler);
+eos_evt_handler_t eos_sock_get_handler(unsigned char sock);
+
+int eos_sock_open_udp(eos_evt_handler_t handler);
+void eos_sock_close(unsigned char sock);
+int eos_sock_sendto(unsigned char sock, unsigned char *buffer, uint16_t size, unsigned char more, EOSNetAddr *addr);
+void eos_sock_getfrom(unsigned char *buffer, EOSNetAddr *addr);
diff --git a/fw/fe310/eos/spi.c b/fw/fe310/eos/spi.c
new file mode 100644
index 0000000..c5e953e
--- /dev/null
+++ b/fw/fe310/eos/spi.c
@@ -0,0 +1,338 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "interrupt.h"
+#include "event.h"
+
+#include "board.h"
+
+#include "spi.h"
+#include "spi_priv.h"
+
+#define SPI_MODE0 0x00
+#define SPI_MODE1 0x01
+#define SPI_MODE2 0x02
+#define SPI_MODE3 0x03
+
+#define SPI_FLAG_XCHG 0x10
+
+#define SPI_IOF_MASK (((uint32_t)1 << IOF_SPI1_SCK) | ((uint32_t)1 << IOF_SPI1_MOSI) | ((uint32_t)1 << IOF_SPI1_MISO)) | SPI_IOF_MASK_CS
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static uint8_t spi_cspin;
+static volatile uint8_t spi_state_flags;
+static unsigned char spi_evt;
+static unsigned char spi_in_xchg;
+
+static uint32_t spi_state_len = 0;
+static uint32_t spi_state_idx_tx = 0;
+static uint32_t spi_state_idx_rx = 0;
+static unsigned char *spi_state_buf = NULL;
+
+static eos_evt_handler_t evt_handler[EOS_SPI_MAX_EVT];
+
+static void spi_handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & ~EOS_EVT_MASK) - 1;
+ if (idx < EOS_SPI_MAX_EVT) {
+ evt_handler[idx](type, buffer, len);
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+void eos_spi_init(void) {
+ int i;
+
+ for (i=0; i<EOS_SPI_MAX_EVT; i++) {
+ evt_handler[i] = eos_evtq_bad_handler;
+ }
+ eos_evtq_set_handler(EOS_EVT_SPI, spi_handle_evt);
+ eos_intr_set(INT_SPI1_BASE, IRQ_PRIORITY_SPI_XCHG, NULL);
+
+ SPI1_REG(SPI_REG_SCKMODE) = SPI_MODE0;
+ SPI1_REG(SPI_REG_FMT) = SPI_FMT_PROTO(SPI_PROTO_S) |
+ SPI_FMT_ENDIAN(SPI_ENDIAN_MSB) |
+ SPI_FMT_DIR(SPI_DIR_RX) |
+ SPI_FMT_LEN(8);
+
+ GPIO_REG(GPIO_IOF_SEL) &= ~SPI_IOF_MASK;
+ GPIO_REG(GPIO_IOF_EN) |= SPI_IOF_MASK;
+
+ // There is no way here to change the CS polarity.
+ // SPI1_REG(SPI_REG_CSDEF) = 0xFFFF;
+}
+
+void eos_spi_start(uint16_t div, uint8_t csid, uint8_t cspin, unsigned char evt) {
+ spi_state_flags = 0;
+ spi_evt = evt;
+ SPI1_REG(SPI_REG_SCKDIV) = div;
+ SPI1_REG(SPI_REG_CSID) = csid;
+ if (csid != SPI_CSID_NONE) {
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ } else {
+ spi_cspin = cspin;
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_OFF;
+ }
+ eos_intr_set_handler(INT_SPI1_BASE, eos_spi_handle_xchg);
+}
+
+void eos_spi_stop(void) {
+ eos_spi_flush();
+ spi_evt = 0;
+}
+
+void eos_spi_set_handler(unsigned char evt, eos_evt_handler_t handler) {
+ if (handler == NULL) handler = eos_evtq_bad_handler;
+ if (evt && (evt <= EOS_SPI_MAX_EVT)) evt_handler[evt - 1] = handler;
+}
+
+void _eos_spi_xchg_init(unsigned char *buffer, uint16_t len, uint8_t flags) {
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= (SPI_FLAG_XCHG | flags);
+ spi_state_buf = buffer;
+ spi_state_len = len;
+ spi_state_idx_tx = 0;
+ spi_state_idx_rx = 0;
+}
+
+static void spi_xchg_finish(void) {
+ uint8_t done = 0;
+
+ while (!done) {
+ clear_csr(mstatus, MSTATUS_MIE);
+ done = !(spi_state_flags & SPI_FLAG_XCHG);
+ if (!done) asm volatile ("wfi");
+ set_csr(mstatus, MSTATUS_MIE);
+ }
+ spi_in_xchg = 0;
+}
+
+void eos_spi_xchg(unsigned char *buffer, uint16_t len, uint8_t flags) {
+ if (spi_in_xchg) spi_xchg_finish();
+
+ spi_in_xchg = 1;
+ _eos_spi_xchg_init(buffer, len, flags);
+
+ eos_spi_cs_set();
+ SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(SPI_SIZE_WM);
+ SPI1_REG(SPI_REG_IE) = SPI_IP_TXWM;
+}
+
+void eos_spi_handle_xchg(void) {
+ int i;
+ uint16_t sz_chunk = MIN(spi_state_len - spi_state_idx_tx, SPI_SIZE_CHUNK);
+
+ for (i=0; i<sz_chunk; i++) {
+ volatile uint32_t x = SPI1_REG(SPI_REG_TXFIFO);
+ if (x & SPI_TXFIFO_FULL) break;
+ SPI1_REG(SPI_REG_TXFIFO) = spi_state_buf[spi_state_idx_tx+i];
+ }
+ spi_state_idx_tx += i;
+
+ for (i=0; i<spi_state_idx_tx - spi_state_idx_rx; i++) {
+ volatile uint32_t x = SPI1_REG(SPI_REG_RXFIFO);
+ if (x & SPI_RXFIFO_EMPTY) break;
+ spi_state_buf[spi_state_idx_rx+i] = x & 0xFF;
+ }
+ spi_state_idx_rx += i;
+
+ if (spi_state_idx_tx == spi_state_len) {
+ if ((spi_state_idx_rx == spi_state_len) || (spi_state_flags & EOS_SPI_FLAG_TX)) {
+ spi_state_flags &= ~SPI_FLAG_XCHG;
+ if (!(spi_state_flags & EOS_SPI_FLAG_MORE)) eos_spi_cs_clear();
+ SPI1_REG(SPI_REG_IE) = 0x0;
+ if (spi_evt) eos_evtq_push_isr(EOS_EVT_SPI | spi_evt, spi_state_buf, spi_state_len);
+ } else {
+ SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(MIN(spi_state_len - spi_state_idx_rx - 1, SPI_SIZE_WM - 1));
+ SPI1_REG(SPI_REG_IE) = SPI_IP_RXWM;
+ }
+ }
+}
+
+void eos_spi_cs_set(void) {
+ /* cs low */
+ if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_OFF) {
+ GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << spi_cspin);
+ } else {
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD;
+ }
+}
+
+void eos_spi_cs_clear(void) {
+ /* cs high */
+ if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_OFF) {
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << spi_cspin);
+ } else {
+ SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO;
+ }
+}
+
+uint8_t eos_spi_xchg8(uint8_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = data;
+
+ if (rx) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ }
+
+ return x & 0xFF;
+}
+
+uint16_t eos_spi_xchg16(uint16_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+ uint16_t r = 0;
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+
+ if (flags & EOS_SPI_FLAG_BSWAP) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF);
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0xFF00) >> 8;
+ } else {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0xFF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF);
+ }
+
+ if (rx) {
+ if (flags & EOS_SPI_FLAG_BSWAP) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF);
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ } else {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF);
+ }
+ }
+
+ return r;
+}
+
+uint32_t eos_spi_xchg24(uint32_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+ uint32_t r = 0;
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+
+ if (flags & EOS_SPI_FLAG_BSWAP) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x000000FF);
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x0000FF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF0000) >> 16;
+ } else {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF0000) >> 16;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x0000FF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x000000FF);
+ }
+
+ if (rx) {
+ if (flags & EOS_SPI_FLAG_BSWAP) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF);
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 16;
+ } else {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 16;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF);
+ }
+ }
+
+ return r;
+}
+
+uint32_t eos_spi_xchg32(uint32_t data, uint8_t flags) {
+ volatile uint32_t x = 0;
+ uint8_t rx = !(flags & EOS_SPI_FLAG_TX);
+ uint32_t r = 0;
+
+ spi_state_flags &= 0xF0;
+ spi_state_flags |= flags;
+
+ if (flags & EOS_SPI_FLAG_BSWAP) {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x000000FF);
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x0000FF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF0000) >> 16;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0xFF000000) >> 24;
+ } else {
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0xFF000000) >> 24;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x00FF0000) >> 16;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x0000FF00) >> 8;
+ while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL);
+ SPI1_REG(SPI_REG_TXFIFO) = (data & 0x000000FF);
+ }
+
+ if (rx) {
+ if (flags & EOS_SPI_FLAG_BSWAP) {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF);
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 16;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 24;
+ } else {
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 24;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 16;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF) << 8;
+ while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY);
+ r |= (x & 0xFF);
+ }
+ }
+
+ return r;
+}
+
+void eos_spi_flush(void) {
+ volatile uint32_t x = 0;
+
+ if (spi_in_xchg) spi_xchg_finish();
+
+ SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(1);
+ while (!x) {
+ if (SPI1_REG(SPI_REG_IP) & SPI_IP_TXWM) x = SPI1_REG(SPI_REG_RXFIFO) & SPI_RXFIFO_EMPTY;
+ }
+}
diff --git a/fw/fe310/eos/spi.h b/fw/fe310/eos/spi.h
new file mode 100644
index 0000000..06db0d0
--- /dev/null
+++ b/fw/fe310/eos/spi.h
@@ -0,0 +1,28 @@
+#include <stdint.h>
+#include "event.h"
+
+#define EOS_SPI_FLAG_TX 0x01
+#define EOS_SPI_FLAG_MORE 0x02
+#define EOS_SPI_FLAG_BSWAP 0x04
+
+#define EOS_SPI_EVT_SDC 1
+#define EOS_SPI_EVT_CAM 2
+
+#define EOS_SPI_MAX_EVT 2
+
+void eos_spi_init(void);
+void eos_spi_start(uint16_t div, uint8_t csid, uint8_t cspin, unsigned char evt);
+void eos_spi_stop(void);
+void eos_spi_set_handler(unsigned char evt, eos_evt_handler_t handler);
+
+void _eos_spi_xchg_init(unsigned char *buffer, uint16_t len, uint8_t flags);
+void eos_spi_xchg(unsigned char *buffer, uint16_t len, uint8_t flags);
+void eos_spi_handle_xchg(void);
+
+void eos_spi_cs_set(void);
+void eos_spi_cs_clear(void);
+uint8_t eos_spi_xchg8(uint8_t data, uint8_t flags);
+uint16_t eos_spi_xchg16(uint16_t data, uint8_t flags);
+uint32_t eos_spi_xchg24(uint32_t data, uint8_t flags);
+uint32_t eos_spi_xchg32(uint32_t data, uint8_t flags);
+void eos_spi_flush(void);
diff --git a/fw/fe310/eos/spi_cfg.h b/fw/fe310/eos/spi_cfg.h
new file mode 100644
index 0000000..43b0763
--- /dev/null
+++ b/fw/fe310/eos/spi_cfg.h
@@ -0,0 +1,35 @@
+#include <stdint.h>
+
+typedef struct {
+ uint16_t div;
+ uint8_t csid;
+ uint8_t cspin;
+ unsigned char evt;
+} SPIConfig;
+
+static const SPIConfig spi_cfg[] = {
+ { // DEV_NET
+ .div = SPI_DIV_NET,
+ .csid = SPI_CSID_NET,
+ .cspin = SPI_CSPIN_NET,
+ .evt = 0, // Not SPI event
+ },
+ { // DEV_EVE
+ .div = SPI_DIV_EVE,
+ .csid = SPI_CSID_EVE,
+ .cspin = SPI_CSPIN_EVE,
+ .evt = 0,
+ },
+ { // DEV_SDC
+ .div = SPI_DIV_SDC,
+ .csid = SPI_CSID_SDC,
+ .cspin = SPI_CSPIN_SDC,
+ .evt = EOS_SPI_EVT_SDC,
+ },
+ { // DEV_CAM
+ .div = SPI_DIV_CAM,
+ .csid = SPI_CSID_CAM,
+ .cspin = SPI_CSPIN_CAM,
+ .evt = EOS_SPI_EVT_CAM,
+ },
+};
diff --git a/fw/fe310/eos/spi_dev.c b/fw/fe310/eos/spi_dev.c
new file mode 100644
index 0000000..0448f76
--- /dev/null
+++ b/fw/fe310/eos/spi_dev.c
@@ -0,0 +1,93 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "msgq.h"
+#include "interrupt.h"
+#include "event.h"
+
+#include "board.h"
+
+#include "net.h"
+#include "spi.h"
+#include "spi_priv.h"
+#include "spi_cfg.h"
+#include "spi_dev.h"
+
+static uint8_t spi_dev;
+static uint8_t spi_lock;
+static uint16_t spi_div[EOS_SPI_MAX_DEV];
+
+void eos_spi_dev_init(void) {
+ int i;
+
+ for (i=0; i<EOS_SPI_MAX_DEV; i++) {
+ spi_div[i] = spi_cfg[i].div;
+ if (spi_cfg[i].csid == SPI_CSID_NONE) {
+ GPIO_REG(GPIO_INPUT_EN) &= ~(1 << spi_cfg[i].cspin);
+ GPIO_REG(GPIO_OUTPUT_EN) |= (1 << spi_cfg[i].cspin);
+ GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << spi_cfg[i].cspin);
+ GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << spi_cfg[i].cspin);
+ GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << spi_cfg[i].cspin);
+ }
+ }
+}
+
+int eos_spi_select(unsigned char dev) {
+ if (dev == EOS_SPI_DEV_NET) return EOS_ERR;
+ if (spi_lock) return EOS_ERR_BUSY;
+
+ if (spi_dev == EOS_SPI_DEV_NET) {
+ eos_net_stop();
+ } else {
+ eos_spi_stop();
+ }
+
+ spi_dev = dev;
+ eos_spi_start(spi_div[dev], spi_cfg[dev].csid, spi_cfg[dev].cspin, spi_cfg[dev].evt);
+
+ return EOS_OK;
+}
+
+int eos_spi_deselect(void) {
+ if (spi_dev == EOS_SPI_DEV_NET) return EOS_ERR;
+ if (spi_lock) return EOS_ERR_BUSY;
+
+ eos_spi_stop();
+
+ spi_dev = EOS_SPI_DEV_NET;
+ eos_net_start(0);
+
+ return EOS_OK;
+}
+
+uint8_t eos_spi_dev(void) {
+ return spi_dev;
+}
+
+uint16_t eos_spi_div(unsigned char dev) {
+ return spi_div[dev];
+}
+
+uint8_t eos_spi_csid(unsigned char dev) {
+ return spi_cfg[dev].csid;
+}
+
+uint8_t eos_spi_cspin(unsigned char dev) {
+ return spi_cfg[dev].cspin;
+}
+
+void eos_spi_lock(void) {
+ spi_lock = 1;
+}
+
+void eos_spi_unlock(void) {
+ spi_lock = 0;
+}
+
+void eos_spi_set_div(unsigned char dev, uint16_t div) {
+ spi_div[dev] = div;
+}
diff --git a/fw/fe310/eos/spi_dev.h b/fw/fe310/eos/spi_dev.h
new file mode 100644
index 0000000..ff0e2a6
--- /dev/null
+++ b/fw/fe310/eos/spi_dev.h
@@ -0,0 +1,21 @@
+#include <stdint.h>
+
+#define EOS_SPI_DEV_NET 0
+#define EOS_SPI_DEV_EVE 1
+#define EOS_SPI_DEV_SDC 2
+#define EOS_SPI_DEV_CAM 3
+
+#define EOS_SPI_MAX_DEV 4
+
+void eos_spi_dev_init(void);
+int eos_spi_select(unsigned char dev);
+int eos_spi_deselect(void);
+
+uint8_t eos_spi_dev(void);
+uint16_t eos_spi_div(unsigned char dev);
+uint8_t eos_spi_csid(unsigned char dev);
+uint8_t eos_spi_cspin(unsigned char dev);
+
+void eos_spi_lock(void);
+void eos_spi_unlock(void);
+void eos_spi_set_div(unsigned char dev, uint16_t div);
diff --git a/fw/fe310/eos/spi_priv.h b/fw/fe310/eos/spi_priv.h
new file mode 100644
index 0000000..72c2dae
--- /dev/null
+++ b/fw/fe310/eos/spi_priv.h
@@ -0,0 +1,8 @@
+#include <stdint.h>
+
+#define SPI_CSID_NONE 1
+#define SPI_CSPIN_NONE 0xff
+
+/* DO NOT TOUCH THEESE */
+#define SPI_SIZE_CHUNK 4
+#define SPI_SIZE_WM 2
diff --git a/fw/fe310/eos/timer.c b/fw/fe310/eos/timer.c
new file mode 100644
index 0000000..d8cf107
--- /dev/null
+++ b/fw/fe310/eos/timer.c
@@ -0,0 +1,135 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "msgq.h"
+#include "event.h"
+#include "timer.h"
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static eos_timer_handler_t timer_handler[EOS_TIMER_MAX_ETYPE + 1];
+static uint64_t timer_next[EOS_TIMER_MAX_ETYPE + 1];
+
+static void timer_handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & ~EOS_EVT_MASK);
+
+ if (idx && (idx <= EOS_TIMER_MAX_ETYPE) && timer_handler[idx]) {
+ timer_handler[idx](type);
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+void _eos_timer_handle(void) {
+ int i;
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ uint64_t *mtimecmp = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
+ uint64_t now = *mtime;
+ uint64_t next = 0;
+
+ for (i = 0; i <= EOS_TIMER_MAX_ETYPE; i++) {
+ if (timer_next[i] && (timer_next[i] <= now)) {
+ timer_next[i] = 0;
+ if (i == 0) {
+ timer_handler[0](0);
+ } else {
+ eos_evtq_push_isr(EOS_EVT_TIMER | i, NULL, 0);
+ }
+ }
+ next = next && timer_next[i] ? MIN(next, timer_next[i]) : (next ? next : timer_next[i]);
+ }
+ *mtimecmp = next;
+ if (*mtimecmp == 0) clear_csr(mie, MIP_MTIP);
+}
+
+void eos_timer_init(void) {
+ int i;
+ uint64_t *mtimecmp = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
+
+ clear_csr(mie, MIP_MTIP);
+ *mtimecmp = 0;
+ for (i=0; i<=EOS_TIMER_MAX_ETYPE; i++) {
+ timer_next[i] = 0;
+ timer_handler[i] = NULL;
+ }
+ eos_evtq_set_handler(EOS_EVT_TIMER, timer_handle_evt);
+}
+
+void eos_timer_set_handler(unsigned char evt, eos_timer_handler_t handler) {
+ uint64_t *mtimecmp = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
+
+ if (!evt && (*mtimecmp != 0)) clear_csr(mie, MIP_MTIP);
+ timer_handler[evt] = handler;
+ if (!evt && (*mtimecmp != 0)) set_csr(mie, MIP_MTIP);
+}
+
+uint32_t eos_timer_get(unsigned char evt) {
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ uint64_t *mtimecmp = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
+ uint64_t now;
+ uint32_t ret;
+
+ if (*mtimecmp != 0) clear_csr(mie, MIP_MTIP);
+ now = *mtime;
+ if (timer_next[evt]) {
+ ret = (timer_next[evt] > now) ? (timer_next[evt] - now) * 1000 / EOS_TIMER_RTC_FREQ : 0;
+ } else {
+ ret = EOS_TIMER_NONE;
+ }
+ if (*mtimecmp != 0) set_csr(mie, MIP_MTIP);
+
+ return ret;
+}
+
+void eos_timer_set(uint32_t msec, unsigned char evt) {
+ int i;
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ uint64_t *mtimecmp = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
+ uint64_t tick = *mtime + msec * (uint64_t)EOS_TIMER_RTC_FREQ / 1000;
+ uint64_t next = 0;
+
+ if (*mtimecmp != 0) clear_csr(mie, MIP_MTIP);
+ timer_next[evt] = tick;
+ for (i = 0; i <= EOS_TIMER_MAX_ETYPE; i++) {
+ next = next && timer_next[i] ? MIN(next, timer_next[i]) : (next ? next : timer_next[i]);
+ }
+ *mtimecmp = next;
+ if (*mtimecmp != 0) set_csr(mie, MIP_MTIP);
+}
+
+void eos_timer_clear(unsigned char evt) {
+ int i;
+ uint64_t *mtimecmp = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
+ uint64_t next = 0;
+
+ if (*mtimecmp != 0) clear_csr(mie, MIP_MTIP);
+ if (timer_next[evt]) {
+ timer_next[evt] = 0;
+ for (i = 0; i <= EOS_TIMER_MAX_ETYPE; i++) {
+ next = next && timer_next[i] ? MIN(next, timer_next[i]) : (next ? next : timer_next[i]);
+ }
+ *mtimecmp = next;
+ }
+ if (*mtimecmp != 0) set_csr(mie, MIP_MTIP);
+}
+
+
+void eos_time_sleep(uint32_t msec) {
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ uint64_t now_ms = *mtime * 1000 / EOS_TIMER_RTC_FREQ;
+
+ while (*mtime * 1000 / EOS_TIMER_RTC_FREQ < now_ms + msec);
+}
+
+uint64_t eos_time_get_tick(void) {
+ volatile uint64_t *mtime = (uint64_t *) (CLINT_CTRL_ADDR + CLINT_MTIME);
+ return *mtime;
+}
+
+uint32_t eos_time_since(uint32_t start) {
+ return (eos_time_get_tick() - start) * 1000 / EOS_TIMER_RTC_FREQ;
+ }
diff --git a/fw/fe310/eos/timer.h b/fw/fe310/eos/timer.h
new file mode 100644
index 0000000..99ffa7a
--- /dev/null
+++ b/fw/fe310/eos/timer.h
@@ -0,0 +1,23 @@
+#include <stdint.h>
+
+#define EOS_TIMER_ETYPE_UI 1
+#define EOS_TIMER_ETYPE_ECP 2
+#define EOS_TIMER_ETYPE_USER 4
+
+#define EOS_TIMER_MAX_ETYPE 4
+
+#define EOS_TIMER_NONE 0xffffffff
+#define EOS_TIMER_RTC_FREQ 32768
+
+typedef void (*eos_timer_handler_t) (unsigned char);
+
+void eos_timer_init(void);
+void eos_timer_set_handler(unsigned char evt, eos_timer_handler_t handler);
+
+uint32_t eos_timer_get(unsigned char evt);
+void eos_timer_set(uint32_t msec, unsigned char evt);
+void eos_timer_clear(unsigned char evt);
+
+void eos_time_sleep(uint32_t msec);
+uint64_t eos_time_get_tick(void);
+uint32_t eos_time_since(uint32_t start);
diff --git a/fw/fe310/eos/trap_entry.S b/fw/fe310/eos/trap_entry.S
new file mode 100644
index 0000000..d54243f
--- /dev/null
+++ b/fw/fe310/eos/trap_entry.S
@@ -0,0 +1,517 @@
+#include "encoding.h"
+#include "sifive/bits.h"
+
+#define MCAUSE_INT 0x80000000
+#define MCAUSE_EXT (MCAUSE_INT | IRQ_M_EXT)
+#define MCAUSE_TIMER (MCAUSE_INT | IRQ_M_TIMER)
+
+#define PLIC_PRIORITY 0x0C000000
+#define PLIC_THRESHOLD 0x0C200000
+#define PLIC_CLAIM 0x0C200004
+
+#define PWM0_CTRL_ADDR 0x10015000
+#define PWM1_CTRL_ADDR 0x10025000
+#define PWM2_CTRL_ADDR 0x10035000
+#include "sifive/devices/pwm.h"
+
+#define GPIO_CTRL_ADDR 0x10012000
+#include "sifive/devices/gpio.h"
+
+#define SPI0_CTRL_ADDR 0x10014000
+#define SPI1_CTRL_ADDR 0x10024000
+#include "sifive/devices/spi.h"
+
+#define INT_PWM0_BASE 40
+#define INT_PWM1_BASE 44
+#define INT_PWM2_BASE 48
+
+#include "board.h"
+#include "irq_def.h"
+#include "evt_def.h"
+#include "i2s_def.h"
+#include "i2s_priv.h"
+#include "msgq_priv.h"
+
+ .section .data.entry
+ .align 4
+
+.global eos_trap_entry
+eos_trap_entry:
+ addi sp, sp, -8*REGBYTES
+ STORE x8, 0*REGBYTES(sp)
+ STORE x9, 1*REGBYTES(sp)
+ STORE x18, 2*REGBYTES(sp)
+ STORE x19, 3*REGBYTES(sp)
+ STORE x20, 4*REGBYTES(sp)
+ STORE x21, 5*REGBYTES(sp)
+ STORE x22, 6*REGBYTES(sp)
+ STORE x23, 7*REGBYTES(sp)
+
+ csrr x8, mcause
+ li x18, MCAUSE_EXT
+ bne x8, x18, handle_intr
+ li x18, PLIC_CLAIM
+ lw x9, 0(x18)
+ li x18, I2S_IRQ_WS_ID
+ beq x9, x18, i2s_handle_ws
+ li x18, I2S_IRQ_SD_ID
+ beq x9, x18, i2s_handle_sd
+ j handle_intr
+
+evtq_push:
+ la x9, _eos_event_q
+ lbu x18, MSGQ_OFF_IDXR(x9)
+ lbu x19, MSGQ_OFF_IDXW(x9)
+ lbu x20, MSGQ_OFF_SIZE(x9)
+
+ sub x18, x19, x18
+ andi x18, x18, 0xff
+ beq x18, x20, 0f
+
+ addi x20, x20, -1
+ and x20, x20, x19
+ li x18, MSGQ_ITEM_SIZE
+ mul x20, x20, x18
+ lw x21, MSGQ_OFF_ARRAY(x9)
+ add x21, x21, x20
+
+ addi x19, x19, 1
+ sb x19, MSGQ_OFF_IDXW(x9)
+ jalr x0, x22
+
+0:
+ mv x20, x0
+ jalr x0, x21
+
+i2s_handle_sd:
+ # exit if too early
+ li x18, I2S_CTRL_ADDR_WS_SPK
+ lw x8, PWM_COUNT(x18)
+ lw x9, PWM_CMP3(x18)
+ bltu x8, x9, i2s_handle_sd_exit
+
+ # disable sd irq
+ li x18, PLIC_PRIORITY
+ sw x0, 4*I2S_IRQ_SD_ID(x18)
+
+ la x9, _eos_i2s_fmt
+ lw x23, 0(x9)
+
+i2s_abuf_pop:
+ # pop from spk buf -> x8
+ mv x8, x0
+ la x9, _eos_i2s_spk_buf
+ lhu x18, I2S_ABUF_OFF_IDXR(x9)
+ lhu x19, I2S_ABUF_OFF_IDXW(x9)
+ lhu x20, I2S_ABUF_OFF_SIZE(x9)
+
+ beq x18, x19, i2s_handle_sd_xchg
+
+ addi x20, x20, -1
+ and x20, x20, x18
+ lw x21, I2S_ABUF_OFF_ARRAY(x9)
+ add x21, x21, x20
+ bnez x23, 0f
+ lbu x8, 0(x21)
+ addi x18, x18, 1
+ j 1f
+0:
+ lb x8, 0(x21)
+ lbu x20, 1(x21)
+ slli x8, x8, 8
+ or x8, x8, x20
+ addi x18, x18, 2
+1:
+ sh x18, I2S_ABUF_OFF_IDXR(x9)
+
+ li x21, 0xffff
+ sub x18, x19, x18
+ and x18, x18, x21
+
+ # check for push to event queue
+ la x9, _eos_i2s_spk_wm
+ lw x20, 0(x9)
+ beqz x20, i2s_decode
+ bgtu x18, x20, i2s_decode
+
+ la x9, _eos_i2s_spk_evt_enable
+ lw x18, 0(x9)
+ beqz x18, i2s_decode
+ sw x0, 0(x9)
+
+ # push to event queue
+ jal x22, evtq_push
+ beqz x21, i2s_decode
+ li x18, (EOS_EVT_I2S | EOS_I2S_ETYPE_SPK)
+ sb x18, MSGQ_ITEM_OFF_TYPE(x21)
+
+i2s_decode:
+ bnez x23, i2s_handle_sd_xchg
+ # aLaw decode -> x8
+ xori x8, x8, 0x55
+ andi x9, x8, 0x80
+ beqz x9, 0f
+ li x9, 1
+ slli x9, x9, 7
+ not x9, x9
+ and x8, x8, x9
+ li x9, -1
+0:
+ andi x18, x8, 0xf0
+ srli x18, x18, 4
+ addi x18, x18, 4
+
+ li x19, 4
+ beq x18, x19, 1f
+
+ andi x8, x8, 0x0f
+ addi x19, x18, -4
+ sll x8, x8, x19
+
+ li x19, 1
+ sll x19, x19, x18
+ or x8, x8, x19
+
+ li x19, 1
+ addi x18, x18, -5
+ sll x19, x19, x18
+ or x8, x8, x19
+ j 2f
+1:
+ slli x8, x8, 1
+ ori x8, x8, 1
+2:
+ beqz x9, i2s_handle_sd_xchg
+ mul x8, x8, x9
+
+i2s_handle_sd_xchg:
+ # read/write shift reg: x8 -> sr -> x8
+ li x18, GPIO_CTRL_ADDR
+ li x19, (0x1 << I2S_PIN_SD_IN)
+ li x20, (0x1 << I2S_PIN_SD_OUT)
+ li x21, (0x1 << I2S_PIN_CK_SR)
+ lw x22, GPIO_OUTPUT_VAL(x18)
+
+ lw x9, GPIO_OUTPUT_EN(x18)
+ or x9, x9, x20
+ sw x9, GPIO_OUTPUT_EN(x18)
+
+ not x20, x20
+ xor x22, x22, x21
+
+ li x23, 16
+0:
+ # write bit
+ li x9, 1
+ slli x9, x9, 15
+ and x9, x8, x9
+ slli x8, x8, 1
+#if I2S_PIN_SD_OUT > 15
+ slli x9, x9, (I2S_PIN_SD_OUT - 15)
+#else
+ srli x9, x9, (15 - I2S_PIN_SD_OUT)
+#endif
+ and x22, x22, x20
+ or x22, x22, x9
+
+ # read bit
+ lw x9, GPIO_INPUT_VAL(x18)
+ and x9, x9, x19
+ srli x9, x9, I2S_PIN_SD_IN
+ or x8, x8, x9
+
+ # 74HC595 ck low (I2S_PIN_CK_SR high)
+ xor x22, x22, x21
+ sw x22, GPIO_OUTPUT_VAL(x18)
+
+ # idle
+ li x9, I2S_IDLE_CYCLES
+1:
+ addi x9, x9, -1
+ bnez x9, 1b
+
+ # 74HC595 ck high (I2S_PIN_CK_SR low)
+ xor x22, x22, x21
+ sw x22, GPIO_OUTPUT_VAL(x18)
+
+ # idle
+ li x9, I2S_IDLE_CYCLES
+1:
+ addi x9, x9, -1
+ bnez x9, 1b
+
+ addi x23, x23, -1
+ beqz x23, 2f
+ j 0b
+
+2:
+ # 74HC595 ck low (I2S_PIN_CK_SR high)
+ xor x22, x22, x21
+ sw x22, GPIO_OUTPUT_VAL(x18)
+
+ lw x9, GPIO_OUTPUT_EN(x18)
+ and x9, x9, x20
+ sw x9, GPIO_OUTPUT_EN(x18)
+
+ slli x8, x8, 16
+ srai x8, x8, 16
+
+ la x9, _eos_i2s_fmt
+ lw x23, 0(x9)
+
+i2s_encode:
+ bnez x23, i2s_abuf_push
+ # aLaw encode -> x8
+ li x18, 0x800
+ li x19, 7
+ bgez x8, 0f
+ neg x8, x8
+ lui x9, 0x80000
+ or x8, x8, x9
+0:
+ and x9, x8, x18
+ beq x9, x18, 1f
+ beqz x19, 1f
+ srli x18, x18, 1
+ addi x19, x19, -1
+ j 0b
+1:
+ mv x9, x19
+ bnez x9, 2f
+ addi x9, x9, 1
+2:
+ sra x8, x8, x9
+ li x9, 0x8000000f
+ and x8, x8, x9
+ slli x19, x19, 4
+ or x8, x8, x19
+ bgez x8, 3f
+ ori x8, x8, 0x80
+3:
+ xori x8, x8, 0x55
+ andi x8, x8, 0xff
+
+i2s_abuf_push:
+ # push to mic buf
+ la x9, _eos_i2s_mic_buf
+ lhu x18, I2S_ABUF_OFF_IDXR(x9)
+ lhu x19, I2S_ABUF_OFF_IDXW(x9)
+ lhu x20, I2S_ABUF_OFF_SIZE(x9)
+ li x21, 0xffff
+
+ sub x18, x19, x18
+ and x18, x18, x21
+ beq x18, x20, i2s_handle_sd_exit
+
+ addi x20, x20, -1
+ and x20, x20, x19
+ lw x21, I2S_ABUF_OFF_ARRAY(x9)
+ add x21, x21, x20
+ bnez x23, 0f
+ sb x8, 0(x21)
+ addi x19, x19, 1
+ addi x18, x18, 1
+ j 1f
+0:
+ sb x8, 1(x21)
+ srli x8, x8, 8
+ sb x8, 0(x21)
+ addi x19, x19, 2
+ addi x18, x18, 2
+1:
+ sh x19, I2S_ABUF_OFF_IDXW(x9)
+
+ # check for push to event queue
+ la x9, _eos_i2s_mic_wm
+ lw x20, 0(x9)
+ beqz x20, i2s_handle_sd_exit
+ bltu x18, x20, i2s_handle_sd_exit
+
+ la x9, _eos_i2s_mic_evt_enable
+ lw x18, 0(x9)
+ beqz x18, i2s_handle_sd_exit
+ sw x0, 0(x9)
+
+ # push to event queue
+ jal x22, evtq_push
+ beqz x21, i2s_handle_sd_exit
+ li x18, (EOS_EVT_I2S | EOS_I2S_ETYPE_MIC)
+ sb x18, MSGQ_ITEM_OFF_TYPE(x21)
+
+i2s_handle_sd_exit:
+ # complete
+ li x18, I2S_IRQ_SD_ID
+ li x19, PLIC_CLAIM
+ sw x18, 0(x19)
+
+ # exit
+ j trap_exit_data
+
+i2s_handle_ws:
+ # enable sd irq
+ li x18, PLIC_PRIORITY
+ li x19, IRQ_PRIORITY_I2S_SD
+ sw x19, 4*I2S_IRQ_SD_ID(x18)
+
+ # complete
+ li x18, I2S_IRQ_WS_ID
+ li x19, PLIC_CLAIM
+ sw x18, 0(x19)
+
+ # exit
+ j trap_exit_data
+
+.global _eos_i2s_start_pwm
+_eos_i2s_start_pwm:
+ addi sp, sp, -8*REGBYTES
+ STORE x8, 0*REGBYTES(sp)
+ STORE x9, 1*REGBYTES(sp)
+ STORE x18, 2*REGBYTES(sp)
+ STORE x19, 3*REGBYTES(sp)
+ STORE x20, 4*REGBYTES(sp)
+ STORE x21, 5*REGBYTES(sp)
+ STORE x22, 6*REGBYTES(sp)
+ STORE x23, 7*REGBYTES(sp)
+
+ li x18, I2S_CTRL_ADDR_CK
+ li x19, I2S_CTRL_ADDR_WS_MIC
+ li x20, I2S_CTRL_ADDR_WS_SPK
+ li x21, PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | I2S_PWM_SCALE_CK
+ li x22, PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | PWM_CFG_CMP2GANG
+ li x23, PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | PWM_CFG_CMP1GANG
+ sw x21, PWM_CFG(x18)
+ sw x22, PWM_CFG(x19)
+ sw x23, PWM_CFG(x20)
+
+ LOAD x8, 0*REGBYTES(sp)
+ LOAD x9, 1*REGBYTES(sp)
+ LOAD x18, 2*REGBYTES(sp)
+ LOAD x19, 3*REGBYTES(sp)
+ LOAD x20, 4*REGBYTES(sp)
+ LOAD x21, 5*REGBYTES(sp)
+ LOAD x22, 6*REGBYTES(sp)
+ LOAD x23, 7*REGBYTES(sp)
+ addi sp, sp, 8*REGBYTES
+
+ ret
+
+.global _eos_flash_set
+_eos_flash_set:
+ li a3, SPI0_CTRL_ADDR
+ sw x0, SPI_REG_FCTRL(a3)
+ sw a0, SPI_REG_SCKDIV(a3)
+ sw a1, SPI_REG_FFMT(a3)
+ li a0, 1
+ sw a0, SPI_REG_FCTRL(a3)
+ ret
+
+trap_exit_data:
+ # Remain in M-mode after mret
+ li x18, MSTATUS_MPP
+ csrs mstatus, x18
+
+ LOAD x8, 0*REGBYTES(sp)
+ LOAD x9, 1*REGBYTES(sp)
+ LOAD x18, 2*REGBYTES(sp)
+ LOAD x19, 3*REGBYTES(sp)
+ LOAD x20, 4*REGBYTES(sp)
+ LOAD x21, 5*REGBYTES(sp)
+ LOAD x22, 6*REGBYTES(sp)
+ LOAD x23, 7*REGBYTES(sp)
+ addi sp, sp, 8*REGBYTES
+
+ mret
+
+handle_intr:
+ lui x18, %hi(trap_entry_text)
+ addi x18, x18, %lo(trap_entry_text)
+ jalr x0, x18
+
+ .section .text.entry
+ .align 2
+
+trap_entry_text:
+ addi sp, sp, -24*REGBYTES
+
+ STORE x1, 0*REGBYTES(sp)
+ STORE x2, 1*REGBYTES(sp)
+ STORE x3, 2*REGBYTES(sp)
+ STORE x4, 3*REGBYTES(sp)
+ STORE x5, 4*REGBYTES(sp)
+ STORE x6, 5*REGBYTES(sp)
+ STORE x7, 6*REGBYTES(sp)
+ STORE x10, 7*REGBYTES(sp)
+ STORE x11, 8*REGBYTES(sp)
+ STORE x12, 9*REGBYTES(sp)
+ STORE x13, 10*REGBYTES(sp)
+ STORE x14, 11*REGBYTES(sp)
+ STORE x15, 12*REGBYTES(sp)
+ STORE x16, 13*REGBYTES(sp)
+ STORE x17, 14*REGBYTES(sp)
+ STORE x24, 15*REGBYTES(sp)
+ STORE x25, 16*REGBYTES(sp)
+ STORE x26, 17*REGBYTES(sp)
+ STORE x27, 18*REGBYTES(sp)
+ STORE x28, 19*REGBYTES(sp)
+ STORE x29, 20*REGBYTES(sp)
+ STORE x30, 21*REGBYTES(sp)
+ STORE x31, 22*REGBYTES(sp)
+
+ li x18, MCAUSE_TIMER
+ beq x8, x18, handle_timer
+ li x18, MCAUSE_EXT
+ beq x8, x18, handle_ext
+ mv a0, x8
+ call _exit
+
+handle_timer:
+ call _eos_timer_handle
+ j trap_exit_text
+
+handle_ext:
+ mv a0, x9
+ call eos_intr_handle
+ li x18, PLIC_CLAIM
+ sw a0, 0(x18)
+ j trap_exit_text
+
+trap_exit_text:
+ # Remain in M-mode after mret
+ li t0, MSTATUS_MPP
+ csrs mstatus, t0
+
+ LOAD x1, 0*REGBYTES(sp)
+ LOAD x2, 1*REGBYTES(sp)
+ LOAD x3, 2*REGBYTES(sp)
+ LOAD x4, 3*REGBYTES(sp)
+ LOAD x5, 4*REGBYTES(sp)
+ LOAD x6, 5*REGBYTES(sp)
+ LOAD x7, 6*REGBYTES(sp)
+ LOAD x10, 7*REGBYTES(sp)
+ LOAD x11, 8*REGBYTES(sp)
+ LOAD x12, 9*REGBYTES(sp)
+ LOAD x13, 10*REGBYTES(sp)
+ LOAD x14, 11*REGBYTES(sp)
+ LOAD x15, 12*REGBYTES(sp)
+ LOAD x16, 13*REGBYTES(sp)
+ LOAD x17, 14*REGBYTES(sp)
+ LOAD x24, 15*REGBYTES(sp)
+ LOAD x25, 16*REGBYTES(sp)
+ LOAD x26, 17*REGBYTES(sp)
+ LOAD x27, 18*REGBYTES(sp)
+ LOAD x28, 19*REGBYTES(sp)
+ LOAD x29, 20*REGBYTES(sp)
+ LOAD x30, 21*REGBYTES(sp)
+ LOAD x31, 22*REGBYTES(sp)
+
+ LOAD x8, 24*REGBYTES(sp)
+ LOAD x9, 25*REGBYTES(sp)
+ LOAD x18, 26*REGBYTES(sp)
+ LOAD x19, 27*REGBYTES(sp)
+ LOAD x20, 28*REGBYTES(sp)
+ LOAD x21, 29*REGBYTES(sp)
+ LOAD x22, 30*REGBYTES(sp)
+ LOAD x23, 31*REGBYTES(sp)
+
+ addi sp, sp, 32*REGBYTES
+ mret
diff --git a/fw/fe310/eos/uart.c b/fw/fe310/eos/uart.c
new file mode 100644
index 0000000..c10b6d5
--- /dev/null
+++ b/fw/fe310/eos/uart.c
@@ -0,0 +1,92 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "encoding.h"
+#include "platform.h"
+
+#include "eos.h"
+#include "interrupt.h"
+#include "event.h"
+
+#include "uart.h"
+
+static eos_uart_handler_t uart_handler[EOS_UART_MAX_ETYPE];
+
+static void uart_handle_evt(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char idx = (type & ~EOS_EVT_MASK) - 1;
+
+ if ((idx < EOS_UART_MAX_ETYPE) && uart_handler[idx]) {
+ uart_handler[idx](type);
+ } else {
+ eos_evtq_bad_handler(type, buffer, len);
+ }
+}
+
+static void uart_handle_intr(void) {
+ if (UART0_REG(UART_REG_IP) & UART_IP_TXWM) {
+ UART0_REG(UART_REG_IE) &= ~UART_IP_TXWM;
+ eos_evtq_push_isr(EOS_EVT_UART | EOS_UART_ETYPE_TX, NULL, 0);
+ }
+ if (UART0_REG(UART_REG_IP) & UART_IP_RXWM) {
+ UART0_REG(UART_REG_IE) &= ~UART_IP_RXWM;
+ eos_evtq_push_isr(EOS_EVT_UART | EOS_UART_ETYPE_RX, NULL, 0);
+ }
+}
+
+void eos_uart_init(void) {
+ int i;
+
+ for (i=0; i<EOS_UART_MAX_ETYPE; i++) {
+ uart_handler[i] = NULL;
+ }
+ eos_evtq_set_handler(EOS_EVT_UART, uart_handle_evt);
+ eos_intr_set(INT_UART0_BASE, IRQ_PRIORITY_UART, uart_handle_intr);
+}
+
+void eos_uart_set_handler(unsigned char type, eos_uart_handler_t handler) {
+ if (type && (type <= EOS_UART_MAX_ETYPE)) uart_handler[type - 1] = handler;
+}
+
+void eos_uart_txwm_set(uint8_t wm) {
+ UART0_REG(UART_REG_TXCTRL) &= ~UART_TXWM(0xFFFF);
+ UART0_REG(UART_REG_TXCTRL) |= UART_TXWM(wm);
+ UART0_REG(UART_REG_IE) |= UART_IP_TXWM;
+}
+
+void eos_uart_txwm_clear(void) {
+ UART0_REG(UART_REG_IE) &= ~UART_IP_TXWM;
+}
+
+void eos_uart_rxwm_set(uint8_t wm) {
+ UART0_REG(UART_REG_RXCTRL) &= ~UART_RXWM(0xFFFF);
+ UART0_REG(UART_REG_RXCTRL) |= UART_RXWM(wm);
+ UART0_REG(UART_REG_IE) |= UART_IP_RXWM;
+}
+
+void eos_uart_rxwm_clear(void) {
+ UART0_REG(UART_REG_IE) &= ~UART_IP_RXWM;
+}
+
+int eos_uart_putc(int c, char b) {
+ if (b) {
+ while (UART0_REG(UART_REG_TXFIFO) & 0x80000000);
+ UART0_REG(UART_REG_TXFIFO) = c & 0xff;
+ } else {
+ if (UART0_REG(UART_REG_TXFIFO) & 0x80000000) return EOS_ERR_FULL;
+ UART0_REG(UART_REG_TXFIFO) = c & 0xff;
+ }
+ return EOS_OK;
+}
+
+int eos_uart_getc(char b) {
+ volatile uint32_t r;
+
+ if (b) {
+ while ((r = UART0_REG(UART_REG_RXFIFO)) & 0x80000000);
+ } else {
+ r = UART0_REG(UART_REG_RXFIFO);
+ if (r & 0x80000000) return EOS_ERR_EMPTY;
+ }
+ return r & 0xff;
+} \ No newline at end of file
diff --git a/fw/fe310/eos/uart.h b/fw/fe310/eos/uart.h
new file mode 100644
index 0000000..da5faab
--- /dev/null
+++ b/fw/fe310/eos/uart.h
@@ -0,0 +1,18 @@
+#include <stdint.h>
+
+#define EOS_UART_ETYPE_TX 1
+#define EOS_UART_ETYPE_RX 2
+
+#define EOS_UART_MAX_ETYPE 2
+
+typedef void (*eos_uart_handler_t) (unsigned char);
+
+void eos_uart_init(void);
+void eos_uart_set_handler(unsigned char type, eos_uart_handler_t handler);
+
+void eos_uart_txwm_set(uint8_t wm);
+void eos_uart_txwm_clear(void);
+void eos_uart_rxwm_set(uint8_t wm);
+void eos_uart_rxwm_clear(void);
+int eos_uart_putc(int c, char b);
+int eos_uart_getc(char b); \ No newline at end of file
diff --git a/fw/fe310/eos/unicode.c b/fw/fe310/eos/unicode.c
new file mode 100644
index 0000000..29100c7
--- /dev/null
+++ b/fw/fe310/eos/unicode.c
@@ -0,0 +1,195 @@
+#include "unicode.h"
+
+int utf8_enc(utf32_t ch, utf8_t *str) {
+ if (ch <= 0x7f) {
+ str[0] = ch;
+ return 1;
+ } else if (ch <= 0x7ff) {
+ str[0] = 0xc0 | (ch >> 6);
+ str[1] = 0x80 | (ch & 0x3f);
+ return 2;
+ } else if (ch <= 0xffff) {
+ if ((ch >= 0xd800) && (ch <= 0xdfff)) return UTF_ERR;
+ str[0] = 0xe0 | (ch >> 12);
+ str[1] = 0x80 | ((ch >> 6) & 0x3f);
+ str[2] = 0x80 | (ch & 0x3f);
+ return 3;
+ } else if (ch <= 0x10ffff) {
+ str[0] = 0xf0 | (ch >> 18);
+ str[1] = 0x80 | ((ch >> 12) & 0x3f);
+ str[2] = 0x80 | ((ch >> 6) & 0x3f);
+ str[3] = 0x80 | (ch & 0x3f);
+ return 4;
+ } else {
+ return UTF_ERR;
+ }
+}
+
+int utf8_dec(utf8_t *str, utf32_t *ch) {
+ if ((str[0] & 0x80) == 0x00) {
+ *ch = str[0];
+ return 1;
+ } else if ((str[0] & 0xe0) == 0xc0) {
+ if ((str[1] & 0xc0) != 0x80) return UTF_ERR;
+ *ch = (utf32_t)(str[0] & 0x1f) << 6;
+ *ch |= (utf32_t)(str[1] & 0x3f);
+ if (*ch < 0x80) return UTF_ERR;
+ return 2;
+ } else if ((str[0] & 0xf0) == 0xe0) {
+ if (((str[1] & 0xc0) != 0x80) || ((str[2] & 0xc0) != 0x80)) return UTF_ERR;
+ *ch = (utf32_t)(str[0] & 0x0f) << 12;
+ *ch |= (utf32_t)(str[1] & 0x3f) << 6;
+ *ch |= (utf32_t)(str[2] & 0x3f);
+ if ((*ch >= 0xd800) && (*ch <= 0xdfff)) return UTF_ERR;
+ if (*ch < 0x800) return UTF_ERR;
+ return 3;
+ } else if ((str[0] & 0xf8) == 0xf0) {
+ if (((str[1] & 0xc0) != 0x80) || ((str[2] & 0xc0) != 0x80) || ((str[3] & 0xc0) != 0x80)) return UTF_ERR;
+ *ch = (utf32_t)(str[0] & 0x07) << 18;
+ *ch |= (utf32_t)(str[1] & 0x0f) << 12;
+ *ch |= (utf32_t)(str[2] & 0x3f) << 6;
+ *ch |= (utf32_t)(str[3] & 0x3f);
+ if (*ch < 0x010000) return UTF_ERR;
+ if (*ch > 0x10ffff) return UTF_ERR;
+ return 4;
+ } else {
+ return UTF_ERR;
+ }
+}
+
+int utf8_len(utf8_t *str) {
+ if ((*str & 0xf8) == 0xf0) return 4;
+ if ((*str & 0xf0) == 0xe0) return 3;
+ if ((*str & 0xe0) == 0xc0) return 2;
+ if ((*str & 0x80) == 0x00) return 1;
+
+ return UTF_ERR;
+}
+
+int utf8_seek(utf8_t *str, int off, utf32_t *ch) {
+ int i;
+ int len = 0;
+
+ if (off < 0) {
+ off = -off;
+ for (i=0; i<off; i++) {
+ len--;
+ while ((str[len] & 0xc0) == 0x80) len--;
+ }
+ } else {
+ for (i=0; i<off; i++) {
+ if ((str[len] & 0x80) == 0x00) {
+ len += 1;
+ } else if ((str[0] & 0xe0) == 0xc0) {
+ len += 2;
+ } else if ((str[0] & 0xf0) == 0xe0) {
+ len += 3;
+ } else if ((str[0] & 0xf8) == 0xf0) {
+ len += 4;
+ }
+ }
+ }
+ utf8_dec(str + len, ch);
+ return len;
+}
+
+int utf8_verify(utf8_t *str, int str_size, int *str_len) {
+ utf32_t ch;
+ uint8_t ch_l;
+ int len = 0;
+
+ while (len < str_size) {
+ if (str_size - len < 4) {
+ int _len = utf8_len(str + len);
+ if ((_len == UTF_ERR) || ((str_size - len) < _len)) break;
+ }
+ ch_l = utf8_dec(str + len, &ch);
+ if (ch_l > 0) {
+ if (ch == 0) {
+ if (str_len) *str_len = len;
+ return UTF_OK;
+ }
+ len += ch_l;
+ } else {
+ break;
+ }
+ }
+
+ if (str_len) *str_len = len;
+ return UTF_ERR;
+}
+
+int utf16_enc(utf32_t ch, uint8_t *str) {
+ if (ch <= 0xffff) {
+ if ((ch >= 0xd800) && (ch <= 0xdfff)) return UTF_ERR;
+ str[0] = ch >> 8;
+ str[1] = ch & 0xff;
+ return 2;
+ } else if (ch <= 0x10ffff) {
+ uint16_t hi;
+ uint16_t lo;
+
+ ch -= 0x10000;
+ hi = (ch >> 10) + 0xd800;
+ lo = (ch & 0x3ff) + 0xdc00;
+ str[0] = hi >> 8;
+ str[1] = hi & 0xff;
+ str[2] = lo >> 8;
+ str[3] = lo & 0xff;
+ return 4;
+ } else {
+ return UTF_ERR;
+ }
+}
+
+int utf16_dec(uint8_t *str, utf32_t *ch) {
+ *ch = (str[0] << 8) | str[1];
+ if ((*ch >= 0xd800) && (*ch <= 0xdfff)) {
+ uint16_t hi = *ch;
+ uint16_t lo;
+
+ if (hi > 0xdbff) return UTF_ERR;
+ lo = (str[2] << 8) | str[3];
+ if ((lo < 0xdc00) || (lo > 0xdfff)) return UTF_ERR;
+ *ch = (((hi - 0xd800) << 10) | (lo - 0xdc00)) + 0x10000;
+ return 4;
+ } else {
+ return 2;
+ }
+}
+
+int utf16_len(uint8_t *str) {
+ uint16_t ch = (str[0] << 8) | str[1];
+
+ if ((ch >= 0xdc00) && (ch <= 0xdfff)) return UTF_ERR;
+ if ((ch >= 0xd800) && (ch <= 0xdfff)) return 4;
+ return 2;
+}
+
+int utf16_seek(uint8_t *str, int off, utf32_t *ch) {
+ int i;
+ int len = 0;
+ uint16_t cu;
+
+ if (off < 0) {
+ off = -off;
+ for (i=0; i<off; i++) {
+ len -= 2;
+ cu = (str[len] << 8) | str[len + 1];
+ if ((cu >= 0xdc00) && (cu <= 0xdfff)) {
+ len -= 2;
+ }
+ }
+ } else {
+ for (i=0; i<off; i++) {
+ cu = (str[len] << 8) | str[len + 1];
+ if ((cu >= 0xd800) && (cu <= 0xdbff)) {
+ len += 4;
+ } else {
+ len += 2;
+ }
+ }
+ }
+ utf16_dec(str + len, ch);
+ return len;
+}
diff --git a/fw/fe310/eos/unicode.h b/fw/fe310/eos/unicode.h
new file mode 100644
index 0000000..12fa99c
--- /dev/null
+++ b/fw/fe310/eos/unicode.h
@@ -0,0 +1,19 @@
+#include <stdint.h>
+
+#define UTF_OK 0
+#define UTF_ERR -1
+
+typedef uint8_t utf8_t;
+typedef uint16_t utf16_t;
+typedef uint32_t utf32_t;
+
+int utf8_enc(utf32_t ch, utf8_t *str);
+int utf8_dec(utf8_t *str, utf32_t *ch);
+int utf8_len(utf8_t *str);
+int utf8_seek(utf8_t *str, int off, utf32_t *ch);
+int utf8_verify(utf8_t *str, int str_size, int *str_len);
+
+int utf16_enc(utf32_t ch, uint8_t *str);
+int utf16_dec(uint8_t *str, utf32_t *ch);
+int utf16_len(uint8_t *str);
+int utf16_seek(uint8_t *str, int off, utf32_t *ch);
diff --git a/fw/fe310/eos/wifi.c b/fw/fe310/eos/wifi.c
new file mode 100644
index 0000000..ca29a95
--- /dev/null
+++ b/fw/fe310/eos/wifi.c
@@ -0,0 +1,45 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "eos.h"
+#include "event.h"
+#include "net.h"
+
+#include "wifi.h"
+
+static eos_evt_handler_t evt_handler[EOS_WIFI_MAX_MTYPE];
+
+static void wifi_handle_msg(unsigned char type, unsigned char *buffer, uint16_t len) {
+ unsigned char mtype;
+
+ if ((buffer == NULL) || (len < 1)) {
+ eos_net_bad_handler(type, buffer, len);
+ return;
+ }
+
+ mtype = buffer[0];
+ if ((mtype < EOS_WIFI_MAX_MTYPE) && evt_handler[mtype]) {
+ evt_handler[mtype](mtype, buffer, len);
+ } else {
+ eos_net_bad_handler(type, buffer, len);
+ }
+}
+
+void eos_wifi_init(void) {
+ int i;
+
+ for (i=0; i<EOS_WIFI_MAX_MTYPE; i++) {
+ evt_handler[i] = NULL;
+ }
+ eos_net_set_handler(EOS_NET_MTYPE_WIFI, wifi_handle_msg);
+}
+
+void eos_wifi_set_handler(unsigned char mtype, eos_evt_handler_t handler) {
+ if (mtype < EOS_WIFI_MAX_MTYPE) evt_handler[mtype] = handler;
+}
+
+eos_evt_handler_t eos_wifi_get_handler(unsigned char mtype) {
+ if (mtype < EOS_WIFI_MAX_MTYPE) return evt_handler[mtype];
+ return NULL;
+}
diff --git a/fw/fe310/eos/wifi.h b/fw/fe310/eos/wifi.h
new file mode 100644
index 0000000..bb629ed
--- /dev/null
+++ b/fw/fe310/eos/wifi.h
@@ -0,0 +1,13 @@
+#include <stdint.h>
+#include "event.h"
+
+#define EOS_WIFI_MTYPE_SCAN 1
+#define EOS_WIFI_MTYPE_CONFIG 2
+#define EOS_WIFI_MTYPE_CONNECT 3
+#define EOS_WIFI_MTYPE_DISCONNECT 4
+
+#define EOS_WIFI_MAX_MTYPE 5
+
+void eos_wifi_init(void);
+void eos_wifi_set_handler(unsigned char mtype, eos_evt_handler_t handler);
+eos_evt_handler_t eos_wifi_get_handler(unsigned char mtype);
diff --git a/fw/fe310/fsfat/Makefile b/fw/fe310/fsfat/Makefile
new file mode 100644
index 0000000..a20a5be
--- /dev/null
+++ b/fw/fe310/fsfat/Makefile
@@ -0,0 +1,17 @@
+include ../common.mk
+
+CFLAGS += -I. -I..
+
+obj = diskio.o ff.o
+
+
+%.o: %.c %.h
+ $(CC) $(CFLAGS) -c $<
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c $<
+
+all: $(obj)
+
+clean:
+ rm -f *.o
diff --git a/fw/fe310/fsfat/diskio.c b/fw/fe310/fsfat/diskio.c
new file mode 100644
index 0000000..026ffd2
--- /dev/null
+++ b/fw/fe310/fsfat/diskio.c
@@ -0,0 +1,141 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
+/*-----------------------------------------------------------------------*/
+/* If a working storage control module is available, it should be */
+/* attached to the FatFs via a glue function rather than modifying it. */
+/* This is an example of glue functions to attach various exsisting */
+/* storage control modules to the FatFs module with a defined API. */
+/*-----------------------------------------------------------------------*/
+
+#include "ff.h" /* Obtains integer types */
+#include "diskio.h" /* Declarations of disk functions */
+
+#include <eos/sdcard.h>
+
+#define TIMEOUT 1000
+#define TIMEOUT_TRIM 60000
+
+/*-----------------------------------------------------------------------*/
+/* Get Drive Status */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_status (
+ BYTE pdrv /* Physical drive nmuber to identify the drive */
+)
+{
+ if (pdrv || (eos_sdc_type() == 0)) return STA_NOINIT;
+ return 0;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Inidialize a Drive */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_initialize (
+ BYTE pdrv /* Physical drive nmuber to identify the drive */
+)
+{
+ if (pdrv || (eos_sdc_type() == 0)) return STA_NOINIT;
+ return 0;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Sector(s) */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+ BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ BYTE *buff, /* Data buffer to store read data */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to read */
+)
+{
+ int rv;
+
+ if (pdrv) return RES_PARERR;
+ if (eos_sdc_type() == 0) return RES_NOTRDY;
+
+ rv = eos_sdc_sect_read(sector, count, (uint8_t *)buff);
+ return rv ? RES_ERROR : RES_OK;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Write Sector(s) */
+/*-----------------------------------------------------------------------*/
+
+#if FF_FS_READONLY == 0
+
+DRESULT disk_write (
+ BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ const BYTE *buff, /* Data to be written */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to write */
+)
+{
+ int rv;
+
+ if (pdrv) return RES_PARERR;
+ if (eos_sdc_type() == 0) return RES_NOTRDY;
+
+ rv = eos_sdc_sect_write(sector, count, (uint8_t *)buff);
+ return rv ? RES_ERROR : RES_OK;
+}
+
+#endif
+
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_ioctl (
+ BYTE pdrv, /* Physical drive nmuber (0..) */
+ BYTE cmd, /* Control code */
+ void *buff /* Buffer to send/receive control data */
+)
+{
+ DRESULT res;
+ uint32_t ret;
+ int rv;
+
+ if (pdrv) return RES_PARERR;
+ if (eos_sdc_type() == 0) return RES_NOTRDY;
+
+ res = RES_ERROR;
+
+ switch (cmd) {
+ case CTRL_SYNC:
+ rv = eos_sdc_sync(TIMEOUT);
+ if (!rv) res = RES_OK;
+ break;
+
+ case GET_SECTOR_COUNT:
+ rv = eos_sdc_get_sect_count(TIMEOUT, &ret);
+ if (!rv) {
+ *(LBA_t*)buff = ret;
+ res = RES_OK;
+ }
+ break;
+
+ case GET_BLOCK_SIZE:
+ rv = eos_sdc_get_blk_size(TIMEOUT, &ret);
+ if (!rv) {
+ *(DWORD*)buff = ret;
+ res = RES_OK;
+ }
+ break;
+
+ case CTRL_TRIM:
+ rv = eos_sdc_erase(((LBA_t*)buff)[0], ((LBA_t*)buff)[1], TIMEOUT_TRIM);
+ if (!rv) res = RES_OK;
+ break;
+ }
+
+ return res;
+}
diff --git a/fw/fe310/fsfat/diskio.h b/fw/fe310/fsfat/diskio.h
new file mode 100644
index 0000000..c22afaa
--- /dev/null
+++ b/fw/fe310/fsfat/diskio.h
@@ -0,0 +1,77 @@
+/*-----------------------------------------------------------------------/
+/ Low level disk interface modlue include file (C)ChaN, 2019 /
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 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;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+
+DSTATUS disk_initialize (BYTE pdrv);
+DSTATUS disk_status (BYTE pdrv);
+DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
+DRESULT disk_ioctl (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 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) */
+
+/* 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
diff --git a/fw/fe310/fsfat/ff.c b/fw/fe310/fsfat/ff.c
new file mode 100644
index 0000000..cbdf685
--- /dev/null
+++ b/fw/fe310/fsfat/ff.c
@@ -0,0 +1,6982 @@
+/*----------------------------------------------------------------------------/
+/ FatFs - Generic FAT Filesystem Module R0.14b /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2021, 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 <string.h>
+#include "ff.h" /* Declarations of FatFs API */
+#include "diskio.h" /* Declarations of device I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if FF_DEFINED != 86631 /* 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 IsSeparator(c) ((c) == '/' || (c) == '\\')
+#define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!'))
+#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 in FAT */
+#define AM_MASKX 0x37 /* Mask of defined bits in exFAT */
+
+
+/* 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 */
+
+#define GPTH_Sign 0 /* GPT: Header signature (8-byte) */
+#define GPTH_Rev 8 /* GPT: Revision (DWORD) */
+#define GPTH_Size 12 /* GPT: Header size (DWORD) */
+#define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */
+#define GPTH_CurLba 24 /* GPT: Main header LBA (QWORD) */
+#define GPTH_BakLba 32 /* GPT: Backup header LBA (QWORD) */
+#define GPTH_FstLba 40 /* GPT: First LBA for partitions (QWORD) */
+#define GPTH_LstLba 48 /* GPT: Last LBA for partitions (QWORD) */
+#define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */
+#define GPTH_PtOfs 72 /* GPT: Partation table LBA (QWORD) */
+#define GPTH_PtNum 80 /* GPT: Number of table entries (DWORD) */
+#define GPTH_PteSize 84 /* GPT: Size of table entry (DWORD) */
+#define GPTH_PtBcc 88 /* GPT: Partation table BCC (DWORD) */
+#define SZ_GPTE 128 /* GPT: Size of partition table entry */
+#define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */
+#define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */
+#define GPTE_FstLba 32 /* GPT PTE: First LBA (QWORD) */
+#define GPTE_LstLba 40 /* GPT PTE: Last LBA inclusive (QWORD) */
+#define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */
+#define GPTE_Name 56 /* GPT PTE: Name */
+
+
+/* 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 in 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 logical drive - 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 number (0:auto search, 1..:forced partition number) */
+#else
+#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
+#define LD2PT(vol) 0 /* Auto partition search */
+#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
+
+#if FF_LBA64
+#if FF_MIN_GPT > 0x100000000
+#error Wrong FF_MIN_GPT setting
+#endif
+static const BYTE GUID_MS_Basic[16] = {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7};
+#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
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* 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 */
+/*-----------------------------------------------------------------------*/
+
+/* Test if the byte 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 byte 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 Unicode code point from the TCHAR string in defined API encodeing */
+static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 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 an encoding 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 || IsSurrogate(uc)) 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;
+}
+
+
+/* Store a Unicode char in defined API encoding */
+static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
+ DWORD chr, /* UTF-16 encoded character (Surrogate pair 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 it back into the volume */
+ 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 */
+ LBA_t sect /* Sector LBA to make appearance in the fs->win[] */
+)
+{
+ FRESULT res = FR_OK;
+
+
+ if (sect != fs->winsect) { /* Window offset changed? */
+#if !FF_FS_READONLY
+ res = sync_window(fs); /* Flush the window */
+#endif
+ if (res == FR_OK) { /* Fill sector window with new data */
+ if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) {
+ sect = (LBA_t)0 - 1; /* Invalidate window if read data is not valid */
+ res = FR_DISK_ERR;
+ }
+ fs->winsect = sect;
+ }
+ }
+ 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 */
+ memset(fs->win, 0, sizeof fs->win);
+ st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */
+ st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */
+ st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */
+ st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */
+ st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */
+ fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */
+ 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 LBA_t 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 + (LBA_t)fs->csize * clst; /* Start sector number of the cluster */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of an 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)((LBA_t)((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;
+ }
+ }
+ val = 1; /* Internal error */
+ break;
+#endif
+ default:
+ val = 1; /* Internal error */
+ }
+ }
+
+ return val;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of an 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; /* Update 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)); /* Update 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;
+ LBA_t 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
+ LBA_t 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 to be freed */
+ rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
+ disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
+#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 */
+)
+{
+ LBA_t 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 */
+ memset(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? */
+ memset(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 = (DWORD)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 n_ent /* 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)) { /* Is the entry free? */
+#else
+ if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */
+#endif
+ if (++n == n_ent) break; /* Is a block of contiguous free entries found? */
+ } else {
+ n = 0; /* Not a free entry, restart to search */
+ }
+ res = dir_next(dp, 1); /* Next entry with table stretch enabled */
+ } while (res == FR_OK);
+ }
+
+ 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 following items */
+ } 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 in directory form */
+ const WCHAR* lfn, /* Pointer to LFN */
+ UINT seq /* Sequence number */
+)
+{
+ BYTE ns[8], c;
+ UINT i, j;
+ WCHAR wc;
+ DWORD sreg;
+
+
+ memcpy(dst, src, 11); /* Prepare the SFN to be modified */
+
+ if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */
+ sreg = seq;
+ while (*lfn) { /* Create a CRC as hash value */
+ wc = *lfn++;
+ for (i = 0; i < 16; i++) {
+ sreg = (sreg << 1) + (wc & 1);
+ wc >>= 1;
+ if (sreg & 0x10000) sreg ^= 0x11021;
+ }
+ }
+ seq = (UINT)sreg;
+ }
+
+ /* Make suffix (~ + hexdecimal) */
+ i = 7;
+ do {
+ c = (BYTE)((seq % 16) + '0'); seq /= 16;
+ if (c > '9') c += 7;
+ ns[i--] = c;
+ } while (i && seq);
+ ns[i] = '~';
+
+ /* Append the suffix to the SFN body */
+ for (j = 0; j < i && dst[j] != ' '; j++) { /* Find the offset to append */
+ if (dbc_1st(dst[j])) { /* To avoid DBC break up */
+ if (j == i - 1) break;
+ j++;
+ }
+ }
+ do { /* Append the suffix */
+ 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
+
+
+
+/*-----------------------------------*/
+/* 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 */
+ memcpy(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 */
+ memcpy(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)) memcpy(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;
+ memcpy(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 */
+ memset(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) && !memcmp(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) && !memcmp(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, len, n_ent;
+ BYTE sn[12], sum;
+
+
+ if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
+ for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */
+
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
+ res = dir_alloc(dp, n_ent); /* Allocate directory entries */
+ if (res != FR_OK) return res;
+ dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 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);
+ st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);
+ fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */
+ 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 */
+ memcpy(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. */
+ n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */
+ res = dir_alloc(dp, n_ent); /* Allocate entries */
+ if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */
+ res = dir_sdi(dp, dp->dptr - n_ent * 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)n_ent, sum);
+ fs->wflag = 1;
+ res = dir_next(dp, 0); /* Next entry */
+ } while (res == FR_OK && --n_ent);
+ }
+ }
+
+#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) {
+ memset(dp->dir, 0, SZDIRE); /* Clean the entry */
+ memcpy(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;
+ UINT nw;
+#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) { /* exFAT volume */
+ UINT nc = 0;
+
+ si = SZDIRE * 2; di = 0; /* 1st C1 entry in the entry block */
+ hs = 0;
+ while (nc < fs->dirbuf[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(fs->dirbuf + si); si += 2; nc++; /* Get a character */
+ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
+ hs = wc; continue; /* Get low surrogate */
+ }
+ nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
+ if (nw == 0) { di = 0; break; } /* Buffer overflow or wrong char? */
+ di += nw;
+ 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 = fs->dirbuf[XDIR_Attr] & AM_MASKX; /* Attribute */
+ fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */
+ fno->ftime = ld_word(fs->dirbuf + XDIR_ModTime + 0); /* Time */
+ fno->fdate = ld_word(fs->dirbuf + XDIR_ModTime + 2); /* Date */
+ return;
+ } else
+#endif
+ { /* FAT/FAT32 volume */
+ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */
+ si = di = 0;
+ 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 */
+ }
+ nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
+ if (nw == 0) { di = 0; break; } /* Buffer overflow or wrong char? */
+ di += nw;
+ 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? */
+ nw = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in API encoding */
+ if (nw == 0) { di = 0; break; } /* Buffer overflow? */
+ di += nw;
+#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; /* Terminate the SFN */
+#endif
+
+ fno->fattrib = dp->dir[DIR_Attr] & AM_MASK; /* 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 */
+/*-----------------------------------------------------------------------*/
+
+#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */
+
+
+static DWORD get_achar ( /* Get a character and advance 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_match ( /* 0:mismatched, 1:matched */
+ const TCHAR* pat, /* Matching pattern */
+ const TCHAR* nam, /* String to be tested */
+ UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
+ UINT recur /* Recursion count */
+)
+{
+ const TCHAR *pptr, *nptr;
+ DWORD pchr, nchr;
+ UINT sk;
+
+
+ while ((skip & 0xFF) != 0) { /* Pre-skip name chars */
+ if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
+ skip--;
+ }
+ if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */
+
+ do {
+ pptr = pat; nptr = nam; /* Top of pattern and name to match */
+ for (;;) {
+ if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */
+ if (recur == 0) return 0; /* Too many wildcard terms? */
+ sk = 0;
+ do { /* Analyze the wildcard term */
+ if (*pptr++ == '?') sk++; else sk |= 0x100;
+ } while (*pptr == '?' || *pptr == '*');
+ if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */
+ nchr = *nptr; break; /* Branch mismatched */
+ }
+ pchr = get_achar(&pptr); /* Get a pattern char */
+ nchr = get_achar(&nptr); /* Get a name char */
+ if (pchr != nchr) break; /* Branch mismatched? */
+ if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */
+ }
+ get_achar(&nam); /* nam++ */
+ } while (skip && nchr); /* 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 < ' ' || IsSeparator(wc)) break; /* Break if end of the path or a separator is found */
+ if (wc < 0x80 && strchr("*:<>|\"\?\x7F", (int)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 */
+ }
+ if (wc < ' ') { /* Stopped at end of the path? */
+ cf = NS_LAST; /* Last segment */
+ } else { /* Stopped at a separator */
+ while (IsSeparator(*p)) p++; /* Skip duplicated separators if exist */
+ cf = 0; /* Next segment may follow */
+ if (IsTerminator(*p)) cf = NS_LAST; /* Ignore terminating separator */
+ }
+ *path = p; /* Return pointer to the next segment */
+
+#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) */
+
+ memset(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 an extended character? */
+ cf |= NS_LFN; /* LFN entry needs to be created */
+#if FF_CODE_PAGE == 0
+ if (ExCvt) { /* In 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 { /* In DBCS cfg */
+ wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */
+ }
+#elif FF_CODE_PAGE < 900 /* In 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 /* In DBCS cfg */
+ wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-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 || strchr("+,;=[]", (int)wc)) { /* Replace illegal characters for SFN */
+ 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;
+ memset(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 (!IsSeparator(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 (IsSeparator(c)) { /* Break if a separator is found */
+ while (IsSeparator(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 (strchr("*+,:;<=>[]|\"\?\x7F", (int)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 <= ' ' || p[si] <= ' ') ? 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 (!IsSeparator(*path) && (FF_STR_VOLUME_ID != 2 || !IsTerminator(*path))) { /* Without heading separator */
+ dp->obj.sclust = fs->cdir; /* Start at the current directory */
+ } else
+#endif
+ { /* With heading separator */
+ while (IsSeparator(*path)) path++; /* Strip separators */
+ dp->obj.sclust = 0; /* Start from the 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;
+ int 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 (!IsTerminator(tc) && 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 == '/') { /* Is there a volume ID? */
+ while (*(tp + 1) == '/') tp++; /* Skip duplicated separator */
+ i = 0;
+ do {
+ tt = tp; sp = VolumeStr[i]; /* Path name and this string volume ID */
+ do { /* Compare the volume ID with path name */
+ c = *sp++; tc = *(++tt);
+ if (IsLower(c)) c -= 0x20;
+ if (IsLower(tc)) tc -= 0x20;
+ } while (c && (TCHAR)c == tc);
+ } while ((c || (tc != '/' && !IsTerminator(tc))) && ++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 = tt; /* 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 */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* GPT support functions */
+/*-----------------------------------------------------------------------*/
+
+#if FF_LBA64
+
+/* Calculate CRC32 in byte-by-byte */
+
+static DWORD crc32 ( /* Returns next CRC value */
+ DWORD crc, /* Current CRC value */
+ BYTE d /* A byte to be processed */
+)
+{
+ BYTE b;
+
+
+ for (b = 1; b; b <<= 1) {
+ crc ^= (d & b) ? 1 : 0;
+ crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1;
+ }
+ return crc;
+}
+
+
+/* Check validity of GPT header */
+
+static int test_gpt_header ( /* 0:Invalid, 1:Valid */
+ const BYTE* gpth /* Pointer to the GPT header */
+)
+{
+ UINT i;
+ DWORD bcc;
+
+
+ if (memcmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0; /* Check sign, version (1.0) and length (92) */
+ for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check header BCC */
+ bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]);
+ }
+ if (~bcc != ld_dword(gpth + GPTH_Bcc)) return 0;
+ if (ld_dword(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* Table entry size (must be SZ_GPTE bytes) */
+ if (ld_dword(gpth + GPTH_PtNum) > 128) return 0; /* Table size (must be 128 entries or less) */
+
+ return 1;
+}
+
+#if !FF_FS_READONLY && FF_USE_MKFS
+
+/* Generate random value */
+static DWORD make_rand (
+ DWORD seed, /* Seed value */
+ BYTE* buff, /* Output buffer */
+ UINT n /* Data length */
+)
+{
+ UINT r;
+
+
+ if (seed == 0) seed = 1;
+ do {
+ for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ 0xA3000000 : seed >> 1; /* Shift 8 bits the 32-bit LFSR */
+ *buff++ = (BYTE)seed;
+ } while (--n);
+ return seed;
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT VBR */
+/*-----------------------------------------------------------------------*/
+
+/* Check what the sector is */
+
+static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
+ FATFS* fs, /* Filesystem object */
+ LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */
+)
+{
+ WORD w, sign;
+ BYTE b;
+
+
+ fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */
+ if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */
+ sign = ld_word(fs->win + BS_55AA);
+#if FF_FS_EXFAT
+ if (sign == 0xAA55 && !memcmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */
+#endif
+ b = fs->win[BS_JmpBoot];
+ if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */
+ if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) {
+ return 0; /* It is an FAT32 VBR */
+ }
+ /* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */
+ w = ld_word(fs->win + BPB_BytsPerSec);
+ b = fs->win[BPB_SecPerClus];
+ if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */
+ && b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size (2^n) */
+ && ld_word(fs->win + BPB_RsvdSecCnt) != 0 /* Properness of reserved sectors (MNBZ) */
+ && (UINT)fs->win[BPB_NumFATs] - 1 <= 1 /* Properness of FATs (1 or 2) */
+ && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root dir entries (MNBZ) */
+ && (ld_word(fs->win + BPB_TotSec16) >= 128 || ld_dword(fs->win + BPB_TotSec32) >= 0x10000) /* Properness of volume sectors (>=128) */
+ && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */
+ return 0; /* It can be presumed an FAT VBR */
+ }
+ }
+ return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */
+}
+
+
+/* Find an FAT volume */
+/* (It supports only generic partitioning rules, MBR, GPT and SFD) */
+
+static UINT find_volume ( /* Returns BS status found in the hosting drive */
+ FATFS* fs, /* Filesystem object */
+ UINT part /* Partition to fined = 0:auto, 1..:forced */
+)
+{
+ UINT fmt, i;
+ DWORD mbr_pt[4];
+
+
+ fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD format */
+ if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */
+
+ /* Sector 0 is not an FAT VBR or forced partition number wants a partition */
+
+#if FF_LBA64
+ if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? */
+ DWORD n_ent, v_ent, ofs;
+ QWORD pt_lba;
+
+ if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT header sector (next to MBR) */
+ if (!test_gpt_header(fs->win)) return 3; /* Check if GPT header is valid */
+ n_ent = ld_dword(fs->win + GPTH_PtNum); /* Number of entries */
+ pt_lba = ld_qword(fs->win + GPTH_PtOfs); /* Table location */
+ for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT partition */
+ if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4; /* PT sector */
+ ofs = i * SZ_GPTE % SS(fs); /* Offset in the sector */
+ if (!memcmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */
+ v_ent++;
+ fmt = check_fs(fs, ld_qword(fs->win + ofs + GPTE_FstLba)); /* Load VBR and check status */
+ if (part == 0 && fmt <= 1) return fmt; /* Auto search (valid FAT volume found first) */
+ if (part != 0 && v_ent == part) return fmt; /* Forced partition order (regardless of it is valid or not) */
+ }
+ }
+ return 3; /* Not found */
+ }
+#endif
+ if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has 4 partitions max */
+ for (i = 0; i < 4; i++) { /* Load partition offset in the MBR */
+ mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba);
+ }
+ i = part ? part - 1 : 0; /* Table index to find first */
+ do { /* Find an FAT volume */
+ fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check if the partition is FAT */
+ } while (part == 0 && fmt >= 2 && ++i < 4);
+ return fmt;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Determine logical drive number and mount the volume if needed */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT mount_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 */
+)
+{
+ int vol;
+ DSTATUS stat;
+ LBA_t bsect;
+ DWORD tsect, sysect, fasize, nclst, szbfat;
+ WORD nrsv;
+ FATFS *fs;
+ UINT fmt;
+
+
+ /* 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 already valid */
+ }
+ }
+
+ /* The filesystem object is not valid. */
+ /* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */
+
+ fs->fs_type = 0; /* Clear the filesystem object */
+ fs->pdrv = LD2PD(vol); /* Volume hosting 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 volume on the drive */
+ fmt = find_volume(fs, LD2PT(vol));
+ if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */
+ if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */
+ bsect = fs->winsect; /* Volume offset */
+
+ /* 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, i;
+
+ 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) */
+ return FR_NO_FILESYSTEM;
+ }
+
+ maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */
+ if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be accessed 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) 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 sectors) */
+
+ 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, (DWORD)fs->dirbase) + so) != FR_OK) 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) return FR_NO_FILESYSTEM; /* (Wrong cluster#) */
+ 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) 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)) 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 = (BYTE)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 buuffer */
+#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 to be registered (NULL:unmount)*/
+ const TCHAR* path, /* Logical drive number to be mounted/unmounted */
+ BYTE opt /* Mount 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) 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 = mount_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 open mode flags */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+#if !FF_FS_READONLY
+ DWORD cl, bcs, clst, tm;
+ LBA_t 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 = mount_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 */
+ memset(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */
+ memset(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 */
+ tm = GET_FATTIME(); /* Set created time */
+ st_dword(dj.dir + DIR_CrtTime, tm);
+ st_dword(dj.dir + DIR_ModTime, tm);
+ cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
+ 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 */
+ sc = fs->winsect;
+ res = remove_chain(&dj.obj, cl, 0);
+ if (res == FR_OK) {
+ res = move_window(fs, sc);
+ 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
+ memset(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 */
+ sc = clst2sect(fs, clst);
+ if (sc == 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
+ }
+ }
+#if FF_FS_LOCK != 0
+ if (res != FR_OK) dec_lock(fp->obj.lockid); /* Decrement file open counter if seek failed */
+#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, /* Open file to be read */
+ void* buff, /* Data buffer to store the read data */
+ UINT btr, /* Number of bytes to read */
+ UINT* br /* Number of bytes read */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD clst;
+ LBA_t sect;
+ FSIZE_t remain;
+ UINT rcnt, cc, csect;
+ BYTE *rbuff = (BYTE*)buff;
+
+
+ *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) 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 > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { /* Repeat until btr bytes read */
+ 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) 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 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) 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) {
+ memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
+ }
+#else
+ if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
+ memcpy(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) 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); /* Fill sector cache */
+ }
+#endif
+ fp->sect = sect;
+ }
+ rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains 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 */
+ memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
+#else
+ memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
+#endif
+ }
+
+ LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+ FIL* fp, /* Open file to be written */
+ const void* buff, /* Data to be written */
+ UINT btw, /* Number of bytes to write */
+ UINT* bw /* Number of bytes written */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ DWORD clst;
+ LBA_t sect;
+ UINT wcnt, cc, csect;
+ const BYTE *wbuff = (const BYTE*)buff;
+
+
+ *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) 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 > 0; btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { /* Repeat until all data written */
+ 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) break; /* Could not allocate a new cluster (disk full) */
+ if (clst == 1) ABORT(fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) 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) 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 */
+ memcpy(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 */
+ memcpy(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 remains 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 */
+ memcpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
+ fs->wflag = 1;
+#else
+ memcpy(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);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+ FIL* fp /* Open file to be synced */
+)
+{
+ 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); /* Update start cluster */
+ st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); /* Update file size */
+ st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); /* (FatFs does not support Valid File Size feature) */
+ 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 /* Open file 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 = mount_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 if in 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 = mount_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;
+ LBA_t nsect;
+ FSIZE_t ifptr;
+#if FF_USE_FASTSEEK
+ DWORD cl, pcl, ncl, tcl, tlen, ulen;
+ DWORD *tbl;
+ LBA_t dsc;
+#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_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 = mount_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_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */
+#if FF_USE_LFN && FF_USE_FIND == 2
+ if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) 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 = mount_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, stat;
+ LBA_t sect;
+ UINT i;
+ FFOBJID obj;
+
+
+ /* Get logical drive */
+ res = mount_volume(&path, &fs, 0);
+ if (res == FR_OK) {
+ *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);
+ }
+ }
+ if (res == FR_OK) { /* Update parameters if succeeded */
+ *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 = mount_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 = mount_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) */
+ memset(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);
+ memcpy(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)); /* Directory 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;
+ LBA_t sect;
+ DEF_NAMBUF
+
+
+ get_ldnumber(&path_new); /* Snip the drive number of new name off */
+ res = mount_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;
+
+ memcpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */
+ memcpy(&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);
+ memcpy(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 */
+ memcpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */
+ memcpy(&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 */
+ memcpy(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 */
+ sect = clst2sect(fs, ld_clust(fs, dir));
+ if (sect == 0) {
+ res = FR_INT_ERR;
+ } else {
+/* Start of critical section where an interruption can cause a cross-link */
+ res = move_window(fs, sect);
+ 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 = mount_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 = mount_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 = mount_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;
+ UINT nw;
+
+ 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;
+ }
+ nw = put_utf((DWORD)hs << 16 | wc, &label[di], 4); /* Store it in API encoding */
+ if (nw == 0) { di = 0; break; } /* Encode error? */
+ di += nw;
+ 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) { di = 0; break; } /* Invalid char in current code page? */
+ di += put_utf(wc, &label[di], 4); /* Store it in Unicode */
+#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[18] = "+.,;=[]" "/*:<>|\\\"\?\x7F"; /* [0..16] for FAT, [7..16] for exFAT */
+#if FF_USE_LFN
+ DWORD dc;
+#endif
+
+ /* Get logical drive */
+ res = mount_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 */
+ memset(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 || strchr(&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 */
+ memset(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 || strchr(&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 */
+ memcpy(dj.dir + XDIR_Label, dirvn, 22);
+ } else {
+ if (di != 0) {
+ memcpy(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) {
+ memset(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;
+ memcpy(dj.dir + XDIR_Label, dirvn, 22);
+ } else {
+ dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */
+ memcpy(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;
+ LBA_t 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 > 0 && (*func)(0, 0); fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { /* Repeat until all data transferred or stream goes busy */
+ 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 remains 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_FS_READONLY && FF_USE_MKFS
+/*-----------------------------------------------------------------------*/
+/* Create FAT/exFAT volume (with sub-functions) */
+/*-----------------------------------------------------------------------*/
+
+#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
+#define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */
+#define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
+
+
+/* Create partitions on the physical drive in format of MBR or GPT */
+
+static FRESULT create_partition (
+ BYTE drv, /* Physical drive number */
+ const LBA_t plst[], /* Partition list */
+ BYTE sys, /* System ID (for only MBR, temp setting) */
+ BYTE* buf /* Working buffer for a sector */
+)
+{
+ UINT i, cy;
+ LBA_t sz_drv;
+ DWORD sz_drv32, nxt_alloc32, sz_part32;
+ BYTE *pte;
+ BYTE hd, n_hd, sc, n_sc;
+
+ /* Get physical drive size */
+ if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR;
+
+#if FF_LBA64
+ if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT format */
+ WORD ss;
+ UINT sz_ptbl, pi, si, ofs;
+ DWORD bcc, rnd, align;
+ QWORD nxt_alloc, sz_part, sz_pool, top_bpt;
+ static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
+
+#if FF_MAX_SS != FF_MIN_SS
+ if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; /* Get sector size */
+ if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
+#else
+ ss = FF_MAX_SS;
+#endif
+ rnd = (DWORD)sz_drv + GET_FATTIME(); /* Random seed */
+ align = GPT_ALIGN / ss; /* Partition alignment for GPT [sector] */
+ sz_ptbl = GPT_ITEMS * SZ_GPTE / ss; /* Size of partition table [sector] */
+ top_bpt = sz_drv - sz_ptbl - 1; /* Backup partiiton table start sector */
+ nxt_alloc = 2 + sz_ptbl; /* First allocatable sector */
+ sz_pool = top_bpt - nxt_alloc; /* Size of allocatable area */
+ bcc = 0xFFFFFFFF; sz_part = 1;
+ pi = si = 0; /* partition table index, size table index */
+ do {
+ if (pi * SZ_GPTE % ss == 0) memset(buf, 0, ss); /* Clean the buffer if needed */
+ if (sz_part != 0) { /* Is the size table not termintated? */
+ nxt_alloc = (nxt_alloc + align - 1) & ((QWORD)0 - align); /* Align partition start */
+ sz_part = plst[si++]; /* Get a partition size */
+ if (sz_part <= 100) { /* Is the size in percentage? */
+ sz_part = sz_pool * sz_part / 100;
+ sz_part = (sz_part + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */
+ }
+ if (nxt_alloc + sz_part > top_bpt) { /* Clip the size at end of the pool */
+ sz_part = (nxt_alloc < top_bpt) ? top_bpt - nxt_alloc : 0;
+ }
+ }
+ if (sz_part != 0) { /* Add a partition? */
+ ofs = pi * SZ_GPTE % ss;
+ memcpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Set partition GUID (Microsoft Basic Data) */
+ rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Set unique partition GUID */
+ st_qword(buf + ofs + GPTE_FstLba, nxt_alloc); /* Set partition start sector */
+ st_qword(buf + ofs + GPTE_LstLba, nxt_alloc + sz_part - 1); /* Set partition end sector */
+ nxt_alloc += sz_part; /* Next allocatable sector */
+ }
+ if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the buffer if it is filled up */
+ for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* Calculate table check sum */
+ if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to primary table */
+ if (disk_write(drv, buf, top_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to secondary table */
+ }
+ } while (++pi < GPT_ITEMS);
+
+ /* Create primary GPT header */
+ memset(buf, 0, ss);
+ memcpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */
+ st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */
+ st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */
+ st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of secondary header */
+ st_qword(buf + GPTH_FstLba, 2 + sz_ptbl); /* LBA of first allocatable sector */
+ st_qword(buf + GPTH_LstLba, top_bpt - 1); /* LBA of last allocatable sector */
+ st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */
+ st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */
+ st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */
+ rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */
+ for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */
+ st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */
+ if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR;
+
+ /* Create secondary GPT header */
+ st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */
+ st_qword(buf + GPTH_BakLba, 1); /* LBA of primary header */
+ st_qword(buf + GPTH_PtOfs, top_bpt); /* LBA of this table */
+ st_dword(buf + GPTH_Bcc, 0);
+ for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */
+ st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */
+ if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR;
+
+ /* Create protective MBR */
+ memset(buf, 0, ss);
+ memcpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */
+ st_word(buf + BS_55AA, 0xAA55);
+ if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;
+
+ } else
+#endif
+ { /* Create partitions in MBR format */
+ sz_drv32 = (DWORD)sz_drv;
+ n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */
+ for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ;
+ if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */
+
+ memset(buf, 0, FF_MAX_SS); /* Clear MBR */
+ pte = buf + MBR_Table; /* Partition table in the MBR */
+ for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) {
+ sz_part32 = (DWORD)plst[i]; /* Get partition size */
+ if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32; /* Size in percentage? */
+ if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32; /* Clip at drive size */
+ if (sz_part32 == 0) break; /* End of table or no sector to allocate? */
+
+ st_dword(pte + PTE_StLba, nxt_alloc32); /* Start LBA */
+ st_dword(pte + PTE_SizLba, sz_part32); /* Number of sectors */
+ pte[PTE_System] = sys; /* System type */
+
+ cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* Start cylinder */
+ hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* Start head */
+ sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* Start sector */
+ pte[PTE_StHead] = hd;
+ pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
+ pte[PTE_StCyl] = (BYTE)cy;
+
+ cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* End cylinder */
+ hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* End head */
+ sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* End sector */
+ pte[PTE_EdHead] = hd;
+ pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
+ pte[PTE_EdCyl] = (BYTE)cy;
+
+ pte += SZ_PTE; /* Next entry */
+ }
+
+ st_word(buf + BS_55AA, 0xAA55); /* MBR signature */
+ if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */
+ }
+
+ return FR_OK;
+}
+
+
+
+FRESULT f_mkfs (
+ const TCHAR* path, /* Logical drive number */
+ const MKFS_PARM* opt, /* Format options */
+ void* work, /* Pointer to working buffer (null: use heap memory) */
+ UINT len /* Size of working buffer [byte] */
+)
+{
+ 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) */
+ static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
+ BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart;
+ WORD ss; /* Sector size */
+ DWORD sz_buf, sz_blk, n_clst, pau, nsect, n, vsn;
+ LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, Base LBA of volume, fat, data */
+ LBA_t sect, lba[2];
+ DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */
+ UINT n_fat, n_root, i; /* Index, Number of FATs and Number of roor dir entries */
+ int vol;
+ DSTATUS ds;
+ FRESULT fr;
+
+
+ /* 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 fs object if mounted */
+ pdrv = LD2PD(vol); /* Physical drive */
+ ipart = LD2PT(vol); /* Partition (0:create as new, 1..:get from partition table) */
+ if (!opt) opt = &defopt; /* Use default parameter if it is not given */
+
+ /* Get physical drive status (sz_drv, sz_blk, ss) */
+ ds = disk_initialize(pdrv);
+ if (ds & STA_NOINIT) return FR_NOT_READY;
+ if (ds & STA_PROTECT) return FR_WRITE_PROTECTED;
+ sz_blk = opt->align;
+ if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK) sz_blk = 1;
+ if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1;
+#if FF_MAX_SS != FF_MIN_SS
+ 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
+ /* Options for FAT sub-type and FAT parameters */
+ fsopt = opt->fmt & (FM_ANY | FM_SFD);
+ n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1;
+ n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512;
+ sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & (opt->au_size - 1)) == 0) ? opt->au_size : 0;
+ sz_au /= ss; /* Byte --> Sector */
+
+ /* Get working buffer */
+ sz_buf = len / ss; /* Size of working buffer [sector] */
+ if (sz_buf == 0) return FR_NOT_ENOUGH_CORE;
+ buf = (BYTE*)work; /* Working buffer */
+#if FF_USE_LFN == 3
+ if (!buf) buf = ff_memalloc(sz_buf * ss); /* Use heap memory for working buffer */
+#endif
+ if (!buf) return FR_NOT_ENOUGH_CORE;
+
+ /* Determine where the volume to be located (b_vol, sz_vol) */
+ b_vol = sz_vol = 0;
+ if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume associated with any specific partition? */
+ /* Get partition location from the existing partition table */
+ 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 */
+#if FF_LBA64
+ if (buf[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? */
+ DWORD n_ent, ofs;
+ QWORD pt_lba;
+
+ /* Get the partition location from GPT */
+ if (disk_read(pdrv, buf, 1, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load GPT header sector (next to MBR) */
+ if (!test_gpt_header(buf)) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */
+ n_ent = ld_dword(buf + GPTH_PtNum); /* Number of entries */
+ pt_lba = ld_qword(buf + GPTH_PtOfs); /* Table start sector */
+ ofs = i = 0;
+ while (n_ent) { /* Find MS Basic partition with order of ipart */
+ if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */
+ if (!memcmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */
+ b_vol = ld_qword(buf + ofs + GPTE_FstLba);
+ sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - b_vol + 1;
+ break;
+ }
+ n_ent--; ofs = (ofs + SZ_GPTE) % ss; /* Next entry */
+ }
+ if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* Partition not found */
+ fsopt |= 0x80; /* Partitioning is in GPT */
+ } else
+#endif
+ { /* Get the partition location from MBR partition table */
+ pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE);
+ if (ipart > 4 || 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 { /* The volume is associated with a physical drive */
+ if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+ if (!(fsopt & FM_SFD)) { /* To be partitioned? */
+ /* Create a single-partition on the drive in this function */
+#if FF_LBA64
+ if (sz_vol >= FF_MIN_GPT) { /* Which partition type to create, MBR or GPT? */
+ fsopt |= 0x80; /* Partitioning is in GPT */
+ b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS * SZ_GPTE / ss + 1; /* Estimated partition offset and size */
+ } else
+#endif
+ { /* Partitioning is in MBR */
+ if (sz_vol > N_SEC_TRACK) {
+ b_vol = N_SEC_TRACK; sz_vol -= b_vol; /* Estimated partition offset and size */
+ }
+ }
+ }
+ }
+ if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */
+
+ /* Now start to create an FAT volume at b_vol and sz_vol */
+
+ do { /* Pre-determine the FAT type */
+ if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT possible? */
+ if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || sz_au > 128) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */
+ fsty = FS_EXFAT; break;
+ }
+ }
+#if FF_LBA64
+ if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too large volume for FAT/FAT32 */
+#endif
+ if (sz_au > 128) sz_au = 128; /* Invalid AU for FAT/FAT32? */
+ if (fsopt & FM_FAT32) { /* FAT32 possible? */
+ if (!(fsopt & FM_FAT)) { /* no-FAT? */
+ fsty = FS_FAT32; break;
+ }
+ }
+ if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */
+ fsty = FS_FAT16;
+ } while (0);
+
+ vsn = (DWORD)sz_vol + GET_FATTIME(); /* VSN generated from current time and partitiion size */
+
+#if FF_FS_EXFAT
+ if (fsty == FS_EXFAT) { /* Create an exFAT volume */
+ DWORD szb_bit, szb_case, sum, nbit, clu, clen[3];
+ WCHAR ch, si;
+ UINT j, st;
+
+ if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */
+#if FF_USE_TRIM
+ lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
+ disk_ioctl(pdrv, CTRL_TRIM, lba);
+#endif
+ /* Determine FAT location, data location and number of clusters */
+ if (sz_au == 0) { /* AU auto-selection */
+ sz_au = 8;
+ if (sz_vol >= 0x80000) sz_au = 64; /* >= 512Ks */
+ if (sz_vol >= 0x4000000) sz_au = 256; /* >= 64Ms */
+ }
+ b_fat = b_vol + 32; /* FAT start at offset 32 */
+ sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */
+ b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)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 = (DWORD)(sz_vol - (b_data - b_vol)) / sz_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 */
+ clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
+
+ /* Create a compressed up-case table */
+ sect = b_data + sz_au * clen[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 chars */
+ }
+ st = 1; /* Do not compress short run */
+ /* FALLTHROUGH */
+ 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 == sz_buf * ss) { /* 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);
+ clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
+ clen[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 bitmap sectors */
+ nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */
+ do {
+ memset(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */
+ for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */
+ 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 = nbit = clu = 0;
+ do {
+ memset(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */
+ if (clu == 0) { /* Initialize FAT [0] and FAT[1] */
+ st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++;
+ st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++;
+ }
+ do { /* Create chains of bitmap, up-case and root dir */
+ while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */
+ st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF);
+ i += 4; clu++; nbit--;
+ }
+ if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */
+ } while (nbit != 0 && i < sz_buf * ss);
+ 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 */
+ memset(buf, 0, sz_buf * ss);
+ buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */
+ 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 + clen[0]); /* cluster */
+ st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
+ sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_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);
+ memset(buf, 0, ss); /* Rest of entries are filled with zero */
+ 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) */
+ memset(buf, 0, ss);
+ memcpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */
+ st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */
+ st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */
+ st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */
+ st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
+ st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
+ st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
+ st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */
+ st_dword(buf + BPB_VolIDEx, vsn); /* 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 = sz_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) */
+ memset(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) */
+ memset(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 = sz_au;
+ /* Pre-determine number of clusters and FAT sub-type */
+ if (fsty == FS_FAT32) { /* FAT32 volume */
+ if (pau == 0) { /* AU auto-selection */
+ n = (DWORD)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 = (DWORD)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 = (DWORD)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 = (DWORD)sz_vol / pau;
+ if (n_clst > MAX_FAT12) {
+ n = n_clst * 2 + 4; /* FAT size [byte] */
+ } else {
+ fsty = 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_root * SZDIRE / ss; /* Root dir size [sector] */
+ }
+ b_fat = b_vol + sz_rsv; /* FAT base */
+ b_data = b_fat + sz_fat * n_fat + sz_dir; /* Data base */
+
+ /* Align data area to erase block boundary (for flash memory media) */
+ n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data); /* Sectors to next nearest from current data base */
+ if (fsty == FS_FAT32) { /* FAT32: Move FAT */
+ sz_rsv += n; b_fat += n;
+ } else { /* FAT: Expand FAT */
+ if (n % n_fat) { /* Adjust fractional error if needed */
+ n--; sz_rsv++; b_fat++;
+ }
+ sz_fat += n / n_fat;
+ }
+
+ /* 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 = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - sz_dir) / pau;
+ if (fsty == FS_FAT32) {
+ if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32? */
+ if (sz_au == 0 && (sz_au = pau / 2) != 0) continue; /* Adjust cluster size and retry */
+ LEAVE_MKFS(FR_MKFS_ABORTED);
+ }
+ }
+ if (fsty == FS_FAT16) {
+ if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */
+ if (sz_au == 0 && (pau * 2) <= 64) {
+ sz_au = pau * 2; continue; /* Adjust cluster size and retry */
+ }
+ if ((fsopt & FM_FAT32)) {
+ fsty = FS_FAT32; continue; /* Switch type to FAT32 and retry */
+ }
+ if (sz_au == 0 && (sz_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 (sz_au == 0 && (sz_au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
+ LEAVE_MKFS(FR_MKFS_ABORTED);
+ }
+ }
+ if (fsty == 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
+ lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
+ disk_ioctl(pdrv, CTRL_TRIM, lba);
+#endif
+ /* Create FAT VBR */
+ memset(buf, 0, ss);
+ memcpy(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_fat; /* Number of FATs */
+ st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root)); /* 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, (DWORD)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, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */
+ if (fsty == FS_FAT32) {
+ st_dword(buf + BS_VolID32, vsn); /* 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 */
+ memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */
+ } else {
+ st_dword(buf + BS_VolID, vsn); /* 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 */
+ memcpy(buf + BS_VolLab, "NO NAME " "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 (fsty == FS_FAT32) {
+ disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */
+ memset(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 */
+ memset(buf, 0, sz_buf * ss);
+ sect = b_fat; /* FAT start sector */
+ for (i = 0; i < n_fat; i++) { /* Initialize FATs each */
+ if (fsty == FS_FAT32) {
+ st_dword(buf + 0, 0xFFFFFFF8); /* FAT[0] */
+ st_dword(buf + 4, 0xFFFFFFFF); /* FAT[1] */
+ st_dword(buf + 8, 0x0FFFFFFF); /* FAT[2] (root directory) */
+ } else {
+ st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* FAT[0] and FAT[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);
+ memset(buf, 0, ss); /* Rest of FAT all are cleared */
+ sect += n; nsect -= n;
+ } while (nsect);
+ }
+
+ /* Initialize root directory (fill with zero) */
+ nsect = (fsty == 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);
+ }
+
+ /* A FAT volume has been created here */
+
+ /* Determine system ID in the MBR partition table */
+ if (FF_FS_EXFAT && fsty == FS_EXFAT) {
+ sys = 0x07; /* exFAT */
+ } else {
+ if (fsty == FS_FAT32) {
+ sys = 0x0C; /* FAT32X */
+ } else {
+ if (sz_vol >= 0x10000) {
+ sys = 0x06; /* FAT12/16 (large) */
+ } else {
+ sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */
+ }
+ }
+ }
+
+ /* Update partition information */
+ if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the existing partition */
+ if (!FF_LBA64 || !(fsopt & 0x80)) {
+ /* 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 + (ipart - 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 { /* Volume as a new single partition */
+ if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */
+ lba[0] = sz_vol; lba[1] = 0;
+ fr = create_partition(pdrv, lba, sys, buf);
+ if (fr != FR_OK) LEAVE_MKFS(fr);
+ }
+ }
+
+ 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 LBA_t ptbl[], /* Pointer to the size table for each partitions */
+ void* work /* Pointer to the working buffer (null: use heap memory) */
+)
+{
+ BYTE *buf = (BYTE*)work;
+ DSTATUS stat;
+
+
+ stat = disk_initialize(pdrv);
+ if (stat & STA_NOINIT) return FR_NOT_READY;
+ if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#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;
+
+ LEAVE_MKFS(create_partition(pdrv, ptbl, 0x07, buf));
+}
+
+#endif /* FF_MULTI_PARTITION */
+#endif /* !FF_FS_READONLY && FF_USE_MKFS */
+
+
+
+
+#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 buffer to store read string */
+ 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); /* Get a code unit */
+ if (rc != 1) break; /* EOF? */
+ wc = s[0];
+ if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */
+ f_read(fp, s, 1, &rc); /* Get 2nd byte */
+ if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong code? */
+ wc = wc << 8 | s[0];
+ }
+ dc = ff_oem2uni(wc, CODEPAGE); /* Convert ANSI/OEM into Unicode */
+ if (dc == 0) continue; /* Conversion error? */
+#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
+ f_read(fp, s, 2, &rc); /* Get a code unit */
+ if (rc != 2) break; /* EOF? */
+ dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+ if (IsSurrogateL(dc)) continue; /* Broken surrogate pair? */
+ if (IsSurrogateH(dc)) { /* High surrogate? */
+ f_read(fp, s, 2, &rc); /* Get low surrogate */
+ if (rc != 2) break; /* EOF? */
+ wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+ if (!IsSurrogateL(wc)) continue; /* Broken surrogate pair? */
+ dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); /* Merge surrogate pair */
+ }
+#else /* Read a character in UTF-8 */
+ f_read(fp, s, 1, &rc); /* Get a code unit */
+ if (rc != 1) break; /* EOF? */
+ dc = s[0];
+ if (dc >= 0x80) { /* Multi-byte sequence? */
+ ct = 0;
+ if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* 2-byte sequence? */
+ if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte sequence? */
+ if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte sequence? */
+ if (ct == 0) continue;
+ f_read(fp, s, ct, &rc); /* Get trailing bytes */
+ if (rc != ct) break;
+ rc = 0;
+ do { /* Merge the byte sequence */
+ 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
+ /* A code point is avaialble in dc to be output */
+
+ 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) { /* Single byte? */
+ *p++ = (TCHAR)dc;
+ nc++;
+ if (dc == '\n') break; /* End of line? */
+ } else {
+ if (dc < 0x800) { /* 2-byte sequence? */
+ *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 2;
+ } else {
+ if (dc < 0x10000) { /* 3-byte sequence? */
+ *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
+ *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 3;
+ } else { /* 4-byte sequence? */
+ *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 read without any conversion (ANSI/OEM API) */
+ len -= 1; /* Make a room for the terminator */
+ while (nc < len) {
+ f_read(fp, s, 1, &rc); /* Get a byte */
+ if (rc != 1) break; /* EOF? */
+ 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 <stdarg.h>
+#define SZ_PUTC_BUF 64
+#define SZ_NUM_BUF 32
+
+/*-----------------------------------------------------------------------*/
+/* Put a Character to the File (with sub-functions) */
+/*-----------------------------------------------------------------------*/
+
+/* Output buffer and work area */
+
+typedef struct {
+ 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[SZ_PUTC_BUF]; /* Write buffer */
+} putbuff;
+
+
+/* Buffered file write with code conversion */
+
+static void putc_bfd (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;
+ const 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; /* In write error? */
+ nc = pb->nchr; /* Write unit counter */
+
+#if FF_USE_LFN && FF_LFN_UNICODE
+#if FF_LFN_UNICODE == 1 /* UTF-16 input */
+ if (IsSurrogateH(c)) { /* High surrogate? */
+ pb->hs = c; return; /* Save it for next */
+ }
+ hs = pb->hs; pb->hs = 0;
+ if (hs != 0) { /* There is a leading high surrogate */
+ if (!IsSurrogateL(c)) hs = 0; /* Discard high surrogate if not a surrogate pair */
+ } else {
+ if (IsSurrogateL(c)) return; /* Discard stray low surrogate */
+ }
+ 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; /* Single byte? */
+ if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte sequence? */
+ if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte sequence? */
+ if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte sequence? */
+ 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 = (const TCHAR*)pb->bs;
+ dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
+ if (dc == 0xFFFFFFFF) return; /* Wrong code? */
+ wc = (WCHAR)dc;
+ hs = (WCHAR)(dc >> 16);
+#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
+ if (IsSurrogate(c) || c >= 0x110000) return; /* Discard invalid code */
+ if (c >= 0x10000) { /* Out of BMP? */
+ hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */
+ wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */
+ } else {
+ hs = 0;
+ wc = (WCHAR)c;
+ }
+#endif
+ /* A code point in UTF-16 is available in hs and wc */
+
+#if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */
+ if (hs != 0) { /* Surrogate pair? */
+ st_word(&pb->buf[i], hs);
+ i += 2;
+ nc++;
+ }
+ st_word(&pb->buf[i], wc);
+ i += 2;
+#elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */
+ if (hs != 0) { /* Surrogate pair? */
+ 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 a code point in UTF-8 */
+ if (hs != 0) { /* 4-byte sequence? */
+ 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) { /* Single byte? */
+ pb->buf[i++] = (BYTE)wc;
+ } else {
+ if (wc < 0x800) { /* 2-byte sequence? */
+ nc += 1;
+ pb->buf[i++] = (BYTE)(0xC0 | wc >> 6);
+ } else { /* 3-byte sequence */
+ 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 a code point 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-encoding) */
+ 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;
+}
+
+
+/* Flush remaining characters in the buffer */
+
+static int putc_flush (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 -1;
+}
+
+
+/* Initialize write buffer */
+
+static void putc_init (putbuff* pb, FIL* fp)
+{
+ memset(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 (with sub-functions) */
+/*-----------------------------------------------------------------------*/
+#if FF_PRINT_FLOAT && FF_INTDEF == 2
+#include <math.h>
+
+static int ilog10 (double n) /* Calculate log10(n) in integer output */
+{
+ int rv = 0;
+
+ while (n >= 10) { /* Decimate digit in right shift */
+ if (n >= 100000) {
+ n /= 100000; rv += 5;
+ } else {
+ n /= 10; rv++;
+ }
+ }
+ while (n < 1) { /* Decimate digit in left shift */
+ if (n < 0.00001) {
+ n *= 100000; rv -= 5;
+ } else {
+ n *= 10; rv--;
+ }
+ }
+ return rv;
+}
+
+
+static double i10x (int n) /* Calculate 10^n in integer input */
+{
+ double rv = 1;
+
+ while (n > 0) { /* Left shift */
+ if (n >= 5) {
+ rv *= 100000; n -= 5;
+ } else {
+ rv *= 10; n--;
+ }
+ }
+ while (n < 0) { /* Right shift */
+ if (n <= -5) {
+ rv /= 100000; n += 5;
+ } else {
+ rv /= 10; n++;
+ }
+ }
+ return rv;
+}
+
+
+static void ftoa (
+ char* buf, /* Buffer to output the floating point string */
+ double val, /* Value to output */
+ int prec, /* Number of fractional digits */
+ TCHAR fmt /* Notation */
+)
+{
+ int d;
+ int e = 0, m = 0;
+ char sign = 0;
+ double w;
+ const char *er = 0;
+ const char ds = FF_PRINT_FLOAT == 2 ? ',' : '.';
+
+
+ if (isnan(val)) { /* Not a number? */
+ er = "NaN";
+ } else {
+ if (prec < 0) prec = 6; /* Default precision? (6 fractional digits) */
+ if (val < 0) { /* Nagative? */
+ val = 0 - val; sign = '-';
+ } else {
+ sign = '+';
+ }
+ if (isinf(val)) { /* Infinite? */
+ er = "INF";
+ } else {
+ if (fmt == 'f') { /* Decimal notation? */
+ val += i10x(0 - prec) / 2; /* Round (nearest) */
+ m = ilog10(val);
+ if (m < 0) m = 0;
+ if (m + prec + 3 >= SZ_NUM_BUF) er = "OV"; /* Buffer overflow? */
+ } else { /* E notation */
+ if (val != 0) { /* Not a true zero? */
+ val += i10x(ilog10(val) - prec) / 2; /* Round (nearest) */
+ e = ilog10(val);
+ if (e > 99 || prec + 7 >= SZ_NUM_BUF) { /* Buffer overflow or E > +99? */
+ er = "OV";
+ } else {
+ if (e < -99) e = -99;
+ val /= i10x(e); /* Normalize */
+ }
+ }
+ }
+ }
+ if (!er) { /* Not error condition */
+ if (sign == '-') *buf++ = sign; /* Add a - if negative value */
+ do { /* Put decimal number */
+ if (m == -1) *buf++ = ds; /* Insert a decimal separator when get into fractional part */
+ w = i10x(m); /* Snip the highest digit d */
+ d = (int)(val / w); val -= d * w;
+ *buf++ = (char)('0' + d); /* Put the digit */
+ } while (--m >= -prec); /* Output all digits specified by prec */
+ if (fmt != 'f') { /* Put exponent if needed */
+ *buf++ = (char)fmt;
+ if (e < 0) {
+ e = 0 - e; *buf++ = '-';
+ } else {
+ *buf++ = '+';
+ }
+ *buf++ = (char)('0' + e / 10);
+ *buf++ = (char)('0' + e % 10);
+ }
+ }
+ }
+ if (er) { /* Error condition */
+ if (sign) *buf++ = sign; /* Add sign if needed */
+ do *buf++ = *er++; while (*er); /* Put error symbol */
+ }
+ *buf = 0; /* Term */
+}
+#endif /* FF_PRINT_FLOAT && FF_INTDEF == 2 */
+
+
+
+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;
+ UINT i, j, w, f, r;
+ int prec;
+#if FF_PRINT_LLI && FF_INTDEF == 2
+ QWORD v;
+#else
+ DWORD v;
+#endif
+ TCHAR tc, pad, *tp;
+ TCHAR nul = 0;
+ char d, str[SZ_NUM_BUF];
+
+
+ putc_init(&pb, fp);
+
+ va_start(arp, fmt);
+
+ for (;;) {
+ tc = *fmt++;
+ if (tc == 0) break; /* End of format string */
+ if (tc != '%') { /* Not an escape character (pass-through) */
+ putc_bfd(&pb, tc);
+ continue;
+ }
+ f = w = 0; pad = ' '; prec = -1; /* Initialize parms */
+ tc = *fmt++;
+ if (tc == '0') { /* Flag: '0' padded */
+ pad = '0'; tc = *fmt++;
+ } else if (tc == '-') { /* Flag: Left aligned */
+ f = 2; tc = *fmt++;
+ }
+ if (tc == '*') { /* Minimum width from an argument */
+ w = va_arg(arp, int);
+ tc = *fmt++;
+ } else {
+ while (IsDigit(tc)) { /* Minimum width */
+ w = w * 10 + tc - '0';
+ tc = *fmt++;
+ }
+ }
+ if (tc == '.') { /* Precision */
+ tc = *fmt++;
+ if (tc == '*') { /* Precision from an argument */
+ prec = va_arg(arp, int);
+ tc = *fmt++;
+ } else {
+ prec = 0;
+ while (IsDigit(tc)) { /* Precision */
+ prec = prec * 10 + tc - '0';
+ tc = *fmt++;
+ }
+ }
+ }
+ if (tc == 'l') { /* Size: long int */
+ f |= 4; tc = *fmt++;
+#if FF_PRINT_LLI && FF_INTDEF == 2
+ if (tc == 'l') { /* Size: long long int */
+ f |= 8; tc = *fmt++;
+ }
+#endif
+ }
+ if (tc == 0) break; /* End of format string */
+ switch (tc) { /* Atgument type is... */
+ 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 (lower case) */
+ case 'X': /* Unsigned hexdecimal (upper case) */
+ r = 16; break;
+ case 'c': /* Character */
+ putc_bfd(&pb, (TCHAR)va_arg(arp, int));
+ continue;
+ case 's': /* String */
+ tp = va_arg(arp, TCHAR*); /* Get a pointer argument */
+ if (!tp) tp = &nul; /* Null ptr generates a null string */
+ for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */
+ if (prec >= 0 && j > (UINT)prec) j = prec; /* Limited length of string body */
+ for ( ; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */
+ while (*tp && prec--) putc_bfd(&pb, *tp++); /* Body */
+ while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */
+ continue;
+#if FF_PRINT_FLOAT && FF_INTDEF == 2
+ case 'f': /* Floating point (decimal) */
+ case 'e': /* Floating point (e) */
+ case 'E': /* Floating point (E) */
+ ftoa(str, va_arg(arp, double), prec, tc); /* Make a flaoting point string */
+ for (j = strlen(str); !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */
+ for (i = 0; str[i]; putc_bfd(&pb, str[i++])) ; /* Body */
+ while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */
+ continue;
+#endif
+ default: /* Unknown type (pass-through) */
+ putc_bfd(&pb, tc); continue;
+ }
+
+ /* Get an integer argument and put it in numeral */
+#if FF_PRINT_LLI && FF_INTDEF == 2
+ if (f & 8) { /* long long argument? */
+ v = (QWORD)va_arg(arp, LONGLONG);
+ } else {
+ if (f & 4) { /* long argument? */
+ v = (tc == 'd') ? (QWORD)(LONGLONG)va_arg(arp, long) : (QWORD)va_arg(arp, unsigned long);
+ } else { /* int/short/char argument */
+ v = (tc == 'd') ? (QWORD)(LONGLONG)va_arg(arp, int) : (QWORD)va_arg(arp, unsigned int);
+ }
+ }
+ if (tc == 'd' && (v & 0x8000000000000000)) { /* Negative value? */
+ v = 0 - v; f |= 1;
+ }
+#else
+ if (f & 4) { /* long argument? */
+ v = (DWORD)va_arg(arp, long);
+ } else { /* int/short/char argument */
+ v = (tc == 'd') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int);
+ }
+ if (tc == 'd' && (v & 0x80000000)) { /* Negative value? */
+ v = 0 - v; f |= 1;
+ }
+#endif
+ i = 0;
+ do { /* Make an integer number string */
+ d = (char)(v % r); v /= r;
+ if (d > 9) d += (tc == 'x') ? 0x27 : 0x07;
+ str[i++] = d + '0';
+ } while (v && i < SZ_NUM_BUF);
+ if (f & 1) str[i++] = '-'; /* Sign */
+ /* Write it */
+ for (j = i; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */
+ do putc_bfd(&pb, (TCHAR)str[--i]); while (i); /* Body */
+ while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */
+ }
+
+ 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[22] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0};
+ static const BYTE* const tables[22] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct855, 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/fw/fe310/fsfat/ff.h b/fw/fe310/fsfat/ff.h
new file mode 100644
index 0000000..64bf79d
--- /dev/null
+++ b/fw/fe310/fsfat/ff.h
@@ -0,0 +1,422 @@
+/*----------------------------------------------------------------------------/
+/ FatFs - Generic FAT Filesystem module R0.14b /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2021, 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 86631 /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ffconf.h" /* FatFs configuration options */
+
+#if FF_DEFINED != FFCONF_DEF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+/* Integer types used for FatFs API */
+
+#if defined(_WIN32) /* Windows VC++ (for development only) */
+#define FF_INTDEF 2
+#include <windows.h>
+typedef unsigned __int64 QWORD;
+#include <float.h>
+#define isnan(v) _isnan(v)
+#define isinf(v) (!_finite(v))
+
+#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
+#define FF_INTDEF 2
+#include <stdint.h>
+typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
+typedef unsigned char BYTE; /* char must be 8-bit */
+typedef uint16_t WORD; /* 16-bit unsigned integer */
+typedef uint32_t DWORD; /* 32-bit unsigned integer */
+typedef uint64_t QWORD; /* 64-bit unsigned integer */
+typedef WORD WCHAR; /* UTF-16 character type */
+
+#else /* Earlier than C99 */
+#define FF_INTDEF 1
+typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
+typedef unsigned char BYTE; /* char must be 8-bit */
+typedef unsigned short WORD; /* 16-bit unsigned integer */
+typedef unsigned long DWORD; /* 32-bit unsigned integer */
+typedef WORD WCHAR; /* UTF-16 character type */
+#endif
+
+
+/* Type of file size and LBA variables */
+
+#if FF_FS_EXFAT
+#if FF_INTDEF != 2
+#error exFAT feature wants C99 or later
+#endif
+typedef QWORD FSIZE_t;
+#if FF_LBA64
+typedef QWORD LBA_t;
+#else
+typedef DWORD LBA_t;
+#endif
+#else
+#if FF_LBA64
+#error exFAT needs to be enabled when enable 64-bit LBA
+#endif
+typedef DWORD FSIZE_t;
+typedef DWORD LBA_t;
+#endif
+
+
+
+/* Type of path name strings on FatFs API (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
+
+
+
+/* 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 mapping table */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifndef FF_VOLUME_STRS
+extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
+#endif
+#endif
+
+
+
+/* Filesystem object structure (FATFS) */
+
+typedef struct {
+ 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] */
+ LBA_t volbase; /* Volume base sector */
+ LBA_t fatbase; /* FAT base sector */
+ LBA_t dirbase; /* Root directory base sector/cluster */
+ LBA_t database; /* Data base sector */
+#if FF_FS_EXFAT
+ LBA_t bitbase; /* Allocation bitmap base sector */
+#endif
+ LBA_t winsect; /* Current sector appearing in the win[] */
+ BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
+} 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) */
+ LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
+#if !FF_FS_READONLY
+ LBA_t 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]; /* File private data read/write window */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (DIR) */
+
+typedef struct {
+ FFOBJID obj; /* Object identifier */
+ DWORD dptr; /* Current read/write offset */
+ DWORD clust; /* Current cluster */
+ LBA_t 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;
+
+
+
+/* Format parameter structure (MKFS_PARM) */
+
+typedef struct {
+ BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
+ BYTE n_fat; /* Number of FATs */
+ UINT align; /* Data area alignment (sector) */
+ UINT n_root; /* Number of root directory entries */
+ DWORD au_size; /* Cluster size (byte) */
+} MKFS_PARM;
+
+
+
+/* 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 */
+ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
+} 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_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 */
+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, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
+FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], 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)
+
+
+
+
+/*--------------------------------------------------------------*/
+/* 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_DIR 0x10 /* Directory */
+#define AM_ARC 0x20 /* Archive */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FF_DEFINED */
diff --git a/fw/fe310/fsfat/ffconf.h b/fw/fe310/fsfat/ffconf.h
new file mode 100644
index 0000000..e822ee0
--- /dev/null
+++ b/fw/fe310/fsfat/ffconf.h
@@ -0,0 +1,301 @@
+/*---------------------------------------------------------------------------/
+/ FatFs Functional Configurations
+/---------------------------------------------------------------------------*/
+
+#define FFCONF_DEF 86631 /* 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_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 1
+/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FASTSEEK 0
+/* This option switches fast seek function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_EXPAND 1
+/* 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 1
+/* This option switches volume label functions, f_getlabel() and f_setlabel().
+/ (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FORWARD 1
+/* This option switches f_forward() function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_STRFUNC 0
+#define FF_PRINT_LLI 0
+#define FF_PRINT_FLOAT 0
+#define FF_STRF_ENCODE 0
+/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
+/ f_printf().
+/
+/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
+/ 1: Enable without LF-CRLF conversion.
+/ 2: Enable with LF-CRLF conversion.
+/
+/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
+ makes f_printf() support floating point argument. These features want C99 or later.
+/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
+/ encoding in it. FF_STRF_ENCODE 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
+*/
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_CODE_PAGE 437
+/* 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 0
+#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 it 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() exemplified 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_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 "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
+/* 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",...
+*/
+
+
+#define FF_MULTI_PARTITION 1
+/* 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_LBA64 0
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
+/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
+
+
+#define FF_MIN_GPT 0x10000000
+/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
+/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
+
+
+#define FF_USE_TRIM 1
+/* 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. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_TINY 1
+/* 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 0
+/* 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 2020
+/* The option FF_FS_NORTC switches timestamp functiton. 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 in read-only configuration (FF_FS_READONLY = 1). */
+
+
+#define FF_FS_NOFSINFO 0
+/* 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.
+*/
+
+
+#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 <somertos.h> // 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/fw/fe310/test/Makefile b/fw/fe310/test/Makefile
new file mode 100644
index 0000000..8476db8
--- /dev/null
+++ b/fw/fe310/test/Makefile
@@ -0,0 +1,24 @@
+include ../common.mk
+
+CRYPTO_DIR = ../../../crypto
+
+CFLAGS += -I.. -I../eos -I../bsp/include -I../bsp/drivers -I$(CRYPTO_DIR)
+LDFLAGS = $(CFLAGS) -L.. -Wl,--gc-sections -nostartfiles -nostdlib -Wl,--start-group -lc -lm -lgcc -leos -Wl,--end-group -T../bsp/default.lds
+
+DEPS = main.o status.o cell_dev.o cell_pdp.o phone.o modem.o wifi.o cam.o fs.o test.o
+TARGET = phone
+
+all: $(TARGET)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
+
+$(TARGET): $(DEPS)
+ $(CC) $(DEPS) $(LDFLAGS) -o $@
+
+clean:
+ rm -f *.o *.a $(TARGET)
+
+upload: $(TARGET)
+ ../bsp/upload --elf ./$(TARGET) --openocd $(RISCV_OPENOCD_HOME)/bin/openocd --gdb $(RISCV_HOME)/bin/riscv64-unknown-elf-gdb --openocd-config ../bsp/openocd.cfg
+
diff --git a/fw/fe310/test/cam.c b/fw/fe310/test/cam.c
new file mode 100644
index 0000000..caab2cc
--- /dev/null
+++ b/fw/fe310/test/cam.c
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <cam.h>
+
+#include <i2c.h>
+#include <i2c/ov2640.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include <board.h>
+
+#include "cam.h"
+
+#include <stdio.h>
+
+static int cam_init = 0;
+static int cam_capture = 0;
+
+#define CHUNK_SIZE 512
+
+#define CAM_W 640
+#define CAM_H 480
+
+static void transfer_chunk(uint8_t *fbuf, size_t size, int first, int last, uint32_t addr) {
+ int rv;
+
+ eos_cam_fbuf_read(fbuf, size, first);
+ if (last) {
+ eos_cam_fbuf_done();
+ eos_cam_capture();
+ }
+
+ eos_spi_select(EOS_SPI_DEV_EVE);
+ eve_cmd_burst_start();
+ if (first) eve_cmd(CMD_LOADIMAGE, "ww+", addr, EVE_OPT_NODL);
+ eve_cmd_write(fbuf, size);
+ if (last) eve_cmd_end();
+ eve_cmd_burst_end();
+ rv = eve_cmd_exec(last);
+ if (rv) printf("CMD EXEC ERR\n");
+ if (!last) eos_spi_select(EOS_SPI_DEV_CAM);
+}
+
+static void transfer_img(uint32_t addr) {
+ int i;
+ uint32_t fb_size;
+ uint32_t fb_div;
+ uint32_t fb_mod;
+ uint8_t fbuf[CHUNK_SIZE];
+
+ cam_capture = 1;
+ fb_size = eos_cam_fbuf_size();
+
+ fb_div = fb_size / CHUNK_SIZE;
+ fb_mod = fb_size % CHUNK_SIZE;
+ for (i=0; i<fb_div; i++) {
+ transfer_chunk(fbuf, CHUNK_SIZE, i == 0, (fb_mod == 0) && (i + 1 == fb_div), addr);
+ }
+ if (fb_mod) {
+ transfer_chunk(fbuf, fb_mod, fb_size < CHUNK_SIZE, 1, addr);
+ }
+ printf("CAPTURE DONE. ADDR:%x SIZE:%d\n", addr, fb_size);
+}
+
+static void user_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ eos_spi_select(EOS_SPI_DEV_CAM);
+ if (eos_cam_capture_done()) {
+ EVEWindowRoot *root = app_root();
+ uint32_t addr = root->mem_next;
+
+ transfer_img(addr);
+ eve_window_root_draw(root);
+ }
+ eos_spi_deselect();
+ eos_evtq_push(EOS_EVT_USER, NULL, 0);
+}
+
+static void image_draw(EVEFreeWidget *widget) {
+ EVEWindowRoot *root = widget->w.page->v.window->root;
+ uint32_t addr = root->mem_next;
+
+ if (cam_capture) {
+ // eve_freew_tag(widget);
+ eve_cmd_dl(TAG_MASK(0));
+ eve_cmd_dl(BEGIN(EVE_BITMAPS));
+ // eve_cmd_dl(BITMAP_HANDLE(15));
+ eve_cmd_dl(BITMAP_SOURCE(addr));
+ eve_cmd_dl(BITMAP_LAYOUT(EVE_RGB565, CAM_W * 2, CAM_H));
+ eve_cmd_dl(BITMAP_LAYOUT_H(CAM_W * 2, CAM_H));
+ eve_cmd_dl(BITMAP_SIZE(EVE_NEAREST, EVE_BORDER, EVE_BORDER, CAM_H, CAM_W));
+ eve_cmd_dl(BITMAP_SIZE_H(CAM_H, CAM_W));
+ eve_cmd(CMD_LOADIDENTITY, "");
+ eve_cmd(CMD_TRANSLATE, "ww", CAM_H * 65536, 0);
+ eve_cmd(CMD_ROTATE, "w", 90 * 65536 / 360);
+ eve_cmd(CMD_SETMATRIX, "");
+ eve_cmd_dl(VERTEX2F(0, 0));
+ eve_cmd_dl(TAG_MASK(1));
+ }
+}
+
+static int image_touch(EVEFreeWidget *widget, EVETouch *touch, uint16_t evt) {
+ return 0;
+}
+
+void fbuf_print(uint8_t *fbuf, size_t size) {
+ int i;
+
+ for (i=0; i<size; i++) {
+ if (i % 128 == 0) printf("\n");
+ printf("%.2x", fbuf[i]);
+ }
+}
+
+void app_cam(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .widget.type = EVE_WIDGET_TYPE_FREE,
+ .widget.g.h = CAM_W,
+ .widget.spec.free.draw = image_draw,
+ .widget.spec.free.touch = image_touch,
+ },
+ };
+ EVEForm *form = eve_form_create(window, stack, spec, 1, NULL, NULL, app_cam_close);
+ int rv = EOS_OK;
+
+ eve_gpio_set(EVE_GPIO_CAM, 1);
+ eos_time_sleep(100);
+ eos_i2c_start(100000);
+
+ rv = eos_ov2640_init();
+ if (!rv) rv = eos_ov2640_set_pixfmt(PIXFORMAT_JPEG);
+ if (!rv) rv = eos_ov2640_set_framesize(FRAMESIZE_VGA);
+ eos_i2c_stop();
+
+ if (!rv) {
+ printf("CAM INIT\n");
+ } else {
+ printf("CAM INIT ERR:%d\n", rv);
+ }
+ eos_evtq_set_handler(EOS_EVT_USER, user_handler);
+ eos_evtq_push(EOS_EVT_USER, NULL, 0);
+ eos_spi_select(EOS_SPI_DEV_CAM);
+ eos_cam_capture();
+ eos_spi_select(EOS_SPI_DEV_EVE);
+}
+
+void app_cam_close(EVEForm *form) {
+ eve_form_destroy(form);
+ eve_gpio_set(EVE_GPIO_CAM, 0);
+ eos_evtq_get(EOS_EVT_USER, NULL, NULL);
+ eos_evtq_set_handler(EOS_EVT_USER, NULL);
+}
diff --git a/fw/fe310/test/cam.h b/fw/fe310/test/cam.h
new file mode 100644
index 0000000..fdf07b8
--- /dev/null
+++ b/fw/fe310/test/cam.h
@@ -0,0 +1,2 @@
+void app_cam(EVEWindow *window, EVEViewStack *stack);
+void app_cam_close(EVEForm *form);
diff --git a/fw/fe310/test/cell_dev.c b/fw/fe310/test/cell_dev.c
new file mode 100644
index 0000000..40f2736
--- /dev/null
+++ b/fw/fe310/test/cell_dev.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <net.h>
+#include <cell.h>
+
+#include "status.h"
+#include "cell_dev.h"
+
+static void cell_dev_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ switch (type) {
+ case EOS_CELL_MTYPE_READY:
+ app_status_msg_set("Modem ready", 1);
+ break;
+ }
+ eos_net_free(buffer, 0);
+}
+
+void app_cell_dev_init(void) {
+ eos_cell_set_handler(EOS_CELL_MTYPE_DEV, cell_dev_handler);
+}
diff --git a/fw/fe310/test/cell_dev.h b/fw/fe310/test/cell_dev.h
new file mode 100644
index 0000000..19b7717
--- /dev/null
+++ b/fw/fe310/test/cell_dev.h
@@ -0,0 +1 @@
+void app_cell_dev_init(void); \ No newline at end of file
diff --git a/fw/fe310/test/cell_pdp.c b/fw/fe310/test/cell_pdp.c
new file mode 100644
index 0000000..9fc4a21
--- /dev/null
+++ b/fw/fe310/test/cell_pdp.c
@@ -0,0 +1,108 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <net.h>
+#include <cell.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include "status.h"
+#include "cell_pdp.h"
+
+static void cell_pdp_connect(char *apn, char *user, char *pass) {
+ unsigned char *buffer, *p;
+
+ buffer = eos_net_alloc();
+ buffer[0] = EOS_CELL_MTYPE_PDP | EOS_CELL_MTYPE_PDP_CONFIG;
+ p = buffer + 1;
+ strcpy(p, user);
+ p += strlen(user) + 1;
+ strcpy(p, user);
+ p += strlen(user) + 1;
+ strcpy(p, pass);
+ p += strlen(pass) + 1;
+ eos_net_send(EOS_NET_MTYPE_CELL, buffer, p - buffer, 1);
+
+ buffer = eos_net_alloc();
+ buffer[0] = EOS_CELL_MTYPE_PDP | EOS_CELL_MTYPE_PDP_CONNECT;
+ eos_net_send(EOS_NET_MTYPE_CELL, buffer, 1, 0);
+}
+
+static void cell_pdp_disconnect(void) {
+ unsigned char *buffer = eos_net_alloc();
+ buffer[0] = EOS_CELL_MTYPE_PDP | EOS_CELL_MTYPE_PDP_DISCONNECT;
+ eos_net_send(EOS_NET_MTYPE_CELL, buffer, 1, 0);
+}
+
+static void cell_pdp_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ switch (type) {
+ case EOS_CELL_MTYPE_PDP_CONNECT:
+ app_status_msg_set("Cell data connected", 1);
+ break;
+
+ case EOS_CELL_MTYPE_PDP_DISCONNECT:
+ app_status_msg_set("Cell data disconnected", 1);
+ break;
+
+ default:
+ break;
+ }
+ eos_net_free(buffer, 0);
+}
+
+void app_cell_pdp(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .label.g.w = APP_SCREEN_W / 3,
+ .label.title = "APN:",
+
+ .widget.type = EVE_WIDGET_TYPE_STR,
+ .widget.spec.str.str_size = 128,
+ },
+ {
+ .label.g.w = APP_SCREEN_W / 3,
+ .label.title = "User:",
+
+ .widget.type = EVE_WIDGET_TYPE_STR,
+ .widget.spec.str.str_size = 128,
+ },
+ {
+ .label.g.w = APP_SCREEN_W / 3,
+ .label.title = "Pass:",
+
+ .widget.type = EVE_WIDGET_TYPE_STR,
+ .widget.spec.str.str_size = 128,
+ },
+ };
+
+ EVEForm *form = eve_form_create(window, stack, spec, 3, NULL, app_cell_pdp_action, app_cell_pdp_close);
+}
+
+void app_cell_pdp_action(EVEForm *form) {
+ EVEStrWidget *apn = (EVEStrWidget *)eve_page_widget(&form->p, 0);
+ EVEStrWidget *user = (EVEStrWidget *)eve_page_widget(&form->p, 1);
+ EVEStrWidget *pass = (EVEStrWidget *)eve_page_widget(&form->p, 2);
+
+ cell_pdp_connect(apn->str, user->str, pass->str);
+}
+
+void app_cell_pdp_close(EVEForm *form) {
+ eve_form_destroy(form);
+}
+
+void app_cell_pdp_init(void) {
+ eos_cell_set_handler(EOS_CELL_MTYPE_PDP, cell_pdp_handler);
+}
diff --git a/fw/fe310/test/cell_pdp.h b/fw/fe310/test/cell_pdp.h
new file mode 100644
index 0000000..3c28176
--- /dev/null
+++ b/fw/fe310/test/cell_pdp.h
@@ -0,0 +1,4 @@
+void app_cell_pdp(EVEWindow *window, EVEViewStack *stack);
+void app_cell_pdp_action(EVEForm *form);
+void app_cell_pdp_close(EVEForm *form);
+void app_cell_pdp_init(void); \ No newline at end of file
diff --git a/fw/fe310/test/fs.c b/fw/fe310/test/fs.c
new file mode 100644
index 0000000..e8b3587
--- /dev/null
+++ b/fw/fe310/test/fs.c
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <sdc_crypto.h>
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <aes/aes.h>
+#include <fsfat/ff.h>
+
+#include "fs.h"
+
+FATFS fs;
+
+#define TEXT_SIZE 128
+#define TEXT_FN "test.txt"
+#define KEY "passwordpassword"
+
+PARTITION VolToPart[FF_VOLUMES] = {
+ {0, 1} /* "0:" ==> 1st partition on the pd#0 */
+};
+
+static EOSSDCCrypto sdcc;
+static AESCtx ctx_crypt;
+static AESCtx ctx_essiv;
+
+void app_fs(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .label.title = "Text",
+ .widget.type = EVE_WIDGET_TYPE_STR,
+ .widget.spec.str.str_size = TEXT_SIZE,
+ },
+ };
+ EVEForm *form = eve_form_create(window, stack, spec, 1, NULL, NULL, app_fs_close);
+ EVEStrWidget *text = (EVEStrWidget *)eve_page_widget(&form->p, 0);
+ FIL f;
+ FRESULT rv;
+
+ eos_spi_select(EOS_SPI_DEV_SDC);
+ rv = f_open(&f, TEXT_FN, FA_READ);
+ printf("f_open:%d\n", rv);
+ if (!rv) {
+ UINT r;
+
+ rv = f_read(&f, text->str, TEXT_SIZE-1, &r);
+ printf("f_read:%d\n", rv);
+ if (rv != FR_OK) r = 0;
+ text->str[r] = '\0';
+ f_close(&f);
+ }
+ eos_spi_select(EOS_SPI_DEV_EVE);
+ eve_strw_update(text);
+}
+
+void app_fs_close(EVEForm *form) {
+ EVEStrWidget *text = (EVEStrWidget *)eve_page_widget(&form->p, 0);
+ FIL f;
+ FRESULT rv;
+
+ eos_spi_select(EOS_SPI_DEV_SDC);
+ rv = f_open(&f, TEXT_FN, FA_WRITE | FA_CREATE_ALWAYS);
+ printf("f_open:%d\n", rv);
+ if (!rv) {
+ UINT w;
+
+ rv = f_write(&f, text->str, strlen(text->str), &w);
+ printf("f_write:%d\n", rv);
+ f_close(&f);
+ }
+ eos_spi_select(EOS_SPI_DEV_EVE);
+ eve_form_destroy(form);
+}
+
+void app_fs_init(void) {
+ FRESULT rv;
+
+ eos_sdcc_init(&sdcc, KEY, &ctx_crypt, (eve_sdcc_init_t)aes_init, (eve_sdcc_crypt_t)aes_cbc_encrypt, (eve_sdcc_crypt_t)aes_cbc_decrypt, &ctx_essiv, (eve_sdcc_init_t)aes_init, (eve_sdcc_essiv_t)aes_ecb_encrypt);
+ eos_spi_select(EOS_SPI_DEV_SDC);
+ rv = f_mount(&fs, "", 1);
+ printf("f_mount:%d\n", rv);
+ if (rv == FR_NO_FILESYSTEM) {
+ uint8_t w[FF_MAX_SS];
+ LBA_t plist[] = {100, 0};
+
+ rv = f_fdisk(0, plist, w);
+ printf("f_fdisk:%d\n", rv);
+ rv = f_mkfs("0:", 0, w, sizeof(w));
+ printf("f_mkfs:%d\n", rv);
+ rv = f_mount(&fs, "", 1);
+ printf("f_mount:%d\n", rv);
+ }
+ eos_spi_deselect();
+} \ No newline at end of file
diff --git a/fw/fe310/test/fs.h b/fw/fe310/test/fs.h
new file mode 100644
index 0000000..c525c5b
--- /dev/null
+++ b/fw/fe310/test/fs.h
@@ -0,0 +1,3 @@
+void app_fs(EVEWindow *window, EVEViewStack *stack);
+void app_fs_close(EVEForm *form);
+void app_fs_init(void);
diff --git a/fw/fe310/test/main.c b/fw/fe310/test/main.c
new file mode 100644
index 0000000..844dedc
--- /dev/null
+++ b/fw/fe310/test/main.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include "status.h"
+#include "cell_dev.h"
+#include "cell_pdp.h"
+#include "phone.h"
+#include "modem.h"
+#include "wifi.h"
+#include "cam.h"
+#include "fs.h"
+#include "test.h"
+
+void app_home_page(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "Phone",
+ .widget.spec.page.constructor = app_phone
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "WiFi",
+ .widget.spec.page.constructor = app_wifi
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "Cellular data",
+ .widget.spec.page.constructor = app_cell_pdp
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "Modem",
+ .widget.spec.page.constructor = app_modem
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "Camera",
+ .widget.spec.page.constructor = app_cam
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "File system",
+ .widget.spec.page.constructor = app_fs
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_PAGE,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.page.title = "Test",
+ .widget.spec.page.constructor = app_test
+ },
+ };
+
+ EVEForm *form = eve_form_create(window, stack, spec, 7, NULL, NULL, NULL);
+}
+
+int main() {
+ printf("\nREADY.\n");
+
+ eos_init();
+
+ app_root_init(app_home_page);
+ app_status_init();
+ app_phone_init();
+ app_wifi_init();
+ app_cell_dev_init();
+ app_cell_pdp_init();
+ app_fs_init();
+
+ eos_evtq_loop();
+}
diff --git a/fw/fe310/test/modem.c b/fw/fe310/test/modem.c
new file mode 100644
index 0000000..b9fd1b1
--- /dev/null
+++ b/fw/fe310/test/modem.c
@@ -0,0 +1,168 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <uart.h>
+#include <net.h>
+#include <cell.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_text.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include "modem.h"
+
+typedef struct {
+ uint32_t mem;
+ EVEViewStack *stack;
+ eos_evt_handler_t cell_dev_handler;
+ EVEText text;
+} VParam;
+
+static void key_down(void *p, int c) {
+ EVEView *view = p;
+ EVEText *text = &((VParam *)view->param)->text;
+ unsigned char *buf;
+ int i = 2;
+
+ if (c == 0x11) {
+ app_modem_close(view);
+ return;
+ }
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_DEV | EOS_CELL_MTYPE_UART_DATA;
+ if (c == '\n') {
+ buf[1] = '\r';
+ buf[2] = '\n';
+ i++;
+ } else {
+ buf[1] = c;
+ }
+
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, i, 0);
+ eve_text_scroll0(text);
+}
+
+static void handle_uart(unsigned char type) {
+ int i = 0;
+ int c = 0;
+ unsigned char *buf = NULL;
+
+ c = eos_uart_getc(0);
+ if (c == EOS_ERR_EMPTY) return;
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_DEV | EOS_CELL_MTYPE_UART_DATA;
+ buf[1] = c;
+ i = 2;
+ while ((c = eos_uart_getc(0)) != EOS_ERR_EMPTY) {
+ buf[i] = c;
+ i++;
+ if (i == EOS_NET_MTU) break;
+ }
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, i, 0);
+ eos_uart_rxwm_set(0);
+}
+
+static void handle_cell_msg(unsigned char type, unsigned char *buffer, uint16_t len) {
+ EVEWindowRoot *root = app_root();
+ EVEWindow *window = eve_window_search(&root->w, "main");
+ VParam *param = window->view->param;
+
+ if (type == EOS_CELL_MTYPE_UART_DATA) {
+ EVEText *text = &param->text;
+ int i;
+
+ eve_spi_start();
+ for (i=1; i<len; i++) {
+ if (buffer[i] != '\r') eve_text_putc(text, buffer[i]);
+ }
+ if (text->dirty) {
+ text->dirty = 0;
+ eve_window_root_draw(root);
+ }
+ eve_spi_stop();
+ eos_net_free(buffer, 0);
+ } else {
+ param->cell_dev_handler(type, buffer, len);
+ }
+}
+
+static uint8_t modem_draw(EVEView *view, uint8_t tag0) {
+ VParam *param = view->param;
+ EVEText *text = &param->text;
+
+ tag0 = eve_view_clear(view, tag0, 0);
+ return eve_text_draw(text, tag0);
+}
+
+static int modem_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ VParam *param = view->param;
+ EVEText *text = &param->text;
+
+ return eve_text_touch(text, touch, evt, tag0);
+}
+
+void app_modem(EVEWindow *window, EVEViewStack *stack) {
+ unsigned char *buf;
+ EVEWindowRoot *root = window->root;
+ EVEKbd *kbd = eve_window_kbd(window);
+ EVERect g = {0, 60, 480, 512};
+ EVEView *view;
+ VParam *param;
+
+ view = eve_malloc(sizeof(EVEView));
+ param = eve_malloc(sizeof(VParam));
+ param->mem = root->mem_next;
+ param->stack = stack;
+ param->cell_dev_handler = eos_cell_get_handler(EOS_CELL_MTYPE_DEV);
+ eve_text_init(&param->text, &g, 30, 16, 200, root->mem_next, &root->mem_next);
+ eve_view_init(view, window, modem_draw, modem_touch, NULL, param);
+
+ eve_kbd_set_handler(kbd, key_down, view);
+ eve_window_kbd_attach(window);
+
+ eos_uart_set_handler(EOS_UART_ETYPE_RX, handle_uart);
+ eos_cell_set_handler(EOS_CELL_MTYPE_DEV, handle_cell_msg);
+ eos_net_acquire_for_evt(EOS_EVT_UART | EOS_UART_ETYPE_RX, 1);
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_DEV | EOS_CELL_MTYPE_UART_TAKE;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1, 0);
+ eos_uart_rxwm_set(0);
+}
+
+void app_modem_close(EVEView *view) {
+ unsigned char *buf = eos_net_alloc();
+ VParam *param = view->param;
+ EVEWindow *window = view->window;
+ EVEWindowRoot *root = window->root;
+ EVEKbd *kbd = eve_window_kbd(window);
+ EVEViewStack *stack = param->stack;
+
+ buf[0] = EOS_CELL_MTYPE_DEV | EOS_CELL_MTYPE_RESET;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1, 0);
+ eos_uart_rxwm_clear();
+ eos_uart_set_handler(EOS_UART_ETYPE_RX, NULL);
+ eos_cell_set_handler(EOS_CELL_MTYPE_DEV, param->cell_dev_handler);
+ eos_net_acquire_for_evt(EOS_EVT_UART | EOS_UART_ETYPE_RX, 0);
+
+ root->mem_next = param->mem;
+ eve_window_kbd_detach(window);
+ eve_kbd_set_handler(kbd, NULL, NULL);
+
+ eve_free(param);
+ eve_free(view);
+ eve_view_destroy(window, stack);
+}
diff --git a/fw/fe310/test/modem.h b/fw/fe310/test/modem.h
new file mode 100644
index 0000000..3a6bc35
--- /dev/null
+++ b/fw/fe310/test/modem.h
@@ -0,0 +1,2 @@
+void app_modem(EVEWindow *window, EVEViewStack *stack);
+void app_modem_close(EVEView *view);
diff --git a/fw/fe310/test/phone.c b/fw/fe310/test/phone.c
new file mode 100644
index 0000000..78531b5
--- /dev/null
+++ b/fw/fe310/test/phone.c
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <i2s.h>
+#include <net.h>
+#include <cell.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include "status.h"
+#include "phone.h"
+
+#define ABUF_SIZE 128
+#define MIC_WM 64
+
+static uint8_t mic_arr[ABUF_SIZE];
+static uint8_t spk_arr[ABUF_SIZE];
+
+#define VOICE_STATE_IDLE 0
+#define VOICE_STATE_DIAL 1
+#define VOICE_STATE_RING 2
+#define VOICE_STATE_CIP 3
+
+static unsigned char voice_state = 0;
+
+static void handle_mic(unsigned char type) {
+ uint16_t size;
+ unsigned char *buf = eos_net_alloc();
+
+ buf[0] = EOS_CELL_MTYPE_VOICE_PCM;
+ size = eos_i2s_mic_read(buf+1, MIC_WM);
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, size+1, 0);
+}
+
+static void cell_voice_handler(unsigned char type, unsigned char *buffer, uint16_t len) {
+ char msg[128];
+
+ msg[0] = 0;
+ switch (type) {
+ case EOS_CELL_MTYPE_VOICE_RING:
+ voice_state = VOICE_STATE_RING;
+ sprintf(msg, "RING:%s", buffer+1);
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_MISS:
+ voice_state = VOICE_STATE_IDLE;
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_BEGIN:
+ printf("VOICE BEGIN\n");
+ voice_state = VOICE_STATE_CIP;
+ eos_i2s_start(8000, EOS_I2S_FMT_PCM16);
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_END:
+ voice_state = VOICE_STATE_IDLE;
+ eos_i2s_stop();
+ break;
+
+ case EOS_CELL_MTYPE_VOICE_PCM:
+ if (voice_state == VOICE_STATE_CIP) {
+ eos_i2s_spk_write(buffer+1, len-1);
+ }
+ break;
+ }
+ app_status_msg_set(msg, 1);
+ eos_net_free(buffer, 0);
+}
+
+void app_phone(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .label.title = "Phone:",
+
+ .widget.type = EVE_WIDGET_TYPE_STR,
+ .widget.spec.str.str_size = 128,
+ },
+ };
+
+ EVEForm *form = eve_form_create(window, stack, spec, 1, NULL, app_phone_action, NULL);
+}
+
+void app_phone_action(EVEForm *form) {
+ char msg[128];
+ EVEStrWidget *w = (EVEStrWidget *)eve_page_widget(&form->p, 0);
+ unsigned char *buf = eos_net_alloc();
+
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_DIAL;
+ strcpy(buf + 1, w->str);
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1 + strlen(w->str), 0);
+
+ voice_state = VOICE_STATE_DIAL;
+ sprintf(msg, "DIAL:%s", w->str);
+ app_status_msg_set(msg, 0);
+}
+
+void app_phone_init(void) {
+ eos_cell_set_handler(EOS_CELL_MTYPE_VOICE, cell_voice_handler);
+
+ eos_i2s_mic_init(mic_arr, ABUF_SIZE);
+ eos_i2s_mic_set_wm(MIC_WM);
+ eos_i2s_mic_set_handler(handle_mic);
+ eos_i2s_spk_init(spk_arr, ABUF_SIZE);
+ eos_net_acquire_for_evt(EOS_EVT_I2S | EOS_I2S_ETYPE_MIC, 1);
+}
+
+unsigned char app_phone_state_get(void) {
+ return voice_state;
+}
diff --git a/fw/fe310/test/phone.h b/fw/fe310/test/phone.h
new file mode 100644
index 0000000..f346a38
--- /dev/null
+++ b/fw/fe310/test/phone.h
@@ -0,0 +1,9 @@
+#define VOICE_STATE_IDLE 0
+#define VOICE_STATE_DIAL 1
+#define VOICE_STATE_RING 2
+#define VOICE_STATE_CIP 3
+
+void app_phone(EVEWindow *window, EVEViewStack *stack);
+void app_phone_action(EVEForm *form);
+void app_phone_init(void);
+unsigned char app_phone_state_get(void);
diff --git a/fw/fe310/test/status.c b/fw/fe310/test/status.c
new file mode 100644
index 0000000..42d193a
--- /dev/null
+++ b/fw/fe310/test/status.c
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <net.h>
+#include <cell.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include "phone.h"
+#include "status.h"
+
+static char status_msg[128];
+
+static int status_touch(EVEView *view, EVETouch *touch, uint16_t evt, uint8_t tag0) {
+ unsigned char state = app_phone_state_get();
+ int8_t touch_idx = eve_touch_get_idx(touch);
+
+ if (touch_idx != 0) return 0;
+
+ evt = eve_touch_evt(touch, evt, tag0, view->tag, 2);
+ if (touch && (evt & EVE_TOUCH_ETYPE_POINT_UP)) {
+ if ((state == VOICE_STATE_RING) && (touch->eevt & EVE_TOUCH_EETYPE_TRACK_LEFT)) {
+ unsigned char *buf = eos_net_alloc();
+
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_ANSWER;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1, 0);
+ status_msg[0] = '\0';
+ }
+ if ((state != VOICE_STATE_IDLE) && (touch->eevt & EVE_TOUCH_EETYPE_TRACK_RIGHT)) {
+ unsigned char *buf = eos_net_alloc();
+
+ buf[0] = EOS_CELL_MTYPE_VOICE | EOS_CELL_MTYPE_VOICE_HANGUP;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1, 0);
+ status_msg[0] = '\0';
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static uint8_t status_draw(EVEView *view, uint8_t tag0) {
+ uint8_t tag_opt = EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_XY;
+
+ tag0 = eve_view_clear(view, tag0, tag_opt);
+
+ if (tag0 != EVE_NOTAG) {
+ eve_touch_set_opt(tag0, eve_touch_get_opt(tag0) | tag_opt);
+ eve_cmd_dl(TAG(tag0));
+ tag0++;
+ }
+
+ eve_cmd(CMD_TEXT, "hhhhs", 0, 0, 31, 0, status_msg);
+
+ return tag0;
+}
+
+void app_status_msg_set(char *msg, int refresh) {
+ strcpy(status_msg, msg);
+
+ if (refresh) app_root_refresh();
+}
+
+void app_status_init(void) {
+ EVEWindowRoot *root = app_root();
+ EVEWindow *status = eve_window_search(&root->w, "status");
+ status->view->touch = status_touch;
+ status->view->draw = status_draw;
+}
diff --git a/fw/fe310/test/status.h b/fw/fe310/test/status.h
new file mode 100644
index 0000000..c891b7d
--- /dev/null
+++ b/fw/fe310/test/status.h
@@ -0,0 +1,2 @@
+void app_status_msg_set(char *msg, int refresh);
+void app_status_init(void);
diff --git a/fw/fe310/test/test.c b/fw/fe310/test/test.c
new file mode 100644
index 0000000..9e5c790
--- /dev/null
+++ b/fw/fe310/test/test.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <i2c.h>
+#include <i2c/bq25895.h>
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include <board.h>
+
+#include "test.h"
+
+#include <stdio.h>
+
+int app_test_uievt(EVEForm *form, uint16_t evt, void *param) {
+ uint8_t data = 0;
+ int ret = 0, i;
+
+ switch (evt) {
+ case EVE_UIEVT_PAGE_TOUCH:
+ printf("PAGE TOUCH\n");
+ printf("BQ25895:\n");
+ eos_i2c_start(400000);
+ for (i=0; i<0x15; i++) {
+ ret = eos_i2c_read8(BQ25895_ADDR, i, &data);
+ if (!ret) printf("REG%02x: %02x\n", i, data);
+ }
+ eos_i2c_stop();
+ break;
+
+ default:
+ ret = eve_form_uievt(form, evt, param);
+ break;
+ }
+ return ret;
+}
+
+void app_test(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .widget.type = EVE_WIDGET_TYPE_SPACER,
+ .widget.g.h = 1,
+ },
+ };
+ EVEForm *form = eve_form_create(window, stack, spec, 1, app_test_uievt, NULL, app_test_close);
+}
+
+void app_test_close(EVEForm *form) {
+ eve_form_destroy(form);
+}
diff --git a/fw/fe310/test/test.h b/fw/fe310/test/test.h
new file mode 100644
index 0000000..daf1f1a
--- /dev/null
+++ b/fw/fe310/test/test.h
@@ -0,0 +1,2 @@
+void app_test(EVEWindow *window, EVEViewStack *stack);
+void app_test_close(EVEForm *form);
diff --git a/fw/fe310/test/wifi.c b/fw/fe310/test/wifi.c
new file mode 100644
index 0000000..814d808
--- /dev/null
+++ b/fw/fe310/test/wifi.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <eos.h>
+#include <net.h>
+#include <wifi.h>
+
+#include <eve/eve.h>
+#include <eve/eve_kbd.h>
+#include <eve/eve_font.h>
+
+#include <eve/screen/window.h>
+#include <eve/screen/page.h>
+#include <eve/screen/form.h>
+
+#include <eve/widget/widgets.h>
+
+#include <app/app_root.h>
+
+#include "status.h"
+#include "wifi.h"
+
+static void wifi_scan(void) {
+ unsigned char *buffer = eos_net_alloc();
+ buffer[0] = EOS_WIFI_MTYPE_SCAN;
+ eos_net_send(EOS_NET_MTYPE_WIFI, buffer, 1, 0);
+}
+
+static void wifi_connect(const char *ssid, const char *pass) {
+ unsigned char *buffer, *p;
+
+ buffer = eos_net_alloc();
+ buffer[0] = EOS_WIFI_MTYPE_CONFIG;
+ p = buffer + 1;
+ strcpy(p, ssid);
+ p += strlen(ssid) + 1;
+ strcpy(p, pass);
+ p += strlen(pass) + 1;
+ eos_net_send(EOS_NET_MTYPE_WIFI, buffer, p - buffer, 1);
+
+ buffer = eos_net_alloc();
+ buffer[0] = EOS_WIFI_MTYPE_CONNECT;
+ eos_net_send(EOS_NET_MTYPE_WIFI, buffer, 1, 0);
+}
+
+static void wifi_disconnect(void) {
+ unsigned char *buffer = eos_net_alloc();
+ buffer[0] = EOS_WIFI_MTYPE_DISCONNECT;
+ eos_net_send(EOS_NET_MTYPE_WIFI, buffer, 1, 0);
+}
+
+void wifi_scan_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ EVEWindowRoot *root = app_root();
+ EVEWindow *window = eve_window_search(&root->w, "main");
+ EVEForm *form = (EVEForm *)window->view;
+ EVESelectWidget *select = (EVESelectWidget *)eve_page_widget(&form->p, 0);
+
+ eve_selectw_option_set(select, buffer + 1, size - 1);
+ eos_net_free(buffer, 0);
+
+ app_root_refresh();
+}
+
+static void wifi_connect_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ app_status_msg_set("WiFi connected", 1);
+ eos_net_free(buffer, 0);
+}
+
+static void wifi_disconnect_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ app_status_msg_set("WiFi disconnected", 1);
+ eos_net_free(buffer, 0);
+}
+
+void app_wifi(EVEWindow *window, EVEViewStack *stack) {
+ EVEWidgetSpec spec[] = {
+ {
+ .label.g.w = APP_SCREEN_W,
+ .label.title = "Select network:",
+
+ .widget.type = EVE_WIDGET_TYPE_SELECT,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.spec.select.option_size = 1500,
+ },
+ {
+ .widget.type = EVE_WIDGET_TYPE_SPACER,
+ .widget.g.w = APP_SCREEN_W,
+ .widget.g.h = 50,
+ },
+ {
+ .label.title = "Password:",
+
+ .widget.type = EVE_WIDGET_TYPE_STR,
+ .widget.spec.str.str_size = 128,
+ },
+ };
+
+ EVEForm *form = eve_form_create(window, stack, spec, 3, NULL, app_wifi_action, app_wifi_close);
+ wifi_scan();
+}
+
+void app_wifi_action(EVEForm *form) {
+ EVESelectWidget *sel = (EVESelectWidget *)eve_page_widget(&form->p, 0);
+ EVEStrWidget *str = (EVEStrWidget *)eve_page_widget(&form->p, 2);
+ char *ssid = eve_selectw_option_get_select(sel);
+
+ if (ssid) wifi_connect(ssid, str->str);
+}
+
+void app_wifi_close(EVEForm *form) {
+ eve_form_destroy(form);
+}
+
+void app_wifi_init(void) {
+ eos_wifi_set_handler(EOS_WIFI_MTYPE_SCAN, wifi_scan_handler);
+ eos_wifi_set_handler(EOS_WIFI_MTYPE_CONNECT, wifi_connect_handler);
+ eos_wifi_set_handler(EOS_WIFI_MTYPE_DISCONNECT, wifi_disconnect_handler);
+}
diff --git a/fw/fe310/test/wifi.h b/fw/fe310/test/wifi.h
new file mode 100644
index 0000000..5adaebd
--- /dev/null
+++ b/fw/fe310/test/wifi.h
@@ -0,0 +1,4 @@
+void app_wifi(EVEWindow *window, EVEViewStack *stack);
+void app_wifi_action(EVEForm *form);
+void app_wifi_close(EVEForm *form);
+void app_wifi_init(void); \ No newline at end of file