summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES103
-rw-r--r--firmware/asm/arm/corelock.c4
-rw-r--r--firmware/asm/arm/lcd-as-memframe.S4
-rw-r--r--firmware/asm/arm/memcpy.S20
-rw-r--r--firmware/asm/arm/memmove.S20
-rw-r--r--firmware/asm/arm/memset.S26
-rw-r--r--firmware/asm/arm/memset16.S20
-rw-r--r--firmware/asm/arm/thread.c6
-rw-r--r--firmware/asm/thread.h10
-rw-r--r--firmware/backlight.c96
-rw-r--r--firmware/buflib_malloc.c263
-rw-r--r--firmware/buflib_mempool.c (renamed from firmware/buflib.c)540
-rw-r--r--firmware/chunk_alloc.c319
-rw-r--r--firmware/common/diacritic.c3
-rw-r--r--firmware/common/dir.c99
-rw-r--r--firmware/common/dircache.c92
-rw-r--r--firmware/common/disk.c221
-rw-r--r--firmware/common/file.c21
-rw-r--r--firmware/common/file_internal.c109
-rw-r--r--firmware/common/fileobj_mgr.c103
-rw-r--r--firmware/common/inflate.c34
-rw-r--r--firmware/common/linked_list.c53
-rw-r--r--firmware/common/multiboot.c113
-rw-r--r--firmware/common/pathfuncs.c110
-rw-r--r--firmware/common/rb-loader.c95
-rw-r--r--firmware/common/rb_namespace.c383
-rw-r--r--firmware/common/rectangle.c141
-rw-r--r--firmware/common/strlcat.c19
-rw-r--r--firmware/common/strlcpy.c64
-rw-r--r--firmware/common/strmemccpy.c38
-rw-r--r--firmware/common/strnatcmp.c16
-rw-r--r--firmware/common/strptokspn.c77
-rw-r--r--firmware/common/structec.c193
-rw-r--r--firmware/common/unicode.c35
-rw-r--r--firmware/common/zip.c27
-rw-r--r--firmware/core_alloc.c60
-rw-r--r--firmware/drivers/ata.c45
-rw-r--r--firmware/drivers/audio/ak4376.c22
-rw-r--r--firmware/drivers/audio/eros_qn_codec.c57
-rw-r--r--firmware/drivers/audio/es9018.c4
-rw-r--r--firmware/drivers/audio/es9018k2m.c161
-rw-r--r--firmware/drivers/audio/x1000-codec.c286
-rw-r--r--firmware/drivers/axp-pmu.c202
-rw-r--r--firmware/drivers/button.c210
-rw-r--r--firmware/drivers/fat.c4
-rw-r--r--firmware/drivers/lcd-16bit-common.c675
-rw-r--r--firmware/drivers/lcd-16bit-vert.c214
-rw-r--r--firmware/drivers/lcd-16bit.c213
-rw-r--r--firmware/drivers/lcd-1bit-vert.c394
-rw-r--r--firmware/drivers/lcd-24bit.c436
-rw-r--r--firmware/drivers/lcd-2bit-horz.c442
-rw-r--r--firmware/drivers/lcd-2bit-vert.c448
-rw-r--r--firmware/drivers/lcd-2bit-vi.c450
-rw-r--r--firmware/drivers/lcd-bitmap-common.c344
-rw-r--r--firmware/drivers/lcd-color-common.c181
-rw-r--r--firmware/drivers/lcd-scroll.c14
-rw-r--r--firmware/drivers/rds.c37
-rw-r--r--firmware/drivers/rtc/rtc_pcf50605.c7
-rw-r--r--firmware/drivers/tuner/si4700.c31
-rw-r--r--firmware/drivers/usb-designware.c2
-rw-r--r--firmware/events.c97
-rw-r--r--firmware/export/ak4376.h8
-rw-r--r--firmware/export/ata.h3
-rw-r--r--firmware/export/audio.h1
-rw-r--r--firmware/export/audiohw.h14
-rw-r--r--firmware/export/axp-pmu.h6
-rw-r--r--firmware/export/backlight.h6
-rw-r--r--firmware/export/backtrace.h3
-rw-r--r--firmware/export/config.h83
-rw-r--r--firmware/export/config/erosqnative.h15
-rw-r--r--firmware/export/config/fiiom3k.h23
-rw-r--r--firmware/export/config/fiiom3klinux.h2
-rw-r--r--firmware/export/config/gigabeats.h3
-rw-r--r--firmware/export/config/hibylinux.h2
-rw-r--r--firmware/export/config/ipod6g.h8
-rw-r--r--firmware/export/config/mrobe500.h5
-rw-r--r--firmware/export/config/sansaclipplus.h2
-rw-r--r--firmware/export/config/sansaclipzip.h1
-rw-r--r--firmware/export/config/sansaconnect.h3
-rw-r--r--firmware/export/config/sansae200.h1
-rw-r--r--firmware/export/config/sansafuze.h1
-rw-r--r--firmware/export/config/sansafuzeplus.h1
-rw-r--r--firmware/export/config/sansafuzev2.h1
-rw-r--r--firmware/export/config/shanlingq1.h13
-rw-r--r--firmware/export/disk.h3
-rw-r--r--firmware/export/eros_qn_codec.h16
-rw-r--r--firmware/export/es9018k2m.h74
-rw-r--r--firmware/export/font.h4
-rw-r--r--firmware/export/i2c-coldfire.h2
-rw-r--r--firmware/export/i2c-rk27xx.h2
-rw-r--r--firmware/export/i2c-s5l8700.h2
-rw-r--r--firmware/export/i2c-s5l8702.h2
-rw-r--r--firmware/export/i2c.h4
-rw-r--r--firmware/export/jz4740.h3
-rw-r--r--firmware/export/lcd.h15
-rw-r--r--firmware/export/linuxboot.h192
-rw-r--r--firmware/export/mi4-loader.h22
-rw-r--r--firmware/export/multiboot.h30
-rw-r--r--firmware/export/mv.h13
-rw-r--r--firmware/export/pathfuncs.h9
-rw-r--r--firmware/export/pcm_sampr.h6
-rw-r--r--firmware/export/powermgmt.h28
-rw-r--r--firmware/export/rbpaths.h3
-rw-r--r--firmware/export/rectangle.h75
-rw-r--r--firmware/export/screendump.h14
-rw-r--r--firmware/export/si4700.h8
-rw-r--r--firmware/export/system.h13
-rw-r--r--firmware/export/usb.h1
-rw-r--r--firmware/export/x1000-codec.h184
-rw-r--r--firmware/export/x1000.h60
-rw-r--r--firmware/font.c164
-rw-r--r--firmware/general.c4
-rw-r--r--firmware/include/buflib.h591
-rw-r--r--firmware/include/buflib_malloc.h65
-rw-r--r--firmware/include/buflib_mempool.h97
-rw-r--r--firmware/include/chunk_alloc.h71
-rw-r--r--firmware/include/core_alloc.h48
-rw-r--r--firmware/include/dir.h6
-rw-r--r--firmware/include/dircache_redirect.h76
-rw-r--r--firmware/include/file_internal.h32
-rw-r--r--firmware/include/fileobj_mgr.h5
-rw-r--r--firmware/include/fs_defines.h18
-rw-r--r--firmware/include/inflate.h24
-rw-r--r--firmware/include/linked_list.h38
-rw-r--r--firmware/include/rb-loader.h20
-rw-r--r--firmware/include/rb_namespace.h82
-rw-r--r--firmware/include/rbendian.h237
-rw-r--r--firmware/include/string-extra.h1
-rw-r--r--firmware/include/strmemccpy.h (renamed from firmware/export/structec.h)21
-rw-r--r--firmware/include/strptokspn_r.h26
-rw-r--r--firmware/kernel/include/mutex.h5
-rw-r--r--firmware/kernel/include/queue.h3
-rw-r--r--firmware/kernel/include/tick.h2
-rw-r--r--firmware/kernel/pthread/corelock.c18
-rw-r--r--firmware/kernel/pthread/mutex.c21
-rw-r--r--firmware/kernel/pthread/thread.c204
-rw-r--r--firmware/kernel/thread.c2
-rw-r--r--firmware/libc/include/string.h4
-rw-r--r--firmware/libc/memccpy.c119
-rw-r--r--firmware/libc/strcspn.c45
-rw-r--r--firmware/libc/strtok.c63
-rw-r--r--firmware/linuxboot.c335
-rw-r--r--firmware/panic.c16
-rw-r--r--firmware/pcm.c4
-rw-r--r--firmware/powermgmt.c615
-rw-r--r--firmware/rolo.c17
-rw-r--r--firmware/scroll_engine.c36
-rw-r--r--firmware/target/arm/as3525/lcd-ssd1303.c3
-rw-r--r--firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c3
-rw-r--r--firmware/target/arm/ata-as-arm.S32
-rw-r--r--firmware/target/arm/ata-nand-telechips.c2
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c4
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenv.c3
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c3
-rw-r--r--firmware/target/arm/imx233/debug-imx233.c6
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c3
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c49
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c2
-rw-r--r--firmware/target/arm/imx31/i2c-imx31.c2
-rw-r--r--firmware/target/arm/imx31/i2c-imx31.h2
-rw-r--r--firmware/target/arm/ipod/button-target.h9
-rw-r--r--firmware/target/arm/ipod/lcd-gray.c9
-rw-r--r--firmware/target/arm/ipod/video/lcd-as-video.S26
-rw-r--r--firmware/target/arm/lcd-ssd1815.c6
-rw-r--r--firmware/target/arm/olympus/mrobe-100/lcd-mr100.c6
-rw-r--r--firmware/target/arm/pcm-telechips.c6
-rw-r--r--firmware/target/arm/pp/mi4-loader.c89
-rw-r--r--firmware/target/arm/pp/pcm-pp.c8
-rw-r--r--firmware/target/arm/rk27xx/lcdif-rk27xx.c3
-rw-r--r--firmware/target/arm/s3c2440/i2c-s3c2440.h2
-rw-r--r--firmware/target/arm/s5l8700/debug-s5l8700.c4
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/button-target.h9
-rw-r--r--firmware/target/arm/s5l8700/yps3/lcd-yps3.c11
-rw-r--r--firmware/target/arm/s5l8702/debug-s5l8702.c4
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/button-target.h9
-rw-r--r--firmware/target/arm/samsung/yh920/lcd-yh920.c3
-rw-r--r--firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c2
-rw-r--r--firmware/target/arm/tms320dm320/i2c-dm320.h2
-rw-r--r--firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c4
-rw-r--r--firmware/target/coldfire/debug-coldfire.c5
-rw-r--r--firmware/target/coldfire/iaudio/m3/lcd-m3.c6
-rw-r--r--firmware/target/coldfire/iaudio/m5/lcd-m5.c6
-rw-r--r--firmware/target/coldfire/iriver/h100/lcd-h100.c6
-rw-r--r--firmware/target/coldfire/mpio/hd300/lcd-hd300.c3
-rw-r--r--firmware/target/hosted/filesystem-app.c14
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.c2
-rw-r--r--firmware/target/hosted/ibasso/sysfs-ibasso.c2
-rw-r--r--firmware/target/hosted/rolo.c16
-rw-r--r--firmware/target/hosted/sdl/lcd-sdl.c6
-rw-r--r--firmware/target/hosted/sdl/sim-ui-defines.h8
-rw-r--r--firmware/target/hosted/sdl/thread-sdl.c2
-rw-r--r--firmware/target/hosted/system-hosted.c1
-rw-r--r--firmware/target/hosted/usb-hiby.c4
-rw-r--r--firmware/target/mips/exception-mips.S181
-rw-r--r--firmware/target/mips/ingenic_jz47xx/app.lds34
-rw-r--r--firmware/target/mips/ingenic_jz47xx/boot.lds6
-rw-r--r--firmware/target/mips/ingenic_jz47xx/crt0.S186
-rw-r--r--firmware/target/mips/ingenic_jz47xx/debug-jz4760.c8
-rw-r--r--firmware/target/mips/ingenic_jz47xx/system-jz4740.c76
-rw-r--r--firmware/target/mips/ingenic_jz47xx/system-jz4760.c48
-rw-r--r--firmware/target/mips/ingenic_jz47xx/system-target.h1
-rw-r--r--firmware/target/mips/ingenic_jz47xx/usb-jz4760.c251
-rw-r--r--firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/app.lds64
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.c285
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.h38
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c59
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.h13
-rw-r--r--firmware/target/mips/ingenic_x1000/crt0.S278
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c19
-rw-r--r--firmware/target/mips/ingenic_x1000/dma-x1000.h2
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c117
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c8
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h30
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h3
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c25
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c129
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.c10
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.h11
-rw-r--r--firmware/target/mips/ingenic_x1000/i2c-x1000.h2
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.c25
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.h1
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.c3
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.h8
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c289
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h128
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c198
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c4
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-nand-x1000.c (renamed from firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c)51
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-start.S2
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c227
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.h24
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds9
-rw-r--r--firmware/target/mips/ingenic_x1000/system-target.h3
-rw-r--r--firmware/target/mips/ingenic_x1000/system-x1000.c49
-rw-r--r--firmware/target/mips/mmu-mips.h6
-rw-r--r--firmware/target/mips/system-mips.c175
-rw-r--r--firmware/target/mips/system-mips.h40
-rw-r--r--firmware/usb.c69
-rw-r--r--firmware/usbstack/usb_core.c50
-rw-r--r--firmware/usbstack/usb_storage.c9
250 files changed, 9159 insertions, 7459 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 87db67d8fd..f6d35fb5ea 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -4,7 +4,11 @@
ata_idle_notify.c
events.c
backlight.c
-buflib.c
+#if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL
+buflib_mempool.c
+#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC
+buflib_malloc.c
+#endif
core_alloc.c
general.c
powermgmt.c
@@ -33,6 +37,7 @@ logf.c
#endif /* ROCKBOX_HAS_LOGF */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
load_code.c
+linuxboot.c
#ifdef RB_PROFILE
profile.c
#endif /* RB_PROFILE */
@@ -46,11 +51,14 @@ timer.c
debug.c
#endif /* PLATFORM_NATIVE */
panic.c
-
#if (CONFIG_PLATFORM & PLATFORM_HOSTED) && defined(BOOTFILE)
target/hosted/rolo.c
#endif
+#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
+common/multiboot.c
+#endif
+
#ifdef HAVE_SDL
target/hosted/sdl/button-sdl.c
target/hosted/sdl/kernel-sdl.c
@@ -201,39 +209,10 @@ target/hosted/samsungypr/ypr1/wmcodec-ypr1.c
target/hosted/maemo/maemo-thread.c
#endif
-/* Standard library */
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(__MINGW32__) || defined(__CYGWIN__)
-libc/strtok.c
-#endif /* PLATFORM_NATIVE || __MINGW32__ || __CYGWIN__ */
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(HAVE_ROCKBOX_C_LIBRARY)
-libc/atoi.c
-libc/errno.c
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
-/* our ctype.[ch] comes from newlib and is incompitble with most desktop's ctype */
-libc/ctype.c
-/* alsa on linux requires a more advanced sprintf, i.e. not ours */
-libc/sprintf.c
-#endif
-
-libc/memchr.c
-libc/memcmp.c
-
-libc/qsort.c
-libc/random.c
-libc/strcat.c
-libc/strchr.c
-libc/strcmp.c
-libc/strcpy.c
-
-libc/strncmp.c
-libc/strrchr.c
-libc/strstr.c
-libc/mktime.c
-libc/gmtime.c
-#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */
-
/* Common */
#ifndef BOOTLOADER
+chunk_alloc.c
+common/strptokspn.c
common/ap_int.c
#endif
common/version.c
@@ -259,12 +238,16 @@ common/dircache.c
common/pathfuncs.c
common/fdprintf.c
common/linked_list.c
+#if (!(CONFIG_PLATFORM & PLATFORM_HOSTED))
+common/rb_namespace.c
+#endif
+common/rectangle.c
common/strcasecmp.c
common/strcasestr.c
common/strnatcmp.c
common/strlcat.c
common/strlcpy.c
-common/structec.c
+common/strmemccpy.c
common/timefuncs.c
common/unicode.c
common/vuprintf.c
@@ -272,6 +255,35 @@ common/zip.c
common/adler32.c
common/inflate.c
+/* Standard library */
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(HAVE_ROCKBOX_C_LIBRARY)
+libc/atoi.c
+libc/errno.c
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+/* our ctype.[ch] comes from newlib and is incompitble with most desktop's ctype */
+libc/ctype.c
+/* alsa on linux requires a more advanced sprintf, i.e. not ours */
+libc/sprintf.c
+#endif
+
+libc/memccpy.c
+libc/memchr.c
+libc/memcmp.c
+
+libc/qsort.c
+libc/random.c
+libc/strcat.c
+libc/strchr.c
+libc/strcmp.c
+libc/strcpy.c
+libc/strcspn.c
+libc/strncmp.c
+libc/strrchr.c
+libc/strstr.c
+libc/mktime.c
+libc/gmtime.c
+#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */
+
/* Display */
scroll_engine.c
@@ -281,7 +293,7 @@ font_cache.c
font.c
hangul.c
lru.c
-#ifndef BOOTLOADER
+#ifdef HAVE_SCREENDUMP
screendump.c
#endif
#if LCD_DEPTH == 1
@@ -295,7 +307,7 @@ drivers/lcd-2bit-vert.c
drivers/lcd-2bit-vi.c
#endif /* LCD_PIXELFORMAT */
#elif LCD_DEPTH == 16
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
drivers/lcd-16bit-vert.c
#else
drivers/lcd-16bit.c
@@ -509,6 +521,7 @@ drivers/audio/es9018.c
drivers/audio/es9218.c
#elif defined (HAVE_EROS_QN_CODEC)
drivers/audio/eros_qn_codec.c
+drivers/audio/es9018k2m.c
#endif /* defined(HAVE_*) */
#else /* PLATFORM_HOSTED */
#if defined(SAMSUNG_YPR0) && defined(HAVE_AS3514)
@@ -774,10 +787,14 @@ target/arm/crt0.S
#endif /* defined(CPU_*) */
#elif defined(CPU_MIPS) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
+target/mips/exception-mips.S
target/mips/mmu-mips.c
+target/mips/system-mips.c
#if CONFIG_CPU==JZ4732 || CONFIG_CPU==JZ4760B
target/mips/ingenic_jz47xx/crt0.S
-#endif /* CONFIG_CPU == JZ4732 || JZ4760B */
+#elif CONFIG_CPU==X1000
+target/mips/ingenic_x1000/crt0.S
+#endif
#else
@@ -1639,8 +1656,8 @@ drivers/nand_id.c
#endif /* CONFIG_CPU == JZ4760B */
#if CONFIG_CPU == X1000
-target/mips/ingenic_x1000/crt0.S
target/mips/ingenic_x1000/aic-x1000.c
+target/mips/ingenic_x1000/boot-x1000.c
target/mips/ingenic_x1000/clk-x1000.c
target/mips/ingenic_x1000/debug-x1000.c
target/mips/ingenic_x1000/dma-x1000.c
@@ -1669,6 +1686,10 @@ target/mips/ingenic_x1000/spl-start.S
target/mips/ingenic_x1000/spl-x1000.c
common/ucl_decompress.c
#endif
+#if (defined(HAVE_X1000_ICODEC_PLAY) || defined(HAVE_X1000_ICODEC_REC)) \
+ && !defined(BOOTLOADER)
+drivers/audio/x1000-codec.c
+#endif
#endif /* CONFIG_CPU == X1000 */
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
@@ -1700,7 +1721,7 @@ target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
-target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* FIIO_M3K */
#if defined(SHANLING_Q1)
@@ -1709,7 +1730,7 @@ target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
-target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* SHANLING_Q1 */
#if defined(EROS_QN)
@@ -1718,7 +1739,7 @@ target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
-target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* EROS_QN */
#if defined(LYRE_PROTO1)
diff --git a/firmware/asm/arm/corelock.c b/firmware/asm/arm/corelock.c
index b36a40b45b..a60299436f 100644
--- a/firmware/asm/arm/corelock.c
+++ b/firmware/asm/arm/corelock.c
@@ -61,6 +61,7 @@ int corelock_try_lock(struct corelock *cl)
/* Relies on the fact that core IDs are complementary bitmasks (0x55,0xaa) */
asm volatile (
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"mov r1, %[id] \n" /* r1 = PROCESSOR_ID */
"ldrb r1, [r1] \n"
"strb r1, [%[cl], r1, lsr #7] \n" /* cl->myl[core] = core */
@@ -71,8 +72,9 @@ int corelock_try_lock(struct corelock *cl)
"bne 1f \n" /* yes? lock acquired */
"ldrb %[rv], [%[cl], #2] \n" /* || cl->turn == core? */
"ands %[rv], %[rv], r1 \n"
- "streqb %[rv], [%[cl], r1, lsr #7] \n" /* if not, cl->myl[core] = 0 */
+ "strbeq %[rv], [%[cl], r1, lsr #7] \n" /* if not, cl->myl[core] = 0 */
"1: \n" /* Done */
+ END_ARM_ASM_SYNTAX_UNIFIED
: [rv] "=r"(rval)
: [id] "i" (&PROCESSOR_ID), [cl] "r" (cl)
: "r1","r2","cc"
diff --git a/firmware/asm/arm/lcd-as-memframe.S b/firmware/asm/arm/lcd-as-memframe.S
index 52ab0447c2..d42b2a920d 100644
--- a/firmware/asm/arm/lcd-as-memframe.S
+++ b/firmware/asm/arm/lcd-as-memframe.S
@@ -91,9 +91,9 @@ lcd_copy_buffer_rect: @
stmia r0!, { r6-r12, r14 } @
bgt 30b @ octword loop @
40: @ finish line @
- ldreqh r6, [r1], #2 @ finish last halfword if eq ...
+ ldrheq r6, [r1], #2 @ finish last halfword if eq ...
add r1, r1, r4, lsl #1 @
- streqh r6, [r0], #2 @ ...
+ strheq r6, [r0], #2 @ ...
add r0, r0, r4, lsl #1 @
subs r3, r3, #1 @ next line
bgt 10b @ copy line @
diff --git a/firmware/asm/arm/memcpy.S b/firmware/asm/arm/memcpy.S
index 83d43293e6..86fc6b7930 100644
--- a/firmware/asm/arm/memcpy.S
+++ b/firmware/asm/arm/memcpy.S
@@ -99,22 +99,22 @@ memcpy:
7: ldmfd sp!, {r5 - r8}
8: movs r2, r2, lsl #31
- ldrneb r3, [r1], #1
- ldrcsb r4, [r1], #1
- ldrcsb ip, [r1]
- strneb r3, [r0], #1
- strcsb r4, [r0], #1
- strcsb ip, [r0]
+ ldrbne r3, [r1], #1
+ ldrbcs r4, [r1], #1
+ ldrbcs ip, [r1]
+ strbne r3, [r0], #1
+ strbcs r4, [r0], #1
+ strbcs ip, [r0]
ldmpc regs="r0, r4"
9: rsb ip, ip, #4
cmp ip, #2
- ldrgtb r3, [r1], #1
- ldrgeb r4, [r1], #1
+ ldrbgt r3, [r1], #1
+ ldrbge r4, [r1], #1
ldrb lr, [r1], #1
- strgtb r3, [r0], #1
- strgeb r4, [r0], #1
+ strbgt r3, [r0], #1
+ strbge r4, [r0], #1
subs r2, r2, ip
strb lr, [r0], #1
blt 8b
diff --git a/firmware/asm/arm/memmove.S b/firmware/asm/arm/memmove.S
index d8cab048be..e5c9b42928 100644
--- a/firmware/asm/arm/memmove.S
+++ b/firmware/asm/arm/memmove.S
@@ -106,20 +106,20 @@ memmove:
7: ldmfd sp!, {r5 - r8}
8: movs r2, r2, lsl #31
- ldrneb r3, [r1, #-1]!
- ldrcsb r4, [r1, #-1]!
- ldrcsb ip, [r1, #-1]
- strneb r3, [r0, #-1]!
- strcsb r4, [r0, #-1]!
- strcsb ip, [r0, #-1]
+ ldrbne r3, [r1, #-1]!
+ ldrbcs r4, [r1, #-1]!
+ ldrbcs ip, [r1, #-1]
+ strbne r3, [r0, #-1]!
+ strbcs r4, [r0, #-1]!
+ strbcs ip, [r0, #-1]
ldmpc regs="r0, r4"
9: cmp ip, #2
- ldrgtb r3, [r1, #-1]!
- ldrgeb r4, [r1, #-1]!
+ ldrbgt r3, [r1, #-1]!
+ ldrbge r4, [r1, #-1]!
ldrb lr, [r1, #-1]!
- strgtb r3, [r0, #-1]!
- strgeb r4, [r0, #-1]!
+ strbgt r3, [r0, #-1]!
+ strbge r4, [r0, #-1]!
subs r2, r2, ip
strb lr, [r0, #-1]!
blt 8b
diff --git a/firmware/asm/arm/memset.S b/firmware/asm/arm/memset.S
index 64cd95cc9e..d727f2a5ec 100644
--- a/firmware/asm/arm/memset.S
+++ b/firmware/asm/arm/memset.S
@@ -34,8 +34,8 @@
1: cmp r2, #4 @ 1 do we have enough
blt 5f @ 1 bytes to align with?
cmp r3, #2 @ 1
- strgtb r1, [r0, #-1]! @ 1
- strgeb r1, [r0, #-1]! @ 1
+ strbgt r1, [r0, #-1]! @ 1
+ strbge r1, [r0, #-1]! @ 1
strb r1, [r0, #-1]! @ 1
sub r2, r2, r3 @ 1 r2 = r2 - r3
b 2f
@@ -65,24 +65,24 @@ memset:
mov lr, r1
3: subs r2, r2, #64
- stmgedb r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
- stmgedb r0!, {r1, r3, ip, lr}
- stmgedb r0!, {r1, r3, ip, lr}
- stmgedb r0!, {r1, r3, ip, lr}
+ stmdbge r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
+ stmdbge r0!, {r1, r3, ip, lr}
+ stmdbge r0!, {r1, r3, ip, lr}
+ stmdbge r0!, {r1, r3, ip, lr}
bgt 3b
ldrpc cond=eq @ Now <64 bytes to go.
/*
* No need to correct the count; we're only testing bits from now on
*/
tst r2, #32
- stmnedb r0!, {r1, r3, ip, lr}
- stmnedb r0!, {r1, r3, ip, lr}
+ stmdbne r0!, {r1, r3, ip, lr}
+ stmdbne r0!, {r1, r3, ip, lr}
tst r2, #16
- stmnedb r0!, {r1, r3, ip, lr}
+ stmdbne r0!, {r1, r3, ip, lr}
ldr lr, [sp], #4
5: tst r2, #8
- stmnedb r0!, {r1, r3}
+ stmdbne r0!, {r1, r3}
tst r2, #4
strne r1, [r0, #-4]!
/*
@@ -90,10 +90,10 @@ memset:
* may have an unaligned pointer as well.
*/
6: tst r2, #2
- strneb r1, [r0, #-1]!
- strneb r1, [r0, #-1]!
+ strbne r1, [r0, #-1]!
+ strbne r1, [r0, #-1]!
tst r2, #1
- strneb r1, [r0, #-1]!
+ strbne r1, [r0, #-1]!
bx lr
.end:
.size memset,.end-memset
diff --git a/firmware/asm/arm/memset16.S b/firmware/asm/arm/memset16.S
index 5c787b1bed..226eac39e1 100644
--- a/firmware/asm/arm/memset16.S
+++ b/firmware/asm/arm/memset16.S
@@ -35,7 +35,7 @@
memset16:
tst r0, #2 @ unaligned?
cmpne r2, #0
- strneh r1, [r0], #2 @ store one halfword to align
+ strhne r1, [r0], #2 @ store one halfword to align
subne r2, r2, #1
/*
@@ -54,29 +54,29 @@ memset16:
mov lr, r1
2: subs r2, r2, #32
- stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
- stmgeia r0!, {r1, r3, ip, lr}
- stmgeia r0!, {r1, r3, ip, lr}
- stmgeia r0!, {r1, r3, ip, lr}
+ stmiage r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
+ stmiage r0!, {r1, r3, ip, lr}
+ stmiage r0!, {r1, r3, ip, lr}
+ stmiage r0!, {r1, r3, ip, lr}
bgt 2b
ldrpc cond=eq @ Now <64 bytes to go.
/*
* No need to correct the count; we're only testing bits from now on
*/
tst r2, #16
- stmneia r0!, {r1, r3, ip, lr}
- stmneia r0!, {r1, r3, ip, lr}
+ stmiane r0!, {r1, r3, ip, lr}
+ stmiane r0!, {r1, r3, ip, lr}
tst r2, #8
- stmneia r0!, {r1, r3, ip, lr}
+ stmiane r0!, {r1, r3, ip, lr}
ldr lr, [sp], #4
4: tst r2, #4
- stmneia r0!, {r1, r3}
+ stmiane r0!, {r1, r3}
tst r2, #2
strne r1, [r0], #4
tst r2, #1
- strneh r1, [r0], #2
+ strhne r1, [r0], #2
bx lr
.end:
.size memset16,.end-memset16
diff --git a/firmware/asm/arm/thread.c b/firmware/asm/arm/thread.c
index cf685526e3..30df56e0d9 100644
--- a/firmware/asm/arm/thread.c
+++ b/firmware/asm/arm/thread.c
@@ -73,18 +73,20 @@ static inline void store_context(void* addr)
static inline void load_context(const void* addr)
{
asm volatile(
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"ldr r0, [%0, #40] \n" /* Load start pointer */
"cmp r0, #0 \n" /* Check for NULL */
/* If not already running, jump to start */
#if ARM_ARCH == 4 && defined(USE_THUMB)
- "ldmneia %0, { r0, r12 } \n"
+ "ldmiane %0, { r0, r12 } \n"
"bxne r12 \n"
#else
- "ldmneia %0, { r0, pc } \n"
+ "ldmiane %0, { r0, pc } \n"
#endif
"ldmia %0, { r4-r11, sp, lr } \n" /* Load regs r4 to r14 from context */
+ END_ARM_ASM_SYNTAX_UNIFIED
: : "r" (addr) : "r0" /* only! */
);
}
diff --git a/firmware/asm/thread.h b/firmware/asm/thread.h
index 82edc81deb..5372be73ab 100644
--- a/firmware/asm/thread.h
+++ b/firmware/asm/thread.h
@@ -38,6 +38,16 @@ struct regs
#include <errno.h>
#ifdef HAVE_SIGALTSTACK_THREADS
#include <signal.h>
+ #ifdef _DYNAMIC_STACK_SIZE_SOURCE
+ /* glibc 2.34 made MINSIGSTKSZ non-constant. This is a problem for sim
+ * builds. Hosted targets are using ancient glibc where MINSIGSTKSZ is
+ * still a compile time constant. On platforms where this is a problem
+ * (mainly x86-64 and ARM64) the signal stack size can be big, so let's
+ * give a decent amount of space and hope for the best...
+ * FIXME: this isn't a great solution. */
+ #undef MINSIGSTKSZ
+ #define MINSIGSTKSZ 16384
+ #endif
/* MINSIGSTKSZ for the OS to deliver the signal + 0x3000 for us */
#define DEFAULT_STACK_SIZE (MINSIGSTKSZ+0x3000) /* Bytes */
#elif defined(HAVE_WIN32_FIBER_THREADS)
diff --git a/firmware/backlight.c b/firmware/backlight.c
index e8a71af12c..cc773e0a3b 100644
--- a/firmware/backlight.c
+++ b/firmware/backlight.c
@@ -21,6 +21,10 @@
*
****************************************************************************/
#include "config.h"
+#if !defined(BOOTLOADER)
+#include "settings.h"
+#include "action.h"
+#endif
#include <stdlib.h>
#include "cpu.h"
#include "kernel.h"
@@ -117,10 +121,8 @@ static int backlight_timeout_normal = 5*HZ;
#if CONFIG_CHARGING
static int backlight_timeout_plugged = 5*HZ;
#endif
-#ifdef HAS_BUTTON_HOLD
static int backlight_on_button_hold = 0;
-#endif
-static void backlight_timeout_handler(void);
+static void backlight_handle_timeout(void);
#ifdef HAVE_BUTTON_LIGHT
static int buttonlight_timer;
@@ -548,6 +550,17 @@ static void remote_backlight_update_state(void)
}
#endif /* HAVE_REMOTE_LCD */
+static void backlight_queue_wait(struct queue_event *ev)
+{
+#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
+ || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
+ if (backlight_fading_state != NOT_FADING)
+ queue_wait_w_tmo(&backlight_queue, ev, FADE_DELAY);
+ else
+#endif
+ queue_wait_w_tmo(&backlight_queue, ev, BACKLIGHT_THREAD_TIMEOUT);
+}
+
void backlight_thread(void)
{
struct queue_event ev;
@@ -555,13 +568,7 @@ void backlight_thread(void)
while(1)
{
-#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
- || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
- if (backlight_fading_state != NOT_FADING)
- queue_wait_w_tmo(&backlight_queue, &ev, FADE_DELAY);
- else
-#endif
- queue_wait_w_tmo(&backlight_queue, &ev, BACKLIGHT_THREAD_TIMEOUT);
+ backlight_queue_wait(&ev);
switch(ev.id)
{ /* These events must always be processed */
#ifdef _BACKLIGHT_FADE_BOOST
@@ -665,9 +672,14 @@ void backlight_thread(void)
#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#endif /* HAVE_BUTTON_LIGHT */
+ case SYS_REBOOT:
case SYS_POWEROFF: /* Lock backlight on poweroff so it doesn't */
locked = true; /* go off before power is actually cut. */
- /* fall through */
+#if !defined(BOOTLOADER)
+ if (!global_settings.show_shutdown_message)
+ break;
+#endif
+ /* else fall through */
#if CONFIG_CHARGING
case SYS_CHARGER_CONNECTED:
case SYS_CHARGER_DISCONNECTED:
@@ -678,24 +690,7 @@ void backlight_thread(void)
#endif
break;
case SYS_TIMEOUT:
-#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
- || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
- if (backlight_fading_state != NOT_FADING)
- {
- if ((_backlight_fade_step(backlight_fading_state)))
- { /* finished fading */
-#ifdef HAVE_LCD_SLEEP
- if (backlight_fading_state == FADING_DOWN)
- { /* start sleep countdown */
- backlight_lcd_sleep_countdown(true);
- }
-#endif
- backlight_fading_state = NOT_FADING;
- }
- }
- else
-#endif /* CONFIG_BACKLIGHT_FADING */
- backlight_timeout_handler();
+ backlight_handle_timeout();
break;
}
} /* end while */
@@ -755,6 +750,28 @@ static void backlight_timeout_handler(void)
}
}
+static void backlight_handle_timeout(void)
+{
+#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
+ || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
+ if (backlight_fading_state != NOT_FADING)
+ {
+ if ((_backlight_fade_step(backlight_fading_state)))
+ { /* finished fading */
+#ifdef HAVE_LCD_SLEEP
+ if (backlight_fading_state == FADING_DOWN)
+ { /* start sleep countdown */
+ backlight_lcd_sleep_countdown(true);
+ }
+#endif
+ backlight_fading_state = NOT_FADING;
+ }
+ }
+ else
+#endif /* CONFIG_BACKLIGHT_FADING */
+ backlight_timeout_handler();
+}
+
void backlight_init(void)
{
queue_init(&backlight_queue, true);
@@ -828,18 +845,18 @@ bool is_backlight_on(bool ignore_always_off)
/* return value in ticks; 0 means always on, <0 means always off */
int backlight_get_current_timeout(void)
{
-#ifdef HAS_BUTTON_HOLD
if ((backlight_on_button_hold != 0)
-#ifdef HAVE_REMOTE_LCD_AS_MAIN
+#if (defined(HAVE_REMOTE_LCD_AS_MAIN) && defined(HAS_REMOTE_BUTTON_HOLD))
&& remote_button_hold()
-#else
+#elif defined(HAS_BUTTON_HOLD)
&& button_hold()
+#else
+ && is_keys_locked()
#endif
)
return (backlight_on_button_hold == 2) ? 0 : -1;
/* always on or always off */
else
-#endif
#if CONFIG_CHARGING
if (power_input_present())
return backlight_timeout_plugged;
@@ -875,6 +892,7 @@ void backlight_hold_changed(bool hold_button)
queue_post(&backlight_queue, BACKLIGHT_ON, 0);
}
}
+#endif /* HAS_BUTTON_HOLD */
void backlight_set_on_button_hold(int index)
{
@@ -885,7 +903,6 @@ void backlight_set_on_button_hold(int index)
backlight_on_button_hold = index;
queue_post(&backlight_queue, BACKLIGHT_TMO_CHANGED, 0);
}
-#endif /* HAS_BUTTON_HOLD */
#ifdef HAVE_LCD_SLEEP_SETTING
void lcd_set_sleep_after_backlight_off(int timeout_seconds)
@@ -1047,3 +1064,14 @@ void buttonlight_set_brightness(int val) { (void)val; }
#endif /* HAVE_BUTTON_LIGHT */
#endif /* defined(HAVE_BACKLIGHT) && defined(BACKLIGHT_FULL_INIT) */
+
+#ifndef HAVE_BUTTON_LIGHT /* Dummy Functions */
+void buttonlight_on(void) {}
+void buttonlight_on_ignore(bool value, int timeout){(void)value;(void)timeout;}
+void buttonlight_off(void) {}
+void buttonlight_set_timeout(int value) {(void)value;}
+#endif /* ndef HAVE_BUTTON_LIGHT */
+
+#ifndef HAVE_BUTTONLIGHT_BRIGHTNESS /* Dummy Functions */
+void buttonlight_set_brightness(int val) { (void)val; }
+#endif /* ndef HAVE_BUTTONLIGHT_BRIGHTNESS */
diff --git a/firmware/buflib_malloc.c b/firmware/buflib_malloc.c
new file mode 100644
index 0000000000..0cd292f1e0
--- /dev/null
+++ b/firmware/buflib_malloc.c
@@ -0,0 +1,263 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*
+ * Malloc backed buflib. This is intended for debugging rather than for
+ * serious use - the buffer passed to the context is wasted, and memory
+ * is acquired from malloc() instead. The main point is to make ASAN more
+ * effective by isolating buflib allocations from each other.
+ *
+ * Currently this is a bare-minimum implementation, it doesn't even run
+ * buflib callbacks since it never moves anything. It could later be
+ * extended with stress-testing options, for example by randomly moving
+ * allocations around.
+ */
+
+#include "buflib.h"
+#include "panic.h"
+#include <stdlib.h>
+
+static struct buflib_malloc_handle *get_free_handle(struct buflib_context *ctx)
+{
+ struct buflib_malloc_handle *h;
+ for (size_t i = 0; i < ctx->num_allocs; ++i)
+ {
+ h = &ctx->allocs[i];
+ if (h->size == 0)
+ return h;
+ }
+
+ ctx->num_allocs++;
+ ctx->allocs = realloc(ctx->allocs, ctx->num_allocs * sizeof(*ctx->allocs));
+ if (!ctx->allocs)
+ panicf("buflib %p handle OOM", ctx);
+
+ h = &ctx->allocs[ctx->num_allocs - 1];
+ h->size = 0;
+ return h;
+}
+
+static int get_handle_num(struct buflib_context *ctx,
+ struct buflib_malloc_handle *handle)
+{
+ return (handle - ctx->allocs) + 1;
+}
+
+static struct buflib_malloc_handle *get_handle(struct buflib_context *ctx,
+ int handle)
+{
+ return &ctx->allocs[handle - 1];
+}
+
+struct buflib_callbacks buflib_ops_locked = {
+ .move_callback = NULL,
+ .shrink_callback = NULL,
+ .sync_callback = NULL,
+};
+
+void buflib_init(struct buflib_context *ctx, void *buf, size_t size)
+{
+ ctx->allocs = NULL;
+ ctx->num_allocs = 0;
+ ctx->buf = buf;
+ ctx->bufsize = size;
+}
+
+size_t buflib_available(struct buflib_context *ctx)
+{
+ return ctx->bufsize;
+}
+
+size_t buflib_allocatable(struct buflib_context *ctx)
+{
+ return ctx->bufsize;
+}
+
+bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
+{
+ ctx->buf = buf;
+ return true;
+}
+
+int buflib_alloc(struct buflib_context *ctx, size_t size)
+{
+ return buflib_alloc_ex(ctx, size, NULL);
+}
+
+int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
+ struct buflib_callbacks *ops)
+{
+ struct buflib_malloc_handle *handle = get_free_handle(ctx);
+
+ handle->data = malloc(size);
+ handle->user = handle->data;
+ handle->size = size;
+ handle->pin_count = 0;
+ handle->ops = ops;
+
+ if (!handle->data)
+ panicf("buflib %p data OOM", ctx);
+
+ return get_handle_num(ctx, handle);
+}
+
+int buflib_alloc_maximum(struct buflib_context* ctx,
+ size_t *size, struct buflib_callbacks *ops)
+{
+ *size = ctx->bufsize;
+
+ return buflib_alloc_ex(ctx, *size, ops);
+}
+
+bool buflib_shrink(struct buflib_context *ctx, int handle,
+ void *newstart, size_t new_size)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+ if (newstart < h->user || new_size > h->size - (newstart - h->user))
+ return false;
+
+ /* XXX: this might be allowed, but what would be the point... */
+ if (new_size == 0)
+ {
+ panicf("weird shrink");
+ return false;
+ }
+
+ /* due to buflib semantics we must not realloc */
+ h->user = newstart;
+ h->size = new_size;
+ return true;
+}
+
+void buflib_pin(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ h->pin_count++;
+}
+
+void buflib_unpin(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ h->pin_count--;
+}
+
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ return h->pin_count;
+}
+
+void _buflib_malloc_put_data_pinned(struct buflib_context *ctx, void *data)
+{
+ for (size_t i = 0; i < ctx->num_allocs; ++i)
+ {
+ if (ctx->allocs[i].user == data)
+ {
+ ctx->allocs[i].pin_count--;
+ break;
+ }
+ }
+}
+
+int buflib_free(struct buflib_context *ctx, int handle)
+{
+ if (handle <= 0)
+ return 0;
+
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ free(h->data);
+ h->size = 0;
+
+ return 0;
+}
+
+#ifdef BUFLIB_DEBUG_GET_DATA
+void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ /* kind of silly since it's better for ASAN to catch this but... */
+ if (handle <= 0 || handle > ctx->num_allocs)
+ panicf("buflib %p: invalid handle %d", ctx, handle);
+
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+ if (h->user == NULL)
+ panicf("buflib %p: handle %d use after free", ctx, handle);
+
+ return h->user;
+}
+#endif
+
+void *buflib_buffer_out(struct buflib_context *ctx, size_t *size)
+{
+ if (*size == 0)
+ *size = ctx->bufsize;
+
+ void *ret = ctx->buf;
+
+ ctx->buf += *size;
+ return ret;
+}
+
+void buflib_buffer_in(struct buflib_context *ctx, int size)
+{
+ ctx->buf -= size;
+}
+
+#ifdef BUFLIB_DEBUG_PRINT
+int buflib_get_num_blocks(struct buflib_context *ctx)
+{
+ return ctx->num_allocs;
+}
+
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize)
+{
+ if (block_num >= ctx->num_allocs)
+ {
+ if (bufsize > 0)
+ *buf = '\0';
+ return false;
+ }
+
+ struct buflib_malloc_handle *handle = &ctx->allocs[block_num];
+ if (handle->data)
+ {
+ snprintf(buf, bufsize, "%03d addr:%8p length:%zu",
+ block_num, handle->data, handle->size);
+ }
+ else
+ {
+ snprintf(buf, bufsize, "%03d (unallocated)", block_num);
+ }
+
+ return true;
+}
+#endif
+
+#ifdef BUFLIB_DEBUG_CHECK_VALID
+void buflib_check_valid(struct buflib_context *ctx)
+{
+ (void)ctx;
+}
+#endif
diff --git a/firmware/buflib.c b/firmware/buflib_mempool.c
index 0e90e7fe72..9d1c055bb9 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib_mempool.c
@@ -30,13 +30,14 @@
#include <stdio.h> /* for snprintf() */
#include <stddef.h> /* for ptrdiff_t */
#include "buflib.h"
-#include "string-extra.h" /* strlcpy() */
+#include "string-extra.h" /* strmemccpy() */
#include "debug.h"
#include "panic.h"
-#include "crc32.h"
#include "system.h" /* for ALIGN_*() */
-/* The main goal of this design is fast fetching of the pointer for a handle.
+/* FIXME: This comment is pretty out of date now and wrong in some details.
+ *
+ * The main goal of this design is fast fetching of the pointer for a handle.
* For that reason, the handles are stored in a table at the end of the buffer
* with a fixed address, so that returning the pointer for a handle is a simple
* table lookup. To reduce the frequency with which allocated blocks will need
@@ -65,7 +66,7 @@
* H - handle table entry pointer
* C - pointer to struct buflib_callbacks
* c - variable sized string identifier
- * L2 - second length marker for string identifier
+ * L2 - length of the metadata
* crc - crc32 protecting buflib metadata integrity
* X - actual payload
* Y - unallocated space
@@ -97,11 +98,47 @@
#define BPANICF panicf
-#define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback)
+/* Available paranoia checks */
+#define PARANOIA_CHECK_LENGTH (1 << 0)
+#define PARANOIA_CHECK_BLOCK_HANDLE (1 << 1)
+#define PARANOIA_CHECK_PINNING (1 << 2)
+/* Bitmask of enabled paranoia checks */
+#define BUFLIB_PARANOIA \
+ (PARANOIA_CHECK_LENGTH | \
+ PARANOIA_CHECK_BLOCK_HANDLE | PARANOIA_CHECK_PINNING)
+
+struct buflib_callbacks buflib_ops_locked = {
+ .move_callback = NULL,
+ .shrink_callback = NULL,
+ .sync_callback = NULL,
+};
+
+#define IS_MOVABLE(a) \
+ (!a[BUFLIB_IDX_OPS].ops || a[BUFLIB_IDX_OPS].ops->move_callback)
+
static union buflib_data* find_first_free(struct buflib_context *ctx);
static union buflib_data* find_block_before(struct buflib_context *ctx,
union buflib_data* block,
bool is_free);
+
+/* Check the length of a block to ensure it does not go beyond the end
+ * of the allocated area. The block can be either allocated or free.
+ *
+ * This verifies that it is safe to iterate to the next block in a loop.
+ */
+static void check_block_length(struct buflib_context *ctx,
+ union buflib_data *block);
+
+/* Check a block's handle pointer to ensure it is within the handle
+ * table, and that the user pointer is pointing within the block.
+ *
+ * This verifies that it is safe to dereference the entry and ensures
+ * that the pointer in the handle table points within the block, as
+ * determined by the length field at the start of the block.
+ */
+static void check_block_handle(struct buflib_context *ctx,
+ union buflib_data *block);
+
/* Initialize buffer manager */
void
buflib_init(struct buflib_context *ctx, void *buf, size_t size)
@@ -138,7 +175,7 @@ bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
/* cannot continue if the buffer is not aligned, since we would need
* to reduce the size of the buffer for aligning */
- if ((uintptr_t)buf & 0x3)
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(union buflib_data)))
return false;
/* relocate the handle table entries */
@@ -190,9 +227,22 @@ union buflib_data* handle_alloc(struct buflib_context *ctx)
if (handle >= ctx->alloc_end)
ctx->last_handle--;
else
+ {
+ /* We know the table is full, so update first_free_handle */
+ ctx->first_free_handle = ctx->last_handle - 1;
return NULL;
+ }
}
+
+ /* We know there are no free handles between the old first_free_handle
+ * and the found handle, therefore we can update first_free_handle */
+ ctx->first_free_handle = handle - 1;
+
+ /* We need to set the table entry to a non-NULL value to ensure that
+ * compactions triggered by an allocation do not compact the handle
+ * table and delete this handle. */
handle->val = -1;
+
return handle;
}
@@ -216,33 +266,35 @@ void handle_free(struct buflib_context *ctx, union buflib_data *handle)
static inline
union buflib_data* handle_to_block(struct buflib_context* ctx, int handle)
{
- union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data));
- /* this is a valid case, e.g. during buflib_alloc_ex() when the handle
- * has already been allocated but not the data */
- if (!data)
+ void *ptr = buflib_get_data(ctx, handle);
+
+ /* this is a valid case for shrinking if handle
+ * was freed by the shrink callback */
+ if (!ptr)
return NULL;
- volatile size_t len = data[-2].val;
- return data - (len + 4);
+
+ return _buflib_get_block_header(ptr);
}
/* Shrink the handle table, returning true if its size was reduced, false if
* not
*/
-static inline
-bool
-handle_table_shrink(struct buflib_context *ctx)
+static inline bool handle_table_shrink(struct buflib_context *ctx)
{
- bool rv;
union buflib_data *handle;
- for (handle = ctx->last_handle; !(handle->alloc); handle++);
+ union buflib_data *old_last = ctx->last_handle;
+
+ for (handle = ctx->last_handle; handle != ctx->handle_table; ++handle)
+ if (handle->alloc)
+ break;
+
if (handle > ctx->first_free_handle)
ctx->first_free_handle = handle - 1;
- rv = handle != ctx->last_handle;
+
ctx->last_handle = handle;
- return rv;
+ return handle != old_last;
}
-
/* If shift is non-zero, it represents the number of places to move
* blocks in memory. Calculate the new address for this block,
* update its entry in the handle table, and then move its contents.
@@ -254,32 +306,21 @@ static bool
move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
{
char* new_start;
+ union buflib_data *new_block;
- if (block < ctx->buf_start || block > ctx->alloc_end)
- buflib_panic(ctx, "buflib data corrupted %p", block);
-
- union buflib_data *new_block, *tmp = block[1].handle, *crc_slot;
- struct buflib_callbacks *ops = block[2].ops;
- crc_slot = (union buflib_data*)tmp->alloc - 1;
- if (crc_slot < ctx->buf_start || crc_slot > ctx->alloc_end)
- buflib_panic(ctx, "buflib metadata corrupted %p", crc_slot);
+ check_block_handle(ctx, block);
+ union buflib_data *h_entry = block[BUFLIB_IDX_HANDLE].handle;
- const int metadata_size = (crc_slot - block)*sizeof(union buflib_data);
- uint32_t crc = crc_32((void *)block, metadata_size, 0xffffffff);
-
- /* check for metadata validity */
- if (crc != crc_slot->crc)
- buflib_panic(ctx, "buflib metadata corrupted, crc: 0x%08x, expected: 0x%08x",
- (unsigned int)crc, (unsigned int)crc_slot->crc);
-
- if (!IS_MOVABLE(block))
+ if (!IS_MOVABLE(block) || block[BUFLIB_IDX_PIN].pincount > 0)
return false;
- int handle = ctx->handle_table - tmp;
- BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name,
+ int handle = ctx->handle_table - h_entry;
+ BDEBUGF("%s(): moving id=%d by %d(%d)\n", __func__,
handle, shift, shift*(int)sizeof(union buflib_data));
new_block = block + shift;
- new_start = tmp->alloc + shift*sizeof(union buflib_data);
+ new_start = h_entry->alloc + shift*sizeof(union buflib_data);
+
+ struct buflib_callbacks *ops = block[BUFLIB_IDX_OPS].ops;
/* If move must be synchronized with use, user should have specified a
callback that handles this */
@@ -287,10 +328,10 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
ops->sync_callback(handle, true);
bool retval = false;
- if (!ops || ops->move_callback(handle, tmp->alloc, new_start)
+ if (!ops || ops->move_callback(handle, h_entry->alloc, new_start)
!= BUFLIB_CB_CANNOT_MOVE)
{
- tmp->alloc = new_start; /* update handle table */
+ h_entry->alloc = new_start; /* update handle table */
memmove(new_block, block, block->val * sizeof(union buflib_data));
retval = true;
}
@@ -327,6 +368,8 @@ buflib_compact(struct buflib_context *ctx)
* For simplicity only 1 hole at a time is considered */
for(block = find_first_free(ctx); block < ctx->alloc_end; block += len)
{
+ check_block_length(ctx, block);
+
bool movable = true; /* cache result to avoid 2nd call to move_block */
len = block->val;
/* This block is free, add its length to the shift value */
@@ -403,41 +446,52 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
this < ctx->alloc_end;
before = this, this += abs(this->val))
{
- if (this->val > 0 && this[2].ops
- && this[2].ops->shrink_callback)
+ check_block_length(ctx, this);
+ if (this->val < 0)
+ continue;
+
+ struct buflib_callbacks *ops = this[BUFLIB_IDX_OPS].ops;
+ if (!ops || !ops->shrink_callback)
+ continue;
+
+ check_block_handle(ctx, this);
+ union buflib_data* h_entry = this[BUFLIB_IDX_HANDLE].handle;
+ int handle = ctx->handle_table - h_entry;
+
+ unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
+ /* adjust what we ask for if there's free space in the front
+ * this isn't too unlikely assuming this block is
+ * shrinkable but not movable */
+ if (pos_hints == BUFLIB_SHRINK_POS_FRONT &&
+ before != this && before->val < 0)
{
- int ret;
- int handle = ctx->handle_table - this[1].handle;
- char* data = this[1].handle->alloc;
- bool last = (this+this->val) == ctx->alloc_end;
- unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
- /* adjust what we ask for if there's free space in the front
- * this isn't too unlikely assuming this block is
- * shrinkable but not movable */
- if (pos_hints == BUFLIB_SHRINK_POS_FRONT
- && before != this && before->val < 0)
- {
- size_t free_space = (-before->val) * sizeof(union buflib_data);
- size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
- if (wanted < free_space) /* no shrink needed? */
- continue;
- wanted -= free_space;
- shrink_hints = pos_hints | wanted;
- }
- ret = this[2].ops->shrink_callback(handle, shrink_hints,
- data, (char*)(this+this->val)-data);
- result |= (ret == BUFLIB_CB_OK);
- /* 'this' might have changed in the callback (if it shrinked
- * from the top or even freed the handle), get it again */
- this = handle_to_block(ctx, handle);
- /* The handle was possibly be freed in the callback,
- * re-run the loop with the handle before */
- if (!this)
- this = before;
- /* could also change with shrinking from back */
- else if (last)
- ctx->alloc_end = this + this->val;
+ size_t free_space = (-before->val) * sizeof(union buflib_data);
+ size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
+ if (wanted < free_space) /* no shrink needed? */
+ continue;
+ wanted -= free_space;
+ shrink_hints = pos_hints | wanted;
}
+
+ char* data = h_entry->alloc;
+ char* data_end = (char*)(this + this->val);
+ bool last = (data_end == (char*)ctx->alloc_end);
+
+ int ret = ops->shrink_callback(handle, shrink_hints,
+ data, data_end - data);
+ result |= (ret == BUFLIB_CB_OK);
+
+ /* 'this' might have changed in the callback (if it shrinked
+ * from the top or even freed the handle), get it again */
+ this = handle_to_block(ctx, handle);
+
+ /* The handle was possibly be freed in the callback,
+ * re-run the loop with the handle before */
+ if (!this)
+ this = before;
+ /* could also change with shrinking from back */
+ else if (last)
+ ctx->alloc_end = this + this->val;
}
/* shrinking was successful at least once, try compaction again */
if (result)
@@ -500,13 +554,12 @@ buflib_buffer_in(struct buflib_context *ctx, int size)
int
buflib_alloc(struct buflib_context *ctx, size_t size)
{
- return buflib_alloc_ex(ctx, size, NULL, NULL);
+ return buflib_alloc_ex(ctx, size, NULL);
}
/* Allocate a buffer of size bytes, returning a handle for it.
*
- * The additional name parameter gives the allocation a human-readable name,
- * the ops parameter points to caller-implemented callbacks for moving and
+ * The ops parameter points to caller-implemented callbacks for moving and
* shrinking.
*
* If you pass NULL for "ops", buffers are movable by default.
@@ -515,20 +568,16 @@ buflib_alloc(struct buflib_context *ctx, size_t size)
*/
int
-buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
+buflib_alloc_ex(struct buflib_context *ctx, size_t size,
struct buflib_callbacks *ops)
{
union buflib_data *handle, *block;
- size_t name_len = name ? B_ALIGN_UP(strlen(name)+1) : 0;
bool last;
/* This really is assigned a value before use */
int block_len;
- size += name_len;
size = (size + sizeof(union buflib_data) - 1) /
sizeof(union buflib_data)
- /* add 5 objects for alloc len, pointer to handle table entry and
- * name length, the ops pointer and crc */
- + 5;
+ + BUFLIB_NUM_FIELDS;
handle_alloc:
handle = handle_alloc(ctx);
if (!handle)
@@ -538,7 +587,7 @@ handle_alloc:
*/
union buflib_data* last_block = find_block_before(ctx,
ctx->alloc_end, false);
- struct buflib_callbacks* ops = last_block[2].ops;
+ struct buflib_callbacks* ops = last_block[BUFLIB_IDX_OPS].ops;
unsigned hints = 0;
if (!ops || !ops->shrink_callback)
{ /* the last one isn't shrinkable
@@ -562,7 +611,7 @@ buffer_alloc:
/* need to re-evaluate last before the loop because the last allocation
* possibly made room in its front to fit this, so last would be wrong */
last = false;
- for (block = find_first_free(ctx);;block += block_len)
+ for (block = find_first_free(ctx);; block += block_len)
{
/* If the last used block extends all the way to the handle table, the
* block "after" it doesn't have a header. Because of this, it's easier
@@ -578,6 +627,8 @@ buffer_alloc:
block = NULL;
break;
}
+
+ check_block_length(ctx, block);
block_len = block->val;
/* blocks with positive length are already allocated. */
if(block_len > 0)
@@ -598,7 +649,6 @@ buffer_alloc:
{
goto buffer_alloc;
} else {
- handle->val=1;
handle_free(ctx, handle);
return -2;
}
@@ -607,23 +657,15 @@ buffer_alloc:
/* Set up the allocated block, by marking the size allocated, and storing
* a pointer to the handle.
*/
- union buflib_data *name_len_slot, *crc_slot;
- block->val = size;
- block[1].handle = handle;
- block[2].ops = ops;
- if (name_len > 0)
- strcpy(block[3].name, name);
- name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len);
- name_len_slot->val = 1 + name_len/sizeof(union buflib_data);
- crc_slot = (union buflib_data*)(name_len_slot + 1);
- crc_slot->crc = crc_32((void *)block,
- (crc_slot - block)*sizeof(union buflib_data),
- 0xffffffff);
- handle->alloc = (char*)(crc_slot + 1);
-
- BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n",
- (unsigned int)size, (void *)handle, (void *)ops,
- (unsigned int)crc_slot->crc, name ? block[3].name:"");
+ block[BUFLIB_IDX_LEN].val = size;
+ block[BUFLIB_IDX_HANDLE].handle = handle;
+ block[BUFLIB_IDX_OPS].ops = ops;
+ block[BUFLIB_IDX_PIN].pincount = 0;
+
+ handle->alloc = (char*)&block[BUFLIB_NUM_FIELDS];
+
+ BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p\n",
+ (unsigned int)size, (void *)handle, (void *)ops);
block += size;
/* alloc_end must be kept current if we're taking the last block. */
@@ -639,13 +681,14 @@ buffer_alloc:
static union buflib_data*
find_first_free(struct buflib_context *ctx)
{
- union buflib_data* ret = ctx->buf_start;
- while(ret < ctx->alloc_end)
+ union buflib_data *ret;
+ for(ret = ctx->buf_start; ret < ctx->alloc_end; ret += ret->val)
{
+ check_block_length(ctx, ret);
if (ret->val < 0)
break;
- ret += ret->val;
}
+
/* ret is now either a free block or the same as alloc_end, both is fine */
return ret;
}
@@ -658,29 +701,31 @@ find_block_before(struct buflib_context *ctx, union buflib_data* block,
union buflib_data *ret = ctx->buf_start,
*next_block = ret;
+ /* no previous block */
+ if (next_block == block)
+ return NULL;
+
/* find the block that's before the current one */
- while (next_block < block)
+ while (next_block != block)
{
+ check_block_length(ctx, ret);
ret = next_block;
next_block += abs(ret->val);
}
- /* If next_block == block, the above loop didn't go anywhere. If it did,
- * and the block before this one is empty, that is the wanted one
- */
- if (next_block == block && ret < block)
- {
- if (is_free && ret->val >= 0) /* NULL if found block isn't free */
- return NULL;
- return ret;
- }
- return NULL;
+ /* don't return it if the found block isn't free */
+ if (is_free && ret->val >= 0)
+ return NULL;
+
+ return ret;
}
/* Free the buffer associated with handle_num. */
int
buflib_free(struct buflib_context *ctx, int handle_num)
{
+ if (handle_num <= 0) /* invalid or already free */
+ return handle_num;
union buflib_data *handle = ctx->handle_table - handle_num,
*freed_block = handle_to_block(ctx, handle_num),
*block, *next_block;
@@ -694,13 +739,13 @@ buflib_free(struct buflib_context *ctx, int handle_num)
}
else
{
- /* Otherwise, set block to the newly-freed block, and mark it free, before
- * continuing on, since the code below expects block to point to a free
- * block which may have free space after it.
- */
+ /* Otherwise, set block to the newly-freed block, and mark it free,
+ * before continuing on, since the code below expects block to point
+ * to a free block which may have free space after it. */
block = freed_block;
block->val = -block->val;
}
+
next_block = block - block->val;
/* Check if we are merging with the free space at alloc_end. */
if (next_block == ctx->alloc_end)
@@ -723,11 +768,10 @@ static size_t
free_space_at_end(struct buflib_context* ctx)
{
/* subtract 5 elements for
- * val, handle, name_len, ops and the handle table entry*/
- ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5);
+ * val, handle, meta_len, ops and the handle table entry*/
+ ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - BUFLIB_NUM_FIELDS);
diff -= 16; /* space for future handles */
diff *= sizeof(union buflib_data); /* make it bytes */
- diff -= 16; /* reserve 16 for the name */
if (diff > 0)
return diff;
@@ -739,24 +783,31 @@ free_space_at_end(struct buflib_context* ctx)
size_t
buflib_allocatable(struct buflib_context* ctx)
{
- union buflib_data *this;
size_t free_space = 0, max_free_space = 0;
+ intptr_t block_len;
/* make sure buffer is as contiguous as possible */
if (!ctx->compact)
buflib_compact(ctx);
/* now look if there's free in holes */
- for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val))
+ for(union buflib_data *block = find_first_free(ctx);
+ block < ctx->alloc_end;
+ block += block_len)
{
- if (this->val < 0)
+ check_block_length(ctx, block);
+ block_len = block->val;
+
+ if (block_len < 0)
{
- free_space += -this->val;
+ block_len = -block_len;
+ free_space += block_len;
continue;
}
+
/* an unmovable section resets the count as free space
* can't be contigous */
- if (!IS_MOVABLE(this))
+ if (!IS_MOVABLE(block))
{
if (max_free_space < free_space)
max_free_space = free_space;
@@ -779,17 +830,16 @@ buflib_allocatable(struct buflib_context* ctx)
size_t
buflib_available(struct buflib_context* ctx)
{
- union buflib_data *this;
size_t free_space = 0;
- /* now look if there's free in holes */
- for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val))
+ /* add up all holes */
+ for(union buflib_data *block = find_first_free(ctx);
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- if (this->val < 0)
- {
- free_space += -this->val;
- continue;
- }
+ check_block_length(ctx, block);
+ if (block->val < 0)
+ free_space += -block->val;
}
free_space *= sizeof(union buflib_data); /* make it bytes */
@@ -807,11 +857,8 @@ buflib_available(struct buflib_context* ctx)
* serviced anyway).
*/
int
-buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size, struct buflib_callbacks *ops)
+buflib_alloc_maximum(struct buflib_context* ctx, size_t *size, struct buflib_callbacks *ops)
{
- /* limit name to 16 since that's what buflib_available() accounts for it */
- char buf[16];
-
/* ignore ctx->compact because it's true if all movable blocks are contiguous
* even if the buffer has holes due to unmovable allocations */
unsigned hints;
@@ -827,9 +874,7 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size,
if (*size <= 0) /* OOM */
return -1;
- strlcpy(buf, name, sizeof(buf));
-
- return buflib_alloc_ex(ctx, *size, buf, ops);
+ return buflib_alloc_ex(ctx, *size, ops);
}
/* Shrink the allocation indicated by the handle according to new_start and
@@ -839,10 +884,8 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size,
bool
buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size)
{
- union buflib_data *crc_slot;
- int size_for_crc32;
char* oldstart = buflib_get_data(ctx, handle);
- char* newstart = new_start;
+ char* newstart = new_start != NULL ? new_start : oldstart;
char* newend = newstart + new_size;
/* newstart must be higher and new_size not "negative" */
@@ -864,9 +907,10 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
metadata_size.val = aligned_oldstart - block;
/* update val and the handle table entry */
new_block = aligned_newstart - metadata_size.val;
- block[0].val = new_next_block - new_block;
+ block[BUFLIB_IDX_LEN].val = new_next_block - new_block;
- block[1].handle->alloc = newstart;
+ check_block_handle(ctx, block);
+ block[BUFLIB_IDX_HANDLE].handle->alloc = newstart;
if (block != new_block)
{
/* move metadata over, i.e. pointer to handle table entry and name
@@ -886,11 +930,6 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
block = new_block;
}
- /* update crc of the metadata */
- crc_slot = (union buflib_data*)new_block[1].handle->alloc - 1;
- size_for_crc32 = (crc_slot - new_block)*sizeof(union buflib_data);
- crc_slot->crc = crc_32((void *)new_block, size_for_crc32, 0xffffffff);
-
/* Now deal with size changes that create free blocks after the allocation */
if (old_next_block != new_next_block)
{
@@ -910,17 +949,40 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
return true;
}
-const char* buflib_get_name(struct buflib_context *ctx, int handle)
+void buflib_pin(struct buflib_context *ctx, int handle)
{
- union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data));
- size_t len = data[-2].val;
- if (len <= 1)
- return NULL;
- return data[-len-1].name;
+ if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
+ buflib_panic(ctx, "invalid handle pin: %d", handle);
+
+ union buflib_data *data = handle_to_block(ctx, handle);
+ data[BUFLIB_IDX_PIN].pincount++;
}
-#ifdef DEBUG
+void buflib_unpin(struct buflib_context *ctx, int handle)
+{
+ if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
+ buflib_panic(ctx, "invalid handle unpin: %d", handle);
+ union buflib_data *data = handle_to_block(ctx, handle);
+ if (BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING)
+ {
+ if (data[BUFLIB_IDX_PIN].pincount == 0)
+ buflib_panic(ctx, "handle pin underflow: %d", handle);
+ }
+
+ data[BUFLIB_IDX_PIN].pincount--;
+}
+
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
+{
+ if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
+ buflib_panic(ctx, "invalid handle: %d", handle);
+
+ union buflib_data *data = handle_to_block(ctx, handle);
+ return data[BUFLIB_IDX_PIN].pincount;
+}
+
+#ifdef BUFLIB_DEBUG_GET_DATA
void *buflib_get_data(struct buflib_context *ctx, int handle)
{
if (handle <= 0)
@@ -928,105 +990,115 @@ void *buflib_get_data(struct buflib_context *ctx, int handle)
return (void*)(ctx->handle_table[-handle].alloc);
}
+#endif
+#ifdef BUFLIB_DEBUG_CHECK_VALID
void buflib_check_valid(struct buflib_context *ctx)
{
- union buflib_data *crc_slot;
- int metadata_size;
- uint32_t crc;
-
- for(union buflib_data* this = ctx->buf_start;
- this < ctx->alloc_end;
- this += abs(this->val))
+ for(union buflib_data *block = ctx->buf_start;
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- if (this->val < 0)
+ check_block_length(ctx, block);
+ if (block->val < 0)
continue;
- crc_slot = (union buflib_data*)
- ((union buflib_data*)this[1].handle)->alloc - 1;
- metadata_size = (crc_slot - this)*sizeof(union buflib_data);
- crc = crc_32((void *)this, metadata_size, 0xffffffff);
-
- if (crc != crc_slot->crc)
- buflib_panic(ctx, "crc mismatch: 0x%08x, expected: 0x%08x",
- (unsigned int)crc, (unsigned int)crc_slot->crc);
+ check_block_handle(ctx, block);
}
}
#endif
-#ifdef BUFLIB_DEBUG_BLOCKS
-void buflib_print_allocs(struct buflib_context *ctx,
- void (*print)(int, const char*))
+#ifdef BUFLIB_DEBUG_PRINT
+int buflib_get_num_blocks(struct buflib_context *ctx)
{
- union buflib_data *this, *end = ctx->handle_table;
- char buf[128];
- for(this = end - 1; this >= ctx->last_handle; this--)
+ int i = 0;
+
+ for(union buflib_data *block = ctx->buf_start;
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- if (!this->alloc) continue;
-
- int handle_num;
- const char *name;
- union buflib_data *block_start, *alloc_start;
- intptr_t alloc_len;
-
- handle_num = end - this;
- alloc_start = buflib_get_data(ctx, handle_num);
- name = buflib_get_name(ctx, handle_num);
- block_start = (union buflib_data*)name - 3;
- alloc_len = block_start->val * sizeof(union buflib_data);
-
- snprintf(buf, sizeof(buf),
- "%s(%d):\t%p\n"
- " \t%p\n"
- " \t%ld\n",
- name?:"(null)", handle_num, block_start, alloc_start, alloc_len);
- /* handle_num is 1-based */
- print(handle_num - 1, buf);
+ check_block_length(ctx, block);
+ ++i;
}
+
+ return i;
}
-void buflib_print_blocks(struct buflib_context *ctx,
- void (*print)(int, const char*))
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize)
{
- char buf[128];
- int i = 0;
- for(union buflib_data* this = ctx->buf_start;
- this < ctx->alloc_end;
- this += abs(this->val))
+ for(union buflib_data *block = ctx->buf_start;
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)",
- this, this->val,
- this->val > 0? this[3].name:"<unallocated>");
- print(i++, buf);
+ check_block_length(ctx, block);
+
+ if (block_num-- == 0)
+ {
+ snprintf(buf, bufsize, "%8p: val: %4ld (%sallocated)",
+ block, (long)block->val,
+ block->val > 0 ? "" : "un");
+ return true;
+ }
}
+
+ if (bufsize > 0)
+ *buf = '\0';
+ return false;
}
#endif
-#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
-int buflib_get_num_blocks(struct buflib_context *ctx)
+static void check_block_length(struct buflib_context *ctx,
+ union buflib_data *block)
{
- int i = 0;
- for(union buflib_data* this = ctx->buf_start;
- this < ctx->alloc_end;
- this += abs(this->val))
+ if (BUFLIB_PARANOIA & PARANOIA_CHECK_LENGTH)
{
- i++;
+ intptr_t length = block[BUFLIB_IDX_LEN].val;
+
+ /* Check the block length does not pass beyond the end */
+ if (length == 0 || block > ctx->alloc_end - abs(length))
+ {
+ buflib_panic(ctx, "block len wacky [%p]=%ld",
+ (void*)&block[BUFLIB_IDX_LEN], (long)length);
+ }
}
- return i;
}
-void buflib_print_block_at(struct buflib_context *ctx, int block_num,
- char* buf, size_t bufsize)
+static void check_block_handle(struct buflib_context *ctx,
+ union buflib_data *block)
{
- union buflib_data* this = ctx->buf_start;
- while(block_num > 0 && this < ctx->alloc_end)
+ if (BUFLIB_PARANOIA & PARANOIA_CHECK_BLOCK_HANDLE)
{
- this += abs(this->val);
- block_num -= 1;
+ intptr_t length = block[BUFLIB_IDX_LEN].val;
+ union buflib_data *h_entry = block[BUFLIB_IDX_HANDLE].handle;
+
+ /* Check the handle pointer is properly aligned */
+ /* TODO: Can we ensure the compiler doesn't optimize this out?
+ * I dunno, maybe the compiler can assume the pointer is always
+ * properly aligned due to some C standard voodoo?? */
+ if (!IS_ALIGNED((uintptr_t)h_entry, alignof(*h_entry)))
+ {
+ buflib_panic(ctx, "handle unaligned [%p]=%p",
+ &block[BUFLIB_IDX_HANDLE], h_entry);
+ }
+
+ /* Check the pointer is actually inside the handle table */
+ if (h_entry < ctx->last_handle || h_entry >= ctx->handle_table)
+ {
+ buflib_panic(ctx, "handle out of bounds [%p]=%p",
+ &block[BUFLIB_IDX_HANDLE], h_entry);
+ }
+
+ /* Now check the allocation is within the block.
+ * This is stricter than check_handle(). */
+ void *alloc = h_entry->alloc;
+ void *alloc_begin = block;
+ void *alloc_end = block + length;
+ /* buflib allows zero length allocations, so alloc_end is inclusive */
+ if (alloc < alloc_begin || alloc > alloc_end)
+ {
+ buflib_panic(ctx, "alloc outside block [%p]=%p, %p-%p",
+ h_entry, alloc, alloc_begin, alloc_end);
+ }
}
- snprintf(buf, bufsize, "%8p: val: %4ld (%s)",
- this, (long)this->val,
- this->val > 0? this[3].name:"<unallocated>");
}
-
-#endif
diff --git a/firmware/chunk_alloc.c b/firmware/chunk_alloc.c
new file mode 100644
index 0000000000..02cc7a056c
--- /dev/null
+++ b/firmware/chunk_alloc.c
@@ -0,0 +1,319 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 by William Wilgus
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/* [chunk_alloc] allows arrays (or any data) to be allocated in smaller chunks */
+
+#include "chunk_alloc.h"
+#include "panic.h"
+
+//#define LOGF_ENABLE
+#include "logf.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*Note on offsets returned
+ * variable data will have variable offsets you need to
+ * store these as [chunk_alloc] doesn't keep track
+ * if you have a fixed size for each alloc you can do
+ * offset / sizeof(data) or index * sizeof(data) to convert
+ */
+
+struct chunk_alloc
+{
+ int handle; /* data handle of buflib allocated bytes */
+ size_t max_start_offset; /* start of last allocation */
+};
+
+#define CHUNK_ARRSZ(n) (sizeof(struct chunk_alloc) * n)
+
+static struct chunk_alloc* get_chunk_array(struct buflib_context *ctx, int handle)
+{
+ return (struct chunk_alloc*)buflib_get_data_pinned(ctx, handle);
+}
+
+static void put_chunk_array(struct buflib_context *ctx, struct chunk_alloc *data)
+{
+ buflib_put_data_pinned(ctx, data);
+}
+
+/* shrink or grow chunk allocation
+ * chunks greater than max_chunks will be freed
+ * new allocs will default to chunk_size
+ * previous or current chunk < max_chunks will NOT be changed
+ * Returns true on success false on failure
+*/
+bool chunk_realloc(struct chunk_alloc_header *hdr,
+ size_t chunk_size, size_t max_chunks)
+{
+ struct buflib_context *ctx = hdr->context;
+ struct chunk_alloc *new_chunk = NULL;
+ struct chunk_alloc *old_chunk;
+ size_t min_chunk = 1;
+ int new_handle = 0;
+
+ if (max_chunks > hdr->count) /* need room for more chunks */
+ {
+ new_handle = buflib_alloc(ctx, CHUNK_ARRSZ(max_chunks));
+
+ if (new_handle <= 0)
+ {
+ logf("%s Error OOM %ld chunks", __func__, max_chunks);
+ return false;
+ }
+ new_chunk = get_chunk_array(ctx, new_handle);
+ /* ensure all chunks data is zeroed, we depend on it */
+ memset(new_chunk, 0, CHUNK_ARRSZ(max_chunks));
+ put_chunk_array(ctx, new_chunk);
+ }
+ if (hdr->chunk_handle > 0) /* handle existing chunk */
+ {
+ logf("%s %ld chunks => %ld chunks", __func__, hdr->count, max_chunks);
+
+ old_chunk = get_chunk_array(ctx, hdr->chunk_handle);
+
+ if (new_handle > 0) /* copy any valid old chunks to new */
+ {
+ min_chunk = MIN(max_chunks, hdr->current + 1);
+ logf("%s copying %ld chunks", __func__, min_chunk);
+ new_chunk = get_chunk_array(ctx, new_handle);
+ memcpy(new_chunk, old_chunk, CHUNK_ARRSZ(min_chunk));
+ put_chunk_array(ctx, new_chunk);
+ }
+ /* free any chunks that no longer fit */
+ for (size_t i = max_chunks; i <= hdr->current; i++)
+ {
+ logf("%s discarding chunk[%ld]", __func__, i);
+ buflib_free(ctx, old_chunk[i].handle);
+ }
+ put_chunk_array(ctx, old_chunk);
+
+ if (max_chunks < hdr->count && max_chunks > 0)
+ {
+ logf("%s shrink existing chunk array", __func__);
+ min_chunk = max_chunks;
+ buflib_shrink(ctx, hdr->chunk_handle,
+ NULL, CHUNK_ARRSZ(max_chunks));
+
+ new_handle = hdr->chunk_handle;
+ }
+ else
+ {
+ logf("%s free existing chunk array", __func__);
+ buflib_free(ctx, hdr->chunk_handle); /* free old chunk array */
+ }
+
+ hdr->current = min_chunk - 1;
+ }
+ else
+ {
+ logf("chunk_alloc_init %ld chunks", hdr->count);
+ }
+ hdr->cached_chunk.handle = 0; /* reset last chunk to force new lookup */
+ hdr->chunk_handle = new_handle;
+ hdr->chunk_size = chunk_size;
+ hdr->count = max_chunks;
+
+ return true;
+}
+
+/* frees all allocations */
+void chunk_alloc_free(struct chunk_alloc_header *hdr)
+{
+ logf("%s freeing %ld chunks", __func__, hdr->count);
+ chunk_realloc(hdr, 0, 0);
+}
+
+/* initialize chunk allocator
+ * chunk_size specifies initial size of each chunk
+ * a single allocation CAN be larger than this
+ * max_chunks * chunk_size is the total expected size of the buffer
+ * more data will not be allocated once all chunks used
+ * Returns true on success or false on failure
+*/
+bool chunk_alloc_init(struct chunk_alloc_header *hdr,
+ struct buflib_context *ctx,
+ size_t chunk_size, size_t max_chunks)
+{
+ /* initialize header */
+ memset(hdr, 0, sizeof(struct chunk_alloc_header));
+ hdr->context = ctx;
+
+ return chunk_realloc(hdr, chunk_size, max_chunks);
+}
+
+/* shrink current chunk to size used */
+static void finalize(struct chunk_alloc_header *hdr, struct chunk_alloc *chunk_array)
+{
+ /* Note calling functions check if chunk_bytes_free > 0 */
+ size_t idx = hdr->current;
+ if (idx >= hdr->count)
+ return;
+ int handle = chunk_array[idx].handle;
+ struct buflib_context *ctx = hdr->context;
+
+ hdr->chunk_bytes_total -= hdr->chunk_bytes_free;
+ hdr->chunk_bytes_free = 0;
+
+ buflib_shrink(ctx, handle, NULL, hdr->chunk_bytes_total);
+
+ logf("%s shrink hdr idx[%ld] offset[%ld]: new size: %ld",
+ __func__, idx, chunk_array[idx].max_start_offset, hdr->chunk_bytes_total);
+}
+
+/* shrink current chunk to size used */
+void chunk_alloc_finalize(struct chunk_alloc_header *hdr)
+{
+ if (hdr->chunk_bytes_free > 0)
+ {
+ struct chunk_alloc *chunk;
+ chunk = get_chunk_array(hdr->context, hdr->chunk_handle);
+ finalize(hdr, chunk);
+ put_chunk_array(hdr->context, chunk);
+ }
+}
+
+/* allocates from current chunk if size > bytes remaining
+ * current chunk shrinks to size used and a new chunk is allocated
+ * returns virtual offset on success or CHUNK_ALLOC_INVALID on error
+*/
+size_t chunk_alloc(struct chunk_alloc_header *hdr, size_t size)
+{
+ size_t virtual_offset = CHUNK_ALLOC_INVALID;
+ size_t idx = hdr->current;
+ int handle = hdr->chunk_handle;
+ struct buflib_context *ctx = hdr->context;
+
+ struct chunk_alloc *chunk = get_chunk_array(ctx, handle);
+
+ while (size > 0)
+ {
+ if (idx >= hdr->count)
+ {
+ logf("%s Error OOM -- out of chunks", __func__);
+ break; /* Out of chunks */
+ }
+ hdr->current = idx;
+
+ if(chunk[idx].handle <= 0) /* need to make an new allocation */
+ {
+ size_t new_alloc_size = MAX(size, hdr->chunk_size);
+
+ chunk[idx].handle = buflib_alloc(ctx, new_alloc_size);
+
+ if (chunk[idx].handle <= 0)
+ {
+ logf("%s Error OOM", __func__);
+ break; /* OOM */
+ }
+
+ hdr->chunk_bytes_total = new_alloc_size;
+ hdr->chunk_bytes_free = new_alloc_size;
+
+ chunk[idx].max_start_offset =
+ (idx > 0 ? (chunk[idx - 1].max_start_offset) : 0);
+
+ logf("%s New alloc hdr idx[%ld] offset[%ld]: available: %ld",
+ __func__, idx, chunk[idx].max_start_offset, new_alloc_size);
+ }
+
+ if(size <= hdr->chunk_bytes_free) /* request will fit */
+ {
+ virtual_offset = chunk[idx].max_start_offset;
+ chunk[idx].max_start_offset += size;
+ hdr->chunk_bytes_free -= size;
+
+ if (hdr->cached_chunk.handle == chunk[idx].handle)
+ hdr->cached_chunk.max_offset = chunk[idx].max_start_offset;
+
+ /*logf("%s hdr idx[%ld] offset[%ld] size: %ld",
+ __func__, idx, offset, size);*/
+
+ break; /* Success */
+ }
+ else if (hdr->chunk_bytes_free > 0) /* shrink the current chunk */
+ {
+ finalize(hdr, chunk);
+ }
+ idx++;
+ }
+
+ put_chunk_array(ctx, chunk);
+ return virtual_offset;
+}
+
+/* retrieves chunk given virtual offset
+ * returns actual offset
+*/
+static size_t chunk_get_at_offset(struct chunk_alloc_header *hdr, size_t offset)
+{
+ if ((hdr->cached_chunk.handle > 0)
+ && (offset >= hdr->cached_chunk.min_offset)
+ && (offset < hdr->cached_chunk.max_offset))
+ {
+ /* convert virtual offset to real internal offset */
+ return offset - hdr->cached_chunk.min_offset;
+ }
+
+ /* chunk isn't cached perform new lookup */
+ struct chunk_alloc *chunk = get_chunk_array(hdr->context, hdr->chunk_handle);
+ logf("%s search for offset[%ld]", __func__, offset);
+ for (size_t idx = hdr->current; idx < hdr->count; idx--)
+ {
+ size_t min_offset = (idx == 0 ? 0 : chunk[idx - 1].max_start_offset);
+ if (offset < chunk[idx].max_start_offset && offset >= min_offset)
+ {
+ logf("%s found hdr idx[%ld] min offset[%ld] max offset[%ld]",
+ __func__, idx, min_offset, chunk[idx].max_start_offset);
+
+ /* store found chunk */
+ hdr->cached_chunk.handle = chunk[idx].handle;
+ hdr->cached_chunk.max_offset = chunk[idx].max_start_offset;
+ hdr->cached_chunk.min_offset = min_offset;
+ put_chunk_array(hdr->context, chunk);
+ /* convert virtual offset to real internal offset */
+ return offset - hdr->cached_chunk.min_offset;
+ }
+ }
+ panicf("%s Error offset %d does not exist", __func__, (unsigned int)offset);
+ return CHUNK_ALLOC_INVALID;
+}
+
+/* get data - buffer chunk can't be moved while pinned
+ * multiple calls will up pin count so put should be called for each
+ * Returns data at offset
+*/
+void* chunk_get_data(struct chunk_alloc_header *hdr, size_t offset)
+{
+ size_t real = chunk_get_at_offset(hdr, offset);
+ logf("%s offset: %ld real: %ld", __func__, offset, real);
+ return buflib_get_data_pinned(hdr->context, hdr->cached_chunk.handle) + real;
+}
+
+/* release a pinned buffer, chunk can't be moved till pin count == 0 */
+void chunk_put_data(struct chunk_alloc_header *hdr, void* data, size_t offset)
+{
+ size_t real = chunk_get_at_offset(hdr, offset);
+ buflib_put_data_pinned(hdr->context, data - real);
+}
diff --git a/firmware/common/diacritic.c b/firmware/common/diacritic.c
index 92c2400203..a21b3a40b1 100644
--- a/firmware/common/diacritic.c
+++ b/firmware/common/diacritic.c
@@ -232,7 +232,10 @@ bool is_diacritic(const unsigned short char_code, bool *is_rtl)
/* Add MRU entry */
if (mru_len < MRU_MAX_LEN)
+ {
+ diacritic_mru[mru_len] = i;
mru_len++;
+ }
Found:
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index 245947b134..9a78d910a7 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -28,17 +28,14 @@
#include "pathfuncs.h"
#include "timefuncs.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/* structure used for open directory streams */
static struct dirstr_desc
{
struct filestr_base stream; /* basic stream info (first!) */
- struct dirscan_info scan; /* directory scan cursor */
+ struct ns_scan_info scan; /* directory scan cursor */
struct dirent entry; /* current parsed entry information */
-#ifdef HAVE_MULTIVOLUME
- int volumecounter; /* counter for root volume entries */
-#endif
} open_streams[MAX_OPEN_DIRS];
/* check and return a struct dirstr_desc* from a DIR* */
@@ -48,7 +45,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
dir = NULL;
- else if (dir->stream.flags & FDO_BUSY)
+ else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
return dir;
int errnum;
@@ -105,50 +102,6 @@ static struct dirstr_desc * alloc_dirstr(void)
return NULL;
}
-#ifdef HAVE_MULTIVOLUME
-static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
-{
- /* Volumes (secondary file systems) get inserted into the system root
- * directory. If the path specified volume 0, enumeration will not
- * include other volumes, but just its own files and directories.
- *
- * Fake special directories, which don't really exist, that will get
- * redirected upon opendir()
- */
- while (++dir->volumecounter < NUM_VOLUMES)
- {
- /* on the system root */
- if (!fat_ismounted(dir->volumecounter))
- continue;
-
- get_volume_name(dir->volumecounter, entry->d_name);
- dir->entry.info.attr = ATTR_MOUNT_POINT;
- dir->entry.info.size = 0;
- dir->entry.info.wrtdate = 0;
- dir->entry.info.wrttime = 0;
- return 1;
- }
-
- /* do normal directory entry fetching */
- return 0;
-}
-#endif /* HAVE_MULTIVOLUME */
-
-static inline int readdir_volume(struct dirstr_desc *dir,
- struct dirent *entry)
-{
-#ifdef HAVE_MULTIVOLUME
- /* fetch virtual volume entries? */
- if (dir->volumecounter < NUM_VOLUMES)
- return readdir_volume_inner(dir, entry);
-#endif /* HAVE_MULTIVOLUME */
-
- /* do normal directory entry fetching */
- return 0;
- (void)dir; (void)entry;
-}
-
-
/** POSIX interface **/
/* open a directory */
@@ -166,21 +119,13 @@ DIR * opendir(const char *dirname)
if (!dir)
FILE_ERROR(EMFILE, RC);
- rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL);
+ rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
if (rc < 0)
{
DEBUGF("Open failed: %d\n", rc);
FILE_ERROR(ERRNO, RC);
}
-#ifdef HAVE_MULTIVOLUME
- /* volume counter is relevant only to the system root */
- dir->volumecounter = rc > 1 ? 0 : INT_MAX;
-#endif /* HAVE_MULTIVOLUME */
-
- fat_rewind(&dir->stream.fatstr);
- rewinddir_dirent(&dir->scan);
-
dirp = (DIR *)dir;
file_error:
file_internal_unlock_WRITER();
@@ -205,7 +150,7 @@ int closedir(DIR *dirp)
FILE_ERROR(EBADF, -2);
}
- rc = close_stream_internal(&dir->stream);
+ rc = ns_close_stream(&dir->stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 3);
@@ -223,16 +168,11 @@ struct dirent * readdir(DIR *dirp)
struct dirent *res = NULL;
- int rc = readdir_volume(dir, &dir->entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
- if (rc < 0)
- FILE_ERROR(EIO, RC);
- }
-
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
if (rc > 0)
res = &dir->entry;
+ else if (rc < 0)
+ FILE_ERROR(EIO, RC);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -259,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
if (!dir)
FILE_ERROR_RETURN(ERRNO, -1);
- int rc = readdir_volume(dir, entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, entry);
- if (rc < 0)
- FILE_ERROR(EIO, rc * 10 - 4);
- }
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(EIO, rc * 10 - 4);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -289,12 +225,7 @@ void rewinddir(DIR *dirp)
if (!dir)
FILE_ERROR_RETURN(ERRNO);
- rewinddir_dirent(&dir->scan);
-
-#ifdef HAVE_MULTIVOLUME
- if (dir->volumecounter != INT_MAX)
- dir->volumecounter = 0;
-#endif /* HAVE_MULTIVOLUME */
+ ns_dirscan_rewind(&dir->scan);
RELEASE_DIRSTR(READER, dir);
}
@@ -413,3 +344,9 @@ struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry)
file_error:
return (struct dirinfo){ .attribute = 0 };
}
+
+const char* root_realpath(void)
+{
+ /* Native only, for APP and SIM see respective filesystem-.c files */
+ return root_get_realpath(); /* rb_namespace.c */
+}
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 3b880d3382..c274b6c62c 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -249,7 +249,6 @@ static struct dircache_runinfo
/* cache buffer info */
int handle; /* buflib buffer handle */
size_t bufsize; /* size of buflib allocation - 1 */
- int buflocked; /* don't move due to other allocs */
union {
void *p; /* address of buffer - ENTRYSIZE */
struct dircache_entry *pentry; /* alias of .p to assist entry resolution */
@@ -329,29 +328,9 @@ static inline void dumpster_clean_buffer(void *p, size_t size)
*/
static int move_callback(int handle, void *current, void *new)
{
- if (dircache_runinfo.buflocked)
- return BUFLIB_CB_CANNOT_MOVE;
-
+ (void)handle; (void)current;
dircache_runinfo.p = new - ENTRYSIZE;
-
return BUFLIB_CB_OK;
- (void)handle; (void)current;
-}
-
-/**
- * add a "don't move" lock count
- */
-static inline void buffer_lock(void)
-{
- dircache_runinfo.buflocked++;
-}
-
-/**
- * remove a "don't move" lock count
- */
-static inline void buffer_unlock(void)
-{
- dircache_runinfo.buflocked--;
}
@@ -500,7 +479,7 @@ static void binding_dissolve_volume(struct dircache_runinfo_volume *dcrivolp)
static int alloc_cache(size_t size)
{
/* pad with one extra-- see alloc_name() and free_name() */
- return core_alloc_ex("dircache", size + 1, &dircache_runinfo.ops);
+ return core_alloc_ex(size + 1, &dircache_runinfo.ops);
}
/**
@@ -530,14 +509,14 @@ static void set_buffer(int handle, size_t size)
/**
* remove the allocation from dircache control and return the handle
+ * Note that dircache must not be using the buffer!
*/
static int reset_buffer(void)
{
int handle = dircache_runinfo.handle;
if (handle > 0)
{
- /* don't mind .p; it might get changed by the callback even after
- this call; buffer presence is determined by the following: */
+ /* don't mind .p; buffer presence is determined by the following: */
dircache_runinfo.handle = 0;
dircache_runinfo.bufsize = 0;
}
@@ -1473,7 +1452,7 @@ static void sab_process_volume(struct dircache_volume *dcvolp)
*/
int dircache_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
struct file_base_info *dirinfop = stream->infop;
@@ -1728,8 +1707,8 @@ static int sab_process_dir(struct dircache_entry *ce)
/* save current paths size */
int pathpos = strlen(sab_path);
/* append entry */
- strlcpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos);
- strlcpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1);
+ strmemccpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos);
+ strmemccpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1);
int rc = sab_process_dir(ce->down);
/* restore path */
@@ -1756,11 +1735,11 @@ static int sab_process_dir(struct dircache_entry *ce)
static int sab_process_volume(IF_MV(int volume,) struct dircache_entry *ce)
{
memset(ce, 0, sizeof(struct dircache_entry));
- strlcpy(sab_path, "/", sizeof sab_path);
+ strmemccpy(sab_path, "/", sizeof sab_path);
return sab_process_dir(ce);
}
-int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result)
+int dircache_readdir_r(struct dircache_dirscan *dir, struct DIRENT *result)
{
if (dircache_state != DIRCACHE_READY)
return readdir_r(dir->###########3, result, &result);
@@ -1776,7 +1755,7 @@ int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result)
dir->scanidx = ce - dircache_root;
- strlcpy(result->d_name, ce->d_name, sizeof (result->d_name));
+ strmemccpy(result->d_name, ce->d_name, sizeof (result->d_name));
result->info = ce->dirinfo;
return 1;
@@ -1857,7 +1836,7 @@ static void reset_cache(void)
*/
static void build_volumes(void)
{
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
for (int i = 0; i < NUM_VOLUMES; i++)
{
@@ -1903,12 +1882,15 @@ static void build_volumes(void)
logf("Done, %ld KiB used", dircache.size / 1024);
- buffer_unlock();
+ if (dircache_runinfo.handle > 0) /* dircache may have been disabled */
+ core_unpin(dircache_runinfo.handle);
}
/**
* allocate buffer and return whether or not a synchronous build should take
* place; if 'realloced' is NULL, it's just a query about what will happen
+ *
+ * Note this must be called with the dircache_lock() active.
*/
static int prepare_build(bool *realloced)
{
@@ -1958,17 +1940,14 @@ static int prepare_build(bool *realloced)
*realloced = true;
reset_cache();
- buffer_lock();
-
int handle = reset_buffer();
- dircache_unlock();
+ dircache_unlock(); /* release lock held by caller */
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
handle = alloc_cache(size);
- dircache_lock();
+ dircache_lock(); /* reacquire lock */
if (dircache_runinfo.suspended && handle > 0)
{
@@ -1980,13 +1959,9 @@ static int prepare_build(bool *realloced)
}
if (handle <= 0)
- {
- buffer_unlock();
return -1;
- }
set_buffer(handle, size);
- buffer_unlock();
return syncbuild;
}
@@ -2164,8 +2139,7 @@ static void dircache_suspend_internal(bool freeit)
dircache_unlock();
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
thread_wait(thread_id);
@@ -2386,9 +2360,9 @@ void dircache_fileop_create(struct file_base_info *dirinfop,
if ((dinp->attr & ATTR_DIRECTORY) && !is_dotdir_name(basename))
{
/* scan-in the contents of the new directory at this level only */
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
sab_process_dir(infop, false);
- buffer_unlock();
+ core_unpin(dircache_runinfo.handle);
}
}
@@ -2541,13 +2515,10 @@ static ssize_t get_path_sub(int idx, struct get_path_sub_data *data)
cename = "";
#ifdef HAVE_MULTIVOLUME
+ /* prepend the volume specifier */
int volume = IF_MV_VOL(-idx - 1);
- if (volume > 0)
- {
- /* prepend the volume specifier for volumes > 0 */
- cename = alloca(VOL_MAX_LEN+1);
- get_volume_name(volume, cename);
- }
+ cename = alloca(VOL_MAX_LEN+1);
+ get_volume_name(volume, cename);
#endif /* HAVE_MULTIVOLUME */
data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum,
@@ -2920,7 +2891,7 @@ void dircache_dump(void)
if (dircache_runinfo.handle)
{
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
/* bin */
write(fdbin, dircache_runinfo.p + ENTRYSIZE,
@@ -2990,7 +2961,7 @@ void dircache_dump(void)
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
- buffer_unlock();
+ core_unpin(dircache_runinfo.handle);
}
dircache_unlock();
@@ -3109,7 +3080,6 @@ int dircache_load(void)
}
dircache_lock();
- buffer_lock();
if (!dircache_is_clean(false))
goto error;
@@ -3118,6 +3088,7 @@ int dircache_load(void)
dircache = maindata.dircache;
set_buffer(handle, bufsize);
+ core_pin(handle);
hasbuffer = true;
/* convert back to in-RAM representation */
@@ -3172,17 +3143,17 @@ int dircache_load(void)
dircache_enable_internal(false);
/* cache successfully loaded */
+ core_unpin(handle);
logf("Done, %ld KiB used", dircache.size / 1024);
rc = 0;
error:
if (rc < 0 && hasbuffer)
reset_buffer();
- buffer_unlock();
dircache_unlock();
error_nolock:
- if (rc < 0 && handle > 0)
+ if (rc < 0)
core_free(handle);
if (fd >= 0)
@@ -3204,8 +3175,9 @@ int dircache_save(void)
if (fd < 0)
return -1;
+ /* it seems the handle *must* exist if this function is called */
dircache_lock();
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
int rc = -1;
@@ -3274,7 +3246,7 @@ int dircache_save(void)
that makes what was saved completely invalid */
rc = 0;
error:
- buffer_unlock();
+ core_unpin(dircache_runinfo.handle);
dircache_unlock();
if (rc < 0)
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index c096878e86..2fec38995a 100644
--- a/firmware/common/disk.c
+++ b/firmware/common/disk.c
@@ -27,7 +27,7 @@
#include "disk_cache.h"
#include "fileobj_mgr.h"
#include "dir.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "disk.h"
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
@@ -44,7 +44,7 @@
#define disk_writer_lock() file_internal_lock_WRITER()
#define disk_writer_unlock() file_internal_unlock_WRITER()
-/* Partition table entry layout:
+/* "MBR" Partition table entry layout:
-----------------------
0: 0x80 - active
1: starting head
@@ -58,6 +58,16 @@
12-15: nr of sectors in partition
*/
+#define BYTES2INT64(array, pos) \
+ (((uint64_t)array[pos+0] << 0) | \
+ ((uint64_t)array[pos+1] << 8) | \
+ ((uint64_t)array[pos+2] << 16) | \
+ ((uint64_t)array[pos+3] << 24) | \
+ ((uint64_t)array[pos+4] << 32) | \
+ ((uint64_t)array[pos+5] << 40) | \
+ ((uint64_t)array[pos+6] << 48) | \
+ ((uint64_t)array[pos+7] << 56) )
+
#define BYTES2INT32(array, pos) \
(((uint32_t)array[pos+0] << 0) | \
((uint32_t)array[pos+1] << 8) | \
@@ -65,25 +75,40 @@
((uint32_t)array[pos+3] << 24))
#define BYTES2INT16(array, pos) \
- (((uint32_t)array[pos+0] << 0) | \
- ((uint32_t)array[pos+1] << 8))
+ (((uint16_t)array[pos+0] << 0) | \
+ ((uint16_t)array[pos+1] << 8))
+
+static struct partinfo part[NUM_DRIVES*MAX_PARTITIONS_PER_DRIVE];
+static struct volumeinfo volumes[NUM_VOLUMES];
+
+/* check if the entry points to a free volume */
+static bool is_free_volume(const struct volumeinfo *vi)
+{
+ return vi->drive < 0;
+}
-/* space for 4 partitions on 2 drives */
-static struct partinfo part[NUM_DRIVES*4];
-/* mounted to which drive (-1 if none) */
-static int vol_drive[NUM_VOLUMES];
+/* mark a volume entry as free */
+static void mark_free_volume(struct volumeinfo *vi)
+{
+ vi->drive = -1;
+ vi->partition = -1;
+}
static int get_free_volume(void)
{
for (int i = 0; i < NUM_VOLUMES; i++)
- {
- if (vol_drive[i] == -1) /* unassigned? */
+ if (is_free_volume(&volumes[i]))
return i;
- }
return -1; /* none found */
}
+static void init_volume(struct volumeinfo *vi, int drive, int part)
+{
+ vi->drive = drive;
+ vi->partition = part;
+}
+
#ifdef MAX_LOG_SECTOR_SIZE
static int disk_sector_multiplier[NUM_DRIVES] =
{ [0 ... NUM_DRIVES-1] = 1 };
@@ -120,12 +145,13 @@ bool disk_init(IF_MD_NONVOID(int drive))
/* For each drive, start at a different position, in order not to
destroy the first entry of drive 0. That one is needed to calculate
config sector position. */
- struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4];
+ struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
+ uint8_t is_gpt = 0;
disk_writer_lock();
/* parse partitions */
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < MAX_PARTITIONS_PER_DRIVE && i < 4; i++)
{
unsigned char* ptr = sector + 0x1be + 16*i;
pinfo[i].type = ptr[4];
@@ -140,8 +166,115 @@ bool disk_init(IF_MD_NONVOID(int drive))
{
/* not handled yet */
}
- }
+ if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
+ is_gpt = 1;
+ }
+ }
+
+ while (is_gpt) {
+ /* Re-start partition parsing using GPT */
+ uint64_t part_lba;
+ uint32_t part_entries;
+ uint32_t part_entry_size;
+ unsigned char* ptr = sector;
+
+ storage_read_sectors(IF_MD(drive,) 1, 1, sector);
+
+ part_lba = BYTES2INT64(ptr, 0);
+ if (part_lba != 0x5452415020494645ULL) {
+ DEBUGF("GPT: Invalid signature\n");
+ break;
+ }
+ part_entry_size = BYTES2INT32(ptr, 8);
+ if (part_entry_size != 0x00010000) {
+ DEBUGF("GPT: Invalid version\n");
+ break;
+ }
+ part_entry_size = BYTES2INT32(ptr, 12);
+ if (part_entry_size != 0x5c) {
+ DEBUGF("GPT: Invalid header size\n");
+ break;
+ }
+ // XXX checksum header -- u32 @ offset 16
+ part_entry_size = BYTES2INT32(ptr, 24);
+ if (part_entry_size != 1) {
+ DEBUGF("GPT: Invalid header LBA\n");
+ break;
+ }
+
+ part_lba = BYTES2INT64(ptr, 72);
+ part_entries = BYTES2INT32(ptr, 80);
+ part_entry_size = BYTES2INT32(ptr, 84);
+
+ int part = 0;
+reload:
+ storage_read_sectors(IF_MD(drive,) part_lba, 1, sector);
+ uint8_t *pptr = ptr;
+ while (part < MAX_PARTITIONS_PER_DRIVE && part_entries) {
+ if (pptr - ptr >= SECTOR_SIZE) {
+ part_lba++;
+ goto reload;
+ }
+
+ /* Parse GPT entry. We only care about the "General Data" type, ie:
+ EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
+ LE32 LE16 LE16 BE16 BE16
+ */
+ uint64_t tmp;
+ tmp = BYTES2INT32(pptr, 0);
+ if (tmp != 0xEBD0A0A2)
+ goto skip;
+ tmp = BYTES2INT16(pptr, 4);
+ if (tmp != 0xB9E5)
+ goto skip;
+ tmp = BYTES2INT16(pptr, 6);
+ if (tmp != 0x4433)
+ goto skip;
+ if (pptr[8] != 0x87 || pptr[9] != 0xc0)
+ goto skip;
+ if (pptr[10] != 0x68 || pptr[11] != 0xb6 || pptr[12] != 0xb7 ||
+ pptr[13] != 0x26 || pptr[14] != 0x99 || pptr[15] != 0xc7)
+ goto skip;
+
+ tmp = BYTES2INT64(pptr, 48); /* Flags */
+ if (tmp) {
+ DEBUGF("GPT: Skip parition with flags\n");
+ goto skip; /* Any flag makes us ignore this */
+ }
+ tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
+ if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
+ DEBUGF("GPT: partition starts after 2GiB mark\n");
+ goto skip;
+ }
+ if (tmp < 34) {
+ DEBUGF("GPT: Invalid start LBA\n");
+ goto skip;
+ }
+ pinfo[part].start = tmp;
+ tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
+ if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
+ DEBUGF("GPT: partition ends after 2GiB mark\n");
+ goto skip;
+ }
+ if (tmp <= pinfo[part].start) {
+ DEBUGF("GPT: Invalid end LBA\n");
+ goto skip;
+ }
+ pinfo[part].size = tmp - pinfo[part].start + 1;
+ pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
+
+ DEBUGF("GPart%d: start: %08lx size: %08lx\n",
+ part,pinfo[part].start,pinfo[part].size);
+ part++;
+
+ skip:
+ pptr += part_entry_size;
+ part_entries--;
+ }
+
+ is_gpt = 0; /* To break out of the loop */
+ }
disk_writer_unlock();
init = true;
@@ -192,8 +325,7 @@ int disk_mount(int drive)
disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
#endif
-
- /* try "superfloppy" mode */
+ /* try "superfloppy" mode */
DEBUGF("Trying to mount sector 0.\n");
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
@@ -203,19 +335,21 @@ int disk_mount(int drive)
fat_get_bytes_per_sector(IF_MV(volume)) / SECTOR_SIZE;
#endif
mounted = 1;
- vol_drive[volume] = drive; /* remember the drive for this volume */
+ init_volume(&volumes[volume], drive, 0);
volume_onmount_internal(IF_MV(volume));
}
if (mounted == 0 && volume != -1) /* not a "superfloppy"? */
{
for (int i = CONFIG_DEFAULT_PARTNUM;
- volume != -1 && i < 4 && mounted < NUM_VOLUMES_PER_DRIVE;
+ volume != -1 && i < MAX_PARTITIONS_PER_DRIVE && mounted < NUM_VOLUMES_PER_DRIVE;
i++)
{
if (pinfo[i].type == 0 || pinfo[i].type == 5)
continue; /* skip free/extended partitions */
+ DEBUGF("Trying to mount partition %d.\n", i);
+
#ifdef MAX_LOG_SECTOR_SIZE
for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
{
@@ -224,7 +358,7 @@ int disk_mount(int drive)
pinfo[i].start *= j;
pinfo[i].size *= j;
mounted++;
- vol_drive[volume] = drive; /* remember the drive for this volume */
+ init_volume(&volumes[volume], drive, i);
disk_sector_multiplier[drive] = j;
volume_onmount_internal(IF_MV(volume));
volume = get_free_volume(); /* prepare next entry */
@@ -235,7 +369,7 @@ int disk_mount(int drive)
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start))
{
mounted++;
- vol_drive[volume] = drive; /* remember the drive for this volume */
+ init_volume(&volumes[volume], drive, i);
volume_onmount_internal(IF_MV(volume));
volume = get_free_volume(); /* prepare next entry */
}
@@ -257,26 +391,11 @@ int disk_mount_all(void)
volume_onunmount_internal(IF_MV(-1));
fat_init();
+ /* mark all volumes as free */
for (int i = 0; i < NUM_VOLUMES; i++)
- vol_drive[i] = -1; /* mark all as unassigned */
+ mark_free_volume(&volumes[i]);
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
- unsigned int crc = 0;
- int boot_volume = 0;
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
- if(crc == boot_data.crc)
- {
- boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */
- }
- #ifdef HAVE_HOTSWAP
- if (storage_present(boot_volume))
- #endif
- mounted += disk_mount(boot_volume); /* mount boot volume first */
- for (int i = 0; i < NUM_DRIVES; i++)
- if (i != boot_volume)
-#else
for (int i = 0; i < NUM_DRIVES; i++)
-#endif
{
#ifdef HAVE_HOTSWAP
if (storage_present(i))
@@ -299,13 +418,13 @@ int disk_unmount(int drive)
for (int i = 0; i < NUM_VOLUMES; i++)
{
- if (vol_drive[i] == drive)
- { /* force releasing resources */
- vol_drive[i] = -1; /* mark unused */
-
+ struct volumeinfo *vi = &volumes[i];
+ /* unmount any volumes on the drive */
+ if (vi->drive == drive)
+ {
+ mark_free_volume(vi); /* FIXME: should do this after unmount? */
volume_onunmount_internal(IF_MV(i));
fat_unmount(IF_MV(i));
-
unmounted++;
}
}
@@ -404,6 +523,7 @@ enum volume_info_type
#if defined (HAVE_MULTIDRIVE) || defined (HAVE_DIRCACHE)
VP_DRIVE,
#endif
+ VP_PARTITION,
};
static int volume_properties(int volume, enum volume_info_type infotype)
@@ -414,22 +534,25 @@ static int volume_properties(int volume, enum volume_info_type infotype)
if (CHECK_VOL(volume))
{
- int vd = vol_drive[volume];
+ struct volumeinfo *vi = &volumes[volume];
switch (infotype)
{
#ifdef HAVE_HOTSWAP
case VP_REMOVABLE:
- res = storage_removable(vd) ? 1 : 0;
+ res = storage_removable(vi->drive) ? 1 : 0;
break;
case VP_PRESENT:
- res = storage_present(vd) ? 1 : 0;
+ res = storage_present(vi->drive) ? 1 : 0;
break;
#endif
#if defined(HAVE_MULTIDRIVE) || defined(HAVE_DIRCACHE)
case VP_DRIVE:
- res = vd;
+ res = vi->drive;
break;
#endif
+ case VP_PARTITION:
+ res = vi->partition;
+ break;
}
}
@@ -456,6 +579,11 @@ int volume_drive(int volume)
}
#endif /* HAVE_MULTIDRIVE */
+int volume_partition(int volume)
+{
+ return volume_properties(volume, VP_PARTITION);
+}
+
#ifdef HAVE_DIRCACHE
bool volume_ismounted(IF_MV_NONVOID(int volume))
{
@@ -463,4 +591,5 @@ bool volume_ismounted(IF_MV_NONVOID(int volume))
}
#endif /* HAVE_DIRCACHE */
+
#endif /* HAVE_HOTSWAP || HAVE_MULTIDRIVE || HAVE_DIRCACHE */
diff --git a/firmware/common/file.c b/firmware/common/file.c
index c048d182f4..202410db81 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -28,9 +28,18 @@
#include "file.h"
#include "fileobj_mgr.h"
#include "disk_cache.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "string-extra.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#ifdef LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#define DEBUGF logf
+#endif
+
+
/**
* These functions provide a roughly POSIX-compatible file I/O API.
*/
@@ -494,6 +503,8 @@ static int open_internal_inner1(const char *path, int oflag,
return fildes;
file_error:
+ if (fildes >= 0)
+ close(fildes);
return rc;
}
@@ -598,8 +609,10 @@ static inline ssize_t readwrite_partial(struct filestr_desc *file,
static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
bool write)
{
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("readwrite(%p,%lx,%lu,%s)\n",
file, (long)buf, (unsigned long)nbyte, write ? "write" : "read");
+#endif
const file_size_t size = *file->sizep;
file_size_t filerem;
@@ -764,8 +777,9 @@ file_error:;
/* error or not, update the file offset and size if anything was
transferred */
file->offset += done;
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("file offset: %ld\n", file->offset);
-
+#endif
/* adjust file size to length written */
if (write && file->offset > size)
*file->sizep = file->offset;
@@ -899,8 +913,9 @@ file_error:
/* move the read/write file offset */
off_t lseek(int fildes, off_t offset, int whence)
{
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("lseek(fd=%d,ofs=%ld,wh=%d)\n", fildes, (long)offset, whence);
-
+#endif
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
if (!file)
FILE_ERROR_RETURN(ERRNO, -1);
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index b92c4ea115..a73d9beaa2 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -26,12 +26,18 @@
#include "pathfuncs.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dir.h"
-#include "dircache_redirect.h"
-#include "dircache.h"
+#include "rb_namespace.h"
#include "string-extra.h"
#include "rbunicode.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#ifdef LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#define DEBUGF logf
+#endif
+
/** Internal common filesystem service functions **/
/* for internal functions' scanning use to save quite a bit of stack space -
@@ -89,9 +95,9 @@ void file_cache_free(struct filestr_cache *cachep)
/** Stream base APIs **/
-static inline void filestr_clear(struct filestr_base *stream)
+static inline void filestr_clear(struct filestr_base *stream, unsigned int flags)
{
- stream->flags = 0;
+ stream->flags = flags;
stream->bindp = NULL;
#if 0
stream->mtx = NULL;
@@ -155,7 +161,7 @@ void filestr_discard_cache(struct filestr_base *stream)
/* Initialize the base descriptor */
void filestr_base_init(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, FD_VALID);
file_cache_init(&stream->cache);
stream->cachep = &stream->cache;
}
@@ -163,7 +169,7 @@ void filestr_base_init(struct filestr_base *stream)
/* free base descriptor resources */
void filestr_base_destroy(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, 0);
filestr_free_cache(stream);
}
@@ -221,6 +227,7 @@ void iso_decode_d_name(char *d_name)
return;
char shortname[13];
+ /* this only gets called in the case of DOS (8.3) filenames */
size_t len = strlcpy(shortname, d_name, sizeof (shortname));
/* This MUST be the default codepage thus not something that could be
loaded on call */
@@ -229,7 +236,7 @@ void iso_decode_d_name(char *d_name)
#ifdef HAVE_DIRCACHE
/* nullify all the fields of the struct dirent */
-void empty_dirent(struct dirent *entry)
+void empty_dirent(struct DIRENT *entry)
{
entry->d_name[0] = '\0';
entry->info.attr = 0;
@@ -251,7 +258,7 @@ void fill_dirinfo_native(struct dirinfo_native *dinp)
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
struct fat_direntry fatent;
int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
@@ -295,7 +302,7 @@ struct pathwalk_component
#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
#define WALK_RC_FOUND 1 /* found and opened */
-#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
+#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
/* return another struct pathwalk_component from the pool, or NULL if the
@@ -399,10 +406,9 @@ static int walk_open_info(struct pathwalk *walkp,
/* make open official if not simply probing for presence - must do it here
or compp->info on stack will get destroyed before it was copied */
- if (!(callflags & FF_PROBE))
+ if (!(callflags & (FF_PROBE|FF_NOFS)))
fileop_onopen_internal(stream, &compp->info, callflags);
-
- return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
+ return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
}
/* check the component against the prefix test info */
@@ -509,6 +515,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
if (len > MAX_COMPNAME)
return -ENAMETOOLONG;
+ /* no filesystem is mounted here */
+ if (walkp->callflags & FF_NOFS)
+ return -ENOENT;
+
/* check for "." and ".." */
if (name[0] == '.')
{
@@ -577,7 +587,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
/* This lets it be passed quietly to directory scanning */
- stream->flags = callflags & FF_MASK;
+ stream->flags |= callflags & FF_MASK;
struct pathwalk walk;
walk.path = path;
@@ -587,81 +597,36 @@ int open_stream_internal(const char *path, unsigned int callflags,
struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
rootp->nextp = NULL;
- rootp->attr = ATTR_SYSTEM_ROOT;
-
-#ifdef HAVE_MULTIVOLUME
- int volume = 0, rootrc = WALK_RC_FOUND;
-#endif /* HAVE_MULTIVOLUME */
while (1)
{
- const char *pathptr = walk.path;
-
- #ifdef HAVE_MULTIVOLUME
- /* this seamlessly integrates secondary filesystems into the
- root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
- const char *p;
- volume = path_strip_volume(pathptr, &p, false);
- if (!CHECK_VOL(volume))
- {
- DEBUGF("No such device or address: %d\n", volume);
- FILE_ERROR(ENXIO, -2);
- }
-
- if (p == pathptr)
- {
- /* the root of this subpath is the system root */
- rootp->attr = ATTR_SYSTEM_ROOT;
- rootrc = WALK_RC_FOUND_ROOT;
- }
- else
- {
- /* this subpath specifies a mount point */
- rootp->attr = ATTR_MOUNT_POINT;
- rootrc = WALK_RC_FOUND;
- }
-
- walk.path = p;
- #endif /* HAVE_MULTIVOLUME */
-
- /* set name to start at last leading separator; names of volume
- specifiers will be returned as "/<fooN>" */
- rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
- rootp->length =
- IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
+ rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
+ if (rc < 0)
+ break;
- rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
+ rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
if (rc < 0)
- {
- /* not mounted */
- DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
- rc = -ENXIO;
break;
- }
- get_rootinfo_internal(&rootp->info);
+ walk.path = rootp->name + rootp->length;
+
rc = walk_path(&walk, rootp, stream);
if (rc != WALK_RC_CONT_AT_ROOT)
break;
}
- switch (rc)
+ if (rc >= 0)
{
- case WALK_RC_FOUND_ROOT:
- IF_MV( rc = rootrc; )
- /* fallthrough */
- case WALK_RC_NOT_FOUND:
- case WALK_RC_FOUND:
/* FF_PROBE leaves nothing for caller to clean up */
- if (callflags & FF_PROBE)
+ if (walk.callflags & FF_PROBE)
filestr_base_destroy(stream);
-
- break;
-
- default: /* utter, abject failure :`( */
+ }
+ else
+ {
+ /* utter, abject failure :`( */
DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
filestr_base_destroy(stream);
- FILE_ERROR(-rc, -3);
+ FILE_ERROR(-rc, -1);
}
file_error:
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index e34a460e10..da82681acc 100644
--- a/firmware/common/fileobj_mgr.c
+++ b/firmware/common/fileobj_mgr.c
@@ -20,12 +20,13 @@
****************************************************************************/
#include "config.h"
#include "system.h"
+#include <errno.h>
#include "debug.h"
#include "file.h"
#include "dir.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/**
* Manages file and directory streams on all volumes
@@ -34,8 +35,8 @@
*/
-/* there will always be enough of these for all user handles, thus these
- functions don't return failure codes */
+/* there will always be enough of these for all user handles, thus most of
+ these functions don't return failure codes */
#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
/* describes the file as an image on the storage medium */
@@ -84,6 +85,14 @@ static struct ll_head busy_bindings[NUM_VOLUMES];
for (struct filestr_base *s = STREAM_##what(start); \
s; s = STREAM_NEXT(s))
+/* once a file/directory, always a file/directory; such a change
+ is a bug */
+#define CHECK_FO_DIRECTORY(callflags, fobp) \
+ if (((callflags) ^ (fobp)->flags) & FO_DIRECTORY) \
+ { \
+ DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", \
+ __func__, (fobp), (callflags)); \
+ }
/* syncs information for the stream's old and new parent directory if any are
currently opened */
@@ -96,6 +105,9 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
continue; /* not directory or removed can't be parent of anything */
struct filestr_base *parentstrp = STREAM_FIRST(fobp);
+ if (!parentstrp)
+ continue;
+
struct fat_file *parentfilep = &parentstrp->infop->fatfile;
for (int i = 0; i < count; i++)
@@ -111,8 +123,8 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
}
/* see if this file has open streams and return that fileobj_binding if so,
- else grab a new one from the free list; returns true if this stream is
- the only open one */
+ else grab a new one from the free list; returns true if this is new */
+
static bool binding_assign(const struct file_base_info *srcinfop,
struct fileobj_binding **fobpp)
{
@@ -123,7 +135,7 @@ static bool binding_assign(const struct file_base_info *srcinfop,
if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
{
- /* already has open streams */
+ /* already has open streams/mounts*/
*fobpp = fobp;
return false;
}
@@ -143,6 +155,22 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp)
ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
}
+static void bind_source_info(const struct file_base_info *srcinfop,
+ struct fileobj_binding **fobpp)
+{
+ if (!binding_assign(srcinfop, fobpp))
+ return; /* already in use */
+ /* is new */
+ (*fobpp)->bind.info = *srcinfop;
+ fileobj_bind_file(&(*fobpp)->bind);
+}
+
+static void release_binding(struct fileobj_binding *fobp)
+{
+ fileobj_unbind_file(&fobp->bind);
+ binding_add_to_free_list(fobp);
+}
+
/** File and directory internal interface **/
void file_binding_insert_last(struct file_base_binding *bindp)
@@ -169,6 +197,33 @@ void file_binding_remove_next(struct file_base_binding *prevp,
}
#endif /* HAVE_DIRCACHE */
+/* mounts a file object as a target from elsewhere */
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp)
+{
+ struct fileobj_binding *fobp;
+ bind_source_info(srcinfop, &fobp);
+ CHECK_FO_DIRECTORY(callflags, fobp);
+ if (fobp->flags & FO_MOUNTTARGET)
+ return false; /* already mounted */
+ fobp->flags |= FDO_BUSY | FO_MOUNTTARGET |
+ (callflags & FO_DIRECTORY);
+ *bindpp = &fobp->bind;
+ return true;
+}
+/* unmounts the file object and frees it if now unusued */
+void fileobj_unmount(struct file_base_binding *bindp)
+{
+ struct fileobj_binding *fobp = (struct fileobj_binding *)bindp;
+ if (!(fobp->flags & FO_MOUNTTARGET))
+ return; /* not mounted */
+ if (STREAM_FIRST(fobp) == NULL)
+ release_binding(fobp); /* no longer in use */
+ else
+ fobp->flags &= ~FO_MOUNTTARGET;
+}
+
/* opens the file object for a new stream and sets up the caches;
* the stream must already be opened at the FS driver level and *stream
* initialized.
@@ -180,10 +235,13 @@ void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags)
{
+ /* assign base file information */
struct fileobj_binding *fobp;
- bool first = binding_assign(srcinfop, &fobp);
+ bind_source_info(srcinfop, &fobp);
+ unsigned int foflags = fobp->flags;
/* add stream to this file's list */
+ bool first = STREAM_FIRST(fobp) == NULL;
ll_insert_last(&fobp->list, &stream->node);
/* initiate the new stream into the enclave */
@@ -197,27 +255,16 @@ void fileobj_fileop_open(struct filestr_base *stream,
if (first)
{
/* first stream for file */
- fobp->bind.info = *srcinfop;
- fobp->flags = FDO_BUSY | FO_SINGLE |
- (callflags & (FO_DIRECTORY|FO_TRUNC));
- fobp->writers = 0;
- fobp->size = 0;
-
- fileobj_bind_file(&fobp->bind);
+ fobp->flags = foflags | FDO_BUSY | FO_SINGLE |
+ (callflags & (FO_DIRECTORY|FO_TRUNC));
+ fobp->writers = 0;
+ fobp->size = 0;
}
else
{
/* additional stream for file */
- fobp->flags &= ~FO_SINGLE;
- fobp->flags |= callflags & FO_TRUNC;
-
- /* once a file/directory, always a file/directory; such a change
- is a bug */
- if ((callflags ^ fobp->flags) & FO_DIRECTORY)
- {
- DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
- __func__, stream, callflags);
- }
+ fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC);
+ CHECK_FO_DIRECTORY(callflags, fobp);
}
if ((callflags & FD_WRITE) && ++fobp->writers == 1)
@@ -257,12 +304,14 @@ void fileobj_fileop_close(struct filestr_base *stream)
if (foflags & FO_SINGLE)
{
/* last stream for file; close everything */
- fileobj_unbind_file(&fobp->bind);
-
if (fobp->writers)
file_cache_free(&fobp->cache);
- binding_add_to_free_list(fobp);
+ /* binding must stay valid if something is mounted to here */
+ if (foflags & FO_MOUNTTARGET)
+ fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET);
+ else
+ release_binding(fobp);
}
else
{
diff --git a/firmware/common/inflate.c b/firmware/common/inflate.c
index 26fd191690..e39fe3a14e 100644
--- a/firmware/common/inflate.c
+++ b/firmware/common/inflate.c
@@ -43,6 +43,7 @@
#include "inflate.h"
#include <stdbool.h>
+#include <string.h>
#include "adler32.h"
#include "crc32.h"
#include "system.h"
@@ -757,3 +758,36 @@ int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate
return inflate_blocks(it, st, read, rctx, write, wctx);
}
+
+static uint32_t inflate_buffer_rw(struct inflate_bufferctx* c,
+ void* dst, const void* src, uint32_t block_size)
+{
+ size_t size_left = c->end - c->buf;
+ size_t copy_size = MIN((size_t)block_size, size_left);
+
+ memcpy(dst, src, copy_size);
+ c->buf += copy_size;
+
+ return copy_size;
+}
+
+uint32_t inflate_buffer_reader(void* block, uint32_t block_size, void* ctx)
+{
+ struct inflate_bufferctx* c = ctx;
+ return inflate_buffer_rw(c, block, c->buf, block_size);
+}
+
+uint32_t inflate_buffer_writer(const void* block, uint32_t block_size, void* ctx)
+{
+ struct inflate_bufferctx* c = ctx;
+ return inflate_buffer_rw(c, c->buf, block, block_size);
+}
+
+uint32_t inflate_getsize_writer(const void* block, uint32_t block_size, void* ctx)
+{
+ (void)block;
+
+ size_t* size = ctx;
+ *size += block_size;
+ return block_size;
+}
diff --git a/firmware/common/linked_list.c b/firmware/common/linked_list.c
index b8f2dd181c..30e4e6079a 100644
--- a/firmware/common/linked_list.c
+++ b/firmware/common/linked_list.c
@@ -46,15 +46,6 @@ static struct ll_node * ll_search_prev(struct ll_head *list,
}
/**
- * Initializes the singly-linked list
- */
-void ll_init(struct ll_head *list)
-{
- list->head = NULL;
- list->tail = NULL;
-}
-
-/**
* Adds a node to s singly-linked list using "insert next"
*/
void ll_insert_next(struct ll_head *list, struct ll_node *node,
@@ -142,16 +133,36 @@ void ll_remove(struct ll_head *list, struct ll_node *node)
/** (L)inked (L)ist (D)ouble **/
-/**
- * Initializes the doubly-linked list
- */
-void lld_init(struct lld_head *list)
+void lld_insert_next(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode)
+{
+ struct lld_node **nodep = node != NULL ? &node->next : &list->head;
+ struct lld_node *next = *nodep;
+
+ newnode->next = next;
+ newnode->prev = node;
+ *nodep = newnode;
+
+ if (next == NULL)
+ list->tail = newnode;
+ else
+ next->prev = newnode;
+}
+
+void lld_insert_prev(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode)
{
- list->head = NULL;
- list->tail = NULL;
+ struct lld_node **nodep = node != NULL ? &node->prev : &list->tail;
+ struct lld_node *prev = *nodep;
- /* tail could be stored in first item's prev pointer but this simplifies
- the routines and maintains the non-circularity */
+ newnode->next = node;
+ newnode->prev = prev;
+ *nodep = newnode;
+
+ if (prev == NULL)
+ list->head = newnode;
+ else
+ prev->next = newnode;
}
/**
@@ -238,14 +249,6 @@ static inline struct lldc_node * lldc_insert(struct lldc_head *list,
}
/**
- * Initializes the doubly-linked circular list
- */
-void lldc_init(struct lldc_head *list)
-{
- list->head = NULL;
-}
-
-/**
* Adds a node to a doubly-linked circular list using "insert first"
*/
void lldc_insert_first(struct lldc_head *list, struct lldc_node *node)
diff --git a/firmware/common/multiboot.c b/firmware/common/multiboot.c
new file mode 100644
index 0000000000..c2cedc102d
--- /dev/null
+++ b/firmware/common/multiboot.c
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2017, 2020 by William Wilgus
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "bootdata.h"
+#include "crc32.h"
+#include "loader_strerror.h"
+#include "file.h"
+#include <string.h>
+#include <stdio.h>
+
+/* Write bootdata into location in FIRMWARE marked by magic header
+ * Assumes buffer is already loaded with the firmware image
+ * We just need to find the location and write data into the
+ * payload region along with the crc for later verification and use.
+ * Returns payload len on success,
+ * On error returns EKEY_NOT_FOUND
+ */
+int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
+{
+ struct boot_data_t bl_boot_data;
+ struct boot_data_t *fw_boot_data = NULL;
+ int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
+ int payload_len = EKEY_NOT_FOUND;
+
+ /* search for boot data header prior to search_len */
+ for(int i = 0; i < search_len; i++)
+ {
+ fw_boot_data = (struct boot_data_t*) &buf[i];
+ if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
+ fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
+ continue;
+
+ memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
+ bl_boot_data.boot_volume = boot_volume;
+
+ memset(fw_boot_data->payload, 0, fw_boot_data->length);
+ /* determine maximum bytes we can write to firmware
+ BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
+ payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
+ fw_boot_data->length = payload_len;
+ /* copy data to FIRMWARE bootdata struct */
+ memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
+ /* crc will be used within the firmware to check validity of bootdata */
+ fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
+ break;
+
+ }
+ return payload_len;
+}
+
+#ifdef HAVE_MULTIBOOT
+/* Check in root of this <volume> for rockbox_main.<playername>
+ * if this file empty or there is a single slash '/'
+ * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
+ * If instead '/<*DIRECTORY*>' is supplied
+ * addpath will be set to this DIRECTORY buf =
+ * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
+ * On error returns Negative number or 0
+ * On success returns bytes from snprintf
+ * and generated path will be placed in buf
+ * note: if supplied buffer is too small return will be
+ * the number of bytes that would have been written
+ */
+int get_redirect_dir(char* buf, int buffer_size, int volume,
+ const char* rootdir, const char* firmware)
+{
+ int fd;
+ size_t f_offset;
+ char add_path[MAX_PATH];
+ /* Check in root of volume for rockbox_main.<playername> redirect */
+ snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
+ fd = open(add_path, O_RDONLY);
+ if (fd < 0)
+ return EFILE_NOT_FOUND;
+
+ /*clear add_path for re-use*/
+ memset(add_path, 0, sizeof(add_path));
+ f_offset = read(fd, add_path,sizeof(add_path));
+ close(fd);
+
+ for(size_t i = f_offset - 1; i < f_offset; i--)
+ {
+ /* strip control chars < SPACE or all if path doesn't start with '/' */
+ if (add_path[i] < 0x20 || add_path[0] != '/')
+ add_path[i] = '\0';
+ }
+ /* if '/add_path' is specified in rockbox_main.<playername>
+ path is /<vol#>/add_path/rootdir/firmwarename
+ if add_path is empty or '/' is missing from beginning
+ path is /<vol#>/rootdir/firmwarename
+ */
+ return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
+ rootdir, firmware);
+}
+#endif
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c
index 2b4e6a8eb0..fa296cc2ed 100644
--- a/firmware/common/pathfuncs.c
+++ b/firmware/common/pathfuncs.c
@@ -23,9 +23,9 @@
#include "system.h"
#include "pathfuncs.h"
#include "string-extra.h"
+#include <stdio.h>
#ifdef HAVE_MULTIVOLUME
-#include <stdio.h>
#include "storage.h"
enum storage_name_dec_indexes
@@ -109,12 +109,12 @@ static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] =
/* Returns on which volume this is and sets *nameptr to the portion of the
* path after the volume specifier, which could be the null if the path is
* just a volume root. If *nameptr > name, then a volume specifier was
- * found. If 'greedy' is 'true', then it all separators after the volume
- * specifier are consumed, if one was found.
+ * found. If 'greedy' is 'true', then all separators after the volume
+ * specifier are consumed.
*/
int path_strip_volume(const char *name, const char **nameptr, bool greedy)
{
- int volume = 0;
+ int volume = ROOT_VOLUME;
const char *t = name;
int c, v = 0;
@@ -123,9 +123,15 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
* digits within the brackets is parsed as the volume number and of
* those, only the last ones VOL_MUM_MAX allows.
*/
- c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */
+ t = GOBBLE_PATH_SEPCH(t); /* skip all leading slashes */
+ if (t == name)
+ {
+ volume = -1; /* relative path; don't know */
+ goto psv_out;
+ }
+ c = *t;
if (c != VOL_START_TOK) /* missing start token? no volume */
- goto volume0;
+ goto psv_out;
do
{
@@ -136,7 +142,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
break;
case '\0':
case PATH_SEPCH: /* no closing bracket; no volume */
- goto volume0;
+ goto psv_out;
default: /* something else; reset volume */
v = 0;
}
@@ -146,7 +152,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
if (!(c = *++t)) /* no more path and no '/' is ok */
;
else if (c != PATH_SEPCH) /* more path and no separator after end */
- goto volume0;
+ goto psv_out;
else if (greedy)
t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */
@@ -155,21 +161,59 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
volume = v;
name = t;
-volume0:
+psv_out:
if (nameptr)
*nameptr = name;
return volume;
}
+/* Strip the last volume component in the path and return the remainder of
+ * the path in *nameptr. If 'greedy' is 'true', then all separators after
+ * the volume specifier are consumed.
+ */
+int path_strip_last_volume(const char *name, const char **nameptr, bool greedy)
+{
+ const char *p = name + strlen(name);
+
+ while (p > name)
+ {
+ /* skip the component */
+ while (p > name && p[-1] != PATH_SEPCH)
+ --p;
+
+ /* bail if we reached the beginning */
+ if (p <= name+1)
+ break;
+
+ /* point at the seprator */
+ --p;
+
+ /* try to strip the volume and return it if found */
+ int volume = path_strip_volume(p, nameptr, greedy);
+ if (volume != ROOT_VOLUME)
+ return volume;
+
+ /* skip any extra separators */
+ while (p > name && p[-1] == PATH_SEPCH)
+ --p;
+ }
+
+ /* return whatever is at the beginning of the path */
+ return path_strip_volume(name, nameptr, greedy);
+}
+
/* Returns the volume specifier decorated with the storage type name.
* Assumes the supplied buffer size is at least {VOL_MAX_LEN}+1.
*/
int get_volume_name(int volume, char *buffer)
{
- if (volume < 0)
+ if (volume < 0 || volume == ROOT_VOLUME)
{
- *buffer = '\0';
- return 0;
+ char *t = buffer;
+ if (volume == ROOT_VOLUME)
+ *t++ = PATH_ROOTCHR;
+ *t = '\0';
+ return t - buffer;
}
volume %= VOL_NUM_MAX; /* as path parser would have it */
@@ -182,8 +226,20 @@ int get_volume_name(int volume, char *buffer)
return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c",
VOL_START_TOK, voldec, volume, VOL_END_TOK);
}
+
+/* Returns volume name formatted with the root. Assumes buffer size is at
+ * least {VOL_MAX_LEN}+2 */
+int make_volume_root(int volume, char *buffer)
+{
+ char *t = buffer;
+ if (volume >= 0 && volume != ROOT_VOLUME)
+ *t++ = PATH_ROOTCHR;
+ t += get_volume_name(volume, t);
+ return t - buffer;
+}
#endif /* HAVE_MULTIVOLUME */
+
/* Just like path_strip_volume() but strips a leading drive specifier and
* returns the drive number (A=0, B=1, etc.). -1 means no drive was found.
* If 'greedy' is 'true', all separators after the volume are consumed.
@@ -392,6 +448,10 @@ void path_remove_dot_segments (char *dstpath, const char *path)
}
/* Appends one path to another, adding separators between components if needed.
+ * basepath_max can be used to truncate the basepath if desired
+ * NOTE: basepath is truncated after copying to the buffer so there must be enough
+ * free space for the entirety of the basepath even if the resulting string would fit
+ *
* Return value and behavior is otherwise as strlcpy so that truncation may be
* detected.
*
@@ -399,9 +459,11 @@ void path_remove_dot_segments (char *dstpath, const char *path)
* PA_SEP_HARD adds a separator even if the base path is empty
* PA_SEP_SOFT adds a separator only if the base path is not empty
*/
-size_t path_append(char *buf, const char *basepath,
+size_t path_append_ex(char *buf, const char *basepath, size_t basepath_max,
const char *component, size_t bufsize)
{
+ size_t len;
+ bool separate = false;
const char *base = basepath && basepath[0] ? basepath : buf;
if (!base)
return bufsize; /* won't work to get lengths from buf */
@@ -414,15 +476,25 @@ size_t path_append(char *buf, const char *basepath,
/* 'component' is absolute; replace all */
basepath = component;
component = "";
+ basepath_max = -1u;
}
/* if basepath is not null or empty, buffer contents are replaced,
otherwise buf contains the base path */
- size_t len = base == buf ? strlen(buf) : strlcpy(buf, basepath, bufsize);
- bool separate = false;
+ if (base == buf)
+ len = strlen(buf);
+ else
+ {
+ len = strlcpy(buf, basepath, bufsize);
+ if (basepath_max < len)
+ {
+ len = basepath_max;
+ buf[basepath_max] = '\0';
+ }
+ }
- if (!basepath || !component)
+ if (!basepath || !component || basepath_max == 0)
separate = !len || base[len-1] != PATH_SEPCH;
else if (component[0])
separate = len && base[len-1] != PATH_SEPCH;
@@ -440,6 +512,12 @@ size_t path_append(char *buf, const char *basepath,
return len + strlcpy(buf, component ?: "", bufsize);
}
+
+size_t path_append(char *buf, const char *basepath,
+ const char *component, size_t bufsize)
+{
+ return path_append_ex(buf, basepath, -1u, component, bufsize);
+}
/* Returns the location and length of the next path component, consuming the
* input in the process.
*
diff --git a/firmware/common/rb-loader.c b/firmware/common/rb-loader.c
index 300ba55401..430ed6ec7b 100644
--- a/firmware/common/rb-loader.c
+++ b/firmware/common/rb-loader.c
@@ -26,96 +26,9 @@
#include "loader_strerror.h"
#include "checksum.h"
-#if defined(HAVE_BOOTDATA)
-#include "bootdata.h"
-#include "crc32.h"
-
-/* Write bootdata into location in FIRMWARE marked by magic header
- * Assumes buffer is already loaded with the firmware image
- * We just need to find the location and write data into the
- * payload region along with the crc for later verification and use.
- * Returns payload len on success,
- * On error returns EKEY_NOT_FOUND
- */
-int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
-{
- struct boot_data_t bl_boot_data;
- struct boot_data_t *fw_boot_data = NULL;
- int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
- int payload_len = EKEY_NOT_FOUND;
-
- /* search for boot data header prior to search_len */
- for(int i = 0;i < search_len;i++)
- {
- fw_boot_data = (struct boot_data_t*) &buf[i];
- if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
- fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
- continue;
-
- memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
- bl_boot_data.boot_volume = boot_volume;
-
- memset(fw_boot_data->payload, 0, fw_boot_data->length);
- /* determine maximum bytes we can write to firmware
- BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
- payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
- fw_boot_data->length = payload_len;
- /* copy data to FIRMWARE bootdata struct */
- memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
- /* crc will be used within the firmware to check validity of bootdata */
- fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
- break;
-
- }
- return payload_len;
-}
-#endif /* HAVE_BOOTDATA */
-
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware)
-{
- int fd;
- int f_offset;
- char add_path[MAX_PATH];
- /* Check in root of volume for rockbox_main.<playername> redirect */
- snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
- fd = open(add_path, O_RDONLY);
- if (fd < 0)
- return EFILE_NOT_FOUND;
-
- /*clear add_path for re-use*/
- memset(add_path, 0, sizeof(add_path));
- f_offset = read(fd, add_path,sizeof(add_path));
- close(fd);
-
- for(int i = f_offset - 1;i > 0; i--)
- {
- /* strip control chars < SPACE or all if path doesn't start with '/' */
- if (add_path[i] < 0x20 || add_path[0] != '/')
- add_path[i] = '\0';
- }
- /* if '/add_path' is specified in rockbox_main.<playername>
- path is /<vol#>/add_path/rootdir/firmwarename
- if add_path is empty or '/' is missing from beginning
- path is /<vol#>/rootdir/firmwarename
- */
- return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
- rootdir, firmware);
-}
-#endif /* HAVE_MULTIBOOT */
+#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
+#include "multiboot.h"
+#endif
/* loads a firmware file from supplied filename
* file opened, checks firmware size and checksum
@@ -193,7 +106,7 @@ int load_firmware(unsigned char* buf, const char* firmware, int buffer_size)
* 0 is the default boot volume, it is not checked here
* if found <volume>/rockbox_main.<playername> and firmware
* has a bootdata region this firmware will be loaded */
- for (unsigned int i = NUM_VOLUMES - 1; i > 0 && ret < 0; i--)
+ for (int i = NUM_VOLUMES - 1; i >= MULTIBOOT_MIN_VOLUME && ret < 0; i--)
{
if (get_redirect_dir(filename, sizeof(filename), i,
BOOTDIR, firmware) > 0)
diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c
new file mode 100644
index 0000000000..85f647c474
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,383 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2017 by Michael Sevakis
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include <errno.h>
+#include "fileobj_mgr.h"
+#include "rb_namespace.h"
+#include "file_internal.h"
+#include <stdio.h> /*snprintf*/
+
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#include "logf.h"
+
+#if !defined(HAVE_MULTIVOLUME) && defined(LOGF_ENABLE)
+ int volume = 0;
+#endif
+
+
+#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
+#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
+
+static uint8_t root_entry_flags[NUM_VOLUMES+1];
+static struct file_base_binding *root_bindp;
+
+static inline unsigned int get_root_item_state(int item)
+{
+ return root_entry_flags[item];
+}
+
+static inline void set_root_item_state(int item, unsigned int state)
+{
+ root_entry_flags[item] = state;
+}
+
+static void get_mount_point_entry(IF_MV(int volume,) struct DIRENT *entry)
+{
+#ifdef HAVE_MULTIVOLUME
+ get_volume_name(volume, entry->d_name);
+#else /* */
+ strcpy(entry->d_name, PATH_ROOTSTR);
+#endif /* HAVE_MULTIVOLUME */
+#if defined(_FILESYSTEM_NATIVE_H_)
+ entry->info.attr = ATTR_MOUNT_POINT;
+ entry->info.size = 0;
+ entry->info.wrtdate = 0;
+ entry->info.wrttime = 0;
+#endif /* is dirinfo_native */
+ logf("%s: vol:%d, %s", __func__, volume, entry->d_name);
+}
+
+/* unmount the directory that enumerates into the root namespace */
+static void unmount_item(int item)
+{
+ unsigned int state = get_root_item_state(item);
+ logf("%s: state: %u", __func__, state);
+ if (!state)
+ return;
+
+ if (state & NSITEM_CONTENTS)
+ {
+ fileobj_unmount(root_bindp);
+ root_bindp = NULL;
+ }
+
+ set_root_item_state(item, 0);
+}
+
+static char *root_realpath_internal(void)
+{
+ static char root_realpath[ROOT_MAX_REALPATH];
+ return root_realpath;
+}
+const char* root_get_realpath(void)
+{
+ return root_realpath_internal();
+}
+
+/* mount the directory that enumerates into the root namespace */
+int root_mount_path(const char *path, unsigned int flags)
+{
+ const char *folder = NULL; /* is a folder enumerated in the root? */
+#ifdef HAVE_MULTIVOLUME
+ int volume = path_strip_volume(path, &folder, false);
+ if (volume == ROOT_VOLUME)
+ return -EINVAL;
+ if (!CHECK_VOL(volume))
+ return -ENOENT;
+ char volname[VOL_MAX_LEN+2];
+ make_volume_root(volume, volname);
+#else
+ const char *volname = PATH_ROOTSTR;
+ if (!path_is_absolute(path))
+ {
+ logf("Path not absolute %s", path);
+ return -ENOENT;
+ }
+ path_dirname(path, &folder);
+#endif /* HAVE_MULTIVOLUME */
+ bool contents = flags & NSITEM_CONTENTS;
+ int item = IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+ logf("%s: item:%d, st:%u, %s", __func__, item, state, path);
+ if (contents && state) /* volume must be mounted to enumerate into the root namespace */
+ {
+ if (get_root_item_state(ROOT_CONTENTS_INDEX))
+ return -EBUSY; /* error something is already enumerated */
+ /* cache information about the target */
+ struct filestr_base stream;
+ struct path_component_info compinfo;
+ int e = errno;
+ int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
+ FF_DEVPATH, &stream, &compinfo);
+ if (rc <= 0)
+ {
+ rc = rc ? -errno : -ENOENT;
+ errno = e;
+ return rc;
+ }
+ if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
+ return -EBUSY;
+ int root_state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
+ set_root_item_state(ROOT_CONTENTS_INDEX, root_state);
+ flags |= state; /* preserve the state of the mounted volume */
+ if (!folder)
+ {
+ folder = "";
+ }
+ else
+ {
+ /*if a folder has been enumerated don't mark the whole volume */
+ if (folder[0] != '\0' && folder[1] != '\0')
+ flags &= ~NSITEM_CONTENTS;
+
+ }
+ snprintf(root_realpath_internal(), ROOT_MAX_REALPATH,"%s%s", volname, folder);
+ }
+ else if (state) /* error volume already mounted */
+ return -EBUSY;
+ state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
+ set_root_item_state(item, state);
+ return 0;
+}
+
+/* check if volume in path is mounted in the root namespace */
+bool ns_volume_is_visible(IF_MV_NONVOID(int volume))
+{
+ int item = IF_MV_VOL(volume);
+ if ((item == ROOT_VOLUME) || !CHECK_VOL(item))
+ return false;
+ unsigned int state = get_root_item_state(item);
+ return state && (((state & NSITEM_HIDDEN) == 0) || (state & NSITEM_CONTENTS));
+}
+
+/* inform root that an entire volume is being unmounted */
+void root_unmount_volume(IF_MV_NONVOID(int volume))
+{
+ logf("%s: vol: %d", __func__, volume);
+ FOR_EACH_VOLUME(volume, item)
+ {
+ #ifdef HAVE_MULTIVOLUME
+ uint32_t state = get_root_item_state(item);
+ if (state && (volume < 0 || item == volume))
+ #endif /* HAVE_MULTIVOLUME */
+ unmount_item(item);
+ }
+
+ /* if the volume unmounted contains the root directory contents then
+ the contents must also be unmounted */
+#ifdef HAVE_MULTIVOLUME
+ uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
+ if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
+#endif
+ {
+ unmount_item(ROOT_CONTENTS_INDEX);
+ root_realpath_internal()[0] = '\0';
+ }
+}
+
+/* parse the root part of a path */
+int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
+{
+ logf("%s: path: %s", __func__, path);
+ int volume = ROOT_VOLUME;
+
+#ifdef HAVE_MULTIVOLUME
+ /* this seamlessly integrates secondary filesystems into the
+ root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
+ const char *p;
+ volume = path_strip_volume(path, &p, false);
+ if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
+ {
+ logf("vol: %d is not root", volume);
+ return -ENOENT;
+ }
+#endif /* HAVE_MULTIVOLUME */
+
+ /* set name to start at last leading separator; name of root will
+ * be returned as "/", volume specifiers as "/<fooN>" */
+ *pathp = GOBBLE_PATH_SEPCH(path) - 1;
+ *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
+#ifdef LOGF_ENABLE
+ if (volume == INT_MAX)
+ logf("vol: ROOT(%d) %s", volume, *pathp);
+ else
+ logf("vol: %d %s", volume, *pathp);
+#endif
+#ifdef HAVE_MULTIVOLUME
+ if (*lenp > MAX_COMPNAME+1)
+ {
+ logf("%s: path too long %s", __func__, path);
+ return -ENAMETOOLONG;
+ }
+#endif
+#ifdef LOGF_ENABLE
+ if (volume == INT_MAX)
+ logf("%s: vol: ROOT(%d) path: %s", __func__, volume, path);
+ else
+ logf("%s: vol: %d path: %s", __func__, volume, path);
+#endif
+ return volume;
+}
+
+/* open one of the items in the root */
+int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
+ struct file_base_info *infop, uint16_t *attrp)
+{
+ unsigned int callflags = *callflagsp;
+ bool devpath = !!(callflags & FF_DEVPATH);
+#ifdef HAVE_MULTIVOLUME
+ bool sysroot = volume == ROOT_VOLUME;
+ if (devpath && sysroot)
+ return -ENOENT; /* devpath needs volume spec */
+#else
+ bool sysroot = !devpath; /* always sysroot unless devpath */
+#endif
+
+ int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+ logf("%s: Vol:%d St:%d", __func__, item, state);
+ if (sysroot)
+ {
+ *attrp = ATTR_SYSTEM_ROOT;
+
+ if (state)
+ *infop = root_bindp->info;
+ else
+ {
+ logf("%s: SysRoot Vol:%d St:%d NOT mounted", __func__, item, state);
+ *callflagsp = callflags | FF_NOFS; /* contents not mounted */
+ }
+ }
+ else
+ {
+ *attrp = ATTR_MOUNT_POINT;
+
+ if (!devpath && !state)
+ return -ENOENT; /* regular open requires having been mounted */
+#if CONFIG_PLATFORM & PLATFORM_NATIVE
+ if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
+ {
+ logf("%s: DevPath Vol:%d St:%d NOT mounted", __func__, item, state);
+ return -ENOENT; /* not mounted */
+ }
+#endif
+ get_rootinfo_internal(infop);
+ }
+
+ return 0;
+}
+
+/* read root directory entries */
+int root_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp, struct DIRENT *entry)
+{
+ int rc = 0;
+
+ int item = scanp->item;
+ logf("%s: item: %d", __func__, item);
+
+ /* skip any not-mounted or hidden items */
+ unsigned int state;
+ while (1)
+ {
+ if (item >= NUM_ROOT_ITEMS)
+ goto file_eod;
+
+ state = get_root_item_state(item);
+ if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
+ {
+#if 1 /* hide the volume enumerated into the root namespace */
+ if (item == ROOT_CONTENTS_INDEX || (state & NSITEM_CONTENTS) == 0)
+ {
+ logf("Found mounted item: %d %s", item, entry->d_name);
+ break;
+ }
+#endif
+ }
+
+ item++;
+ }
+
+ if (item == ROOT_CONTENTS_INDEX)
+ {
+ rc = readdir_dirent(stream, &scanp->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(ERRNO, rc * 10 - 1);
+
+ if (rc == 0)
+ {
+ logf("Found root item: %d %s", item, entry->d_name);
+ item++;
+ }
+ }
+ else
+ {
+ get_mount_point_entry(IF_MV(item,) entry);
+ item++;
+ rc = 1;
+ logf("Found mp item:%d %s", item, entry->d_name);
+ }
+
+ scanp->item = item;
+
+file_eod:
+#ifdef HAVE_DIRCACHE
+ if (rc == 0)
+ empty_dirent(entry);
+#endif
+file_error:
+ logf("%s: status: %d", __func__, rc);
+ return rc;
+}
+
+/* opens a stream to enumerate items in a namespace container */
+int ns_open_stream(const char *path, unsigned int callflags,
+ struct filestr_base *stream, struct ns_scan_info *scanp)
+{
+ logf("%s: path: %s", __func__, path);
+ /* stream still needs synchronization even if we don't have a stream */
+ static struct mutex no_contents_mtx SHAREDBSS_ATTR;
+
+ int rc = open_stream_internal(path, callflags, stream, NULL);
+ if (rc < 0)
+ FILE_ERROR(ERRNO, rc * 10 - 1);
+
+ scanp->item = rc > 1 ? 0 : -1;
+
+ if (stream->flags & FDO_BUSY)
+ {
+ /* root contents are mounted */
+ fat_rewind(&stream->fatstr);
+ }
+ else
+ {
+ /* root contents not mounted */
+ mutex_init(&no_contents_mtx);
+ stream->mtx = &no_contents_mtx;
+ }
+
+ ns_dirscan_rewind(scanp);
+
+ rc = 0;
+file_error:
+ return rc;
+}
diff --git a/firmware/common/rectangle.c b/firmware/common/rectangle.c
new file mode 100644
index 0000000000..3ce09f5145
--- /dev/null
+++ b/firmware/common/rectangle.c
@@ -0,0 +1,141 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "rectangle.h"
+#include "system.h"
+
+bool rect_contains(const struct rectangle *ra, const struct rectangle *rb)
+{
+ return ra->x <= rb->x && rb->x + rb->w < ra->x + ra->w &&
+ ra->y <= rb->y && rb->y + rb->h < ra->y + ra->h;
+}
+
+bool rect_overlap(const struct rectangle *ra, const struct rectangle *rb)
+{
+ return ra->x + ra->w > rb->x && rb->x + rb->w > ra->x &&
+ ra->y + ra->h > rb->y && rb->y + rb->h > ra->y;
+}
+
+bool rect_intersect(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out)
+{
+ if (!rect_valid(ra) || !rect_valid(rb))
+ return false;
+
+ int x = MAX(ra->x, rb->x);
+ int y = MAX(ra->y, rb->y);
+ int w = MIN(ra->x + ra->w, rb->x + rb->w) - x;
+ int h = MIN(ra->y + ra->h, rb->y + rb->h) - y;
+
+ r_out->x = x;
+ r_out->y = y;
+ r_out->w = w;
+ r_out->h = h;
+
+ return w > 0 && h > 0;
+}
+
+void rect_union(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out)
+{
+ if (!rect_valid(ra)) {
+ *r_out = *rb;
+ } else if (!rect_valid(rb)) {
+ *r_out = *ra;
+ } else {
+ int x = MIN(ra->x, rb->x);
+ int y = MIN(ra->y, rb->y);
+ int w = MAX(ra->x + ra->w, rb->x + rb->w) - x;
+ int h = MAX(ra->y + ra->h, rb->y + rb->h) - y;
+
+ r_out->x = x;
+ r_out->y = y;
+ r_out->w = w;
+ r_out->h = h;
+ }
+}
+
+int rect_difference(const struct rectangle *rect,
+ const struct rectangle *rsub,
+ struct rectangle rects_out[4])
+{
+ if (!rect_valid(rect) || !rect_valid(rsub)) {
+ rects_out[0] = *rect;
+ return 1;
+ }
+
+ int x0 = MAX(rect->x, rsub->x);
+ int y0 = MAX(rect->y, rsub->y);
+ int x1 = MIN(rect->x + rect->w, rsub->x + rsub->w);
+ int y1 = MIN(rect->y + rect->h, rsub->y + rsub->h);
+
+ /* no intersection */
+ if (x1 - x0 <= 0 || y1 - y0 <= 0) {
+ rects_out[0] = *rect;
+ return 1;
+ }
+
+ /* rect
+ * +-------------+
+ * | . 2 . |
+ * | +-----+ |
+ * | 0 |rsub | 1 |
+ * | +-----+ |
+ * | . 3 . |
+ * +-------------+
+ */
+
+ int n = 0;
+
+ if (rect->x < x0) {
+ rects_out[n].x = rect->x;
+ rects_out[n].y = rect->y;
+ rects_out[n].w = x0 - rect->x;
+ rects_out[n].h = rect->h;
+ n++;
+ }
+
+ if (x1 < rect->x + rect->w) {
+ rects_out[n].x = x1;
+ rects_out[n].y = rect->y;
+ rects_out[n].w = rect->x + rect->w - x1;
+ rects_out[n].h = rect->h;
+ n++;
+ }
+
+ if (rect->y < y0) {
+ rects_out[n].x = x0;
+ rects_out[n].y = rect->y;
+ rects_out[n].w = x1 - x0;
+ rects_out[n].h = y0 - rect->y;
+ n++;
+ }
+
+ if (y1 < rect->y + rect->h) {
+ rects_out[n].x = x0;
+ rects_out[n].y = y1;
+ rects_out[n].w = x1 - x0;
+ rects_out[n].h = rect->y + rect->h - y1;
+ n++;
+ }
+
+ return n;
+}
diff --git a/firmware/common/strlcat.c b/firmware/common/strlcat.c
index 783ea4daba..f1f0031f06 100644
--- a/firmware/common/strlcat.c
+++ b/firmware/common/strlcat.c
@@ -16,7 +16,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <string.h>
+#include "string-extra.h"
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
@@ -29,9 +29,7 @@ size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
- const char *s = src;
- size_t n = siz;
- size_t dlen;
+ size_t dlen, n = siz;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
@@ -39,17 +37,6 @@ strlcat(char *dst, const char *src, size_t siz)
dlen = d - dst;
n = siz - dlen;
- if (n == 0)
- return(dlen + strlen(s));
- while (*s != '\0') {
- if (n != 1) {
- *d++ = *s;
- n--;
- }
- s++;
- }
- *d = '\0';
-
- return(dlen + (s - src)); /* count does not include NUL */
+ return strlcpy(dst + dlen, src, n) + dlen;
}
diff --git a/firmware/common/strlcpy.c b/firmware/common/strlcpy.c
index e320649140..5107ea1207 100644
--- a/firmware/common/strlcpy.c
+++ b/firmware/common/strlcpy.c
@@ -1,51 +1,39 @@
-/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
*
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * Copyright (C) 2022 William Wilgus
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
#include <string.h>
+#include "strmemccpy.h"
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
-size_t
-strlcpy(char *dst, const char *src, size_t siz)
+size_t strlcpy(char *dst, const char *src, size_t siz)
{
- char *d = dst;
- const char *s = src;
- size_t n = siz;
-
- /* Copy as many bytes as will fit */
- if (n != 0) {
- while (--n != 0) {
- if ((*d++ = *s++) == '\0')
- break;
- }
- }
+ /* Copy as many bytes as will fit */
+ char *d = strmemccpy(dst, src, siz);
+ if (d)
+ return (d - dst - 1); /* count does not include NUL */
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0) {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++)
- ;
- }
-
- return(s - src - 1); /* count does not include NUL */
+ /* Not enough room in dst, traverse rest of src */
+ return(siz + strlen(src+siz)); /* count does not include NUL */
}
-
diff --git a/firmware/common/strmemccpy.c b/firmware/common/strmemccpy.c
new file mode 100644
index 0000000000..830907f55e
--- /dev/null
+++ b/firmware/common/strmemccpy.c
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 William Wilgus
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/* (firmware/common/strmemccpy.c) */
+#include "string-extra.h"
+
+/* copies src to a buffer of len bytes stopping after
+ * len or the first NULL (\0) in src
+ * NULL terminates except when len = 0
+ * If len was exceeded NULL is returned otherwise returns
+ * a pointer to the first byte following the NULL in dst.
+*/
+char * strmemccpy(char *dst, const char *src, size_t len)
+{
+ char * ret = (char *)memccpy(dst, src, '\0', len);
+ if (ret == NULL && len > 0)
+ {
+ dst[len - 1] = '\0';
+ }
+ return ret;
+}
diff --git a/firmware/common/strnatcmp.c b/firmware/common/strnatcmp.c
index 0084ff3582..d77f7e995e 100644
--- a/firmware/common/strnatcmp.c
+++ b/firmware/common/strnatcmp.c
@@ -122,7 +122,8 @@ compare_left(char const *a, char const *b)
return 0;
}
-static int strnatcmp0(char const *a, char const *b, int fold_case)
+static int strnatcmp0(char const *a, char const *b,
+ int (*cmp_fn)(const char*, const char*))
{
int ai, bi;
int ca, cb;
@@ -150,13 +151,10 @@ static int strnatcmp0(char const *a, char const *b, int fold_case)
if (!ca && !cb) {
/* The strings compare the same. Call str[case]cmp() to ensure
consistent results. */
- if(fold_case)
- return strcasecmp(a,b);
- else
- return strcmp(a,b);
+ return cmp_fn(a,b);
}
- if (fold_case) {
+ if (cmp_fn == &strcasecmp) {
ca = nat_unify_case(ca);
cb = nat_unify_case(cb);
}
@@ -170,14 +168,12 @@ static int strnatcmp0(char const *a, char const *b, int fold_case)
}
}
-
-
int strnatcmp(const char *a, const char *b) {
- return strnatcmp0(a, b, 0);
+ return strnatcmp0(a, b, strcmp);
}
/* Compare, recognizing numeric string and ignoring case. */
int strnatcasecmp(const char *a, const char *b) {
- return strnatcmp0(a, b, 1);
+ return strnatcmp0(a, b, &strcasecmp);
}
diff --git a/firmware/common/strptokspn.c b/firmware/common/strptokspn.c
new file mode 100644
index 0000000000..16aafc66ef
--- /dev/null
+++ b/firmware/common/strptokspn.c
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 by William WIlgus
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied. *
+ ****************************************************************************/
+
+#include "config.h"
+
+#include <stddef.h>
+#include <string.h>
+#include "strtok_r.h"
+/* strptokspn_r is a custom implementation of strtok_r that does NOT modify
+ * the source string.
+ *
+ * strptokspn_r reads ptr as a series of zero or more tokens,
+ * and sep as delimiters of the tokens
+ * The tokens can be separated by one or more of the delimiters
+ * first call searches for the first token skipping over any leading delimiters.
+ * Returns pointer to first token
+ * Pointer *len contains the span to the first delimeter
+ * (this would be the resulting strlen had token actually been NULL terminated)
+ * Pointer **end contains pointer to first character after the last delimeter
+ *
+ * When strptokspn_r is called with a ptr == NULL, the next token is read from
+ * Pointer **end
+ *
+ * Note the returned token is NOT NULL terminated by the function as in strtok_r
+ * However the caller can use ret[len] = '\0'; to emulate a call to strtok_r
+*/
+const char *strptokspn_r(const char *ptr, const char *sep, size_t *len, const char **end)
+{
+ if (ptr == NULL) /* we got NULL input so then we get last position instead */
+ {
+ ptr = *end;
+ }
+
+ /* pass all letters that are including in the separator string */
+ while (*ptr && strchr(sep, *ptr))
+ ++ptr;
+
+ if (*ptr != '\0')
+ {
+ /* so this is where the next piece of string starts */
+ const char *start = ptr;
+ *len = strcspn(ptr, sep); /* Get span until any sep character in string */
+ *end = ptr + *len;
+ if (**end) /* the end is not a null byte */
+ ++*end;
+ return start;
+ }
+ return NULL;
+}
+
+#if !defined(HAVE_STRTOK_R)
+char * strtok_r(char *ptr, const char *sep, char **end)
+{
+ size_t len;
+ char * ret = (char*) strptokspn_r((const char*)ptr, sep, &len, (const char**) end);
+ if (ret)
+ ret[len] = '\0';
+ return ret;
+}
+#endif
diff --git a/firmware/common/structec.c b/firmware/common/structec.c
deleted file mode 100644
index fb13eaab51..0000000000
--- a/firmware/common/structec.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Miika Pekkarinen
- *
- * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include <ctype.h>
-#include <string.h>
-#include <inttypes.h>
-#include "structec.h"
-#include "system.h"
-#include "file.h"
-
-#define MAX_STRUCT_SIZE 128
-
-/**
- * Convert the struct endianess with the instructions provided.
- *
- * For example:
- * struct test {
- * long par1;
- * short par2;
- * short par3;
- * };
- *
- * structec_convert(instance_of_test, "lss", sizeof(struct test), true);
- *
- * Structures to be converted must be properly padded.
- *
- * @param structure Pointer to the struct being converted.
- * @param ecinst Instructions how to do the endianess conversion.
- * @param count Number of structures to write
- * @param enable Conversion is not made unless this is true.
- */
-void structec_convert(void *structure, const char *ecinst,
- long count, bool enable)
-{
- const char *ecinst_ring = ecinst;
- char *buf = (char *)structure;
-
- if (!enable)
- return;
-
- while (count > 0)
- {
- switch (*ecinst_ring)
- {
- /* Swap nothing. */
- case 'c':
- {
- buf++;
- break;
- }
-
- /* Swap 2 bytes. */
- case 's':
- {
- uint16_t *data = (uint16_t *)buf;
- *data = swap16(*data);
- buf += 2;
- break;
- }
-
- /* Swap 4 bytes. */
- case 'l':
- {
- uint32_t *data = (uint32_t *)buf;
- *data = swap32(*data);
- buf += 4;
- break;
- }
-
- /* Skip N bytes, idea taken from metadata.c */
- default:
- {
- if (isdigit(*ecinst_ring))
- buf += (*ecinst_ring - '0');
-
- break;
- }
- }
-
- ecinst_ring++;
- if (*ecinst_ring == '\0')
- {
- ecinst_ring = ecinst;
- count--;
- }
- }
-}
-
-/**
- * Determines the size of a struct in bytes by using endianess correction
- * string format.
- *
- * @param ecinst endianess correction string.
- * @return length of the struct in bytes.
- */
-static size_t structec_size(const char *ecinst)
-{
- size_t size = 0;
-
- do
- {
- switch (*ecinst)
- {
- case 'c': size += 1; break;
- case 's': size += 2; break;
- case 'l': size += 4; break;
- default:
- if (isdigit(*ecinst))
- size += (*ecinst - '0');
- }
- } while (*(++ecinst) != '\0');
-
- return size;
-}
-
-/**
- * Reads endianess corrected structure members from the given file.
- *
- * @param fd file descriptor of the file being read.
- * @param buf endianess corrected data is placed here.
- * @param scount the number of struct members to read.
- * @param ecinst endianess correction string.
- * @param ec if true, endianess correction is enabled.
- */
-ssize_t ecread(int fd, void *buf, size_t scount, const char *ecinst, bool ec)
-{
- ssize_t ret;
- size_t member_size = structec_size(ecinst);
-
- ret = read(fd, buf, scount * member_size);
- structec_convert(buf, ecinst, scount, ec);
-
- return ret;
-}
-
-/**
- * Writes endianess corrected structure members to the given file.
- *
- * @param fd file descriptor of the file being written to.
- * @param buf endianess corrected data is read here.
- * @param scount the number of struct members to write.
- * @param ecinst endianess correction string.
- * @param ec if true, endianess correction is enabled.
- */
-ssize_t ecwrite(int fd, const void *buf, size_t scount,
- const char *ecinst, bool ec)
-{
- char tmp[MAX_STRUCT_SIZE];
- ssize_t member_size = structec_size(ecinst);
-
- if (ec)
- {
- const char *p = (const char *)buf;
- int maxamount = (int)(MAX_STRUCT_SIZE / member_size);
- int i;
-
- for (i = 0; i < (long)scount; i += maxamount)
- {
- long amount = MIN((int)scount-i, maxamount);
-
- memcpy(tmp, p, member_size * amount);
- structec_convert(tmp, ecinst, amount, true);
- ssize_t ret = write(fd, tmp, amount * member_size);
-
- if(ret != amount * member_size)
- return ret;
-
- p += member_size * amount;
- }
-
- return scount * member_size;
- }
-
- return write(fd, buf, scount * member_size);
-}
diff --git a/firmware/common/unicode.c b/firmware/common/unicode.c
index f0f663f712..1ed2e5e49d 100644
--- a/firmware/common/unicode.c
+++ b/firmware/common/unicode.c
@@ -166,15 +166,23 @@ static unsigned short default_cp_table_buf[MAX_CP_TABLE_SIZE+1];
default_cp_table_buf
#define cp_table_free(handle) \
do {} while (0)
-#define cp_table_alloc(filename, size, opsp) \
+#define cp_table_alloc(size, opsp) \
({ (void)(opsp); 1; })
+#define cp_table_pin(handle) \
+ do { (void)handle; } while(0)
+#define cp_table_unpin(handle) \
+ do { (void)handle; } while(0)
#else
-#define cp_table_alloc(filename, size, opsp) \
- core_alloc_ex((filename), (size), (opsp))
+#define cp_table_alloc(size, opsp) \
+ core_alloc_ex((size), (opsp))
#define cp_table_free(handle) \
core_free(handle)
#define cp_table_get_data(handle) \
core_get_data(handle)
+#define cp_table_pin(handle) \
+ core_pin(handle)
+#define cp_table_unpin(handle) \
+ core_unpin(handle)
#endif
static const unsigned char utf8comp[6] =
@@ -191,21 +199,8 @@ static inline void cptable_tohw16(uint16_t *buf, unsigned int count)
(void)buf; (void)count;
}
-static int move_callback(int handle, void *current, void *new)
-{
- /* we don't keep a pointer but we have to stop it if this applies to a
- buffer not yet swapped-in since it will likely be in use in an I/O
- call */
- return (handle != default_cp_handle || default_cp_table_ref != 0) ?
- BUFLIB_CB_CANNOT_MOVE : BUFLIB_CB_OK;
- (void)current; (void)new;
-}
-
static int alloc_and_load_cp_table(int cp, void *buf)
{
- static struct buflib_callbacks ops =
- { .move_callback = move_callback };
-
/* alloc and read only if there is an associated file */
const char *filename = cp_info[cp].filename;
if (!filename)
@@ -228,13 +223,17 @@ static int alloc_and_load_cp_table(int cp, void *buf)
!(size % (off_t)sizeof (uint16_t))) {
/* if the buffer is provided, use that but don't alloc */
- int handle = buf ? 0 : cp_table_alloc(filename, size, &ops);
- if (handle > 0)
+ int handle = buf ? 0 : cp_table_alloc(size, NULL);
+ if (handle > 0) {
+ cp_table_pin(handle);
buf = cp_table_get_data(handle);
+ }
if (buf && read(fd, buf, size) == size) {
close(fd);
cptable_tohw16(buf, size / sizeof (uint16_t));
+ if (handle > 0)
+ cp_table_unpin(handle);
return handle;
}
diff --git a/firmware/common/zip.c b/firmware/common/zip.c
index 9512d6c239..2d560e472e 100644
--- a/firmware/common/zip.c
+++ b/firmware/common/zip.c
@@ -20,8 +20,7 @@
****************************************************************************/
#include "zip.h"
-#include <string.h>
-#include "strlcpy.h"
+#include "string-extra.h"
#include "file.h"
#include "dir.h"
#include "system.h"
@@ -32,9 +31,7 @@
#include "crc32.h"
#include "rbendian.h"
-#define zip_core_alloc(N) core_alloc_ex("zip",(N),&dummy_ops)
-
-static struct buflib_callbacks dummy_ops;
+#define zip_core_alloc(N) core_alloc_ex((N),&buflib_ops_locked)
enum {
ZIP_SIG_ED = 0x06054b50,
@@ -239,8 +236,7 @@ static int zip_read_ed(struct zip* z) {
rv = 0;
bail:
- if (mem_handle >= 0)
- core_free(mem_handle);
+ core_free(mem_handle);
return rv;
}
@@ -339,10 +335,9 @@ static int zip_read_cd(struct zip* z, bool use_cb) {
rv = 0;
bail:
- if (rv != 0 && cds_handle >= 0)
+ if (rv != 0)
core_free(cds_handle);
- if (mem_handle >= 0)
- core_free(mem_handle);
+ core_free(mem_handle);
return rv;
}
@@ -499,8 +494,7 @@ static int zip_read_entries(struct zip* z) {
rv = 0;
bail:
- if (mem_handle >= 0)
- core_free(mem_handle);
+ core_free(mem_handle);
return rv;
}
@@ -756,10 +750,8 @@ struct zip* zip_open(const char* name, bool try_mem) {
bail:
if (file >= 0)
close(file);
- if (mem_handle >= 0)
- core_free(mem_handle);
- if (zip_handle >= 0)
- core_free(zip_handle);
+ core_free(mem_handle);
+ core_free(zip_handle);
return NULL;
}
@@ -877,8 +869,7 @@ void zip_close(struct zip* z) {
z->close(z);
- if (z->cds_handle >= 0)
- core_free(z->cds_handle);
+ core_free(z->cds_handle);
core_free(z->zip_handle);
}
diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c
index bf2f8e8298..948911b973 100644
--- a/firmware/core_alloc.c
+++ b/firmware/core_alloc.c
@@ -35,8 +35,11 @@ unsigned char *audiobufend = audiobuffer + sizeof(audiobuffer);
extern unsigned char *audiobufend;
#endif
+#ifdef BUFLIB_DEBUG_PRINT
/* debug test alloc */
static int test_alloc;
+#endif
+
void core_allocator_init(void)
{
unsigned char *start = ALIGN_UP(audiobuffer, sizeof(intptr_t));
@@ -51,16 +54,9 @@ void core_allocator_init(void)
buflib_init(&core_ctx, start, audiobufend - start);
- test_alloc = core_alloc("test", 112);
-}
-
-bool core_test_free(void)
-{
- bool ret = test_alloc > 0;
- if (ret)
- test_alloc = core_free(test_alloc);
-
- return ret;
+#ifdef BUFLIB_DEBUG_PRINT
+ test_alloc = core_alloc(112);
+#endif
}
/* Allocate memory in the "core" context. See documentation
@@ -69,14 +65,14 @@ bool core_test_free(void)
* Note: Buffers allocated by this functions are movable.
* Don't pass them to functions that call yield()
* like disc input/output. */
-int core_alloc(const char* name, size_t size)
+int core_alloc(size_t size)
{
- return buflib_alloc_ex(&core_ctx, size, name, NULL);
+ return buflib_alloc_ex(&core_ctx, size, NULL);
}
-int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops)
+int core_alloc_ex(size_t size, struct buflib_callbacks *ops)
{
- return buflib_alloc_ex(&core_ctx, size, name, ops);
+ return buflib_alloc_ex(&core_ctx, size, ops);
}
size_t core_available(void)
@@ -94,9 +90,9 @@ int core_free(int handle)
return buflib_free(&core_ctx, handle);
}
-int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops)
+int core_alloc_maximum(size_t *size, struct buflib_callbacks *ops)
{
- return buflib_alloc_maximum(&core_ctx, name, size, ops);
+ return buflib_alloc_maximum(&core_ctx, size, ops);
}
bool core_shrink(int handle, void* new_start, size_t new_size)
@@ -104,23 +100,43 @@ bool core_shrink(int handle, void* new_start, size_t new_size)
return buflib_shrink(&core_ctx, handle, new_start, new_size);
}
-const char* core_get_name(int handle)
+void core_pin(int handle)
+{
+ buflib_pin(&core_ctx, handle);
+}
+
+void core_unpin(int handle)
+{
+ buflib_unpin(&core_ctx, handle);
+}
+
+unsigned core_pin_count(int handle)
{
- const char *name = buflib_get_name(&core_ctx, handle);
- return name ?: "<anonymous>";
+ return buflib_pin_count(&core_ctx, handle);
}
+#ifdef BUFLIB_DEBUG_PRINT
int core_get_num_blocks(void)
{
return buflib_get_num_blocks(&core_ctx);
}
-void core_print_block_at(int block_num, char* buf, size_t bufsize)
+bool core_print_block_at(int block_num, char* buf, size_t bufsize)
+{
+ return buflib_print_block_at(&core_ctx, block_num, buf, bufsize);
+}
+
+bool core_test_free(void)
{
- buflib_print_block_at(&core_ctx, block_num, buf, bufsize);
+ bool ret = test_alloc > 0;
+ if (ret)
+ test_alloc = core_free(test_alloc);
+
+ return ret;
}
+#endif
-#ifdef DEBUG
+#ifdef BUFLIB_DEBUG_CHECK_VALID
void core_check_valid(void)
{
buflib_check_valid(&core_ctx);
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index ec99239169..1c85b7bd5f 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -155,7 +155,8 @@ static inline bool ata_sleep_timed_out(void)
static inline void schedule_ata_power_off(void)
{
#ifdef HAVE_ATA_POWER_OFF
- power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT;
+ if (!ata_disk_can_poweroff())
+ power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT;
#endif
}
@@ -389,28 +390,44 @@ static ICODE_ATTR void copy_write_sectors(const unsigned char* buf,
int ata_disk_isssd(void)
{
/*
- offset 217 is "Nominal Rotation rate"
- 0x0000 == Not reported
- 0x0001 == Solid State
- 0x0401 -> 0xffe == RPM
- All others reserved
+ Offset 217 is "Nominal Rotation rate"
+ 0x0000 == Not reported
+ 0x0001 == Solid State
+ 0x0401 -> 0xffe == RPM
+ All others reserved
- Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either.
+ Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either.
+ However, this is a relatively recent change, and we can't rely on it,
+ especially for the FC1307A CF->SD adapters!
- However, this is a very recent change, and we can't rely on it,
- especially for the FC1307A CF->SD adapters.
+ Offset 168 is "Nominal Form Factor"
+ all values >= 0x06 are guaranteed to be Solid State (mSATA, m.2, etc)
- So we have to resort to other heuristics.
+ Offset 83 b2 and/or 86 b2 is set to show device implementes CFA commands
- offset 83 b2 is set to show device implementes CFA commands
- offset 0 is 0x848a for CF, but that's not guaranteed, because reasons.
+ Offset 169 b0 is set to show device implements TRIM.
- These don't guarantee this is an SSD but it's better than nothing.
+ Offset 0 is 0x848a for CF, but that's not guaranteed, because reasons.
*/
- return (identify_info[83] & (1<<2) ||
+ return (identify_info[83] & (1<<2) || identify_info[86] & (1<<2) ||
+ (identify_info[168] & 0x0f) >= 0x06 ||
+ identify_info[169] & (1<<0) ||
identify_info[217] == 0x0001 || identify_info[217] == 0x0100);
}
+int ata_disk_can_poweroff(void)
+{
+ /* Some SSDs don't like getting powered off, presumably because
+ in the real world they're not in removable form factors and
+ don't expect to have power removed.
+
+ In particular, mSATA, m.2, and MicroSSD are suspect.
+ */
+
+ return ((identify_info[168] & 0x0f) < 0x06 ||
+ (identify_info[168] & 0x0f) > 0x08);
+}
+
static int ata_transfer_sectors(unsigned long start,
int incount,
void* inbuf,
diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c
index 8206bcf7e0..8d2b9f44f3 100644
--- a/firmware/drivers/audio/ak4376.c
+++ b/firmware/drivers/audio/ak4376.c
@@ -242,11 +242,29 @@ void ak4376_set_freqmode(int fsel, int mult, int power_mode)
/* Handle the DSMLP bit in the MODE_CTRL register */
int mode_ctrl = 0x00;
- if(power_mode || hw_freq_sampr[fsel] <= SAMPR_12)
+ if(power_mode == SOUND_LOW_POWER || hw_freq_sampr[fsel] <= SAMPR_12)
mode_ctrl |= 0x40;
+ /* Handle the LPMODE bit */
+ int pwr3 = power_mode == SOUND_LOW_POWER ? 0x11 : 0x01;
+
+ /* The datasheet says the HP amp must be powered down before changing
+ * the operating mode of the DAC or HP amp. I'm assuming this means
+ * the amp must be shut down when changing DSMLP or LPMODE. */
+ int cur_mode_ctrl = ak4376_read(AK4376_REG_MODE_CTRL);
+ int cur_pwr3 = ak4376_read(AK4376_REG_PWR3);
+ bool disable_amp = mode_ctrl != cur_mode_ctrl || pwr3 != cur_pwr3;
+
/* Program the new settings */
+ if(disable_amp)
+ ak4376_write(AK4376_REG_PWR4, 0x00);
+
ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
- ak4376_write(AK4376_REG_PWR3, power_mode ? 0x11 : 0x01);
+ ak4376_write(AK4376_REG_PWR3, pwr3);
+
+ if(disable_amp) {
+ ak4376_write(AK4376_REG_PWR4, 0x03);
+ mdelay(26);
+ }
}
diff --git a/firmware/drivers/audio/eros_qn_codec.c b/firmware/drivers/audio/eros_qn_codec.c
index da50d62fe5..095b3b5305 100644
--- a/firmware/drivers/audio/eros_qn_codec.c
+++ b/firmware/drivers/audio/eros_qn_codec.c
@@ -26,64 +26,37 @@
#include "audiohw.h"
#include "settings.h"
#include "pcm_sw_volume.h"
-#include "gpio-x1000.h"
-static long int vol_l_hw = 0;
-static long int vol_r_hw = 0;
+#include "gpio-x1000.h"
-/* internal: mute the headphone amp. 0 - unmuted, 1 - muted */
-void audiohw_mute_hp(int mute);
+static long int vol_l_hw = PCM5102A_VOLUME_MIN;
+static long int vol_r_hw = PCM5102A_VOLUME_MIN;
+int es9018k2m_present_flag = 0;
-void pcm5102_set_outputs(void)
+void eros_qn_set_outputs(void)
{
audiohw_set_volume(vol_l_hw, vol_r_hw);
}
-/* this makes less sense here than it does in the audiohw-*.c file,
- * but we need access to settings.h */
-void audiohw_set_volume(int vol_l, int vol_r)
+void eros_qn_set_last_vol(long int vol_l, long int vol_r)
{
- int l, r;
-
vol_l_hw = vol_l;
vol_r_hw = vol_r;
+}
- l = vol_l;
- r = vol_r;
-
-#if (defined(HAVE_HEADPHONE_DETECTION) && defined(HAVE_LINEOUT_DETECTION))
- /* make sure headphones aren't present - don't want to
- * blow out our eardrums cranking it to full */
- if (lineout_inserted() && !headphones_inserted())
- {
- l = r = global_settings.volume_limit * 10;
-
- /* mute the headphone amp if not plugged in */
- audiohw_mute_hp(1);
- }
- else
- {
- /* unmute the headphone amp when plugged in */
- audiohw_mute_hp(0);
- l = vol_l;
- r = vol_r;
- }
-#endif
-
- l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (l / 20);
- r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (r / 20);
-
- pcm_set_master_volume(l, r);
+int eros_qn_get_volume_limit(void)
+{
+ return (global_settings.volume_limit * 10);
}
-void audiohw_mute_hp(int mute)
+void eros_qn_switch_output(int select)
{
- if (mute == 0)
+ if (select == 0)
{
- gpio_set_level(GPIO_MAX97220_SHDN, 1);
+ gpio_set_level(GPIO_STEREOSW_SEL, 0);
}
else
{
- gpio_set_level(GPIO_MAX97220_SHDN, 0);
+ gpio_set_level(GPIO_STEREOSW_SEL, 1);
}
-}
+} \ No newline at end of file
diff --git a/firmware/drivers/audio/es9018.c b/firmware/drivers/audio/es9018.c
index 89e8c1d46f..6a73f7a2d3 100644
--- a/firmware/drivers/audio/es9018.c
+++ b/firmware/drivers/audio/es9018.c
@@ -25,8 +25,8 @@
#include "audio.h"
#include "audiohw.h"
-/* NOTE: The register names are not known, as the register numbering
- listed in the ES9018 datasheet does not match what is described below.. */
+/* NOTE: This implementation is specifically for the ES9018K2M, which has a different register
+ * structure from the ES9018. */
static uint8_t reg0 = 0x00; /* System settings. Default value of register 0 */
static uint8_t reg1 = 0x80; /* Input settings. Manual input, I2S, 32-bit (?) */
diff --git a/firmware/drivers/audio/es9018k2m.c b/firmware/drivers/audio/es9018k2m.c
new file mode 100644
index 0000000000..3f14aaea39
--- /dev/null
+++ b/firmware/drivers/audio/es9018k2m.c
@@ -0,0 +1,161 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * Copyright (c) 2023 Dana Conrad
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "es9018k2m.h"
+#include "i2c-async.h"
+#include "action.h"
+
+//======================================================================================
+// ES9018K2M support stuff
+
+#ifndef ES9018K2M_BUS
+# error "No definition for ES9018K2M I2C bus!"
+#endif
+
+#ifndef ES9018K2M_ADDR
+# error "No definition for ES9018K2M I2C address!"
+#endif
+
+static int vol_tenthdb2hw(const int tdb)
+{
+ if (tdb < ES9018K2M_VOLUME_MIN) {
+ return 0xff;
+ } else if (tdb > ES9018K2M_VOLUME_MAX) {
+ return 0x00;
+ } else {
+ return (-tdb/5);
+ }
+}
+
+/* NOTE: This implementation is for the ES9018K2M specifically. */
+/* Register defaults from the datasheet. */
+/* These are basically just for reference. All defaults work out for us.
+ * static uint8_t reg0_system_settings = 0x00; // System settings. Default value of register 0
+ * static uint8_t reg1_input_configuration = 0x8C; // Input settings. I2S input, 32-bit
+ * static uint8_t reg4_automute_time = 0x00; // Automute time. Default = disabled
+ * static uint8_t reg5_automute_level = 0x68; // Automute level. Default is some level
+ * static uint8_t reg6_deemphasis = 0x4A; // Deemphasis. Default = disabled
+ */
+static uint8_t reg7_general_settings = 0x80; // General settings. Default sharp fir, pcm iir and unmuted
+/*
+ * static uint8_t reg8_gpio_configuration = 0x10; // GPIO configuration
+ * static uint8_t reg10_master_mode_control = 0x05; // Master Mode Control. Default value: master mode off
+ * static uint8_t reg11_channel_mapping = 0x02; // Channel Mapping. Default stereo is Ch1=left, Ch2=right
+ * static uint8_t reg12_dpll_settings = 0x5A; // DPLL Settings. Default = 5 for I2S, A for DSD
+ * static uint8_t reg13_thd_comp = 0x40; // THD Compensation
+ * static uint8_t reg14_softstart_settings = 0x8A; // Soft Start Settings
+ */
+static uint8_t reg21_gpio_input_selection = 0x00; // Oversampling filter. Default: oversampling ON
+
+#define bitSet(value, bit) ((value) |= (1UL << (bit)))
+#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
+
+static uint8_t vol_reg_l = ES9018K2M_REG15_VOLUME_L;
+static uint8_t reg15_vol_l = 0;
+i2c_descriptor vol_desc_l = {
+ .slave_addr = ES9018K2M_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_WRITE,
+ .buffer[0] = &vol_reg_l,
+ .count[0] = 1,
+ .buffer[1] = &reg15_vol_l,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+};
+
+static uint8_t vol_reg_r = ES9018K2M_REG16_VOLUME_R;
+static uint8_t reg16_vol_r = 0;
+i2c_descriptor vol_desc_r = {
+ .slave_addr = ES9018K2M_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_WRITE,
+ .buffer[0] = &vol_reg_r,
+ .count[0] = 1,
+ .buffer[1] = &reg16_vol_r,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+};
+
+void es9018k2m_set_volume_async(int vol_l, int vol_r)
+{
+ /* Queue writes to the DAC's volume.
+ * Note that this needs to be done asynchronously. From testing, calling
+ * synchronous writes from HP/LO detect causes hangs. */
+ reg15_vol_l = vol_tenthdb2hw(vol_l);
+ reg16_vol_r = vol_tenthdb2hw(vol_r);
+ i2c_async_queue(ES9018K2M_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, &vol_desc_l);
+ i2c_async_queue(ES9018K2M_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, &vol_desc_r);
+}
+
+void es9018k2m_set_filter_roll_off(int value)
+{
+ /* Note: the ihifi800 implementation manipulates
+ * bit 0 of reg21, but I think that is incorrect?
+ * Bit 2 is the bypass for the IIR digital filters,
+ * Whereas Bit 0 is the oversampling filter, which
+ * the datasheet seems to say should be left on. */
+
+ /* 0 = "Sharp" / Fast Rolloff (Default)
+ 1 = Slow Rolloff
+ 2 = "Short" / Minimum Phase
+ 3 = Bypass */
+ switch(value)
+ {
+ case 0:
+ bitClear(reg7_general_settings, 5);
+ bitClear(reg7_general_settings, 6);
+ bitClear(reg21_gpio_input_selection, 2);
+ break;
+ case 1:
+ bitSet(reg7_general_settings, 5);
+ bitClear(reg7_general_settings, 6);
+ bitClear(reg21_gpio_input_selection, 2);
+ break;
+ case 2:
+ bitClear(reg7_general_settings, 5);
+ bitSet(reg7_general_settings, 6);
+ bitClear(reg21_gpio_input_selection, 2);
+ break;
+ case 3:
+ bitSet(reg21_gpio_input_selection, 2);
+ break;
+ }
+ es9018k2m_write_reg(ES9018K2M_REG7_GENERAL_SETTINGS, reg7_general_settings);
+ es9018k2m_write_reg(ES9018K2M_REG21_GPIO_INPUT_SELECT, reg21_gpio_input_selection);
+}
+
+/* returns I2C_STATUS_OK upon success, I2C_STATUS_* errors upon error */
+int es9018k2m_write_reg(uint8_t reg, uint8_t val)
+{
+ return i2c_reg_write1(ES9018K2M_BUS, ES9018K2M_ADDR, reg, val);
+}
+
+/* returns register value, or -1 upon error */
+int es9018k2m_read_reg(uint8_t reg)
+{
+ return i2c_reg_read1(ES9018K2M_BUS, ES9018K2M_ADDR, reg);
+} \ No newline at end of file
diff --git a/firmware/drivers/audio/x1000-codec.c b/firmware/drivers/audio/x1000-codec.c
new file mode 100644
index 0000000000..c083882dab
--- /dev/null
+++ b/firmware/drivers/audio/x1000-codec.c
@@ -0,0 +1,286 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021-2022 Aidan MacDonald
+ * Copyright 2014 Ingenic Semiconductor Co.,Ltd
+ * cscheng <shicheng.cheng@ingenic.com>
+ * sound/soc/ingenic/icodec/icdc_d3.c
+ * ALSA SoC Audio driver -- ingenic internal codec (icdc_d3) driver
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "x1000-codec.h"
+#include "audiohw.h"
+#include "pcm_sampr.h"
+#include "kernel.h"
+#include "x1000/aic.h"
+
+static const uint8_t fsel_to_hw[HW_NUM_FREQ] = {
+ [0 ... HW_NUM_FREQ-1] = 0,
+ HW_HAVE_8_([HW_FREQ_8] = 0,)
+ HW_HAVE_11_([HW_FREQ_11] = 1,)
+ HW_HAVE_12_([HW_FREQ_12] = 2,)
+ HW_HAVE_16_([HW_FREQ_16] = 3,)
+ HW_HAVE_22_([HW_FREQ_22] = 4,)
+ HW_HAVE_24_([HW_FREQ_24] = 5,)
+ HW_HAVE_32_([HW_FREQ_32] = 6,)
+ HW_HAVE_44_([HW_FREQ_44] = 7,)
+ HW_HAVE_48_([HW_FREQ_48] = 8,)
+ HW_HAVE_88_([HW_FREQ_88] = 9,)
+ HW_HAVE_96_([HW_FREQ_96] = 10,)
+ HW_HAVE_176_([HW_FREQ_176] = 11,)
+ HW_HAVE_192_([HW_FREQ_192] = 12,)
+};
+
+void x1000_icodec_open(void)
+{
+ /* Ingenic does not specify any timing constraints for reset,
+ * let's do a 1ms delay for fun */
+ jz_writef(AIC_RGADW, ICRST(1));
+ mdelay(1);
+ jz_writef(AIC_RGADW, ICRST(0));
+
+ /* Power-up and initial config sequence */
+ static const uint8_t init_config[] = {
+ JZCODEC_CR_VIC, 0x03, /* ensure codec is powered off */
+ JZCODEC_CR_CK, 0x40, /* MCLK_DIV=1, SHUTDOWN_CLK=0, CRYSTAL=12Mhz */
+ JZCODEC_AICR_DAC, 0x13, /* ADWL=0 (16bit word length)
+ * SLAVE=0 (i2s master mode)
+ * SB_DAC=1 (power down DAC)
+ * AUDIOIF=3 (i2s mode) */
+ JZCODEC_AICR_ADC, 0x13, /* ADWL=0 (16bit word length)
+ * SB_ADC=1 (power down ADC)
+ * AUDIOIF=3 (i2s mode)
+ */
+ JZCODEC_CR_DAC, 0x91, /* DAC mute, power down */
+ JZCODEC_CR_DAC2, 0x38, /* DAC power down */
+ JZCODEC_CR_DMIC, 0x00, /* DMIC clock off */
+ JZCODEC_CR_MIC1, 0x30, /* MIC1 power down */
+ JZCODEC_CR_MIC2, 0x30, /* MIC2 power down */
+ JZCODEC_CR_ADC, 0x90, /* ADC mute, power down */
+ JZCODEC_ICR, 0x00, /* INT_FORM=0 (high level IRQ) */
+ JZCODEC_IMR, 0xff, /* Mask all interrupts */
+ JZCODEC_IMR2, 0xff,
+ JZCODEC_IFR, 0xff, /* Clear all interrupt flags */
+ JZCODEC_IFR2, 0xff,
+ };
+
+ for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
+ x1000_icodec_write(init_config[i], init_config[i+1]);
+
+ /* SB -> 0 (power up) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x02);
+ mdelay(250);
+
+ /* Initial gain setting. Apparently we need to set one gain and
+ * then set another after 10ms; afterward it can be changed freely. */
+ static const uint8_t gain_regs[] = {
+ JZCODEC_GCR_DACL,
+ JZCODEC_GCR_DACR,
+ JZCODEC_GCR_DACL2,
+ JZCODEC_GCR_DACR2,
+ JZCODEC_GCR_MIC1,
+ JZCODEC_GCR_MIC2,
+ JZCODEC_GCR_ADCL,
+ JZCODEC_GCR_ADCR,
+ };
+
+ for(size_t i = 0; i < ARRAYLEN(gain_regs); ++i)
+ x1000_icodec_write(gain_regs[i], 0);
+
+ mdelay(10);
+
+ for(size_t i = 0; i < ARRAYLEN(gain_regs); ++i)
+ x1000_icodec_write(gain_regs[i], 1);
+
+ /* SB_SLEEP -> 0 (exit sleep/standby mode) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x00);
+ mdelay(200);
+}
+
+void x1000_icodec_close(void)
+{
+ /* SB_SLEEP -> 1 (enable sleep mode) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x02);
+
+ /* SB -> 1 (power down) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x03);
+}
+
+/*
+ * DAC configuration
+ */
+
+void x1000_icodec_dac_frequency(int fsel)
+{
+ x1000_icodec_update(JZCODEC_FCR_DAC, 0x0f, fsel_to_hw[fsel]);
+}
+
+/*
+ * ADC configuration
+ */
+
+void x1000_icodec_adc_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_AICR_ADC, 0x10, en ? 0x00 : 0x10);
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x10, en ? 0x00 : 0x10);
+}
+
+void x1000_icodec_adc_mute(bool muted)
+{
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x80, muted ? 0x80 : 0x00);
+}
+
+void x1000_icodec_adc_mic_sel(int sel)
+{
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x40,
+ sel == JZCODEC_MIC_SEL_DIGITAL ? 0x40 : 0x00);
+}
+
+void x1000_icodec_adc_frequency(int fsel)
+{
+ x1000_icodec_update(JZCODEC_FCR_ADC, 0x0f, fsel_to_hw[fsel]);
+}
+
+void x1000_icodec_adc_highpass_filter(bool en)
+{
+ x1000_icodec_update(JZCODEC_FCR_ADC, 0x40, en ? 0x40 : 0x00);
+}
+
+void x1000_icodec_adc_gain(int gain_dB)
+{
+ if(gain_dB < X1000_ICODEC_ADC_GAIN_MIN)
+ gain_dB = X1000_ICODEC_ADC_GAIN_MIN;
+ else if(gain_dB > X1000_ICODEC_ADC_GAIN_MAX)
+ gain_dB = X1000_ICODEC_ADC_GAIN_MAX;
+
+ /* bit 7 = use the same gain for both channels */
+ x1000_icodec_write(JZCODEC_GCR_ADCL, 0x80 | gain_dB);
+}
+
+/*
+ * MIC1 configuration
+ */
+
+void x1000_icodec_mic1_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, 0x10, en ? 0x00 : 0x10);
+}
+
+void x1000_icodec_mic1_bias_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, 0x20, en ? 0x00 : 0x20);
+}
+
+void x1000_icodec_mic1_configure(int settings)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, JZCODEC_MIC1_CONFIGURE_MASK,
+ settings & JZCODEC_MIC1_CONFIGURE_MASK);
+}
+
+void x1000_icodec_mic1_gain(int gain_dB)
+{
+ if(gain_dB < X1000_ICODEC_MIC_GAIN_MIN)
+ gain_dB = X1000_ICODEC_MIC_GAIN_MIN;
+ else if(gain_dB > X1000_ICODEC_MIC_GAIN_MAX)
+ gain_dB = X1000_ICODEC_MIC_GAIN_MAX;
+
+ x1000_icodec_write(JZCODEC_GCR_MIC1, gain_dB/X1000_ICODEC_MIC_GAIN_STEP);
+}
+
+/*
+ * Mixer configuration
+ */
+
+void x1000_icodec_mixer_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIX, 0x80, en ? 0x80 : 0x00);
+}
+
+/*
+ * Register access
+ */
+
+static int x1000_icodec_read_direct(int reg)
+{
+ jz_writef(AIC_RGADW, ADDR(reg));
+ return jz_readf(AIC_RGDATA, DATA);
+}
+
+static void x1000_icodec_write_direct(int reg, int value)
+{
+ jz_writef(AIC_RGADW, ADDR(reg), DATA(value));
+ jz_writef(AIC_RGADW, RGWR(1));
+ while(jz_readf(AIC_RGADW, RGWR));
+}
+
+static void x1000_icodec_update_direct(int reg, int mask, int value)
+{
+ int x = x1000_icodec_read_direct(reg) & ~mask;
+ x |= value;
+ x1000_icodec_write_direct(reg, x);
+}
+
+static int x1000_icodec_read_indirect(int c_reg, int index)
+{
+ x1000_icodec_update_direct(c_reg, 0x7f, index & 0x3f);
+ return x1000_icodec_read_direct(c_reg+1);
+}
+
+static void x1000_icodec_write_indirect(int c_reg, int index, int value)
+{
+ /* NB: The X1000 programming manual says we should write the data
+ * register first, but in fact the control register needs to be
+ * written first (following Ingenic's Linux driver). */
+ x1000_icodec_update_direct(c_reg, 0x7f, 0x40 | (index & 0x3f));
+ x1000_icodec_write_direct(c_reg+1, value);
+}
+
+static void x1000_icodec_update_indirect(int c_reg, int index, int mask, int value)
+{
+ int x = x1000_icodec_read_indirect(c_reg, index) & ~mask;
+ x |= value;
+ x1000_icodec_write_indirect(c_reg, index, x);
+}
+
+int x1000_icodec_read(int reg)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_read_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg));
+ else
+ return x1000_icodec_read_direct(reg);
+}
+
+void x1000_icodec_write(int reg, int value)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_write_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg), value);
+ else
+ return x1000_icodec_write_direct(reg, value);
+}
+
+void x1000_icodec_update(int reg, int mask, int value)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_update_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg),
+ mask, value);
+ else
+ return x1000_icodec_update_direct(reg, mask, value);
+}
diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c
index fd1126dbbf..d59fbb2e3f 100644
--- a/firmware/drivers/axp-pmu.c
+++ b/firmware/drivers/axp-pmu.c
@@ -36,6 +36,8 @@ struct axp_adc_info {
uint8_t reg;
uint8_t en_reg;
uint8_t en_bit;
+ int8_t num;
+ int8_t den;
};
struct axp_supply_info {
@@ -49,17 +51,16 @@ struct axp_supply_info {
};
static const struct axp_adc_info axp_adc_info[NUM_ADC_CHANNELS] = {
- {0x56, AXP_REG_ADCENABLE1, 5}, /* ACIN_VOLTAGE */
- {0x58, AXP_REG_ADCENABLE1, 4}, /* ACIN_CURRENT */
- {0x5a, AXP_REG_ADCENABLE1, 3}, /* VBUS_VOLTAGE */
- {0x5c, AXP_REG_ADCENABLE1, 2}, /* VBUS_CURRENT */
- {0x5e, AXP_REG_ADCENABLE2, 7}, /* INTERNAL_TEMP */
- {0x62, AXP_REG_ADCENABLE1, 1}, /* TS_INPUT */
- {0x78, AXP_REG_ADCENABLE1, 7}, /* BATTERY_VOLTAGE */
- {0x7a, AXP_REG_ADCENABLE1, 6}, /* CHARGE_CURRENT */
- {0x7c, AXP_REG_ADCENABLE1, 6}, /* DISCHARGE_CURRENT */
- {0x7e, AXP_REG_ADCENABLE1, 1}, /* APS_VOLTAGE */
- {0x70, 0xff, 0}, /* BATTERY_POWER */
+ [ADC_ACIN_VOLTAGE] = {0x56, AXP_REG_ADCENABLE1, 1 << 5, 17, 10},
+ [ADC_ACIN_CURRENT] = {0x58, AXP_REG_ADCENABLE1, 1 << 4, 5, 8},
+ [ADC_VBUS_VOLTAGE] = {0x5a, AXP_REG_ADCENABLE1, 1 << 3, 17, 10},
+ [ADC_VBUS_CURRENT] = {0x5c, AXP_REG_ADCENABLE1, 1 << 2, 3, 8},
+ [ADC_INTERNAL_TEMP] = {0x5e, AXP_REG_ADCENABLE2, 1 << 7, 0, 0},
+ [ADC_TS_INPUT] = {0x62, AXP_REG_ADCENABLE1, 1 << 1, 4, 5},
+ [ADC_BATTERY_VOLTAGE] = {0x78, AXP_REG_ADCENABLE1, 1 << 7, 11, 10},
+ [ADC_CHARGE_CURRENT] = {0x7a, AXP_REG_ADCENABLE1, 1 << 6, 1, 2},
+ [ADC_DISCHARGE_CURRENT] = {0x7c, AXP_REG_ADCENABLE1, 1 << 6, 1, 2},
+ [ADC_APS_VOLTAGE] = {0x7e, AXP_REG_ADCENABLE1, 1 << 1, 7, 5},
};
static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = {
@@ -126,57 +127,8 @@ static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = {
#endif
};
-static struct axp_driver {
- int adc_enable;
- int chargecurrent_setting;
- int chip_id;
-} axp;
-
-static void axp_init_enabled_adcs(void)
-{
- axp.adc_enable = 0;
-
- /* Read chip ID, so we can display it on the debug screen.
- * This is undocumented but there's Linux driver code floating around
- * which suggests this should work for many AXP chips. */
- axp.chip_id = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHIP_ID);
-
- /* Read enabled ADCs from the hardware */
- uint8_t regs[2];
- int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR,
- AXP_REG_ADCENABLE1, 2, &regs[0]);
- if(rc != I2C_STATUS_OK)
- return;
-
- /* Parse registers to set ADC enable bits */
- const struct axp_adc_info* info = axp_adc_info;
- for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
- if(info[i].en_reg == 0xff)
- continue;
-
- if(regs[info[i].en_reg - AXP_REG_ADCENABLE1] & info[i].en_bit)
- axp.adc_enable |= 1 << i;
- }
-
- /* Handle battery power ADC */
- if((axp.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
- (axp.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
- axp.adc_enable |= (1 << ADC_BATTERY_POWER);
- }
-}
-
void axp_init(void)
{
- axp_init_enabled_adcs();
-
- /* We need discharge current ADC to reliably poll for a full battery */
- int bits = axp.adc_enable;
- bits |= (1 << ADC_DISCHARGE_CURRENT);
- axp_adc_set_enabled(bits);
-
- /* Read the maximum charging current */
- int value = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGECONTROL1);
- axp.chargecurrent_setting = (value < 0) ? -1 : (value & 0xf);
}
void axp_supply_set_voltage(int supply, int voltage)
@@ -305,22 +257,15 @@ int axp_adc_read(int adc)
int axp_adc_read_raw(int adc)
{
- /* Don't give a reading if the ADC is not enabled */
- if((axp.adc_enable & (1 << adc)) == 0)
- return INT_MIN;
-
/* Read the ADC */
- uint8_t buf[3];
- int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
+ uint8_t buf[2];
uint8_t reg = axp_adc_info[adc].reg;
- int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, count, &buf[0]);
+ int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, 2, &buf[0]);
if(rc != I2C_STATUS_OK)
return INT_MIN;
/* Parse the value */
- if(adc == ADC_BATTERY_POWER)
- return (buf[0] << 16) | (buf[1] << 8) | buf[2];
- else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
+ if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
return (buf[0] << 5) | (buf[1] & 0x1f);
else
return (buf[0] << 4) | (buf[1] & 0xf);
@@ -328,79 +273,33 @@ int axp_adc_read_raw(int adc)
int axp_adc_conv_raw(int adc, int value)
{
- switch(adc) {
- case ADC_ACIN_VOLTAGE:
- case ADC_VBUS_VOLTAGE:
- /* 0 mV ... 6.9615 mV, step 1.7 mV */
- return value * 17 / 10;
- case ADC_ACIN_CURRENT:
- /* 0 mA ... 2.5594 A, step 0.625 mA */
- return value * 5 / 8;
- case ADC_VBUS_CURRENT:
- /* 0 mA ... 1.5356 A, step 0.375 mA */
- return value * 3 / 8;
- case ADC_INTERNAL_TEMP:
- /* -144.7 C ... 264.8 C, step 0.1 C */
+ if(adc == ADC_INTERNAL_TEMP)
return value - 1447;
- case ADC_TS_INPUT:
- /* 0 mV ... 3.276 V, step 0.8 mV */
- return value * 4 / 5;
- case ADC_BATTERY_VOLTAGE:
- /* 0 mV ... 4.5045 V, step 1.1 mV */
- return value * 11 / 10;
- case ADC_CHARGE_CURRENT:
- case ADC_DISCHARGE_CURRENT:
- /* 0 mA to 4.095 A, step 0.5 mA */
- return value / 2;
- case ADC_APS_VOLTAGE:
- /* 0 mV to 5.733 V, step 1.4 mV */
- return value * 7 / 5;
- case ADC_BATTERY_POWER:
- /* 0 uW to 23.6404 W, step 0.55 uW */
- return value * 11 / 20;
- default:
- /* Shouldn't happen */
- return INT_MIN;
- }
-}
-
-int axp_adc_get_enabled(void)
-{
- return axp.adc_enable;
+ else
+ return axp_adc_info[adc].num * value / axp_adc_info[adc].den;
}
void axp_adc_set_enabled(int adc_bits)
{
- /* Ignore no-op */
- if(adc_bits == axp.adc_enable)
- return;
+ uint8_t xfer[3];
+ xfer[0] = 0;
+ xfer[1] = AXP_REG_ADCENABLE2;
+ xfer[2] = 0;
/* Compute the new register values */
const struct axp_adc_info* info = axp_adc_info;
- uint8_t regs[2] = {0, 0};
for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
- if(info[i].en_reg == 0xff)
+ if(!(adc_bits & (1 << i)))
continue;
- if(adc_bits & (1 << i))
- regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
- }
-
- /* These ADCs share an enable bit */
- if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
- adc_bits |= (1 << ADC_CHARGE_CURRENT);
- adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
- }
-
- /* Enable required bits for battery power ADC */
- if(adc_bits & (1 << ADC_BATTERY_POWER)) {
- regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
- regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
+ if(info[i].en_reg == AXP_REG_ADCENABLE1)
+ xfer[0] |= info[i].en_bit;
+ else
+ xfer[2] |= info[i].en_bit;
}
/* Update the configuration */
- i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 2, &regs[0]);
- axp.adc_enable = adc_bits;
+ i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 3, &xfer[0]);
}
int axp_adc_get_rate(void)
@@ -468,38 +367,26 @@ static const int chargecurrent_tbl[] = {
1080, 1160, 1240, 1320,
};
-static const int chargecurrent_tblsz = sizeof(chargecurrent_tbl)/sizeof(int);
-
-void axp_set_charge_current(int maxcurrent)
+void axp_set_charge_current(int current_mA)
{
- /* Find the charge current just higher than maxcurrent */
- int value = 0;
- while(value < chargecurrent_tblsz &&
- chargecurrent_tbl[value] <= maxcurrent)
- ++value;
-
- /* Select the next lower current, the greatest current <= maxcurrent */
- if(value >= chargecurrent_tblsz)
- value = chargecurrent_tblsz - 1;
- else if(value > 0)
- --value;
-
- /* Don't issue i2c write if desired setting is already in use */
- if(value == axp.chargecurrent_setting)
- return;
+ /* find greatest charging current not exceeding requested current */
+ unsigned int index = 0;
+ while(index < ARRAYLEN(chargecurrent_tbl)-1 &&
+ chargecurrent_tbl[index+1] <= current_mA)
+ ++index;
- /* Update register */
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
- AXP_REG_CHARGECONTROL1, 0x0f, value, NULL);
- axp.chargecurrent_setting = value;
+ AXP_REG_CHARGECONTROL1, 0x0f, index, NULL);
}
int axp_get_charge_current(void)
{
- if(axp.chargecurrent_setting < 0)
- return chargecurrent_tbl[0];
- else
- return chargecurrent_tbl[axp.chargecurrent_setting];
+ int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_CHARGECONTROL1);
+ if(ret < 0)
+ ret = 0;
+
+ return chargecurrent_tbl[ret & 0x0f];
}
void axp_power_off(void)
@@ -511,7 +398,6 @@ void axp_power_off(void)
#ifndef BOOTLOADER
enum {
- AXP_DEBUG_CHIP_ID,
AXP_DEBUG_BATTERY_STATUS,
AXP_DEBUG_INPUT_STATUS,
AXP_DEBUG_CHARGE_CURRENT,
@@ -585,12 +471,6 @@ static const char* axp_debug_menu_get_name(int item, void* data,
}
switch(item) {
- case AXP_DEBUG_CHIP_ID: {
- snprintf(buf, buflen, "Chip ID: %d (%02x) [Driver: AXP%d]",
- axp.chip_id, axp.chip_id, HAVE_AXP_PMU);
- return buf;
- } break;
-
case AXP_DEBUG_BATTERY_STATUS: {
switch(axp_battery_status()) {
case AXP_BATT_FULL:
diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c
index 9979bc0155..3220f18ed7 100644
--- a/firmware/drivers/button.c
+++ b/firmware/drivers/button.c
@@ -51,18 +51,14 @@ static long lastbtn; /* Last valid button status */
static long last_read; /* Last button status, for debouncing/filtering */
static intptr_t button_data; /* data value from last message dequeued */
static bool flipped; /* buttons can be flipped to match the LCD flip */
-#ifdef HAVE_BACKLIGHT
-static bool filter_first_keypress;
+
+#ifdef HAVE_BACKLIGHT /* Filter first keypress function pointer */
+static bool (*keypress_filter_fn)(int, int);
#ifdef HAVE_REMOTE_LCD
-static bool remote_filter_first_keypress;
+static bool (*remote_keypress_filter_fn)(int, int);
#endif
#endif /* HAVE_BACKLIGHT */
-#ifdef HAVE_HEADPHONE_DETECTION
-static bool phones_present = false;
-#endif
-#ifdef HAVE_LINEOUT_DETECTION
-static bool lineout_present = false;
-#endif
+
#ifdef HAVE_SW_POWEROFF
static bool enable_sw_poweroff = true;
#endif
@@ -72,7 +68,7 @@ static bool enable_sw_poweroff = true;
/* The next two make repeat "accelerate", which is nice for lists
* which begin to scroll a bit faster when holding until the
- * real list accerelation kicks in (this smooths acceleration).
+ * real list acceleration kicks in (this smooths acceleration).
*
* Note that touchscreen pointing events are not subject to this
* acceleration and always use REPEAT_INTERVAL_TOUCH. (Do repeat
@@ -86,19 +82,24 @@ static bool enable_sw_poweroff = true;
/* repeat interval for touch events */
#define REPEAT_INTERVAL_TOUCH (5*HZ/100)
-#ifdef HAVE_BUTTON_DATA
-static int button_read(int *data);
static int lastdata = 0;
-#else
-static int button_read(void);
-#endif
+static int button_read(int *data);
#ifdef HAVE_TOUCHSCREEN
static long last_touchscreen_touch;
#endif
-#if defined(HAVE_HEADPHONE_DETECTION)
-static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
+static void button_remote_post(void)
+{
+#if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR)
+ /* Post events for the remote control */
+ int btn = remote_control_rx();
+ if(btn)
+ button_try_post(btn, 0);
+#endif
+}
+
+#if defined(HAVE_HEADPHONE_DETECTION)
static int hp_detect_callback(struct timeout *tmo)
{
/* Try to post only transistions */
@@ -111,8 +112,6 @@ static int hp_detect_callback(struct timeout *tmo)
#endif
#if defined(HAVE_LINEOUT_DETECTION)
-static struct timeout lo_detect_timeout; /* Debouncer for lineout plug/unplug */
-
static int lo_detect_callback(struct timeout *tmo)
{
/* Try to post only transistions */
@@ -124,6 +123,34 @@ static int lo_detect_callback(struct timeout *tmo)
}
#endif
+static void check_audio_peripheral_state(void)
+{
+#if defined(HAVE_HEADPHONE_DETECTION)
+ static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
+ static bool phones_present = false;
+
+ if (headphones_inserted() != phones_present)
+ {
+ /* Use the autoresetting oneshot to debounce the detection signal */
+ phones_present = !phones_present;
+ timeout_register(&hp_detect_timeout, hp_detect_callback,
+ HZ/2, phones_present);
+ }
+#endif
+#if defined(HAVE_LINEOUT_DETECTION)
+ static struct timeout lo_detect_timeout; /* Debouncer for lineout plug/unplug */
+ static bool lineout_present = false;
+
+ if (lineout_inserted() != lineout_present)
+ {
+ /* Use the autoresetting oneshot to debounce the detection signal */
+ lineout_present = !lineout_present;
+ timeout_register(&lo_detect_timeout, lo_detect_callback,
+ HZ/2, lineout_present);
+ }
+#endif
+}
+
static bool button_try_post(int button, int data)
{
#ifdef HAVE_TOUCHSCREEN
@@ -153,6 +180,43 @@ static bool button_try_post(int button, int data)
return ret;
}
+#ifdef HAVE_BACKLIGHT
+/* disabled function is shared between Main & Remote LCDs */
+static bool filter_first_keypress_disabled(int button, int data)
+{
+ button_try_post(button, data);
+ return false;
+}
+
+static bool filter_first_keypress_enabled(int button, int data)
+{
+#if defined(HAVE_TRANSFLECTIVE_LCD) && defined(HAVE_LCD_SLEEP)
+ if (is_backlight_on(false) && lcd_active())
+#else
+ if (is_backlight_on(false))
+#endif
+ {
+ return filter_first_keypress_disabled(button, data);
+ }
+ return true;
+}
+
+#ifdef HAVE_REMOTE_LCD
+static bool filter_first_remote_keypress_enabled(int button, int data)
+{
+ if (is_remote_backlight_on(false)
+#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
+ || (remote_type()==REMOTETYPE_H300_NONLCD)
+#endif
+ )
+ {
+ return filter_first_keypress_disabled(button, data);
+ }
+ return true;
+}
+#endif /* def HAVE_REMOTE_LCD */
+#endif /* def HAVE_BACKLIGHT */
+
static void button_tick(void)
{
static int count = 0;
@@ -168,42 +232,13 @@ static void button_tick(void)
#endif
int diff;
int btn;
-#ifdef HAVE_BUTTON_DATA
int data = 0;
-#else
- const int data = 0;
-#endif
-#if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR)
- /* Post events for the remote control */
- btn = remote_control_rx();
- if(btn)
- button_try_post(btn, 0);
-#endif
+ button_remote_post();
-#ifdef HAVE_BUTTON_DATA
btn = button_read(&data);
-#else
- btn = button_read();
-#endif
-#if defined(HAVE_HEADPHONE_DETECTION)
- if (headphones_inserted() != phones_present)
- {
- /* Use the autoresetting oneshot to debounce the detection signal */
- phones_present = !phones_present;
- timeout_register(&hp_detect_timeout, hp_detect_callback,
- HZ/2, phones_present);
- }
-#endif
-#if defined(HAVE_LINEOUT_DETECTION)
- if (lineout_inserted() != lineout_present)
- {
- /* Use the autoresetting oneshot to debounce the detection signal */
- lineout_present = !lineout_present;
- timeout_register(&lo_detect_timeout, lo_detect_callback,
- HZ/2, lineout_present);
- }
-#endif
+
+ check_audio_peripheral_state();
/* Find out if a key has been released */
diff = btn ^ lastbtn;
@@ -348,48 +383,21 @@ static void button_tick(void)
#ifdef HAVE_BACKLIGHT
#ifdef HAVE_REMOTE_LCD
if (btn & BUTTON_REMOTE) {
- if (!remote_filter_first_keypress
- || is_remote_backlight_on(false)
-#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
- || (remote_type()==REMOTETYPE_H300_NONLCD)
-#endif
- )
- button_try_post(btn, data);
- else
- skip_remote_release = true;
+ skip_remote_release = remote_keypress_filter_fn(btn, data);
+ remote_backlight_on();
}
else
#endif
- if (!filter_first_keypress
-#if defined(HAVE_TRANSFLECTIVE_LCD) && defined(HAVE_LCD_SLEEP)
- || (is_backlight_on(false) && lcd_active())
-#else
- || is_backlight_on(false)
-#endif
-#if BUTTON_REMOTE
- || (btn & BUTTON_REMOTE)
-#endif
- )
- button_try_post(btn, data);
- else
- skip_release = true;
+ {
+ skip_release = keypress_filter_fn(btn, data);
+ backlight_on();
+ buttonlight_on();
+ }
#else /* no backlight, nothing to skip */
button_try_post(btn, data);
#endif
post = false;
}
-#ifdef HAVE_REMOTE_LCD
- if(btn & BUTTON_REMOTE)
- remote_backlight_on();
- else
-#endif
- {
- backlight_on();
-#ifdef HAVE_BUTTON_LIGHT
- buttonlight_on();
-#endif
- }
-
reset_poweroff_timer();
}
}
@@ -400,9 +408,8 @@ static void button_tick(void)
}
}
lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
-#ifdef HAVE_BUTTON_DATA
+
lastdata = data;
-#endif
}
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -515,30 +522,23 @@ intptr_t button_get_data(void)
void button_init(void)
{
+ int temp;
/* Init used objects first */
queue_init(&button_queue, true);
-#ifdef HAVE_BUTTON_DATA
- int temp;
-#endif
/* hardware inits */
button_init_device();
-#ifdef HAVE_BUTTON_DATA
button_read(&temp);
lastbtn = button_read(&temp);
-#else
- button_read();
- lastbtn = button_read();
-#endif
reset_poweroff_timer();
flipped = false;
#ifdef HAVE_BACKLIGHT
- filter_first_keypress = false;
+ set_backlight_filter_keypress(false);
#ifdef HAVE_REMOTE_LCD
- remote_filter_first_keypress = false;
+ set_remote_backlight_filter_keypress(false);
#endif
#endif
#ifdef HAVE_TOUCHSCREEN
@@ -649,12 +649,18 @@ void button_set_flip(bool flip)
#ifdef HAVE_BACKLIGHT
void set_backlight_filter_keypress(bool value)
{
- filter_first_keypress = value;
+ if (!value)
+ keypress_filter_fn = filter_first_keypress_disabled;
+ else
+ keypress_filter_fn = filter_first_keypress_enabled;
}
#ifdef HAVE_REMOTE_LCD
void set_remote_backlight_filter_keypress(bool value)
{
- remote_filter_first_keypress = value;
+ if (!value)
+ remote_keypress_filter_fn = filter_first_keypress_disabled;
+ else
+ remote_keypress_filter_fn = filter_first_remote_keypress_enabled;
}
#endif
#endif
@@ -662,13 +668,13 @@ void set_remote_backlight_filter_keypress(bool value)
/*
* Get button pressed from hardware
*/
-#ifdef HAVE_BUTTON_DATA
+
static int button_read(int *data)
{
+#ifdef HAVE_BUTTON_DATA
int btn = button_read_device(data);
#else
-static int button_read(void)
-{
+ (void) data;
int btn = button_read_device();
#endif
int retval;
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index 0c02c8224f..20c8f2b8e0 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -22,7 +22,7 @@
#include "config.h"
#include "system.h"
#include "sys/types.h"
-#include <string.h>
+#include "string-extra.h"
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
@@ -1698,8 +1698,8 @@ static int add_dir_entry(struct bpb *fat_bpb, struct fat_filestr *parentstr,
int rc;
unsigned char basisname[11], shortname[11];
- int n;
int entries_needed;
+ int n = -1;
unsigned long ucslen = 0;
if (is_dotdir_name(name) && (attr & ATTR_DIRECTORY))
diff --git a/firmware/drivers/lcd-16bit-common.c b/firmware/drivers/lcd-16bit-common.c
index 1b84847929..9d24dfe16e 100644
--- a/firmware/drivers/lcd-16bit-common.c
+++ b/firmware/drivers/lcd-16bit-common.c
@@ -31,38 +31,15 @@
/* Clear the current viewport */
void lcd_clear_viewport(void)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int x, y, width, height;
int len, step;
- x = lcd_current_viewport->x;
- y = lcd_current_viewport->y;
- width = lcd_current_viewport->width;
- height = lcd_current_viewport->height;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
+ x = vp->x;
+ y = vp->y;
+ width = vp->width;
+ height = vp->height;
len = STRIDE_MAIN(width, height);
step = STRIDE_MAIN(ROW_INC, COL_INC);
@@ -70,18 +47,18 @@ void lcd_clear_viewport(void)
dst = FBADDR(x, y);
dst_end = FBADDR(x + width - 1 , y + height - 1);
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
do
{
- memset16(dst, lcd_current_viewport->fg_pattern, len);
+ memset16(dst, vp->fg_pattern, len);
dst += step;
}
while (dst <= dst_end);
}
else
{
- if (lcd_backdrop && lcd_current_viewport->buffer == &lcd_framebuffer_default)
+ if (lcd_backdrop && vp->buffer == &lcd_framebuffer_default)
{
do
{
@@ -95,19 +72,19 @@ void lcd_clear_viewport(void)
{
do
{
- memset16(dst, lcd_current_viewport->bg_pattern, len);
+ memset16(dst, vp->bg_pattern, len);
dst += step;
}
while (dst <= dst_end);
}
}
- if (lcd_current_viewport == &default_vp)
+ if (vp == &default_vp)
lcd_scroll_stop();
else
- lcd_scroll_stop_viewport(lcd_current_viewport);
+ lcd_scroll_stop_viewport(vp);
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
+ vp->flags &= ~(VP_FLAG_VP_SET_CLEAN);
}
/*** low-level drawing functions ***/
@@ -152,69 +129,24 @@ lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
unsigned bits = 0;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
int len, step;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->bg_pattern;
+ bits = vp->bg_pattern;
}
else
fillopt = OPT_COPY;
@@ -222,13 +154,13 @@ void lcd_fillrect(int x, int y, int width, int height)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->fg_pattern;
+ bits = vp->fg_pattern;
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x, y);
@@ -283,65 +215,16 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* convert to viewport coordinates */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ struct viewport *vp = lcd_current_viewport;
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* move starting point */
src += stride * (src_y >> 3) + src_x;
src_y &= 7;
unsigned dmask = 0;
- int drmode = lcd_current_viewport->drawmode;
+ int drmode = vp->drawmode;
if (drmode & DRMODE_INVERSEVID)
{
@@ -387,7 +270,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_BG:
- bg = lcd_current_viewport->bg_pattern;
+ bg = vp->bg_pattern;
do {
data = (*src_col++ ^ dmask) >> src_y;
if(!(data & 0x01))
@@ -398,7 +281,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_FG:
- fg = lcd_current_viewport->fg_pattern;
+ fg = vp->fg_pattern;
do {
data = (*src_col++ ^ dmask) >> src_y;
if(data & 0x01)
@@ -409,7 +292,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_SOLID|DRMODE_INT_BD:
- fg = lcd_current_viewport->fg_pattern;
+ fg = vp->fg_pattern;
bo = lcd_backdrop_offset;
do {
data = (*src_col++ ^ dmask) >> src_y;
@@ -423,8 +306,8 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_SOLID:
- fg = lcd_current_viewport->fg_pattern;
- bg = lcd_current_viewport->bg_pattern;
+ fg = vp->fg_pattern;
+ bg = vp->bg_pattern;
do {
data = (*src_col++ ^ dmask) >> src_y;
if(data & 0x01)
@@ -453,7 +336,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
}
-/* About Rockbox' internal alpha channel format (for ALPHA_COLOR_FONT_DEPTH == 2)
+/* About Rockbox' internal alpha channel format (for ALPHA_BPP == 4)
*
* For each pixel, 4bit of alpha information is stored in a byte-stream,
* so two pixels are packed into one byte.
@@ -472,11 +355,15 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
* so lcd_bmp() do expect even rows.
*/
-#define ALPHA_COLOR_FONT_DEPTH 2
-#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
-#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
-#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
-#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
+#define ALPHA_BPP 4
+#define ALPHA_MASK ((1 << ALPHA_BPP) - 1)
+#define ALPHA_PIXELS_PER_BYTE (CHAR_BIT / ALPHA_BPP)
+
+#define ALPHA_WORD_T uint32_t
+#define ALPHA_WORD_LOAD load_le32
+#define ALPHA_WORDSIZE sizeof(ALPHA_WORD_T)
+#define ALPHA_PIXELS_PER_WORD (ALPHA_WORDSIZE * CHAR_BIT / ALPHA_BPP)
+
#ifdef CPU_ARM
#define BLEND_INIT do {} while (0)
#define BLEND_FINISH do {} while(0)
@@ -485,6 +372,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
#define BLEND_CONT(acc, color, alpha) \
asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
#define BLEND_OUT(acc) do {} while (0)
+
#elif defined(CPU_COLDFIRE)
#define ALPHA_BITMAP_READ_WORDS
#define BLEND_INIT \
@@ -496,6 +384,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
#define BLEND_CONT BLEND_START
#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
+
#else
#define BLEND_INIT do {} while (0)
#define BLEND_FINISH do {} while(0)
@@ -507,7 +396,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
/* Blend the given two colors */
static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
{
- a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
+ a += a >> (ALPHA_BPP - 1);
#if (LCD_PIXELFORMAT == RGB565SWAPPED)
c1 = swap16(c1);
c2 = swap16(c2);
@@ -516,9 +405,9 @@ static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
unsigned p;
BLEND_START(p, c1l, a);
- BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
+ BLEND_CONT(p, c2l, ALPHA_MASK + 1 - a);
BLEND_OUT(p);
- p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
+ p = (p >> ALPHA_BPP) & 0x07e0f81f;
p |= (p >> 16);
#if (LCD_PIXELFORMAT == RGB565SWAPPED)
return swap16(p);
@@ -527,301 +416,241 @@ static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
#endif
}
-/* Blend an image with an alpha channel
- * if image is NULL, drawing will happen according to the drawmode
- * src is the alpha channel (4bit per pixel) */
-static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
- const unsigned char *src, int src_x,
- int src_y, int x, int y,
- int width, int height,
- int stride_image, int stride_src)
+static void ICODE_ATTR lcd_alpha_bitmap_part_mix(
+ const fb_data* image, const unsigned char *alpha,
+ int src_x, int src_y,
+ int x, int y, int width, int height,
+ int stride_image, int stride_alpha)
{
- fb_data *dst, *dst_row;
- unsigned dmask = 0x00000000;
- int drmode = lcd_current_viewport->drawmode;
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
- /* initialize blending */
- BLEND_INIT;
+ struct viewport *vp = lcd_current_viewport;
+ unsigned int dmask = 0;
+ int drmode = vp->drawmode;
+ fb_data *dst;
+#ifdef ALPHA_BITMAP_READ_WORDS
+ ALPHA_WORD_T alpha_data, *alpha_word;
+ size_t alpha_offset = 0, alpha_pixels;
+#else
+ unsigned char alpha_data;
+ size_t alpha_pixels;
+#endif
- /* clipping */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- {
- BLEND_FINISH;
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- }
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- /* the following drawmode combinations are possible:
- * 1) COMPLEMENT: just negates the framebuffer contents
- * 2) BG and BG+backdrop: draws _only_ background pixels with either
- * the background color or the backdrop (if any). The backdrop
- * is an image in native lcd format
- * 3) FG and FG+image: draws _only_ foreground pixels with either
- * the foreground color or an image buffer. The image is in
- * native lcd format
- * 4) SOLID, SOLID+backdrop, SOLID+image, SOLID+backdrop+image, i.e. all
- * possible combinations of 2) and 3). Draws both, fore- and background,
- * pixels. The rules of 2) and 3) apply.
- *
- * INVERSEVID swaps fore- and background pixels, i.e. background pixels
- * become foreground ones and vice versa.
- */
if (drmode & DRMODE_INVERSEVID)
{
- dmask = 0xffffffff;
- drmode &= DRMODE_SOLID; /* mask out inversevid */
+ dmask = 0xFFFFFFFFu;
+ drmode &= ~DRMODE_INVERSEVID;
}
- /* Use extra bits to avoid if () in the switch-cases below */
if (image != NULL)
drmode |= DRMODE_INT_IMG;
if ((drmode & DRMODE_BG) && lcd_backdrop)
drmode |= DRMODE_INT_BD;
- dst_row = FBADDR(x, y);
-
- int col, row = height;
- unsigned data, pixels;
- unsigned skip_end = (stride_src - width);
- unsigned skip_start = src_y * stride_src + src_x;
- unsigned skip_start_image = STRIDE_MAIN(src_y * stride_image + src_x,
- src_x * stride_image + src_y);
-
#ifdef ALPHA_BITMAP_READ_WORDS
- uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
- skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
- src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
- data = letoh32(*src_w++) ^ dmask;
- pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
+#define INIT_ALPHA() \
+ do { \
+ alpha_offset = src_y * stride_alpha + src_x; \
+ } while(0)
+#define START_ALPHA() \
+ do { \
+ size_t __byteskip = (uintptr_t)alpha % ALPHA_WORDSIZE; \
+ size_t __byteoff = alpha_offset / ALPHA_PIXELS_PER_BYTE; \
+ alpha_word = (ALPHA_WORD_T *)ALIGN_DOWN(alpha + __byteoff, ALPHA_WORDSIZE); \
+ alpha_data = ALPHA_WORD_LOAD(alpha_word++) ^ dmask; \
+ alpha_pixels = ((__byteoff + __byteskip) % ALPHA_WORDSIZE) * ALPHA_PIXELS_PER_BYTE; \
+ alpha_pixels += alpha_offset % ALPHA_PIXELS_PER_BYTE; \
+ alpha_data >>= alpha_pixels * ALPHA_BPP; \
+ alpha_pixels = ALPHA_PIXELS_PER_WORD - alpha_pixels; \
+ } while(0)
+#define END_ALPHA() \
+ do { \
+ alpha_offset += stride_alpha; \
+ } while(0)
+#define READ_ALPHA() \
+ ({ \
+ if (alpha_pixels == 0) { \
+ alpha_data = ALPHA_WORD_LOAD(alpha_word++) ^ dmask; \
+ alpha_pixels = ALPHA_PIXELS_PER_WORD; \
+ } \
+ ALPHA_WORD_T __ret = alpha_data & ALPHA_MASK; \
+ alpha_data >>= ALPHA_BPP; \
+ alpha_pixels--; \
+ __ret; \
+ })
+#elif ALPHA_BPP == 4
+#define INIT_ALPHA() \
+ do { \
+ alpha_pixels = src_y * stride_alpha + src_x; \
+ stride_alpha = stride_alpha - width; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_pixels &= 1; \
+ if (alpha_pixels) { \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_data >>= ALPHA_BPP; \
+ } \
+ } while(0)
+#define START_ALPHA() do { } while(0)
+#define END_ALPHA() \
+ do { \
+ if (stride_alpha) { \
+ alpha_pixels = stride_alpha - alpha_pixels; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_pixels &= 1; \
+ if (alpha_pixels) { \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_data >>= ALPHA_BPP; \
+ } \
+ } \
+ } while(0)
+#define READ_ALPHA() \
+ ({ \
+ if (alpha_pixels == 0) \
+ alpha_data = *alpha++ ^ dmask; \
+ unsigned char __ret = alpha_data & ALPHA_MASK; \
+ alpha_data >>= ALPHA_BPP; \
+ alpha_pixels ^= 1; \
+ __ret; \
+ })
#else
- src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
- data = *src ^ dmask;
- pixels = skip_start % ALPHA_COLOR_PIXEL_PER_BYTE;
-#endif
- data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
-#ifdef ALPHA_BITMAP_READ_WORDS
- pixels = 8 - pixels;
+#define INIT_ALPHA() \
+ do { \
+ alpha_pixels = src_y * stride_alpha + src_x; \
+ stride_alpha = stride_alpha - width; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_pixels %= ALPHA_PIXELS_PER_BYTE; \
+ alpha_data >>= ALPHA_BPP * alpha_pixels; \
+ alpha_pixels = ALPHA_PIXELS_PER_BYTE - alpha_pixels; \
+ } while(0)
+#define START_ALPHA() do { } while(0)
+#define END_ALPHA() \
+ do { \
+ if ((size_t)stride_alpha <= alpha_pixels) \
+ alpha_pixels -= stride_alpha; \
+ else { \
+ alpha_pixels = stride_alpha - alpha_pixels; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_pixels %= ALPHA_PIXELS_PER_BYTE; \
+ alpha_data >>= ALPHA_BPP * alpha_pixels; \
+ alpha_pixels = ALPHA_PIXELS_PER_BYTE - alpha_pixels; \
+ } \
+ } while(0)
+#define READ_ALPHA() \
+ ({ \
+ if (alpha_pixels == 0) { \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_pixels = ALPHA_PIXELS_PER_BYTE; \
+ } \
+ unsigned char __ret = alpha_data & ALPHA_MASK; \
+ alpha_data >>= ALPHA_BPP; \
+ alpha_pixels--; \
+ __ret; \
+ })
#endif
- /* image is only accessed in DRMODE_INT_IMG cases, i.e. when non-NULL.
- * Therefore NULL accesses are impossible and we can increment
- * unconditionally (applies for stride at the end of the loop as well) */
- image += skip_start_image;
- /* go through the rows and update each pixel */
+ dst = FBADDR(x, y);
+ image += STRIDE_MAIN(src_y * stride_image + src_x,
+ src_x * stride_image + src_y);
+
+ INIT_ALPHA();
+ BLEND_INIT;
+
do
{
- /* saving lcd_current_viewport->fg/bg_pattern and lcd_backdrop_offset into these
- * temp vars just before the loop helps gcc to opimize the loop better
- * (testing showed ~15% speedup) */
- unsigned fg, bg;
- ptrdiff_t bo, img_offset;
- col = width;
- dst = dst_row;
- dst_row += ROW_INC;
-#ifdef ALPHA_BITMAP_READ_WORDS
-#define UPDATE_SRC_ALPHA do { \
- if (--pixels) \
- data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
- else \
- { \
- data = letoh32(*src_w++) ^ dmask; \
- pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
- } \
- } while (0)
-#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
-#define UPDATE_SRC_ALPHA do { \
- if (pixels ^= 1) \
- data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
- else \
- data = *(++src) ^ dmask; \
- } while (0)
-#else
-#define UPDATE_SRC_ALPHA do { \
- if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
- data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
- else \
- data = *(++src) ^ dmask; \
- } while (0)
-#endif
+ intptr_t bo, io;
+ unsigned int fg, bg;
+ int col = width;
+ fb_data *dst_row = dst;
+
+ START_ALPHA();
switch (drmode)
{
- case DRMODE_COMPLEMENT:
- do
- {
- *dst = blend_two_colors(*dst, ~(*dst),
- data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_BG|DRMODE_INT_BD:
- bo = lcd_backdrop_offset;
- do
- {
- fb_data c = *(fb_data *)((uintptr_t)dst + bo);
- *dst = blend_two_colors(c, *dst, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- image += STRIDE_MAIN(1, stride_image);
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_BG:
- bg = lcd_current_viewport->bg_pattern;
- do
- {
- *dst = blend_two_colors(bg, *dst, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_FG|DRMODE_INT_IMG:
- img_offset = image - dst;
- do
- {
- *dst = blend_two_colors(*dst, *(dst + img_offset), data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_FG:
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- *dst = blend_two_colors(*dst, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID|DRMODE_INT_BD:
- bo = lcd_backdrop_offset;
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- fb_data *c = (fb_data *)((uintptr_t)dst + bo);
- *dst = blend_two_colors(*c, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID|DRMODE_INT_IMG:
- bg = lcd_current_viewport->bg_pattern;
- img_offset = image - dst;
- do
- {
- *dst = blend_two_colors(bg, *(dst + img_offset), data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID|DRMODE_INT_BD|DRMODE_INT_IMG:
- bo = lcd_backdrop_offset;
- img_offset = image - dst;
- do
- {
- fb_data *c = (fb_data *)((uintptr_t)dst + bo);
- *dst = blend_two_colors(*c, *(dst + img_offset), data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID:
- bg = lcd_current_viewport->bg_pattern;
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- *dst = blend_two_colors(bg, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- }
-#ifdef ALPHA_BITMAP_READ_WORDS
- if (skip_end < pixels)
- {
- pixels -= skip_end;
- data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
- } else {
- pixels = skip_end - pixels;
- src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
- pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
- data = letoh32(*src_w++) ^ dmask;
- data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
- pixels = 8 - pixels;
- }
-#else
- if (skip_end)
- {
- pixels += skip_end;
- if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
+ case DRMODE_COMPLEMENT:
+ do
{
- src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
- pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
- data = *src ^ dmask;
- data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
- } else
- data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
+ *dst = blend_two_colors(*dst, ~(*dst), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_BG|DRMODE_INT_BD:
+ bo = lcd_backdrop_offset;
+ do
+ {
+ *dst = blend_two_colors(*PTR_ADD(dst, bo), *dst, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_BG:
+ bg = vp->bg_pattern;
+ do
+ {
+ *dst = blend_two_colors(bg, *dst, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_FG|DRMODE_INT_IMG:
+ io = image - dst;
+ do
+ {
+ *dst = blend_two_colors(*dst, *(dst + io), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_FG:
+ fg = vp->fg_pattern;
+ do
+ {
+ *dst = blend_two_colors(*dst, fg, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID|DRMODE_INT_BD:
+ fg = vp->fg_pattern;
+ bo = lcd_backdrop_offset;
+ do
+ {
+ *dst = blend_two_colors(*PTR_ADD(dst, bo), fg, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID|DRMODE_INT_IMG:
+ bg = vp->bg_pattern;
+ io = image - dst;
+ do
+ {
+ *dst = blend_two_colors(bg, *(dst + io), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID|DRMODE_INT_BD|DRMODE_INT_IMG:
+ bo = lcd_backdrop_offset;
+ io = image - dst;
+ do
+ {
+ *dst = blend_two_colors(*PTR_ADD(dst, bo), *(dst + io), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID:
+ fg = vp->fg_pattern;
+ bg = vp->bg_pattern;
+ do
+ {
+ *dst = blend_two_colors(bg, fg, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
}
-#endif
- image += STRIDE_MAIN(stride_image,1);
- } while (--row);
+ END_ALPHA();
+ image += STRIDE_MAIN(stride_image, 1);
+ dst = dst_row + ROW_INC;
+ } while (--height);
BLEND_FINISH;
}
diff --git a/firmware/drivers/lcd-16bit-vert.c b/firmware/drivers/lcd-16bit-vert.c
index 4422fdea50..d4ad218d14 100644
--- a/firmware/drivers/lcd-16bit-vert.c
+++ b/firmware/drivers/lcd-16bit-vert.c
@@ -54,61 +54,25 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
int stride_image, int stride_src);
#include "lcd-color-common.c"
-#include "lcd-16bit-common.c"
#include "lcd-bitmap-common.c"
+#include "lcd-16bit-common.c"
/*** drawing functions ***/
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x;
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int stride_dst;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) ||
- (x1 >= lcd_current_viewport->width) ||
- (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* Adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
- dst = FBADDR(x1 , y );
- stride_dst = lcd_current_viewport->buffer->stride;
+ dst = FBADDR(x1, y);
+ stride_dst = vp->buffer->stride;
dst_end = dst + (x2 - x1) * stride_dst;
do
@@ -122,61 +86,26 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y, height;
+ struct viewport *vp = lcd_current_viewport;
+ int height;
unsigned bits = 0;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) ||
- (y1 >= lcd_current_viewport->height) ||
- (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if(!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
height = y2 - y1 + 1;
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->bg_pattern;
+ bits = vp->bg_pattern;
}
else
fillopt = OPT_COPY;
@@ -184,13 +113,13 @@ void lcd_vline(int x, int y1, int y2)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->fg_pattern;
+ bits = vp->fg_pattern;
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x, y1);
@@ -220,65 +149,16 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
int stride_dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_x + src_y; /* move starting point */
dst = FBADDR(x, y);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
fb_data *dst_end = dst + width * stride_dst;
do
@@ -295,66 +175,16 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
int src_y, int stride, int x,
int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int stride_dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_x + src_y; /* move starting point */
dst = FBADDR(x, y);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
dst_end = dst + width * stride_dst;
do
@@ -363,7 +193,7 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
for(i = 0;i < height;i++)
{
if (src[i] == REPLACEWITHFG_COLOR)
- dst[i] = lcd_current_viewport->fg_pattern;
+ dst[i] = vp->fg_pattern;
else if(src[i] != TRANSPARENT_COLOR)
dst[i] = src[i];
}
diff --git a/firmware/drivers/lcd-16bit.c b/firmware/drivers/lcd-16bit.c
index d6bf5a500d..fbf6700f28 100644
--- a/firmware/drivers/lcd-16bit.c
+++ b/firmware/drivers/lcd-16bit.c
@@ -62,61 +62,26 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x, width;
+ struct viewport *vp = lcd_current_viewport;
+ int width;
unsigned bits = 0;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) ||
- (x1 >= lcd_current_viewport->width) ||
- (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* Adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
width = x2 - x1 + 1;
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->bg_pattern;
+ bits = vp->bg_pattern;
}
else
fillopt = OPT_COPY;
@@ -124,13 +89,13 @@ void lcd_hline(int x1, int x2, int y)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->fg_pattern;
+ bits = vp->fg_pattern;
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x1, y);
@@ -158,52 +123,16 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y;
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int stride_dst;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) ||
- (y1 >= lcd_current_viewport->height) ||
- (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
- return;
-
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- dst = FBADDR(x , y1);
- stride_dst = lcd_current_viewport->buffer->stride;
+ dst = FBADDR(x, y1);
+ stride_dst = vp->buffer->stride;
dst_end = dst + (y2 - y1) * stride_dst;
do
@@ -219,66 +148,16 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
int stride_dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
do
{
@@ -294,64 +173,14 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
int src_y, int stride, int x,
int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
- unsigned fg = lcd_current_viewport->fg_pattern;
- int stride_dst = lcd_current_viewport->buffer->stride;
-
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
+ unsigned fg = vp->fg_pattern;
+ int stride_dst = vp->buffer->stride;
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
diff --git a/firmware/drivers/lcd-1bit-vert.c b/firmware/drivers/lcd-1bit-vert.c
index c86ced9b6c..77eb4ab2a1 100644
--- a/firmware/drivers/lcd-1bit-vert.c
+++ b/firmware/drivers/lcd-1bit-vert.c
@@ -71,6 +71,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCDM(WIDTH),
.height = LCDM(HEIGHT),
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -82,7 +83,7 @@ static void *LCDFN(frameaddress_default)(int x, int y)
{
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = CURRENT_VP->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if defined(MAIN_LCD) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCDM(NATIVE_STRIDE)(fb->stride)) + y;
#else
size_t element = (y * LCDM(NATIVE_STRIDE)(fb->stride)) + x;
@@ -91,6 +92,8 @@ static void *LCDFN(frameaddress_default)(int x, int y)
return fb->FBFN(ptr) + element;/*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void LCDFN(init)(void)
{
@@ -105,43 +108,6 @@ void LCDFN(init)(void)
}
-/*** parameter handling ***/
-
-void LCDFN(set_drawmode)(int mode)
-{
- CURRENT_VP->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int LCDFN(get_drawmode)(void)
-{
- return CURRENT_VP->drawmode;
-}
-
-int LCDFN(getwidth)(void)
-{
- return CURRENT_VP->width;
-}
-
-int LCDFN(getheight)(void)
-{
- return CURRENT_VP->height;
-}
-
-void LCDFN(setfont)(int newfont)
-{
- CURRENT_VP->font = newfont;
-}
-
-int LCDFN(getfont)(void)
-{
- return CURRENT_VP->font;
-}
-
-int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, CURRENT_VP->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -237,188 +203,24 @@ void LCDFN(clear_display)(void)
unsigned bits = (CURRENT_VP->drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
memset(LCDFB(0, 0), bits, FBSIZE);
- LCDFN(scroll_info).lines = 0;
-}
-
-/* Clear the current viewport */
-void LCDFN(clear_viewport)(void)
-{
- int oldmode;
-
- if (CURRENT_VP == &default_vp &&
- default_vp.buffer == &LCDFN(framebuffer_default))
- {
- LCDFN(clear_display)();
- }
- else
- {
- oldmode = CURRENT_VP->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- CURRENT_VP->drawmode = (~CURRENT_VP->drawmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- LCDFN(fillrect)(0, 0, CURRENT_VP->width, CURRENT_VP->height);
-
- CURRENT_VP->drawmode = oldmode;
-
- LCDFN(scroll_stop_viewport)(CURRENT_VP);
- }
-
- CURRENT_VP->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void LCDFN(drawpixel)(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- LCDFN(pixelfuncs)[CURRENT_VP->drawmode](CURRENT_VP->x + x, CURRENT_VP->y + y);
-}
-
-/* Draw a line */
-void LCDFN(drawline)(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[CURRENT_VP->drawmode];
-
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for vertical line - optimisation.\n"); */
- LCDFN(vline)(x1, y1, y2);
- return;
- }
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for horizontal line - optimisation.\n"); */
- LCDFN(hline)(x1, x2, y1);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- pfunc(CURRENT_VP->x + x, CURRENT_VP->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ LCDFN(scroll_stop)();
}
/* Draw a horizontal line (optimised) */
void LCDFN(hline)(int x1, int x2, int y)
{
- int x, width;
+ struct viewport *vp = CURRENT_VP;
+ int width;
unsigned char *dst, *dst_end;
unsigned mask;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)CURRENT_VP->height) || (x1 >= CURRENT_VP->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= CURRENT_VP->width)
- x2 = CURRENT_VP->width-1;
-
- /* adjust to viewport */
- x1 += CURRENT_VP->x;
- x2 += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCDM(HEIGHT)) || (x1 >= LCDM(WIDTH))
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCDM(WIDTH))
- x2 = LCDM(WIDTH)-1;
-#endif
-
width = x2 - x1 + 1;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x1,y>>3);
mask = BIT_N(y & 7);
@@ -431,56 +233,22 @@ void LCDFN(hline)(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void LCDFN(vline)(int x, int y1, int y2)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst;
int stride_dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- ny = y1;
- y1 = y2;
- y2 = ny;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)CURRENT_VP->width) || (y1 >= CURRENT_VP->height)
- || (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= CURRENT_VP->height)
- y2 = CURRENT_VP->height-1;
-
- /* adjust for viewport */
- y1 += CURRENT_VP->y;
- y2 += CURRENT_VP->y;
- x += CURRENT_VP->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCDM(WIDTH)) || (y1 >= LCDM(HEIGHT))
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCDM(HEIGHT))
- y2 = LCDM(HEIGHT)-1;
-#endif
-
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y1>>3);
ny = y2 - (y1 & ~7);
mask = 0xFFu << (y1 & 7);
mask_bottom = 0xFFu >> (~ny & 7);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
for (; ny >= 8; ny -= 8)
{
@@ -492,24 +260,10 @@ void LCDFN(vline)(int x, int y1, int y2)
bfunc(dst, mask, 0xFFu);
}
-/* Draw a rectangular box */
-void LCDFN(drawrect)(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- LCDFN(vline)(x, y, y2);
- LCDFN(vline)(x2, y, y2);
- LCDFN(hline)(x, x2, y);
- LCDFN(hline)(x, x2, y2);
-}
-
/* Fill a rectangular area */
void LCDFN(fillrect)(int x, int y, int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
@@ -518,76 +272,30 @@ void LCDFN(fillrect)(int x, int y, int width, int height)
LCDFN(blockfunc_type) *bfunc;
bool fillopt = false;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
- if (CURRENT_VP->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (CURRENT_VP->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
fillopt = true;
}
}
else
{
- if (CURRENT_VP->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = 0xFFu;
}
}
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y>>3);
ny = height - 1 + (y & 7);
mask = 0xFFu << (y & 7);
mask_bottom = 0xFFu >> (~ny & 7);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
for (; ny >= 8; ny -= 8)
{
@@ -635,81 +343,31 @@ void ICODE_ATTR LCDFN(bitmap_part)(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int shift, ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 7;
ny = height - 1 + shift + src_y;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
mask = 0xFFu << (shift + src_y);
mask_bottom = 0xFFu >> (~ny & 7);
if (shift == 0)
{
- bool copyopt = (CURRENT_VP->drawmode == DRMODE_SOLID);
+ bool copyopt = (vp->drawmode == DRMODE_SOLID);
for (; ny >= 8; ny -= 8)
{
@@ -781,5 +439,3 @@ void LCDFN(bitmap)(const unsigned char *src, int x, int y, int width,
{
LCDFN(bitmap_part)(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-24bit.c b/firmware/drivers/lcd-24bit.c
index c3aa27f7ce..dbb7e543af 100644
--- a/firmware/drivers/lcd-24bit.c
+++ b/firmware/drivers/lcd-24bit.c
@@ -61,38 +61,15 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
/* Clear the current viewport */
void lcd_clear_viewport(void)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int x, y, width, height;
int len, step;
- x = lcd_current_viewport->x;
- y = lcd_current_viewport->y;
- width = lcd_current_viewport->width;
- height = lcd_current_viewport->height;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
+ x = vp->x;
+ y = vp->y;
+ width = vp->width;
+ height = vp->height;
len = STRIDE_MAIN(width, height);
step = STRIDE_MAIN(ROW_INC, COL_INC);
@@ -100,9 +77,9 @@ void lcd_clear_viewport(void)
dst = FBADDR(x, y);
dst_end = FBADDR(x + width - 1 , y + height - 1);
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- fb_data px = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fb_data px = FB_SCALARPACK(vp->fg_pattern);
do
{
fb_data *end = dst + len;
@@ -117,7 +94,7 @@ void lcd_clear_viewport(void)
{
if (!lcd_backdrop)
{
- fb_data px = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ fb_data px = FB_SCALARPACK(vp->bg_pattern);
do
{
fb_data *end = dst + len;
@@ -140,12 +117,12 @@ void lcd_clear_viewport(void)
}
}
- if (lcd_current_viewport == &default_vp)
+ if (vp == &default_vp)
lcd_scroll_stop();
else
- lcd_scroll_stop_viewport(lcd_current_viewport);
+ lcd_scroll_stop_viewport(vp);
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
+ vp->flags &= ~(VP_FLAG_VP_SET_CLEAN);
}
/*** low-level drawing functions ***/
@@ -191,70 +168,25 @@ lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
int len, step;
fb_data bits;
memset(&bits, 0, sizeof(fb_data));
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ bits = FB_SCALARPACK(vp->bg_pattern);
}
else
fillopt = OPT_COPY;
@@ -262,13 +194,13 @@ void lcd_fillrect(int x, int y, int width, int height)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ bits = FB_SCALARPACK(vp->fg_pattern);
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x, y);
@@ -329,65 +261,16 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
const unsigned char *src_end;
fb_data *dst, *dst_col;
unsigned dmask = 0x100; /* bit 8 == sentinel */
- int drmode = lcd_current_viewport->drawmode;
+ int drmode = vp->drawmode;
int row;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
src_end = src + width;
@@ -452,7 +335,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_BG:
- bg = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ bg = FB_SCALARPACK(vp->bg_pattern);
do
{
if (!(data & 0x01))
@@ -465,7 +348,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_FG:
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
do
{
if (data & 0x01)
@@ -478,7 +361,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_SOLID|DRMODE_INT_BD:
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
bo = lcd_backdrop_offset;
do
{
@@ -491,8 +374,8 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_SOLID:
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
- bg = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
+ bg = FB_SCALARPACK(vp->bg_pattern);
do
{
*dst = (data & 0x01) ? fg : bg;
@@ -562,62 +445,14 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
int width, int height,
int stride_image, int stride_src)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_row;
unsigned dmask = 0x00000000;
- int drmode = lcd_current_viewport->drawmode;
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
+ int drmode = vp->drawmode;
- /* clipping */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* the following drawmode combinations are possible:
* 1) COMPLEMENT: just negates the framebuffer contents
* 2) BG and BG+backdrop: draws _only_ background pixels with either
@@ -739,7 +574,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_BG:
- bg = lcd_current_viewport->bg_pattern;
+ bg = vp->bg_pattern;
do
{
unsigned px = FB_UNPACK_SCALAR_LCD(*dst);
@@ -762,7 +597,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_FG:
- fg = lcd_current_viewport->fg_pattern;
+ fg = vp->fg_pattern;
do
{
unsigned px = FB_UNPACK_SCALAR_LCD(*dst);
@@ -774,7 +609,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
break;
case DRMODE_SOLID|DRMODE_INT_BD:
bo = lcd_backdrop_offset;
- fg = lcd_current_viewport->fg_pattern;
+ fg = vp->fg_pattern;
do
{
unsigned c = FB_UNPACK_SCALAR_LCD(*(fb_data *)((uintptr_t)dst + bo));
@@ -785,7 +620,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_SOLID|DRMODE_INT_IMG:
- bg = lcd_current_viewport->bg_pattern;
+ bg = vp->bg_pattern;
img_offset = image - dst;
do
{
@@ -810,8 +645,8 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_SOLID:
- bg = lcd_current_viewport->bg_pattern;
- fg = lcd_current_viewport->fg_pattern;
+ bg = vp->bg_pattern;
+ fg = vp->fg_pattern;
do
{
*dst = blend_two_colors(bg, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
@@ -858,52 +693,17 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x, width;
+ struct viewport *vp = lcd_current_viewport;
+ int width;
fb_data *dst, *dst_end;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) ||
- (x1 >= lcd_current_viewport->width) ||
- (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* Adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
width = x2 - x1 + 1;
- dst = FBADDR(x1 , y);
+ dst = FBADDR(x1, y);
dst_end = dst + width;
do
{
@@ -915,50 +715,14 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y;
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) ||
- (y1 >= lcd_current_viewport->height) ||
- (y2 < 0))
- return;
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- dst = FBADDR(x , y1);
+ dst = FBADDR(x, y1);
dst_end = dst + (y2 - y1) * LCD_WIDTH;
do
@@ -974,62 +738,12 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
@@ -1047,69 +761,19 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
int src_y, int stride, int x,
int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
fb_data fg, transparent, replacewithfg;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
transparent = FB_SCALARPACK(TRANSPARENT_COLOR);
replacewithfg = FB_SCALARPACK(REPLACEWITHFG_COLOR);
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
#define CMP(c1, c2) (c1.r == c2.r && c1.g == c2.g && c1.b == c2.b)
do
diff --git a/firmware/drivers/lcd-2bit-horz.c b/firmware/drivers/lcd-2bit-horz.c
index b2114ba830..abe7443e01 100644
--- a/firmware/drivers/lcd-2bit-horz.c
+++ b/firmware/drivers/lcd-2bit-horz.c
@@ -64,6 +64,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -80,7 +81,7 @@ static void *lcd_frameaddress_default(int x, int y)
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = lcd_current_viewport->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -88,6 +89,8 @@ static void *lcd_frameaddress_default(int x, int y)
return fb->fb_ptr + element;/*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void lcd_init(void)
{
@@ -102,16 +105,6 @@ void lcd_init(void)
/*** parameter handling ***/
-void lcd_set_drawmode(int mode)
-{
- lcd_current_viewport->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int lcd_get_drawmode(void)
-{
- return lcd_current_viewport->drawmode;
-}
-
void lcd_set_foreground(unsigned brightness)
{
lcd_current_viewport->fg_pattern = brightness;
@@ -134,38 +127,6 @@ unsigned lcd_get_background(void)
return lcd_current_viewport->bg_pattern;
}
-void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
-{
- lcd_set_drawmode(mode);
- lcd_set_foreground(fg_brightness);
- lcd_set_background(bg_brightness);
-}
-
-int lcd_getwidth(void)
-{
- return lcd_current_viewport->width;
-}
-
-int lcd_getheight(void)
-{
- return lcd_current_viewport->height;
-}
-
-void lcd_setfont(int newfont)
-{
- lcd_current_viewport->font = newfont;
-}
-
-int lcd_getfont(void)
-{
- return lcd_current_viewport->font;
-}
-
-int lcd_getstringsize(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, lcd_current_viewport->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -384,185 +345,22 @@ void lcd_clear_display(void)
memset(FBADDR(0,0), bg_pattern, FRAMEBUFFER_SIZE);
}
- lcd_scroll_info.lines = 0;
-}
-
-/* Clear the current viewport */
-void lcd_clear_viewport(void)
-{
- int lastmode;
-
- if (lcd_current_viewport == &default_vp &&
- default_vp.buffer == &lcd_framebuffer_default)
- {
- lcd_clear_display();
- }
- else
- {
- lastmode = lcd_current_viewport->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- lcd_current_viewport->drawmode = (~lastmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- lcd_fillrect(0, 0, lcd_current_viewport->width, lcd_current_viewport->height);
-
- lcd_current_viewport->drawmode = lastmode;
-
- lcd_scroll_stop_viewport(lcd_current_viewport);
- }
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void lcd_drawpixel(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- lcd_pixelfuncs[lcd_current_viewport->drawmode](lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-}
-
-/* Draw a line */
-void lcd_drawline(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[lcd_current_viewport->drawmode];
-
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
- lcd_hline(x1, x2, y1);
- return;
- }
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
- lcd_vline(x1, y1, y2);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- pfunc(lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ lcd_scroll_stop();
}
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
+ struct viewport *vp = lcd_current_viewport;
int nx;
unsigned char *dst;
unsigned mask, mask_right;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- nx = x1;
- x1 = x2;
- x2 = nx;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) || (x1 >= lcd_current_viewport->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* adjust to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x1>>2,y);
nx = x2 - (x1 & ~3);
mask = 0xFFu >> (2 * (x1 & 3));
@@ -580,53 +378,18 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y;
+ struct viewport *vp = lcd_current_viewport;
unsigned char *dst, *dst_end;
int stride_dst;
unsigned mask;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) || (y1 >= lcd_current_viewport->height)
- || (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
- x += lcd_current_viewport->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x>>2,y1);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
mask = pixmask[x & 3];
dst_end = dst + (y2 - y1) * stride_dst;
@@ -638,82 +401,22 @@ void lcd_vline(int x, int y1, int y2)
while (dst <= dst_end);
}
-/* Draw a rectangular box */
-void lcd_drawrect(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- lcd_vline(x, y, y2);
- lcd_vline(x2, y, y2);
- lcd_hline(x, x2, y);
- lcd_hline(x, x2, y2);
-}
-
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int nx;
unsigned char *dst, *dst_end;
int stride_dst;
unsigned mask, mask_right;
lcd_blockfunc_type *bfunc;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) || (y >= lcd_current_viewport->height)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x>>2,y);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
nx = width - 1 + (x & 3);
mask = 0xFFu >> (2 * (x & 3));
mask_right = 0xFFu << (2 * (~nx & 3));
@@ -758,71 +461,23 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
const unsigned char *src_end;
fb_data *dst, *dst_end;
int stride_dst;
unsigned dmask = 0x100; /* bit 8 == sentinel */
unsigned dst_mask;
- int drmode = lcd_current_viewport->drawmode;
+ int drmode = vp->drawmode;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- x += lcd_current_viewport->x; /* adjust for viewport */
- y += lcd_current_viewport->y; /* adjust for viewport */
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
src_end = src + width;
dst = FBADDR(x >> 2,y);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
dst_end = dst + height * stride_dst;
dst_mask = pixmask[x & 3];
@@ -974,71 +629,22 @@ void ICODE_ATTR lcd_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int shift, nx;
unsigned char *dst, *dst_end;
int stride_dst;
unsigned mask, mask_right;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
stride = LCD_FBSTRIDE(stride, 0); /* convert to no. of bytes */
src += stride * src_y + (src_x >> 2); /* move starting point */
src_x &= 3;
x -= src_x;
dst = FBADDR(x>>2,y);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
shift = x & 3;
nx = width - 1 + shift + src_x;
@@ -1082,5 +688,3 @@ void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
{
lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-2bit-vert.c b/firmware/drivers/lcd-2bit-vert.c
index 6a476495d0..6ea367e695 100644
--- a/firmware/drivers/lcd-2bit-vert.c
+++ b/firmware/drivers/lcd-2bit-vert.c
@@ -66,6 +66,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -82,7 +83,7 @@ static void *lcd_frameaddress_default(int x, int y)
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = lcd_current_viewport->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -90,6 +91,8 @@ static void *lcd_frameaddress_default(int x, int y)
return fb->fb_ptr + element; /*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void lcd_init(void)
{
@@ -104,16 +107,6 @@ void lcd_init(void)
/*** parameter handling ***/
-void lcd_set_drawmode(int mode)
-{
- lcd_current_viewport->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int lcd_get_drawmode(void)
-{
- return lcd_current_viewport->drawmode;
-}
-
void lcd_set_foreground(unsigned brightness)
{
lcd_current_viewport->fg_pattern = brightness;
@@ -136,38 +129,6 @@ unsigned lcd_get_background(void)
return lcd_current_viewport->bg_pattern;
}
-void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
-{
- lcd_set_drawmode(mode);
- lcd_set_foreground(fg_brightness);
- lcd_set_background(bg_brightness);
-}
-
-int lcd_getwidth(void)
-{
- return lcd_current_viewport->width;
-}
-
-int lcd_getheight(void)
-{
- return lcd_current_viewport->height;
-}
-
-void lcd_setfont(int newfont)
-{
- lcd_current_viewport->font = newfont;
-}
-
-int lcd_getfont(void)
-{
- return lcd_current_viewport->font;
-}
-
-int lcd_getstringsize(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, lcd_current_viewport->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -386,188 +347,24 @@ void lcd_clear_display(void)
memset(FBADDR(0,0), bg_pattern, FRAMEBUFFER_SIZE);
}
- lcd_scroll_info.lines = 0;
-}
-
-/* Clear the current viewport */
-void lcd_clear_viewport(void)
-{
- int lastmode;
-
- if (lcd_current_viewport == &default_vp &&
- default_vp.buffer == &lcd_framebuffer_default)
- {
- lcd_clear_display();
- }
- else
- {
- lastmode = lcd_current_viewport->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- lcd_current_viewport->drawmode = (~lastmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- lcd_fillrect(0, 0, lcd_current_viewport->width, lcd_current_viewport->height);
-
- lcd_current_viewport->drawmode = lastmode;
-
- lcd_scroll_stop_viewport(lcd_current_viewport);
- }
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void lcd_drawpixel(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- lcd_pixelfuncs[lcd_current_viewport->drawmode](lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-}
-
-/* Draw a line */
-void lcd_drawline(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[lcd_current_viewport->drawmode];
-
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
- lcd_vline(x1, y1, y2);
- return;
- }
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
- lcd_hline(x1, x2, y1);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- pfunc(lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ lcd_scroll_stop();
}
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x;
+ struct viewport *vp = lcd_current_viewport;
int width;
fb_data *dst, *dst_end;
unsigned mask;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) || (x1 >= lcd_current_viewport->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
width = x2 - x1 + 1;
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x1,y>>2);
mask = pixmask[y & 3];
@@ -580,53 +377,19 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
+ struct viewport *vp = lcd_current_viewport;
int ny;
fb_data *dst;
int stride_dst;
unsigned mask, mask_bottom;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- ny = y1;
- y1 = y2;
- y2 = ny;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) || (y1 >= lcd_current_viewport->height)
- || (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
- x += lcd_current_viewport->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x,y1>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = y2 - (y1 & ~3);
mask = 0xFFu << (2 * (y1 & 3));
mask_bottom = 0xFFu >> (2 * (~ny & 3));
@@ -641,24 +404,10 @@ void lcd_vline(int x, int y1, int y2)
bfunc(dst, mask, 0xFFu);
}
-/* Draw a rectangular box */
-void lcd_drawrect(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- lcd_vline(x, y, y2);
- lcd_vline(x2, y, y2);
- lcd_hline(x, x2, y);
- lcd_hline(x, x2, y2);
-}
-
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int ny;
fb_data *dst, *dst_end;
int stride_dst;
@@ -667,58 +416,12 @@ void lcd_fillrect(int x, int y, int width, int height)
lcd_blockfunc_type *bfunc;
bool fillopt = false;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width)
- || (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
- {
- if ((lcd_current_viewport->drawmode & DRMODE_BG) && !lcd_backdrop)
+ if ((vp->drawmode & DRMODE_BG) && !lcd_backdrop)
{
fillopt = true;
bits = bg_pattern;
@@ -726,15 +429,15 @@ void lcd_fillrect(int x, int y, int width, int height)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = fg_pattern;
}
}
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x,y>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = height - 1 + (y & 3);
mask = 0xFFu << (2 * (y & 3));
mask_bottom = 0xFFu >> (2 * (~ny & 3));
@@ -785,77 +488,28 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int shift, ny;
fb_data *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
lcd_blockfunc_type *bfunc;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = FBADDR(x,y>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 3;
ny = height - 1 + shift + src_y;
mask = 0xFFFFu << (2 * (shift + src_y));
/* Overflowing bits aren't important. */
mask_bottom = 0xFFFFu >> (2 * (~ny & 7));
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
if (shift == 0)
{
@@ -985,68 +639,20 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
int shift, ny;
fb_data *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width)
- || (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
src += stride * (src_y >> 2) + src_x; /* move starting point */
src_y &= 3;
y -= src_y;
dst = FBADDR(x,y>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 3;
ny = height - 1 + shift + src_y;
@@ -1124,5 +730,3 @@ void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
{
lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-2bit-vi.c b/firmware/drivers/lcd-2bit-vi.c
index c09bbce2e0..5c615c4c9e 100644
--- a/firmware/drivers/lcd-2bit-vi.c
+++ b/firmware/drivers/lcd-2bit-vi.c
@@ -78,6 +78,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCDM(WIDTH),
.height = LCDM(HEIGHT),
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -94,7 +95,7 @@ static void *LCDFN(frameaddress_default)(int x, int y)
{
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = CURRENT_VP->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if defined(MAIN_LCD) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCDM(NATIVE_STRIDE)(fb->stride)) + y;
#else
size_t element = (y * LCDM(NATIVE_STRIDE)(fb->stride)) + x;
@@ -102,6 +103,8 @@ static void *LCDFN(frameaddress_default)(int x, int y)
return fb->FBFN(ptr) + element;/*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void LCDFN(init)(void)
{
@@ -133,16 +136,6 @@ unsigned lcd_remote_color_to_native(unsigned color)
}
#endif
-void LCDFN(set_drawmode)(int mode)
-{
- CURRENT_VP->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int LCDFN(get_drawmode)(void)
-{
- return CURRENT_VP->drawmode;
-}
-
void LCDFN(set_foreground)(unsigned brightness)
{
CURRENT_VP->fg_pattern = brightness;
@@ -165,38 +158,6 @@ unsigned LCDFN(get_background)(void)
return CURRENT_VP->bg_pattern;
}
-void LCDFN(set_drawinfo)(int mode, unsigned fg_brightness,
- unsigned bg_brightness)
-{
- LCDFN(set_drawmode)(mode);
- LCDFN(set_foreground)(fg_brightness);
- LCDFN(set_background)(bg_brightness);
-}
-
-int LCDFN(getwidth)(void)
-{
- return CURRENT_VP->width;
-}
-
-int LCDFN(getheight)(void)
-{
- return CURRENT_VP->height;
-}
-void LCDFN(setfont)(int newfont)
-{
- CURRENT_VP->font = newfont;
-}
-
-int LCDFN(getfont)(void)
-{
- return CURRENT_VP->font;
-}
-
-int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, CURRENT_VP->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -419,188 +380,24 @@ void LCDFN(clear_display)(void)
FBSIZE);
}
- LCDFN(scroll_info).lines = 0;
-}
-
-/* Clear the current viewport */
-void LCDFN(clear_viewport)(void)
-{
- int lastmode;
-
- if (CURRENT_VP == &default_vp &&
- default_vp.buffer == &LCDFN(framebuffer_default))
- {
- LCDFN(clear_display)();
- }
- else
- {
- lastmode = CURRENT_VP->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- CURRENT_VP->drawmode = (~lastmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- LCDFN(fillrect)(0, 0, CURRENT_VP->width, CURRENT_VP->height);
-
- CURRENT_VP->drawmode = lastmode;
-
- LCDFN(scroll_stop_viewport)(CURRENT_VP);
- }
- CURRENT_VP->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void LCDFN(drawpixel)(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- LCDFN(pixelfuncs)[CURRENT_VP->drawmode](CURRENT_VP->x+x, CURRENT_VP->y+y);
-}
-
-/* Draw a line */
-void LCDFN(drawline)(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[CURRENT_VP->drawmode];
-
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for vertical line - optimisation.\n"); */
- LCDFN(vline)(x1, y1, y2);
- return;
- }
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for horizontal line - optimisation.\n"); */
- LCDFN(hline)(x1, x2, y1);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- pfunc(CURRENT_VP->x + x, CURRENT_VP->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ LCDFN(scroll_stop)();
}
/* Draw a horizontal line (optimised) */
void LCDFN(hline)(int x1, int x2, int y)
{
- int x;
+ struct viewport *vp = CURRENT_VP;
int width;
FBFN(data) *dst, *dst_end;
unsigned mask;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)CURRENT_VP->height) || (x1 >= CURRENT_VP->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= CURRENT_VP->width)
- x2 = CURRENT_VP->width-1;
-
- /* adjust x1 and y to viewport */
- x1 += CURRENT_VP->x;
- x2 += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCDM(HEIGHT)) || (x1 >= LCDM(WIDTH))
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCDM(WIDTH))
- x2 = LCDM(WIDTH)-1;
-#endif
-
width = x2 - x1 + 1;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x1,y>>3);
mask = 0x0101 << (y & 7);
@@ -613,53 +410,19 @@ void LCDFN(hline)(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void LCDFN(vline)(int x, int y1, int y2)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst;
int stride_dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- ny = y1;
- y1 = y2;
- y2 = ny;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)CURRENT_VP->width) || (y1 >= CURRENT_VP->height)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- if (y1 < 0)
- y1 = 0;
- if (y2 >= CURRENT_VP->height)
- y2 = CURRENT_VP->height-1;
-
- /* adjust for viewport */
- y1 += CURRENT_VP->y;
- y2 += CURRENT_VP->y;
- x += CURRENT_VP->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCDM(WIDTH)) || (y1 >= LCDM(HEIGHT))
- || (y2 < 0))
- return;
-
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCDM(HEIGHT))
- y2 = LCDM(HEIGHT)-1;
-#endif
-
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y1>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = y2 - (y1 & ~7);
mask = (0xFFu << (y1 & 7)) & 0xFFu;
mask |= mask << 8;
@@ -676,24 +439,10 @@ void LCDFN(vline)(int x, int y1, int y2)
bfunc(dst, mask, 0xFFFFu);
}
-/* Draw a rectangular box */
-void LCDFN(drawrect)(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- LCDFN(vline)(x, y, y2);
- LCDFN(vline)(x2, y, y2);
- LCDFN(hline)(x, x2, y);
- LCDFN(hline)(x, x2, y2);
-}
-
/* Fill a rectangular area */
void LCDFN(fillrect)(int x, int y, int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
@@ -702,59 +451,12 @@ void LCDFN(fillrect)(int x, int y, int width, int height)
LCDFN(blockfunc_type) *bfunc;
bool fillopt = false;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
-
- if (CURRENT_VP->drawmode & DRMODE_INVERSEVID)
- {
- if ((CURRENT_VP->drawmode & DRMODE_BG) && !backdrop)
+ if ((vp->drawmode & DRMODE_BG) && !backdrop)
{
fillopt = true;
bits = bg_pattern;
@@ -762,15 +464,15 @@ void LCDFN(fillrect)(int x, int y, int width, int height)
}
else
{
- if (CURRENT_VP->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = fg_pattern;
}
}
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = height - 1 + (y & 7);
mask = (0xFFu << (y & 7)) & 0xFFu;
mask |= mask << 8;
@@ -823,74 +525,25 @@ void ICODE_ATTR LCDFN(mono_bitmap_part)(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int shift, ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
unsigned data, mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width) ||
- (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 7;
ny = height - 1 + shift + src_y;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
mask = 0xFFu << (shift + src_y);
/* not byte-doubled here because shift+src_y can be > 7 */
mask_bottom = 0xFFu >> (~ny & 7);
@@ -994,69 +647,20 @@ void ICODE_ATTR LCDFN(bitmap_part)(const FBFN(data) *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int shift, ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 7;
ny = height - 1 + shift + src_y;
@@ -1148,5 +752,3 @@ void LCDFN(bitmap)(const FBFN(data) *src, int x, int y, int width, int height)
{
LCDFN(bitmap_part)(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index 9a5f865f2a..975c494b5a 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -53,6 +53,158 @@
extern void viewport_set_buffer(struct viewport *vp,
struct frame_buffer_t *buffer,
const enum screen_type screen); /* viewport.c */
+
+/*
+ * In-viewport clipping functions:
+ *
+ * These clip a primitive (pixel, line, rect) given in
+ * viewport-relative coordinates to the specified viewport,
+ * and translate it to screen coordinates. They return
+ * false if the resulting primitive would be off-screen.
+ */
+
+static inline bool clip_viewport_pixel(struct viewport *vp, int *x, int *y)
+{
+ if (*x < 0 || *x >= vp->width ||
+ *y < 0 || *y >= vp->height)
+ return false;
+
+ *x += vp->x;
+ *y += vp->y;
+ return true;
+}
+
+static inline bool clip_viewport_hline(struct viewport *vp,
+ int *x1, int *x2, int *y)
+{
+ if (*y < 0 || *y >= vp->height)
+ return false;
+
+ if (*x2 < *x1) {
+ int tmp = *x2;
+ *x2 = *x1;
+ *x1 = tmp;
+ }
+
+ if (*x1 < 0)
+ *x1 = 0;
+ else if (*x1 >= vp->width)
+ return false;
+
+ if (*x2 < 0)
+ return false;
+ else if (*x2 >= vp->width)
+ *x2 = vp->width - 1;
+
+ *x1 += vp->x;
+ *x2 += vp->x;
+ *y += vp->y;
+ return true;
+}
+
+static inline bool clip_viewport_vline(struct viewport *vp,
+ int *x, int *y1, int *y2)
+{
+ if (*x < 0 || *x >= vp->width)
+ return false;
+
+ if (*y2 < *y1) {
+ int tmp = *y2;
+ *y2 = *y1;
+ *y1 = tmp;
+ }
+
+ if (*y1 < 0)
+ *y1 = 0;
+ else if (*y1 >= vp->height)
+ return false;
+
+ if (*y2 < 0)
+ return false;
+ else if (*y2 >= vp->height)
+ *y2 = vp->height - 1;
+
+ *x += vp->x;
+ *y1 += vp->y;
+ *y2 += vp->y;
+ return true;
+}
+
+static inline bool clip_viewport_rect(struct viewport *vp,
+ int *x, int *y, int *width, int *height,
+ int *src_x, int *src_y)
+{
+ if (*x < 0) {
+ *width += *x;
+ if (src_x)
+ *src_x -= *x;
+ *x = 0;
+ }
+
+ if (*y < 0) {
+ *height += *y;
+ if (src_y)
+ *src_y -= *y;
+ *y = 0;
+ }
+
+ if (*x + *width > vp->width)
+ *width = vp->width - *x;
+
+ if (*y + *height > vp->height)
+ *height = vp->height - *y;
+
+ *x += vp->x;
+ *y += vp->y;
+ return *width > 0 && *height > 0;
+}
+
+/*** parameter handling ***/
+
+void LCDFN(set_drawmode)(int mode)
+{
+ LCDFN(current_viewport)->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
+}
+
+int LCDFN(get_drawmode)(void)
+{
+ return LCDFN(current_viewport)->drawmode;
+}
+
+int LCDFN(getwidth)(void)
+{
+ return LCDFN(current_viewport)->width;
+}
+
+int LCDFN(getheight)(void)
+{
+ return LCDFN(current_viewport)->height;
+}
+
+void LCDFN(setfont)(int newfont)
+{
+ LCDFN(current_viewport)->font = newfont;
+}
+
+int LCDFN(getfont)(void)
+{
+ return LCDFN(current_viewport)->font;
+}
+
+int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
+{
+ return font_getstringsize(str, w, h, LCDFN(current_viewport)->font);
+}
+
+#if LCDM(DEPTH) > 1
+void LCDFN(set_drawinfo)(int mode, unsigned foreground, unsigned background)
+{
+ LCDFN(set_drawmode)(mode);
+ LCDFN(set_foreground)(foreground);
+ LCDFN(set_background)(background);
+}
+#endif
+
/*
* draws the borders of the current viewport
**/
@@ -69,6 +221,34 @@ void LCDFN(fill_viewport)(void)
LCDFN(fillrect)(0, 0, LCDFN(current_viewport)->width, LCDFN(current_viewport)->height);
}
+#if LCDM(DEPTH) < 8
+/*
+ * clear the current viewport - grayscale displays
+ */
+void LCDFN(clear_viewport)(void)
+{
+ struct viewport *vp = LCDFN(current_viewport);
+ int oldmode;
+
+ if (vp == &default_vp && default_vp.buffer == &LCDFN(framebuffer_default))
+ {
+ LCDFN(clear_display)();
+ }
+ else
+ {
+ oldmode = vp->drawmode;
+ vp->drawmode ^= DRMODE_INVERSEVID;
+ vp->drawmode |= DRMODE_SOLID;
+
+ LCDFN(fillrect)(0, 0, vp->width, vp->height);
+
+ vp->drawmode = oldmode;
+ LCDFN(scroll_stop_viewport)(vp);
+ }
+
+ vp->flags &= ~VP_FLAG_VP_SET_CLEAN;
+}
+#endif
/*** Viewports ***/
/* init_viewport Notes: When a viewport is initialized
@@ -84,6 +264,7 @@ struct viewport* LCDFN(init_viewport)(struct viewport* vp)
{
vp = &default_vp;
vp->buffer = fb_default;
+ return vp;
}
/* use defaults if no buffer is provided */
@@ -132,16 +313,13 @@ struct viewport* LCDFN(set_viewport_ex)(struct viewport* vp, int flags)
* expected.
*/
- if((unsigned) vp->x > (unsigned) LCDM(WIDTH)
- || (unsigned) vp->y > (unsigned) LCDM(HEIGHT)
+ if( vp->x < 0 || vp->y < 0
+ || vp->x > LCDM(WIDTH)
+ || vp->y > LCDM(HEIGHT)
|| vp->x + vp->width > LCDM(WIDTH)
|| vp->y + vp->height > LCDM(HEIGHT))
{
-#if !defined(HAVE_VIEWPORT_CLIP)
DEBUGF("ERROR: "
-#else
- DEBUGF("NOTE: "
-#endif
"set_viewport out of bounds: x: %d y: %d width: %d height:%d\n",
vp->x, vp->y, vp->width, vp->height);
}
@@ -162,33 +340,22 @@ struct viewport* LCDFN(set_viewport)(struct viewport* vp)
return LCDFN(set_viewport_ex)(vp, VP_FLAG_VP_DIRTY);
}
-struct viewport *LCDFN(get_viewport)(bool *is_default)
-{
-#if 0
- *is_default = memcmp(LCDFN(current_viewport),
- &default_vp, sizeof(struct viewport)) == 0;
-#else
- *is_default = LCDFN(current_viewport) == &default_vp;
-#endif
-
- return LCDFN(current_viewport);
-}
-
void LCDFN(update_viewport)(void)
{
struct viewport* vp = LCDFN(current_viewport);
- if (vp->buffer->stride != LCDFN(framebuffer_default.stride))
- {
- LCDFN(update_viewport_rect)(0,0, vp->width, vp->height);
- return;
- }
- LCDFN(update_rect)(vp->x, vp->y, vp->width, vp->height);
+ LCDFN(update_viewport_rect)(0, 0, vp->width, vp->height);
}
void LCDFN(update_viewport_rect)(int x, int y, int width, int height)
{
struct viewport* vp = LCDFN(current_viewport);
-
+ if ((vp->flags & VP_FLAG_OWNER_UPDATE) == VP_FLAG_OWNER_UPDATE)
+ {
+#ifdef LOGF_ENABLE
+ logf("%s ignored - owner update", __func__);
+#endif
+ return;
+ }
/* handle the case of viewport with differing stride from main screen */
if (vp->buffer->stride != LCDFN(framebuffer_default.stride))
{
@@ -532,7 +699,7 @@ static bool LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
}
/* copy contents to the line buffer */
- strlcpy(s->linebuffer, string, sizeof(s->linebuffer));
+ strmemccpy(s->linebuffer, string, sizeof(s->linebuffer));
/* scroll bidirectional or forward only depending on the string width */
if ( LCDFN(scroll_info).bidir_limit ) {
s->bidir = w < (vp->width) *
@@ -677,3 +844,128 @@ void LCDFN(nine_segment_bmp)(const struct bitmap* bm, int x, int y,
LCDFN(bmp_part)(bm, bm->width - corner_w, bm->width - corner_h,
width - corner_w, height - corner_h, corner_w, corner_h);
}
+
+void LCDFN(drawpixel)(int x, int y)
+{
+ struct viewport *vp = LCDFN(current_viewport);
+ if (clip_viewport_pixel(vp, &x, &y))
+ {
+#if LCDM(DEPTH) >= 8
+ LCDFN(fastpixelfunc_type) *pfunc = LCDFN(fastpixelfuncs)[vp->drawmode];
+ void *(*fbaddr)(int x, int y) = vp->buffer->get_address_fn;
+ pfunc(fbaddr(x, y));
+#else
+ LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[vp->drawmode];
+ pfunc(x, y);
+#endif
+ }
+}
+
+void LCDFN(drawline)(int x1, int y1, int x2, int y2)
+{
+ struct viewport *vp = LCDFN(current_viewport);
+ int numpixels;
+ int i;
+ int deltax, deltay;
+ int d, dinc1, dinc2;
+ int x, xinc1, xinc2;
+ int y, yinc1, yinc2;
+
+ deltay = abs(y2 - y1);
+ if (deltay == 0)
+ {
+ lcd_hline(x1, x2, y1);
+ return;
+ }
+
+ deltax = abs(x2 - x1);
+ if (deltax == 0)
+ {
+ lcd_vline(x1, y1, y2);
+ return;
+ }
+
+ xinc2 = 1;
+ yinc2 = 1;
+
+ if (deltax >= deltay)
+ {
+ numpixels = deltax;
+ d = 2 * deltay - deltax;
+ dinc1 = deltay * 2;
+ dinc2 = (deltay - deltax) * 2;
+ xinc1 = 1;
+ yinc1 = 0;
+ }
+ else
+ {
+ numpixels = deltay;
+ d = 2 * deltax - deltay;
+ dinc1 = deltax * 2;
+ dinc2 = (deltax - deltay) * 2;
+ xinc1 = 0;
+ yinc1 = 1;
+ }
+ numpixels++; /* include endpoints */
+
+ if (x1 > x2)
+ {
+ xinc1 = -xinc1;
+ xinc2 = -xinc2;
+ }
+
+ if (y1 > y2)
+ {
+ yinc1 = -yinc1;
+ yinc2 = -yinc2;
+ }
+
+ x = x1;
+ y = y1;
+
+#if LCDM(DEPTH) >= 8
+ LCDFN(fastpixelfunc_type) *pfunc = LCDFN(fastpixelfuncs)[vp->drawmode];
+ void *(*fbaddr)(int x, int y) = vp->buffer->get_address_fn;
+#else
+ LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[vp->drawmode];
+#endif
+
+ for (i = 0; i < numpixels; i++)
+ {
+ if (x >= 0 && y >= 0 && x < vp->width && y < vp->height)
+ {
+#if LCDM(DEPTH) >= 8
+ pfunc(fbaddr(x + vp->x, y + vp->y));
+#else
+ pfunc(x + vp->x, y + vp->y);
+#endif
+ }
+
+ if (d < 0)
+ {
+ d += dinc1;
+ x += xinc1;
+ y += yinc1;
+ }
+ else
+ {
+ d += dinc2;
+ x += xinc2;
+ y += yinc2;
+ }
+ }
+}
+
+void LCDFN(drawrect)(int x, int y, int width, int height)
+{
+ if ((width <= 0) || (height <= 0))
+ return;
+
+ int x2 = x + width - 1;
+ int y2 = y + height - 1;
+
+ LCDFN(vline)(x, y, y2);
+ LCDFN(vline)(x2, y, y2);
+ LCDFN(hline)(x, x2, y);
+ LCDFN(hline)(x, x2, y2);
+}
diff --git a/firmware/drivers/lcd-color-common.c b/firmware/drivers/lcd-color-common.c
index 935f4e59dd..5f83160dab 100644
--- a/firmware/drivers/lcd-color-common.c
+++ b/firmware/drivers/lcd-color-common.c
@@ -61,6 +61,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -75,7 +76,7 @@ static void *lcd_frameaddress_default(int x, int y)
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = lcd_current_viewport->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -110,16 +111,6 @@ void lcd_clear_display(void)
/*** parameter handling ***/
-void lcd_set_drawmode(int mode)
-{
- lcd_current_viewport->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int lcd_get_drawmode(void)
-{
- return lcd_current_viewport->drawmode;
-}
-
void lcd_set_foreground(unsigned color)
{
lcd_current_viewport->fg_pattern = color;
@@ -140,37 +131,6 @@ unsigned lcd_get_background(void)
return lcd_current_viewport->bg_pattern;
}
-void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
-{
- lcd_set_drawmode(mode);
- lcd_current_viewport->fg_pattern = fg_color;
- lcd_current_viewport->bg_pattern = bg_color;
-}
-
-int lcd_getwidth(void)
-{
- return lcd_current_viewport->width;
-}
-
-int lcd_getheight(void)
-{
- return lcd_current_viewport->height;
-}
-
-void lcd_setfont(int newfont)
-{
- lcd_current_viewport->font = newfont;
-}
-
-int lcd_getfont(void)
-{
- return lcd_current_viewport->font;
-}
-
-int lcd_getstringsize(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, lcd_current_viewport->font);
-}
void lcd_set_backdrop(fb_data* backdrop)
{
@@ -192,124 +152,6 @@ fb_data* lcd_get_backdrop(void)
return lcd_backdrop;
}
-/* Set a single pixel */
-void lcd_drawpixel(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- lcd_fastpixelfuncs[lcd_current_viewport->drawmode](FBADDR(lcd_current_viewport->x+x, lcd_current_viewport->y+y));
-}
-
-/* Draw a line */
-void lcd_drawline(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
- lcd_hline(x1, x2, y1);
- return;
- }
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
- lcd_vline(x1, y1, y2);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- pfunc(FBADDR(x + lcd_current_viewport->x, y + lcd_current_viewport->y));
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
-}
-
-/* Draw a rectangular box */
-void lcd_drawrect(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- lcd_vline(x, y, y2);
- lcd_vline(x2, y, y2);
- lcd_hline(x, x2, y);
- lcd_hline(x, x2, y2);
-}
-
-
/* Draw a full native bitmap */
void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
{
@@ -397,9 +239,10 @@ __attribute__((weak))
#endif
void lcd_blit_yuv(unsigned char * const src[3],
int src_x, int src_y, int stride,
- int x, int y, int width, int height)
+ int dst_x, int dst_y, int width, int height)
{
const unsigned char *ysrc, *usrc, *vsrc;
+
int linecounter;
fb_data *dst, *row_end;
long z;
@@ -409,10 +252,10 @@ void lcd_blit_yuv(unsigned char * const src[3],
linecounter = height >> 1;
#if LCD_WIDTH >= LCD_HEIGHT
- dst = FBADDR(x, y);
+ dst = FBADDR(dst_x, dst_y);
row_end = dst + width;
#else
- dst = FBADDR(LCD_WIDTH - y - 1, x);
+ dst = FBADDR(LCD_WIDTH - dst_y - 1, dst_x);
row_end = dst + LCD_WIDTH * width;
#endif
@@ -428,10 +271,10 @@ void lcd_blit_yuv(unsigned char * const src[3],
do
{
+ int y, cb, cr, rv, guv, bu, r, g, b;
+
do
{
- int y, cb, cr, rv, guv, bu, r, g, b;
-
y = YFAC*(*ysrc++ - 16);
cb = *usrc++ - 128;
cr = *vsrc++ - 128;
@@ -495,8 +338,6 @@ void lcd_blit_yuv(unsigned char * const src[3],
do
{
- int y, cb, cr, rv, guv, bu, r, g, b;
-
y = YFAC*(*ysrc++ - 16);
cb = *usrc++ - 128;
cr = *vsrc++ - 128;
@@ -561,9 +402,9 @@ void lcd_blit_yuv(unsigned char * const src[3],
while (--linecounter > 0);
#if LCD_WIDTH >= LCD_HEIGHT
- lcd_update_rect(x, y, width, height);
+ lcd_update_rect(dst_x, dst_y, width, height);
#else
- lcd_update_rect(LCD_WIDTH - y - height, x, height, width);
+ lcd_update_rect(LCD_WIDTH - dst_y - height, dst_x, height, width);
#endif
}
@@ -589,7 +430,7 @@ void lcd_gradient_fillrect_part(int x, int y, int width, int height,
x1 = x;
x2 = x + width;
- if (height == 0) return;
+ if (height <= 0) return;
step_mul = (1 << 16) / src_height;
int h_r = RGB_UNPACK_RED(start_rgb);
diff --git a/firmware/drivers/lcd-scroll.c b/firmware/drivers/lcd-scroll.c
index d8bfd72dde..26b15732cd 100644
--- a/firmware/drivers/lcd-scroll.c
+++ b/firmware/drivers/lcd-scroll.c
@@ -64,8 +64,8 @@ void LCDFN(scroll_stop_viewport_rect)(const struct viewport *vp, int x, int y, i
struct scrollinfo *s = &LCDFN(scroll_info).scroll[i];
/* check if the specified area crosses the viewport in some way */
if (s->vp == vp
- && (x < (s->x+s->width) && (x+width) >= s->x)
- && (y < (s->y+s->height) && (y+height) >= s->y))
+ && (x < (s->x+s->width) && (x+width) > s->x)
+ && (y < (s->y+s->height) && (y+height) > s->y))
{
/* inform scroller about end of scrolling */
s->line = NULL;
@@ -180,15 +180,14 @@ bool LCDFN(scroll_now)(struct scrollinfo *s)
return ended;
}
-#if !defined(BOOTLOADER) || defined(HAVE_REMOTE_LCD)
+#if !defined(BOOTLOADER)
static void LCDFN(scroll_worker)(void)
{
int index;
bool makedelay;
- bool is_default;
struct scroll_screen_info *si = &LCDFN(scroll_info);
struct scrollinfo *s;
- struct viewport *vp;
+ struct viewport *oldvp;
int step;
for ( index = 0; index < si->lines; index++ )
@@ -206,8 +205,7 @@ static void LCDFN(scroll_worker)(void)
* is unaware of the swapped viewports. the vp must
* be switched early so that lcd_getstringsize() picks the
* correct font */
- vp = LCDFN(get_viewport)(&is_default);
- LCDFN(set_viewport_ex)(s->vp, 0); /* don't mark the last vp as dirty */
+ oldvp = LCDFN(set_viewport_ex)(s->vp, 0); /* don't mark the last vp as dirty */
makedelay = false;
step = si->step;
@@ -220,7 +218,7 @@ static void LCDFN(scroll_worker)(void)
/* put the line onto the display now */
makedelay = LCDFN(scroll_now(s));
- LCDFN(set_viewport_ex)(vp, 0); /* don't mark the last vp as dirty */
+ LCDFN(set_viewport_ex)(oldvp, 0); /* don't mark the last vp as dirty */
if (makedelay)
s->start_tick += si->delay + si->ticks;
diff --git a/firmware/drivers/rds.c b/firmware/drivers/rds.c
index 0b9b227563..48c7d398a9 100644
--- a/firmware/drivers/rds.c
+++ b/firmware/drivers/rds.c
@@ -25,6 +25,8 @@
#include <kernel.h>
#include "rds.h"
#include "time.h"
+#include "timefuncs.h"
+#include "settings.h"
#include "string-extra.h"
#define TIMED_OUT(tick) \
@@ -72,15 +74,6 @@ static int rt_data_idx; /* rt_data[0 or 1] */
#define RT_DATA_INC(x) rt_data[rt_data_idx ^= (x)]
#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
-#if (CONFIG_RDS & RDS_CFG_ISR)
-/* Functions are called in ISR context */
-#define rds_disable_irq_save() disable_irq_save()
-#define rds_restore_irq(old) restore_irq(old)
-#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
-#define rds_disable_irq_save() 0
-#define rds_restore_irq(old) ((void)(old))
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
-
/* RDS code table G0 to UTF-8 translation */
static const uint16_t rds_tbl_g0[0x100-0x20] =
{
@@ -195,8 +188,6 @@ static void register_activity(void)
/* resets the rds parser */
void rds_reset(void)
{
- int oldlevel = rds_disable_irq_save();
-
/* reset general info */
pi_code = 0;
ct_data = 0;
@@ -210,8 +201,6 @@ void rds_reset(void)
ps_segment = 0;
rt_segment = 0;
#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
-
- rds_restore_irq(oldlevel);
}
/* initialises the rds parser */
@@ -223,8 +212,6 @@ void rds_init(void)
/* sync RDS state */
void rds_sync(void)
{
- int oldlevel = rds_disable_irq_save();
-
if (rds_active) {
if (TIMED_OUT(rds_timeout)) {
rds_reset();
@@ -238,8 +225,18 @@ void rds_sync(void)
}
}
}
+}
- rds_restore_irq(oldlevel);
+static void rds_set_time(time_t time)
+{
+ ct_data = time;
+#ifdef CONFIG_RTC
+ if (ct_data && global_settings.sync_rds_time) {
+ struct tm *tm = gmtime(&ct_data);
+
+ set_time(tm);
+ }
+#endif
}
#if (CONFIG_RDS & RDS_CFG_PROCESS)
@@ -384,7 +381,7 @@ static void handle_group4a(const uint16_t data[4])
seconds += hour * 3600;
seconds += minute * 60;
seconds += ((offset_sig == 0) ? offset_abs : -offset_abs) * 1800;
- ct_data = seconds;
+ rds_set_time(seconds);
}
}
@@ -445,7 +442,7 @@ void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size)
SET_TIMEOUT(rt_copy_tmo, TEXT_TIMEOUT);
break;
case RDS_INFO_CT:
- ct_data = (time_t)data;
+ rds_set_time((time_t)data);
break;
default:;
@@ -458,8 +455,6 @@ void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size)
/* read fully-processed RDS data */
size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size)
{
- int oldlevel = rds_disable_irq_save();
-
rds_sync();
switch (info_id) {
@@ -490,7 +485,5 @@ size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size)
default:
size = 0;
}
-
- rds_restore_irq(oldlevel);
return size;
}
diff --git a/firmware/drivers/rtc/rtc_pcf50605.c b/firmware/drivers/rtc/rtc_pcf50605.c
index 42ea15ff2e..4d2560284c 100644
--- a/firmware/drivers/rtc/rtc_pcf50605.c
+++ b/firmware/drivers/rtc/rtc_pcf50605.c
@@ -84,7 +84,7 @@ int rtc_write_datetime(const struct tm *tm)
* Checks the PCF interrupt 1 register bit 7 to see if an alarm interrupt has
* triggered since last we checked.
*/
-bool rtc_check_alarm_flag(void)
+bool rtc_check_alarm_flag(void)
{
return pcf50605_read(0x02) & 0x80;
}
@@ -125,9 +125,9 @@ bool rtc_check_alarm_started(bool release_alarm)
static bool run_before = false, alarm_state;
bool rc;
- if (run_before) {
+ if (run_before) {
rc = alarm_state;
- alarm_state &= ~release_alarm;
+ alarm_state &= !release_alarm;
} else {
char rt[3], at[3];
/* The Ipod bootloader seems to read (and thus clear) the PCF interrupt
@@ -165,4 +165,3 @@ void rtc_get_alarm(int *h, int *m)
*m = BCD2DEC(buf[0]);
*h = BCD2DEC(buf[1]);
}
-
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index 57006b4e3c..8b4d0a1b19 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -553,32 +553,6 @@ void si4700_dbg_info(struct si4700_dbg_info *nfo)
}
#ifdef HAVE_RDS_CAP
-
-#if (CONFIG_RDS & RDS_CFG_ISR)
-static unsigned char isr_regbuf[(RDSD - STATUSRSSI + 1) * 2];
-
-/* Called by RDS interrupt on target */
-void si4700_rds_interrupt(void)
-{
- si4700_rds_read_raw_async(isr_regbuf, sizeof (isr_regbuf));
-}
-
-/* Handle RDS event from ISR */
-void si4700_rds_process(void)
-{
- uint16_t rds_data[4];
- int index = (RDSA - STATUSRSSI) * 2;
-
- for (int i = 0; i < 4; i++) {
- rds_data[i] = isr_regbuf[index] << 8 | isr_regbuf[index + 1];
- index += 2;
- }
-
- rds_process(rds_data);
-}
-
-#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
-
/* Handle RDS event from thread */
void si4700_rds_process(void)
{
@@ -586,7 +560,7 @@ void si4700_rds_process(void)
if (tuner_powered())
{
- si4700_read_reg(RDSD);
+ si4700_read(6);
#if (CONFIG_RDS & RDS_CFG_POLL)
/* we need to keep track of the ready bit because it stays set for 80ms
* and we must avoid processing it twice */
@@ -603,7 +577,6 @@ void si4700_rds_process(void)
mutex_unlock(&fmr_mutex);
}
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
#if (CONFIG_RDS & RDS_CFG_POLL)
static struct event_queue rds_queue;
@@ -626,7 +599,7 @@ static void NORETURN_ATTR rds_thread(void)
/* power up: timeout after 1 tick, else block indefinitely */
timeout = ev.data ? CONFIG_RDS_POLL_TICKS : TIMEOUT_BLOCK;
break;
- case SYS_TIMEOUT:;
+ case SYS_TIMEOUT:
/* Captures RDS data and processes it */
si4700_rds_process();
break;
diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c
index beecb5ea7a..862445f2e8 100644
--- a/firmware/drivers/usb-designware.c
+++ b/firmware/drivers/usb-designware.c
@@ -896,7 +896,7 @@ static void usb_dw_control_received(struct usb_ctrlrequest* req)
break;
default:
- panicf("%s: bad state=%s", __func__, dw_state_str[ep0.state]);
+ panicf("%s: bad state=%s", __func__, ep0.state >= EP0_NUM_STATES ? "unk" : dw_state_str[ep0.state]);
}
}
diff --git a/firmware/events.c b/firmware/events.c
index 8015442cc2..9b810c5d0d 100644
--- a/firmware/events.c
+++ b/firmware/events.c
@@ -28,70 +28,69 @@
struct sysevent {
unsigned short id;
bool oneshot;
- bool has_user_data;
union {
- void (*callback)(unsigned short id, void *event_data);
- struct {
- void (*callback2)(unsigned short id, void *event_data, void *user_data);
- void *user_data;
- };
+ void (*cb)(unsigned short id, void *event_data);
+ void (*cb_ex)(unsigned short id, void *event_data, void *user_data);
} handler;
+ void *user_data;
};
static struct sysevent events[MAX_SYS_EVENTS];
+static int invalid_userdata;
-static bool do_add_event(unsigned short id, bool oneshot, bool user_data_valid,
+static bool do_add_event(unsigned short id, bool oneshot,
void *handler, void *user_data)
{
- int i;
-
- /* Check if the event already exists. */
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+ size_t free = MAX_SYS_EVENTS;
+ struct sysevent *ev;
+ /* Check if the event already exists. & lowest free slot available */
+ for (size_t i = MAX_SYS_EVENTS - 1; i < MAX_SYS_EVENTS; i--)
{
- if (events[i].handler.callback == handler && events[i].id == id
- && (!user_data_valid || (user_data == events[i].handler.user_data)))
+ ev = &events[i];
+ if (ev->handler.cb == NULL)
+ free = i;
+
+ if (ev->id == id && ev->handler.cb == handler && user_data == ev->user_data)
+ {
return false;
+ }
}
-
- /* Try to find a free slot. */
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+
+ /* is there a free slot? */
+ if (free < MAX_SYS_EVENTS)
{
- if (events[i].handler.callback == NULL)
- {
- events[i].id = id;
- events[i].oneshot = oneshot;
- if ((events[i].has_user_data = user_data_valid))
- events[i].handler.user_data = user_data;
- events[i].handler.callback = handler;
- return true;
- }
+ ev = &events[free];
+ ev->id = id;
+ ev->handler.cb = handler;
+ ev->user_data = user_data;
+ ev->oneshot = oneshot;
+
+ return true;
}
-
+
panicf("event line full");
return false;
}
bool add_event(unsigned short id, void (*handler)(unsigned short id, void *data))
{
- return do_add_event(id, false, false, handler, NULL);
+ return do_add_event(id, false, handler, &invalid_userdata);
}
-bool add_event_ex(unsigned short id, bool oneshot, void (*handler)(unsigned short id, void *event_data, void *user_data), void *user_data)
+bool add_event_ex(unsigned short id, bool oneshot,
+ void (*handler)(unsigned short id, void *event_data, void *user_data), void *user_data)
{
- return do_add_event(id, oneshot, true, handler, user_data);
+ return do_add_event(id, oneshot, handler, user_data);
}
-static void do_remove_event(unsigned short id, bool user_data_valid,
- void *handler, void *user_data)
+static void do_remove_event(unsigned short id, void *handler, void *user_data)
{
- int i;
-
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+ for (size_t i = 0; i < MAX_SYS_EVENTS; i++)
{
- if (events[i].id == id && events[i].handler.callback == handler
- && (!user_data_valid || (user_data == events[i].handler.user_data)))
+ struct sysevent *ev = &events[i];
+ if (ev->id == id && ev->handler.cb == handler && user_data == ev->user_data)
{
- events[i].handler.callback = NULL;
+ ev->handler.cb = NULL;
return;
}
}
@@ -99,31 +98,31 @@ static void do_remove_event(unsigned short id, bool user_data_valid,
void remove_event(unsigned short id, void (*handler)(unsigned short id, void *data))
{
- do_remove_event(id, false, handler, NULL);
+ do_remove_event(id, handler, &invalid_userdata);
}
void remove_event_ex(unsigned short id,
void (*handler)(unsigned short id, void *event_data, void *user_data),
void *user_data)
{
- do_remove_event(id, true, handler, user_data);
+ do_remove_event(id, handler, user_data);
}
void send_event(unsigned short id, void *data)
{
- int i;
-
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+ for (size_t i = 0; i < MAX_SYS_EVENTS; i++)
{
- if (events[i].id == id && events[i].handler.callback != NULL)
+ struct sysevent *ev = &events[i];
+ if (ev->id == id && ev->handler.cb != NULL)
{
- if (events[i].has_user_data)
- events[i].handler.callback2(id, data, events[i].handler.user_data);
+ if (ev->user_data != &invalid_userdata)
+ {
+ ev->handler.cb_ex(id, data, ev->user_data);
+ if (ev->oneshot) /* only _ex events have option of oneshot */
+ ev->handler.cb = NULL;
+ }
else
- events[i].handler.callback(id, data);
-
- if (events[i].oneshot)
- events[i].handler.callback = NULL;
+ ev->handler.cb(id, data);
}
}
}
diff --git a/firmware/export/ak4376.h b/firmware/export/ak4376.h
index eab0bc24f3..ad842b2b80 100644
--- a/firmware/export/ak4376.h
+++ b/firmware/export/ak4376.h
@@ -22,7 +22,9 @@
#ifndef __AK4376_H__
#define __AK4376_H__
-#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP)
+/* The target config must define this; defining it here would prevent
+ the target from supporting audio recording via an alternate codec. */
+/* #define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP) */
#define AUDIOHW_HAVE_SHORT2_ROLL_OFF
#define AK4376_MIN_VOLUME (-890)
@@ -144,8 +146,8 @@ extern void ak4376_set_filter_roll_off(int val);
* and power-up / power-down sequences as a frequency switch, so both settings
* are controlled by this function.
*
- * high power mode -- use power_mode=0
- * low power mode -- use power_mode=1
+ * high power mode -- use power_mode=SOUND_HIGH_POWER
+ * low power mode -- use power_mode=SOUND_LOW_POWER
*/
extern void ak4376_set_freqmode(int fsel, int mult, int power_mode);
diff --git a/firmware/export/ata.h b/firmware/export/ata.h
index 7c5fd3a8d0..7c7c60e898 100644
--- a/firmware/export/ata.h
+++ b/firmware/export/ata.h
@@ -168,6 +168,9 @@ int ata_spinup_time(void); /* ticks */
/* Returns 1 if drive is solid-state */
int ata_disk_isssd(void);
+/* Returns 1 if the drive can be powered off safely */
+int ata_disk_can_poweroff(void);
+
#ifdef HAVE_ATA_DMA
/* Returns current DMA mode */
int ata_get_dma_mode(void);
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index c2c23dfd5c..4fa7841b65 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -154,7 +154,6 @@ void audio_record(const char *filename);
void audio_stop_recording(void);
void audio_pause_recording(void);
void audio_resume_recording(void);
-void audio_new_file(const char *filename);
void audio_set_recording_options(struct audio_recording_options *options);
void audio_set_recording_gain(int left, int right, int type);
unsigned long audio_recorded_time(void);
diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h
index 3f1bcb6feb..067118000e 100644
--- a/firmware/export/audiohw.h
+++ b/firmware/export/audiohw.h
@@ -192,6 +192,8 @@ struct sound_settings_info
#include "tsc2100.h"
#elif defined(HAVE_JZ4740_CODEC)
#include "jz4740-codec.h"
+#elif defined(HAVE_X1000_ICODEC_PLAY)
+#include "x1000-codec.h"
#elif defined(HAVE_AK4537)
#include "ak4537.h"
#elif defined(HAVE_AK4376)
@@ -212,6 +214,7 @@ struct sound_settings_info
#include "pcm1792.h"
#elif defined(HAVE_EROS_QN_CODEC)
#include "eros_qn_codec.h"
+#include "es9018k2m.h"
#elif defined(HAVE_NWZ_LINUX_CODEC)
#include "nwzlinux_codec.h"
#elif defined(HAVE_CS4398)
@@ -237,6 +240,11 @@ struct sound_settings_info
#include "erosqlinux_codec.h"
#endif
+#if defined(HAVE_X1000_ICODEC_REC) && !defined(HAVE_X1000_ICODEC_PLAY)
+/* Targets may have an external DAC above, but use icodec for recording only */
+#include "x1000-codec.h"
+#endif
+
/* convert caps into defines */
#ifdef AUDIOHW_CAPS
/* Tone controls */
@@ -598,6 +606,12 @@ void audiohw_set_filter_roll_off(int val);
#endif
#ifdef AUDIOHW_HAVE_POWER_MODE
+enum audiohw_power_mode
+{
+ SOUND_HIGH_POWER = 0,
+ SOUND_LOW_POWER,
+};
+
/**
* Set DAC's power saving mode.
* @param enable 0 - highest performance, 1 - battery saving
diff --git a/firmware/export/axp-pmu.h b/firmware/export/axp-pmu.h
index 457f746e8c..24c992dea3 100644
--- a/firmware/export/axp-pmu.h
+++ b/firmware/export/axp-pmu.h
@@ -37,8 +37,7 @@
#define ADC_CHARGE_CURRENT 7
#define ADC_DISCHARGE_CURRENT 8
#define ADC_APS_VOLTAGE 9
-#define ADC_BATTERY_POWER 10
-#define NUM_ADC_CHANNELS 11
+#define NUM_ADC_CHANNELS 10
/* ADC sampling rates */
#define AXP_ADC_RATE_25HZ 0
@@ -123,7 +122,6 @@ extern int axp_input_status(void);
extern int axp_adc_read(int adc);
extern int axp_adc_read_raw(int adc);
extern int axp_adc_conv_raw(int adc, int value);
-extern int axp_adc_get_enabled(void);
extern void axp_adc_set_enabled(int adc_bits);
extern int axp_adc_get_rate(void);
extern void axp_adc_set_rate(int rate);
@@ -139,7 +137,7 @@ extern void axp_cc_enable(bool en);
extern bool axp_cc_is_enabled(void);
/* Set/get maximum charging current in milliamps */
-extern void axp_set_charge_current(int maxcurrent);
+extern void axp_set_charge_current(int current_mA);
extern int axp_get_charge_current(void);
/* Set the shutdown bit */
diff --git a/firmware/export/backlight.h b/firmware/export/backlight.h
index 6d029790b3..c94be264ca 100644
--- a/firmware/export/backlight.h
+++ b/firmware/export/backlight.h
@@ -53,8 +53,8 @@ void backlight_set_timeout_plugged(int value);
#ifdef HAS_BUTTON_HOLD
void backlight_hold_changed(bool hold_button);
-void backlight_set_on_button_hold(int index);
#endif
+void backlight_set_on_button_hold(int index);
#if defined(HAVE_LCD_SLEEP) && defined(HAVE_LCD_SLEEP_SETTING)
void lcd_set_sleep_after_backlight_off(int timeout_seconds);
@@ -98,16 +98,12 @@ extern int backlight_brightness;
void backlight_set_brightness(int val);
#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_set_brightness(int val);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTON_LIGHT
void buttonlight_on_ignore(bool value, int timeout);
void buttonlight_on(void);
void buttonlight_off(void);
void buttonlight_set_timeout(int value);
-#endif
/* Private API for use in target tree backlight code only */
#ifdef HAVE_BUTTON_LIGHT
diff --git a/firmware/export/backtrace.h b/firmware/export/backtrace.h
index 283e293b2a..fb007ab004 100644
--- a/firmware/export/backtrace.h
+++ b/firmware/export/backtrace.h
@@ -25,6 +25,9 @@
#ifdef BACKTRACE_UNWARMINDER
#include "backtrace-unwarminder.h"
#endif
+#ifdef BACKTRACE_MIPSUNWINDER
+#include "backtrace-mipsunwinder.h"
+#endif
/* Print a backtrace using lcd_* functions, starting at the given line and updating
* the line number. On targets that support it (typically native targets), the
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 277a1d8632..26ed7395ff 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -208,6 +208,8 @@
Usually application ports, and only
if the estimation is better that ours
(which it probably is) */
+#define CURRENT_MEASURE 8 /* Target can report battery charge and/or
+ * discharge current */
/* CONFIG_LCD */
#define LCD_SSD1815 1 /* as used by Sansa M200 and others */
#define LCD_S1D15E06 3 /* as used by iRiver H100 series */
@@ -378,6 +380,10 @@ Lyre prototype 1 */
#define IMX233_FREESCALE (1 << 0) /* Freescale I.MX233 nonstandard two-level MBR */
#define IMX233_CREATIVE (1 << 1) /* Creative MBLK windowing */
+/* CONFIG_BUFLIB_BACKEND */
+#define BUFLIB_BACKEND_MEMPOOL 0 /* Default memory pool backed buflib */
+#define BUFLIB_BACKEND_MALLOC 1 /* malloc() buflib (for debugging) */
+
/* now go and pick yours */
#if defined(IRIVER_H100)
#include "config/iriverh100.h"
@@ -595,6 +601,15 @@ Lyre prototype 1 */
//#error "unknown hwardware platform!"
#endif
+#ifndef CONFIG_CPU
+#define CONFIG_CPU 0
+#endif
+
+// NOTE: should be placed before sim.h (where CONFIG_CPU is undefined)
+#if !(CONFIG_CPU >= PP5002 && CONFIG_CPU <= PP5022) && CODEC_SIZE >= 0x80000
+#define CODEC_AAC_SBR_DEC
+#endif
+
#ifdef __PCTOOL__
#undef CONFIG_CPU
#define CONFIG_CPU 0
@@ -604,6 +619,10 @@ Lyre prototype 1 */
#undef CONFIG_STORAGE
#endif
+#ifndef CONFIG_BUFLIB_BACKEND
+# define CONFIG_BUFLIB_BACKEND BUFLIB_BACKEND_MEMPOOL
+#endif
+
#ifdef APPLICATION
#define CONFIG_CPU 0
#endif
@@ -611,6 +630,9 @@ Lyre prototype 1 */
/* keep this include after the target configs */
#ifdef SIMULATOR
#include "config/sim.h"
+#ifndef HAVE_POWEROFF_WHILE_CHARGING
+ #define HAVE_POWEROFF_WHILE_CHARGING
+#endif
#endif
#ifndef CONFIG_PLATFORM
@@ -723,7 +745,7 @@ Lyre prototype 1 */
#ifdef HAVE_RDS_CAP
/* combinable bitflags */
-#define RDS_CFG_ISR 0x1 /* uses ISR to process packets */
+/* 0x01 can be reused, was RDS_CFG_ISR */
#define RDS_CFG_PROCESS 0x2 /* uses raw packet processing */
#define RDS_CFG_PUSH 0x4 /* pushes processed information */
#define RDS_CFG_POLL 0x8 /* tuner driver provides a polling function */
@@ -767,6 +789,11 @@ Lyre prototype 1 */
#define LCD_SPLIT_LINES 0
#endif
+/* Most displays have a horizontal stride */
+#ifndef LCD_STRIDEFORMAT
+# define LCD_STRIDEFORMAT HORIZONTAL_STRIDE
+#endif
+
/* Simulator LCD dimensions. Set to standard dimensions if undefined */
#ifndef SIM_LCD_WIDTH
#define SIM_LCD_WIDTH LCD_WIDTH
@@ -864,6 +891,12 @@ Lyre prototype 1 */
#define HAVE_MULTIBOOT
#endif
+/* The lowest numbered volume to read a multiboot redirect from; default is to
+ * allow any volume but some targets may wish to exclude the internal drive. */
+#if defined(HAVE_MULTIBOOT) && !defined(MULTIBOOT_MIN_VOLUME)
+# define MULTIBOOT_MIN_VOLUME 0
+#endif
+
#ifndef NUM_DRIVES
#define NUM_DRIVES 1
#endif
@@ -915,6 +948,9 @@ Lyre prototype 1 */
#define INCLUDE_TIMEOUT_API
#define USB_DRIVER_CLOSE
#endif
+#if CONFIG_CPU == X1000
+#define USB_DRIVER_CLOSE
+#endif
#endif
#else /* !BOOTLOADER */
@@ -964,8 +1000,7 @@ Lyre prototype 1 */
|| defined(CPU_S5L870X) || (CONFIG_CPU == S3C2440) \
|| defined(APPLICATION) || (CONFIG_CPU == PP5002) \
|| (CONFIG_CPU == RK27XX) || (CONFIG_CPU == IMX233) || \
- (defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE)))
+ (defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE))
#define HAVE_SEMAPHORE_OBJECTS
#endif
@@ -979,14 +1014,25 @@ Lyre prototype 1 */
#define ROCKBOX_STRICT_ALIGN 1
#endif
+/*
+ * These macros are for switching on unified syntax in inline assembly.
+ * Older versions of GCC emit assembly in divided syntax with no option
+ * to enable unified syntax.
+ *
+ * FIXME: This needs to be looked at after the toolchain bump
+ */
+#define BEGIN_ARM_ASM_SYNTAX_UNIFIED ".syntax unified\n"
+#define END_ARM_ASM_SYNTAX_UNIFIED ".syntax divided\n"
+
#if defined(CPU_ARM) && defined(__ASSEMBLER__)
+.syntax unified
/* ARMv4T doesn't switch the T bit when popping pc directly, we must use BX */
.macro ldmpc cond="", order="ia", regs
#if ARM_ARCH == 4 && defined(USE_THUMB)
- ldm\cond\order sp!, { \regs, lr }
+ ldm\order\cond sp!, { \regs, lr }
bx\cond lr
#else
- ldm\cond\order sp!, { \regs, pc }
+ ldm\order\cond sp!, { \regs, pc }
#endif
.endm
.macro ldrpc cond=""
@@ -1052,7 +1098,7 @@ Lyre prototype 1 */
#if (defined(CPU_PP) || (CONFIG_CPU == AS3525) || (CONFIG_CPU == AS3525v2) || \
(CONFIG_CPU == IMX31L) || (CONFIG_CPU == IMX233) || \
- (CONFIG_CPU == RK27XX) || defined(CPU_COLDFIRE)) \
+ (CONFIG_CPU == RK27XX) || defined(CPU_MIPS) || defined(CPU_COLDFIRE)) \
&& (CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(BOOTLOADER)
/* Functions that have INIT_ATTR attached are NOT guaranteed to survive after
* root_menu() has been called. Their code may be overwritten by other data or
@@ -1064,8 +1110,16 @@ Lyre prototype 1 */
* only while main() runs), otherwise things may go wild,
* from crashes to freezes to exploding daps.
*/
-#define INIT_ATTR __attribute__ ((section(".init")))
-#define INITDATA_ATTR __attribute__ ((section(".initdata")))
+
+
+#if defined(__APPLE__) && defined(__MACH__)
+ #define INIT_ATTR __attribute__((section ("__INIT,.init")))
+ #define INITDATA_ATTR __attribute__((section ("__INITDATA,.initdata")))
+#else
+ #define INIT_ATTR __attribute__ ((section(".init")))
+ #define INITDATA_ATTR __attribute__ ((section(".initdata")))
+#endif
+
#define HAVE_INIT_ATTR
#else
#define INIT_ATTR
@@ -1167,6 +1221,11 @@ Lyre prototype 1 */
#define INCLUDE_TIMEOUT_API
#endif /* HAVE_USB_CHARGING_ENABLE && HAVE_USBSTACK */
+#if defined(USB_STATUS_BY_EVENT) && defined(HAVE_USBSTACK)
+/* Status by event requires timeout for debouncing */
+# define INCLUDE_TIMEOUT_API
+#endif
+
#ifndef SIMULATOR
#if defined(HAVE_USBSTACK) || (CONFIG_STORAGE & STORAGE_NAND) || (CONFIG_STORAGE & STORAGE_RAMDISK)
#define STORAGE_GET_INFO
@@ -1276,6 +1335,14 @@ Lyre prototype 1 */
#endif /* SIMULATOR */
#endif /* default SDL SW volume conditions */
+#if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_SCREENDUMP)
+# define HAVE_SCREENDUMP
+#endif
+
+#if !defined(BOOTLOADER) && MEMORYSIZE > 2
+# define HAVE_PERCEPTUAL_VOLUME
+#endif
+
/* null audiohw setting macro for when codec header is included for reasons
other than audio support */
#define AUDIOHW_SETTING(name, us, nd, st, minv, maxv, defv, expr...)
diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h
index f68830e211..4f87282f1f 100644
--- a/firmware/export/config/erosqnative.h
+++ b/firmware/export/config/erosqnative.h
@@ -63,6 +63,8 @@
#define HAVE_EROS_QN_CODEC
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP)
+#define AUDIOHW_HAVE_SHORT_ROLL_OFF
/* use high-bitdepth volume scaling */
#define PCM_NATIVE_BITDEPTH 24
@@ -78,12 +80,13 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
/* Power management */
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE/*|CURRENT_MEASURE*/)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
@@ -103,6 +106,10 @@
#define CURRENT_BACKLIGHT 180
#define CURRENT_MAX_CHG 500 // bursts higher if needed
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.aigo_erosqn"
+
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
@@ -122,6 +129,11 @@
#define USB_WRITE_BUFFER_SIZE (128 * 1024)
#endif
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_PREV|BUTTON_NEXT))
+#endif
+
/* Rockbox capabilities */
#define HAVE_VOLUME_IN_LIST
#define HAVE_FAT16SUPPORT
@@ -131,3 +143,4 @@
#define HAVE_TAGCACHE
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h
index 61b6123a67..86a9b05402 100644
--- a/firmware/export/config/fiiom3k.h
+++ b/firmware/export/config/fiiom3k.h
@@ -55,11 +55,16 @@
/* Codec / audio hardware defines */
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define REC_SAMPR_CAPS (SAMPR_CAP_ALL_96 & ~SAMPR_CAP_64)
+#define INPUT_SRC_CAPS SRC_CAP_MIC
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP|MIC_GAIN_CAP)
+#define HAVE_RECORDING
+#define HAVE_RECORDING_WITHOUT_MONITORING
#define HAVE_AK4376
+#define HAVE_X1000_ICODEC_REC
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
-
-/* TODO: Need to implement recording */
+#define DEFAULT_REC_MIC_GAIN 12
/* Button defines */
#define CONFIG_KEYPAD FIIO_M3K_PAD
@@ -79,6 +84,7 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
@@ -88,7 +94,7 @@
/* TODO: implement HAVE_RTC_ALARM */
/* Power management */
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
@@ -104,6 +110,10 @@
#define BATTERY_CAPACITY_INC 0
#define BATTERY_TYPES_COUNT 1
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.fiio_m3k"
+
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
@@ -123,6 +133,11 @@
#define USB_WRITE_BUFFER_SIZE (128 * 1024)
#endif
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_VOL_UP|BUTTON_VOL_DOWN))
+#endif
+
/* Rockbox capabilities */
#define HAVE_FAT16SUPPORT
#define HAVE_ALBUMART
@@ -132,5 +147,5 @@
#define HAVE_VOLUME_IN_LIST
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
-#define HAVE_LOCKED_ACTIONS
#define AB_REPEAT_ENABLE
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/config/fiiom3klinux.h b/firmware/export/config/fiiom3klinux.h
index fea464c2de..22b996f6bf 100644
--- a/firmware/export/config/fiiom3klinux.h
+++ b/firmware/export/config/fiiom3klinux.h
@@ -69,7 +69,7 @@
#define CONFIG_RTC APPLICATION
/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x80000
+#define CODEC_SIZE 0x100000
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x100000
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h
index 8d82b44065..edf76d0e98 100644
--- a/firmware/export/config/gigabeats.h
+++ b/firmware/export/config/gigabeats.h
@@ -121,7 +121,6 @@
/* Define this if you have a SI4700 fm radio tuner */
#define CONFIG_TUNER SI4700
#define HAVE_RDS_CAP
-#define CONFIG_RDS (RDS_CFG_ISR | RDS_CFG_PROCESS)
/* define this if you can flip your LCD */
#define HAVE_LCD_FLIP
@@ -164,7 +163,7 @@
#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS)
/* Define this if target has an additional number of threads specific to it */
-#define TARGET_EXTRA_THREADS 1
+#define TARGET_EXTRA_THREADS 2 /* one is for RDS */
/* Type of mobile power - check this out */
#define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */
diff --git a/firmware/export/config/hibylinux.h b/firmware/export/config/hibylinux.h
index f1e52917ba..ad168c9c01 100644
--- a/firmware/export/config/hibylinux.h
+++ b/firmware/export/config/hibylinux.h
@@ -34,7 +34,7 @@
#define HAVE_TAGCACHE
/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x80000
+#define CODEC_SIZE 0x100000
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x100000
diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h
index 2e4afbdf63..b03b1131bd 100644
--- a/firmware/export/config/ipod6g.h
+++ b/firmware/export/config/ipod6g.h
@@ -133,11 +133,9 @@
/* Define this if you have a software controlled poweroff */
#define HAVE_SW_POWEROFF
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x100000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x80000
+/* Buffer for plugins and codecs. */
+#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+#define CODEC_SIZE 0x100000 /* 1 MiB */
/* 6g has a standard battery of 550mAh, except for the thick 6g (2007 160gb)
* which has a standard battery of 850mAh.
diff --git a/firmware/export/config/mrobe500.h b/firmware/export/config/mrobe500.h
index ffc8a6bfb8..621c0b75ca 100644
--- a/firmware/export/config/mrobe500.h
+++ b/firmware/export/config/mrobe500.h
@@ -65,9 +65,6 @@
/* define this if the target has volume keys which can be used in the lists */
#define HAVE_VOLUME_IN_LIST
-/* define this if you want viewport clipping enabled for safe LCD functions */
-#define HAVE_VIEWPORT_CLIP
-
/* LCD dimensions */
#define CONFIG_LCD LCD_MROBE500
@@ -223,7 +220,7 @@
#define HAVE_USB_HID_MOUSE
/* Define this if hardware supports alternate blitting */
-#define HAVE_LCD_MODES LCD_MODE_RGB565 | LCD_MODE_YUV | LCD_MODE_PAL256
+#define HAVE_LCD_MODES (LCD_MODE_RGB565 | LCD_MODE_YUV | LCD_MODE_PAL256)
#define CONFIG_CPU DM320
diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index e0df0c28c3..99eb1d8832 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -11,6 +11,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.clip+"
+#define MULTIBOOT_MIN_VOLUME 1
#define HAVE_MULTIDRIVE
#define NUM_DRIVES 2
@@ -201,6 +202,7 @@
#define HAVE_USBSTACK
#define USB_VENDOR_ID 0x0781
#define USB_PRODUCT_ID 0x74d1
+#define HAVE_USB_HID_MOUSE
#define HAVE_BOOTLOADER_USB_MODE
/* Virtual LED (icon) */
diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h
index 6afe6e129e..d8b18e1a18 100644
--- a/firmware/export/config/sansaclipzip.h
+++ b/firmware/export/config/sansaclipzip.h
@@ -11,6 +11,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.clipzip"
+#define MULTIBOOT_MIN_VOLUME 1
#define HAVE_MULTIDRIVE
#define NUM_DRIVES 2
diff --git a/firmware/export/config/sansaconnect.h b/firmware/export/config/sansaconnect.h
index fa929f3c10..a06ea9b207 100644
--- a/firmware/export/config/sansaconnect.h
+++ b/firmware/export/config/sansaconnect.h
@@ -66,9 +66,6 @@
/* define this if the target has volume keys which can be used in the lists */
#define HAVE_VOLUME_IN_LIST
-/* define this if you want viewport clipping enabled for safe LCD functions */
-#define HAVE_VIEWPORT_CLIP
-
/* LCD dimensions */
#define CONFIG_LCD LCD_CONNECT
diff --git a/firmware/export/config/sansae200.h b/firmware/export/config/sansae200.h
index 59e531241f..6bb2df9bc8 100644
--- a/firmware/export/config/sansae200.h
+++ b/firmware/export/config/sansae200.h
@@ -13,6 +13,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.e200"
+#define MULTIBOOT_MIN_VOLUME 1
/* define this if you have recording possibility */
#define HAVE_RECORDING
diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h
index b72f5b5198..e8f7157deb 100644
--- a/firmware/export/config/sansafuze.h
+++ b/firmware/export/config/sansafuze.h
@@ -10,6 +10,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze"
+#define MULTIBOOT_MIN_VOLUME 1
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h
index a853cb13b5..9b91e114bc 100644
--- a/firmware/export/config/sansafuzeplus.h
+++ b/firmware/export/config/sansafuzeplus.h
@@ -12,6 +12,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze+"
+#define MULTIBOOT_MIN_VOLUME 1
// HW can do it but we don't have the IRAM for mix buffers
//#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h
index fc2ff68257..96d3b2deee 100644
--- a/firmware/export/config/sansafuzev2.h
+++ b/firmware/export/config/sansafuzev2.h
@@ -10,6 +10,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze2"
+#define MULTIBOOT_MIN_VOLUME 1
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h
index 16ce888958..6f5365a97e 100644
--- a/firmware/export/config/shanlingq1.h
+++ b/firmware/export/config/shanlingq1.h
@@ -67,6 +67,7 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
@@ -76,7 +77,7 @@
/* TODO: implement HAVE_RTC_ALARM */
/* Power management */
-#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE)
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
@@ -94,6 +95,10 @@
#define BATTERY_CAPACITY_INC 0
#define BATTERY_TYPES_COUNT 1
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.shanling_q1"
+
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
@@ -113,6 +118,11 @@
#define USB_WRITE_BUFFER_SIZE (128 * 1024)
#endif
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_PREV|BUTTON_NEXT))
+#endif
+
/* Rockbox capabilities */
#define HAVE_FAT16SUPPORT
#define HAVE_ALBUMART
@@ -123,3 +133,4 @@
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
#define AB_REPEAT_ENABLE
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/disk.h b/firmware/export/disk.h
index c66028fe45..e465552fdc 100644
--- a/firmware/export/disk.h
+++ b/firmware/export/disk.h
@@ -35,6 +35,9 @@ struct partinfo
#define PARTITION_TYPE_FAT32_LBA 0x0c
#define PARTITION_TYPE_FAT16 0x06
#define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84
+#define PARTITION_TYPE_GPT_GUARD 0xee
+
+#define MAX_PARTITIONS_PER_DRIVE 4 /* Needs to be at least 4 */
bool disk_init(IF_MD_NONVOID(int drive));
bool disk_partinfo(int partition, struct partinfo *info);
diff --git a/firmware/export/eros_qn_codec.h b/firmware/export/eros_qn_codec.h
index 851ab63362..223ef06779 100644
--- a/firmware/export/eros_qn_codec.h
+++ b/firmware/export/eros_qn_codec.h
@@ -32,13 +32,25 @@
#define PCM5102A_VOLUME_MIN -740
#define PCM5102A_VOLUME_MAX -20
-/* a small DC offset prevents play/pause clicking due to the DAC auto-muting */
+/* a small DC offset prevents play/pause clicking due to the PCM5102A DAC auto-muting */
#define PCM_DC_OFFSET_VALUE -1
AUDIOHW_SETTING(VOLUME, "dB", 0, 2, PCM5102A_VOLUME_MIN/10, PCM5102A_VOLUME_MAX/10, 0)
+/* flag indicating whether this is a new revision unit with the ES9018K2M DAC */
+extern int es9018k2m_present_flag;
+
+/* Switch the output sink. 0 - headphones, 1 - line out */
+void eros_qn_switch_output(int select);
+
+/* Record last volume setting for switching between headphones/line out */
+void eros_qn_set_last_vol(long int vol_l, long int vol_r);
+
/* this just calls audiohw_set_volume() with the last (locally) known volume,
* used for switching to/from fixed line out volume. */
-void pcm5102_set_outputs(void);
+void eros_qn_set_outputs(void);
+
+/* returns (global_settings.volume_limit * 10) */
+int eros_qn_get_volume_limit(void);
#endif
diff --git a/firmware/export/es9018k2m.h b/firmware/export/es9018k2m.h
new file mode 100644
index 0000000000..2824fed505
--- /dev/null
+++ b/firmware/export/es9018k2m.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * Copyright (c) 2023 Dana Conrad
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _ES9018K2M_H
+#define _ES9018K2M_H
+
+//======================================================================================
+// ES9018K2M support stuff
+// Implement audiohw_* functions in audiohw-*.c. These functions are utilities which
+// may be used there.
+
+// AUDIOHW_SETTING(VOLUME, *) not set here, probably best to put it in device-specific *_codec.h
+#ifdef AUDIOHW_HAVE_SHORT_ROLL_OFF
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 3, 0)
+#endif
+
+#ifndef ES9018K2M_VOLUME_MIN
+# define ES9018K2M_VOLUME_MIN -1270
+#endif
+
+#ifndef ES9018K2M_VOLUME_MAX
+# define ES9018K2M_VOLUME_MAX 0
+#endif
+
+#define ES9018K2M_REG0_SYSTEM_SETTINGS 0
+#define ES9018K2M_REG1_INPUT_CONFIG 1
+#define ES9018K2M_REG4_AUTOMUTE_TIME 4
+#define ES9018K2M_REG5_AUTOMUTE_LEVEL 5
+#define ES9018K2M_REG6_DEEMPHASIS 6
+#define ES9018K2M_REG7_GENERAL_SETTINGS 7
+#define ES9018K2M_REG8_GPIO_CONFIG 8
+#define ES9018K2M_REG10_MASTER_MODE_CTRL 10
+#define ES9018K2M_REG11_CHANNEL_MAPPING 11
+#define ES9018K2M_REG12_DPLL_SETTINGS 12
+#define ES9018K2M_REG13_THD_COMP 13
+#define ES9018K2M_REG14_SOFTSTART_SETTINGS 14
+#define ES9018K2M_REG15_VOLUME_L 15
+#define ES9018K2M_REG16_VOLUME_R 16
+#define ES9018K2M_REG21_GPIO_INPUT_SELECT 21
+
+/* writes volume levels to DAC over I2C, asynchronously */
+void es9018k2m_set_volume_async(int vol_l, int vol_r);
+
+/* write filter roll-off setting to DAC over I2C, synchronously */
+void es9018k2m_set_filter_roll_off(int value);
+
+/* writes a single register */
+/* returns I2C_STATUS_OK upon success, I2C_STATUS_* errors upon error */
+int es9018k2m_write_reg(uint8_t reg, uint8_t val);
+
+/* reads a single register */
+/* returns register value, or -1 upon error */
+int es9018k2m_read_reg(uint8_t reg);
+
+#endif \ No newline at end of file
diff --git a/firmware/export/font.h b/firmware/export/font.h
index 067c67e43d..2334a8bd1a 100644
--- a/firmware/export/font.h
+++ b/firmware/export/font.h
@@ -117,7 +117,7 @@ struct font {
/* font routines*/
void font_init(void) INIT_ATTR;
-const char* font_filename(int font_id);
+bool font_filename_matches_loaded_id(int font_id, const char *filename);
int font_load(const char *path);
int font_load_ex(const char *path, size_t buffer_size, int glyphs);
void font_unload(int font_id);
@@ -132,7 +132,7 @@ void font_disable_all(void);
void font_enable_all(void);
struct font* font_get(int font);
-
+int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnumber);
int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber);
int font_get_width(struct font* ft, unsigned short ch);
const unsigned char * font_get_bits(struct font* ft, unsigned short ch);
diff --git a/firmware/export/i2c-coldfire.h b/firmware/export/i2c-coldfire.h
index b0d21a8631..f98daf5d5f 100644
--- a/firmware/export/i2c-coldfire.h
+++ b/firmware/export/i2c-coldfire.h
@@ -30,7 +30,7 @@
#include "cpu.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_read (volatile unsigned char *iface, unsigned char addr,
unsigned char *buf, int count);
int i2c_write(volatile unsigned char *iface, unsigned char addr,
diff --git a/firmware/export/i2c-rk27xx.h b/firmware/export/i2c-rk27xx.h
index 96baf566a7..d16d4c3dbb 100644
--- a/firmware/export/i2c-rk27xx.h
+++ b/firmware/export/i2c-rk27xx.h
@@ -24,7 +24,7 @@
#include "config.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(unsigned char slave, int address, int len, const unsigned char *data);
int i2c_read(unsigned char slave, int address, int len, unsigned char *data);
diff --git a/firmware/export/i2c-s5l8700.h b/firmware/export/i2c-s5l8700.h
index e6ffcfae55..607f361384 100644
--- a/firmware/export/i2c-s5l8700.h
+++ b/firmware/export/i2c-s5l8700.h
@@ -24,7 +24,7 @@
#include "config.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(unsigned char slave, int address, int len, const unsigned char *data);
int i2c_read(unsigned char slave, int address, int len, unsigned char *data);
diff --git a/firmware/export/i2c-s5l8702.h b/firmware/export/i2c-s5l8702.h
index f671e4059e..a1e7be04b4 100644
--- a/firmware/export/i2c-s5l8702.h
+++ b/firmware/export/i2c-s5l8702.h
@@ -24,7 +24,7 @@
#include "config.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(int bus, unsigned char slave, int address, int len, const unsigned char *data);
int i2c_read(int bus, unsigned char slave, int address, int len, unsigned char *data);
diff --git a/firmware/export/i2c.h b/firmware/export/i2c.h
index ac9ddba055..08562b198c 100644
--- a/firmware/export/i2c.h
+++ b/firmware/export/i2c.h
@@ -21,7 +21,9 @@
#ifndef I2C_H
#define I2C_H
-extern void i2c_init(void);
+#include "config.h"
+
+extern void i2c_init(void) INIT_ATTR;
extern void i2c_begin(void);
extern void i2c_end(void);
extern int i2c_write(int device, const unsigned char* buf, int count );
diff --git a/firmware/export/jz4740.h b/firmware/export/jz4740.h
index 379c9f8aaa..354379a950 100644
--- a/firmware/export/jz4740.h
+++ b/firmware/export/jz4740.h
@@ -4986,6 +4986,7 @@ do{ \
#define IPU_V_BASE 0xB3080000
#define IPU__SIZE 0x00001000
+#ifndef __ASSEMBLY__
struct ipu_module
{
unsigned int reg_ctrl; // 0x0
@@ -5022,7 +5023,7 @@ struct Ration2m
float ratio;
int n, m;
};
-
+#endif /* !__ASSEMBLY__ */
// Register offset
#define REG_CTRL 0x0
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index ffaf1a63d2..67b22190ad 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -82,7 +82,7 @@ enum screen_type {
struct scrollinfo;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define STRIDE_MAIN(w, h) (h)
#else
#define STRIDE_MAIN(w, h) (w)
@@ -178,9 +178,12 @@ struct frame_buffer_t {
#define VP_IS_RTL(vp) (((vp)->flags & VP_FLAG_ALIGNMENT_MASK) == VP_FLAG_ALIGN_RIGHT)
-#define VP_FLAG_VP_DIRTY 0x4000
-#define VP_FLAG_CLEAR_FLAG 0x8000
+#define VP_FLAG_OWNER_UPDATE 0x2000 /* block update_vp functions */
+#define VP_FLAG_VP_DIRTY 0x4000
+#define VP_FLAG_CLEAR_FLAG 0x8000
#define VP_FLAG_VP_SET_CLEAN (VP_FLAG_CLEAR_FLAG | VP_FLAG_VP_DIRTY)
+/* flags set by viewport_set_defaults() */
+#define VP_DEFAULT_FLAGS (VP_FLAG_VP_DIRTY)
struct viewport {
int x;
@@ -493,7 +496,7 @@ typedef void lcd_blockfunc_type(fb_data *address, unsigned mask, unsigned bits);
#endif
#ifndef LCD_NBELEMS
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCD_NBELEMS(w, h) (((w-1)*STRIDE_MAIN(w, h)) + h)
#else
#define LCD_NBELEMS(w, h) (((h-1)*STRIDE_MAIN(w, h)) + w)
@@ -507,7 +510,9 @@ typedef void lcd_blockfunc_type(fb_data *address, unsigned mask, unsigned bits);
extern struct viewport* lcd_current_viewport;
-#define FBADDR(x,y) ((fb_data*) lcd_current_viewport->buffer->get_address_fn(x, y))
+#define FB_CURRENTVP_BUFFER (lcd_current_viewport->buffer)
+#define FBADDRBUF(buffer,x,y) ((fb_data*) buffer->get_address_fn(x,y))
+#define FBADDR(x,y) (FBADDRBUF(lcd_current_viewport->buffer,x,y))
#define FRAMEBUFFER_SIZE (sizeof(fb_data)*LCD_FBWIDTH*LCD_FBHEIGHT)
diff --git a/firmware/export/linuxboot.h b/firmware/export/linuxboot.h
new file mode 100644
index 0000000000..de6f24bf57
--- /dev/null
+++ b/firmware/export/linuxboot.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __LINUXBOOT_H__
+#define __LINUXBOOT_H__
+
+#include "system.h"
+#include <stddef.h>
+#include <sys/types.h>
+
+/*
+ * From u-boot's include/image.h
+ * SPDX-License-Identifier: GPL-2.0+
+ * (C) Copyright 2008 Semihalf
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#define IH_MAGIC 0x27051956
+#define IH_NMLEN 32
+
+enum
+{
+ IH_ARCH_INVALID,
+ IH_ARCH_ALPHA,
+ IH_ARCH_ARM,
+ IH_ARCH_I386,
+ IH_ARCH_IA64,
+ IH_ARCH_MIPS,
+ IH_ARCH_MIPS64,
+ IH_ARCH_PPC,
+ IH_ARCH_S390,
+ IH_ARCH_SH,
+ IH_ARCH_SPARC,
+ IH_ARCH_SPARC64,
+ IH_ARCH_M68K,
+ /* NOTE: Other archs not relevant and omitted here, can add if needed */
+ IH_ARCH_COUNT,
+};
+
+enum
+{
+ IH_TYPE_INVALID = 0,
+ IN_TYPE_STANDALONE,
+ IH_TYPE_KERNEL,
+ IH_TYPE_RAMDISK,
+ IH_TYPE_MULTI,
+ IH_TYPE_FIRMWARE,
+ IH_TYPE_SCRIPT,
+ IH_TYPE_FILESYSTEM,
+ IH_TYPE_FLATDT,
+ /* NOTE: Other types not relevant and omitted here, can add if needed */
+ IH_TYPE_COUNT,
+};
+
+enum
+{
+ IH_COMP_NONE = 0,
+ IH_COMP_GZIP,
+ IH_COMP_BZIP2,
+ IH_COMP_LZMA,
+ IH_COMP_LZO,
+ IH_COMP_LZ4,
+ IH_COMP_ZSTD,
+ IH_COMP_COUNT,
+};
+
+/* Legacy U-Boot image header as produced by mkimage(1).
+ *
+ * WARNING: all fields are big-endian so you usually do not want to
+ * access them directly, use the accessor functions instead.
+ */
+struct uimage_header
+{
+ uint32_t ih_magic;
+ uint32_t ih_hcrc;
+ uint32_t ih_time;
+ uint32_t ih_size;
+ uint32_t ih_load;
+ uint32_t ih_ep;
+ uint32_t ih_dcrc;
+ uint8_t ih_os;
+ uint8_t ih_arch;
+ uint8_t ih_type;
+ uint8_t ih_comp;
+ uint8_t ih_name[IH_NMLEN];
+};
+
+#define _uimage_get32_f(name) \
+ static inline uint32_t uimage_get_##name(const struct uimage_header* uh) \
+ { return betoh32(uh->ih_##name); }
+_uimage_get32_f(magic)
+_uimage_get32_f(hcrc)
+_uimage_get32_f(time)
+_uimage_get32_f(size)
+_uimage_get32_f(load)
+_uimage_get32_f(ep)
+_uimage_get32_f(dcrc)
+#undef _uimage_get32_f
+
+#define _uimage_set32_f(name) \
+ static inline void uimage_set_##name(struct uimage_header* uh, uint32_t val) \
+ { uh->ih_##name = htobe32(val); }
+_uimage_set32_f(magic)
+_uimage_set32_f(hcrc)
+_uimage_set32_f(time)
+_uimage_set32_f(size)
+_uimage_set32_f(load)
+_uimage_set32_f(ep)
+_uimage_set32_f(dcrc)
+#undef _uimage_set32_f
+
+#define _uimage_get8_f(name) \
+ static inline uint8_t uimage_get_##name(const struct uimage_header* uh) \
+ { return uh->ih_##name; }
+_uimage_get8_f(os)
+_uimage_get8_f(arch)
+_uimage_get8_f(type)
+_uimage_get8_f(comp)
+#undef _uimage_get8_f
+
+#define _uimage_set8_f(name) \
+ static inline void uimage_set_##name(struct uimage_header* uh, uint8_t val) \
+ { uh->ih_##name = val; }
+_uimage_set8_f(os)
+_uimage_set8_f(arch)
+_uimage_set8_f(type)
+_uimage_set8_f(comp)
+#undef _uimage_set8_f
+
+/*
+ * uImage utilities
+ */
+
+/** Reader callback for use with `uimage_load`
+ *
+ * \param buf Buffer to write into
+ * \param size Number of bytes to read
+ * \param ctx State argument
+ * \return Number of bytes actually read, or -1 on error.
+ */
+typedef ssize_t(*uimage_reader)(void* buf, size_t size, void* ctx);
+
+/** Calculate U-Boot style CRC */
+uint32_t uimage_crc(uint32_t crc, const void* data, size_t size);
+
+/** Calculate CRC of a uImage header */
+uint32_t uimage_calc_hcrc(const struct uimage_header* uh);
+
+/** Load and decompress a uImage
+ *
+ * \param uh Returned header struct (will be filled with read data)
+ * \param out_size Returned size of the decompressed data
+ * \param reader Data reader function
+ * \param rctx Context argument for the reader function
+ * \return Buflib handle containing the decompressed data, or negative on error
+ *
+ * This function will read a uImage, verify checksums and decompress the image
+ * data into a non-moveable buflib allocation. The length of the compressed
+ * data will be taken from the uImage header.
+ */
+int uimage_load(struct uimage_header* uh, size_t* out_size,
+ uimage_reader reader, void* rctx);
+
+/** File reader for use with `uimage_load`
+ *
+ * \param ctx File descriptor, casted to `void*`
+ */
+ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx);
+
+/* helper for patching broken self-extracting kernels on MIPS */
+uint32_t mips_linux_stub_get_entry(void** code_start, size_t code_size);
+
+#endif /* __LINUXBOOT_H__ */
diff --git a/firmware/export/mi4-loader.h b/firmware/export/mi4-loader.h
index f66164ec2c..adc43ebf64 100644
--- a/firmware/export/mi4-loader.h
+++ b/firmware/export/mi4-loader.h
@@ -21,6 +21,9 @@
*
****************************************************************************/
+#ifndef __MI4_LOADER_H__
+#define __MI4_LOADER_H__
+
#include <stdint.h>
#define MI4_HEADER_SIZE 0x200
@@ -50,21 +53,4 @@ struct tea_key {
int load_mi4(unsigned char* buf, const char* firmware, unsigned int buffer_size);
const char *mi4_strerror(int8_t errno);
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-
-/* TODO needs mapped back to debug_menu if root redirect ever becomes a reality */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware);
-#endif
+#endif /* __MI4_LOADER_H__ */
diff --git a/firmware/export/multiboot.h b/firmware/export/multiboot.h
new file mode 100644
index 0000000000..0132b8531f
--- /dev/null
+++ b/firmware/export/multiboot.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2017, 2020 by William Wilgus
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __MULTIBOOT_H__
+#define __MULTIBOOT_H__
+
+extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume);
+#ifdef HAVE_MULTIBOOT
+extern int get_redirect_dir(char* buf, int buffer_size, int volume,
+ const char* rootdir, const char* firmware);
+#endif
+
+#endif /* __MULTIBOOT_H__ */
diff --git a/firmware/export/mv.h b/firmware/export/mv.h
index 3657ef6c98..966ff9a032 100644
--- a/firmware/export/mv.h
+++ b/firmware/export/mv.h
@@ -87,6 +87,10 @@
#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1)
#define VOL_NUM_MAX 100
+#ifndef ROOT_VOLUME
+#define ROOT_VOLUME INT_MAX
+#endif
+
#else /* empty definitions if no multi-volume */
#define IF_MV(x...)
#define IF_MV_NONVOID(x...) void
@@ -99,6 +103,13 @@
#define CHECK_DRV(drive) \
((unsigned int)IF_MD_DRV(drive) < NUM_DRIVES)
+/* contains info about a volume */
+struct volumeinfo
+{
+ int drive; /* drive number */
+ int partition; /* partition number (0 for superfloppy drives) */
+};
+
/* Volume-centric functions (in disk.c) */
void volume_recalc_free(IF_MV_NONVOID(int volume));
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume));
@@ -121,4 +132,6 @@ static inline int volume_drive(int volume)
}
#endif /* HAVE_MULTIDRIVE */
+int volume_partition(int volume);
+
#endif /* __MV_H__ */
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h
index 350dd4e548..1b18f22d06 100644
--- a/firmware/export/pathfuncs.h
+++ b/firmware/export/pathfuncs.h
@@ -30,10 +30,15 @@
/* useful char constants that could be reconfigured if desired */
#define PATH_SEPCH '/'
#define PATH_SEPSTR "/"
+#define PATH_ROOTCHR '/'
#define PATH_ROOTSTR "/"
#define PATH_BADSEPCH '\\'
#define PATH_DRVSEPCH ':'
+#ifndef ROOT_VOLUME
+#define ROOT_VOLUME INT_MAX
+#endif
+
/* a nicer way to check for "." and ".." than two strcmp() calls */
static inline bool is_dotdir_name(const char *name)
{
@@ -74,7 +79,9 @@ static inline bool name_is_dot_dot(const char *name)
#ifdef HAVE_MULTIVOLUME
int path_strip_volume(const char *name, const char **nameptr, bool greedy);
+int path_strip_last_volume(const char *name, const char **nameptr, bool greedy);
int get_volume_name(int volume, char *name);
+int make_volume_root(int volume, char *dst);
#endif
int path_strip_drive(const char *name, const char **nameptr, bool greedy);
@@ -87,6 +94,8 @@ void path_remove_dot_segments(char *dstpath, const char *path);
/* constants useable in basepath and component */
#define PA_SEP_HARD NULL /* separate even if base is empty */
#define PA_SEP_SOFT "" /* separate only if base is nonempty */
+size_t path_append_ex(char *buf, const char *basepath, size_t basepath_max,
+ const char *component, size_t bufsize);
size_t path_append(char *buffer, const char *basepath, const char *component,
size_t bufsize);
ssize_t parse_path_component(const char **pathp, const char **namep);
diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h
index 70f2dc2ba2..fc48a943fd 100644
--- a/firmware/export/pcm_sampr.h
+++ b/firmware/export/pcm_sampr.h
@@ -427,10 +427,10 @@ extern const unsigned long rec_freq_sampr[REC_NUM_FREQ];
#ifdef CONFIG_SAMPR_TYPES
-#define SAMPR_TYPE_MASK (0xff << 24)
-#define SAMPR_TYPE_PLAY (0x00 << 24)
+#define SAMPR_TYPE_MASK (0xffu << 24)
+#define SAMPR_TYPE_PLAY (0x00u << 24)
#ifdef HAVE_RECORDING
-#define SAMPR_TYPE_REC (0x01 << 24)
+#define SAMPR_TYPE_REC (0x01u << 24)
#endif
#ifndef PCM_SAMPR_CONFIG_ONLY
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index c6fc3d5bdf..a78386445e 100644
--- a/firmware/export/powermgmt.h
+++ b/firmware/export/powermgmt.h
@@ -70,6 +70,12 @@ extern unsigned int power_thread_inputs;
#endif /* CONFIG_CHARGING */
+enum shutdown_type
+{
+ SHUTDOWN_POWER_OFF,
+ SHUTDOWN_REBOOT,
+};
+
#if CONFIG_CHARGING == CHARGING_TARGET
/* Include target-specific definitions */
#include "powermgmt-target.h"
@@ -78,13 +84,6 @@ extern unsigned int power_thread_inputs;
/* Start up power management thread */
void powermgmt_init(void) INIT_ATTR;
-/* Generic current values that are intentionally meaningless - config header
- * should define proper numbers.*/
-
-#ifndef CURRENT_USB
-#define CURRENT_USB 2 /* usual current in mA in USB mode */
-#endif
-
#if CONFIG_CHARGING && !defined(CURRENT_MAX_CHG)
#define CURRENT_MAX_CHG 350 /* maximum charging current */
#endif
@@ -94,6 +93,11 @@ void powermgmt_init(void) INIT_ATTR;
#define BATT_AVE_SAMPLES 128
#endif
+#ifndef BATT_CURRENT_AVE_SAMPLES
+/* semi arbitrary but needs to be 'large' for the time estimation algorithm */
+#define BATT_CURRENT_AVE_SAMPLES 128
+#endif
+
#ifndef POWER_THREAD_STEP_TICKS
/* 2HZ sample rate unless otherwise specified */
#define POWER_THREAD_STEP_TICKS (HZ/2)
@@ -118,6 +122,8 @@ int battery_current(void); /* battery current in milliamps
int _battery_level(void); /* percent */
int _battery_time(void); /* minutes */
int _battery_voltage(void); /* voltage in millivolts */
+int _battery_current(void); /* (dis)charge current in milliamps */
+
#if CONFIG_CHARGING >= CHARGING_TARGET
void powermgmt_init_target(void);
void charging_algorithm_close(void);
@@ -136,6 +142,10 @@ unsigned int input_millivolts(void); /* voltage that device is running from */
void reset_battery_filter(int millivolts);
#endif /* HAVE_BATTERY_SWITCH || HAVE_RESET_BATTERY_FILTER */
+
+/* read unfiltered battery info */
+void battery_read_info(int *voltage, int *level);
+
/* Tells if the battery level is safe for disk writes */
bool battery_level_safe(void);
@@ -147,14 +157,16 @@ int get_battery_capacity(void); /* get local battery capacity value */
void set_battery_type(int type); /* set local battery type */
void set_sleeptimer_duration(int minutes);
+bool get_sleep_timer_active(void);
int get_sleep_timer(void);
void set_keypress_restarts_sleep_timer(bool enable);
void handle_auto_poweroff(void);
void set_car_adapter_mode(bool setting);
void reset_poweroff_timer(void);
void cancel_shutdown(void);
-void shutdown_hw(void);
+void shutdown_hw(enum shutdown_type sd_type);
void sys_poweroff(void);
+void sys_reboot(void);
/* Returns true if the system should force shutdown for some reason -
* eg. low battery */
bool query_force_shutdown(void);
diff --git a/firmware/export/rbpaths.h b/firmware/export/rbpaths.h
index a3042d80bc..9dd0a24c6f 100644
--- a/firmware/export/rbpaths.h
+++ b/firmware/export/rbpaths.h
@@ -52,6 +52,9 @@
#define PLUGIN_DIR ROCKBOX_DIR "/rocks"
#define CODECS_DIR ROCKBOX_DIR "/codecs"
+#define RB_ROOT_VOL_HIDDEN(v) (IF_MV_VOL(v) == 0)
+#define RB_ROOT_CONTENTS_DIR "/" IF_MV("<0>")
+
#else /* APPLICATION */
#define HOME_DIR "<HOME>" /* replaced at runtime */
diff --git a/firmware/export/rectangle.h b/firmware/export/rectangle.h
new file mode 100644
index 0000000000..0962611791
--- /dev/null
+++ b/firmware/export/rectangle.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _RECTANGLE_H_
+#define _RECTANGLE_H_
+
+#include <stdbool.h>
+
+struct rectangle
+{
+ int x, y, w, h;
+};
+
+/** Returns true if the rectangle is non-degenerate (positive width/height). */
+static inline bool rect_valid(const struct rectangle *r)
+{
+ return r->w > 0 && r->h > 0;
+}
+
+/** Returns true if ra contains rb. */
+bool rect_contains(const struct rectangle *ra, const struct rectangle *rb);
+
+/** Returns true if ra and rb overlap. */
+bool rect_overlap(const struct rectangle *ra, const struct rectangle *rb);
+
+/**
+ * Computes the biggest rectangle contained in ra and rb.
+ * Returns true if the intersection exists, and writes it to r_out.
+ *
+ * Returns false if there is no intersection, or either input
+ * rectangle is degenerate. In this case r_out may be uninitialized.
+ */
+bool rect_intersect(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out);
+
+/**
+ * Computes the smallest rectangle containing both ra and rb.
+ *
+ * If one input is degenerate, and the other is not, the output is a
+ * copy of the non-degenerate input. If both inputs are degenerate,
+ * then the output is degenerate but is otherwise unspecified.
+ */
+void rect_union(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out);
+
+/**
+ * Computes the result of subtracting 'rsub' from 'rect'. Up to four
+ * non-overlapping output rectangles will written to 'rects_out'; the
+ * return value is the number of rectangles written.
+ *
+ * If 'rsub' does not overlap 'rect', or if either of the inputs are
+ * degenerate, the output is a single rectangle: a copy of 'rect'.
+ */
+int rect_difference(const struct rectangle *rect,
+ const struct rectangle *rsub,
+ struct rectangle rects_out[4]);
+
+#endif /* _RECTANGLE_H_ */
diff --git a/firmware/export/screendump.h b/firmware/export/screendump.h
index cd7793b9d3..b8fc27047d 100644
--- a/firmware/export/screendump.h
+++ b/firmware/export/screendump.h
@@ -52,12 +52,7 @@
#define DUMP_BMP_LINESIZE ((LCD_WIDTH*3 + 3) & ~3)
#endif
-#ifdef BOOTLOADER
-
-#define screen_dump()
-#define remote_screen_dump()
-
-#else /* !BOOTLOADER */
+#ifdef HAVE_SCREENDUMP
/* Save a .BMP file containing the current screen contents. */
void screen_dump(void);
@@ -69,6 +64,11 @@ void screen_dump_set_hook(void (*hook)(int fd));
void remote_screen_dump(void);
#endif
-#endif /* !BOOTLOADER */
+#else /* !HAVE_SCREENDUMP */
+
+#define screen_dump() do { } while(0)
+#define remote_screen_dump() do { } while(0)
+
+#endif /* HAVE_SCREENDUMP */
#endif /* __SCREENDUMP_H__ */
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index 033b435f2a..cf9e0f077b 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -49,15 +49,7 @@ void si4700_rds_init(void) INIT_ATTR;
/* Radio is fully powered up or about to be powered down */
void si4700_rds_powerup(bool on);
-#if (CONFIG_RDS & RDS_CFG_ISR)
-/* Read raw RDS info for processing - asynchronously */
-void si4700_rds_read_raw_async(unsigned char *buf, int count); /* implemented by target */
-void si4700_rds_interrupt(void);
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
-
/* Read raw RDS info for processing.
- * - If RDS_CFG_ISR is set, the tuner driver will call si4700_rds_read_raw_async() which should
- * perform an asynchronous read and call this function when the data has been read.
* - If RDS_CFG_POLL is set, this function will read status and RDS data and process it if a new
* packet is available.
* - Otherwise this function will read a RDS packet and process it under the assumption that it is
diff --git a/firmware/export/system.h b/firmware/export/system.h
index f554ac7cf1..172fe05b6e 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -31,7 +31,13 @@ extern void system_reboot (void);
/* Called from any UIE handler and panicf - wait for a key and return
* to reboot system. */
extern void system_exception_wait(void);
+
+#if NUM_CORES == 1
+extern void system_init(void) INIT_ATTR;
+#else
+/* TODO: probably safe to use INIT_ATTR on multicore but this needs checking */
extern void system_init(void);
+#endif
extern long cpu_frequency;
@@ -258,7 +264,8 @@ static inline void cpu_boost_unlock(void)
#endif
/* Define this if target has support for generating backtraces */
-#ifdef CPU_ARM
+#if defined(CPU_ARM) || \
+ (defined(CPU_MIPS) && (CONFIG_PLATFORM & PLATFORM_NATIVE))
#define HAVE_RB_BACKTRACE
#endif
@@ -307,8 +314,8 @@ static inline void cpu_boost_unlock(void)
/* Define MEM_ALIGN_ATTR which may be used to align e.g. buffers for faster
* access. */
-#if defined(CPU_ARM)
- /* Use ARMs cache alignment. */
+#if defined(HAVE_CPU_CACHE_ALIGN)
+ /* Align to a cache line. */
#define MEM_ALIGN_ATTR CACHEALIGN_ATTR
#define MEM_ALIGN_SIZE CACHEALIGN_SIZE
#elif defined(CPU_COLDFIRE)
diff --git a/firmware/export/usb.h b/firmware/export/usb.h
index fe9f3bcfa1..c075fa83ec 100644
--- a/firmware/export/usb.h
+++ b/firmware/export/usb.h
@@ -134,7 +134,6 @@ enum
/* Supported usb modes. */
enum
{
- USB_MODE_ASK = 0,
USB_MODE_MASS_STORAGE,
USB_MODE_CHARGE,
USB_MODE_ADB
diff --git a/firmware/export/x1000-codec.h b/firmware/export/x1000-codec.h
new file mode 100644
index 0000000000..cfc71dbd60
--- /dev/null
+++ b/firmware/export/x1000-codec.h
@@ -0,0 +1,184 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021-2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __X1000_CODEC_H__
+#define __X1000_CODEC_H__
+
+#include "config.h"
+#include <stdbool.h>
+
+/* Note: the internal X1000 codec supports playback and record, but devices
+ * can employ an external codec for one and the internal codec for the other.
+ * The caveat, in this case, is that only one codec can be used at a time
+ * because the HW cannot mux playback/record independently.
+ *
+ * At present only recording is implemented, since all X1000 ports use an
+ * external DAC for playback.
+ */
+
+#ifdef HAVE_X1000_ICODEC_PLAY
+# error "X1000 icodec playback not implemented"
+#endif
+
+#define X1000_ICODEC_ADC_GAIN_MIN 0
+#define X1000_ICODEC_ADC_GAIN_MAX 43
+#define X1000_ICODEC_ADC_GAIN_STEP 1
+
+#define X1000_ICODEC_MIC_GAIN_MIN 0
+#define X1000_ICODEC_MIC_GAIN_MAX 20
+#define X1000_ICODEC_MIC_GAIN_STEP 4
+
+#ifdef HAVE_X1000_ICODEC_REC
+AUDIOHW_SETTING(MIC_GAIN, "dB", 0, 1, 0, 63, 12)
+#endif
+
+#define JZCODEC_INDIRECT_CREG(r) ((r) & 0xff)
+#define JZCODEC_INDIRECT_INDEX(r) (((r) >> 8) & 0x7)
+#define JZCODEC_INDIRECT_BIT 0x800
+
+#define JZCODEC_INDIRECT(c, i) (JZCODEC_INDIRECT_BIT | ((i) << 8) | (c))
+
+/* Codec registers from Ingenic's kernel sources. The datasheet is badly
+ * screwed up and the addresses listed cannot be trusted. */
+enum {
+ JZCODEC_SR = 0,
+ JZCODEC_SR2,
+ JZCODEC_SIGR,
+ JZCODEC_SIGR2,
+ JZCODEC_SIGR3,
+ JZCODEC_SIGR5,
+ JZCODEC_SIGR7,
+ JZCODEC_MR,
+ JZCODEC_AICR_DAC,
+ JZCODEC_AICR_ADC,
+ JZCODEC_CR_DMIC,
+ JZCODEC_CR_MIC1,
+ JZCODEC_CR_MIC2,
+ JZCODEC_CR_DAC,
+ JZCODEC_CR_DAC2,
+ JZCODEC_CR_ADC,
+ JZCODEC_CR_MIX,
+ JZCODEC_DR_MIX,
+ JZCODEC_CR_VIC,
+ JZCODEC_CR_CK,
+ JZCODEC_FCR_DAC,
+ JZCODEC_SFCCR_DAC,
+ JZCODEC_SFFCR_DAC,
+ JZCODEC_FCR_ADC,
+ JZCODEC_CR_TIMER_MSB,
+ JZCODEC_CR_TIMER_LSB,
+ JZCODEC_ICR,
+ JZCODEC_IMR,
+ JZCODEC_IFR,
+ JZCODEC_IMR2,
+ JZCODEC_IFR2,
+ JZCODEC_GCR_DACL,
+ JZCODEC_GCR_DACR,
+ JZCODEC_GCR_DACL2,
+ JZCODEC_GCR_DACR2,
+ JZCODEC_GCR_MIC1,
+ JZCODEC_GCR_MIC2,
+ JZCODEC_GCR_ADCL,
+ JZCODEC_GCR_ADCR,
+ JZCODEC_GCR_MIXDACL,
+ JZCODEC_GCR_MIXDACR,
+ JZCODEC_GCR_MIXADCL,
+ JZCODEC_GCR_MIXADCR,
+ JZCODEC_CR_DAC_AGC,
+ JZCODEC_DR_DAC_AGC,
+ JZCODEC_CR_DAC2_AGC,
+ JZCODEC_DR_DAC2_AGC,
+ JZCODEC_CR_ADC_AGC,
+ JZCODEC_DR_ADC_AGC,
+ JZCODEC_SR_ADC_AGCDGL,
+ JZCODEC_SR_ADC_AGCDGR,
+ JZCODEC_SR_ADC_AGCAGL,
+ JZCODEC_SR_ADC_AGCAGR,
+ JZCODEC_CR_TR,
+ JZCODEC_DR_TR,
+ JZCODEC_SR_TR1,
+ JZCODEC_SR_TR2,
+ JZCODEC_SR_TR_SRCDAC,
+
+ JZCODEC_MIX0 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 0),
+ JZCODEC_MIX1 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 1),
+ JZCODEC_MIX2 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 2),
+ JZCODEC_MIX3 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 3),
+ JZCODEC_MIX4 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 4),
+
+ JZCODEC_DAC_AGC0 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 0),
+ JZCODEC_DAC_AGC1 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 1),
+ JZCODEC_DAC_AGC2 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 2),
+ JZCODEC_DAC_AGC3 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 3),
+
+ JZCODEC_DAC2_AGC0 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 0),
+ JZCODEC_DAC2_AGC1 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 1),
+ JZCODEC_DAC2_AGC2 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 2),
+ JZCODEC_DAC2_AGC3 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 3),
+
+ JZCODEC_ADC_AGC0 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 0),
+ JZCODEC_ADC_AGC1 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 1),
+ JZCODEC_ADC_AGC2 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 2),
+ JZCODEC_ADC_AGC3 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 3),
+ JZCODEC_ADC_AGC4 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 4),
+};
+
+/* for use with x1000_icodec_mic1_configure() */
+enum {
+ JZCODEC_MIC1_SINGLE_ENDED = (0 << 6),
+ JZCODEC_MIC1_DIFFERENTIAL = (1 << 6),
+
+ JZCODEC_MIC1_BIAS_2_08V = (0 << 3),
+ JZCODEC_MIC1_BIAS_1_66V = (1 << 3),
+
+ JZCODEC_MIC1_CONFIGURE_MASK = (1 << 6) | (1 << 3),
+};
+
+/* for use with x1000_icodec_adc_mic_sel() */
+enum {
+ JZCODEC_MIC_SEL_ANALOG,
+ JZCODEC_MIC_SEL_DIGITAL,
+};
+
+extern void x1000_icodec_open(void);
+extern void x1000_icodec_close(void);
+
+extern void x1000_icodec_dac_frequency(int fsel);
+
+extern void x1000_icodec_adc_enable(bool en);
+extern void x1000_icodec_adc_mute(bool muted);
+extern void x1000_icodec_adc_mic_sel(int sel);
+extern void x1000_icodec_adc_frequency(int fsel);
+extern void x1000_icodec_adc_highpass_filter(bool en);
+extern void x1000_icodec_adc_gain(int gain_dB);
+
+extern void x1000_icodec_mic1_enable(bool en);
+extern void x1000_icodec_mic1_bias_enable(bool en);
+extern void x1000_icodec_mic1_configure(int settings);
+extern void x1000_icodec_mic1_gain(int gain_dB);
+
+extern void x1000_icodec_mixer_enable(bool en);
+
+extern int x1000_icodec_read(int reg);
+extern void x1000_icodec_write(int reg, int value);
+extern void x1000_icodec_update(int reg, int mask, int value);
+
+#endif /* __X1000_CODEC_H__ */
diff --git a/firmware/export/x1000.h b/firmware/export/x1000.h
index 102d4ec978..b71d37d64d 100644
--- a/firmware/export/x1000.h
+++ b/firmware/export/x1000.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -39,24 +39,76 @@
# error "Unsupported EXCLK freq"
#endif
-/* On-chip TCSM (tightly coupled shared memory), aka IRAM */
+/* On-chip TCSM (tightly coupled shared memory), aka IRAM. The SPL runs from
+ * here, but the rest of Rockbox doesn't use it - it is too difficult to use
+ * as a normal memory region because it's not in KSEG0. */
#define X1000_TCSM_BASE 0xf4000000
#define X1000_TCSM_SIZE (16 * 1024)
-/* External SDRAM */
+/* SPL load and entry point addresses, this is defined by the HW boot ROM.
+ * First 4K is used by mask ROM for stack + variables, and the next 2K are
+ * occupied by SPL header. Usable code+data size is 10K. */
+#define X1000_SPL_LOAD_ADDR (X1000_TCSM_BASE + 0x1000)
+#define X1000_SPL_EXEC_ADDR (X1000_TCSM_BASE + 0x1800)
+#define X1000_SPL_SIZE (X1000_TCSM_SIZE - 0x1800)
+
+/* External SDRAM - just one big linear mapping in KSEG0. */
#define X1000_SDRAM_BASE 0x80000000
#define X1000_SDRAM_SIZE (MEMORYSIZE * 1024 * 1024)
+#define X1000_SDRAM_END (X1000_SDRAM_BASE + X1000_SDRAM_SIZE)
-/* Memory definitions for Rockbox */
+/* Memory definitions for Rockbox
+ *
+ * IRAM - Contains the exception handlers and acts as a safe stub area
+ * from which you can overwrite the rest of DRAM (used by RoLo).
+ *
+ * DRAM - This is the main RAM area used for code, data, and bss sections.
+ * The audio, codec, and plugin buffers also reside in here.
+ *
+ * X1000_IRAM_BASE is the base of the exception vectors and must be set to
+ * the base of kseg0 (0x80000000). The X1000 supports the EBase register so
+ * the vectors can be remapped, allowing IRAM to be moved to any 4K-aligned
+ * address, but it would introduce more complexity and there's currently no
+ * good reason to do this.
+ *
+ * X1000_DRAM_BASE doubles as the entry point address. There is some legacy
+ * baggage surrounding this value so be careful when changing it.
+ *
+ * - Rockbox's DRAM_BASE should always equal X1000_STANDARD_DRAM_BASE because
+ * this value is hardcoded by old bootloaders released in 2021. This can be
+ * changed if truly necessary, but it should be avoided.
+ * - The bootloader's DRAM_BASE can be changed freely but if it isn't equal
+ * to X1000_STANDARD_DRAM_BASE, the update package generation *must* be
+ * updated to use the "bootloader2.ucl" filename to ensure old jztools do
+ * not try to incorrectly boot the binary at the wrong load address.
+ *
+ * The bootloader DRAM_BASE is also hardcoded in the SPL, but the SPL is
+ * considered as part of the bootloader to avoid introducing unnecessary
+ * ABI boundaries. Therefore this hardcoded use can safely be ignored.
+ *
+ * There is no requirement that IRAM and DRAM are contiguous, but they must
+ * reside in the same segment (ie. upper 3 address bits must be identical),
+ * otherwise we need long calls to go between the two.
+ */
#define X1000_IRAM_BASE X1000_SDRAM_BASE
#define X1000_IRAM_SIZE (16 * 1024)
#define X1000_IRAM_END (X1000_IRAM_BASE + X1000_IRAM_SIZE)
#define X1000_DRAM_BASE X1000_IRAM_END
#define X1000_DRAM_SIZE (X1000_SDRAM_SIZE - X1000_IRAM_SIZE)
#define X1000_DRAM_END (X1000_DRAM_BASE + X1000_DRAM_SIZE)
+
+/* Stacks are placed in IRAM to avoid various annoying issues in boot code. */
#define X1000_STACKSIZE 0x1e00
#define X1000_IRQSTACKSIZE 0x300
+/* Standard DRAM base address for backward compatibility */
+#define X1000_STANDARD_DRAM_BASE 0x80004000
+
+/* Required for pcm_rec_dma_get_peak_buffer(), doesn't do anything
+ * except on targets with recording. */
+#define HAVE_PCM_DMA_ADDRESS
+#define HAVE_PCM_REC_DMA_ADDRESS
+
/* Convert kseg0 address to physical address or uncached address */
#define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff)
#define UNCACHEDADDR(x) (PHYSADDR(x) | 0xa0000000)
diff --git a/firmware/font.c b/firmware/font.c
index b8fa1c537f..baaec13d3c 100644
--- a/firmware/font.c
+++ b/firmware/font.c
@@ -44,8 +44,15 @@
#include "diacritic.h"
#include "rbpaths.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#include "logf.h"
+
#define MAX_FONTSIZE_FOR_16_BIT_OFFSETS 0xFFDB
+#define FONT_EXT "fnt"
+#define GLYPH_CACHE_EXT "gc"
+
/* max static loadable font buffer size */
#ifndef MAX_FONT_SIZE
#if LCD_HEIGHT > 64
@@ -90,7 +97,8 @@ extern struct font sysfont;
struct buflib_alloc_data {
struct font font; /* must be the first member! */
- int handle_locks; /* is the buflib handle currently locked? */
+ char *path; /* font path and filename (allocd at end of buffer) */
+ size_t path_bufsz; /* size of path buffer */
int refcount; /* how many times has this font been loaded? */
unsigned char buffer[];
};
@@ -107,9 +115,6 @@ static int buflibmove_callback(int handle, void* current, void* new)
struct buflib_alloc_data *alloc = (struct buflib_alloc_data*)current;
ptrdiff_t diff = new - current;
- if (alloc->handle_locks > 0)
- return BUFLIB_CB_CANNOT_MOVE;
-
#define UPDATE(x) if (x) { x = PTR_ADD(x, diff); }
UPDATE(alloc->font.bits);
@@ -119,40 +124,34 @@ static int buflibmove_callback(int handle, void* current, void* new)
UPDATE(alloc->font.buffer_start);
UPDATE(alloc->font.buffer_end);
UPDATE(alloc->font.buffer_position);
+ UPDATE(alloc->path);
UPDATE(alloc->font.cache._index);
UPDATE(alloc->font.cache._lru._base);
-
+ logf("%s %s", __func__, alloc->path);
return BUFLIB_CB_OK;
}
static void lock_font_handle(int handle, bool lock)
{
if ( handle < 0 )
return;
- struct buflib_alloc_data *alloc = core_get_data(handle);
- if ( lock )
- alloc->handle_locks++;
+
+ if (lock)
+ core_pin(handle);
else
- alloc->handle_locks--;
+ core_unpin(handle);
}
void font_lock(int font_id, bool lock)
{
if( font_id < 0 || font_id >= MAXFONTS )
return;
- if( buflib_allocations[font_id] >= 0 )
+ if( buflib_allocations[font_id] > 0 )
lock_font_handle(buflib_allocations[font_id], lock);
}
static struct buflib_callbacks buflibops = {buflibmove_callback, NULL, NULL };
-static inline struct font *pf_from_handle(int handle)
-{
- struct buflib_alloc_data *alloc = core_get_data(handle);
- struct font *pf = &alloc->font;
- return pf;
-}
-
static inline unsigned char *buffer_from_handle(int handle)
{
struct buflib_alloc_data *alloc = core_get_data(handle);
@@ -327,31 +326,36 @@ static struct font* font_load_cached(struct font* pf,
return pf;
}
+bool font_filename_matches_loaded_id(int font_id, const char *filename)
+{
+ if ( font_id >= 0 && font_id < MAXFONTS )
+ {
+ int handle = buflib_allocations[font_id];
+ if (handle > 0)
+ {
+ struct buflib_alloc_data *data = core_get_data(handle);
+ logf("%s id: [%d], %s", __func__, font_id, data->path);
+ return strcmp(data->path, filename) == 0;
+ }
+ }
+ return false;
+}
static int find_font_index(const char* path)
{
- int index = 0, handle;
-
- while (index < MAXFONTS)
+ for(int index = 0; index < MAXFONTS; index++)
{
- handle = buflib_allocations[index];
- if (handle > 0 && !strcmp(core_get_name(handle), path))
+ if(font_filename_matches_loaded_id(index, path))
+ {
+ logf("%s Found id: [%d], %s", __func__, index, path);
return index;
- index++;
+ }
}
+ logf("%s %s Not found using id: [%d], FONT_SYSFIXED",
+ __func__, path, FONT_SYSFIXED);
return FONT_SYSFIXED;
}
-const char* font_filename(int font_id)
-{
- if ( font_id < 0 || font_id >= MAXFONTS )
- return NULL;
- int handle = buflib_allocations[font_id];
- if (handle > 0)
- return core_get_name(handle);
- return NULL;
-}
-
static size_t font_glyphs_to_bufsize(struct font *pf, int glyphs)
{
size_t bufsize;
@@ -406,6 +410,11 @@ static struct font* font_load_header(int fd, struct font *pheader,
/* load a font with room for glyphs, limited to bufsize if not zero */
int font_load_ex( const char *path, size_t buf_size, int glyphs )
{
+ /* needed to handle the font properly after it's loaded */
+ size_t path_len = strlen(path);
+ if ( path_len >= MAX_PATH )
+ return -1;
+
//printf("\nfont_load_ex(%s, %d, %d)\n", path, buf_size, glyphs);
int fd = open(path, O_RDONLY|O_BINARY);
if ( fd < 0 )
@@ -457,7 +466,7 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs )
{
/* already loaded, no need to reload */
struct buflib_alloc_data *pd = core_get_data(buflib_allocations[font_id]);
- if (pd->font.buffer_size < bufsize)
+ if (pd->font.buffer_size < bufsize || pd->path_bufsz < path_len)
{
int old_refcount, old_id;
size_t old_bufsize = pd->font.buffer_size;
@@ -512,19 +521,22 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs )
if ( open_slot == -1 )
return -1;
font_id = open_slot;
-
+ size_t path_bufsz = MAX(path_len + 1, 64); /* enough size for common case */
/* allocate mem */
- int handle = core_alloc_ex( path,
- bufsize + sizeof( struct buflib_alloc_data ),
+ int handle = core_alloc_ex(
+ bufsize + path_bufsz + sizeof( struct buflib_alloc_data ),
&buflibops );
if ( handle <= 0 )
{
return -1;
}
struct buflib_alloc_data *pdata;
- pdata = core_get_data(handle);
- pdata->handle_locks = 1;
+
+ pdata = core_get_data_pinned(handle);
pdata->refcount = 1;
+ pdata->path = pdata->buffer + bufsize;
+ /* save load path so we can recognize this font later */
+ memcpy(pdata->path, path, path_len+1);
/* load and init */
struct font *pf = &pdata->font;
@@ -577,7 +589,8 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs )
}
buflib_allocations[font_id] = handle;
//printf("%s -> [%d] -> %d\n", path, font_id, *handle);
- lock_font_handle( handle, false );
+ core_put_data_pinned(pdata);
+ logf("%s id: [%d], %s", __func__, font_id, path);
return font_id; /* success!*/
}
@@ -598,14 +611,13 @@ void font_unload(int font_id)
pdata->refcount--;
if (pdata->refcount < 1)
{
- //printf("freeing id: %d %s\n", font_id, core_get_name(*handle));
+ logf("%s %s", __func__, pdata->path);
if (pf && pf->fd >= 0)
{
glyph_cache_save(font_id);
close(pf->fd);
}
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
buflib_allocations[font_id] = -1;
}
@@ -659,15 +671,15 @@ static void font_enable(int font_id)
int handle = buflib_allocations[font_id];
if ( handle < 0 )
return;
- struct buflib_alloc_data *pdata = core_get_data(handle);
+ struct buflib_alloc_data *pdata = core_get_data_pinned(handle);
struct font *pf = &pdata->font;
if (pf->disabled && pf->fd < 0)
{
- const char *filename = font_filename(font_id);
- pf->fd = open(filename, O_RDONLY);
+ pf->fd = open(pdata->path, O_RDONLY);
pf->disabled = false;
}
+ core_put_data_pinned(pdata);
}
void font_enable_all(void)
@@ -687,7 +699,7 @@ struct font* font_get(int font)
struct font* pf;
if (font == FONT_UI)
font = MAXFONTS-1;
- if (font <= FONT_SYSFIXED)
+ if (font <= FONT_SYSFIXED || font >= MAXFONTS)
return &sysfont;
while (1) {
@@ -859,9 +871,10 @@ const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
static void font_path_to_glyph_path( const char *font_path, char *glyph_path)
{
/* take full file name, cut extension, and add .glyphcache */
- strlcpy(glyph_path, font_path, MAX_PATH);
- glyph_path[strlen(glyph_path)-4] = '\0';
- strcat(glyph_path, ".gc");
+ strmemccpy(glyph_path, font_path, MAX_PATH);
+ int dotidx = strlen(glyph_path) - sizeof(FONT_EXT);
+ strmemccpy(glyph_path + dotidx, "." GLYPH_CACHE_EXT, MAX_PATH - dotidx);
+ logf("%s %s", __func__, glyph_path);
}
/* call with NULL to flush */
@@ -903,25 +916,28 @@ static void glyph_cache_save(int font_id)
if ( handle < 0 )
return;
- struct font *pf = pf_from_handle(handle);
+ struct buflib_alloc_data *pdata = core_get_data_pinned(handle);
+ struct font *pf = &pdata->font;
+
if(pf && pf->fd >= 0)
{
char filename[MAX_PATH];
- font_path_to_glyph_path(font_filename(font_id), filename);
+ font_path_to_glyph_path(pdata->path, filename);
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
- if (fd < 0)
- return;
-
- cache_pf = pf;
- cache_fd = fd;
- lru_traverse(&cache_pf->cache._lru, glyph_file_write);
- glyph_file_write(NULL);
- if (cache_fd >= 0)
+ if (fd >= 0)
{
- close(cache_fd);
- cache_fd = -1;
+ cache_pf = pf;
+ cache_fd = fd;
+ lru_traverse(&cache_pf->cache._lru, glyph_file_write);
+ glyph_file_write(NULL);
+ if (cache_fd >= 0)
+ {
+ close(cache_fd);
+ cache_fd = -1;
+ }
}
}
+ core_put_data_pinned(pdata);
return;
}
@@ -1051,16 +1067,20 @@ const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
#endif /* BOOTLOADER */
/*
- * Returns the stringsize of a given string.
+ * Returns the stringsize of a given NULL terminated string
+ * stops after maxbytes or NULL (\0) whichever occurs first.
+ * maxbytes = -1 ignores maxbytes and relies on NULL terminator (\0)
+ * to terminate the string
*/
-int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
+int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnum)
{
- struct font* pf = font_get(fontnumber);
+ struct font* pf = font_get(fontnum);
+ font_lock( fontnum, true );
unsigned short ch;
int width = 0;
+ size_t b = maxbytes - 1;
- font_lock( fontnumber, true );
- for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch))
+ for (str = utf8decode(str, &ch); ch != 0 && b < maxbytes; str = utf8decode(str, &ch), b--)
{
if (is_diacritic(ch, NULL))
continue;
@@ -1072,10 +1092,18 @@ int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
*w = width;
if ( h )
*h = pf->height;
- font_lock( fontnumber, false );
+ font_lock( fontnum, false );
return width;
}
+/*
+ * Returns the stringsize of a given NULL terminated string.
+ */
+int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
+{
+ return font_getstringnsize(str, -1, w, h, fontnumber);
+}
+
/* -----------------------------------------------------------------
* vim: et sw=4 ts=8 sts=4 tw=78
*/
diff --git a/firmware/general.c b/firmware/general.c
index 8508b34b88..d421d722a8 100644
--- a/firmware/general.c
+++ b/firmware/general.c
@@ -107,7 +107,7 @@ char *create_numbered_filename(char *buffer, const char *path,
int suffixlen = strlen(suffix);
if (buffer != path)
- strlcpy(buffer, path, MAX_PATH);
+ strmemccpy(buffer, path, MAX_PATH);
pathlen = strlen(buffer);
@@ -181,7 +181,7 @@ char *create_datetime_filename(char *buffer, const char *path,
last_tm = *tm;
if (buffer != path)
- strlcpy(buffer, path, MAX_PATH);
+ strmemccpy(buffer, path, MAX_PATH);
pathlen = strlen(buffer);
snprintf(buffer + pathlen, MAX_PATH - pathlen,
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h
index 7f534c6ce0..3fe8ac1430 100644
--- a/firmware/include/buflib.h
+++ b/firmware/include/buflib.h
@@ -1,382 +1,417 @@
-/***************************************************************************
-* __________ __ ___.
-* Open \______ \ ____ ____ | | _\_ |__ _______ ___
-* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
-* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
-* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
-* \/ \/ \/ \/ \/
-* $Id$
-*
-* This is a memory allocator designed to provide reasonable management of free
-* space and fast access to allocated data. More than one allocator can be used
-* at a time by initializing multiple contexts.
-*
-* Copyright (C) 2009 Andrew Mahone
-* Copyright (C) 2011 Thomas Martitz
-*
-* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
-* KIND, either express or implied.
-*
-****************************************************************************/
-
+/**************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Andrew Mahone
+ * Copyright (C) 2011 Thomas Martitz
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
#ifndef _BUFLIB_H_
#define _BUFLIB_H_
+
+#include "config.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
-/* enable single block debugging */
-#define BUFLIB_DEBUG_BLOCK_SINGLE
+/* Add extra checks to buflib_get_data to catch bad handles */
+//#define BUFLIB_DEBUG_GET_DATA
-union buflib_data
-{
- intptr_t val; /* length of the block in n*sizeof(union buflib_data).
- Includes buflib metadata overhead. A negative value
- indicates block is unallocated */
- char name[1]; /* name, actually a variable sized string */
- struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */
- char* alloc; /* start of allocated memory area */
- union buflib_data *handle; /* pointer to entry in the handle table.
- Used during compaction for fast lookup */
- uint32_t crc; /* checksum of this data to detect corruption */
-};
+/* Support integrity check */
+//#define BUFLIB_DEBUG_CHECK_VALID
-struct buflib_context
-{
- union buflib_data *handle_table;
- union buflib_data *first_free_handle;
- union buflib_data *last_handle;
- union buflib_data *buf_start;
- union buflib_data *alloc_end;
- bool compact;
-};
+/* Support debug printing of memory blocks */
+//#define BUFLIB_DEBUG_PRINT
-/**
- * This declares the minimal overhead that is required per alloc. These
- * are bytes that are allocated from the context's pool in addition
- * to the actually requested number of bytes.
- *
- * The total number of bytes consumed by an allocation is
- * BUFLIB_ALLOC_OVERHEAD + requested bytes + strlen(<name passed to
- * buflib_alloc_ex()) + pad to pointer size
- */
-#define BUFLIB_ALLOC_OVERHEAD (6*sizeof(union buflib_data))
+/* Defined by the backend header. */
+struct buflib_context;
+
+/* Buflib callback return codes. */
+#define BUFLIB_CB_OK 0
+#define BUFLIB_CB_CANNOT_MOVE 1
+#define BUFLIB_CB_CANNOT_SHRINK 1
+
+/* Buflib shrink hints. */
+#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK)
+#define BUFLIB_SHRINK_POS_FRONT (1u<<31)
+#define BUFLIB_SHRINK_POS_BACK (1u<<30)
+#define BUFLIB_SHRINK_POS_MASK (BUFLIB_SHRINK_POS_FRONT|BUFLIB_SHRINK_POS_BACK)
/**
- * Callbacks used by the buflib to inform allocation that compaction
- * is happening (before data is moved)
- *
- * Note that buflib tries to move to satisfy new allocations before shrinking.
- * So if you have something to resize try to do it outside of the callback.
- *
- * Regardless of the above, if the allocation is SHRINKABLE, but not
- * MUST_NOT_MOVE buflib will move the allocation before even attempting to
- * shrink.
+ * Callbacks run by buflib to manage an allocation.
*/
-struct buflib_callbacks {
+struct buflib_callbacks
+{
/**
- * This is called before data is moved. Use this to fix up any cached
- * pointers pointing to inside the allocation. The size is unchanged.
- *
- * This is not needed if you don't cache the data pointer (but always
- * call buflib_get_data()) and don't pass pointer to the data to yielding
- * functions.
+ * \brief Called when buflib wants to move the buffer
+ * \param handle Handle being moved
+ * \param current Current address of the buffer
+ * \param new New address the buffer would have after moving
+ * \return BUFLIB_CB_OK - Allow the buffer to be moved.
+ * \return BUFLIB_CB_CANNOT_MOVE - Do not allow the buffer to be moved.
*
- * handle: The corresponding handle
- * current: The current start of the allocation
- * new: The new start of the allocation, after data movement
+ * This callback allows you to fix up any pointers that might
+ * be pointing to the buffer before it is moved. The task of
+ * actually moving the buffer contents is performed by buflib
+ * after the move callback returns, if movement is allowed.
*
- * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_MOVE if movement
- * is impossible at this moment.
+ * Care must be taken to ensure that the buffer is not accessed
+ * from outside the move callback until the move is complete. If
+ * this is a concern, eg. due to multi-threaded access, then you
+ * must implement a sync_callback() and guard any access to the
+ * buffer with a lock.
*
- * If NULL: this allocation must not be moved around
- * by the buflib when compaction occurs. Attention: Don't confuse
- * that with passing NULL for the whole callback structure
- * to buflib_alloc_ex(). This would enable moving buffers by default.
- * You have to pass NULL inside the "struct buflib_callbacks" structure.
+ * If the move callback is NULL then buflib will never move
+ * the allocation, as if you returned BUFLIB_CB_CANNOT_MOVE.
*/
int (*move_callback)(int handle, void* current, void* new);
+
/**
- * This is called when the buflib desires to shrink a buffer
- * in order to satisfy new allocation. This happens when buflib runs
- * out of memory, e.g. because buflib_alloc_maximum() was called.
- * Move data around as you need to make space and call core_shrink() as
- * appropriate from within the callback to complete the shrink operation.
- * buflib will not move data as part of shrinking.
- *
- * hint: bit mask containing hints on how shrinking is desired (see below)
- * handle: The corresponding handle
- * start: The old start of the allocation
+ * \brief Called when buflib wants to shrink the buffer
+ * \param handle Handle to shrink
+ * \param hints Hints regarding the shrink request
+ * \param start Current address of the buffer
+ * \param size Current size of the buffer as seen by buflib.
+ * This may be rounded up compared to the nominal
+ * allocation size due to alignment requirements.
+ * \return BUFLIB_CB_OK - Was able to shrink the buffer.
+ * \return BUFLIB_CB_CANNOT_SHRINK - Buffer cannot shrink.
*
- * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_SHRINK if shirinking
- * is impossible at this moment.
+ * This callback is run by buflib when it runs out of memory
+ * and starts a compaction run. Buflib will not actually try
+ * to shrink or move memory, you must do that yourself and
+ * call buflib_shrink() to report the new start address and
+ * size of the buffer.
*
- * if NULL: this allocation cannot be resized.
- * It is recommended that allocation that must not move are
- * at least shrinkable
+ * If the shrink callback is NULL then buflib will regard the
+ * buffer as non-shrinkable.
*/
- int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size);
+ int (*shrink_callback)(int handle, unsigned hints,
+ void *start, size_t size);
+
/**
- * This is called when special steps must be taken for synchronization
- * both before the move_callback is called and after the data has been
- * moved.
+ * \brief Called before and after attempting to move the buffer
+ * \param handle Handle being moved
+ * \param lock True to lock, false to unlock
+ *
+ * The purpose of this callback is to block access to the buffer
+ * from other threads while a buffer is being moved, using a lock
+ * such as a mutex.
+ *
+ * It is called with `sync_callback(handle, true)` before running
+ * the move callback and `sync_callback(handle, false)` after the
+ * move is complete, regardless of whether the buffer was actually
+ * moved or not.
*/
- void (*sync_callback)(int handle, bool sync_on);
+ void (*sync_callback)(int handle, bool lock);
};
-#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK)
-#define BUFLIB_SHRINK_POS_FRONT (1u<<31)
-#define BUFLIB_SHRINK_POS_BACK (1u<<30)
-#define BUFLIB_SHRINK_POS_MASK (BUFLIB_SHRINK_POS_FRONT|BUFLIB_SHRINK_POS_BACK)
-
/**
- * Possible return values for the callbacks, some of them can cause
- * compaction to fail and therefore new allocations to fail
+ * A set of all NULL callbacks for use with allocations that need to stay
+ * locked in RAM and not moved or shrunk. These type of allocations should
+ * be avoided as much as possible to avoid memory fragmentation but it can
+ * suitable for short-lived allocations.
+ *
+ * \note Use of this is discouraged. Prefer to use normal moveable
+ * allocations and pin them.
*/
-/* Everything alright */
-#define BUFLIB_CB_OK 0
-/* Tell buflib that moving failed. Buflib may retry to move at any point */
-#define BUFLIB_CB_CANNOT_MOVE 1
-/* Tell buflib that resizing failed, possibly future making allocations fail */
-#define BUFLIB_CB_CANNOT_SHRINK 1
+extern struct buflib_callbacks buflib_ops_locked;
/**
- * Initializes buflib with a caller allocated context instance and memory pool.
- *
- * The buflib_context instance needs to be passed to every other buflib
- * function. It's should be considered opaque, even though it is not yet
- * (that's to make inlining core_get_data() possible). The documentation
- * of the other functions will not describe the context
- * instance parameter further as it's obligatory.
- *
- * context: The new buflib instance to be initialized, allocated by the caller
- * size: The size of the memory pool
+ * \brief Intialize a buflib context
+ * \param ctx Context to initialize
+ * \param buf Buffer which will be used as the context's memory pool
+ * \param size Size of the buffer
*/
-void buflib_init(struct buflib_context *context, void *buf, size_t size);
-
+void buflib_init(struct buflib_context *ctx, void *buf, size_t size);
/**
* Returns the amount of unallocated bytes. It does not mean this amount
* can be actually allocated because they might not be contiguous.
- *
- * Returns: The number of unallocated bytes in the memory pool.
*/
size_t buflib_available(struct buflib_context *ctx);
/**
- * Returns the biggest possible allocation that can be determined to succeed.
- *
- * Returns: The amount of bytes of the biggest unallocated, contiguous region.
+ * Returns the size of the largest possible contiguous allocation, given
+ * the current state of the memory pool. A larger allocation may still
+ * succeed if compaction is able to create a larger contiguous area.
*/
size_t buflib_allocatable(struct buflib_context *ctx);
/**
- * Relocates the fields in *ctx to the new buffer position pointed to by buf.
- * This does _not_ move any data but updates the pointers. The data has
- * to be moved afterwards manually and only if this function returned true.
- *
- * This is intended to be called from within a move_callback(), for
- * buflib-on-buflib scenarios (i.e. a new buflib instance backed by a buffer
- * that was allocated by another buflib instance). Be aware that if the parent
- * move_callback() moves the underlying buffer _no_ move_callback() of the
- * underlying buffer are called.
- *
- * Returns true of the relocation was successful. If it returns false no
- * change to *ctx was made.
- */
-bool buflib_context_relocate(struct buflib_context *ctx, void *buf);
-
-/**
- * Allocates memory from buflib's memory pool
+ * \brief Relocate the buflib memory pool to a new address
+ * \param ctx Context to relocate
+ * \param buf New memory pool address
+ * \return True if relocation should proceed, false if it cannot.
*
- * size: How many bytes to allocate
+ * Updates all pointers inside the buflib context to point to a new pool
+ * address. You must call this function before moving the pool and move
+ * the data manually afterwards only if this function returns true.
*
- * This function passes NULL for the callback structure "ops", so buffers
- * are movable. Don't pass them to functions that yield().
+ * This is intended from a move_callback() in buflib-on-buflib scenarios,
+ * where the memory pool of the "inner" buflib is allocated from an "outer"
+ * buflib.
*
- * Returns: A positive integer handle identifying this allocation, or
- * a negative value on error (0 is also not a valid handle)
+ * \warning This does not run any move callbacks, so it is not safe to
+ * use if any allocations require them.
*/
-int buflib_alloc(struct buflib_context *context, size_t size);
+bool buflib_context_relocate(struct buflib_context *ctx, void *buf);
+/**
+ * \brief Allocate memory from buflib
+ * \param ctx Context to allocate from
+ * \param size Allocation size
+ * \return Handle for the allocation (> 0) or a negative value on error
+ *
+ * This is the same as calling buflib_alloc_ex() with a NULL callbacks
+ * struct. The resulting allocation can be moved by buflib; use pinning
+ * if you need to prevent moves.
+ *
+ * Note that zero is not a valid handle, and will never be returned by
+ * this function. However, this may change, and you should treat a zero
+ * or negative return value as an allocation failure.
+ */
+int buflib_alloc(struct buflib_context *ctx, size_t size);
/**
- * Allocates memory from the buflib's memory pool with additional callbacks
- * and flags
- *
- * name: A string identifier giving this allocation a name
- * size: How many bytes to allocate
- * ops: a struct with pointers to callback functions (see above).
- * if "ops" is NULL: Buffer is movable.
- *
- * Returns: A positive integer handle identifying this allocation, or
- * a negative value on error (0 is also not a valid handle)
+ * \brief Allocate memory from buflib with custom buffer ops
+ * \param ctx Context to allocate from
+ * \param size Allocation size
+ * \param ops Pointer to ops struct or NULL if no ops are needed.
+ * \return Handle for the allocation (> 0) or a negative value on error.
+ *
+ * Use this if you need to pass custom callbacks for responding to buflib
+ * move or shrink operations. Passing a NULL ops pointer means the buffer
+ * can be moved by buflib at any time.
+ *
+ * Note that zero is not a valid handle, and will never be returned by
+ * this function. However, this may change, and you should treat a zero
+ * or negative return value as an allocation failure.
*/
-int buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
+int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
struct buflib_callbacks *ops);
+/**
+ * \brief Attempt a maximum size allocation
+ * \param ctx Context to allocate from
+ * \param size Size of the allocation will be written here on success.
+ * \param ops Pointer to ops struct or NULL if no ops are needed.
+ * \return Handle for the allocation (> 0) or a negative value on error.
+ *
+ * Buflib will attempt to compact and shrink other allocations as much as
+ * possible and then allocate the largest contigous free area. Since this
+ * will consume effectively *all* available memory, future allocations are
+ * likely to fail.
+ *
+ * \note There is rarely any justification to use this with the core_alloc
+ * context due to the impact it has on the entire system. You should
+ * change your code if you think you need this. Of course, if you are
+ * using a private buflib context then this warning does not apply.
+ */
+int buflib_alloc_maximum(struct buflib_context *ctx,
+ size_t *size, struct buflib_callbacks *ops);
/**
- * Gets all available memory from buflib, for temporary use.
- *
- * Since this effectively makes all future allocations fail (unless
- * another allocation is freed in the meantime), you should definitely provide
- * a shrink callback if you plan to hold the buffer for a longer period. This
- * will allow buflib to permit allocations by shrinking the buffer returned by
- * this function.
- *
- * Note that this might return many more bytes than buflib_available() or
- * buflib_allocatable() return, because it aggressively compacts the pool
- * and even shrinks other allocations. However, do not depend on this behavior,
- * it may change.
- *
- * name: A string identifier giving this allocation a name
- * size: The actual size will be returned into size
- * ops: a struct with pointers to callback functions
- *
- * Returns: A positive integer handle identifying this allocation, or
- * a negative value on error (0 is also not a valid handle)
+ * \brief Reduce the size of a buflib allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \param newstart New start address. Must be within the current bounds
+ * of the allocation, as returned by buflib_get_data().
+ * \param new_size New size of the buffer.
+ * \return True if shrinking was successful; otherwise, returns false and
+ * does not modify the allocation.
+ *
+ * Shrinking always succeeds provided the new allocation is contained
+ * within the current allocation. A failure is always a programming
+ * error, so you need not check for it and in the future the failure
+ * case may be changed to a panic or undefined behavior with no return
+ * code.
+ *
+ * The new start address and size need not have any particular alignment,
+ * however buflib cannot work with unaligned addresses so there is rarely
+ * any purpose to creating unaligned allocations.
+ *
+ * Shrinking is typically done from a shrink_callback(), but can be done
+ * at any time if you want to reduce the size of a buflib allocation.
*/
-int buflib_alloc_maximum(struct buflib_context* ctx, const char* name,
- size_t *size, struct buflib_callbacks *ops);
+bool buflib_shrink(struct buflib_context *ctx, int handle,
+ void *newstart, size_t new_size);
/**
- * Queries the data pointer for the given handle. It's actually a cheap
- * operation, don't hesitate using it extensively.
- *
- * Notice that you need to re-query after every direct or indirect yield(),
- * because compaction can happen by other threads which may get your data
- * moved around (or you can get notified about changes by callbacks,
- * see further above).
+ * \brief Increment an allocation's pin count
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
*
- * handle: The handle corresponding to the allocation
+ * The pin count acts like a reference count. Buflib will not attempt to
+ * move any buffer with a positive pin count, nor invoke any move or sync
+ * callbacks. Hence, when pinned, it is safe to hold pointers to a buffer
+ * across yields or use them for I/O.
*
- * Returns: The start pointer of the allocation
+ * Note that shrink callbacks can still be invoked for pinned handles.
*/
-#ifdef DEBUG
-void* buflib_get_data(struct buflib_context *ctx, int handle);
-#else
-static inline void* buflib_get_data(struct buflib_context *ctx, int handle)
-{
- return (void*)(ctx->handle_table[-handle].alloc);
-}
-#endif
+void buflib_pin(struct buflib_context *ctx, int handle);
/**
- * Shrink the memory allocation associated with the given handle
- * Mainly intended to be used with the shrink callback, but it can also
- * be called outside as well, e.g. to give back buffer space allocated
- * with buflib_alloc_maximum().
- *
- * Note that you must move/copy data around yourself before calling this,
- * buflib will not do this as part of shrinking.
- *
- * handle: The handle identifying this allocation
- * new_start: the new start of the allocation
- * new_size: the new size of the allocation
- *
- * Returns: true if shrinking was successful. Otherwise it returns false,
- * without having modified memory.
- *
+ * \brief Decrement an allocation's pin count
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
*/
-bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size);
+void buflib_unpin(struct buflib_context *ctx, int handle);
/**
- * Frees memory associated with the given handle
- *
- * Returns: 0 (to invalidate handles in one line, 0 is not a valid handle)
+ * \brief Return the pin count of an allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Current pin count; zero means the handle is not pinned.
+ */
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle);
+
+/**
+ * \brief Free an allocation and return its memory to the pool
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Always returns zero (zero is not a valid handle, so this can
+ * be used to invalidate the variable containing the handle).
*/
int buflib_free(struct buflib_context *context, int handle);
/**
- * Moves the underlying buflib buffer up by size bytes (as much as
- * possible for size == 0) without moving the end. This effectively
- * reduces the available space by taking away manageable space from the
- * front. This space is not available for new allocations anymore.
- *
- * To make space available in the front, everything is moved up.
- * It does _NOT_ call the move callbacks
- *
- *
- * size: size in bytes to move the buffer up (take away). The actual
- * bytes moved is returned in this
- * Returns: The new start of the underlying buflib buffer
+ * \brief Get a pointer to the buffer for an allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Pointer to the allocation's memory.
+ *
+ * Note that buflib can move allocations in order to free up space when
+ * making new allocations. For this reason, it's unsafe to hold a pointer
+ * to a buffer across a yield() or any other operation that can cause a
+ * context switch. This includes any function that may block, and even
+ * some functions that might not block -- eg. if a low priority thread
+ * acquires a mutex, calling mutex_unlock() may trigger a context switch
+ * to a higher-priority thread.
+ *
+ * buflib_get_data() is a very cheap operation, however, costing only
+ * a few pointer lookups. Don't hesitate to use it extensively.
+ *
+ * If you need to hold a pointer across a possible context switch, pin
+ * the handle with buflib_pin() to prevent the buffer from being moved.
+ * This is required when doing I/O into buflib allocations, for example.
*/
-void* buflib_buffer_out(struct buflib_context *ctx, size_t *size);
+#ifdef BUFLIB_DEBUG_GET_DATA
+void *buflib_get_data(struct buflib_context *ctx, int handle);
+#else
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle);
+#endif
/**
- * Moves the underlying buflib buffer down by size bytes without
- * moving the end. This grows the buflib buffer by adding space to the front.
- * The new bytes are available for new allocations.
+ * \brief Get a pinned pointer to a buflib allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Pointer to the allocation's memory.
*
- * Everything is moved down, and the new free space will be in the middle.
- * It does _NOT_ call the move callbacks.
+ * Functionally equivalent to buflib_pin() followed by buflib_get_data(),
+ * but this call is more efficient and should be preferred over separate
+ * calls.
*
- * size: size in bytes to move the buffer down (new free space)
+ * To unpin the data, call buflib_put_data_pinned() and pass the pointer
+ * returned by this function.
*/
-void buflib_buffer_in(struct buflib_context *ctx, int size);
-
-/* debugging */
+static inline void *buflib_get_data_pinned(struct buflib_context *ctx, int handle);
/**
- * Returns the name, as given to buflib_alloc() and buflib_allloc_ex(), of the
- * allocation associated with the given handle. As naming allocations
- * is optional, there might be no name associated.
- *
- * handle: The handle indicating the allocation
+ * \brief Release a pinned pointer to a buflib allocation
+ * \param ctx Buflib context of the allocation
+ * \param data Pointer returned by buflib_get_data()
*
- * Returns: A pointer to the string identifier of the allocation, or NULL
- * if none was specified with buflib_alloc_ex().
+ * Decrements the pin count, allowing the buffer to be moved once the
+ * pin count drops to zero. This is more efficient than buflib_unpin()
+ * and should be preferred when you have a pointer to the buflib data.
*/
-const char* buflib_get_name(struct buflib_context *ctx, int handle);
+static inline void buflib_put_data_pinned(struct buflib_context *ctx, void *data);
/**
- * Prints an overview of all current allocations with the help
- * of the passed printer helper
- *
- * This walks only the handle table and prints only valid allocations
- *
- * Only available if BUFLIB_DEBUG_BLOCKS is defined
+ * \brief Shift allocations up to free space at the start of the pool
+ * \param ctx Context to operate on
+ * \param size Indicates number of bytes to free up, or 0 to free
+ * up as much as possible. On return, the actual number
+ * of bytes freed is written here.
+ * \return Pointer to the start of the free area
+ *
+ * If `*size` is non-zero, the actual amount of space freed up might
+ * be less than `*size`.
+ *
+ * \warning This will move data around in the pool without calling any
+ * move callbacks!
+ * \warning This function is deprecated and will eventually be removed.
*/
-void buflib_print_allocs(struct buflib_context *ctx, void (*print)(int, const char*));
+void* buflib_buffer_out(struct buflib_context *ctx, size_t *size);
/**
- * Prints an overview of all blocks in the buflib buffer, allocated
- * or unallocated, with the help of the passed printer helper
+ * \brief Shift allocations down into free space below the pool
+ * \param ctx Context to operate on
+ * \param size Number of bytes to add to the pool.
*
- * This walks the entire buffer and prints unallocated space also.
- * The output is also different from buflib_print_allocs().
+ * This operation should only be used to return memory that was previously
+ * taken from the pool with buflib_buffer_out(), by passing the same size
+ * that you got from that function.
*
- * Only available if BUFLIB_DEBUG_BLOCKS is defined
+ * \warning This will move data around in the pool without calling any
+ * move callbacks!
+ * \warning This function is deprecated and will eventually be removed.
*/
-void buflib_print_blocks(struct buflib_context *ctx, void (*print)(int, const char*));
+void buflib_buffer_in(struct buflib_context *ctx, int size);
+#ifdef BUFLIB_DEBUG_PRINT
/**
- * Gets the number of blocks in the entire buffer, allocated or unallocated
+ * Return the number of blocks in the buffer, allocated or unallocated.
*
- * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
+ * Only available if BUFLIB_DEBUG_PRINT is defined.
*/
int buflib_get_num_blocks(struct buflib_context *ctx);
/**
- * Print information about a single block as indicated by block_num
- * into buf
+ * Write a string describing the block at index block_num to the
+ * provided buffer. The buffer will always be null terminated and
+ * there is no provision to detect truncation. (A 40-byte buffer
+ * is enough to contain any returned string.)
*
- * buflib_get_num_blocks() beforehand to get the total number of blocks,
- * as passing an block_num higher than that is undefined
+ * Returns false if the block index is out of bounds, and writes
+ * an empty string.
*
- * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
+ * Only available if BUFLIB_DEBUG_PRINT is defined.
*/
-void buflib_print_block_at(struct buflib_context *ctx, int block_num,
- char* buf, size_t bufsize);
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize);
+#endif
+#ifdef BUFLIB_DEBUG_CHECK_VALID
/**
* Check integrity of given buflib context
*/
void buflib_check_valid(struct buflib_context *ctx);
#endif
+
+#if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL
+#include "buflib_mempool.h"
+#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC
+#include "buflib_malloc.h"
+#endif
+
+#ifndef BUFLIB_ALLOC_OVERHEAD
+# define BUFLIB_ALLOC_OVERHEAD 0
+#endif
+
+#endif /* _BUFLIB_H_ */
diff --git a/firmware/include/buflib_malloc.h b/firmware/include/buflib_malloc.h
new file mode 100644
index 0000000000..a17c75c29a
--- /dev/null
+++ b/firmware/include/buflib_malloc.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _BUFLIB_MALLOC_H_
+#define _BUFLIB_MALLOC_H_
+
+#ifndef _BUFLIB_H_
+# error "include buflib.h instead"
+#endif
+
+struct buflib_malloc_handle
+{
+ void *data;
+ void *user;
+ size_t size;
+ unsigned int pin_count;
+ struct buflib_callbacks *ops;
+};
+
+struct buflib_context
+{
+ struct buflib_malloc_handle *allocs;
+ size_t num_allocs;
+
+ void *buf;
+ size_t bufsize;
+};
+
+#ifndef BUFLIB_DEBUG_GET_DATA
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ return ctx->allocs[handle - 1].user;
+}
+#endif
+
+static inline void *buflib_get_data_pinned(struct buflib_context *ctx, int handle)
+{
+ buflib_pin(ctx, handle);
+ return buflib_get_data(ctx, handle);
+}
+
+void _buflib_malloc_put_data_pinned(struct buflib_context *ctx, void *data);
+static inline void buflib_put_data_pinned(struct buflib_context *ctx, void *data)
+{
+ _buflib_malloc_put_data_pinned(ctx, data);
+}
+
+#endif /* _BUFLIB_MALLOC_H_ */
diff --git a/firmware/include/buflib_mempool.h b/firmware/include/buflib_mempool.h
new file mode 100644
index 0000000000..448e40963a
--- /dev/null
+++ b/firmware/include/buflib_mempool.h
@@ -0,0 +1,97 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* This is a memory allocator designed to provide reasonable management of free
+* space and fast access to allocated data. More than one allocator can be used
+* at a time by initializing multiple contexts.
+*
+* Copyright (C) 2009 Andrew Mahone
+* Copyright (C) 2011 Thomas Martitz
+*
+* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#ifndef _BUFLIB_MEMPOOL_H_
+#define _BUFLIB_MEMPOOL_H_
+
+#ifndef _BUFLIB_H_
+# error "include buflib.h instead"
+#endif
+
+#include "system.h"
+
+/* Indices used to access block fields as block[BUFLIB_IDX_XXX] */
+enum {
+ BUFLIB_IDX_LEN, /* length of the block, must come first */
+ BUFLIB_IDX_HANDLE, /* pointer to entry in the handle table */
+ BUFLIB_IDX_OPS, /* pointer to an ops struct */
+ BUFLIB_IDX_PIN, /* pin count */
+ BUFLIB_NUM_FIELDS,
+};
+
+union buflib_data
+{
+ intptr_t val; /* length of the block in n*sizeof(union buflib_data).
+ Includes buflib metadata overhead. A negative value
+ indicates block is unallocated */
+ volatile unsigned pincount; /* number of pins */
+ struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */
+ char* alloc; /* start of allocated memory area */
+ union buflib_data *handle; /* pointer to entry in the handle table.
+ Used during compaction for fast lookup */
+};
+
+struct buflib_context
+{
+ union buflib_data *handle_table;
+ union buflib_data *first_free_handle;
+ union buflib_data *last_handle;
+ union buflib_data *buf_start;
+ union buflib_data *alloc_end;
+ bool compact;
+};
+
+#define BUFLIB_ALLOC_OVERHEAD (BUFLIB_NUM_FIELDS * sizeof(union buflib_data))
+
+#ifndef BUFLIB_DEBUG_GET_DATA
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ return (void *)ctx->handle_table[-handle].alloc;
+}
+#endif
+
+static inline union buflib_data *_buflib_get_block_header(void *data)
+{
+ union buflib_data *bd = ALIGN_DOWN(data, sizeof(*bd));
+ return bd - BUFLIB_NUM_FIELDS;
+}
+
+static inline void *buflib_get_data_pinned(struct buflib_context *ctx, int handle)
+{
+ void *data = buflib_get_data(ctx, handle);
+ union buflib_data *bd = _buflib_get_block_header(data);
+
+ bd[BUFLIB_IDX_PIN].pincount++;
+ return data;
+}
+
+static inline void buflib_put_data_pinned(struct buflib_context *ctx, void *data)
+{
+ (void)ctx;
+ union buflib_data *bd = _buflib_get_block_header(data);
+ bd[BUFLIB_IDX_PIN].pincount--;
+}
+
+#endif /* _BUFLIB_MEMPOOL_H_ */
diff --git a/firmware/include/chunk_alloc.h b/firmware/include/chunk_alloc.h
new file mode 100644
index 0000000000..ea9f6a64cb
--- /dev/null
+++ b/firmware/include/chunk_alloc.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2023 William Wilgus
+*
+* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+
+#ifndef _CHUNKALLOC_H_
+#define _CHUNKALLOC_H_
+#include <stdbool.h>
+#include <string.h>
+#include "config.h"
+#include "buflib.h"
+
+#define CHUNK_ALLOC_INVALID ((size_t)-1)
+
+
+struct chunk_alloc_header
+{
+ struct buflib_context *context; /* buflib context for all allocations */
+ int chunk_handle; /* data handle of buflib allocated array of struct chunk */
+
+ size_t chunk_bytes_total; /* total bytes in current chunk */
+ size_t chunk_bytes_free; /* free bytes in current chunk */
+ size_t chunk_size; /* default chunk size */
+ size_t count; /* total chunks possible */
+ size_t current; /* current chunk in use */
+
+ struct {
+ int handle;
+ size_t min_offset;
+ size_t max_offset;
+ } cached_chunk;
+};
+
+void chunk_alloc_free(struct chunk_alloc_header *hdr);
+
+bool chunk_alloc_init(struct chunk_alloc_header *hdr,
+ struct buflib_context *ctx,
+ size_t chunk_size, size_t max_chunks);
+
+bool chunk_realloc(struct chunk_alloc_header *hdr,
+ size_t chunk_size, size_t max_chunks);
+
+void chunk_alloc_finalize(struct chunk_alloc_header *hdr);
+
+size_t chunk_alloc(struct chunk_alloc_header *hdr, size_t size); /* Returns offset */
+
+void* chunk_get_data(struct chunk_alloc_header *hdr, size_t offset); /* Returns data */
+
+void chunk_put_data(struct chunk_alloc_header *hdr, void* data, size_t offset);
+
+static inline bool chunk_alloc_is_initialized(struct chunk_alloc_header *hdr)
+{
+ return (hdr->chunk_handle > 0);
+}
+#endif
diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h
index 67fe99dfdc..dc9b2036ec 100644
--- a/firmware/include/core_alloc.h
+++ b/firmware/include/core_alloc.h
@@ -5,38 +5,39 @@
#include <stdbool.h>
#include "config.h"
#include "buflib.h"
+#include "chunk_alloc.h"
/* All functions below are wrappers for functions in buflib.h, except
* they have a predefined context
*/
void core_allocator_init(void) INIT_ATTR;
-int core_alloc(const char* name, size_t size);
-int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops);
-int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops);
+int core_alloc(size_t size);
+int core_alloc_ex(size_t size, struct buflib_callbacks *ops);
+int core_alloc_maximum(size_t *size, struct buflib_callbacks *ops);
bool core_shrink(int handle, void* new_start, size_t new_size);
+void core_pin(int handle);
+void core_unpin(int handle);
+unsigned core_pin_count(int handle);
int core_free(int handle);
size_t core_available(void);
size_t core_allocatable(void);
-const char* core_get_name(int handle);
-#ifdef DEBUG
+
+#ifdef BUFLIB_DEBUG_CHECK_VALID
void core_check_valid(void);
#endif
/* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call
* the move callbacks and are therefore unsafe in the core */
-#ifdef BUFLIB_DEBUG_BLOCKS
-void core_print_allocs(void (*print)(const char*));
-void core_print_blocks(void (*print)(const char*));
-#endif
-#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
-int core_get_num_blocks(void);
-void core_print_block_at(int block_num, char* buf, size_t bufsize);
-#endif
+#ifdef BUFLIB_DEBUG_PRINT
+int core_get_num_blocks(void);
+bool core_print_block_at(int block_num, char* buf, size_t bufsize);
/* frees the debug test alloc created at initialization,
- * since this is the first any further alloc should force a compaction run */
+ * since this is the first any further alloc should force a compaction run
+ * only used if debug print is active */
bool core_test_free(void);
+#endif
static inline void* core_get_data(int handle)
{
@@ -44,4 +45,23 @@ static inline void* core_get_data(int handle)
return buflib_get_data(&core_ctx, handle);
}
+static inline void* core_get_data_pinned(int handle)
+{
+ extern struct buflib_context core_ctx;
+ return buflib_get_data_pinned(&core_ctx, handle);
+}
+
+static inline void core_put_data_pinned(void *data)
+{
+ extern struct buflib_context core_ctx;
+ buflib_put_data_pinned(&core_ctx, data);
+}
+
+/* core context chunk_alloc */
+static inline bool core_chunk_alloc_init(struct chunk_alloc_header *hdr,
+ size_t chunk_size, size_t max_chunks)
+{
+ extern struct buflib_context core_ctx;
+ return chunk_alloc_init(hdr, &core_ctx, chunk_size, max_chunks);
+}
#endif /* __CORE_ALLOC_H__ */
diff --git a/firmware/include/dir.h b/firmware/include/dir.h
index 2f78b11cf5..4599877ede 100644
--- a/firmware/include/dir.h
+++ b/firmware/include/dir.h
@@ -63,6 +63,9 @@
#ifndef dir_exists
#define dir_exists FS_PREFIX(dir_exists)
#endif
+#ifndef root_realpath
+#define root_realpath FS_PREFIX(root_realpath)
+#endif
#endif /* !DIRFUNCTIONS_DEFINED */
#ifndef DIRENT_DEFINED
@@ -83,6 +86,9 @@ struct dirinfo
#ifndef DIRFUNCTIONS_DECLARED
/* TIP: set errno to zero before calling to see if anything failed */
struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry);
+const char* root_realpath(void);
#endif /* !DIRFUNCTIONS_DECLARED */
+
+
#endif /* _DIR_H_ */
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 9fae16b551..36f68b7251 100644
--- a/firmware/include/dircache_redirect.h
+++ b/firmware/include/dircache_redirect.h
@@ -20,8 +20,24 @@
****************************************************************************/
#ifndef _DIRCACHE_REDIRECT_H_
+#include "rbpaths.h"
+#include "pathfuncs.h"
#include "dir.h"
+#include "dircache.h"
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER)
+#include "rb-loader.h"
+#include "multiboot.h"
+#include "bootdata.h"
+#include "crc32.h"
+#endif
+
+#ifndef RB_ROOT_VOL_HIDDEN
+#define RB_ROOT_VOL_HIDDEN(v) (0 == 0)
+#endif
+#ifndef RB_ROOT_CONTENTS_DIR
+#define RB_ROOT_CONTENTS_DIR "/"
+#endif
/***
** Internal redirects that depend upon whether or not dircache is made
**
@@ -123,10 +139,65 @@ static inline void fileop_onsync_internal(struct filestr_base *stream)
static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
{
+#if (defined(HAVE_MULTIVOLUME) || (defined(HAVE_MULTIBOOT) && !defined(BOOTLOADER)))
+ char path[VOL_MAX_LEN+2];
+#endif
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER)
+ char rtpath[MAX_PATH / 2];
+ make_volume_root(volume, path);
+
+ unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
+ if (crc > 0 && crc == boot_data.crc)
+ {
+ /* we need to mount the drive before we can access it */
+ root_mount_path(path, 0); /* root could be different folder don't hide */
+
+/*BUGFIX bootloader is less selective about which drives it will mount -- revisit */
+#if defined(HAVE_MULTIDRIVE) && (NUM_VOLUMES_PER_DRIVE == 1)
+ if (volume_drive(volume) == boot_data.boot_volume
+ || volume == boot_data.boot_volume)
+#else
+ if (volume == boot_data.boot_volume) /* boot volume contained in uint8_t payload */
+#endif
+ {
+ int rtlen = get_redirect_dir(rtpath, sizeof(rtpath), volume, "", "");
+ while (rtlen > 0 && rtpath[--rtlen] == PATH_SEPCH)
+ rtpath[rtlen] = '\0'; /* remove extra separators */
+
+#if 0 /*removed, causes issues with playback for now?*/
+ if (rtlen <= 0 || rtpath[rtlen] == VOL_END_TOK)
+ root_unmount_volume(volume); /* unmount so root can be hidden*/
+#endif
+ if (rtlen <= 0) /* Error occurred, card removed? Set root to default */
+ {
+ root_unmount_volume(volume); /* unmount so root can be hidden*/
+ goto standard_redirect;
+ }
+ root_mount_path(rtpath, NSITEM_CONTENTS);
+ }
+
+ } /*CRC OK*/
+ else
+ {
+standard_redirect:
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+ if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false))
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
+ }
+#elif defined(HAVE_MULTIVOLUME)
+ make_volume_root(volume, path);
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+ if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false))
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
+#else
+ const char *path = PATH_ROOTSTR;
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
+#endif /* HAVE_MULTIBOOT */
+
#ifdef HAVE_DIRCACHE
dircache_mount();
#endif
- IF_MV( (void)volume; )
}
static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
@@ -135,6 +206,7 @@ static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
/* First, to avoid update of something about to be destroyed anyway */
dircache_unmount(IF_MV(volume));
#endif
+ root_unmount_volume(IF_MV(volume));
fileobj_mgr_unmount(IF_MV(volume));
}
@@ -152,7 +224,7 @@ static inline void fileop_onunmount_internal(struct filestr_base *stream)
static inline int readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
#ifdef HAVE_DIRCACHE
return dircache_readdir_dirent(stream, scanp, entry);
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d62b5a8541..ec0c5d28f0 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -30,6 +30,7 @@
#include "fs_attr.h"
#include "fs_defines.h"
#include "fat.h"
+#include "dir.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
@@ -72,17 +73,20 @@ enum fildes_and_obj_flags
/* used in descriptor and common */
FDO_BUSY = 0x0001, /* descriptor/object is in use */
/* only used in individual stream descriptor */
- FD_WRITE = 0x0002, /* descriptor has write mode */
- FD_WRONLY = 0x0004, /* descriptor is write mode only */
- FD_APPEND = 0x0008, /* descriptor is append mode */
+ FD_VALID = 0x0002, /* descriptor is valid but not registered */
+ FD_WRITE = 0x0004, /* descriptor has write mode */
+ FD_WRONLY = 0x0008, /* descriptor is write mode only */
+ FD_APPEND = 0x0010, /* descriptor is append mode */
FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
/* only used as common flags */
- FO_DIRECTORY = 0x0010, /* fileobj is a directory */
- FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */
- FO_REMOVED = 0x0040, /* fileobj was deleted while open */
- FO_SINGLE = 0x0080, /* fileobj has only one stream open */
- FDO_MASK = 0x00ff,
- FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
+ FO_DIRECTORY = 0x0020, /* fileobj is a directory */
+ FO_TRUNC = 0x0040, /* fileobj is opened to be truncated */
+ FO_REMOVED = 0x0080, /* fileobj was deleted while open */
+ FO_SINGLE = 0x0100, /* fileobj has only one stream open */
+ FO_MOUNTTARGET = 0x0200, /* fileobj kept open as a mount target */
+ FDO_MASK = 0x03ff,
+ FDO_CHG_MASK = FO_TRUNC,
+ /* fileobj permitted external change */ /* fileobj permitted external change */
/* bitflags that instruct various 'open' functions how to behave;
* saved in stream flags (only) but not used by manager */
FF_FILE = 0x00000000, /* expect file; accept file only */
@@ -95,7 +99,9 @@ enum fildes_and_obj_flags
FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */
FF_INFO = 0x00400000, /* return info on self */
FF_PARENTINFO = 0x00800000, /* return info on parent */
- FF_MASK = 0x00ff0000,
+ FF_DEVPATH = 0x01000000, /* path is a device path, not root-based */
+ FF_NOFS = 0x02000000, /* no filesystem mounted here */
+ FF_MASK = 0x03ff0000,
};
/** Common data structures used throughout **/
@@ -229,10 +235,10 @@ int test_stream_exists_internal(const char *path, unsigned int callflags);
int open_noiso_internal(const char *path, int oflag); /* file.c */
void force_close_writer_internal(struct filestr_base *stream); /* file.c */
-struct dirent;
+struct DIRENT;
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry);
+ struct DIRENT *entry);
void uncached_rewinddir_dirent(struct dirscan_info *scanp);
int uncached_readdir_internal(struct filestr_base *stream,
@@ -333,7 +339,7 @@ static inline struct fat_direntry *get_dir_fatent(void)
void iso_decode_d_name(char *d_name);
#ifdef HAVE_DIRCACHE
-void empty_dirent(struct dirent *entry);
+void empty_dirent(struct DIRENT *entry);
void fill_dirinfo_native(struct dirinfo_native *din);
#endif /* HAVE_DIRCACHE */
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h
index 627d2df341..0db3520d34 100644
--- a/firmware/include/fileobj_mgr.h
+++ b/firmware/include/fileobj_mgr.h
@@ -29,6 +29,11 @@ void file_binding_remove(struct file_base_binding *bindp);
void file_binding_remove_next(struct file_base_binding *prevp,
struct file_base_binding *bindp);
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp);
+void fileobj_unmount(struct file_base_binding *bindp);
+
void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags);
diff --git a/firmware/include/fs_defines.h b/firmware/include/fs_defines.h
index 538c4b36cd..f4c8385d8a 100644
--- a/firmware/include/fs_defines.h
+++ b/firmware/include/fs_defines.h
@@ -45,17 +45,26 @@
/* limits for number of open descriptors - if you increase these values, make
certain that the disk cache has enough available buffers */
+
+#if MEMORYSIZE < 8
#define MAX_OPEN_FILES 11
#define MAX_OPEN_DIRS 12
+#else
+#define MAX_OPEN_FILES 31
+#define MAX_OPEN_DIRS 32
+#endif /* MEMORYSIZE */
+
/* internal functions open streams as well; make sure they don't fail if all
user descs are busy; this needs to be at least the greatest quantity needed
at once by all internal functions */
+#define MOUNT_AUX_FILEOBJS 1
#ifdef HAVE_DIRCACHE
-#define AUX_FILEOBJS 3
+#define DIRCACHE_AUX_FILEOBJS 1
#else
-#define AUX_FILEOBJS 2
+#define DIRCACHE_AUX_FILEOBJS 0
#endif
+#define AUX_FILEOBJS (2+DIRCACHE_AUX_FILEOBJS+MOUNT_AUX_FILEOBJS)
/* number of components statically allocated to handle the vast majority
of path depths; should maybe be tuned for >= 90th percentile but for now,
@@ -94,10 +103,7 @@
#if MEMORYSIZE < 8
#define DC_NUM_ENTRIES 32
#define DC_MAP_NUM_ENTRIES 128
-#elif MEMORYSIZE <= 32
-#define DC_NUM_ENTRIES 48
-#define DC_MAP_NUM_ENTRIES 128
-#else /* MEMORYSIZE > 32 */
+#else
#define DC_NUM_ENTRIES 64
#define DC_MAP_NUM_ENTRIES 256
#endif /* MEMORYSIZE */
diff --git a/firmware/include/inflate.h b/firmware/include/inflate.h
index b56ccf181a..1fce186f34 100644
--- a/firmware/include/inflate.h
+++ b/firmware/include/inflate.h
@@ -23,6 +23,7 @@
#define _INFLATE_H_
#include <stdint.h>
+#include <stddef.h>
enum {
INFLATE_RAW,
@@ -43,4 +44,27 @@ extern const uint32_t inflate_align;
// see above enum for possible options.
int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx);
+struct inflate_bufferctx {
+ // initialize this with your input/output buffer.
+ // the pointer is updated as data is read or written.
+ void* buf;
+
+ // buffer end marker (= buf + buf_size).
+ void* end;
+};
+
+// reader and writer for using an in-memory buffer.
+// Use 'inflate_bufferctx' as the context argument.
+uint32_t inflate_buffer_reader(void* block, uint32_t block_size, void* ctx);
+uint32_t inflate_buffer_writer(const void* block, uint32_t block_size, void* ctx);
+
+// dummy writer used if you just want to figure out how big the decompressed
+// data will be. It does not actually write any data. Example usage:
+//
+// size_t size = 0;
+// inflate(it, st, read, rctx, inflate_getsize_writer, &size);
+//
+// Now 'size' will be the size of the decompressed data (assuming no errors).
+uint32_t inflate_getsize_writer(const void* block, uint32_t block_size, void* ctx);
+
#endif
diff --git a/firmware/include/linked_list.h b/firmware/include/linked_list.h
index c678cfa7eb..1e077be22a 100644
--- a/firmware/include/linked_list.h
+++ b/firmware/include/linked_list.h
@@ -21,6 +21,8 @@
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
+#include <stddef.h>
+
/***
** NOTES:
** Node field order chosen so that one type can alias the other for forward
@@ -51,7 +53,15 @@ struct ll_node
struct ll_node *next; /* Next list item */
};
-void ll_init(struct ll_head *list);
+/**
+ * Initializes the singly-linked list
+ */
+static inline void ll_init(struct ll_head *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+}
+
void ll_insert_next(struct ll_head *list, struct ll_node *node,
struct ll_node *newnode);
void ll_insert_last(struct ll_head *list, struct ll_node *node);
@@ -81,7 +91,22 @@ struct lld_node
struct lld_node *prev; /* Previous list item */
};
-void lld_init(struct lld_head *list);
+/**
+ * Initializes the doubly-linked list
+ */
+static inline void lld_init(struct lld_head *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+
+ /* tail could be stored in first item's prev pointer but this simplifies
+ the routines and maintains the non-circularity */
+}
+
+void lld_insert_next(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode);
+void lld_insert_prev(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode);
void lld_insert_first(struct lld_head *list, struct lld_node *node);
void lld_insert_last(struct lld_head *list, struct lld_node *node);
void lld_remove(struct lld_head *list, struct lld_node *node);
@@ -106,7 +131,14 @@ struct lldc_node
struct lldc_node *prev; /* Previous list item */
};
-void lldc_init(struct lldc_head *list);
+/**
+ * Initializes the doubly-linked circular list
+ */
+static inline void lldc_init(struct lldc_head *list)
+{
+ list->head = NULL;
+}
+
void lldc_insert_first(struct lldc_head *list, struct lldc_node *node);
void lldc_insert_last(struct lldc_head *list, struct lldc_node *node);
void lldc_remove(struct lldc_head *list, struct lldc_node *node);
diff --git a/firmware/include/rb-loader.h b/firmware/include/rb-loader.h
index 71b6e038aa..d554ace95e 100644
--- a/firmware/include/rb-loader.h
+++ b/firmware/include/rb-loader.h
@@ -18,21 +18,9 @@
*
****************************************************************************/
+#ifndef __RB_LOADER_H__
+#define __RB_LOADER_H__
+
int load_firmware(unsigned char* buf, const char* firmware, int buffer_size);
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware);
-#endif
+#endif /* __RB_LOADER_H__ */
diff --git a/firmware/include/rb_namespace.h b/firmware/include/rb_namespace.h
new file mode 100644
index 0000000000..5cd8c2dd29
--- /dev/null
+++ b/firmware/include/rb_namespace.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2017 by Michael Sevakis
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef RB_NAMESPACE_H
+#define RB_NAMESPACE_H
+
+#include "file_internal.h"
+
+enum ns_item_flags
+{
+ NSITEM_MOUNTED = 0x01, /* item is mounted */
+ NSITEM_HIDDEN = 0x02, /* item is not enumerated */
+ NSITEM_CONTENTS = 0x04, /* contents enumerate */
+};
+
+struct ns_scan_info
+{
+ struct dirscan_info scan; /* dirscan info - first! */
+ int item; /* current item in parent */
+};
+
+/* root functions */
+#define ROOT_MAX_REALPATH 80
+const char* root_get_realpath(void);
+int root_mount_path(const char *path, unsigned int flags);
+void root_unmount_volume(IF_MV_NONVOID(int volume));
+int root_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp,
+ struct DIRENT *entry);
+
+/* namespace functions */
+int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp);
+int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
+ struct file_base_info *infop, uint16_t *attrp);
+int ns_open_stream(const char *path, unsigned int callflags,
+ struct filestr_base *stream, struct ns_scan_info *scanp);
+bool ns_volume_is_visible(IF_MV_NONVOID(int volume));
+
+/* closes the namespace stream */
+static inline int ns_close_stream(struct filestr_base *stream)
+{
+ return close_stream_internal(stream);
+}
+
+#include "dircache_redirect.h"
+
+static inline void ns_dirscan_rewind(struct ns_scan_info *scanp)
+{
+ rewinddir_dirent(&scanp->scan);
+ if (scanp->item != -1)
+ scanp->item = 0;
+}
+
+static inline int ns_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp,
+ struct dirent *entry)
+
+{
+ if (scanp->item == -1)
+ return readdir_dirent(stream, &scanp->scan, entry);
+ else
+ return root_readdir_dirent(stream, scanp, entry);
+}
+
+#endif /* RB_NAMESPACE_H */
diff --git a/firmware/include/rbendian.h b/firmware/include/rbendian.h
index 8adcb544f9..8a6bb43a05 100644
--- a/firmware/include/rbendian.h
+++ b/firmware/include/rbendian.h
@@ -184,4 +184,241 @@ static inline uint32_t swaw32_hw(uint32_t value)
#error "Unknown endianness!"
#endif
+/*
+ * Generic unaligned loads
+ */
+static inline uint16_t _generic_load_le16(const void* p)
+{
+ const uint8_t* d = p;
+ return d[0] | (d[1] << 8);
+}
+
+static inline uint32_t _generic_load_le32(const void* p)
+{
+ const uint8_t* d = p;
+ return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+}
+
+static inline uint64_t _generic_load_le64(const void* p)
+{
+ const uint8_t* d = p;
+ return (((uint64_t)d[0] << 0) | ((uint64_t)d[1] << 8) |
+ ((uint64_t)d[2] << 16) | ((uint64_t)d[3] << 24) |
+ ((uint64_t)d[4] << 32) | ((uint64_t)d[5] << 40) |
+ ((uint64_t)d[6] << 48) | ((uint64_t)d[7] << 56));
+}
+
+static inline uint16_t _generic_load_be16(const void* p)
+{
+ const uint8_t* d = p;
+ return (d[0] << 8) | d[1];
+}
+
+static inline uint32_t _generic_load_be32(const void* p)
+{
+ const uint8_t* d = p;
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline uint64_t _generic_load_be64(const void* p)
+{
+ const uint8_t* d = p;
+ return (((uint64_t)d[0] << 56) | ((uint64_t)d[1] << 48) |
+ ((uint64_t)d[2] << 40) | ((uint64_t)d[3] << 32) |
+ ((uint64_t)d[4] << 24) | ((uint64_t)d[5] << 16) |
+ ((uint64_t)d[6] << 8) | ((uint64_t)d[7] << 0));
+}
+
+static inline void _generic_store_le16(void* p, uint16_t val)
+{
+ uint8_t* d = p;
+ d[0] = val & 0xff;
+ d[1] = (val >> 8) & 0xff;
+}
+
+static inline void _generic_store_le32(void* p, uint32_t val)
+{
+ uint8_t* d = p;
+ d[0] = val & 0xff;
+ d[1] = (val >> 8) & 0xff;
+ d[2] = (val >> 16) & 0xff;
+ d[3] = (val >> 24) & 0xff;
+}
+
+static inline void _generic_store_le64(void* p, uint64_t val)
+{
+ uint8_t* d = p;
+ d[0] = val & 0xff;
+ d[1] = (val >> 8) & 0xff;
+ d[2] = (val >> 16) & 0xff;
+ d[3] = (val >> 24) & 0xff;
+ d[4] = (val >> 32) & 0xff;
+ d[5] = (val >> 40) & 0xff;
+ d[6] = (val >> 48) & 0xff;
+ d[7] = (val >> 56) & 0xff;
+}
+
+static inline void _generic_store_be16(void* p, uint16_t val)
+{
+ uint8_t* d = p;
+ d[0] = (val >> 8) & 0xff;
+ d[1] = val & 0xff;
+}
+
+static inline void _generic_store_be32(void* p, uint32_t val)
+{
+ uint8_t* d = p;
+ d[0] = (val >> 24) & 0xff;
+ d[1] = (val >> 16) & 0xff;
+ d[2] = (val >> 8) & 0xff;
+ d[3] = val & 0xff;
+}
+
+static inline void _generic_store_be64(void* p, uint64_t val)
+{
+ uint8_t* d = p;
+ d[0] = (val >> 56) & 0xff;
+ d[1] = (val >> 48) & 0xff;
+ d[2] = (val >> 40) & 0xff;
+ d[3] = (val >> 32) & 0xff;
+ d[4] = (val >> 24) & 0xff;
+ d[5] = (val >> 16) & 0xff;
+ d[6] = (val >> 8) & 0xff;
+ d[7] = val & 0xff;
+}
+
+#if !defined(HAVE_UNALIGNED_LOAD_STORE)
+
+/* Use generic unaligned loads */
+#define load_le16 _generic_load_le16
+#define load_le32 _generic_load_le32
+#define load_le64 _generic_load_le64
+#define load_be16 _generic_load_be16
+#define load_be32 _generic_load_be32
+#define load_be64 _generic_load_be64
+#define store_le16 _generic_store_le16
+#define store_le32 _generic_store_le32
+#define store_le64 _generic_store_le64
+#define store_be16 _generic_store_be16
+#define store_be32 _generic_store_be32
+#define store_be64 _generic_store_be64
+
+/* Define host byte order unaligned load */
+#if defined(ROCKBOX_LITTLE_ENDIAN)
+# define load_h16 load_le16
+# define load_h32 load_le32
+# define load_h64 load_le64
+# define store_h16 store_le16
+# define store_h32 store_le32
+# define store_h64 store_le64
+#elif defined(ROCKBOX_BIG_ENDIAN)
+# define load_h16 load_be16
+# define load_h32 load_be32
+# define load_h64 load_be64
+# define store_h16 store_be16
+# define store_h32 store_be32
+# define store_h64 store_be64
+#else
+# error
+#endif
+
+#else /* HAVE_UNALIGNED_LOAD_STORE */
+
+/* The arch should define unaligned loads in host byte order */
+#if defined(ROCKBOX_LITTLE_ENDIAN)
+# define load_le16 load_h16
+# define load_le32 load_h32
+# define load_le64 load_h64
+# define load_be16(p) swap16(load_h16((p)))
+# define load_be32(p) swap32(load_h32((p)))
+# define load_be64(p) swap64(load_h64((p)))
+# define store_le16 store_h16
+# define store_le32 store_h32
+# define store_le64 store_h64
+# define store_be16(p,v) store_h16((p),swap16((v)))
+# define store_be32(p,v) store_h32((p),swap32((v)))
+# define store_be64(p,v) store_h64((p),swap64((v)))
+#elif defined(ROCKBOX_BIG_ENDIAN)
+# define load_le16(p) swap16(load_h16((p)))
+# define load_le32(p) swap32(load_h32((p)))
+# define load_le64(p) swap64(load_h64((p)))
+# define load_be16 load_h16
+# define load_be32 load_h32
+# define load_be64 load_h64
+# define store_le16(p,v) store_h16((p),swap16((v)))
+# define store_le32(p,v) store_h32((p),swap32((v)))
+# define store_le64(p,v) store_h64((p),swap64((v)))
+# define store_be16 store_h16
+# define store_be32 store_h32
+# define store_be64 store_h64
+#else
+# error
+#endif
+
+#endif /* HAVE_UNALIGNED_LOAD_STORE */
+
+/*
+ * Aligned loads
+ */
+
+static inline uint16_t load_h16_aligned(const void* p)
+{
+ return *(const uint16_t*)p;
+}
+
+static inline uint32_t load_h32_aligned(const void* p)
+{
+ return *(const uint32_t*)p;
+}
+
+static inline uint64_t load_h64_aligned(const void* p)
+{
+ return *(const uint64_t*)p;
+}
+
+static inline void store_h16_aligned(void* p, uint16_t val)
+{
+ *(uint16_t*)p = val;
+}
+
+static inline void store_h32_aligned(void* p, uint32_t val)
+{
+ *(uint32_t*)p = val;
+}
+
+static inline void store_h64_aligned(void* p, uint64_t val)
+{
+ *(uint64_t*)p = val;
+}
+
+#if defined(ROCKBOX_LITTLE_ENDIAN)
+# define load_le16_aligned load_h16_aligned
+# define load_le32_aligned load_h32_aligned
+# define load_le64_aligned load_h64_aligned
+# define load_be16_aligned(p) swap16(load_h16_aligned((p)))
+# define load_be32_aligned(p) swap32(load_h32_aligned((p)))
+# define load_be64_aligned(p) swap64(load_h64_aligned((p)))
+# define store_le16_aligned store_h16_aligned
+# define store_le32_aligned store_h32_aligned
+# define store_le64_aligned store_h64_aligned
+# define store_be16_aligned(p,v) store_h16_aligned((p),swap16((v)))
+# define store_be32_aligned(p,v) store_h32_aligned((p),swap32((v)))
+# define store_be64_aligned(p,v) store_h64_aligned((p),swap64((v)))
+#elif defined(ROCKBOX_BIG_ENDIAN)
+# define load_le16_aligned(p) swap16(load_h16_aligned((p)))
+# define load_le32_aligned(p) swap32(load_h32_aligned((p)))
+# define load_le64_aligned(p) swap64(load_h64_aligned((p)))
+# define load_be16_aligned load_h16_aligned
+# define load_be32_aligned load_h32_aligned
+# define load_be64_aligned load_h64_aligned
+# define store_le16_aligned(p,v) store_h16_aligned((p),swap16((v)))
+# define store_le32_aligned(p,v) store_h32_aligned((p),swap32((v)))
+# define store_le64_aligned(p,v) store_h64_aligned((p),swap64((v)))
+# define store_be16_aligned store_h16_aligned
+# define store_be32_aligned store_h32_aligned
+# define store_be64_aligned store_h64_aligned
+#else
+# error "Unknown endian!"
+#endif
+
#endif /* _RBENDIAN_H_ */
diff --git a/firmware/include/string-extra.h b/firmware/include/string-extra.h
index 549a018dfc..a9b34661a7 100644
--- a/firmware/include/string-extra.h
+++ b/firmware/include/string-extra.h
@@ -25,6 +25,7 @@
#include "strlcat.h"
#include "strcasecmp.h"
#include "strcasestr.h"
+#include "strmemccpy.h"
#include "strtok_r.h"
#include "memset16.h"
diff --git a/firmware/export/structec.h b/firmware/include/strmemccpy.h
index b3e7d69efa..c7004610dd 100644
--- a/firmware/export/structec.h
+++ b/firmware/include/strmemccpy.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2007 by Miika Pekkarinen
+ * Copyright (C) 2022 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,15 +19,14 @@
*
****************************************************************************/
-#ifndef _STRUCTEC_H
-#define _STRUCTEC_H
-#include <sys/types.h>
-#include <stdbool.h>
-
-void structec_convert(void *structure, const char *ecinst,
- long count, bool enable);
-ssize_t ecread(int fd, void *buf, size_t scount, const char *ecinst, bool ec);
-ssize_t ecwrite(int fd, const void *buf, size_t scount, const char *ecinst, bool ec);
+#ifndef __STRMEMCCPY_H__
+#define __STRMEMCCPY_H__
+/* copies src to a buffer of len bytes stopping after
+ * len or the first NULL (\0) in src
+ * NULL terminates except when len = 0
+ * If len was exceeded NULL is returned otherwise returns
+ * a pointer to the first byte following the NULL in dst.
+*/
+char * strmemccpy(char *dst, const char *src, size_t len);
#endif
-
diff --git a/firmware/include/strptokspn_r.h b/firmware/include/strptokspn_r.h
new file mode 100644
index 0000000000..d565118190
--- /dev/null
+++ b/firmware/include/strptokspn_r.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 William Wilgus
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef __STRPTOKSPN_R_H__
+#define __STRPTOKSPN_R_H__
+const char *strptokspn_r(const char *ptr, const char *sep, size_t *len, const char **end);
+#endif
diff --git a/firmware/kernel/include/mutex.h b/firmware/kernel/include/mutex.h
index 4778eb7f11..816d8040cb 100644
--- a/firmware/kernel/include/mutex.h
+++ b/firmware/kernel/include/mutex.h
@@ -36,10 +36,5 @@ struct mutex
extern void mutex_init(struct mutex *m);
extern void mutex_lock(struct mutex *m);
extern void mutex_unlock(struct mutex *m);
-#ifndef HAVE_PRIORITY_SCHEDULING
-/* Deprecated but needed for now - firmware/drivers/ata_mmc.c */
-static inline bool mutex_test(const struct mutex *m)
- { return m->blocker.thread != NULL; }
-#endif /* HAVE_PRIORITY_SCHEDULING */
#endif /* MUTEX_H */
diff --git a/firmware/kernel/include/queue.h b/firmware/kernel/include/queue.h
index 515a7e43a8..a9c3b5a93a 100644
--- a/firmware/kernel/include/queue.h
+++ b/firmware/kernel/include/queue.h
@@ -38,7 +38,7 @@
/* make sure SYS_EVENT_CLS_BITS has enough range */
/* Bit 31->|S|c...c|i...i| */
-#define SYS_EVENT ((long)(int)(1 << 31))
+#define SYS_EVENT ((long)(int)(1u << 31))
#define SYS_EVENT_CLS_BITS (3)
#define SYS_EVENT_CLS_SHIFT (31-SYS_EVENT_CLS_BITS)
#define SYS_EVENT_CLS_MASK (((1l << SYS_EVENT_CLS_BITS)-1) << SYS_EVENT_SHIFT)
@@ -58,6 +58,7 @@
#define SYS_CHARGER_CONNECTED MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 1)
#define SYS_CHARGER_DISCONNECTED MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 2)
#define SYS_BATTERY_UPDATE MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 3)
+#define SYS_REBOOT MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 4)
#define SYS_FS_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_FILESYS, 0)
#define SYS_HOTSWAP_INSERTED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 0)
#define SYS_HOTSWAP_EXTRACTED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 1)
diff --git a/firmware/kernel/include/tick.h b/firmware/kernel/include/tick.h
index 9810f4a1e5..d1933dbe9b 100644
--- a/firmware/kernel/include/tick.h
+++ b/firmware/kernel/include/tick.h
@@ -23,7 +23,7 @@
#include "config.h"
#include "system.h" /* for NULL */
-extern void init_tick(void);
+extern void init_tick(void) INIT_ATTR;
#define HZ 100 /* number of ticks per second */
diff --git a/firmware/kernel/pthread/corelock.c b/firmware/kernel/pthread/corelock.c
deleted file mode 100644
index 10b4329639..0000000000
--- a/firmware/kernel/pthread/corelock.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <pthread.h>
-#include "kernel.h"
-
-void corelock_init(struct corelock *lk)
-{
- lk->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
-}
-
-void corelock_lock(struct corelock *lk)
-{
- pthread_mutex_lock(&lk->mutex);
-}
-
-
-void corelock_unlock(struct corelock *lk)
-{
- pthread_mutex_unlock(&lk->mutex);
-}
diff --git a/firmware/kernel/pthread/mutex.c b/firmware/kernel/pthread/mutex.c
deleted file mode 100644
index 49503b5d82..0000000000
--- a/firmware/kernel/pthread/mutex.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <pthread.h>
-#include "kernel.h"
-
-void mutex_init(struct mutex *m)
-{
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&m->mutex, &attr);
- pthread_mutexattr_destroy(&attr);
-}
-
-void mutex_lock(struct mutex *m)
-{
- pthread_mutex_lock(&m->mutex);
-}
-
-void mutex_unlock(struct mutex *m)
-{
- pthread_mutex_unlock(&m->mutex);
-}
diff --git a/firmware/kernel/pthread/thread.c b/firmware/kernel/pthread/thread.c
deleted file mode 100644
index 71cbd1d136..0000000000
--- a/firmware/kernel/pthread/thread.c
+++ /dev/null
@@ -1,204 +0,0 @@
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <pthread.h>
-#include "/usr/include/semaphore.h"
-#include "thread-internal.h"
-#include "kernel.h"
-
-#define NSEC_PER_SEC 1000000000L
-static inline void timespec_add_ns(struct timespec *a, uint64_t ns)
-{
- lldiv_t q = lldiv(a->tv_nsec + ns, NSEC_PER_SEC);
- a->tv_sec += q.quot;
- a->tv_nsec = q.rem;
-}
-
-static int threads_initialized;
-
-struct thread_init_data {
- void (*function)(void);
- bool start_frozen;
- sem_t init_sem;
- struct thread_entry *entry;
-};
-
-__thread struct thread_entry *_current;
-
-unsigned int thread_self(void)
-{
- return (unsigned) pthread_self();
-}
-
-static struct thread_entry_item {
- unsigned thread_id;
- struct thread_entry *entry;
-} entry_lookup[32];
-
-
-
-static struct thread_entry_item *__find_thread_entry(unsigned thread_id)
-{
- int i;
-
- for (i = 0; i < 32; i++)
- {
- if (entry_lookup[i].thread_id == thread_id)
- return &entry_lookup[i];
- }
- return NULL;
-}
-
-static struct thread_entry *find_thread_entry(unsigned thread_id)
-{
- return __find_thread_entry(thread_id)->entry;
-}
-
-static void *trampoline(void *arg)
-{
- struct thread_init_data *data = arg;
-
- void (*thread_fn)(void) = data->function;
-
- _current = data->entry;
-
- if (data->start_frozen)
- {
- struct corelock thaw_lock;
- corelock_init(&thaw_lock);
- corelock_lock(&thaw_lock);
-
- _current->lock = &thaw_lock;
- sem_post(&data->init_sem);
- block_thread_switch(_current, _current->lock);
- _current->lock = NULL;
-
- corelock_unlock(&thaw_lock);
- }
- else
- sem_post(&data->init_sem);
-
- free(data);
- thread_fn();
-
- return NULL;
-}
-
-void thread_thaw(unsigned int thread_id)
-{
- struct thread_entry *e = find_thread_entry(thread_id);
- if (e->lock)
- {
- corelock_lock(e->lock);
- wakeup_thread(e);
- corelock_unlock(e->lock);
- }
- /* else: no lock. must be running already */
-}
-
-void init_threads(void)
-{
- struct thread_entry_item *item0 = &entry_lookup[0];
- item0->entry = calloc(1, sizeof(struct thread_entry));
- item0->thread_id = pthread_self();
-
- _current = item0->entry;
- pthread_cond_init(&item0->entry->cond, NULL);
- threads_initialized = 1;
-}
-
-
-unsigned int create_thread(void (*function)(void),
- void* stack, size_t stack_size,
- unsigned flags, const char *name
- //~ IF_PRIO(, int priority)
- IF_COP(, unsigned int core))
-{
- pthread_t retval;
-
- struct thread_init_data *data = calloc(1, sizeof(struct thread_init_data));
- struct thread_entry *entry = calloc(1, sizeof(struct thread_entry));
- struct thread_entry_item *item;
-
- if (!threads_initialized)
- abort();
-
- data->function = function;
- data->start_frozen = flags & CREATE_THREAD_FROZEN;
- data->entry = entry;
- pthread_cond_init(&entry->cond, NULL);
- entry->runnable = true;
-
- sem_init(&data->init_sem, 0, 0);
-
- if (pthread_create(&retval, NULL, trampoline, data) < 0)
- return -1;
-
- sem_wait(&data->init_sem);
-
- item = __find_thread_entry(0);
- item->thread_id = retval;
- item->entry = entry;
-
- pthread_setname_np(retval, name);
-
-
- return retval;
-}
-
-/* for block_thread(), _w_tmp() and wakeup_thread() t->lock must point
- * to a corelock instance, and this corelock must be held by the caller */
-void block_thread_switch(struct thread_entry *t, struct corelock *cl)
-{
- t->runnable = false;
- if (wait_queue_ptr(t))
- wait_queue_register(t);
- while(!t->runnable)
- pthread_cond_wait(&t->cond, &cl->mutex);
-}
-
-void block_thread_switch_w_tmo(struct thread_entry *t, int timeout,
- struct corelock *cl)
-{
- int err = 0;
- struct timespec ts;
-
- clock_gettime(CLOCK_REALTIME, &ts);
- timespec_add_ns(&ts, timeout * (NSEC_PER_SEC/HZ));
-
- t->runnable = false;
- wait_queue_register(t->wqp, t);
- while(!t->runnable && !err)
- err = pthread_cond_timedwait(&t->cond, &cl->mutex, &ts);
-
- if (err == ETIMEDOUT)
- { /* the thread timed out and was not explicitely woken up.
- * we need to do this now to mark it runnable again */
- t->runnable = true;
- /* NOTE: objects do their own removal upon timer expiration */
- }
-}
-
-unsigned int wakeup_thread(struct thread_entry *t)
-{
- if (t->wqp)
- wait_queue_remove(t);
- t->runnable = true;
- pthread_cond_signal(&t->cond);
- return THREAD_OK;
-}
-
-
-void yield(void) {}
-
-unsigned sleep(unsigned ticks)
-{
- struct timespec ts;
-
- ts.tv_sec = ticks/HZ;
- ts.tv_nsec = (ticks % HZ) * (NSEC_PER_SEC/HZ);
-
- nanosleep(&ts, NULL);
-
- return 0;
-}
diff --git a/firmware/kernel/thread.c b/firmware/kernel/thread.c
index a422624df7..25677c79f9 100644
--- a/firmware/kernel/thread.c
+++ b/firmware/kernel/thread.c
@@ -1019,7 +1019,7 @@ void switch_thread(void)
#ifdef RB_PROFILE
profile_thread_stopped(THREAD_ID_SLOT(thread->id));
#endif
-#ifdef DEBUG
+#ifdef BUFLIB_DEBUG_CHECK_VALID
/* Check core_ctx buflib integrity */
core_check_valid();
#endif
diff --git a/firmware/libc/include/string.h b/firmware/libc/include/string.h
index 9815c62805..6217fff15c 100644
--- a/firmware/libc/include/string.h
+++ b/firmware/libc/include/string.h
@@ -20,6 +20,7 @@ extern "C" {
_PTR _EXFUN(memchr,(const _PTR, int, size_t));
int _EXFUN(memcmp,(const _PTR, const _PTR, size_t));
_PTR _EXFUN(memcpy,(_PTR, const _PTR, size_t));
+_PTR _EXFUN(memccpy,(_PTR, const _PTR, int, size_t));
_PTR _EXFUN(mempcpy,(_PTR, const _PTR, size_t));
_PTR _EXFUN(memmove,(_PTR, const _PTR, size_t));
_PTR _EXFUN(memset,(_PTR, int, size_t));
@@ -39,9 +40,6 @@ size_t _EXFUN(strspn,(const char *, const char *));
char *_EXFUN(strstr,(const char *, const char *));
char *_EXFUN(strcasestr,(const char *, const char *));
-size_t strlcpy(char *dst, const char *src, size_t siz);
-size_t strlcat(char *dst, const char *src, size_t siz);
-
#ifndef _REENT_ONLY
char *_EXFUN(strtok,(char *, const char *));
#endif
diff --git a/firmware/libc/memccpy.c b/firmware/libc/memccpy.c
new file mode 100644
index 0000000000..fa9316616e
--- /dev/null
+++ b/firmware/libc/memccpy.c
@@ -0,0 +1,119 @@
+/*
+FUNCTION
+ <<memccpy>>---copy memory regions with end-token check
+ANSI_SYNOPSIS
+ #include <string.h>
+ void* memccpy(void *restrict <[out]>, const void *restrict <[in]>,
+ int <[endchar]>, size_t <[n]>);
+TRAD_SYNOPSIS
+ void *memccpy(<[out]>, <[in]>, <[endchar]>, <[n]>
+ void *<[out]>;
+ void *<[in]>;
+ int <[endchar]>;
+ size_t <[n]>;
+DESCRIPTION
+ This function copies up to <[n]> bytes from the memory region
+ pointed to by <[in]> to the memory region pointed to by
+ <[out]>. If a byte matching the <[endchar]> is encountered,
+ the byte is copied and copying stops.
+ If the regions overlap, the behavior is undefined.
+RETURNS
+ <<memccpy>> returns a pointer to the first byte following the
+ <[endchar]> in the <[out]> region. If no byte matching
+ <[endchar]> was copied, then <<NULL>> is returned.
+PORTABILITY
+<<memccpy>> is a GNU extension.
+<<memccpy>> requires no supporting OS subroutines.
+ */
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+#include "_ansi.h" /* for _DEFUN */
+
+/* Nonzero if either X or Y is not aligned on a "long" boundary. */
+#define ROCKBOX_UNALIGNED(X, Y) \
+ (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
+/* How many bytes are copied each iteration of the word copy loop. */
+#define LITTLEBLOCKSIZE (sizeof (long))
+/* Threshhold for punting to the byte copier. */
+#define TOO_SMALL(LEN) ((LEN) < LITTLEBLOCKSIZE)
+/* Macros for detecting endchar */
+#if LONG_MAX == 2147483647L
+#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
+#else
+#if LONG_MAX == 9223372036854775807L
+/* Nonzero if X (a long int) contains a NULL byte. */
+#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
+#else
+#error long int is not a 32bit or 64bit type.
+#endif
+#endif
+_PTR
+_DEFUN (memccpy, (dst0, src0, endchar, len0),
+ _PTR __restrict dst0 _AND
+ _CONST _PTR __restrict src0 _AND
+ int endchar0 _AND
+ size_t len0)
+{
+#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
+ _PTR ptr = NULL;
+ char *dst = (char *) dst0;
+ char *src = (char *) src0;
+ char endchar = endchar0 & 0xff;
+ while (len0--)
+ {
+ if ((*dst++ = *src++) == endchar)
+ {
+ ptr = dst;
+ break;
+ }
+ }
+ return ptr;
+#else
+ _PTR ptr = NULL;
+ char *dst = dst0;
+ _CONST char *src = src0;
+ long *aligned_dst;
+ _CONST long *aligned_src;
+ char endchar = endchar0 & 0xff;
+ /* If the size is small, or either SRC or DST is unaligned,
+ then punt into the byte copy loop. This should be rare. */
+ if (!TOO_SMALL(len0) && !ROCKBOX_UNALIGNED (src, dst))
+ {
+ unsigned int i;
+ unsigned long mask = 0;
+ aligned_dst = (long*)dst;
+ aligned_src = (long*)src;
+ /* The fast code reads the ASCII one word at a time and only
+ performs the bytewise search on word-sized segments if they
+ contain the search character, which is detected by XORing
+ the word-sized segment with a word-sized block of the search
+ character and then detecting for the presence of NULL in the
+ result. */
+ for (i = 0; i < LITTLEBLOCKSIZE; i++)
+ mask = (mask << 8) + endchar;
+ /* Copy one long word at a time if possible. */
+ while (len0 >= LITTLEBLOCKSIZE)
+ {
+ unsigned long buffer = (unsigned long)(*aligned_src);
+ buffer ^= mask;
+ if (DETECTNULL (buffer))
+ break; /* endchar is found, go byte by byte from here */
+ *aligned_dst++ = *aligned_src++;
+ len0 -= LITTLEBLOCKSIZE;
+ }
+ /* Pick up any residual with a byte copier. */
+ dst = (char*)aligned_dst;
+ src = (char*)aligned_src;
+ }
+ while (len0--)
+ {
+ if ((*dst++ = *src++) == endchar)
+ {
+ ptr = dst;
+ break;
+ }
+ }
+ return ptr;
+#endif /* not PREFER_SIZE_OVER_SPEED */
+}
diff --git a/firmware/libc/strcspn.c b/firmware/libc/strcspn.c
new file mode 100644
index 0000000000..ee50066ed1
--- /dev/null
+++ b/firmware/libc/strcspn.c
@@ -0,0 +1,45 @@
+/*
+FUNCTION
+ <<strcspn>>---count characters not in string
+INDEX
+ strcspn
+ANSI_SYNOPSIS
+ size_t strcspn(const char *<[s1]>, const char *<[s2]>);
+TRAD_SYNOPSIS
+ size_t strcspn(<[s1]>, <[s2]>)
+ char *<[s1]>;
+ char *<[s2]>;
+DESCRIPTION
+ This function computes the length of the initial part of
+ the string pointed to by <[s1]> which consists entirely of
+ characters <[NOT]> from the string pointed to by <[s2]>
+ (excluding the terminating null character).
+RETURNS
+ <<strcspn>> returns the length of the substring found.
+PORTABILITY
+<<strcspn>> is ANSI C.
+<<strcspn>> requires no supporting OS subroutines.
+ */
+#include <string.h>
+#include "_ansi.h" /* for _DEFUN */
+
+size_t
+_DEFUN (strcspn, (s1, s2),
+ _CONST char *s1 _AND
+ _CONST char *s2)
+{
+ _CONST char *s = s1;
+ _CONST char *c;
+ while (*s1)
+ {
+ for (c = s2; *c; c++)
+ {
+ if (*s1 == *c)
+ break;
+ }
+ if (*c)
+ break;
+ s1++;
+ }
+ return s1 - s;
+}
diff --git a/firmware/libc/strtok.c b/firmware/libc/strtok.c
deleted file mode 100644
index 9e2eddf599..0000000000
--- a/firmware/libc/strtok.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Daniel Stenberg
- *
- * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied. *
- ****************************************************************************/
-
-#include "config.h"
-
-#ifndef HAVE_STRTOK_R
-#include <stddef.h>
-#include <string.h>
-
-char *
-strtok_r(char *ptr, const char *sep, char **end)
-{
- if (!ptr)
- /* we got NULL input so then we get our last position instead */
- ptr = *end;
-
- /* pass all letters that are including in the separator string */
- while (*ptr && strchr(sep, *ptr))
- ++ptr;
-
- if (*ptr) {
- /* so this is where the next piece of string starts */
- char *start = ptr;
-
- /* set the end pointer to the first byte after the start */
- *end = start + 1;
-
- /* scan through the string to find where it ends, it ends on a
- null byte or a character that exists in the separator string */
- while (**end && !strchr(sep, **end))
- ++*end;
-
- if (**end) {
- /* the end is not a null byte */
- **end = '\0'; /* zero terminate it! */
- ++*end; /* advance last pointer to beyond the null byte */
- }
-
- return start; /* return the position where the string starts */
- }
-
- /* we ended up on a null byte, there are no more strings to find! */
- return NULL;
-}
-
-#endif /* this was only compiled if strtok_r wasn't present */
diff --git a/firmware/linuxboot.c b/firmware/linuxboot.c
new file mode 100644
index 0000000000..6803bb466b
--- /dev/null
+++ b/firmware/linuxboot.c
@@ -0,0 +1,335 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "linuxboot.h"
+#include "system.h"
+#include "core_alloc.h"
+#include "crc32.h"
+#include "inflate.h"
+#include "file.h"
+#include <string.h>
+
+/* compression support options - can be decided per target if needed,
+ * for now default to enabling everything */
+#define HAVE_UIMAGE_COMP_NONE
+#define HAVE_UIMAGE_COMP_GZIP
+
+enum {
+ E_OUT_OF_MEMORY = -1,
+ E_BUFFER_OVERFLOW = -2,
+ E_MAGIC_MISMATCH = -3,
+ E_HCRC_MISMATCH = -4,
+ E_DCRC_MISMATCH = -5,
+ E_UNSUPPORTED_COMPRESSION = -6,
+ E_READ = -7,
+ E_INFLATE = -8,
+ E_INFLATE_UNCONSUMED = -9,
+};
+
+uint32_t uimage_crc(uint32_t crc, const void* data, size_t size)
+{
+ return letoh32(crc_32r(data, size, htole32(crc ^ 0xffffffff))) ^ 0xffffffff;
+}
+
+uint32_t uimage_calc_hcrc(const struct uimage_header* uh)
+{
+ struct uimage_header h = *uh;
+ uimage_set_hcrc(&h, 0);
+ return uimage_crc(0, &h, sizeof(h));
+}
+
+static int uimage_check_header(const struct uimage_header* uh)
+{
+ if(uimage_get_magic(uh) != IH_MAGIC)
+ return E_MAGIC_MISMATCH;
+
+ if(uimage_get_hcrc(uh) != uimage_calc_hcrc(uh))
+ return E_HCRC_MISMATCH;
+
+ return 0;
+}
+
+static int uimage_alloc_state(const struct uimage_header* uh)
+{
+ size_t size;
+
+ switch(uimage_get_comp(uh)) {
+#ifdef HAVE_UIMAGE_COMP_NONE
+ case IH_COMP_NONE:
+ return 0;
+#endif
+
+#ifdef HAVE_UIMAGE_COMP_GZIP
+ case IH_COMP_GZIP:
+ size = inflate_size + inflate_align - 1;
+ return core_alloc_ex(size, &buflib_ops_locked);
+#endif
+
+ default:
+ return E_UNSUPPORTED_COMPRESSION;
+ }
+}
+
+#ifdef HAVE_UIMAGE_COMP_GZIP
+struct uimage_inflatectx
+{
+ uimage_reader reader;
+ void* rctx;
+ uint32_t dcrc;
+ size_t remain;
+ int err;
+};
+
+static uint32_t uimage_inflate_reader(void* block, uint32_t block_size, void* ctx)
+{
+ struct uimage_inflatectx* c = ctx;
+ ssize_t len = c->reader(block, block_size, c->rctx);
+ if(len < 0) {
+ c->err = E_READ;
+ return 0;
+ }
+
+ len = MIN(c->remain, (size_t)len);
+ c->remain -= len;
+ c->dcrc = uimage_crc(c->dcrc, block, len);
+ return len;
+}
+
+static int uimage_decompress_gzip(const struct uimage_header* uh, int state_h,
+ void* out, size_t* out_size,
+ uimage_reader reader, void* rctx)
+{
+ size_t hbufsz = inflate_size + inflate_align - 1;
+ void* hbuf = core_get_data(state_h);
+ ALIGN_BUFFER(hbuf, hbufsz, inflate_align);
+
+ struct uimage_inflatectx r_ctx;
+ r_ctx.reader = reader;
+ r_ctx.rctx = rctx;
+ r_ctx.dcrc = 0;
+ r_ctx.remain = uimage_get_size(uh);
+ r_ctx.err = 0;
+
+ struct inflate_bufferctx w_ctx;
+ w_ctx.buf = out;
+ w_ctx.end = out + *out_size;
+
+ int ret = inflate(hbuf, INFLATE_GZIP,
+ uimage_inflate_reader, &r_ctx,
+ inflate_buffer_writer, &w_ctx);
+ if(ret) {
+ if(r_ctx.err)
+ return r_ctx.err;
+ else if(w_ctx.end == w_ctx.buf)
+ return E_BUFFER_OVERFLOW;
+ else
+ /* note: this will likely mask DCRC_MISMATCH errors */
+ return E_INFLATE;
+ }
+
+ if(r_ctx.remain > 0)
+ return E_INFLATE_UNCONSUMED;
+ if(r_ctx.dcrc != uimage_get_dcrc(uh))
+ return E_DCRC_MISMATCH;
+
+ *out_size = w_ctx.end - w_ctx.buf;
+ return 0;
+}
+#endif /* HAVE_UIMAGE_COMP_GZIP */
+
+static int uimage_decompress(const struct uimage_header* uh, int state_h,
+ void* out, size_t* out_size,
+ uimage_reader reader, void* rctx)
+{
+ size_t in_size = uimage_get_size(uh);
+ ssize_t len;
+
+ switch(uimage_get_comp(uh)) {
+#ifdef HAVE_UIMAGE_COMP_NONE
+ case IH_COMP_NONE:
+ if(*out_size < in_size)
+ return E_BUFFER_OVERFLOW;
+
+ len = reader(out, in_size, rctx);
+ if(len < 0 || (size_t)len != in_size)
+ return E_READ;
+
+ if(uimage_crc(0, out, in_size) != uimage_get_dcrc(uh))
+ return E_DCRC_MISMATCH;
+
+ *out_size = in_size;
+ break;
+#endif
+
+#ifdef HAVE_UIMAGE_COMP_GZIP
+ case IH_COMP_GZIP:
+ return uimage_decompress_gzip(uh, state_h, out, out_size, reader, rctx);
+#endif
+
+ default:
+ return E_UNSUPPORTED_COMPRESSION;
+ }
+
+ return 0;
+}
+
+int uimage_load(struct uimage_header* uh, size_t* out_size,
+ uimage_reader reader, void* rctx)
+{
+ if(reader(uh, sizeof(*uh), rctx) != (ssize_t)sizeof(*uh))
+ return E_READ;
+
+ int ret = uimage_check_header(uh);
+ if(ret)
+ return ret;
+
+ int state_h = uimage_alloc_state(uh);
+ if(state_h < 0)
+ return E_OUT_OF_MEMORY;
+
+ *out_size = 0;
+ int out_h = core_alloc_maximum(out_size, &buflib_ops_locked);
+ if(out_h <= 0) {
+ ret = E_OUT_OF_MEMORY;
+ goto err;
+ }
+
+ ret = uimage_decompress(uh, state_h, core_get_data(out_h), out_size,
+ reader, rctx);
+ if(ret)
+ goto err;
+
+ core_shrink(out_h, NULL, *out_size);
+ ret = 0;
+
+ err:
+ core_free(state_h);
+ if(out_h > 0) {
+ if(ret == 0)
+ ret = out_h;
+ else
+ core_free(out_h);
+ }
+
+ return ret;
+}
+
+ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx)
+{
+ int fd = (intptr_t)ctx;
+ return read(fd, buf, size);
+}
+
+/* Linux's self-extracting kernels are broken on MIPS. The decompressor stub
+ * doesn't flush caches after extracting the kernel code which can cause the
+ * boot to fail horribly. This has been true since at least 2009 and at the
+ * time of writing (2022) it's *still* broken.
+ *
+ * The FiiO M3K and Shanling Q1 both have broken kernels of this type, so we
+ * work around this by replacing the direct call to the kernel entry point with
+ * a thunk that adds the necessary cache flush.
+ */
+uint32_t mips_linux_stub_get_entry(void** code_start, size_t code_size)
+{
+ /* The jump to the kernel entry point looks like this:
+ *
+ * move a0, s0
+ * move a1, s1
+ * move a2, s2
+ * move a3, s3
+ * ...
+ * la k0, KERNEL_ENTRY
+ * jr k0
+ * --- or in kernels since 2021: ---
+ * la t9, KERNEL_ENTRY
+ * jalr t9
+ *
+ * We're trying to identify this code and decode the kernel entry
+ * point address, and return a suitable address where we can patch
+ * in a call to our thunk.
+ */
+
+ /* We should only need to scan within the first 128 bytes
+ * but do up to 256 just in case. */
+ uint32_t* start = *code_start;
+ uint32_t* end = start + (MIN(code_size, 256) + 3) / 4;
+
+ /* Scan for the "move aN, sN" sequence */
+ uint32_t* move_instr = start;
+ for(move_instr += 4; move_instr < end; ++move_instr) {
+ if(move_instr[-4] == 0x02002021 && /* move a0, s0 */
+ move_instr[-3] == 0x02202821 && /* move a1, s1 */
+ move_instr[-2] == 0x02403021 && /* move a2, s2 */
+ move_instr[-1] == 0x02603821) /* move a3, s3 */
+ break;
+ }
+
+ if(move_instr == end)
+ return 0;
+
+ /* Now search forward for the next jr/jalr instruction */
+ int jreg = 0;
+ uint32_t* jump_instr = move_instr;
+ for(; jump_instr != end; ++jump_instr) {
+ if((jump_instr[0] & 0xfc1ff83f) == 0xf809 ||
+ (jump_instr[0] & 0xfc00003f) == 0x8) {
+ /* jalr rN */
+ jreg = (jump_instr[0] >> 21) & 0x1f;
+ break;
+ }
+ }
+
+ /* Need room here for 4 instructions. Assume everything between the
+ * moves and the jump is safe to overwrite; otherwise, we'll need to
+ * take a different approach.
+ *
+ * Count +1 instruction for the branch delay slot and another +1 because
+ * "move_instr" points to the instruction following the last move. */
+ if(jump_instr - move_instr + 2 < 4)
+ return 0;
+ if(!jreg)
+ return 0;
+
+ /* Now scan from the end of the move sequence until the jump instruction
+ * and try to reconstruct the entry address. We check for lui/ori/addiu. */
+ const uint32_t lui_mask = 0xffff0000;
+ const uint32_t lui = 0x3c000000 | (jreg << 16);
+ const uint32_t ori_mask = 0xffff0000;
+ const uint32_t ori = 0x34000000 | (jreg << 21) | (jreg << 16);
+ const uint32_t addiu_mask = 0xffff0000;
+ const uint32_t addiu = 0x24000000 | (jreg << 21) | (jreg << 16);
+
+ /* Can use any initial value here */
+ uint32_t jreg_val = 0xdeadbeef;
+
+ for(uint32_t* instr = move_instr; instr != jump_instr; ++instr) {
+ if((instr[0] & lui_mask) == lui)
+ jreg_val = (instr[0] & 0xffff) << 16;
+ else if((instr[0] & ori_mask) == ori)
+ jreg_val |= instr[0] & 0xffff;
+ else if((instr[0] & addiu_mask) == addiu)
+ jreg_val += instr[0] & 0xffff;
+ }
+
+ /* Success! Probably! */
+ *code_start = move_instr;
+ return jreg_val;
+}
diff --git a/firmware/panic.c b/firmware/panic.c
index fcfa8b2bb8..d7f330caf1 100644
--- a/firmware/panic.c
+++ b/firmware/panic.c
@@ -32,12 +32,18 @@
#include "system.h"
#include "logf.h"
-#if defined(CPU_ARM)
+#ifdef HAVE_RB_BACKTRACE
#include "gcc_extensions.h"
-#include <backtrace.h>
+#include "backtrace.h"
#endif
+#if (defined(CPU_MIPS) && (CONFIG_PLATFORM & PLATFORM_NATIVE))
+/* TODO: see comment above exception_dump in system-mips.c */
+char panic_buf[128];
+#else
static char panic_buf[128];
+#endif
+
#define LINECHARS (LCD_WIDTH/SYSFONT_WIDTH) - 2
#if defined(CPU_ARM)
@@ -65,6 +71,12 @@ void panicf_f( const char *fmt, ...)
);
int pc = (int)__builtin_return_address(0);
+#elif defined(BACKTRACE_MIPSUNWINDER)
+void panicf( const char *fmt, ... )
+{
+ /* NOTE: these are obtained by the backtrace lib */
+ const int pc = 0;
+ const int sp = 0;
#else
void panicf( const char *fmt, ...)
{
diff --git a/firmware/pcm.c b/firmware/pcm.c
index 6fc0b626f7..de01af484f 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -23,7 +23,7 @@
#include "kernel.h"
/* Define LOGF_ENABLE to enable logf output in this file */
-/*#define LOGF_ENABLE*/
+//#define LOGF_ENABLE
#include "logf.h"
#include "audio.h"
#include "sound.h"
@@ -314,7 +314,7 @@ void pcm_play_stop(void)
* what pcm_apply_settings will set */
void pcm_set_frequency(unsigned int samplerate)
{
- logf("pcm_set_frequency");
+ logf("pcm_set_frequency %u", samplerate);
int index;
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index 13e810e926..95763dc950 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -52,11 +52,7 @@
#include "pcf50606.h"
#endif
-/** Shared by sim **/
static int last_sent_battery_level = 100;
-/* battery level (0-100%) */
-int battery_percent = -1;
-void send_battery_level_event(void);
static void set_sleep_timer(int seconds);
static bool sleeptimer_active = false;
@@ -83,41 +79,6 @@ void handle_auto_poweroff(void);
static int poweroff_timeout = 0;
static long last_event_tick = 0;
-#if CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
-#ifdef SIMULATOR
-int _battery_level(void) { return -1; }
-#endif
-#else
-int _battery_level(void) { return -1; }
-#endif
-
-#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
-/*
- * Average battery voltage and charger voltage, filtered via a digital
- * exponential filter (aka. exponential moving average, scaled):
- * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N.
- */
-static unsigned int avgbat;
-/* filtered battery voltage, millivolts */
-static unsigned int battery_millivolts;
-#else
-#ifndef SIMULATOR
-int _battery_voltage(void) { return -1; }
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11];
-const unsigned short percent_to_volt_charge[11];
-#endif
-#endif
-
-#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
-#ifdef CURRENT_NORMAL
-static int powermgmt_est_runningtime_min;
-int _battery_time(void) { return powermgmt_est_runningtime_min; }
-#else
-int _battery_time(void) { return -1; }
-#endif
-#endif
-
-/* default value, mAh */
#if BATTERY_CAPACITY_INC > 0
static int battery_capacity = BATTERY_CAPACITY_DEFAULT;
#else
@@ -141,82 +102,209 @@ static char power_stack[DEFAULT_STACK_SIZE/2];
#endif
static const char power_thread_name[] = "power";
+/* Time estimation requires 64 bit math so don't use it in the bootloader.
+ * Also we need to be able to measure current, and not have a better time
+ * estimate source available. */
+#define HAVE_TIME_ESTIMATION \
+ (!defined(BOOTLOADER) && !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) && \
+ (defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)))
-static int voltage_to_battery_level(int battery_millivolts);
-static void battery_status_update(void);
+#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
+int _battery_level(void) { return -1; }
+#endif
+static int percent_now; /* Cached to avoid polling too often */
-#if BATTERY_TYPES_COUNT > 1
-void set_battery_type(int type)
-{
- if (type != battery_type) {
- if ((unsigned)type >= BATTERY_TYPES_COUNT)
- type = 0;
+#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
+int _battery_time(void) { return -1; }
+#else
+static int time_now; /* Cached to avoid polling too often */
+#endif
- battery_type = type;
- battery_status_update(); /* recalculate the battery status */
- }
-}
+#if HAVE_TIME_ESTIMATION
+static int time_now; /* reported time in minutes */
+static int64_t time_cnt; /* reported time in seconds */
+static int64_t time_err; /* error... it's complicated */
#endif
-#if BATTERY_CAPACITY_INC > 0
-void set_battery_capacity(int capacity)
-{
- if (capacity > BATTERY_CAPACITY_MAX)
- capacity = BATTERY_CAPACITY_MAX;
- if (capacity < BATTERY_CAPACITY_MIN)
- capacity = BATTERY_CAPACITY_MIN;
+#if !(CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+int _battery_voltage(void) { return -1; }
+#else
+/* Data for the digital exponential filter */
+static int voltage_avg, voltage_now;
+#endif
- battery_capacity = capacity;
+#if !(CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
+int _battery_current(void) { return -1; }
+#else
+static int current_avg, current_now;
+#endif
- battery_status_update(); /* recalculate the battery status */
+/* The battery level can be obtained in two ways. If the target reports
+ * voltage, the battery level can be estminated using percent_to_volt_*
+ * curves. If the target can report the percentage directly, then that
+ * will be used instead of voltage-based estimation. */
+int battery_level(void)
+{
+#ifdef HAVE_BATTERY_SWITCH
+ if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
+ return -1;
+#endif
+
+ return percent_now;
}
+
+/* The time remaining to full charge/discharge can be provided by the
+ * target if it has an accurate way of doing this. Otherwise, if the
+ * target defines a valid battery capacity and can report the charging
+ * and discharging current, the time remaining will be estimated based
+ * on the battery level and the actual current usage. */
+int battery_time(void)
+{
+#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || HAVE_TIME_ESTIMATION
+ return time_now;
+#else
+ return -1;
#endif
+}
-int get_battery_capacity(void)
+/* Battery voltage should always be reported if available, but it is
+ * optional if the the target reports battery percentage directly. */
+int battery_voltage(void)
{
- return battery_capacity;
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ return voltage_now;
+#else
+ return -1;
+#endif
}
-int battery_time(void)
+/* Battery current can be estimated if the target defines CURRENT_NORMAL
+ * as the number of milliamps usually consumed by the device in a normal
+ * state. The target can also define other CURRENT_* values to estimate
+ * the power consumed by the backlight, remote display, SPDIF, etc. */
+int battery_current(void)
{
-#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
- /* Note: This should not really be possible but it might occur
- * as a degenerate case for targets that don't define any battery
- * capacity at all..? */
- if(battery_capacity <= 0)
- return -1;
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ return current_now;
+#elif defined(CURRENT_NORMAL)
+ int current = CURRENT_NORMAL;
+
+#ifndef BOOTLOADER
+#if defined(HAVE_BACKLIGHT) && defined(CURRENT_BACKLIGHT)
+ if (backlight_get_current_timeout() == 0) /* LED always on */
+ current += CURRENT_BACKLIGHT;
+#endif
+
+#if defined(HAVE_RECORDING) && defined(CURRENT_RECORD)
+ if (audio_status() & AUDIO_STATUS_RECORD)
+ current += CURRENT_RECORD;
#endif
- return _battery_time();
+#if defined(HAVE_SPDIF_POWER) && defined(CURRENT_SPDIF_OUT)
+ if (spdif_powered())
+ current += CURRENT_SPDIF_OUT;
+#endif
+
+#if defined(HAVE_REMOTE_LCD) && defined(CURRENT_REMOTE)
+ if (remote_detect())
+ current += CURRENT_REMOTE;
+#endif
+
+#if defined(HAVE_ATA_POWER_OFF) && defined(CURRENT_ATA)
+ if (ide_powered())
+ current += CURRENT_ATA;
+#endif
+
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ /* While charging we must report the charging current. */
+ if (charging_state()) {
+ current = CURRENT_MAX_CHG - current;
+ current = MIN(current, 1);
+ }
+#endif
+
+#endif /* BOOTLOADER */
+
+ return current;
+#else
+ return -1;
+#endif
}
-/* Returns battery level in percent */
-int battery_level(void)
+/* Initialize the battery voltage/current filters. This is called
+ * once by the power thread before entering the main polling loop. */
+static void average_init(void)
{
-#ifdef HAVE_BATTERY_SWITCH
- if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
- return -1;
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ voltage_now = _battery_voltage() + 15;
+
+ /* The battery voltage is usually a little lower directly after
+ turning on, because the disk was used heavily. Raise it by 5% */
+#ifdef HAVE_DISK_STORAGE
+#if CONFIG_CHARGING
+ if(!charger_inserted())
+#endif
+ {
+ voltage_now += (percent_to_volt_discharge[battery_type][6] -
+ percent_to_volt_discharge[battery_type][5]) / 2;
+ }
+#endif /* HAVE_DISK_STORAGE */
+
+ voltage_avg = voltage_now * BATT_AVE_SAMPLES;
+#endif /* CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE */
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ current_now = _battery_current();
+ current_avg = current_now * BATT_CURRENT_AVE_SAMPLES;
#endif
- return battery_percent;
}
-/* Tells if the battery level is safe for disk writes */
-bool battery_level_safe(void)
+/* Sample the battery voltage/current and update the filters.
+ * Updated once every POWER_THREAD_STEP_TICKS. */
+static void average_step(bool low_battery)
{
-#if defined(NO_LOW_BATTERY_SHUTDOWN)
- return true;
-#elif CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
- return (battery_percent > 0);
-#elif defined(HAVE_BATTERY_SWITCH)
- /* Cannot rely upon the battery reading to be valid and the
- * device could be powered externally. */
- return input_millivolts() > battery_level_dangerous[battery_type];
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ int millivolts = _battery_voltage();
+ if(low_battery) {
+ voltage_now = (millivolts + voltage_now + 1) / 2;
+ voltage_avg += voltage_now - voltage_avg / BATT_AVE_SAMPLES;
+ } else {
+ voltage_avg += millivolts - voltage_avg / BATT_AVE_SAMPLES;
+ voltage_now = voltage_avg / BATT_AVE_SAMPLES;
+ }
#else
- return battery_millivolts > battery_level_dangerous[battery_type];
+ (void)low_battery;
+#endif
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ current_avg += _battery_current() - current_avg / BATT_CURRENT_AVE_SAMPLES;
+ current_now = current_avg / BATT_CURRENT_AVE_SAMPLES;
#endif
}
-/* look into the percent_to_volt_* table and get a realistic battery level */
+/* Send system battery level update events on reaching certain significant
+ * levels. This is called by battery_status_update() and does not have to
+ * be called separately. */
+static void send_battery_level_event(int percent)
+{
+ static const int levels[] = { 5, 15, 30, 50, 0 };
+ const int *level = levels;
+
+ while (*level)
+ {
+ if (percent <= *level && last_sent_battery_level > *level) {
+ last_sent_battery_level = *level;
+ queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
+ break;
+ }
+
+ level++;
+ }
+}
+
+#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) && \
+ (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+/* Look into the percent_to_volt_* table and estimate the battery level. */
static int voltage_to_percent(int voltage, const short* table)
{
if (voltage <= table[0]) {
@@ -238,20 +326,19 @@ static int voltage_to_percent(int voltage, const short* table)
}
}
-/* update battery level and estimated runtime, called once per minute or
- * when battery capacity / type settings are changed */
-static int voltage_to_battery_level(int battery_millivolts)
+/* Convert voltage to a battery level percentage using the appropriate
+ * percent_to_volt_* lookup table. */
+static int voltage_to_battery_level(int millivolts)
{
int level;
- if (battery_millivolts < 0)
+ if (millivolts < 0)
return -1;
#if CONFIG_CHARGING >= CHARGING_MONITOR
if (charging_state()) {
/* battery level is defined to be < 100% until charging is finished */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_charge);
+ level = voltage_to_percent(millivolts, percent_to_volt_charge);
if (level > 99)
level = 99;
}
@@ -259,105 +346,147 @@ static int voltage_to_battery_level(int battery_millivolts)
#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */
{
/* DISCHARGING or error state */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_discharge[battery_type]);
+ level = voltage_to_percent(millivolts, percent_to_volt_discharge[battery_type]);
}
return level;
}
+#endif
+/* Update battery percentage and time remaining information.
+ *
+ * This will be called by the power thread after polling new battery data.
+ * It must also be called if the battery type or capacity changes.
+ */
static void battery_status_update(void)
{
- int millivolt = battery_voltage();
+#if CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
int level = _battery_level();
+#elif CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ int level = voltage_to_battery_level(voltage_now);
+#else
+ /* This should be a compile time error? */
+ int level = -1;
+#endif
- if (level < 0)
- level = voltage_to_battery_level(millivolt);
+#if CONFIG_BATTERY_MEASURE & TIME_MEASURE
+ time_now = _battery_time();
+#elif HAVE_TIME_ESTIMATION
+ /* TODO: This is essentially a bad version of coloumb counting,
+ * so in theory using coloumb counters when they are available
+ * should provide a more accurate result. Also note that this
+ * is hard-coded with a HZ/2 update rate to simplify arithmetic. */
-#ifdef CURRENT_NORMAL
int current = battery_current();
+ int resolution = battery_capacity * 36;
+ int time_est = 0;
+ if(level >= 0 && current > 0) {
#if CONFIG_CHARGING >= CHARGING_MONITOR
- if (charging_state()) {
- /* charging: remaining charging time */
- if(current > 0) {
- powermgmt_est_runningtime_min =
- (100 - level) * battery_capacity * 60 / 100 / current;
- }
- }
- else
+ if (charging_state())
+ time_est = (100 - level) * battery_capacity * 36 / current;
+ else
#endif
+ time_est = level * battery_capacity * 36 / current;
+
+ /* The first term nudges the counter toward the estimate. */
+ time_err += current * (time_est - time_cnt);
+ }
+
+ /* The second term decrements the counter due to elapsed time. */
+ time_err -= resolution;
+
+ /* Arbitrary cutoff to ensure we don't get too far out
+ * of sync. Seems to work well on synthetic tests. */
+ if(time_err > resolution * 12 ||
+ time_err < -resolution * 13) {
+ time_cnt = time_est;
+ time_err = 0;
+ }
- /* discharging: remaining running time */
- if (level >= 0 && current > 0) {
- powermgmt_est_runningtime_min =
- (level + battery_percent) * battery_capacity * 60 / 200 / current;
+ if(resolution > 0) {
+ /* Convert the error into a time and adjust the counter. */
+ int64_t adjustment = time_err / (2 * resolution);
+ time_cnt += adjustment;
+ time_err -= adjustment * (2 * resolution);
}
- if (powermgmt_est_runningtime_min < 0 || current <= 0)
- powermgmt_est_runningtime_min = 0;
+ /* Update the reported time based on the counter. */
+ time_now = (time_cnt + 30) / 60;
+ if(time_now < 0)
+ time_now = 0;
#endif
- battery_percent = level;
- send_battery_level_event();
+ percent_now = level;
+ send_battery_level_event(level);
}
-#ifdef CURRENT_NORMAL
-int battery_current(void)
+void battery_read_info(int *voltage, int *level)
{
- int current = CURRENT_NORMAL;
+ int millivolts = _battery_voltage();
-#ifndef BOOTLOADER
- if (usb_inserted()
-#ifdef HAVE_USB_POWER
- #if (CURRENT_USB < CURRENT_NORMAL)
- || usb_powered_only()
- #else
- && !usb_powered_only()
- #endif
-#endif
- ) {
- current = CURRENT_USB;
- }
-
-#if defined(HAVE_BACKLIGHT) && defined(CURRENT_BACKLIGHT)
- if (backlight_get_current_timeout() == 0) /* LED always on */
- current += CURRENT_BACKLIGHT;
-#endif
+ if (voltage)
+ *voltage = millivolts;
-#if defined(HAVE_RECORDING) && defined(CURRENT_RECORD)
- if (audio_status() & AUDIO_STATUS_RECORD)
- current += CURRENT_RECORD;
+ if (level) {
+#if (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
+ *level = _battery_level();
+#elif (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+ *level = voltage_to_battery_level(millivolts);
+#else
+ *level = -1;
#endif
+ }
+}
-#if defined(HAVE_SPDIF_POWER) && defined(CURRENT_SPDIF_OUT)
- if (spdif_powered())
- current += CURRENT_SPDIF_OUT;
-#endif
+#if BATTERY_TYPES_COUNT > 1
+void set_battery_type(int type)
+{
+ if(type < 0 || type > BATTERY_TYPES_COUNT)
+ type = 0;
-#if defined(HAVE_REMOTE_LCD) && defined(CURRENT_REMOTE)
- if (remote_detect())
- current += CURRENT_REMOTE;
+ if (type != battery_type) {
+ battery_type = type;
+ battery_status_update(); /* recalculate the battery status */
+ }
+}
#endif
-#if defined(HAVE_ATA_POWER_OFF) && defined(CURRENT_ATA)
- if (ide_powered())
- current += CURRENT_ATA;
-#endif
+#if BATTERY_CAPACITY_INC > 0
+void set_battery_capacity(int capacity)
+{
+ if (capacity > BATTERY_CAPACITY_MAX)
+ capacity = BATTERY_CAPACITY_MAX;
+ if (capacity < BATTERY_CAPACITY_MIN)
+ capacity = BATTERY_CAPACITY_MIN;
-#if CONFIG_CHARGING >= CHARGING_MONITOR
- /* While charging we must report the charging current. */
- if (charging_state()) {
- current = CURRENT_MAX_CHG - current;
- current = MIN(current, 1);
+ if (capacity != battery_capacity) {
+ battery_capacity = capacity;
+ battery_status_update(); /* recalculate the battery status */
}
+}
#endif
-#endif /* BOOTLOADER */
+int get_battery_capacity(void)
+{
+ return battery_capacity;
+}
- return current;
+/* Tells if the battery level is safe for disk writes */
+bool battery_level_safe(void)
+{
+#if defined(NO_LOW_BATTERY_SHUTDOWN)
+ return true;
+#elif CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
+ return percent_now > 0;
+#elif defined(HAVE_BATTERY_SWITCH)
+ /* Cannot rely upon the battery reading to be valid and the
+ * device could be powered externally. */
+ return input_millivolts() > battery_level_dangerous[battery_type];
+#else
+ return voltage_now > battery_level_dangerous[battery_type];
+#endif
}
-#endif /* CURRENT_NORMAL */
/* Check to see whether or not we've received an alarm in the last second */
#ifdef HAVE_RTC_ALARM
@@ -371,16 +500,22 @@ static void power_thread_rtc_process(void)
/* switch off unit if battery level is too low for reliable operation */
bool query_force_shutdown(void)
{
+#if CONFIG_CHARGING
+ /* It doesn't make sense to force shutdown when externally powered. */
+ if (power_input_present())
+ return false;
+#endif
+
#if defined(NO_LOW_BATTERY_SHUTDOWN)
return false;
#elif CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
- return battery_percent == 0;
+ return percent_now == 0;
#elif defined(HAVE_BATTERY_SWITCH)
/* Cannot rely upon the battery reading to be valid and the
* device could be powered externally. */
return input_millivolts() < battery_level_shutoff[battery_type];
#else
- return battery_millivolts < battery_level_shutoff[battery_type];
+ return voltage_now < battery_level_shutoff[battery_type];
#endif
}
@@ -391,8 +526,13 @@ bool query_force_shutdown(void)
*/
void reset_battery_filter(int millivolts)
{
- avgbat = millivolts * BATT_AVE_SAMPLES;
- battery_millivolts = millivolts;
+ voltage_avg = millivolts * BATT_AVE_SAMPLES;
+ voltage_now = millivolts;
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ /* current would probably be inaccurate too */
+ current_now = _battery_current();
+ current_avg = current_now * BATT_CURRENT_AVE_SAMPLES;
+#endif
battery_status_update();
}
#endif /* HAVE_BATTERY_SWITCH */
@@ -527,90 +667,15 @@ static inline bool detect_charger(unsigned int pwr)
}
#endif /* CONFIG_CHARGING */
-
#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
-/* Returns filtered battery voltage [millivolts] */
-int battery_voltage(void)
-{
- return battery_millivolts;
-}
-
-static void average_init(void)
-{
- /* initialize the voltages for the exponential filter */
- avgbat = _battery_voltage() + 15;
-
-#ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
- /* The battery voltage is usually a little lower directly after
- turning on, because the disk was used heavily. Raise it by 5% */
-#if CONFIG_CHARGING
- if (!charger_inserted()) /* only if charger not connected */
-#endif
- {
- avgbat += (percent_to_volt_discharge[battery_type][6] -
- percent_to_volt_discharge[battery_type][5]) / 2;
- }
-#endif /* HAVE_DISK_STORAGE */
-
- avgbat = avgbat * BATT_AVE_SAMPLES;
- battery_millivolts = power_history[0] = avgbat / BATT_AVE_SAMPLES;
-}
-
-static void average_step(void)
-{
- avgbat += _battery_voltage() - avgbat / BATT_AVE_SAMPLES;
- /*
- * battery_millivolts is the millivolt-scaled filtered battery value.
- */
- battery_millivolts = avgbat / BATT_AVE_SAMPLES;
-}
-
-static void average_step_low(void)
-{
- battery_millivolts = (_battery_voltage() + battery_millivolts + 1) / 2;
- avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES;
-}
-
-static void init_battery_percent(void)
-{
-#if CONFIG_CHARGING
- if (charger_inserted()) {
- battery_percent = voltage_to_percent(battery_millivolts,
- percent_to_volt_charge);
- }
- else
-#endif
- {
- battery_percent = voltage_to_percent(battery_millivolts,
- percent_to_volt_discharge[battery_type]);
- battery_percent += battery_percent < 100;
- }
-
-}
-
static int power_hist_item(void)
{
- return battery_millivolts;
+ return voltage_now;
}
-#define power_history_unit() battery_millivolts
-
#else
-int battery_voltage(void)
-{
- return -1;
-}
-
-static void average_init(void) {}
-static void average_step(void) {}
-static void average_step_low(void) {}
-static void init_battery_percent(void)
-{
- battery_percent = _battery_level();
-}
-
static int power_hist_item(void)
{
- return battery_percent;
+ return percent_now;
}
#endif
@@ -651,13 +716,11 @@ static inline void power_thread_step(void)
|| charger_input_state == CHARGER
#endif
) {
- average_step();
- /* update battery status every time an update is available */
+ average_step(false);
battery_status_update();
}
- else if (battery_percent < 8) {
- average_step_low();
- /* update battery status every time an update is available */
+ else if (percent_now < 8) {
+ average_step(true);
battery_status_update();
/*
@@ -703,7 +766,7 @@ static void power_thread(void)
/* initialize voltage averaging (if available) */
average_init();
/* get initial battery level value (in %) */
- init_battery_percent();
+ battery_status_update();
/* get some initial data for the power curve */
collect_power_history();
@@ -754,7 +817,7 @@ void powermgmt_init(void)
}
/* Various hardware housekeeping tasks relating to shutting down the player */
-void shutdown_hw(void)
+void shutdown_hw(enum shutdown_type sd_type)
{
charging_algorithm_close();
audio_stop();
@@ -794,7 +857,17 @@ void shutdown_hw(void)
eeprom chips are quite slow and might be still writing the last
byte. */
sleep(HZ/4);
- power_off();
+
+ switch (sd_type) {
+ case SHUTDOWN_POWER_OFF:
+ default:
+ power_off();
+ break;
+
+ case SHUTDOWN_REBOOT:
+ system_reboot();
+ break;
+ }
}
void set_poweroff_timeout(int timeout)
@@ -809,10 +882,9 @@ void reset_poweroff_timer(void)
set_sleep_timer(sleeptimer_duration);
}
-void sys_poweroff(void)
-{
#ifndef BOOTLOADER
- logf("sys_poweroff()");
+static void sys_shutdown_common(void)
+{
/* If the main thread fails to shut down the system, we will force a
power off after an 20 second timeout - 28 seconds if recording */
if (shutdown_timeout == 0) {
@@ -830,9 +902,26 @@ void sys_poweroff(void)
shutdown_timeout += HZ*20;
#endif
}
+}
+#endif /* BOOTLOADER */
+void sys_poweroff(void)
+{
+#ifndef BOOTLOADER
+ logf("sys_poweroff()");
+ sys_shutdown_common();
queue_broadcast(SYS_POWEROFF, 0);
-#endif /* BOOTLOADER */
+#endif
+}
+
+/* not to be confused with system_reboot... :( */
+void sys_reboot(void)
+{
+#ifndef BOOTLOADER
+ logf("sys_reboot()");
+ sys_shutdown_common();
+ queue_broadcast(SYS_REBOOT, 0);
+#endif
}
void cancel_shutdown(void)
@@ -848,25 +937,6 @@ void cancel_shutdown(void)
shutdown_timeout = 0;
}
-/* Send system battery level update events on reaching certain significant
- levels. This must be called after battery_percent has been updated. */
-void send_battery_level_event(void)
-{
- static const int levels[] = { 5, 15, 30, 50, 0 };
- const int *level = levels;
-
- while (*level)
- {
- if (battery_percent <= *level && last_sent_battery_level > *level) {
- last_sent_battery_level = *level;
- queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
- break;
- }
-
- level++;
- }
-}
-
void set_sleeptimer_duration(int minutes)
{
set_sleep_timer(minutes * 60);
@@ -885,6 +955,11 @@ static void set_sleep_timer(int seconds)
sleeptimer_duration = seconds;
}
+bool get_sleep_timer_active(void)
+{
+ return sleeptimer_active;
+}
+
int get_sleep_timer(void)
{
if (sleeptimer_active && (sleeptimer_endtick >= current_tick))
diff --git a/firmware/rolo.c b/firmware/rolo.c
index 5f936c95f4..a3e6d5c2b9 100644
--- a/firmware/rolo.c
+++ b/firmware/rolo.c
@@ -41,23 +41,19 @@
#include "loader_strerror.h"
#if defined(MI4_FORMAT)
#include "mi4-loader.h"
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
-#include "bootdata.h"
-#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*mi4-loader.c*/
-#endif
#define LOAD_FIRMWARE(a,b,c) load_mi4(a,b,c)
#elif defined(RKW_FORMAT)
#include "rkw-loader.h"
#define LOAD_FIRMWARE(a,b,c) load_rkw(a,b,c)
#else
#include "rb-loader.h"
+#define LOAD_FIRMWARE(a,b,c) load_firmware(a,b,c)
+#endif
+
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
+#include "multiboot.h"
#include "bootdata.h"
#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*rb-loader.c*/
-#endif
-#define LOAD_FIRMWARE(a,b,c) load_firmware(a,b,c)
#endif
#if CONFIG_CPU == AS3525v2
@@ -133,9 +129,10 @@ static void rolo_error(const char *text)
lcd_scroll_stop();
}
-#if CONFIG_CPU == IMX31L || CONFIG_CPU == RK27XX
+#if CONFIG_CPU == IMX31L || CONFIG_CPU == RK27XX || CONFIG_CPU == X1000
/* this is in firmware/target/arm/imx31/rolo_restart.c for IMX31 */
/* this is in firmware/target/arm/rk27xx/rolo_restart.c for rk27xx */
+/* this is in firmware/target/mips/ingenic_x1000/boot-x1000.c for X1000 */
extern void rolo_restart(const unsigned char* source, unsigned char* dest,
int length);
#else
@@ -242,7 +239,7 @@ int rolo_load(const char* filename)
/* get the system buffer. release only in case of error, otherwise
* we don't return anyway */
- rolo_handle = core_alloc_maximum("rolo", &filebuf_size, NULL);
+ rolo_handle = core_alloc_maximum(&filebuf_size, &buflib_ops_locked);
if (rolo_handle < 0)
{
rolo_error("OOM");
diff --git a/firmware/scroll_engine.c b/firmware/scroll_engine.c
index 91f9d1f868..c32f4632e2 100644
--- a/firmware/scroll_engine.c
+++ b/firmware/scroll_engine.c
@@ -37,28 +37,15 @@
#endif
#include "scroll_engine.h"
-
-/* private helper function for the scroll engine. Do not use in apps/.
- * defined in lcd-bitmap-common.c */
-extern struct viewport *lcd_get_viewport(bool *is_defaut);
-#ifdef HAVE_REMOTE_LCD
-extern struct viewport *lcd_remote_get_viewport(bool *is_defaut);
-#endif
-
static const char scroll_tick_table[18] = {
/* Hz values [f(x)=100.8/(x+.048)]:
1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33, 49.2, 96.2 */
100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1
};
-static void scroll_thread(void);
-static const char scroll_name[] = "scroll";
-
#include "drivers/lcd-scroll.c"
#ifdef HAVE_REMOTE_LCD
-static struct event_queue scroll_queue SHAREDBSS_ATTR;
-
/* copied from lcd-remote-1bit.c */
/* Compile 1 bit vertical packing LCD driver for remote LCD */
#undef LCDFN
@@ -67,6 +54,10 @@ static struct event_queue scroll_queue SHAREDBSS_ATTR;
#define LCDM(ma) LCD_REMOTE_ ## ma
#include "drivers/lcd-scroll.c"
+#endif /* HAVE_REMOTE_LCD */
+
+#if defined(HAVE_REMOTE_LCD) && !defined(BOOTLOADER)
+static struct event_queue scroll_queue SHAREDBSS_ATTR;
static void sync_display_ticks(void)
{
@@ -109,11 +100,14 @@ static bool scroll_process_message(int delay)
return false;
}
-#endif /* HAVE_REMOTE_LCD */
+#endif /* HAVE_REMOTE_LCD && !BOOTLOADER */
+#if !defined(BOOTLOADER)
+static void scroll_thread(void);
+static const char scroll_name[] = "scroll";
static void scroll_thread(void) NORETURN_ATTR;
-#ifdef HAVE_REMOTE_LCD
+#ifdef HAVE_REMOTE_LCD
static void scroll_thread(void)
{
enum
@@ -178,24 +172,20 @@ static void scroll_thread(void)
}
}
}
-#else
+#else /* !HAVE_REMOTE_LCD */
static void scroll_thread(void)
{
while (1)
{
sleep(lcd_scroll_info.ticks);
-#if !defined(BOOTLOADER)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
if (lcd_active())
#endif
lcd_scroll_worker();
-#endif /* !BOOTLOADER */
}
}
-#endif /* HAVE_REMOTE_LCD */
-
+#endif /* !HAVE_REMOTE_LCD */
-#ifndef BOOTLOADER
void scroll_init(void)
{
static char scroll_stack[DEFAULT_STACK_SIZE*3];
@@ -207,9 +197,9 @@ void scroll_init(void)
IF_PRIO(, PRIORITY_USER_INTERFACE)
IF_COP(, CPU));
}
-#else
+#else /* BOOTLOADER */
void scroll_init(void)
{
/* DUMMY */
}
-#endif /* ndef BOOTLOADER*/
+#endif /* BOOTLOADER*/
diff --git a/firmware/target/arm/as3525/lcd-ssd1303.c b/firmware/target/arm/as3525/lcd-ssd1303.c
index 2aa0b844c5..186fdcacbe 100644
--- a/firmware/target/arm/as3525/lcd-ssd1303.c
+++ b/firmware/target/arm/as3525/lcd-ssd1303.c
@@ -310,6 +310,7 @@ static void internal_update_rect(int x, int y, int width, int height)
const int column_high = get_column_high_byte(x);
const int column_low = get_column_low_byte(x);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= height; y++)
{
@@ -320,7 +321,7 @@ static void internal_update_rect(int x, int y, int width, int height)
(column_low)
);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
lcd_write_command (LCD_NOP); /* return to command mode */
diff --git a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
index 03ed1de5d5..48594a2ac9 100644
--- a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
+++ b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
@@ -440,9 +440,10 @@ void lcd_update_rect(int x, int y, int width, int height)
/* setup GRAM write window */
lcd_setup_rect(x, x_end - 1, y, y_end - 1);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* write to GRAM */
for (row = y; row < y_end; row++) {
- lcd_write_data(FBADDR(x,row), width);
+ lcd_write_data(fbaddr(x,row), width);
}
}
diff --git a/firmware/target/arm/ata-as-arm.S b/firmware/target/arm/ata-as-arm.S
index 101bc4dcc1..16c2928bf1 100644
--- a/firmware/target/arm/ata-as-arm.S
+++ b/firmware/target/arm/ata-as-arm.S
@@ -139,9 +139,9 @@ copy_read_sectors:
.r_end2_u:
tst r1, #1 /* one halfword left? */
- ldrneh r4, [r2]
+ ldrhne r4, [r2]
orrne r3, r3, r4, lsl #8
- strneh r3, [r0], #2
+ strhne r3, [r0], #2
movne r3, r4, lsr #8
strb r3, [r0], #1 /* store final byte */
@@ -151,8 +151,8 @@ copy_read_sectors:
/* 16-bit aligned */
.r_aligned:
tst r0, #2 /* 32 bit aligned? */
- ldrneh r3, [r2] /* no: read first halfword */
- strneh r3, [r0], #2 /* store */
+ ldrhne r3, [r2] /* no: read first halfword */
+ strhne r3, [r0], #2 /* store */
subne r1, r1, #1 /* one halfword taken */
sub r1, r1, #8 /* adjust for zero-check and doing 8 halfwords/loop */
@@ -186,14 +186,14 @@ copy_read_sectors:
.r_end4_a:
tst r1, #2 /* 2 or more halfwords left? */
- ldrneh r3, [r2]
- ldrneh r4, [r2]
+ ldrhne r3, [r2]
+ ldrhne r4, [r2]
orrne r3, r3, r4, lsl #16
strne r3, [r0], #4
tst r1, #1 /* one halfword left? */
- ldrneh r3, [r2]
- strneh r3, [r0], #2
+ ldrhne r3, [r2]
+ strhne r3, [r0], #2
ldmpc regs=r4-r5
@@ -291,9 +291,9 @@ copy_write_sectors:
.w_end2_u:
tst r1, #1 /* one halfword left? */
- ldrneh r4, [r0], #2
+ ldrhne r4, [r0], #2
orrne r3, r3, r4, lsl #8
- strneh r3, [r2]
+ strhne r3, [r2]
movne r3, r3, lsr #16
ldrb r4, [r0], #1 /* load final byte */
@@ -305,8 +305,8 @@ copy_write_sectors:
/* 16-bit aligned */
.w_aligned:
tst r0, #2 /* 32 bit aligned? */
- ldrneh r3, [r0], #2 /* no: load first halfword */
- strneh r3, [r2] /* write */
+ ldrhne r3, [r0], #2 /* no: load first halfword */
+ strhne r3, [r2] /* write */
subne r1, r1, #1 /* one halfword taken */
sub r1, r1, #8 /* adjust for zero-check and doing 8 halfwords/loop */
@@ -341,13 +341,13 @@ copy_write_sectors:
tst r1, #2 /* 2 or more halfwords left? */
ldrne r3, [r0], #4
- strneh r3, [r2]
+ strhne r3, [r2]
movne r3, r3, lsr #16
- strneh r3, [r2]
+ strhne r3, [r2]
tst r1, #1 /* one halfword left? */
- ldrneh r3, [r0], #2
- strneh r3, [r2]
+ ldrhne r3, [r0], #2
+ strhne r3, [r2]
ldmpc regs=r4-r5
diff --git a/firmware/target/arm/ata-nand-telechips.c b/firmware/target/arm/ata-nand-telechips.c
index 55f6b1f3f7..d61c223219 100644
--- a/firmware/target/arm/ata-nand-telechips.c
+++ b/firmware/target/arm/ata-nand-telechips.c
@@ -915,7 +915,7 @@ int nand_init(void)
#ifndef BOOTLOADER
/* Use chip info to allocate the correct size LPT buffer */
lptbuf_size = sizeof(struct lpt_entry) * segments_per_bank * total_banks;
- lpt_handle = core_alloc("lpt lookup", lptbuf_size);
+ lpt_handle = core_alloc(lptbuf_size);
struct lpt_entry* lpt_lookup = core_get_data(lpt_handle);
#else
/* Use a static array in the bootloader */
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c b/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
index c1bc379a49..ab4466300b 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
@@ -149,6 +149,8 @@ void lcd_update_rect(int x, int y, int w, int h)
lcd_write_reg(0x17, y | (y + h - 1) << 8);
lcd_write_reg(0x21, y * LCD_WIDTH + x);
lcd_write_reg(0x22, 0);
+
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int yy = y; yy < y + h; yy++)
- imx233_lcdif_pio_send(true, 2 * w, FBADDR(x, yy));
+ imx233_lcdif_pio_send(true, 2 * w, fbaddr(x,yy));
}
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenv.c b/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
index 06b0f158f3..75d2775814 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
@@ -172,8 +172,9 @@ void lcd_update_rect(int x, int y, int w, int h)
lcd_send(false, 0x75); lcd_send(true, y); lcd_send(true, y + h - 1);
lcd_send(false, 0x5c);
imx233_lcdif_set_word_length(16);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int yy = y; yy < y + h; yy++)
- imx233_lcdif_pio_send(true, w, FBADDR(x, yy));
+ imx233_lcdif_pio_send(true, w, fbaddr(x,yy));
}
#ifndef BOOTLOADER
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c b/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
index ce0bcc3885..825b0072a3 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
@@ -282,8 +282,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
index 890ff0b586..d0084900e7 100644
--- a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
+++ b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
@@ -241,8 +241,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
index 59496f2d24..d5f25a523c 100644
--- a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
+++ b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
@@ -188,8 +188,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/debug-imx233.c b/firmware/target/arm/imx233/debug-imx233.c
index bfc38b20dc..4487952162 100644
--- a/firmware/target/arm/imx233/debug-imx233.c
+++ b/firmware/target/arm/imx233/debug-imx233.c
@@ -940,8 +940,8 @@ bool dbg_hw_info_emi(void)
bool dbg_hw_info_audio(void)
{
- static const char *hp_sel[2] = {"DAC", "Line1"};
- static const char *mux_sel[4] = {"Mic", "Line1", "HP", "Line2"};
+ static const char * const hp_sel[2] = {"DAC", "Line1"};
+ static const char * const mux_sel[4] = {"Mic", "Line1", "HP", "Line2"};
lcd_setfont(FONT_SYSFIXED);
while(1)
@@ -1171,7 +1171,7 @@ bool dbg_hw_info_button(void)
}
else if(MAP[i].periph == IMX233_BUTTON_LRADC)
{
- static const char *op_name[] =
+ static const char * const op_name[] =
{
[IMX233_BUTTON_EQ] = "eq",
[IMX233_BUTTON_GT] = "gt",
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
index 92864c9ed7..ceb7b4e090 100644
--- a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
@@ -637,8 +637,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c b/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
index cfcf85bfc0..8f49bfa3eb 100644
--- a/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
+++ b/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
@@ -228,8 +228,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c b/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
index 999f4ee525..862522da15 100644
--- a/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
+++ b/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
@@ -189,8 +189,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
index 5b0c71110d..c84f1cf41c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
@@ -125,53 +125,30 @@ bool si4700_st(void)
return (GPIO1_DR & (1 << 28)) >> 28;
}
-
/* Low-level RDS Support */
-static bool int_restore;
-
-/* Called after I2C read cycle completes */
-static void si4700_rds_read_raw_async_callback(struct i2c_transfer_desc *xfer)
-{
- if (xfer->rxcount == 0)
- si4700_rds_process();
- /* else read didn't finish */
-
- if (int_restore)
- gpio_int_enable(SI4700_EVENT_ID);
-}
-
-/* Called to read registers from ISR context */
-void si4700_rds_read_raw_async(unsigned char *buf, int count)
-{
- /* transfer descriptor for RDS async operations */
- static struct i2c_transfer_desc xfer = { .node = &si4700_i2c_node };
-
- xfer.txdata = NULL;
- xfer.txcount = 0;
- xfer.rxdata = buf;
- xfer.rxcount = count;
- xfer.callback = si4700_rds_read_raw_async_callback;
- xfer.next = NULL;
-
- i2c_transfer(&xfer);
-}
+static struct semaphore rds_sema;
+static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
/* RDS GPIO interrupt handler - start RDS data read */
void INT_SI4700_RDS(void)
{
- /* mask and clear the interrupt until we're done */
- gpio_int_disable(SI4700_EVENT_ID);
gpio_int_clear(SI4700_EVENT_ID);
+ semaphore_release(&rds_sema);
+}
- /* tell radio driver about it */
- si4700_rds_interrupt();
+/* Captures RDS data and processes it */
+static void NORETURN_ATTR rds_thread(void)
+{
+ while (true) {
+ semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
+ si4700_rds_process();
+ }
}
/* Called with on=true after full radio power up, and with on=false before
powering down */
void si4700_rds_powerup(bool on)
{
- int_restore = on;
gpio_int_disable(SI4700_EVENT_ID);
gpio_int_clear(SI4700_EVENT_ID);
gpio_enable_event(SI4700_EVENT_ID, on);
@@ -180,5 +157,7 @@ void si4700_rds_powerup(bool on)
/* One-time RDS init at startup */
void si4700_rds_init(void)
{
- /* nothing to do */
+ semaphore_init(&rds_sema, 1, 0);
+ create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
+ IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
}
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
index d7ebeea024..22873bfef0 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
@@ -199,7 +199,7 @@ void system_exception_wait(void)
system_halt();
}
-void INIT_ATTR system_init(void)
+void system_init(void)
{
static const enum IMX31_CG_LIST disable_clocks[] INITDATA_ATTR =
{
diff --git a/firmware/target/arm/imx31/i2c-imx31.c b/firmware/target/arm/imx31/i2c-imx31.c
index 972c7d465b..ce5e4cc8f8 100644
--- a/firmware/target/arm/imx31/i2c-imx31.c
+++ b/firmware/target/arm/imx31/i2c-imx31.c
@@ -367,7 +367,7 @@ int i2c_write(struct i2c_node *node, const unsigned char *data,
return -1;
}
-void INIT_ATTR i2c_init(void)
+void i2c_init(void)
{
/* Do one-time inits for each module that will be used - leave
* module disabled and unclocked until something wants it. */
diff --git a/firmware/target/arm/imx31/i2c-imx31.h b/firmware/target/arm/imx31/i2c-imx31.h
index b7305931d1..dad5a3da00 100644
--- a/firmware/target/arm/imx31/i2c-imx31.h
+++ b/firmware/target/arm/imx31/i2c-imx31.h
@@ -76,7 +76,7 @@ struct i2c_sync_transfer_desc
};
/* One-time init of i2c driver */
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
/* Enable or disable the node - modules will be switched on/off accordingly. */
void i2c_enable_node(struct i2c_node *node, bool enable);
diff --git a/firmware/target/arm/ipod/button-target.h b/firmware/target/arm/ipod/button-target.h
index 82f600d302..75bc191608 100644
--- a/firmware/target/arm/ipod/button-target.h
+++ b/firmware/target/arm/ipod/button-target.h
@@ -47,15 +47,20 @@ void ipod_4g_button_int(void);
/* Remote control's buttons */
#ifdef IPOD_ACCESSORY_PROTOCOL
+#define BUTTON_RC_DOWN 0x01000000
+#define BUTTON_RC_UP 0x00800000
+#define BUTTON_RC_SELECT 0x00400000
+#define BUTTON_RC_MENU 0x00200000
#define BUTTON_RC_PLAY 0x00100000
#define BUTTON_RC_STOP 0x00080000
-
#define BUTTON_RC_LEFT 0x00040000
#define BUTTON_RC_RIGHT 0x00020000
#define BUTTON_RC_VOL_UP 0x00010000
#define BUTTON_RC_VOL_DOWN 0x00008000
-#define BUTTON_REMOTE (BUTTON_RC_PLAY|BUTTON_RC_STOP\
+#define BUTTON_REMOTE (BUTTON_RC_UP|BUTTON_RC_DOWN \
+ |BUTTON_RC_SELECT|BUTTON_RC_PLAY \
+ |BUTTON_RC_PLAY|BUTTON_RC_STOP \
|BUTTON_RC_LEFT|BUTTON_RC_RIGHT\
|BUTTON_RC_VOL_UP|BUTTON_RC_VOL_DOWN)
#endif
diff --git a/firmware/target/arm/ipod/lcd-gray.c b/firmware/target/arm/ipod/lcd-gray.c
index d8695cdb10..883897b997 100644
--- a/firmware/target/arm/ipod/lcd-gray.c
+++ b/firmware/target/arm/ipod/lcd-gray.c
@@ -333,17 +333,18 @@ void lcd_update_rect(int x, int y, int width, int height)
x >>= 3;
width = xmax - x + 1;
- for (; y <= ymax; y++)
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
+ for (; y <= ymax; y++)
{
lcd_cmd_and_data(R_RAM_ADDR_SET, (y << 5) + addr_offset - x);
lcd_prepare_cmd(R_RAM_DATA);
-
+ fb_data *data = fbaddr(2*x,y);
#if defined(IPOD_MINI) || defined(IPOD_MINI2G)
if (pix_offset == -2)
- lcd_write_data_shifted(FBADDR(2*x, y), width);
+ lcd_write_data_shifted(data, width);
else
#endif
- lcd_write_data(FBADDR(2*x, y), width);
+ lcd_write_data(data, width);
}
}
diff --git a/firmware/target/arm/ipod/video/lcd-as-video.S b/firmware/target/arm/ipod/video/lcd-as-video.S
index 47155b8c75..7d6caef448 100644
--- a/firmware/target/arm/ipod/video/lcd-as-video.S
+++ b/firmware/target/arm/ipod/video/lcd-as-video.S
@@ -40,24 +40,24 @@ lcd_write_data: /* r1 = pixel count, must be even */
subs r1, r1, #16
.loop16:
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- subges r1, r1, #16
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ subsge r1, r1, #16
bge .loop16
/* no need to correct the count, we're just checking bits from now */
tst r1, #8
- ldmneia r0!, {r2-r4, r12}
- stmneia lr, {r2-r4, r12}
+ ldmiane r0!, {r2-r4, r12}
+ stmiane lr, {r2-r4, r12}
tst r1, #4
- ldmneia r0!, {r2-r3}
- stmneia lr, {r2-r3}
+ ldmiane r0!, {r2-r3}
+ stmiane lr, {r2-r3}
tst r1, #2
ldrne r3, [r0], #4
strne r3, [lr]
diff --git a/firmware/target/arm/lcd-ssd1815.c b/firmware/target/arm/lcd-ssd1815.c
index 028362f91c..0af20cd34f 100644
--- a/firmware/target/arm/lcd-ssd1815.c
+++ b/firmware/target/arm/lcd-ssd1815.c
@@ -221,6 +221,7 @@ void lcd_update(void)
{
int y;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -228,7 +229,7 @@ void lcd_update(void)
lcd_write_command (LCD_CNTL_HIGHCOL | ((xoffset >> 4) & 0xf));
lcd_write_command (LCD_CNTL_LOWCOL | (xoffset & 0xf));
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -249,6 +250,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -256,6 +258,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command (LCD_CNTL_HIGHCOL | (((x+xoffset) >> 4) & 0xf));
lcd_write_command (LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf));
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c b/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c
index d336ad7419..c10b4ca8f6 100644
--- a/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c
+++ b/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c
@@ -232,6 +232,7 @@ void lcd_update(void)
cmd1 = LCD_CNTL_HIGHCOL | (((xoffset) >> 4) & 0xf);
cmd2 = LCD_CNTL_LOWCOL | ((xoffset) & 0xf);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -239,7 +240,7 @@ void lcd_update(void)
lcd_write_command(cmd1);
lcd_write_command(cmd2);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -264,6 +265,7 @@ void lcd_update_rect(int x, int y, int width, int height)
cmd1 = LCD_CNTL_HIGHCOL | (((x + xoffset) >> 4) & 0xf);
cmd2 = LCD_CNTL_LOWCOL | ((x + xoffset) & 0xf);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -271,6 +273,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command(cmd1);
lcd_write_command(cmd2);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c
index 336b5626ca..45044bc664 100644
--- a/firmware/target/arm/pcm-telechips.c
+++ b/firmware/target/arm/pcm-telechips.c
@@ -218,6 +218,7 @@ void fiq_handler(void)
* r0-r3 and r12 is a working register.
*/
asm volatile (
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"sub lr, lr, #4 \n"
"stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
"mov r14, #0 \n" /* Was the callback called? */
@@ -251,7 +252,7 @@ void fiq_handler(void)
"stmia r11, { r8-r9 } \n" /* save p and size */
"cmp r14, #0 \n" /* Callback called? */
- "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
+ "ldmfdeq sp!, { r0-r3, pc }^ \n" /* no? -> exit */
"ldr r1, =pcm_play_status_callback \n"
"ldr r1, [r1] \n"
@@ -268,11 +269,12 @@ void fiq_handler(void)
"mov lr, pc \n"
"ldr pc, =pcm_play_dma_complete_callback \n"
"cmp r0, #0 \n" /* any more to play? */
- "ldmneia r11, { r8-r9 } \n" /* load new p and size */
+ "ldmiane r11, { r8-r9 } \n" /* load new p and size */
"cmpne r9, #0x0f \n" /* did we actually get enough data? */
"bhi .fill_fifo \n" /* not stop and enough? refill */
"ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
".ltorg \n"
+ END_ARM_ASM_SYNTAX_UNIFIED
: : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED)
);
}
diff --git a/firmware/target/arm/pp/mi4-loader.c b/firmware/target/arm/pp/mi4-loader.c
index 0104496e9d..f609e3ff7a 100644
--- a/firmware/target/arm/pp/mi4-loader.c
+++ b/firmware/target/arm/pp/mi4-loader.c
@@ -30,96 +30,9 @@
#include "crc32.h"
#include "file.h"
#if defined(HAVE_BOOTDATA)
-#include "system.h"
-#include "bootdata.h"
-
-/* Write bootdata into location in FIRMWARE marked by magic header
- * Assumes buffer is already loaded with the firmware image
- * We just need to find the location and write data into the
- * payload region along with the crc for later verification and use.
- * Returns payload len on success,
- * On error returns EKEY_NOT_FOUND
- */
-int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
-{
- struct boot_data_t bl_boot_data;
- struct boot_data_t *fw_boot_data = NULL;
- int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
- int payload_len = EKEY_NOT_FOUND;
-
- /* search for boot data header prior to search_len */
- for(int i = 0;i < search_len;i++)
- {
- fw_boot_data = (struct boot_data_t*) &buf[i];
- if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
- fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
- continue;
-
- memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
- bl_boot_data.boot_volume = boot_volume;
-
- memset(fw_boot_data->payload, 0, fw_boot_data->length);
- /* determine maximum bytes we can write to firmware
- BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
- payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
- fw_boot_data->length = payload_len;
- /* copy data to FIRMWARE bootdata struct */
- memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
- /* crc will be used within the firmware to check validity of bootdata */
- fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
- break;
-
- }
- return payload_len;
-}
+#include "multiboot.h"
#endif /* HAVE_BOOTDATA */
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware)
-{
- int fd;
- int f_offset;
- char add_path[MAX_PATH];
- /* Check in root of volume for rockbox_main.<playername> redirect */
- snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
- fd = open(add_path, O_RDONLY);
- if (fd < 0)
- return EFILE_NOT_FOUND;
-
- /*clear add_path for re-use*/
- memset(add_path, 0, sizeof(add_path));
- f_offset = read(fd, add_path,sizeof(add_path));
- close(fd);
-
- for(int i = f_offset - 1;i > 0; i--)
- {
- /* strip control chars < SPACE or all if path doesn't start with '/' */
- if (add_path[i] < 0x20 || add_path[0] != '/')
- add_path[i] = '\0';
- }
- /* if '/add_path' is specified in rockbox_main.<playername>
- path is /<vol#>/add_path/rootdir/firmwarename
- if add_path is empty or '/' is missing from beginning
- path is /<vol#>/rootdir/firmwarename
- */
- return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
- rootdir, firmware);
-}
-#endif /* HAVE_MULTIBOOT */
-
static inline unsigned int le2int(unsigned char* buf)
{
int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c
index 0d61eb44ff..94b1c5ae10 100644
--- a/firmware/target/arm/pp/pcm-pp.c
+++ b/firmware/target/arm/pp/pcm-pp.c
@@ -327,6 +327,7 @@ void fiq_playback(void)
*/
asm volatile (
/* No external calls */
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"sub lr, lr, #4 \n" /* Prepare return address */
"stmfd sp!, { lr } \n" /* stack lr so we can use it */
"ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux ... */
@@ -349,8 +350,8 @@ void fiq_playback(void)
"bhi 0b \n" /* ... yes, continue */
"cmp r9, #0 \n" /* either FIFO full or size empty? */
- "stmneia r11, { r8-r9 } \n" /* save p and size, if not empty */
- "ldmnefd sp!, { pc }^ \n" /* RFE if not empty */
+ "stmiane r11, { r8-r9 } \n" /* save p and size, if not empty */
+ "ldmfdne sp!, { pc }^ \n" /* RFE if not empty */
/* Making external calls */
"1: \n"
@@ -363,7 +364,7 @@ void fiq_playback(void)
"mov lr, pc \n" /* long call (not in same section) */
"bx r3 \n"
"cmp r0, #0 \n" /* more data? */
- "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
+ "ldmfdeq sp!, { r0-r3, pc }^ \n" /* no? -> exit */
"ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */
"ands r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */
@@ -394,6 +395,7 @@ void fiq_playback(void)
"bne 3b \n" /* no? -> go return */
"b 2b \n" /* yes -> get even more */
".ltorg \n"
+ END_ARM_ASM_SYNTAX_UNIFIED
: /* These must only be integers! No regs */
: "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED));
}
diff --git a/firmware/target/arm/rk27xx/lcdif-rk27xx.c b/firmware/target/arm/rk27xx/lcdif-rk27xx.c
index e6af0d978a..618b476480 100644
--- a/firmware/target/arm/rk27xx/lcdif-rk27xx.c
+++ b/firmware/target/arm/rk27xx/lcdif-rk27xx.c
@@ -198,9 +198,10 @@ static void create_llp(int x, int y, int width, int height)
width = width>>1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* build LLPs */
for (i=0; i<height; i++)
- llp_setup((void *)FBADDR(x,y+i),
+ llp_setup((void *)fbaddr(x,y+i),
(void*)(LCD_BUFF+((i%4)*4*width)),
&(scr_llp[i]),
width);
diff --git a/firmware/target/arm/s3c2440/i2c-s3c2440.h b/firmware/target/arm/s3c2440/i2c-s3c2440.h
index 793ee213fd..8b739358af 100644
--- a/firmware/target/arm/s3c2440/i2c-s3c2440.h
+++ b/firmware/target/arm/s3c2440/i2c-s3c2440.h
@@ -41,6 +41,6 @@
/* IICLC */
#define I2C_FLT_ENB (1 << 2)
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
void i2c_write(int addr, const unsigned char *data, int count);
diff --git a/firmware/target/arm/s5l8700/debug-s5l8700.c b/firmware/target/arm/s5l8700/debug-s5l8700.c
index ecb15df5d0..c42eac0438 100644
--- a/firmware/target/arm/s5l8700/debug-s5l8700.c
+++ b/firmware/target/arm/s5l8700/debug-s5l8700.c
@@ -112,7 +112,7 @@ bool dbg_hw_info(void)
_DEBUG_PRINTF("PMU:");
for(i=0;i<7;i++)
{
- char *device[] = {"(unknown)",
+ static const char * const device[] = {"(unknown)",
"(CLICKWHEEL)",
"(LCD)",
"(AUDIO)",
@@ -139,7 +139,7 @@ bool dbg_hw_info(void)
char line_cfg[4];
int abr_stat;
uint32_t abr_cnt;
- char *abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
+ static const char * const abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
uartc_port_get_line_info(&ser_port,
&tx_stat, &rx_stat, &tx_speed, &rx_speed, line_cfg);
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/button-target.h b/firmware/target/arm/s5l8700/ipodnano2g/button-target.h
index 82f600d302..75bc191608 100644
--- a/firmware/target/arm/s5l8700/ipodnano2g/button-target.h
+++ b/firmware/target/arm/s5l8700/ipodnano2g/button-target.h
@@ -47,15 +47,20 @@ void ipod_4g_button_int(void);
/* Remote control's buttons */
#ifdef IPOD_ACCESSORY_PROTOCOL
+#define BUTTON_RC_DOWN 0x01000000
+#define BUTTON_RC_UP 0x00800000
+#define BUTTON_RC_SELECT 0x00400000
+#define BUTTON_RC_MENU 0x00200000
#define BUTTON_RC_PLAY 0x00100000
#define BUTTON_RC_STOP 0x00080000
-
#define BUTTON_RC_LEFT 0x00040000
#define BUTTON_RC_RIGHT 0x00020000
#define BUTTON_RC_VOL_UP 0x00010000
#define BUTTON_RC_VOL_DOWN 0x00008000
-#define BUTTON_REMOTE (BUTTON_RC_PLAY|BUTTON_RC_STOP\
+#define BUTTON_REMOTE (BUTTON_RC_UP|BUTTON_RC_DOWN \
+ |BUTTON_RC_SELECT|BUTTON_RC_PLAY \
+ |BUTTON_RC_PLAY|BUTTON_RC_STOP \
|BUTTON_RC_LEFT|BUTTON_RC_RIGHT\
|BUTTON_RC_VOL_UP|BUTTON_RC_VOL_DOWN)
#endif
diff --git a/firmware/target/arm/s5l8700/yps3/lcd-yps3.c b/firmware/target/arm/s5l8700/yps3/lcd-yps3.c
index a9830bca57..eec11e34b8 100644
--- a/firmware/target/arm/s5l8700/yps3/lcd-yps3.c
+++ b/firmware/target/arm/s5l8700/yps3/lcd-yps3.c
@@ -299,14 +299,15 @@ void lcd_update_rect(int x, int y, int width, int height)
{
fb_data* p;
int h, w;
-
+
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
if (lcd_type == 1) {
/* TODO implement and test */
lcd_set_window1(x, y, width, height);
lcd_set_position1(x, y);
-
+
for (h = 0; h < height; h++) {
- p = FBADDR(0,y);
+ p = fbaddr(0,y);
for (w = 0; w < LCD_WIDTH; w++) {
while (LCD_STATUS & 0x10);
LCD_WDATA = *p++;
@@ -317,9 +318,9 @@ void lcd_update_rect(int x, int y, int width, int height)
else {
lcd_set_window2(x, y, width, height);
lcd_set_position2(x, y);
-
+
for (h = 0; h < height; h++) {
- p = FBADDR(x,y);
+ p = fbaddr(x,y);
for (w = 0; w < width; w++) {
while (LCD_STATUS & 0x10);
LCD_WDATA = *p++;
diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c
index 68b16f39f7..26b8e557a0 100644
--- a/firmware/target/arm/s5l8702/debug-s5l8702.c
+++ b/firmware/target/arm/s5l8702/debug-s5l8702.c
@@ -104,7 +104,7 @@ bool dbg_hw_info(void)
_DEBUG_PRINTF("PMU:");
for(i=0;i<7;i++)
{
- char *device[] = {"unknown",
+ static const char *const device[] = {"unknown",
"unknown",
"LCD",
"AUDIO",
@@ -157,7 +157,7 @@ bool dbg_hw_info(void)
char line_cfg[4];
int abr_stat;
uint32_t abr_cnt;
- char *abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
+ static const char * const abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
uartc_port_get_line_info(&ser_port,
&tx_stat, &rx_stat, &tx_speed, &rx_speed, line_cfg);
diff --git a/firmware/target/arm/s5l8702/ipod6g/button-target.h b/firmware/target/arm/s5l8702/ipod6g/button-target.h
index ed17fc4baa..a5c4771b7c 100644
--- a/firmware/target/arm/s5l8702/ipod6g/button-target.h
+++ b/firmware/target/arm/s5l8702/ipod6g/button-target.h
@@ -47,15 +47,20 @@ void ipod_4g_button_int(void);
/* Remote control's buttons */
#ifdef IPOD_ACCESSORY_PROTOCOL
+#define BUTTON_RC_DOWN 0x01000000
+#define BUTTON_RC_UP 0x00800000
+#define BUTTON_RC_SELECT 0x00400000
+#define BUTTON_RC_MENU 0x00200000
#define BUTTON_RC_PLAY 0x00100000
#define BUTTON_RC_STOP 0x00080000
-
#define BUTTON_RC_LEFT 0x00040000
#define BUTTON_RC_RIGHT 0x00020000
#define BUTTON_RC_VOL_UP 0x00010000
#define BUTTON_RC_VOL_DOWN 0x00008000
-#define BUTTON_REMOTE (BUTTON_RC_PLAY|BUTTON_RC_STOP\
+#define BUTTON_REMOTE (BUTTON_RC_UP|BUTTON_RC_DOWN \
+ |BUTTON_RC_SELECT|BUTTON_RC_PLAY \
+ |BUTTON_RC_PLAY|BUTTON_RC_STOP \
|BUTTON_RC_LEFT|BUTTON_RC_RIGHT\
|BUTTON_RC_VOL_UP|BUTTON_RC_VOL_DOWN)
#endif
diff --git a/firmware/target/arm/samsung/yh920/lcd-yh920.c b/firmware/target/arm/samsung/yh920/lcd-yh920.c
index 06aa3d718d..6a579f382a 100644
--- a/firmware/target/arm/samsung/yh920/lcd-yh920.c
+++ b/firmware/target/arm/samsung/yh920/lcd-yh920.c
@@ -253,13 +253,14 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
lcd_write_reg(LCD_CNTL_PAGE, y);
lcd_write_reg(LCD_CNTL_COLUMN, x);
- addr = FBADDR(x,y);
+ addr = fbaddr(x,y);
lcd_send_cmd(LCD_CNTL_DATA_WRITE);
lcd_write_data(addr, width);
diff --git a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
index f13904628d..a1985472a0 100644
--- a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
+++ b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
@@ -303,7 +303,7 @@ static void cfs_init(void)
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, &sector);
inode = (struct cfs_inode*)&sector;
#ifndef BOOTLOADER
- sectors_handle = core_alloc("ata sectors", VFAT_SECTOR_SIZE(inode->filesize));
+ sectors_handle = core_alloc(VFAT_SECTOR_SIZE(inode->filesize));
unsigned long *sectors = core_get_data(sectors_handle);
#else
static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */
diff --git a/firmware/target/arm/tms320dm320/i2c-dm320.h b/firmware/target/arm/tms320dm320/i2c-dm320.h
index 43271692eb..89a8e6f1a4 100644
--- a/firmware/target/arm/tms320dm320/i2c-dm320.h
+++ b/firmware/target/arm/tms320dm320/i2c-dm320.h
@@ -24,7 +24,7 @@
#include "system.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(unsigned short address, const unsigned char *data, int count);
int i2c_read(unsigned short address, unsigned char* buf, int count);
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c
index d952d3d40d..8620c672e1 100644
--- a/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c
+++ b/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c
@@ -38,7 +38,7 @@
#if CONFIG_ORIENTATION == SCREEN_PORTRAIT
#define LCD_USE_DMA
-#elif defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#elif LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCD_USE_DMA
#endif
@@ -511,7 +511,7 @@ void lcd_update_rect(int x, int y, int width, int height)
#else
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#if defined(LCD_USE_DMA)
dma_start_transfer16( (char *)FBADDR(0,0), x, y, LCD_HEIGHT,
diff --git a/firmware/target/coldfire/debug-coldfire.c b/firmware/target/coldfire/debug-coldfire.c
index 56f1bbe1a7..ef44a82176 100644
--- a/firmware/target/coldfire/debug-coldfire.c
+++ b/firmware/target/coldfire/debug-coldfire.c
@@ -144,7 +144,7 @@ bool dbg_ports(void)
adc_buttons = adc_read(ADC_BUTTONS);
adc_remote = adc_read(ADC_REMOTE);
-
+ battery_read_info(&adc_battery_voltage, &adc_battery_level);
#if defined(IAUDIO_X5) || defined(IAUDIO_M5) || defined(IRIVER_H300_SERIES)
lcd_putsf(0, line++, "ADC_BUTTONS (%c): %02x",
button_scan_enabled() ? '+' : '-', adc_buttons);
@@ -162,8 +162,7 @@ bool dbg_ports(void)
adc_read(ADC_REMOTEDETECT));
#endif
- adc_battery_voltage = _battery_voltage();
- adc_battery_level = battery_level();
+ battery_read_info(&adc_battery_voltage, &adc_battery_level);
lcd_putsf(0, line++, "Batt: %d.%03dV %d%% ", adc_battery_voltage / 1000,
adc_battery_voltage % 1000, adc_battery_level);
diff --git a/firmware/target/coldfire/iaudio/m3/lcd-m3.c b/firmware/target/coldfire/iaudio/m3/lcd-m3.c
index 3da608a0ef..5e84cbacd3 100644
--- a/firmware/target/coldfire/iaudio/m3/lcd-m3.c
+++ b/firmware/target/coldfire/iaudio/m3/lcd-m3.c
@@ -258,6 +258,7 @@ void lcd_update(void)
int y;
if (initialized)
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(y = 0;y < LCD_FBHEIGHT;y++)
{
/* Copy display bitmap to hardware.
@@ -266,7 +267,7 @@ void lcd_update(void)
have to update one page at a time. */
lcd_write_command(LCD_SET_PAGE | (y > 5 ? y + 2 : y));
lcd_write_command_e(LCD_SET_COLUMN | 0, 0);
- lcd_write_data(FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data(fbaddr(0,y), LCD_WIDTH);
}
}
}
@@ -289,6 +290,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectangle bitmap to hardware
COM48-COM63 are not connected, so we need to skip those */
for (; y <= ymax; y++)
@@ -296,7 +298,7 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command(LCD_SET_PAGE | ((y > 5 ? y + 2 : y) & 0xf));
lcd_write_command_e(LCD_SET_COLUMN | ((x >> 4) & 0xf), x & 0xf);
- lcd_write_data(FBADDR(x,y), width);
+ lcd_write_data(fbaddr(x,y), width);
}
}
}
diff --git a/firmware/target/coldfire/iaudio/m5/lcd-m5.c b/firmware/target/coldfire/iaudio/m5/lcd-m5.c
index 8f022adf96..35fd173f18 100644
--- a/firmware/target/coldfire/iaudio/m5/lcd-m5.c
+++ b/firmware/target/coldfire/iaudio/m5/lcd-m5.c
@@ -200,6 +200,7 @@ void lcd_update(void)
{
int y;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -207,7 +208,7 @@ void lcd_update(void)
lcd_write_command_ex(LCD_CNTL_COLUMN, 0, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -228,6 +229,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -235,6 +237,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/coldfire/iriver/h100/lcd-h100.c b/firmware/target/coldfire/iriver/h100/lcd-h100.c
index b13751b9eb..b6eea1dfa3 100644
--- a/firmware/target/coldfire/iriver/h100/lcd-h100.c
+++ b/firmware/target/coldfire/iriver/h100/lcd-h100.c
@@ -209,6 +209,7 @@ void lcd_update(void)
{
int y;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -216,7 +217,7 @@ void lcd_update(void)
lcd_write_command_ex(LCD_CNTL_COLUMN, 0, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -237,6 +238,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -244,6 +246,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/coldfire/mpio/hd300/lcd-hd300.c b/firmware/target/coldfire/mpio/hd300/lcd-hd300.c
index 509ed4cd53..9cff9cd712 100644
--- a/firmware/target/coldfire/mpio/hd300/lcd-hd300.c
+++ b/firmware/target/coldfire/mpio/hd300/lcd-hd300.c
@@ -231,6 +231,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -238,6 +239,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/hosted/filesystem-app.c b/firmware/target/hosted/filesystem-app.c
index 2121af7752..09b3365a9e 100644
--- a/firmware/target/hosted/filesystem-app.c
+++ b/firmware/target/hosted/filesystem-app.c
@@ -43,6 +43,10 @@
#undef PIVOT_ROOT
#endif
+#if defined(DBTOOL)
+#define PIVOT_ROOT "."
+#endif
+
#if defined(__PCTOOL__)
/* We don't want this for tools */
#undef HAVE_SPECIAL_DIRS
@@ -222,7 +226,7 @@ const char * handle_special_dirs(const char *dir, unsigned flags,
#define PIVOT_ROOT_LEN (sizeof(PIVOT_ROOT)-1)
/* Prepend root prefix to find actual path */
if (strncmp(PIVOT_ROOT, dir, PIVOT_ROOT_LEN)
-#ifdef MULTIDRIVE_DIR
+#if defined(MULTIDRIVE_DIR) && defined(MULTIDRIVE_DIR_LEN)
/* Unless it's a MULTIDRIVE dir, in which case use as-is */
&& strncmp(MULTIDRIVE_DIR, dir, MULTIDRIVE_DIR_LEN)
#endif
@@ -580,10 +584,11 @@ ssize_t app_readlink(const char *path, char *buf, size_t bufsiz)
int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize)
{
-#ifdef HAVE_MULTIVOLUME
+#if defined(HAVE_MULTIVOLUME) && !(CONFIG_PLATFORM & PLATFORM_HOSTED)
char volname[VOL_MAX_LEN + 1];
get_volume_name(volume, volname);
#else
+ IF_MV((void)volume;)
const char *volname = "/";
#endif
@@ -595,3 +600,8 @@ int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize)
return 0;
}
+
+const char* app_root_realpath(void)
+{
+ return PATH_ROOTSTR;
+}
diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.c b/firmware/target/hosted/ibasso/pcm-ibasso.c
index 5c68ded3e1..889386b7c6 100644
--- a/firmware/target/hosted/ibasso/pcm-ibasso.c
+++ b/firmware/target/hosted/ibasso/pcm-ibasso.c
@@ -123,7 +123,7 @@ static void* pcm_thread_run(void* nothing)
/* https://github.com/tinyalsa/tinyalsa/blob/master/tinypcminfo.c */
-static const char* format_lookup[] =
+static const char* const format_lookup[] =
{
/*[0] =*/ "S8",
"U8",
diff --git a/firmware/target/hosted/ibasso/sysfs-ibasso.c b/firmware/target/hosted/ibasso/sysfs-ibasso.c
index 8f62e3fec2..e3a0f911af 100644
--- a/firmware/target/hosted/ibasso/sysfs-ibasso.c
+++ b/firmware/target/hosted/ibasso/sysfs-ibasso.c
@@ -32,7 +32,7 @@
#include "sysfs-ibasso.h"
-static const char* SYSFS_PATHS[] =
+static const char* const SYSFS_PATHS[] =
{
/* SYSFS_DX50_CODEC_VOLUME */
"/dev/codec_volume",
diff --git a/firmware/target/hosted/rolo.c b/firmware/target/hosted/rolo.c
index 04b21d553e..dfe483c964 100644
--- a/firmware/target/hosted/rolo.c
+++ b/firmware/target/hosted/rolo.c
@@ -37,12 +37,6 @@
//#define LOGF_ENABLE
#include "logf.h"
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
-#include "bootdata.h"
-#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*rb-loader.c*/
-#endif
-
static void rolo_error(const char *text, const char *text2)
{
lcd_clear_display();
@@ -72,16 +66,6 @@ int rolo_load(const char* filename)
audio_stop();
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
- /* write the bootdata as if rolo were the bootloader */
- unsigned int crc = 0;
- if (strcmp(filename, BOOTDIR "/" BOOTFILE) == 0)
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
-
- if(crc > 0 && crc == boot_data.crc)
- write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */
-#endif
-
#ifdef HAVE_STORAGE_FLUSH
lcd_puts(0, 2, "Flushing storage buffers");
lcd_update();
diff --git a/firmware/target/hosted/sdl/lcd-sdl.c b/firmware/target/hosted/sdl/lcd-sdl.c
index de19de365a..8cbb6c5651 100644
--- a/firmware/target/hosted/sdl/lcd-sdl.c
+++ b/firmware/target/hosted/sdl/lcd-sdl.c
@@ -31,9 +31,9 @@ void sdl_update_rect(SDL_Surface *surface, int x_start, int y_start, int width,
unsigned long (*getpixel)(int, int))
{
SDL_Rect dest;
-#if LCD_DEPTH >= 8 && (LCD_PIXELFORMAT == RGB565) \
- && !defined(LCD_STRIDEFORMAT) && !defined(HAVE_LCD_SPLIT) \
- && !defined(HAVE_REMOTE_LCD)
+#if LCD_DEPTH >= 8 && (LCD_PIXELFORMAT == RGB565) && \
+ (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE) && \
+ !defined(HAVE_LCD_SPLIT) && !defined(HAVE_REMOTE_LCD)
SDL_Rect src;
(void)max_x;
(void)max_y;
diff --git a/firmware/target/hosted/sdl/sim-ui-defines.h b/firmware/target/hosted/sdl/sim-ui-defines.h
index baba357ccc..7d60deac34 100644
--- a/firmware/target/hosted/sdl/sim-ui-defines.h
+++ b/firmware/target/hosted/sdl/sim-ui-defines.h
@@ -523,10 +523,10 @@
#elif defined(FIIO_M3K) || defined(FIIO_M3K_LINUX)
#define UI_TITLE "FiiO M3K"
-#define UI_WIDTH 287 /* width of GUI window */
-#define UI_HEIGHT 589 /* height of GUI window */
-#define UI_LCD_POSX 25
-#define UI_LCD_POSY 15
+#define UI_WIDTH 334 /* width of GUI window */
+#define UI_HEIGHT 684 /* height of GUI window */
+#define UI_LCD_POSX 49
+#define UI_LCD_POSY 37
#elif defined(SHANLING_Q1)
diff --git a/firmware/target/hosted/sdl/thread-sdl.c b/firmware/target/hosted/sdl/thread-sdl.c
index a76941f103..17a0f847b5 100644
--- a/firmware/target/hosted/sdl/thread-sdl.c
+++ b/firmware/target/hosted/sdl/thread-sdl.c
@@ -215,7 +215,7 @@ void switch_thread(void)
} /* STATE_SLEEPING: */
}
-#ifdef DEBUG
+#ifdef BUFLIB_DEBUG_CHECK_VALID
core_check_valid();
#endif
__running_self_entry() = current;
diff --git a/firmware/target/hosted/system-hosted.c b/firmware/target/hosted/system-hosted.c
index ce47fd5f5c..c4ae5a404f 100644
--- a/firmware/target/hosted/system-hosted.c
+++ b/firmware/target/hosted/system-hosted.c
@@ -90,6 +90,7 @@ static void sig_handler(int sig, siginfo_t *siginfo, void *context)
void power_off(void)
{
backlight_hw_off();
+ sync();
system("/sbin/poweroff");
while (1); /* halt */
}
diff --git a/firmware/target/hosted/usb-hiby.c b/firmware/target/hosted/usb-hiby.c
index b82fa5c4ce..050c86e446 100644
--- a/firmware/target/hosted/usb-hiby.c
+++ b/firmware/target/hosted/usb-hiby.c
@@ -63,8 +63,8 @@ void usb_enable(bool on)
*/
int disk_mount_all(void)
{
- const char *dev[] = {"/dev/mmcblk0p1", "/dev/mmcblk0"};
- const char *fs[] = {"vfat", "exfat"};
+ const char * const dev[] = {"/dev/mmcblk0p1", "/dev/mmcblk0"};
+ const char * const fs[] = {"vfat", "exfat"};
sysfs_set_string("/sys/class/android_usb/android0/f_mass_storage/lun/file", "");
diff --git a/firmware/target/mips/exception-mips.S b/firmware/target/mips/exception-mips.S
new file mode 100644
index 0000000000..605817c7d5
--- /dev/null
+++ b/firmware/target/mips/exception-mips.S
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "mips.h"
+
+ .text
+ .set push
+ .set mips32
+
+ .section .vectors.1, "ax", %progbits
+ la k1, tlb_refill_handler
+ j _exception_enter
+
+ .section .vectors.2, "ax", %progbits
+ la k1, cache_error_handler
+ j _exception_enter
+
+ .section .vectors.3, "ax", %progbits
+ li k1, M_CauseExcCode
+ mfc0 k0, C0_CAUSE
+ and k0, k1
+ beq zero, k0, 1f
+ la k1, exception_handler
+ j _exception_enter
+1:
+ la k1, intr_handler
+ j _exception_enter
+
+ .section .vectors.4, "ax", %progbits
+ /* same as above */
+ li k1, M_CauseExcCode
+ mfc0 k0, C0_CAUSE
+ and k0, k1
+ beq zero, k0, 1f
+ nop
+ la k1, exception_handler
+ j _exception_enter
+1:
+ la k1, intr_handler
+ j _exception_enter
+
+ .set push
+ .set noat
+ .set noreorder
+ .section .vectors, "ax", %progbits
+
+_exception_enter:
+ la k0, _irqstackend
+ addiu k0, -0x84
+
+ sw $1, 0x00(k0)
+ sw $2, 0x04(k0)
+ sw $3, 0x08(k0)
+ sw $4, 0x0c(k0)
+ sw $5, 0x10(k0)
+ sw $6, 0x14(k0)
+ sw $7, 0x18(k0)
+ sw $8, 0x1c(k0)
+ sw $9, 0x20(k0)
+ sw $10, 0x24(k0)
+ sw $11, 0x28(k0)
+ sw $12, 0x2c(k0)
+ sw $13, 0x30(k0)
+ sw $14, 0x34(k0)
+ sw $15, 0x38(k0)
+ sw $16, 0x3c(k0)
+ sw $17, 0x40(k0)
+ sw $18, 0x44(k0)
+ sw $19, 0x48(k0)
+ sw $20, 0x4c(k0)
+ sw $21, 0x50(k0)
+ sw $22, 0x54(k0)
+ sw $23, 0x58(k0)
+ sw $24, 0x5c(k0)
+ sw $25, 0x60(k0)
+ sw $28, 0x64(k0)
+ sw $29, 0x68(k0)
+ sw $30, 0x6c(k0)
+ sw $31, 0x70(k0)
+
+ mflo t0
+ ssnop
+ sw t0, 0x74(k0)
+
+ mfhi t0
+ ssnop
+ sw t0, 0x78(k0)
+
+ mfc0 t0, C0_STATUS
+ ssnop
+ ssnop
+ ssnop
+ sw t0, 0x7c(k0)
+
+ mfc0 a1, C0_EPC
+ ssnop
+ ssnop
+ ssnop
+ sw a1, 0x80(k0)
+
+ move sp, k0
+
+ jalr k1
+ move a0, sp
+
+_exception_return:
+ /* restore all GPRs except sp */
+ lw $1, 0x00(sp)
+ lw $2, 0x04(sp)
+ lw $3, 0x08(sp)
+ lw $4, 0x0c(sp)
+ lw $5, 0x10(sp)
+ lw $6, 0x14(sp)
+ lw $7, 0x18(sp)
+ lw $8, 0x1c(sp)
+ lw $9, 0x20(sp)
+ lw $10, 0x24(sp)
+ lw $11, 0x28(sp)
+ lw $12, 0x2c(sp)
+ lw $13, 0x30(sp)
+ lw $14, 0x34(sp)
+ lw $15, 0x38(sp)
+ lw $16, 0x3c(sp)
+ lw $17, 0x40(sp)
+ lw $18, 0x44(sp)
+ lw $19, 0x48(sp)
+ lw $20, 0x4c(sp)
+ lw $21, 0x50(sp)
+ lw $22, 0x54(sp)
+ lw $23, 0x58(sp)
+ lw $24, 0x5c(sp)
+ lw $25, 0x60(sp)
+ lw $28, 0x64(sp)
+ lw $30, 0x6c(sp)
+ lw $31, 0x70(sp)
+
+ lw k0, 0x74(sp)
+ mtlo k0
+ ssnop
+
+ lw k0, 0x78(sp)
+ mthi k0
+ ssnop
+
+ lw k0, 0x7c(sp)
+ mtc0 k0, C0_STATUS
+ ssnop
+ ssnop
+ ssnop
+
+ lw k0, 0x80(sp)
+ mtc0 k0, C0_EPC
+ ssnop
+ ssnop
+ ssnop
+
+ /* restore stack pointer last */
+ lw sp, 0x68(sp)
+ eret
+ ssnop
+
+ .set pop
+ .set pop
diff --git a/firmware/target/mips/ingenic_jz47xx/app.lds b/firmware/target/mips/ingenic_jz47xx/app.lds
index 89deb63f89..1d300fed82 100644
--- a/firmware/target/mips/ingenic_jz47xx/app.lds
+++ b/firmware/target/mips/ingenic_jz47xx/app.lds
@@ -1,9 +1,13 @@
#include "config.h"
+#define __ASSEMBLY__
+#include "cpu.h"
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_jz47xx/crt0.o)
+INPUT(target/mips/exception-mips.o)
+INPUT(target/mips/system-mips.o)
#define STUBOFFSET 0x4000
@@ -18,22 +22,34 @@ STARTUP(target/mips/ingenic_jz47xx/crt0.o)
/* Where the codec buffer ends, and the plugin buffer starts */
#define ENDCODECADDR (ENDAUDIOADDR + CODEC_SIZE)
+/* Place init code in the codec buffer */
+#define INITBASE ENDAUDIOADDR
+#define INITSIZE CODEC_SIZE
+
MEMORY
{
DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
+ INIT : ORIGIN = INITBASE, LENGTH = INITSIZE
}
SECTIONS
{
. = DRAMORIG;
- .text :
+ .startup :
{
loadaddress = .;
_loadaddress = .;
- *(.init.text);
+ *(.startup.text);
+ } > DRAM
+
+ .text :
+ {
*(.text*);
+#ifndef HAVE_INIT_ATTR
+ *(.init*);
+#endif
} > DRAM
. = ALIGN(4);
@@ -75,6 +91,16 @@ SECTIONS
} > IRAM
_iramcopy = LOADADDR(.iram);
+#ifdef HAVE_INIT_ATTR
+ .init :
+ {
+ _initstart = .;
+ *(.init*);
+ _initend = .;
+ } > INIT AT> DRAM
+ _initcopy = LOADADDR(.init);
+#endif
+
. = ALIGN(4);
.stack (NOLOAD):
@@ -83,9 +109,9 @@ SECTIONS
stackbegin = .;
. += 0x1d00;
stackend = .;
- irqstackbegin = .;
+ _irqstackbegin = .;
. += 0x400;
- irqstackend = .;
+ _irqstackend = .;
} > IRAM
.bss (NOLOAD):
diff --git a/firmware/target/mips/ingenic_jz47xx/boot.lds b/firmware/target/mips/ingenic_jz47xx/boot.lds
index b5a3f51c01..b1ea5ddf13 100644
--- a/firmware/target/mips/ingenic_jz47xx/boot.lds
+++ b/firmware/target/mips/ingenic_jz47xx/boot.lds
@@ -4,6 +4,8 @@ OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_jz47xx/crt0.o)
+INPUT(target/mips/exception-mips.o)
+INPUT(target/mips/system-mips.o)
#define DRAMSIZE ((MEMORYSIZE-4) * 0x100000)
@@ -87,9 +89,9 @@ SECTIONS
stackbegin = .;
. += 0x1d00;
stackend = .;
- irqstackbegin = .;
+ _irqstackbegin = .;
. += 0x400;
- irqstackend = .;
+ _irqstackend = .;
} > IRAM
/DISCARD/ :
diff --git a/firmware/target/mips/ingenic_jz47xx/crt0.S b/firmware/target/mips/ingenic_jz47xx/crt0.S
index 258a5c02fc..b73a43d8f2 100644
--- a/firmware/target/mips/ingenic_jz47xx/crt0.S
+++ b/firmware/target/mips/ingenic_jz47xx/crt0.S
@@ -40,11 +40,12 @@
.text
- .extern system_main
+ .extern system_early_init
.extern main
.global _start
- .section .init.text
+ .section .startup.text,"ax",%progbits
+ .set push
.set mips32
.set noreorder
.set noat
@@ -138,6 +139,19 @@ _iram_loop:
bne t1, t2, _iram_loop
sw t3, -4(t1)
+#ifdef HAVE_INIT_ATTR
+ /* Copy init code */
+ la t0, _initcopy
+ la t1, _initstart
+ la t2, _initend
+_init_loop:
+ lw t3, 0(t0)
+ addiu t1, 4
+ addiu t0, 4
+ bne t1, t2, _init_loop
+ sw t3, -4(t1)
+#endif
+
/*
----------------------------------------------------
Clear BSS section
@@ -169,8 +183,8 @@ _stack_loop:
Set up interrupt stack
----------------------------------------------------
*/
- la k0, irqstackend
- la t0, irqstackbegin
+ la k0, _irqstackend
+ la t0, _irqstackbegin
_irq_stack_loop:
addiu t0, 4
@@ -182,167 +196,9 @@ _irq_stack_loop:
Jump to C code
----------------------------------------------------
*/
- jal system_main /* Init clocks etc first */
+ jal system_early_init /* Init clocks etc first */
ssnop
j main
- ssnop
-
-
- /*
- * 0x0 - Simple TLB refill handler
- * 0x100 - Cache error handler
- * 0x180 - Exception/Interrupt handler
- * 0x200 - Special Exception Interrupt handler (when IV is set in CP0_CAUSE)
- */
-
- .section .vectors.1, "ax", %progbits
- j tlb_refill_handler
- ssnop
-
- .section .vectors.2, "ax", %progbits
- j real_exception_handler
- ssnop
-
- .section .vectors.3, "ax", %progbits
- j real_exception_handler
- ssnop
-
- .section .vectors.4, "ax", %progbits
- j real_exception_handler
- ssnop
-
- .section .vectors, "ax", %progbits
-real_exception_handler:
-
- /* Store stack pointer */
- move k0, sp
- /* jump to IRQ stack */
- la sp, irqstackend
-
- /* Push crap on frame */
- addiu sp, -0x84
- /* store current stack pointer */
- sw k0, 0x80(sp)
-
- sw ra, 0(sp)
- sw fp, 4(sp)
- sw gp, 8(sp)
- sw t9, 0xC(sp)
- sw t8, 0x10(sp)
- sw s7, 0x14(sp)
- sw s6, 0x18(sp)
- sw s5, 0x1C(sp)
- sw s4, 0x20(sp)
- sw s3, 0x24(sp)
- sw s2, 0x28(sp)
- sw s1, 0x2C(sp)
- sw s0, 0x30(sp)
- sw t7, 0x34(sp)
- sw t6, 0x38(sp)
- sw t5, 0x3C(sp)
- sw t4, 0x40(sp)
- sw t3, 0x44(sp)
- sw t2, 0x48(sp)
- sw t1, 0x4C(sp)
- sw t0, 0x50(sp)
- sw a3, 0x54(sp)
- sw a2, 0x58(sp)
- sw a1, 0x5C(sp)
- sw a0, 0x60(sp)
- sw v1, 0x64(sp)
- sw v0, 0x68(sp)
- sw $1, 0x6C(sp)
- mflo k0
- ssnop
- sw k0, 0x70(sp)
- mfhi k0
- ssnop
- sw k0, 0x74(sp)
- mfc0 k0, C0_STATUS
- ssnop
- ssnop
- ssnop
- sw k0, 0x78(sp)
- mfc0 k0, C0_EPC
- ssnop
- ssnop
- ssnop
- sw k0, 0x7C(sp)
-
- li k1, M_CauseExcCode
- mfc0 k0, C0_CAUSE
- and k0, k1
- beq zero, k0, _int
- ssnop
- j _exception
- ssnop
-
-_int:
- jal intr_handler
- ssnop
- j _exception_return
-
-_exception:
- move a0, sp
- mfc0 a1, C0_CAUSE
- ssnop
- ssnop
- ssnop
- mfc0 a2, C0_EPC
- ssnop
- ssnop
- ssnop
- jal exception_handler
- ssnop
+ move ra, zero /* init backtrace root */
-_exception_return:
- lw ra, 0(sp)
- lw fp, 4(sp)
- lw gp, 8(sp)
- lw t9, 0xC(sp)
- lw t8, 0x10(sp)
- lw s7, 0x14(sp)
- lw s6, 0x18(sp)
- lw s5, 0x1C(sp)
- lw s4, 0x20(sp)
- lw s3, 0x24(sp)
- lw s2, 0x28(sp)
- lw s1, 0x2C(sp)
- lw s0, 0x30(sp)
- lw t7, 0x34(sp)
- lw t6, 0x38(sp)
- lw t5, 0x3C(sp)
- lw t4, 0x40(sp)
- lw t3, 0x44(sp)
- lw t2, 0x48(sp)
- lw t1, 0x4C(sp)
- lw t0, 0x50(sp)
- lw a3, 0x54(sp)
- lw a2, 0x58(sp)
- lw a1, 0x5C(sp)
- lw a0, 0x60(sp)
- lw v1, 0x64(sp)
- lw v0, 0x68(sp)
- lw $1, 0x6C(sp)
- lw k0, 0x70(sp)
- mtlo k0
- ssnop
- lw k0, 0x74(sp)
- mthi k0
- ssnop
- lw k0, 0x78(sp)
- mtc0 k0, C0_STATUS
- ssnop
- ssnop
- ssnop
- lw k0, 0x7C(sp)
- mtc0 k0, C0_EPC
- ssnop
- ssnop
- ssnop
- /* Restore previous stack pointer */
- lw sp, 0x80(sp)
- eret
- ssnop
- .set reorder
- .set at
+ .set pop
diff --git a/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c b/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c
index 65b5e93887..f5624cf469 100644
--- a/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c
@@ -537,7 +537,7 @@ static const char* hw_info_menu_get_name(int item, void * data,
(void)data;
struct tm *cur_time;
uint32_t *stackptr;
- extern uint32_t irqstackend,irqstackbegin;
+ extern uint32_t _irqstackend,_irqstackbegin;
int btn;
#ifdef HAVE_TOUCHSCREEN
int touch;
@@ -557,10 +557,10 @@ static const char* hw_info_menu_get_name(int item, void * data,
read_cp0_15(), (REG_CPM_CLKGR0 & BIT31) >> 31);
return buffer;
case 2: /*IRQstack*/
- stackptr = &irqstackbegin;
- for ( ; stackptr < &irqstackend && *stackptr == 0xDEADBEEF; stackptr++) {}
+ stackptr = &_irqstackbegin;
+ for ( ; stackptr < &_irqstackend && *stackptr == 0xDEADBEEF; stackptr++) {}
snprintf(buffer, buffer_len, "IRQ stack max: %lu",
- (uint32_t)&irqstackend - (uint32_t)stackptr);
+ (uint32_t)&_irqstackend - (uint32_t)stackptr);
return buffer;
case 5: /*Touch/BTN*/
#ifdef HAVE_TOUCHSCREEN
diff --git a/firmware/target/mips/ingenic_jz47xx/system-jz4740.c b/firmware/target/mips/ingenic_jz47xx/system-jz4740.c
index fdc335ad21..5aff144327 100644
--- a/firmware/target/mips/ingenic_jz47xx/system-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/system-jz4740.c
@@ -219,80 +219,6 @@ void intr_handler(void)
irqvector[irq-1]();
}
-#define EXC(x,y) case (x): return (y);
-static char* parse_exception(unsigned int cause)
-{
- switch(cause & M_CauseExcCode)
- {
- EXC(EXC_INT, "Interrupt");
- EXC(EXC_MOD, "TLB Modified");
- EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
- EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
- EXC(EXC_ADES, "Address Error (Store)");
- EXC(EXC_TLBS, "TLB Exception (Store)");
- EXC(EXC_IBE, "Instruction Bus Error");
- EXC(EXC_DBE, "Data Bus Error");
- EXC(EXC_SYS, "Syscall");
- EXC(EXC_BP, "Breakpoint");
- EXC(EXC_RI, "Reserved Instruction");
- EXC(EXC_CPU, "Coprocessor Unusable");
- EXC(EXC_OV, "Overflow");
- EXC(EXC_TR, "Trap Instruction");
- EXC(EXC_FPE, "Floating Point Exception");
- EXC(EXC_C2E, "COP2 Exception");
- EXC(EXC_MDMX, "MDMX Exception");
- EXC(EXC_WATCH, "Watch Exception");
- EXC(EXC_MCHECK, "Machine Check Exception");
- EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
- default:
- return NULL;
- }
-}
-
-void exception_handler(void* stack_ptr, unsigned int cause, unsigned int epc)
-{
-#if EXTENDED_EXCEPTION_DESC
- (void)epc;
-
- /* Depends on crt0.S ! */
- char *registers[] = { "ra", "fp", "gp", "t9", "t8", "s7", "s6", "s5", "s4",
- "s3", "s2", "s1", "s0", "t7", "t6", "t5", "t4", "t3",
- "t2", "t1", "t0", "a3", "a2", "a1", "a0", "v1", "v0",
- "$1", "LO", "HI", "STATUS", "EPC" };
- int i;
-
-#if LCD_DEPTH > 1
- lcd_set_backdrop(NULL);
- lcd_set_drawmode(DRMODE_SOLID);
- lcd_set_foreground(LCD_BLACK);
- lcd_set_background(LCD_WHITE);
-#endif
- lcd_setfont(FONT_SYSFIXED);
- lcd_set_viewport(NULL);
-
- lcd_clear_display();
- backlight_hw_on();
-
- lcd_puts(0, 0, parse_exception(cause));
- lcd_putsf(0, 1, "0x%08x at 0x%08x", read_c0_badvaddr(), epc);
- for(i=0; i< 0x80/4; i+=2)
- {
- unsigned int* ptr = (unsigned int*)(stack_ptr + i*4);
- lcd_putsf(0, 3 + i/2, "%s: 0x%08x %s: 0x%08x", registers[i], *ptr, registers[i+1], *(ptr+1));
- }
- lcd_update();
-
- system_exception_wait();
-#else
- panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)", parse_exception(cause), read_c0_badvaddr(), epc, (unsigned int)stack_ptr);
-#endif
-}
-
-void tlb_refill_handler(void)
-{
- panicf("TLB refill handler at 0x%08lx! [0x%x]", read_c0_epc(), read_c0_badvaddr());
-}
-
void udelay(unsigned int usec)
{
unsigned int i = usec * (__cpm_get_cclk() / 2000000);
@@ -508,7 +434,7 @@ static void sdram_init(void)
}
/* Gets called *before* main */
-void ICODE_ATTR system_main(void)
+void ICODE_ATTR system_early_init(void)
{
int i;
diff --git a/firmware/target/mips/ingenic_jz47xx/system-jz4760.c b/firmware/target/mips/ingenic_jz47xx/system-jz4760.c
index 10973f3e2b..3a1321b992 100644
--- a/firmware/target/mips/ingenic_jz47xx/system-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/system-jz4760.c
@@ -301,46 +301,6 @@ top:
goto top;
}
-#define EXC(x,y) case (x): return (y);
-static char* parse_exception(unsigned int cause)
-{
- switch(cause & M_CauseExcCode)
- {
- EXC(EXC_INT, "Interrupt");
- EXC(EXC_MOD, "TLB Modified");
- EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
- EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
- EXC(EXC_ADES, "Address Error (Store)");
- EXC(EXC_TLBS, "TLB Exception (Store)");
- EXC(EXC_IBE, "Instruction Bus Error");
- EXC(EXC_DBE, "Data Bus Error");
- EXC(EXC_SYS, "Syscall");
- EXC(EXC_BP, "Breakpoint");
- EXC(EXC_RI, "Reserved Instruction");
- EXC(EXC_CPU, "Coprocessor Unusable");
- EXC(EXC_OV, "Overflow");
- EXC(EXC_TR, "Trap Instruction");
- EXC(EXC_FPE, "Floating Point Exception");
- EXC(EXC_C2E, "COP2 Exception");
- EXC(EXC_MDMX, "MDMX Exception");
- EXC(EXC_WATCH, "Watch Exception");
- EXC(EXC_MCHECK, "Machine Check Exception");
- EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
- default:
- return NULL;
- }
-}
-
-void exception_handler(void* stack_ptr, unsigned int cause, unsigned int epc)
-{
- panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)", parse_exception(cause), read_c0_badvaddr(), epc, (unsigned int)stack_ptr);
-}
-
-void tlb_refill_handler(void)
-{
- panicf("TLB refill handler at 0x%08lx! [0x%x]", read_c0_epc(), read_c0_badvaddr());
-}
-
#ifdef HW_UDELAY_TIMER
/* This enables the HW timer, set to EXT_XTAL / 4 (so @ 12/4 = 3MHz, 1 us = 3 ticks) */
static void init_delaytimer(void)
@@ -573,7 +533,7 @@ static void serial_setbrg(void)
*uart_lcr = tmp;
}
-int serial_preinit(void)
+static int serial_preinit(void)
{
volatile u8 *uart_fcr = (volatile u8 *)(CFG_UART_BASE + OFF_FCR);
volatile u8 *uart_lcr = (volatile u8 *)(CFG_UART_BASE + OFF_LCR);
@@ -609,7 +569,7 @@ int serial_preinit(void)
#define cpu_frequency CPU_FREQ
#endif
-void usb_preinit(void)
+static void usb_preinit(void)
{
/* Clear ECS bit of CPCCR, 0:clock source is EXCLK, 1:clock source is EXCLK/2 */
REG_CPM_CPCCR &= ~CPCCR_ECS;
@@ -661,7 +621,7 @@ void usb_preinit(void)
udelay(300);
}
-void dma_preinit(void)
+static void dma_preinit(void)
{
__cpm_start_mdma();
__cpm_start_dmac();
@@ -676,7 +636,7 @@ void dma_preinit(void)
}
/* Gets called *before* main */
-void ICODE_ATTR system_main(void)
+void ICODE_ATTR system_early_init(void)
{
int i;
diff --git a/firmware/target/mips/ingenic_jz47xx/system-target.h b/firmware/target/mips/ingenic_jz47xx/system-target.h
index 2725944452..1db0ee4671 100644
--- a/firmware/target/mips/ingenic_jz47xx/system-target.h
+++ b/firmware/target/mips/ingenic_jz47xx/system-target.h
@@ -27,6 +27,7 @@
#include "config.h"
#include "cpu.h"
#include "mipsregs.h"
+#include "system-mips.h"
#define CACHE_SIZE 16*1024
#define CACHEALIGN_BITS 5
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
index 474d45edee..8ff6d4bc1e 100644
--- a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
@@ -54,16 +54,14 @@
OUT = HOST->DEV, (ie we recv)
*/
-enum ep_type
-{
+enum ep_type {
ep_control,
ep_bulk,
ep_interrupt,
ep_isochronous
};
-struct usb_endpoint
-{
+struct usb_endpoint {
const enum ep_type type;
const long fifo_addr;
unsigned short fifo_size;
@@ -76,8 +74,7 @@ struct usb_endpoint
volatile void *buf;
volatile size_t length;
- union
- {
+ union {
volatile size_t sent;
volatile size_t received;
};
@@ -94,8 +91,7 @@ struct usb_endpoint
#define short_not_ok 1 /* only works for mass storage.. */
#define ep_doublebuf(__ep) 0
-static union
-{
+static union {
int buf[64 / sizeof(int)];
struct usb_ctrlrequest request;
} ep0_rx;
@@ -106,7 +102,7 @@ static volatile bool ep0_data_requested = false;
static struct usb_endpoint endpoints[] =
{
EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL),
- EP_INIT(ep_control, USB_FIFO_EP(0), 64, ep0_rx.buf),
+ EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL),
EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL),
EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL),
EP_INIT(ep_interrupt, USB_FIFO_EP(2), 512, NULL),
@@ -127,19 +123,14 @@ static void readFIFO(struct usb_endpoint *ep, unsigned int size)
register unsigned int s = size >> 2;
register unsigned int x;
- if(size > 0)
- {
- if( ((unsigned int)ptr & 3) == 0 )
- {
- while(s--)
+ if(size > 0) {
+ if (((unsigned int)ptr & 3) == 0) {
+ while(s--) {
*ptr32++ = REG32(ep->fifo_addr);
-
+ }
ptr = (unsigned char*)ptr32;
- }
- else
- {
- while(s--)
- {
+ } else {
+ while(s--) {
x = REG32(ep->fifo_addr);
*ptr++ = x & 0xFF; x >>= 8;
*ptr++ = x & 0xFF; x >>= 8;
@@ -147,10 +138,10 @@ static void readFIFO(struct usb_endpoint *ep, unsigned int size)
*ptr++ = x;
}
}
-
s = size & 3;
- while(s--)
+ while(s--) {
*ptr++ = REG8(ep->fifo_addr);
+ }
}
}
@@ -163,18 +154,14 @@ static void writeFIFO(struct usb_endpoint *ep, size_t size)
register size_t s = size >> 2;
register unsigned int x;
- if(size > 0)
- {
- if( ((unsigned int)d8 & 3) == 0 )
- {
- while (s--)
+ if (size > 0) {
+ if (((unsigned int)d8 & 3) == 0) {
+ while (s--) {
REG32(ep->fifo_addr) = *d32++;
+ }
d8 = (unsigned char *)d32;
- }
- else
- {
- while (s--)
- {
+ } else {
+ while (s--) {
x = (unsigned int)(*d8++) & 0xff;
x |= ((unsigned int)(*d8++) & 0xff) << 8;
x |= ((unsigned int)(*d8++) & 0xff) << 16;
@@ -182,11 +169,9 @@ static void writeFIFO(struct usb_endpoint *ep, size_t size)
REG32(ep->fifo_addr) = x;
}
}
-
- if( (s = size & 3) )
- {
- while (s--)
- REG8(ep->fifo_addr) = *d8++;
+ s = size & 3;
+ while (s--) {
+ REG8(ep->fifo_addr) = *d8++;
}
}
}
@@ -195,18 +180,17 @@ static void flushFIFO(struct usb_endpoint *ep)
{
logf("%s(%d)", __func__, EP_NUMBER(ep));
- switch (ep->type)
- {
- case ep_control:
+ switch (ep->type) {
+ case ep_control:
break;
-
- case ep_bulk:
- case ep_interrupt:
- case ep_isochronous:
- if(EP_IS_IN(ep))
- REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT);
- else
- REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT);
+ case ep_bulk:
+ case ep_interrupt:
+ case ep_isochronous:
+ if (EP_IS_IN(ep)) {
+ REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT);
+ } else {
+ REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT);
+ }
break;
}
}
@@ -217,8 +201,9 @@ static inline void ep_transfer_completed(struct usb_endpoint* ep)
ep->length = 0;
ep->buf = NULL;
ep->busy = false;
- if(ep->wait)
+ if (ep->wait) {
semaphore_release(&ep->complete);
+ }
}
static void EP0_send(void)
@@ -232,27 +217,26 @@ static void EP0_send(void)
logf("%s(): 0x%x %d %d", __func__, csr0, ep->sent, ep->length);
- if(ep->sent == 0)
- {
+ if (ep->sent == 0) {
length = MIN(ep->length, ep->fifo_size);
REG_USB_CSR0 = (csr0 | USB_CSR0_FLUSHFIFO);
- }
- else
+ } else {
length = MIN(EP_BUF_LEFT(ep), ep->fifo_size);
+ }
writeFIFO(ep, length);
ep->sent += length;
- if(ep->sent >= ep->length)
- {
+ if (ep->sent >= ep->length) {
REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */
- if (!ep->wait)
+ if (!ep->wait) {
usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, 0, ep->sent);
+ }
ep->rc = 0;
ep_transfer_completed(ep);
- }
- else
+ } else {
REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY);
+ }
}
static void EP0_handler(void)
@@ -270,8 +254,7 @@ static void EP0_handler(void)
/* Check for SentStall:
This bit is set when a STALL handshake is transmitted. The CPU should clear this bit.
*/
- if(csr0 & USB_CSR0_SENTSTALL)
- {
+ if (csr0 & USB_CSR0_SENTSTALL) {
REG_USB_CSR0 = csr0 & ~USB_CSR0_SENTSTALL;
return;
}
@@ -281,68 +264,62 @@ static void EP0_handler(void)
An interrupt will be generated and the FIFO flushed at this time.
The bit is cleared by the CPU writing a 1 to the ServicedSetupEnd bit.
*/
- if(csr0 & USB_CSR0_SETUPEND)
- {
+ if (csr0 & USB_CSR0_SETUPEND) {
csr0 |= USB_CSR0_SVDSETUPEND;
REG_USB_CSR0 = csr0;
ep0_data_supplied = false;
ep0_data_requested = false;
- if (ep_send->busy)
- {
+ if (ep_send->busy) {
if (!ep_send->wait)
usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0);
ep_transfer_completed(ep_send);
}
- if (ep_recv->busy)
- {
+ if (ep_recv->busy) {
usb_core_transfer_complete(EP_CONTROL, USB_DIR_OUT, -1, 0);
ep_transfer_completed(ep_recv);
}
}
/* Call relevant routines for endpoint 0 state */
- if(csr0 & USB_CSR0_OUTPKTRDY) /* There is a packet in the fifo */
- {
- if (ep_send->busy)
- {
- if (!ep_send->wait)
+ if (csr0 & USB_CSR0_OUTPKTRDY) { /* There is a packet in the fifo */
+ if (ep_send->busy) {
+ if (!ep_send->wait) {
usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0);
+ }
ep_transfer_completed(ep_send);
}
- if (ep_recv->busy && ep_recv->buf && ep_recv->length)
- {
+ if (ep_recv->busy && ep_recv->buf && ep_recv->length) {
unsigned int size = REG_USB_COUNT0;
readFIFO(ep_recv, size);
ep_recv->received += size;
- if (size < ep_recv->fifo_size || ep_recv->received >= ep_recv->length)
- {
+ if (size < ep_recv->fifo_size || ep_recv->received >= ep_recv->length) {
REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND; /* Set data end! */
usb_core_transfer_complete(EP_CONTROL, USB_DIR_OUT, 0, ep_recv->received);
ep_transfer_completed(ep_recv);
+ } else {
+ REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */
}
- else REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */
- }
- else if (!ep0_data_supplied)
- {
+ } else if (!ep0_data_supplied) {
ep_recv->buf = ep0_rx.buf;
readFIFO(ep_recv, REG_USB_COUNT0);
csr0 |= USB_CSR0_SVDOUTPKTRDY;
- if (!ep0_rx.request.wLength)
- {
+ if (!ep0_rx.request.wLength) {
csr0 |= USB_CSR0_DATAEND; /* Set data end! */
ep0_data_requested = false;
ep0_data_supplied = false;
- }
- else if (ep0_rx.request.bRequestType & USB_DIR_IN)
+ } else if (ep0_rx.request.bRequestType & USB_DIR_IN) {
ep0_data_requested = true;
- else ep0_data_supplied = true;
+ } else {
+ ep0_data_supplied = true;
+ }
REG_USB_CSR0 = csr0;
usb_core_legacy_control_request(&ep0_rx.request);
ep_transfer_completed(ep_recv);
}
}
- else if (ep_send->busy)
+ else if (ep_send->busy) {
EP0_send();
+ }
}
/* Does new work */
@@ -429,7 +406,7 @@ static void EPIN_send(unsigned int endpoint)
#endif
/* Non-DMA code */
- if(ep->sent == 0)
+ if (ep->sent == 0)
length = MIN(ep->length, ep->fifo_size);
else
length = MIN(EP_BUF_LEFT(ep), ep->fifo_size);
@@ -494,7 +471,7 @@ static void EPIN_complete(unsigned int endpoint)
logf("EP%d: %d -> %d", endpoint, ep->sent, ep->length);
- if(ep->sent >= ep->length) {
+ if (ep->sent >= ep->length) {
if (!ep->wait)
usb_core_transfer_complete(endpoint, USB_DIR_IN, 0, ep->sent);
ep->rc = 0;
@@ -516,9 +493,9 @@ static void EPOUT_handler(unsigned int endpoint)
}
select_endpoint(endpoint);
- while((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) {
+ while ((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) {
logf("%s(%d): 0x%x", __func__, endpoint, csr);
- if(csr & USB_OUTCSR_SENTSTALL) {
+ if (csr & USB_OUTCSR_SENTSTALL) {
logf("stall sent, flushing fifo..");
flushFIFO(ep);
REG_USB_OUTCSR = csr & ~USB_OUTCSR_SENTSTALL;
@@ -593,7 +570,7 @@ static void EPOUT_handler(unsigned int endpoint)
logf("received: %d max length: %d", ep->received, ep->length);
- if(size < ep->fifo_size || ep->received >= ep->length) {
+ if (size < ep->fifo_size || ep->received >= ep->length) {
usb_core_transfer_complete(endpoint, USB_DIR_OUT, 0, ep->received);
ep_transfer_completed(ep);
logf("receive transfer_complete");
@@ -613,8 +590,7 @@ static void EPOUT_ready(unsigned int endpoint)
select_endpoint(endpoint);
csr = REG_USB_OUTCSR;
- if(!ep->busy)
- {
+ if (!ep->busy) {
logf("Entered EPOUT handler without work!");
return;
}
@@ -717,15 +693,15 @@ static void setup_endpoint(struct usb_endpoint *ep)
select_endpoint(endpoint);
- if (ep->busy)
- {
- if(EP_IS_IN(ep))
- {
+ if (ep->busy) {
+ if (EP_IS_IN(ep)) {
if (ep->wait)
semaphore_release(&ep->complete);
- else usb_core_transfer_complete(endpoint, USB_DIR_IN, -1, 0);
+ else
+ usb_core_transfer_complete(endpoint, USB_DIR_IN, -1, 0);
+ } else {
+ usb_core_transfer_complete(endpoint, USB_DIR_OUT, -1, 0);
}
- else usb_core_transfer_complete(endpoint, USB_DIR_OUT, -1, 0);
}
ep->busy = false;
@@ -733,17 +709,16 @@ static void setup_endpoint(struct usb_endpoint *ep)
ep->sent = 0;
ep->length = 0;
- if(ep->type != ep_control)
+ if (ep->type != ep_control)
ep->fifo_size = usb_drv_port_speed() ? 512 : 64;
ep->config = REG_USB_CONFIGDATA;
- if(EP_IS_IN(ep))
- {
+ if(EP_IS_IN(ep)) {
csr = (USB_INCSR_FF | USB_INCSR_CDT);
csrh = USB_INCSRH_MODE;
- if(ep->type == ep_interrupt)
+ if (ep->type == ep_interrupt)
csrh |= USB_INCSRH_FRCDATATOG;
REG_USB_INMAXP = ep->fifo_size;
@@ -754,9 +729,7 @@ static void setup_endpoint(struct usb_endpoint *ep)
if (ep->allocated)
REG_USB_INTRINE |= USB_INTR_EP(EP_NUMBER2(ep));
- }
- else
- {
+ } else {
csr = (USB_OUTCSR_FF | USB_OUTCSR_CDT);
csrh = 0;
@@ -813,8 +786,7 @@ static void udc_reset(void)
endpoints[0].config = REG_USB_CONFIGDATA;
endpoints[1].config = REG_USB_CONFIGDATA;
- if (endpoints[0].busy)
- {
+ if (endpoints[0].busy) {
if (endpoints[0].wait)
semaphore_release(&endpoints[0].complete);
else
@@ -837,7 +809,7 @@ static void udc_reset(void)
endpoints[1].allocated = true;
/* Reset other endpoints */
- for(i=2; i<TOTAL_EP(); i++)
+ for (i=2; i<TOTAL_EP(); i++)
setup_endpoint(&endpoints[i]);
ep0_data_supplied = false;
@@ -864,26 +836,26 @@ void OTG(void)
logf("IRQ %x %x %x %x", intrUSB, intrIn, intrOut, intrDMA);
/* EPIN & EPOUT are all handled in DMA */
- if(intrIn & USB_INTR_EP(0))
+ if (intrIn & USB_INTR_EP(0))
EP0_handler();
- if(intrIn & USB_INTR_EP(1))
+ if (intrIn & USB_INTR_EP(1))
EPIN_complete(1);
- if(intrIn & USB_INTR_EP(2))
+ if (intrIn & USB_INTR_EP(2))
EPIN_complete(2);
- if(intrOut & USB_INTR_EP(1))
+ if (intrOut & USB_INTR_EP(1))
EPOUT_ready(1);
- if(intrOut & USB_INTR_EP(2))
+ if (intrOut & USB_INTR_EP(2))
EPOUT_ready(2);
- if(intrUSB & USB_INTR_RESET)
+ if (intrUSB & USB_INTR_RESET)
udc_reset();
- if(intrUSB & USB_INTR_SUSPEND)
+ if (intrUSB & USB_INTR_SUSPEND)
logf("USB suspend");
- if(intrUSB & USB_INTR_RESUME)
+ if (intrUSB & USB_INTR_RESUME)
logf("USB resume");
#ifdef USE_USB_DMA
- if(intrDMA & (1<<USB_INTR_DMA_BULKIN))
+ if (intrDMA & (1<<USB_INTR_DMA_BULKIN))
EPDMA_handler(USB_INTR_DMA_BULKIN);
- if(intrDMA & (1<<USB_INTR_DMA_BULKOUT))
+ if (intrDMA & (1<<USB_INTR_DMA_BULKOUT))
EPDMA_handler(USB_INTR_DMA_BULKOUT);
#endif
}
@@ -896,11 +868,10 @@ bool usb_drv_stalled(int endpoint, bool in)
select_endpoint(endpoint);
- if(endpoint == EP_CONTROL)
+ if (endpoint == EP_CONTROL) {
return (REG_USB_CSR0 & USB_CSR0_SENDSTALL) != 0;
- else
- {
- if(in)
+ } else {
+ if (in)
return (REG_USB_INCSR & USB_INCSR_SENDSTALL) != 0;
else
return (REG_USB_OUTCSR & USB_OUTCSR_SENDSTALL) != 0;
@@ -915,24 +886,18 @@ void usb_drv_stall(int endpoint, bool stall, bool in)
select_endpoint(endpoint);
- if(endpoint == EP_CONTROL)
- {
+ if(endpoint == EP_CONTROL) {
if(stall)
REG_USB_CSR0 |= USB_CSR0_SENDSTALL;
else
REG_USB_CSR0 &= ~USB_CSR0_SENDSTALL;
- }
- else
- {
- if(in)
- {
+ } else {
+ if(in) {
if(stall)
REG_USB_INCSR |= USB_INCSR_SENDSTALL;
else
REG_USB_INCSR = (REG_USB_INCSR & ~USB_INCSR_SENDSTALL) | USB_INCSR_CDT;
- }
- else
- {
+ } else {
if(stall)
REG_USB_OUTCSR |= USB_OUTCSR_SENDSTALL;
else
@@ -965,7 +930,7 @@ void usb_init_device(void)
system_enable_irq(IRQ_OTG);
- for(unsigned i=0; i<TOTAL_EP(); i++)
+ for (unsigned i=0; i<TOTAL_EP(); i++)
semaphore_init(&endpoints[i].complete, 1, 0);
}
@@ -979,7 +944,7 @@ static int usb_oneshot_callback(struct timeout *tmo)
* and post appropriate event. */
usb_status_event(state);
- if(state == USB_EXTRACTED)
+ if (state == USB_EXTRACTED)
__gpio_as_irq_rise_edge(PIN_USB_DET);
else
__gpio_as_irq_fall_edge(PIN_USB_DET);
@@ -996,7 +961,7 @@ void GPIO_USB_DET(void)
void usb_enable(bool on)
{
- if(on)
+ if (on)
usb_core_init();
else
usb_core_exit();
@@ -1070,7 +1035,7 @@ static void usb_drv_send_internal(struct usb_endpoint* ep, void* ptr, int length
ep->sent = 0;
ep->length = length;
ep->busy = true;
- if(blocking) {
+ if (blocking) {
ep->rc = -1;
ep->wait = true;
} else {
@@ -1085,7 +1050,7 @@ static void usb_drv_send_internal(struct usb_endpoint* ep, void* ptr, int length
restore_irq(flags);
- if(blocking) {
+ if (blocking) {
semaphore_wait(&ep->complete, HZ);
ep->wait = false;
}
@@ -1097,8 +1062,7 @@ int usb_drv_send_nonblocking(int endpoint, void* ptr, int length)
logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length);
- if (ep->allocated)
- {
+ if (ep->allocated) {
usb_drv_send_internal(ep, ptr, length, false);
return 0;
}
@@ -1112,8 +1076,7 @@ int usb_drv_send(int endpoint, void* ptr, int length)
logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length);
- if (ep->allocated)
- {
+ if (ep->allocated) {
usb_drv_send_internal(ep, ptr, length, true);
return ep->rc;
}
@@ -1188,7 +1151,8 @@ void usb_drv_cancel_all_transfers(void)
{
logf("%s()", __func__);
- unsigned int i, flags = disable_irq_save();
+ unsigned int i;
+ unsigned int flags = disable_irq_save();
#ifdef USE_USB_DMA
/* Disable DMA */
@@ -1204,7 +1168,8 @@ void usb_drv_cancel_all_transfers(void)
usb_core_transfer_complete(i >> 1, USB_DIR_OUT, -1, 0);
else if (endpoints[i].wait)
semaphore_release(&endpoints[i].complete);
- else usb_core_transfer_complete(i >> 1, USB_DIR_IN, -1, 0);
+ else
+ usb_core_transfer_complete(i >> 1, USB_DIR_IN, -1, 0);
}
if(i != 1) /* ep0 out needs special handling */
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
index 47b646be34..2c1c3a226e 100644
--- a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
+++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
@@ -532,6 +532,7 @@ void lcd_update(void)
const int column_high = get_column_high_byte(0);
const int column_low = get_column_low_byte(0);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -542,7 +543,7 @@ void lcd_update(void)
(column_low)
);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -586,6 +587,7 @@ void lcd_update_rect(int x, int y, int width, int height)
ymax = (y + height-1) >> 3;
y >>= 3;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -596,6 +598,6 @@ void lcd_update_rect(int x, int y, int width, int height)
(column_low)
);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/mips/ingenic_x1000/app.lds b/firmware/target/mips/ingenic_x1000/app.lds
index 26b2854728..fe06e1cd8d 100644
--- a/firmware/target/mips/ingenic_x1000/app.lds
+++ b/firmware/target/mips/ingenic_x1000/app.lds
@@ -5,6 +5,19 @@ OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_x1000/crt0.o)
+INPUT(target/mips/exception-mips.o)
+INPUT(target/mips/system-mips.o)
+
+#ifdef BOOTLOADER
+# undef PLUGIN_BUFFER_SIZE
+# undef CODEC_SIZE
+# define PLUGIN_BUFFER_SIZE 0
+# define CODEC_SIZE 0
+# if defined(HAVE_INIT_ATTR)
+/* This needs to be fixed for the bootloader */
+# error "bootloader does not support INIT_ATTR"
+# endif
+#endif
/* End of the audio buffer, where the codec buffer starts */
#define ENDAUDIOADDR (X1000_DRAM_END - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
@@ -12,20 +25,33 @@ STARTUP(target/mips/ingenic_x1000/crt0.o)
/* Where the codec buffer ends, and the plugin buffer starts */
#define ENDCODECADDR (ENDAUDIOADDR + CODEC_SIZE)
+/* Place init code in the codec buffer */
+#define INIT_BASE ENDAUDIOADDR
+#define INIT_SIZE CODEC_SIZE
+
MEMORY
{
IRAM : ORIGIN = X1000_IRAM_BASE, LENGTH = X1000_IRAM_SIZE
DRAM : ORIGIN = X1000_DRAM_BASE, LENGTH = X1000_DRAM_SIZE
+ TCSM : ORIGIN = X1000_TCSM_BASE, LENGTH = X1000_TCSM_SIZE
+ INIT : ORIGIN = INIT_BASE, LENGTH = INIT_SIZE
}
SECTIONS
{
- .text :
+ .startup :
{
loadaddress = .;
_loadaddress = .;
- *(.init.text);
+ *(.startup.text);
+ } > DRAM
+
+ .text :
+ {
*(.text*);
+#ifndef HAVE_INIT_ATTR
+ *(.init*);
+#endif
} > DRAM
. = ALIGN(4);
@@ -41,7 +67,13 @@ SECTIONS
*(.sdata*);
} > DRAM
- .iram X1000_IRAM_BASE: AT (_bssbegin)
+ /*
+ * The following sections are loaded after normal DRAM sections
+ * but are copied elsewhere by the startup code.
+ */
+ _noloaddram = .;
+
+ .iram :
{
_iramstart = .;
. = 0x000; /* TLB refill */
@@ -58,9 +90,29 @@ SECTIONS
*(.irodata);
*(.idata);
_iramend = .;
- } > IRAM
+ } > IRAM AT> DRAM
_iramcopy = LOADADDR(.iram);
+ .tcsm :
+ {
+ _tcsmstart = .;
+ KEEP(*(.tcsm*));
+ _tcsmend = .;
+ } > TCSM AT> DRAM
+ _tcsmcopy = LOADADDR(.tcsm);
+
+#ifdef HAVE_INIT_ATTR
+ .init :
+ {
+ _initstart = .;
+ *(.init*);
+ _initend = .;
+ } > INIT AT> DRAM
+ _initcopy = LOADADDR(.init);
+#endif
+
+ /* Sections below have no data. */
+
. = ALIGN(4);
.stack (NOLOAD) :
{
@@ -73,7 +125,7 @@ SECTIONS
_irqstackend = .;
} > IRAM
- .bss (NOLOAD) :
+ .bss _noloaddram (NOLOAD) :
{
_bssbegin = .;
*(.sbss*);
@@ -88,10 +140,8 @@ SECTIONS
{
. = ALIGN(4);
audiobuffer = .;
- loadbuffer = .;
} > DRAM
- loadbufferend = ENDAUDIOADDR;
audiobufend = ENDAUDIOADDR;
codecbuf = ENDAUDIOADDR;
pluginbuf = ENDCODECADDR;
diff --git a/firmware/target/mips/ingenic_x1000/boot-x1000.c b/firmware/target/mips/ingenic_x1000/boot-x1000.c
new file mode 100644
index 0000000000..2f2714c67a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.c
@@ -0,0 +1,285 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "usb.h"
+#include "boot-x1000.h"
+#include "nand-x1000.h"
+#include "gpio-x1000.h"
+#include "clk-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/lcd.h"
+#include "x1000/uart.h"
+#include <string.h>
+
+#define HDR_BEGIN 128 /* header must begin within this many bytes */
+#define HDR_LEN 256 /* header length cannot exceed this */
+
+/* search for header value, label must be a 4-character string.
+ * Returns the found value or 0 if the label wasn't found. */
+static uint32_t search_header(const unsigned char* source, size_t length,
+ const char* label)
+{
+ size_t search_len = MIN(length, HDR_BEGIN);
+ if(search_len < 8)
+ return 0;
+ search_len -= 7;
+
+ /* find the beginning marker */
+ size_t i;
+ for(i = 8; i < search_len; i += 4)
+ if(!memcmp(&source[i], "BEGINHDR", 8))
+ break;
+ if(i >= search_len)
+ return 0;
+ i += 8;
+
+ /* search within the header */
+ search_len = MIN(length, i + HDR_LEN) - 7;
+ for(; i < search_len; i += 8) {
+ if(!memcmp(&source[i], "ENDH", 4)) {
+ break;
+ } else if(!memcmp(&source[i], label, 4)) {
+ i += 4;
+ /* read little-endian value */
+ uint32_t ret = source[i];
+ ret |= source[i+1] << 8;
+ ret |= source[i+2] << 16;
+ ret |= source[i+3] << 24;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void iram_memmove(void* dest, const void* source, size_t length)
+ __attribute__((section(".icode")));
+
+static void iram_memmove(void* dest, const void* source, size_t length)
+{
+ unsigned char* d = dest;
+ const unsigned char* s = source;
+
+ if(s < d && d < s + length) {
+ d += length;
+ s += length;
+ while(length--)
+ *--d = *--s;
+ } else {
+ while(length--)
+ *d++ = *s++;
+ }
+}
+
+void x1000_boot_rockbox(const void* source, size_t length)
+{
+ uint32_t load_addr = search_header(source, length, "LOAD");
+ if(!load_addr)
+ load_addr = X1000_STANDARD_DRAM_BASE;
+
+ disable_irq();
+
+ /* --- Beyond this point, do not call into DRAM --- */
+
+ iram_memmove((void*)load_addr, source, length);
+ commit_discard_idcache();
+
+ typedef void(*entry_fn)(void);
+ entry_fn fn = (entry_fn)load_addr;
+ fn();
+ while(1);
+}
+
+void x1000_boot_linux(const void* source, size_t length,
+ void* load, void* entry, const char* args)
+{
+ size_t args_len = strlen(args);
+
+ /* Shut off USB to avoid "irq 21 nobody cared" error */
+ usb_close();
+ usb_enable(false);
+
+ /* clear USB PHY voodoo bits, not all kernels use them */
+ jz_writef(CPM_OPCR, GATE_USBPHY_CLK(0));
+ jz_writef(CPM_USBCDR, PHY_GATE(0));
+
+ disable_irq();
+
+ /* --- Beyond this point, do not call into DRAM --- */
+
+ void* safe_mem = (void*)X1000_IRAM_END;
+
+ /* copy argument string to a safe location */
+ char* args_copy = safe_mem + 32;
+ iram_memmove(args_copy, args, args_len+1);
+
+ /* generate argv array */
+ char** argv = safe_mem;
+ argv[0] = NULL;
+ argv[1] = args_copy;
+
+ iram_memmove(load, source, length);
+ commit_discard_idcache();
+
+ typedef void(*entry_fn)(long, char**, long, long);
+ entry_fn fn = (entry_fn)entry;
+ fn(2, argv, 0, 0);
+ while(1);
+}
+
+void rolo_restart(const unsigned char* source, unsigned char* dest, int length)
+{
+ (void)dest;
+ x1000_boot_rockbox(source, length);
+}
+
+void x1000_dualboot_cleanup(void)
+{
+ /* - disable all LCD interrupts since the M3K can't cope with them
+ * - disable BEDN bit since it creates garbled graphics on the Q1 */
+ jz_writef(CPM_CLKGR, LCD(0));
+ jz_writef(LCD_CTRL, BEDN(0), EOFM(0), SOFM(0), IFUM(0), QDM(0));
+ jz_writef(CPM_CLKGR, LCD(1));
+
+#if defined(FIIO_M3K) || defined(EROS_QN)
+ /*
+ * Need to bring up MPLL before booting Linux
+ * (Doesn't apply to Q1 since it sticks with the default Ingenic config)
+ */
+
+ /* 24 MHz * 25 = 600 MHz */
+ jz_writef(CPM_MPCR, BS(1), PLLM(25 - 1), PLLN(0), PLLOD(0), ENABLE(1));
+ while(jz_readf(CPM_MPCR, ON) == 0);
+
+ /* 600 MHz / 3 = 200 MHz */
+ clk_set_ddr(X1000_CLK_MPLL, 3);
+
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(MPLL) |
+ CLKMUX_AHB2(MPLL));
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(3) | /* 200 MHz */
+ CLKDIV_AHB2(3) | /* 200 MHz */
+ CLKDIV_PCLK(6)); /* 100 MHz */
+#endif
+}
+
+void x1000_dualboot_init_clocktree(void)
+{
+ /* Make sure these are gated to match the OF behavior. */
+ jz_writef(CPM_CLKGR, PCM(1), MAC(1), LCD(1), MSC0(1), MSC1(1), OTG(1), CIM(1));
+
+ /* Set clock sources, and make sure every clock starts out stopped */
+ jz_writef(CPM_I2SCDR, CS_V(EXCLK));
+ jz_writef(CPM_PCMCDR, CS_V(EXCLK));
+
+ jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MACCDR, BUSY));
+
+ jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_LPCDR, BUSY));
+
+ jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MSC0CDR, BUSY));
+
+ jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MSC1CDR, BUSY));
+
+ jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_CIMCDR, BUSY));
+
+ jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
+ while(jz_readf(CPM_USBCDR, BUSY));
+}
+
+void x1000_dualboot_init_uart2(void)
+{
+ /* Ungate the clock and select UART2 device function */
+ jz_writef(CPM_CLKGR, UART2(0));
+ gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
+
+ /* Disable all interrupts */
+ jz_write(UART_UIER(2), 0);
+
+ /* FIFO configuration */
+ jz_overwritef(UART_UFCR(2),
+ RDTR(3), /* FIFO trigger level = 60? */
+ UME(0), /* UART module disable */
+ DME(1), /* DMA mode enable? */
+ TFRT(1), /* transmit FIFO reset */
+ RFRT(1), /* receive FIFO reset */
+ FME(1)); /* FIFO mode enable */
+
+ /* IR mode configuration */
+ jz_overwritef(UART_ISR(2),
+ RDPL(1), /* Zero is negative pulse for receive */
+ TDPL(1), /* ... and for transmit */
+ XMODE(1), /* Pulse width 1.6us */
+ RCVEIR(0), /* Disable IR for recieve */
+ XMITIR(0)); /* ... and for transmit */
+
+ /* Line configuration */
+ jz_overwritef(UART_ULCR(2), DLAB(0),
+ WLS_V(8BITS), /* 8 bit words */
+ SBLS_V(1_STOP_BIT), /* 1 stop bit */
+ PARE(0), /* no parity */
+ SBK(0)); /* don't set break */
+
+ /* Set the baud rate... not too sure how this works. (Docs unclear!) */
+ const unsigned divisor = 0x0004;
+ jz_writef(UART_ULCR(2), DLAB(1));
+ jz_write(UART_UDLHR(2), (divisor >> 8) & 0xff);
+ jz_write(UART_UDLLR(2), divisor & 0xff);
+ jz_write(UART_UMR(2), 16);
+ jz_write(UART_UACR(2), 0);
+ jz_writef(UART_ULCR(2), DLAB(0));
+
+ /* Enable UART */
+ jz_overwritef(UART_UFCR(2),
+ RDTR(0), /* FIFO trigger level = 1 */
+ DME(0), /* DMA mode disable */
+ UME(1), /* UART module enable */
+ TFRT(1), /* transmit FIFO reset */
+ RFRT(1), /* receive FIFO reset */
+ FME(1)); /* FIFO mode enable */
+}
+
+int x1000_dualboot_load_pdma_fw(void)
+{
+ struct nand_drv* n = nand_init();
+ nand_lock(n);
+
+ int ret = nand_open(n);
+ if(ret != NAND_SUCCESS)
+ goto err_unlock;
+
+ /* NOTE: hardcoded address is used by all current targets */
+ jz_writef(CPM_CLKGR, PDMA(0));
+ ret = nand_read_bytes(n, 0x4000, 0x2000, (void*)0xb3422000);
+
+ nand_close(n);
+ err_unlock:
+ nand_unlock(n);
+ return ret;
+}
diff --git a/firmware/target/mips/ingenic_x1000/boot-x1000.h b/firmware/target/mips/ingenic_x1000/boot-x1000.h
index 400eb93dc1..eb476c513d 100644
--- a/firmware/target/mips/ingenic_x1000/boot-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.h
@@ -24,18 +24,10 @@
#include "x1000/cpm.h"
#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
enum {
- BOOT_OPTION_ROCKBOX = 0,
- BOOT_OPTION_OFW_PLAYER,
- BOOT_OPTION_OFW_RECOVERY,
-};
-
-enum {
- /* 3 bits to store the boot option selected by the SPL */
- BOOT_OPTION_MASK = 0x7,
- BOOT_OPTION_SHIFT = 0,
-
/* Set after running clk_init() and setting up system clocks */
BOOT_FLAG_CLK_INIT = (1 << 31),
@@ -43,6 +35,18 @@ enum {
BOOT_FLAG_USB_BOOT = (1 << 30),
};
+void x1000_boot_rockbox(const void* source, size_t length)
+ __attribute__((section(".icode.x1000_boot_rockbox")));
+void x1000_boot_linux(const void* source, size_t length,
+ void* load, void* entry, const char* args)
+ __attribute__((section(".icode.x1000_boot_linux")));
+
+/* dual boot support code */
+void x1000_dualboot_cleanup(void);
+void x1000_dualboot_init_clocktree(void);
+void x1000_dualboot_init_uart2(void);
+int x1000_dualboot_load_pdma_fw(void);
+
/* Note: these functions are inlined to minimize SPL code size.
* They are private to the X1000 early boot code anyway... */
@@ -74,18 +78,4 @@ static inline void clr_boot_flag(uint32_t bit)
cpm_scratch_set(REG_CPM_SCRATCH & ~bit);
}
-static inline void set_boot_option(int opt)
-{
- uint32_t r = REG_CPM_SCRATCH;
- r &= ~(BOOT_OPTION_MASK << BOOT_OPTION_SHIFT);
- r |= (opt & BOOT_OPTION_MASK) << BOOT_OPTION_SHIFT;
- cpm_scratch_set(r);
-}
-
-static inline int get_boot_option(void)
-{
- uint32_t r = REG_CPM_SCRATCH;
- return (r >> BOOT_OPTION_SHIFT) & BOOT_OPTION_MASK;
-}
-
#endif /* __BOOT_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
index 4988e7c3bf..e3b0f792bb 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -265,39 +265,36 @@ void clk_init(void)
jz_writef(CPM_APCR, BS(1), PLLM(42 - 1), PLLN(0), PLLOD(0), ENABLE(1));
while(jz_readf(CPM_APCR, ON) == 0);
-#if (defined(FIIO_M3K) || defined(EROS_QN))
+#if defined(FIIO_M3K) || defined(EROS_QN)
/* TODO: Allow targets to define their clock frequencies in their config,
* instead of having this be a random special case. */
- if(get_boot_option() == BOOT_OPTION_ROCKBOX) {
- clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
- CLKDIV_L2(2) | /* 504 MHz */
- CLKDIV_AHB0(5) | /* 201.6 MHz */
- CLKDIV_AHB2(5) | /* 201.6 MHz */
- CLKDIV_PCLK(10)); /* 100.8 MHz */
- clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
- CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(SCLK_A) |
- CLKMUX_AHB2(SCLK_A));
-
- /* DDR to 201.6 MHz */
- clk_set_ddr(X1000_CLK_SCLK_A, 5);
-
- /* Disable MPLL */
- jz_writef(CPM_MPCR, ENABLE(0));
- while(jz_readf(CPM_MPCR, ON));
- } else {
-#endif
- clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
- CLKDIV_L2(2) | /* 504 MHz */
- CLKDIV_AHB0(3) | /* 200 MHz */
- CLKDIV_AHB2(3) | /* 200 MHz */
- CLKDIV_PCLK(6)); /* 100 MHz */
- clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
- CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(MPLL) |
- CLKMUX_AHB2(MPLL));
-#if (defined(FIIO_M3K) || defined(EROS_QN))
- }
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(5) | /* 201.6 MHz */
+ CLKDIV_AHB2(5) | /* 201.6 MHz */
+ CLKDIV_PCLK(10)); /* 100.8 MHz */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) |
+ CLKMUX_AHB2(SCLK_A));
+
+ /* DDR to 201.6 MHz */
+ clk_set_ddr(X1000_CLK_SCLK_A, 5);
+
+ /* Disable MPLL */
+ jz_writef(CPM_MPCR, ENABLE(0));
+ while(jz_readf(CPM_MPCR, ON));
+#else
+ /* Default configuration matching the Ingenic OF */
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(3) | /* 200 MHz */
+ CLKDIV_AHB2(3) | /* 200 MHz */
+ CLKDIV_PCLK(6)); /* 100 MHz */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(MPLL) |
+ CLKMUX_AHB2(MPLL));
#endif
/* mark that clocks have been initialized */
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h
index f7153da564..4b1d4ef838 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.h
@@ -22,8 +22,9 @@
#ifndef __CLK_X1000_H__
#define __CLK_X1000_H__
-#include <stdint.h>
+#include "config.h"
#include "x1000/cpm.h"
+#include <stdint.h>
/* Used as arguments to clk_set_ccr_mux() */
#define CLKMUX_SCLK_A(x) jz_orf(CPM_CCR, SEL_SRC_V(x))
@@ -67,17 +68,17 @@ extern uint32_t clk_get(x1000_clk_t clk);
extern const char* clk_get_name(x1000_clk_t clk);
/* Clock initialization */
-extern void clk_init_early(void);
-extern void clk_init(void);
+extern void clk_init_early(void) INIT_ATTR;
+extern void clk_init(void) INIT_ATTR;
/* Sets system clock multiplexers */
-extern void clk_set_ccr_mux(uint32_t muxbits);
+extern void clk_set_ccr_mux(uint32_t muxbits) INIT_ATTR;
/* Sets system clock dividers */
-extern void clk_set_ccr_div(uint32_t divbits);
+extern void clk_set_ccr_div(uint32_t divbits) INIT_ATTR;
/* Sets DDR clock source and divider */
-extern void clk_set_ddr(x1000_clk_t src, uint32_t div);
+extern void clk_set_ddr(x1000_clk_t src, uint32_t div) INIT_ATTR;
/* Returns the smallest n such that infreq/n <= outfreq */
static inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq)
diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S
index 304f8d682f..6c0942b0db 100644
--- a/firmware/target/mips/ingenic_x1000/crt0.S
+++ b/firmware/target/mips/ingenic_x1000/crt0.S
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,10 +21,12 @@
#include "config.h"
#include "mips.h"
+#include "bootdata.h"
.text
.extern main
.extern system_early_init
+ .extern _loadaddress
.global _start
.set push
@@ -32,204 +34,126 @@
.set noreorder
.set noat
- .section .init.text
+ .section .startup.text,"ax",%progbits
_start:
- /* Cache init */
- li v0, 0x80000000
- ori v1, v0, 0x4000
- mtc0 zero, C0_TAGLO
- mtc0 zero, C0_TAGHI
-_cache_loop:
- cache ICIndexStTag, 0(v0)
- cache DCIndexStTag, 0(v0)
- addiu v0, v0, 32
- bne v0, v1, _cache_loop
- nop
+ b _realstart
+ nop
+
+ /* Header entries are 4-byte string labels (not null terminated!) followed
+ * by 4-byte values. Header should begin in the first 128 bytes and should
+ * be no more than 256 bytes in length. */
+_header:
+ .ascii "BEGINHDR" /* beginning of header */
+ .ascii "LOAD"
+ .word _loadaddress
+ .ascii "ENDH" /* end of header structure */
+
+#ifndef BOOTLOADER
+ /* Multiboot support header; this is not part of the above header. */
+ put_boot_data_here
+#endif
+
+_realstart:
+ /* Save bootloader arguments. */
+ move s0, a0
+ move s1, a1
+ move s2, a2
+ move s3, a3
- /* Invalidate BTB */
- mfc0 v0, C0_Config, 7
+ /* Copy IRAM from BSS to low memory. */
+ la a0, _iramcopy
+ la a1, _iramstart
+ la a2, _iramend
+ bal _copy
nop
- ori v0, v0, 2
- mtc0 v0, C0_Config, 7
+
+ /* Copy TCSM from BSS */
+ la a0, _tcsmcopy
+ la a1, _tcsmstart
+ la a2, _tcsmend
+ bal _copy
nop
- /* Copy IRAM from BSS to low memory. */
- la t0, _iramcopy
- la t1, _iramstart
- la t2, _iramend
-_iram_loop:
- lw t3, 0(t0)
- addiu t1, 4
- addiu t0, 4
- bne t1, t2, _iram_loop
- sw t3, -4(t1)
+#ifdef HAVE_INIT_ATTR
+ /* Copy init code */
+ la a0, _initcopy
+ la a1, _initstart
+ la a2, _initend
+ bal _copy
+ nop
+#endif
/* Clear the BSS segment (needed to zero-initialize C static values) */
- la t0, _bssbegin
- la t1, _bssend
- beq t0, t1, _bss_done
-_bss_loop:
- addiu t0, 4
- bne t0, t1, _bss_loop
- sw zero, -4(t0)
-_bss_done:
+ la a0, _bssbegin
+ la a1, _bssend
+ bal _clear
+ move a2, $0
/* Set stack pointer and clear the stack */
la sp, stackend
- la t0, stackbegin
- li t1, 0xDEADBEEF
-_stack_loop:
- addiu t0, 4
- bne t0, sp, _stack_loop
- sw t1, -4(t0)
+ la a0, stackbegin
+ li a2, 0xDEADBEEF
+ bal _clear
+ move a1, sp
/* Clear the IRQ stack */
la k0, _irqstackend
- la t0, _irqstackbegin
-_irqstack_loop:
- addiu t0, 4
- bne t0, k0, _irqstack_loop
- sw t1, -4(t0)
+ la a0, _irqstackbegin
+ bal _clear
+ move a1, k0
- /* Jump to C code */
- jal system_early_init
- nop
- j main
- nop
-
- /* Exception entry points */
- .section .vectors.1, "ax", %progbits
- j tlb_refill_handler
- nop
-
- .section .vectors.2, "ax", %progbits
- j real_exception_handler
- nop
+ /* Write back D-cache and invalidate I-cache */
+ li v0, 0x80000000
+ ori v1, v0, (0x4000 - 32)
+ mtc0 zero, C0_TAGLO
+ mtc0 zero, C0_TAGHI
+1:
+ cache DCIndexWBInv, 0(v0)
+ cache ICIndexStTag, 0(v0)
+ bne v0, v1, 1b
+ addiu v0, v0, 32
- .section .vectors.3, "ax", %progbits
- j real_exception_handler
+ /* Invalidate BTB */
+ mfc0 v0, C0_Config, 7
nop
-
- .section .vectors.4, "ax", %progbits
- j real_exception_handler
+ ori v0, v0, 2
+ mtc0 v0, C0_Config, 7
nop
- .section .vectors, "ax", %progbits
-real_exception_handler:
- move k0, sp
- la sp, _irqstackend
- addiu sp, -0x84
- sw k0, 0x80(sp)
- sw ra, 0x00(sp)
- sw fp, 0x04(sp)
- sw gp, 0x08(sp)
- sw t9, 0x0c(sp)
- sw t8, 0x10(sp)
- sw s7, 0x14(sp)
- sw s6, 0x18(sp)
- sw s5, 0x1c(sp)
- sw s4, 0x20(sp)
- sw s3, 0x24(sp)
- sw s2, 0x28(sp)
- sw s1, 0x2c(sp)
- sw s0, 0x30(sp)
- sw t7, 0x34(sp)
- sw t6, 0x38(sp)
- sw t5, 0x3c(sp)
- sw t4, 0x40(sp)
- sw t3, 0x44(sp)
- sw t2, 0x48(sp)
- sw t1, 0x4c(sp)
- sw t0, 0x50(sp)
- sw a3, 0x54(sp)
- sw a2, 0x58(sp)
- sw a1, 0x5c(sp)
- sw a0, 0x60(sp)
- sw v1, 0x64(sp)
- sw v0, 0x68(sp)
- sw $1, 0x6c(sp)
- mflo k0
- nop
- sw k0, 0x70(sp)
- mfhi k0
- nop
- sw k0, 0x74(sp)
- mfc0 k0, C0_STATUS
- nop
- nop
- nop
- sw k0, 0x78(sp)
- mfc0 k0, C0_EPC
- nop
- nop
+ /* Jump to C code */
+ jal system_early_init
nop
- sw k0, 0x7c(sp)
- li k1, M_CauseExcCode
- mfc0 a0, C0_CAUSE
- and k0, a0, k1
- bnez k0, _exception
- nop
- jal intr_handler
- nop
- j _exception_return
+ /* Restore bootloader arguments, jump to main. */
+ move a0, s0
+ move a1, s1
+ move a2, s2
+ move a3, s3
-_exception:
- mfc0 a1, C0_EPC
- nop
- nop
- nop
- jal exception_handler
- move a2, sp
-
-_exception_return:
- lw ra, 0x00(sp)
- lw fp, 0x04(sp)
- lw gp, 0x08(sp)
- lw t9, 0x0c(sp)
- lw t8, 0x10(sp)
- lw s7, 0x14(sp)
- lw s6, 0x18(sp)
- lw s5, 0x1c(sp)
- lw s4, 0x20(sp)
- lw s3, 0x24(sp)
- lw s2, 0x28(sp)
- lw s1, 0x2c(sp)
- lw s0, 0x30(sp)
- lw t7, 0x34(sp)
- lw t6, 0x38(sp)
- lw t5, 0x3c(sp)
- lw t4, 0x40(sp)
- lw t3, 0x44(sp)
- lw t2, 0x48(sp)
- lw t1, 0x4c(sp)
- lw t0, 0x50(sp)
- lw a3, 0x54(sp)
- lw a2, 0x58(sp)
- lw a1, 0x5c(sp)
- lw a0, 0x60(sp)
- lw v1, 0x64(sp)
- lw v0, 0x68(sp)
- lw $1, 0x6c(sp)
- lw k0, 0x70(sp)
- mtlo k0
- nop
- lw k0, 0x74(sp)
- mthi k0
- nop
- lw k0, 0x78(sp)
- mtc0 k0, C0_STATUS
- nop
- nop
- nop
- lw k0, 0x7c(sp)
- mtc0 k0, C0_EPC
- nop
- nop
- nop
- lw sp, 0x80(sp)
- eret
+ j main
+ move ra, zero /* init backtrace root */
+
+ /* copy(void* src, void* dst, void* dst_end) */
+_copy:
+ beq a1, a2, 1f
+ addiu a1, 4
+ lw t0, 0(a0)
+ addiu a0, 4
+ b _copy
+ sw t0, -4(a1)
+1:
+ jr ra
+ nop
+
+ /* clear(void* dst, void* dst_end, int value) */
+_clear:
+ beq a0, a1, 1f
+ addiu a0, 4
+ b _clear
+ sw a2, -4(a0)
+1:
+ jr ra
nop
.set pop
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c
index 98b8f95fb5..827bb37855 100644
--- a/firmware/target/mips/ingenic_x1000/debug-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c
@@ -118,12 +118,31 @@ static bool dbg_gpios(void)
}
extern volatile unsigned aic_tx_underruns;
+#ifdef HAVE_RECORDING
+extern volatile unsigned aic_rx_overruns;
+#endif
+#ifdef HAVE_EROS_QN_CODEC
+extern int es9018k2m_present_flag;
+#endif
static bool dbg_audio(void)
{
do {
lcd_clear_display();
lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns);
+#ifdef HAVE_RECORDING
+ lcd_putsf(0, 1, "RX overruns: %u", aic_rx_overruns);
+#endif
+#ifdef HAVE_EROS_QN_CODEC
+ if (es9018k2m_present_flag)
+ {
+ lcd_putsf(0, 2, "(%d) ES9018K2M HWVOL", es9018k2m_present_flag);
+ }
+ else
+ {
+ lcd_putsf(0, 2, "(%d) SWVOL", es9018k2m_present_flag);
+ }
+#endif
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
diff --git a/firmware/target/mips/ingenic_x1000/dma-x1000.h b/firmware/target/mips/ingenic_x1000/dma-x1000.h
index 8bb5f1ddaa..4a526cec02 100644
--- a/firmware/target/mips/ingenic_x1000/dma-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/dma-x1000.h
@@ -64,7 +64,7 @@ typedef struct dma_desc dma_desc;
typedef void(*dma_cb_func)(int event);
-extern void dma_init(void);
+extern void dma_init(void) INIT_ATTR;
extern void dma_set_callback(int chn, dma_cb_func cb);
#endif /* __DMA_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
index b32a32a3a3..df97aba0c8 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
@@ -19,28 +19,36 @@
*
****************************************************************************/
-#include "audiohw.h"
#include "system.h"
+#include "audiohw.h"
+#include "pcm_sw_volume.h"
#include "pcm_sampr.h"
+#include "i2c-target.h"
+#include "button.h"
+
+// #define LOGF_ENABLE
+#include "logf.h"
+
#include "aic-x1000.h"
#include "i2c-x1000.h"
#include "gpio-x1000.h"
-#include "logf.h"
-/* Audio path appears to be:
- * DAC --> HP Amp --> Stereo Switch --> HP OUT
- * \--> LO OUT
+/*
+ * Earlier devices audio path appears to be:
+ * DAC \--> HP Amp --> Stereo Switch --> HP OUT
+ * \-> LO OUT
*
- * The real purpose of the Stereo Switch is not clear.
- * It appears to switch sources between the HP amp and something,
- * likely something unimplemented. */
+ * Recent devices, the audio path seems to have changed to:
+ * DAC --> HP Amp --> Stereo Switch \--> HP OUT
+ * \-> LO OUT
+ */
void audiohw_init(void)
{
/* explicitly mute everything */
- gpio_set_level(GPIO_MAX97220_SHDN, 0);
- gpio_set_level(GPIO_ISL54405_MUTE, 1);
- gpio_set_level(GPIO_PCM5102A_XMIT, 0);
+ gpio_set_level(GPIO_HPAMP_SHDN, 0);
+ gpio_set_level(GPIO_STEREOSW_MUTE, 1);
+ gpio_set_level(GPIO_DAC_PWR, 0);
aic_set_play_last_sample(true);
aic_set_external_codec(true);
@@ -53,8 +61,8 @@ void audiohw_init(void)
mdelay(10);
/* power on DAC and HP Amp */
- gpio_set_level(GPIO_PCM5102A_ANALOG_PWR, 1);
- gpio_set_level(GPIO_MAX97220_POWER, 1);
+ gpio_set_level(GPIO_DAC_ANALOG_PWR, 1);
+ gpio_set_level(GPIO_HPAMP_POWER, 1);
}
void audiohw_postinit(void)
@@ -76,23 +84,39 @@ void audiohw_postinit(void)
jz_writef(AIC_CCR, ERPL(0));
/* unmute - attempt to make power-on pop-free */
- gpio_set_level(GPIO_ISL54405_SEL, 0);
- gpio_set_level(GPIO_MAX97220_SHDN, 1);
+ gpio_set_level(GPIO_STEREOSW_SEL, 0);
+ gpio_set_level(GPIO_HPAMP_SHDN, 1);
mdelay(10);
- gpio_set_level(GPIO_PCM5102A_XMIT, 1);
+ gpio_set_level(GPIO_DAC_PWR, 1);
mdelay(10);
- gpio_set_level(GPIO_ISL54405_MUTE, 0);
+ gpio_set_level(GPIO_STEREOSW_MUTE, 0);
+
+ i2c_x1000_set_freq(ES9018K2M_BUS, I2C_FREQ_400K);
+
+ int ret = es9018k2m_read_reg(ES9018K2M_REG0_SYSTEM_SETTINGS);
+ if (ret >= 0) /* Detected ES9018K2M DAC */
+ {
+ logf("ES9018K2M found: ret=%d", ret);
+ es9018k2m_present_flag = 1;
+
+ /* Default is 32-bit data, and it works ok. Enabling the following
+ * causes issue. Which is weird, I definitely thought AIC was configured
+ * for 24-bit data... */
+ // es9018k2m_write_reg(ES9018K2M_REG1_INPUT_CONFIG, 0b01001100); // 24-bit data
+
+ } else { /* Default to SWVOL for PCM5102A DAC */
+ logf("Default to SWVOL: ret=%d", ret);
+ }
}
-/* TODO: get shutdown just right according to dac datasheet */
void audiohw_close(void)
{
/* mute - attempt to make power-off pop-free */
- gpio_set_level(GPIO_ISL54405_MUTE, 1);
+ gpio_set_level(GPIO_STEREOSW_MUTE, 1);
mdelay(10);
- gpio_set_level(GPIO_PCM5102A_XMIT, 0);
+ gpio_set_level(GPIO_DAC_PWR, 0);
mdelay(10);
- gpio_set_level(GPIO_MAX97220_SHDN, 0);
+ gpio_set_level(GPIO_HPAMP_SHDN, 0);
}
void audiohw_set_frequency(int fsel)
@@ -105,3 +129,54 @@ void audiohw_set_frequency(int fsel)
aic_enable_i2s_bit_clock(true);
}
+void audiohw_set_volume(int vol_l, int vol_r)
+{
+ int l, r;
+
+ eros_qn_set_last_vol(vol_l, vol_r);
+
+ l = vol_l;
+ r = vol_r;
+
+#if (defined(HAVE_HEADPHONE_DETECTION) && defined(HAVE_LINEOUT_DETECTION))
+ /* make sure headphones aren't present - don't want to
+ * blow out our eardrums cranking it to full */
+ if (lineout_inserted() && !headphones_inserted())
+ {
+ eros_qn_switch_output(1);
+
+ l = r = eros_qn_get_volume_limit();
+ }
+ else
+ {
+ eros_qn_switch_output(0);
+ }
+#endif
+
+ if (es9018k2m_present_flag) /* ES9018K2M */
+ {
+ /* Same volume range and mute point for both DACs, so use PCM5102A_VOLUME_MIN */
+ l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : l;
+ r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : r;
+
+ /* set software volume just below unity due to
+ * DAC offset. We don't want to overflow the PCM system. */
+ pcm_set_master_volume(-1, -1);
+ es9018k2m_set_volume_async(l, r);
+ }
+ else /* PCM5102A */
+ {
+ l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (l / 20);
+ r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (r / 20);
+
+ pcm_set_master_volume(l, r);
+ }
+}
+
+void audiohw_set_filter_roll_off(int value)
+{
+ if (es9018k2m_present_flag)
+ {
+ es9018k2m_set_filter_roll_off(value);
+ }
+} \ No newline at end of file
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
index 1583db175a..0d2207af2a 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
@@ -127,7 +127,7 @@ bool headphones_inserted(void)
{
hp_detect_reg_old = hp_detect_reg;
#if !defined(BOOTLOADER)
- pcm5102_set_outputs();
+ eros_qn_set_outputs();
#endif
}
return hp_detect_reg & 0x10 ? false : true;
@@ -140,7 +140,7 @@ bool lineout_inserted(void)
{
hp_detect_reg_old = hp_detect_reg;
#if !defined(BOOTLOADER)
- pcm5102_set_outputs();
+ eros_qn_set_outputs();
#endif
}
return hp_detect_reg & 0x20 ? false : true;
@@ -233,6 +233,8 @@ int button_read_device(void)
* Rockbox treats these buttons differently. */
queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
enc_position = 0;
+ reset_poweroff_timer();
+ backlight_on();
}
else if (enc_position < -1)
{
@@ -240,6 +242,8 @@ int button_read_device(void)
* Rockbox treats these buttons differently. */
queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
enc_position = 0;
+ reset_poweroff_timer();
+ backlight_on();
}
return r;
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
index 376eae136e..72052c261f 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
@@ -15,35 +15,39 @@
/* ---------------------------------------------- */
+
/* Name Port Pins Function */
DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1))
DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1))
DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1))
DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1))
DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
/* Name Pin Function */
-/* mute DAC - 0 - mute, 1 - play. Affects both HP and LO. */
-DEFINE_GPIO(PCM5102A_XMIT, GPIO_PB(12), GPIOF_OUTPUT(0))
+/* mute DAC: 0 - mute, 1 - play */
+/* Note: This seems to actually be power to the DAC in general,
+ * at least on the ES9018K2M devices. Was "DAC_XMIT". */
+DEFINE_GPIO(DAC_PWR, GPIO_PB(12), GPIOF_OUTPUT(0))
-/* mute HP amp, no effect on LO. 0 - mute, 1 - play */
-DEFINE_GPIO(MAX97220_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0))
+/* mute HP amp: 0 - mute, 1 - play */
+DEFINE_GPIO(HPAMP_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0))
-/* mute audio mux, only affects Headphone out.
- * 0 - play, 1 - mute */
-DEFINE_GPIO(ISL54405_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1))
+/* mute audio mux: 0 - play, 1 - mute */
+DEFINE_GPIO(STEREOSW_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1))
-/* switches HP on/off - 0 HP on, 1 hp off, has no effect on LO.
- * As best I can tell, it switches HP Out sources between HP amp and something
- * not implemented - there seem to be resistors missing. */
-DEFINE_GPIO(ISL54405_SEL, GPIO_PB(5), GPIOF_OUTPUT(0))
+/*
+ * Original devices: switches HP on/off - 0 HP on, 1 HP off, no effect on LO.
+ * Newer devices: switches between HP and LO - 0 HP, 1 LO.
+ */
+DEFINE_GPIO(STEREOSW_SEL, GPIO_PB(5), GPIOF_OUTPUT(0))
/* DAC AVDD */
-DEFINE_GPIO(PCM5102A_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0))
+DEFINE_GPIO(DAC_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0))
/* Headphone Amp power */
-DEFINE_GPIO(MAX97220_POWER, GPIO_PB(6), GPIOF_OUTPUT(0))
+DEFINE_GPIO(HPAMP_POWER, GPIO_PB(6), GPIOF_OUTPUT(0))
/* SD card */
DEFINE_GPIO(MSC0_CD, GPIO_PB(11), GPIOF_INPUT)
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
index 8d0b8a6e20..89d995f33a 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
@@ -25,6 +25,9 @@
#define I2C_ASYNC_BUS_COUNT 3
#define I2C_ASYNC_QUEUE_SIZE 4
+#define ES9018K2M_BUS 1
+#define ES9018K2M_ADDR 0x48
+
#define AXP_PMU_BUS 2
#define AXP_PMU_ADDR 0x34
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
index 073bddb8b4..0d43a3f010 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
@@ -99,6 +99,20 @@ static const uint32_t erosqnative_lcd_cmd_enable[] = {
/* Display On */
LCD_INSTR_CMD, 0x29,
LCD_INSTR_UDELAY, 20000,
+ LCD_INSTR_END,
+};
+
+static const uint32_t erosqnative_lcd_of_compat_cmd[] = {
+ /* Pixel Format Set */
+ LCD_INSTR_CMD, 0x3a,
+ LCD_INSTR_DAT, 0x66, /* 18 bpp */
+ /* Exit Sleep */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 120000,
+ /* Display On */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_UDELAY, 20000,
+ LCD_INSTR_END,
};
/* sleep and wake copied directly from m3k */
@@ -179,6 +193,15 @@ void lcd_tgt_enable(bool enable)
}
}
+void lcd_tgt_enable_of(bool enable)
+{
+ /* silence the unused parameter warning */
+ if (enable)
+ {}
+
+ lcd_exec_commands(&erosqnative_lcd_of_compat_cmd[0]);
+}
+
void lcd_tgt_sleep(bool sleep)
{
if(sleep)
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
index 5573919aa2..ab6393a9fe 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
@@ -63,10 +63,15 @@ void power_init(void)
/* Set lowest sample rate */
axp_adc_set_rate(AXP_ADC_RATE_25HZ);
- /* Ensure battery voltage ADC is enabled */
- int bits = axp_adc_get_enabled();
- bits |= (1 << ADC_BATTERY_VOLTAGE);
- axp_adc_set_enabled(bits);
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
/* Turn on all power outputs */
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
@@ -78,11 +83,13 @@ void power_init(void)
* OF's setting, although it's not strictly within the USB spec. */
axp_set_charge_current(780);
+#ifdef BOOTLOADER
/* Delay to give power outputs time to stabilize.
* With the power thread delay, this can apparently go as low as 50,
* Keeping a higher value here just to ensure the bootloader works
* correctly. */
mdelay(200);
+#endif
}
#ifdef HAVE_USB_CHARGING_ENABLE
@@ -111,3 +118,13 @@ int _battery_voltage(void)
{
return axp_adc_read(ADC_BATTERY_VOLTAGE);
}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
index 8528b803f7..9374d23a81 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -20,6 +20,7 @@
****************************************************************************/
#include "audiohw.h"
+#include "audio.h"
#include "system.h"
#include "pcm_sampr.h"
#include "aic-x1000.h"
@@ -27,8 +28,13 @@
#include "gpio-x1000.h"
#include "logf.h"
+static int cur_audio_source = AUDIO_SRC_PLAYBACK;
+static int cur_vol_r = AK4376_MIN_VOLUME;
+static int cur_vol_l = AK4376_MIN_VOLUME;
+static int cur_recvol = 0;
+static int cur_filter_roll_off = 0;
static int cur_fsel = HW_FREQ_48;
-static int cur_power_mode = 0;
+static int cur_power_mode = SOUND_HIGH_POWER;
static void set_ak_freqmode(void)
{
@@ -60,29 +66,138 @@ void audiohw_postinit(void)
void audiohw_close(void)
{
- ak4376_close();
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_close();
+ else if(cur_audio_source == AUDIO_SRC_MIC)
+ x1000_icodec_close();
+}
+
+void audio_set_output_source(int source)
+{
+ /* this is a no-op */
+ (void)source;
+}
+
+void audio_input_mux(int source, unsigned flags)
+{
+ (void)flags;
+
+ if(source == cur_audio_source)
+ return;
+
+ /* close whatever codec is currently in use */
+ audiohw_close();
+ aic_enable_i2s_bit_clock(false);
+
+ /* switch to new source */
+ cur_audio_source = source;
+
+ if(source == AUDIO_SRC_PLAYBACK) {
+ /* power on DAC */
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(AIC_I2S_MASTER_MODE);
+ ak4376_open();
+
+ /* apply the old settings */
+ audiohw_set_volume(cur_vol_l, cur_vol_r);
+ audiohw_set_filter_roll_off(cur_filter_roll_off);
+ set_ak_freqmode();
+ } else if(source == AUDIO_SRC_MIC) {
+ aic_set_external_codec(false);
+ aic_set_i2s_mode(AIC_I2S_SLAVE_MODE);
+ /* Note: Sampling frequency is irrelevant here */
+ aic_set_i2s_clock(X1000_CLK_EXCLK, 48000, 0);
+ aic_enable_i2s_bit_clock(true);
+
+ x1000_icodec_open();
+
+ /* configure the mic */
+ x1000_icodec_mic1_enable(true);
+ x1000_icodec_mic1_bias_enable(true);
+ x1000_icodec_mic1_configure(JZCODEC_MIC1_DIFFERENTIAL |
+ JZCODEC_MIC1_BIAS_2_08V);
+
+ /* configure the ADC */
+ x1000_icodec_adc_mic_sel(JZCODEC_MIC_SEL_ANALOG);
+ x1000_icodec_adc_highpass_filter(true);
+ x1000_icodec_adc_frequency(cur_fsel);
+ x1000_icodec_adc_enable(true);
+
+ /* configure the mixer to put mic input in both channels */
+ x1000_icodec_mixer_enable(true);
+ x1000_icodec_write(JZCODEC_MIX2, 0x10);
+
+ /* set gain and unmute */
+ audiohw_set_recvol(cur_recvol, 0, AUDIO_GAIN_MIC);
+ x1000_icodec_adc_mute(false);
+ } else {
+ logf("bad audio input source: %d (flags: %x)", source, flags);
+ }
}
void audiohw_set_volume(int vol_l, int vol_r)
{
- ak4376_set_volume(vol_l, vol_r);
+ cur_vol_l = vol_l;
+ cur_vol_r = vol_r;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_set_volume(vol_l, vol_r);
}
void audiohw_set_filter_roll_off(int val)
{
- ak4376_set_filter_roll_off(val);
+ cur_filter_roll_off = val;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_set_filter_roll_off(val);
}
void audiohw_set_frequency(int fsel)
{
cur_fsel = fsel;
- set_ak_freqmode();
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ set_ak_freqmode();
+ else
+ x1000_icodec_adc_frequency(fsel);
}
void audiohw_set_power_mode(int mode)
{
cur_power_mode = mode;
- set_ak_freqmode();
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ set_ak_freqmode();
+}
+
+static int round_step_up(int x, int step)
+{
+ int rem = x % step;
+ if(rem > 0)
+ rem -= step;
+ return x - rem;
+}
+
+void audiohw_set_recvol(int left, int right, int type)
+{
+ (void)right;
+
+ if(type == AUDIO_GAIN_MIC) {
+ cur_recvol = left;
+
+ if(cur_audio_source == AUDIO_SRC_MIC) {
+ int mic_gain = round_step_up(left, X1000_ICODEC_MIC_GAIN_STEP);
+ mic_gain = MIN(mic_gain, X1000_ICODEC_MIC_GAIN_MAX);
+ mic_gain = MAX(mic_gain, X1000_ICODEC_MIC_GAIN_MIN);
+
+ int adc_gain = left - mic_gain;
+ adc_gain = MIN(adc_gain, X1000_ICODEC_ADC_GAIN_MAX);
+ adc_gain = MAX(adc_gain, X1000_ICODEC_ADC_GAIN_MIN);
+
+ x1000_icodec_adc_gain(adc_gain);
+ x1000_icodec_mic1_gain(mic_gain);
+ }
+ }
}
void ak4376_set_pdn_pin(int level)
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
index 5c92fa81e2..840be36a75 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
@@ -61,10 +61,15 @@ void power_init(void)
/* Set lowest sample rate */
axp_adc_set_rate(AXP_ADC_RATE_25HZ);
- /* Ensure battery voltage ADC is enabled */
- int bits = axp_adc_get_enabled();
- bits |= (1 << ADC_BATTERY_VOLTAGE);
- axp_adc_set_enabled(bits);
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
/* Turn on all power outputs */
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
@@ -106,3 +111,13 @@ int _battery_voltage(void)
{
return axp_adc_read(ADC_BATTERY_VOLTAGE);
}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
deleted file mode 100644
index cbbe8b1d5d..0000000000
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include "system.h"
-#include "clk-x1000.h"
-#include "spl-x1000.h"
-#include "gpio-x1000.h"
-
-#define CMDLINE_COMMON \
- "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
-#define CMDLINE_NORMAL \
- " init=/linuxrc ubi.mtd=3 root=ubi0:rootfs ubi.mtd=4 rootfstype=ubifs rw loglevel=8"
-
-static int dualboot_setup(void)
-{
- spl_dualboot_init_clocktree();
- spl_dualboot_init_uart2();
-
- /* load PDMA MCU firmware */
- jz_writef(CPM_CLKGR, PDMA(0));
- return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
-}
-
-const struct spl_boot_option spl_boot_options[] = {
- [BOOT_OPTION_ROCKBOX] = {
- .storage_addr = 0x6800,
- .storage_size = 102 * 1024,
- .load_addr = X1000_DRAM_BASE,
- .exec_addr = X1000_DRAM_BASE,
- .flags = BOOTFLAG_UCLPACK,
- },
- [BOOT_OPTION_OFW_PLAYER] = {
- .storage_addr = 0x20000,
- .storage_size = 4 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
- [BOOT_OPTION_OFW_RECOVERY] = {
- .storage_addr = 0x420000,
- .storage_size = 5 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
-};
-
-int spl_get_boot_option(void)
-{
- /* Button debounce time in OST clock cycles */
- const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
-
- /* Buttons to poll */
- const unsigned port = GPIO_A;
- const uint32_t recov_pin = (1 << 19); /* Volume Up */
- const uint32_t orig_fw_pin = (1 << 17); /* Play */
-
- uint32_t pin = -1, lastpin = 0;
- uint32_t deadline = 0;
- int iter_count = 30; /* to avoid an infinite loop */
-
- /* set GPIOs to input state */
- gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
-
- /* Poll until we get a stable reading */
- do {
- lastpin = pin;
- pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
- if(pin != lastpin) {
- deadline = __ost_read32() + btn_stable_time;
- iter_count -= 1;
- }
- } while(iter_count > 0 && __ost_read32() < deadline);
-
- if(iter_count >= 0 && (pin & orig_fw_pin)) {
- if(pin & recov_pin)
- return BOOT_OPTION_OFW_RECOVERY;
- else
- return BOOT_OPTION_OFW_PLAYER;
- }
-
- return BOOT_OPTION_ROCKBOX;
-}
-
-void spl_error(void)
-{
- /* Flash the buttonlight */
- int level = 0;
- while(1) {
- gpio_set_function(GPIO_PC(24), GPIOF_OUTPUT(level));
- mdelay(100);
- level = 1 - level;
- }
-}
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.c b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
index 14195359df..0ebe424566 100644
--- a/firmware/target/mips/ingenic_x1000/gpio-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
@@ -21,7 +21,7 @@
#include "gpio-x1000.h"
-const struct gpio_setting gpio_settings[PIN_COUNT] = {
+static const struct gpio_setting gpio_settings[PIN_COUNT] INITDATA_ATTR = {
#define DEFINE_GPIO(_name, _gpio, _func) \
{.gpio = _gpio, .func = _func},
#define DEFINE_PINGROUP(...)
@@ -30,7 +30,7 @@ const struct gpio_setting gpio_settings[PIN_COUNT] = {
#undef DEFINE_PINGROUP
};
-const struct pingroup_setting pingroup_settings[PINGROUP_COUNT] = {
+static const struct pingroup_setting pingroup_settings[PINGROUP_COUNT] INITDATA_ATTR = {
#define DEFINE_GPIO(...)
#define DEFINE_PINGROUP(_name, _port, _pins, _func) \
{.port = _port, .pins = _pins, .func = _func},
@@ -39,7 +39,8 @@ const struct pingroup_setting pingroup_settings[PINGROUP_COUNT] = {
#undef DEFINE_PINGROUP
};
-const char* const gpio_names[PIN_COUNT] = {
+#if 0 /* not needed for the time being */
+static const char* const gpio_names[PIN_COUNT] = {
#define DEFINE_GPIO(_name, ...) #_name,
#define DEFINE_PINGROUP(...)
#include "gpio-target.h"
@@ -47,13 +48,14 @@ const char* const gpio_names[PIN_COUNT] = {
#undef DEFINE_PINGROUP
};
-const char* const pingroup_names[PINGROUP_COUNT] = {
+static const char* const pingroup_names[PINGROUP_COUNT] = {
#define DEFINE_GPIO(...)
#define DEFINE_PINGROUP(_name, ...) #_name,
#include "gpio-target.h"
#undef DEFINE_GPIO
#undef DEFINE_PINGROUP
};
+#endif
void gpio_init(void)
{
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.h b/firmware/target/mips/ingenic_x1000/gpio-x1000.h
index eac5f8651f..310a457ab5 100644
--- a/firmware/target/mips/ingenic_x1000/gpio-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.h
@@ -23,6 +23,7 @@
#define __GPIO_X1000_H__
#include "x1000/gpio.h"
+#include "config.h"
/* GPIO port numbers */
#define GPIO_A 0
@@ -103,16 +104,8 @@ enum {
PINGROUP_COUNT,
};
-/* arrays which define the target's GPIO settings */
-extern const struct gpio_setting gpio_settings[PIN_COUNT];
-extern const struct pingroup_setting pingroup_settings[PINGROUP_COUNT];
-
-/* stringified names for use in debug menus */
-extern const char* const gpio_names[PIN_COUNT];
-extern const char* const pingroup_names[PINGROUP_COUNT];
-
/* called at early init to set up GPIOs */
-extern void gpio_init(void);
+extern void gpio_init(void) INIT_ATTR;
/* Use GPIO Z to reconfigure several pins atomically */
extern void gpioz_configure(int port, uint32_t pins, int func);
diff --git a/firmware/target/mips/ingenic_x1000/i2c-x1000.h b/firmware/target/mips/ingenic_x1000/i2c-x1000.h
index e76624d511..9b9ba5e088 100644
--- a/firmware/target/mips/ingenic_x1000/i2c-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/i2c-x1000.h
@@ -27,7 +27,7 @@
#define I2C_FREQ_100K 100000
#define I2C_FREQ_400K 400000
-extern void i2c_init(void);
+extern void i2c_init(void) INIT_ATTR;
/* Configure the I2C controller prior to use.
*
diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.c b/firmware/target/mips/ingenic_x1000/installer-x1000.c
index 0a09ad0e91..d2f9e4e5e0 100644
--- a/firmware/target/mips/ingenic_x1000/installer-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/installer-x1000.c
@@ -62,10 +62,13 @@ static const struct update_part updates[] = {
static const int num_updates = sizeof(updates) / sizeof(struct update_part);
+static const uint8_t flash_sig_magic[8] =
+ {0x06, 0x05, 0x04, 0x03, 0x02, 0x55, 0xaa, 0x55};
+
/* calculate the offset and length of the update image; this is constant
* for a given target, based on the update parts and the NAND chip geometry.
*/
-static void get_image_loc(nand_drv* ndrv, size_t* offptr, size_t* lenptr)
+static void get_image_loc(struct nand_drv* ndrv, size_t* offptr, size_t* lenptr)
{
size_t blk_size = ndrv->chip->page_size << ndrv->chip->log2_ppb;
size_t img_off = 0;
@@ -119,7 +122,7 @@ struct updater {
size_t img_len; /* image length in flash = size of the buffer */
mtar_t* tar;
- nand_drv* ndrv;
+ struct nand_drv* ndrv;
};
static int updater_init(struct updater* u)
@@ -148,7 +151,7 @@ static int updater_init(struct updater* u)
/* buf_len is a bit oversized here, but it's not really important */
u->buf_len = u->img_len + sizeof(mtar_t) + 2*CACHEALIGN_SIZE;
- u->buf_hnd = core_alloc("boot_image", u->buf_len);
+ u->buf_hnd = core_alloc_ex(u->buf_len, &buflib_ops_locked);
if(u->buf_hnd < 0) {
rc = IERR_OUT_OF_MEMORY;
goto error;
@@ -178,8 +181,7 @@ static void updater_cleanup(struct updater* u)
if(u->tar && mtar_is_open(u->tar))
mtar_close(u->tar);
- if(u->buf_hnd >= 0)
- core_free(u->buf_hnd);
+ core_free(u->buf_hnd);
if(u->ndrv) {
nand_close(u->ndrv);
@@ -250,6 +252,12 @@ int backup_bootloader(const char* filename)
goto error;
}
+ /* bail if we're backing up something that looks like garbage */
+ if (memcmp(u.img_buf, flash_sig_magic, 8)) {
+ rc = IERR_CORRUPTED_BACKUP;
+ goto error;
+ }
+
/* write to file */
fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY);
if(fd < 0) {
@@ -294,6 +302,12 @@ int restore_bootloader(const char* filename)
goto error;
}
+ /* safety check to reduce risk of flashing complete garbage */
+ if (memcmp(u.img_buf, flash_sig_magic, 8)) {
+ rc = IERR_CORRUPTED_BACKUP;
+ goto error;
+ }
+
/* write image */
rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
if(rc != NAND_SUCCESS) {
@@ -321,6 +335,7 @@ const char* installer_strerror(int rc)
case IERR_NAND_OPEN: return "NAND open error";
case IERR_NAND_READ: return "NAND read error";
case IERR_NAND_WRITE: return "NAND write error";
+ case IERR_CORRUPTED_BACKUP: return "Backup is corrupt";
default: return "Unknown error!?";
}
}
diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.h b/firmware/target/mips/ingenic_x1000/installer-x1000.h
index b71839a907..9b0f1e4bd6 100644
--- a/firmware/target/mips/ingenic_x1000/installer-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/installer-x1000.h
@@ -45,6 +45,7 @@ enum {
IERR_NAND_OPEN,
IERR_NAND_READ,
IERR_NAND_WRITE,
+ IERR_CORRUPTED_BACKUP,
};
extern int install_bootloader(const char* filename);
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
index b66359a598..979febf066 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
@@ -428,7 +428,10 @@ void lcd_enable(bool en)
/* Handle turning the LCD back on */
if(!bit && en)
+ {
+ send_event(LCD_EVENT_ACTIVATION, NULL);
lcd_dma_start();
+ }
}
#endif
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.h b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
index 749fac8240..e88a8733ed 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
@@ -105,6 +105,14 @@ extern void lcd_exec_commands(const uint32_t* cmdseq);
*/
extern void lcd_tgt_enable(bool on);
+/* Enable/disable the LCD controller, but intended for booting the OF.
+ *
+ * This is only used for the Eros Q Native port, as the OF seems to be
+ * unable to initialize the LCD in the kernel boot rather than having
+ * the bootloader do it.
+ */
+extern void lcd_tgt_enable_of(bool on);
+
/* Enter or exit sleep mode to save power, normally by sending the necessary
* commands with lcd_exec_commands().
*/
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index a818ba10aa..af0f972eae 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -22,75 +22,98 @@
#include "nand-x1000.h"
#include "sfc-x1000.h"
#include "system.h"
+#include "logf.h"
#include <string.h>
-/* cmd mode a d phase format has data */
-#define NANDCMD_RESET SFC_CMD(0xff, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
-#define NANDCMD_READID(x,y) SFC_CMD(0x9f, SFC_TMODE_1_1_1, x, y, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_WR_EN SFC_CMD(0x06, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
-#define NANDCMD_GET_FEATURE SFC_CMD(0x0f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_SET_FEATURE SFC_CMD(0x1f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_PAGE_READ(x) SFC_CMD(0x13, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0)
-#define NANDCMD_READ_CACHE(x) SFC_CMD(0x0b, SFC_TMODE_1_1_1, x, 8, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_READ_CACHE_x4(x) SFC_CMD(0x6b, SFC_TMODE_1_1_4, x, 8, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_PROGRAM_LOAD(x) SFC_CMD(0x02, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_PROGRAM_LOAD_x4(x) SFC_CMD(0x32, SFC_TMODE_1_1_4, x, 0, SFC_PFMT_ADDR_FIRST, 1)
-#define NANDCMD_PROGRAM_EXECUTE(x) SFC_CMD(0x10, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0)
-#define NANDCMD_BLOCK_ERASE(x) SFC_CMD(0xd8, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0)
-
-/* Feature registers are found in linux/mtd/spinand.h,
- * apparently these are pretty standardized */
-#define FREG_PROT 0xa0
-#define FREG_PROT_UNLOCK 0x00
-
-#define FREG_CFG 0xb0
-#define FREG_CFG_OTP_ENABLE (1 << 6)
-#define FREG_CFG_ECC_ENABLE (1 << 4)
-#define FREG_CFG_QUAD_ENABLE (1 << 0)
-
-#define FREG_STATUS 0xc0
-#define FREG_STATUS_BUSY (1 << 0)
-#define FREG_STATUS_EFAIL (1 << 2)
-#define FREG_STATUS_PFAIL (1 << 3)
-#define FREG_STATUS_ECC_MASK (3 << 4)
-#define FREG_STATUS_ECC_NO_FLIPS (0 << 4)
-#define FREG_STATUS_ECC_HAS_FLIPS (1 << 4)
-#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
-
-const nand_chip supported_nand_chips[] = {
-#if defined(FIIO_M3K) || defined(SHANLING_Q1) || defined(EROS_QN)
- {
- /* ATO25D1GA */
- .mf_id = 0x9b,
- .dev_id = 0x12,
- .row_cycles = 3,
- .col_cycles = 2,
- .log2_ppb = 6, /* 64 pages */
- .page_size = 2048,
- .oob_size = 64,
- .nr_blocks = 1024,
- .clock_freq = 150000000,
- .dev_conf = jz_orf(SFC_DEV_CONF,
- CE_DL(1), HOLD_DL(1), WP_DL(1),
- CPHA(0), CPOL(0),
- TSH(7), TSETUP(0), THOLD(0),
- STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
- SMP_DELAY(1)),
- .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT,
- },
-#else
- { 0 },
-#endif
+static void winbond_setup_chip(struct nand_drv* drv);
+
+static const struct nand_chip chip_ato25d1ga = {
+ .log2_ppb = 6, /* 64 pages */
+ .page_size = 2048,
+ .oob_size = 64,
+ .nr_blocks = 1024,
+ .bbm_pos = 2048,
+ .clock_freq = 150000000,
+ .dev_conf = jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(7), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(1)),
+ .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT,
+ .cmd_page_read = NANDCMD_PAGE_READ,
+ .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
+ .cmd_block_erase = NANDCMD_BLOCK_ERASE,
+ .cmd_read_cache = NANDCMD_READ_CACHE_x4,
+ .cmd_program_load = NANDCMD_PROGRAM_LOAD_x4,
};
-const size_t nr_supported_nand_chips =
- sizeof(supported_nand_chips) / sizeof(nand_chip);
+static const struct nand_chip chip_w25n01gvxx = {
+ .log2_ppb = 6, /* 64 pages */
+ .page_size = 2048,
+ .oob_size = 64,
+ .nr_blocks = 1024,
+ .bbm_pos = 2048,
+ .clock_freq = 150000000,
+ .dev_conf = jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(11), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(1)),
+ .flags = NAND_CHIPFLAG_ON_DIE_ECC,
+ /* TODO: quad mode? */
+ .cmd_page_read = NANDCMD_PAGE_READ,
+ .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
+ .cmd_block_erase = NANDCMD_BLOCK_ERASE,
+ .cmd_read_cache = NANDCMD_READ_CACHE_SLOW,
+ .cmd_program_load = NANDCMD_PROGRAM_LOAD,
+ .setup_chip = winbond_setup_chip,
+};
+
+static const struct nand_chip chip_gd5f1gq4xexx = {
+ .log2_ppb = 6, /* 64 pages */
+ .page_size = 2048,
+ .oob_size = 64, /* 128B when hardware ECC is disabled */
+ .nr_blocks = 1024,
+ .bbm_pos = 2048,
+ .clock_freq = 150000000,
+ .dev_conf = jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(7), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(1)),
+ .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT |
+ NAND_CHIPFLAG_ON_DIE_ECC,
+ .cmd_page_read = NANDCMD_PAGE_READ,
+ .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
+ .cmd_block_erase = NANDCMD_BLOCK_ERASE,
+ .cmd_read_cache = NANDCMD_READ_CACHE_x4,
+ .cmd_program_load = NANDCMD_PROGRAM_LOAD_x4,
+};
+
+#define chip_ds35x1gaxxx chip_gd5f1gq4xexx
+#define chip_gd5f1gq5xexxg chip_gd5f1gq4xexx
+
+const struct nand_chip_id supported_nand_chips[] = {
+ NAND_CHIP_ID(&chip_ato25d1ga, NAND_READID_ADDR, 0x9b, 0x12),
+ NAND_CHIP_ID(&chip_w25n01gvxx, NAND_READID_ADDR, 0xef, 0xaa, 0x21),
+ NAND_CHIP_ID(&chip_gd5f1gq4xexx, NAND_READID_ADDR, 0xc8, 0xd1),
+ NAND_CHIP_ID(&chip_gd5f1gq4xexx, NAND_READID_ADDR, 0xc8, 0xc1),
+ NAND_CHIP_ID(&chip_ds35x1gaxxx, NAND_READID_ADDR, 0xe5, 0x71), /* 3.3 V */
+ NAND_CHIP_ID(&chip_ds35x1gaxxx, NAND_READID_ADDR, 0xe5, 0x21), /* 1.8 V */
+ NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x51), /* 3.3 V */
+ NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x41), /* 1.8 V */
+};
+
+const size_t nr_supported_nand_chips = ARRAYLEN(supported_nand_chips);
-static nand_drv static_nand_drv;
+static struct nand_drv static_nand_drv;
static uint8_t static_scratch_buf[NAND_DRV_SCRATCHSIZE] CACHEALIGN_ATTR;
static uint8_t static_page_buf[NAND_DRV_MAXPAGESIZE] CACHEALIGN_ATTR;
-nand_drv* nand_init(void)
+struct nand_drv* nand_init(void)
{
static bool inited = false;
if(!inited) {
@@ -103,19 +126,19 @@ nand_drv* nand_init(void)
return &static_nand_drv;
}
-static uint8_t nand_get_reg(nand_drv* drv, uint8_t reg)
+static uint8_t nand_get_reg(struct nand_drv* drv, uint8_t reg)
{
sfc_exec(NANDCMD_GET_FEATURE, reg, drv->scratch_buf, 1|SFC_READ);
return drv->scratch_buf[0];
}
-static void nand_set_reg(nand_drv* drv, uint8_t reg, uint8_t val)
+static void nand_set_reg(struct nand_drv* drv, uint8_t reg, uint8_t val)
{
drv->scratch_buf[0] = val;
sfc_exec(NANDCMD_SET_FEATURE, reg, drv->scratch_buf, 1|SFC_WRITE);
}
-static void nand_upd_reg(nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val)
+static void nand_upd_reg(struct nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val)
{
uint8_t x = nand_get_reg(drv, reg);
x &= ~msk;
@@ -123,56 +146,50 @@ static void nand_upd_reg(nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val)
nand_set_reg(drv, reg, x);
}
-static bool identify_chip(nand_drv* drv)
+static const struct nand_chip* identify_chip_method(uint8_t method,
+ const uint8_t* id_buf)
+{
+ for (size_t i = 0; i < nr_supported_nand_chips; ++i) {
+ const struct nand_chip_id* chip_id = &supported_nand_chips[i];
+ if (chip_id->method == method &&
+ !memcmp(chip_id->id_bytes, id_buf, chip_id->num_id_bytes))
+ return chip_id->chip;
+ }
+
+ return NULL;
+}
+
+static bool identify_chip(struct nand_drv* drv)
{
/* Read ID command has some variations; Linux handles these 3:
* - no address or dummy bytes
* - 1 byte address, no dummy byte
* - no address byte, 1 byte dummy
*
- * Right now there is only a need for the 2nd variation, as that is
- * the method used by the ATO25D1GA.
- *
- * Some chips also output more than 2 ID bytes.
+ * Currently we use the 2nd method, aka. address read ID, the
+ * other methods can be added when needed.
*/
- sfc_exec(NANDCMD_READID(1, 0), 0, drv->scratch_buf, 2|SFC_READ);
- drv->mf_id = drv->scratch_buf[0];
- drv->dev_id = drv->scratch_buf[1];
-
- for(size_t i = 0; i < nr_supported_nand_chips; ++i) {
- const nand_chip* chip = &supported_nand_chips[i];
- if(chip->mf_id == drv->mf_id && chip->dev_id == drv->dev_id) {
- drv->chip = chip;
- return true;
- }
- }
+ sfc_exec(NANDCMD_READID_ADDR, 0, drv->scratch_buf, 4|SFC_READ);
+ drv->chip = identify_chip_method(NAND_READID_ADDR, drv->scratch_buf);
+ if (drv->chip)
+ return true;
return false;
}
-static void setup_chip_data(nand_drv* drv)
+static void setup_chip_data(struct nand_drv* drv)
{
drv->ppb = 1 << drv->chip->log2_ppb;
drv->fpage_size = drv->chip->page_size + drv->chip->oob_size;
}
-static void setup_chip_commands(nand_drv* drv)
+static void winbond_setup_chip(struct nand_drv* drv)
{
- /* Select commands appropriate for the chip */
- drv->cmd_page_read = NANDCMD_PAGE_READ(drv->chip->row_cycles);
- drv->cmd_program_execute = NANDCMD_PROGRAM_EXECUTE(drv->chip->row_cycles);
- drv->cmd_block_erase = NANDCMD_BLOCK_ERASE(drv->chip->row_cycles);
-
- if(drv->chip->flags & NAND_CHIPFLAG_QUAD) {
- drv->cmd_read_cache = NANDCMD_READ_CACHE_x4(drv->chip->col_cycles);
- drv->cmd_program_load = NANDCMD_PROGRAM_LOAD_x4(drv->chip->col_cycles);
- } else {
- drv->cmd_read_cache = NANDCMD_READ_CACHE(drv->chip->col_cycles);
- drv->cmd_program_load = NANDCMD_PROGRAM_LOAD(drv->chip->col_cycles);
- }
+ /* Ensure we are in buffered read mode. */
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_WINBOND_BUF, FREG_CFG_WINBOND_BUF);
}
-static void setup_chip_registers(nand_drv* drv)
+static void setup_chip_registers(struct nand_drv* drv)
{
/* Set chip registers to enter normal operation */
if(drv->chip->flags & NAND_CHIPFLAG_HAS_QE_BIT) {
@@ -181,22 +198,38 @@ static void setup_chip_registers(nand_drv* drv)
en ? FREG_CFG_QUAD_ENABLE : 0);
}
+ if(drv->chip->flags & NAND_CHIPFLAG_ON_DIE_ECC) {
+ /* Enable on-die ECC */
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_ECC_ENABLE, FREG_CFG_ECC_ENABLE);
+ }
+
/* Clear OTP bit to access the main data array */
nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE, 0);
/* Clear write protection bits */
nand_set_reg(drv, FREG_PROT, FREG_PROT_UNLOCK);
+
+ /* Call any chip-specific hooks */
+ if(drv->chip->setup_chip)
+ drv->chip->setup_chip(drv);
}
-int nand_open(nand_drv* drv)
+int nand_open(struct nand_drv* drv)
{
- if(drv->refcount > 0)
+ if(drv->refcount > 0) {
+ drv->refcount++;
return NAND_SUCCESS;
+ }
/* Initialize the controller */
sfc_open();
- sfc_set_dev_conf(supported_nand_chips[0].dev_conf);
- sfc_set_clock(supported_nand_chips[0].clock_freq);
+ sfc_set_dev_conf(jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(15), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(0)));
+ sfc_set_clock(X1000_EXCLK_FREQ);
/* Send the software reset command */
sfc_exec(NANDCMD_RESET, 0, NULL, 0);
@@ -207,7 +240,6 @@ int nand_open(nand_drv* drv)
return NAND_ERR_UNKNOWN_CHIP;
setup_chip_data(drv);
- setup_chip_commands(drv);
/* Set new SFC parameters */
sfc_set_dev_conf(drv->chip->dev_conf);
@@ -220,9 +252,10 @@ int nand_open(nand_drv* drv)
return NAND_SUCCESS;
}
-void nand_close(nand_drv* drv)
+void nand_close(struct nand_drv* drv)
{
- if(drv->refcount == 0)
+ --drv->refcount;
+ if(drv->refcount > 0)
return;
/* Let's reset the chip... the idea is to restore the registers
@@ -231,10 +264,15 @@ void nand_close(nand_drv* drv)
mdelay(10);
sfc_close();
- drv->refcount--;
}
-static uint8_t nand_wait_busy(nand_drv* drv)
+void nand_enable_otp(struct nand_drv* drv, bool enable)
+{
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE,
+ enable ? FREG_CFG_OTP_ENABLE : 0);
+}
+
+static uint8_t nand_wait_busy(struct nand_drv* drv)
{
uint8_t reg;
do {
@@ -243,10 +281,10 @@ static uint8_t nand_wait_busy(nand_drv* drv)
return reg;
}
-int nand_block_erase(nand_drv* drv, nand_block_t block)
+int nand_block_erase(struct nand_drv* drv, nand_block_t block)
{
sfc_exec(NANDCMD_WR_EN, 0, NULL, 0);
- sfc_exec(drv->cmd_block_erase, block, NULL, 0);
+ sfc_exec(drv->chip->cmd_block_erase, block, NULL, 0);
uint8_t status = nand_wait_busy(drv);
if(status & FREG_STATUS_EFAIL)
@@ -255,11 +293,12 @@ int nand_block_erase(nand_drv* drv, nand_block_t block)
return NAND_SUCCESS;
}
-int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer)
+int nand_page_program(struct nand_drv* drv, nand_page_t page, const void* buffer)
{
sfc_exec(NANDCMD_WR_EN, 0, NULL, 0);
- sfc_exec(drv->cmd_program_load, 0, (void*)buffer, drv->fpage_size|SFC_WRITE);
- sfc_exec(drv->cmd_program_execute, page, NULL, 0);
+ sfc_exec(drv->chip->cmd_program_load,
+ 0, (void*)buffer, drv->fpage_size|SFC_WRITE);
+ sfc_exec(drv->chip->cmd_program_execute, page, NULL, 0);
uint8_t status = nand_wait_busy(drv);
if(status & FREG_STATUS_PFAIL)
@@ -268,15 +307,29 @@ int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer)
return NAND_SUCCESS;
}
-int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer)
+int nand_page_read(struct nand_drv* drv, nand_page_t page, void* buffer)
{
- sfc_exec(drv->cmd_page_read, page, NULL, 0);
+ sfc_exec(drv->chip->cmd_page_read, page, NULL, 0);
nand_wait_busy(drv);
- sfc_exec(drv->cmd_read_cache, 0, buffer, drv->fpage_size|SFC_READ);
+ sfc_exec(drv->chip->cmd_read_cache, 0, buffer, drv->fpage_size|SFC_READ);
+
+ if(drv->chip->flags & NAND_CHIPFLAG_ON_DIE_ECC) {
+ uint8_t status = nand_get_reg(drv, FREG_STATUS);
+
+ if(status & FREG_STATUS_ECC_UNCOR_ERR) {
+ logf("ecc uncorrectable error on page %08lx", (unsigned long)page);
+ return NAND_ERR_ECC_FAIL;
+ }
+
+ if(status & FREG_STATUS_ECC_HAS_FLIPS) {
+ logf("ecc corrected bitflips on page %08lx", (unsigned long)page);
+ }
+ }
+
return NAND_SUCCESS;
}
-int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer)
+int nand_read_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer)
{
if(byte_len == 0)
return NAND_SUCCESS;
@@ -290,21 +343,21 @@ int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void*
if(rc < 0)
return rc;
- memcpy(buffer, &drv->page_buf[offset], MIN(pg_size, byte_len));
+ memcpy(buffer, &drv->page_buf[offset], MIN(pg_size - offset, byte_len));
- if(byte_len <= pg_size)
+ if(byte_len <= pg_size - offset)
break;
+ byte_len -= pg_size - offset;
+ buffer += pg_size - offset;
offset = 0;
- byte_len -= pg_size;
- buffer += pg_size;
page++;
}
return NAND_SUCCESS;
}
-int nand_write_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer)
+int nand_write_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer)
{
if(byte_len == 0)
return NAND_SUCCESS;
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h
index 711bf190b5..0ccd075079 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h
@@ -32,6 +32,7 @@
#define NAND_ERR_PROGRAM_FAIL (-2)
#define NAND_ERR_ERASE_FAIL (-3)
#define NAND_ERR_UNALIGNED (-4)
+#define NAND_ERR_ECC_FAIL (-5)
/* keep max page size in sync with the NAND chip table in the .c file */
#define NAND_DRV_SCRATCHSIZE 32
@@ -41,6 +42,47 @@
#define NAND_CHIPFLAG_QUAD 0x0001
/* Chip requires QE bit set to enable quad I/O mode */
#define NAND_CHIPFLAG_HAS_QE_BIT 0x0002
+/* True if the chip has on-die ECC */
+#define NAND_CHIPFLAG_ON_DIE_ECC 0x0004
+
+/* cmd mode a d phase format has data */
+#define NANDCMD_RESET SFC_CMD(0xff, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_READID_OPCODE SFC_CMD(0x9f, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READID_ADDR SFC_CMD(0x9f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READID_DUMMY SFC_CMD(0x9f, SFC_TMODE_1_1_1, 0, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_WR_EN SFC_CMD(0x06, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_GET_FEATURE SFC_CMD(0x0f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_SET_FEATURE SFC_CMD(0x1f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_PAGE_READ SFC_CMD(0x13, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_READ_CACHE_SLOW SFC_CMD(0x03, SFC_TMODE_1_1_1, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READ_CACHE SFC_CMD(0x0b, SFC_TMODE_1_1_1, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READ_CACHE_x4 SFC_CMD(0x6b, SFC_TMODE_1_1_4, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_PROGRAM_EXECUTE SFC_CMD(0x10, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_PROGRAM_LOAD SFC_CMD(0x02, SFC_TMODE_1_1_1, 2, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_PROGRAM_LOAD_x4 SFC_CMD(0x32, SFC_TMODE_1_1_4, 2, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_BLOCK_ERASE SFC_CMD(0xd8, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
+
+/* Feature registers are found in linux/mtd/spinand.h,
+ * apparently these are pretty standardized */
+#define FREG_PROT 0xa0
+#define FREG_PROT_UNLOCK 0x00
+
+#define FREG_CFG 0xb0
+#define FREG_CFG_OTP_ENABLE (1 << 6)
+#define FREG_CFG_ECC_ENABLE (1 << 4)
+#define FREG_CFG_QUAD_ENABLE (1 << 0)
+
+/* Winbond-specific bit used on the W25N01GVxx */
+#define FREG_CFG_WINBOND_BUF (1 << 3)
+
+#define FREG_STATUS 0xc0
+#define FREG_STATUS_BUSY (1 << 0)
+#define FREG_STATUS_EFAIL (1 << 2)
+#define FREG_STATUS_PFAIL (1 << 3)
+#define FREG_STATUS_ECC_MASK (3 << 4)
+#define FREG_STATUS_ECC_NO_FLIPS (0 << 4)
+#define FREG_STATUS_ECC_HAS_FLIPS (1 << 4)
+#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
/* Types to distinguish between block & page addresses in the API.
*
@@ -59,15 +101,9 @@
typedef uint32_t nand_block_t;
typedef uint32_t nand_page_t;
-typedef struct nand_chip {
- /* Manufacturer and device ID bytes */
- uint8_t mf_id;
- uint8_t dev_id;
-
- /* Row/column address width */
- uint8_t row_cycles;
- uint8_t col_cycles;
+struct nand_drv;
+struct nand_chip {
/* Base2 logarithm of the number of pages per block */
unsigned log2_ppb;
@@ -78,6 +114,9 @@ typedef struct nand_chip {
/* Total number of blocks in the chip */
unsigned nr_blocks;
+ /* Bad block marker offset within the 1st page of a bad block */
+ unsigned bbm_pos;
+
/* Clock frequency to use */
uint32_t clock_freq;
@@ -86,9 +125,38 @@ typedef struct nand_chip {
/* Chip specific flags */
uint32_t flags;
-} nand_chip;
-typedef struct nand_drv {
+ /* SFC commands for issuing I/O ops */
+ uint32_t cmd_page_read;
+ uint32_t cmd_program_execute;
+ uint32_t cmd_block_erase;
+ uint32_t cmd_read_cache;
+ uint32_t cmd_program_load;
+
+ /* Chip-specific setup routine */
+ void(*setup_chip)(struct nand_drv* drv);
+};
+
+enum nand_readid_method {
+ NAND_READID_OPCODE,
+ NAND_READID_ADDR,
+ NAND_READID_DUMMY,
+};
+
+struct nand_chip_id {
+ uint8_t method;
+ uint8_t num_id_bytes;
+ uint8_t id_bytes[4];
+ const struct nand_chip* chip;
+};
+
+#define NAND_CHIP_ID(_chip, _method, ...) \
+ { .method = _method, \
+ .num_id_bytes = ARRAYLEN(((uint8_t[]){__VA_ARGS__})), \
+ .id_bytes = {__VA_ARGS__}, \
+ .chip = _chip }
+
+struct nand_drv {
/* NAND access lock. Needs to be held during any operations. */
struct mutex mutex;
@@ -110,27 +178,16 @@ typedef struct nand_drv {
uint8_t* page_buf;
/* Pointer to the chip data. */
- const nand_chip* chip;
+ const struct nand_chip* chip;
/* Pages per block = 1 << chip->log2_ppb */
unsigned ppb;
/* Full page size = chip->page_size + chip->oob_size */
unsigned fpage_size;
+};
- /* Probed mf_id / dev_id for debugging, in case identification fails. */
- uint8_t mf_id;
- uint8_t dev_id;
-
- /* SFC commands used for I/O, these are set based on chip data */
- uint32_t cmd_page_read;
- uint32_t cmd_read_cache;
- uint32_t cmd_program_load;
- uint32_t cmd_program_execute;
- uint32_t cmd_block_erase;
-} nand_drv;
-
-extern const nand_chip supported_nand_chips[];
+extern const struct nand_chip_id supported_nand_chips[];
extern const size_t nr_supported_nand_chips;
/* Return the static NAND driver instance.
@@ -138,14 +195,14 @@ extern const size_t nr_supported_nand_chips;
* ALL normal Rockbox code should use this instance. The SPL does not
* use it, because it needs to manually place buffers in external RAM.
*/
-extern nand_drv* nand_init(void);
+extern struct nand_drv* nand_init(void);
-static inline void nand_lock(nand_drv* drv)
+static inline void nand_lock(struct nand_drv* drv)
{
mutex_lock(&drv->mutex);
}
-static inline void nand_unlock(nand_drv* drv)
+static inline void nand_unlock(struct nand_drv* drv)
{
mutex_unlock(&drv->mutex);
}
@@ -159,8 +216,11 @@ static inline void nand_unlock(nand_drv* drv)
*
* These functions require the lock to be held.
*/
-extern int nand_open(nand_drv* drv);
-extern void nand_close(nand_drv* drv);
+extern int nand_open(struct nand_drv* drv);
+extern void nand_close(struct nand_drv* drv);
+
+/* Enable/disable OTP access. OTP data pages are usually vendor-specific. */
+void nand_enable_otp(struct nand_drv* drv, bool enable);
/* Read / program / erase operations. Buffer needs to be cache-aligned for DMA.
* Read and program operate on full page data, ie. including OOB data areas.
@@ -168,15 +228,15 @@ extern void nand_close(nand_drv* drv);
* NOTE: ECC is not implemented. If it ever needs to be, these functions will
* probably use ECC transparently. All code should be written to expect this.
*/
-extern int nand_block_erase(nand_drv* drv, nand_block_t block);
-extern int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer);
-extern int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer);
+extern int nand_block_erase(struct nand_drv* drv, nand_block_t block);
+extern int nand_page_program(struct nand_drv* drv, nand_page_t page, const void* buffer);
+extern int nand_page_read(struct nand_drv* drv, nand_page_t page, void* buffer);
/* Wrappers to read/write bytes. For simple access to the main data area only.
* The write address / length must align to a block boundary. Reads do not have
* any alignment requirement. OOB data is never read, and is written as 0xff.
*/
-extern int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer);
-extern int nand_write_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer);
+extern int nand_read_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer);
+extern int nand_write_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer);
#endif /* __NAND_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
index ef54d45e62..4b9cb85b57 100644
--- a/firmware/target/mips/ingenic_x1000/pcm-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,28 +31,31 @@
#include "x1000/aic.h"
#include "x1000/cpm.h"
-#define AIC_STATE_STOPPED 0
-#define AIC_STATE_PLAYING 1
+#define AIC_STATE_STOPPED 0x00
+#define AIC_STATE_PLAYING 0x01
+#define AIC_STATE_RECORDING 0x02
volatile unsigned aic_tx_underruns = 0;
static int aic_state = AIC_STATE_STOPPED;
-static int aic_lock = 0;
-static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
-
-static dma_desc aic_dma_desc;
-
+static int play_lock = 0;
+static volatile int play_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc play_dma_desc;
static void pcm_play_dma_int_cb(int event);
+
#ifdef HAVE_RECORDING
+volatile unsigned aic_rx_overruns = 0;
+static int rec_lock = 0;
+static volatile int rec_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc rec_dma_desc;
+
static void pcm_rec_dma_int_cb(int event);
#endif
void pcm_play_dma_init(void)
{
- /* Ungate clock, assign pins. NB this overlaps with pins labeled "sa0-sa4"
- * on Ingenic's datasheets but I'm not sure what they are. Probably safe to
- * assume they are not useful to Rockbox... */
+ /* Ungate clock */
jz_writef(CPM_CLKGR, AIC(0));
/* Configure AIC with some sane defaults */
@@ -79,7 +82,7 @@ void pcm_play_dma_init(void)
#endif
/* Set DMA settings */
- jz_writef(AIC_CFG, TFTH(16), RFTH(16));
+ jz_writef(AIC_CFG, TFTH(16), RFTH(15));
dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb);
#ifdef HAVE_RECORDING
dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb);
@@ -106,23 +109,23 @@ void pcm_dma_apply_settings(void)
audiohw_set_frequency(pcm_fsel);
}
-static void pcm_dma_start(const void* addr, size_t size)
+static void play_dma_start(const void* addr, size_t size)
{
- aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
- SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
- STDE(0), TIE(1), LINK(0));
- aic_dma_desc.sa = PHYSADDR(addr);
- aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
- aic_dma_desc.tc = size;
- aic_dma_desc.sd = 0;
- aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
- aic_dma_desc.pad0 = 0;
- aic_dma_desc.pad1 = 0;
-
- commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
+ play_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(0), TIE(1), LINK(0));
+ play_dma_desc.sa = PHYSADDR(addr);
+ play_dma_desc.ta = PHYSADDR(JA_AIC_DR);
+ play_dma_desc.tc = size;
+ play_dma_desc.sd = 0;
+ play_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
+ play_dma_desc.pad0 = 0;
+ play_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&play_dma_desc, sizeof(dma_desc));
commit_dcache_range(addr, size);
- REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc);
+ REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&play_dma_desc);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1));
@@ -130,13 +133,13 @@ static void pcm_dma_start(const void* addr, size_t size)
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
-static void pcm_dma_handle_event(int event)
+static void play_dma_handle_event(int event)
{
if(event == DMA_EVENT_COMPLETE) {
const void* addr;
size_t size;
if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
} else if(event == DMA_EVENT_NONE) {
/* ignored, so callers don't need to check for this */
} else {
@@ -146,43 +149,60 @@ static void pcm_dma_handle_event(int event)
static void pcm_play_dma_int_cb(int event)
{
- if(aic_lock) {
- aic_dma_pending_event = event;
+ if(play_lock) {
+ play_dma_pending_event = event;
return;
} else {
- pcm_dma_handle_event(event);
+ play_dma_handle_event(event);
}
}
void pcm_play_dma_start(const void* addr, size_t size)
{
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_PLAYING;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_PLAYING;
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1));
}
void pcm_play_dma_stop(void)
{
- jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0));
- jz_writef(AIC_CCR, TFLUSH(1));
+ /* disable DMA and underrun interrupts */
+ jz_writef(AIC_CCR, TDMS(0), ETUR(0));
+
+ /*
+ * wait for FIFO to be empty - on targets
+ * with >16bit samples, flushing the fifo
+ * may result in swapping l and r channels!
+ * (ensure bit clock is running first)
+ */
+ if (jz_readf(AIC_I2SCR, STPBK) == 0) {
+ while(jz_readf(AIC_SR, TFL) != 0);
+ } else {
+ panicf("pcm_play_dma_stop: No bit clock running!");
+ }
+
+ /* disable playback */
+ jz_writef(AIC_CCR, ERPL(0));
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_STOPPED;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_PLAYING;
}
void pcm_play_lock(void)
{
- ++aic_lock;
+ int irq = disable_irq_save();
+ ++play_lock;
+ restore_irq(irq);
}
void pcm_play_unlock(void)
{
int irq = disable_irq_save();
- if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
- pcm_dma_handle_event(aic_dma_pending_event);
- aic_dma_pending_event = DMA_EVENT_NONE;
+ if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) {
+ play_dma_handle_event(play_dma_pending_event);
+ play_dma_pending_event = DMA_EVENT_NONE;
}
restore_irq(irq);
@@ -193,11 +213,56 @@ void pcm_play_unlock(void)
* Recording
*/
-/* FIXME need to implement this!! */
+static void rec_dma_start(void* addr, size_t size)
+{
+ /* NOTE: Rockbox always records in stereo and the AIC pushes in the
+ * sample for each channel separately. One frame therefore requires
+ * two 16-bit transfers from the AIC. */
+ rec_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(0), DAI(1), RDIL(6),
+ SP_V(16BIT), DP_V(16BIT), TSZ_V(16BIT),
+ STDE(0), TIE(1), LINK(0));
+ rec_dma_desc.sa = PHYSADDR(JA_AIC_DR);
+ rec_dma_desc.ta = PHYSADDR(addr);
+ rec_dma_desc.tc = size / 2;
+ rec_dma_desc.sd = 0;
+ rec_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_RX));
+ rec_dma_desc.pad0 = 0;
+ rec_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&rec_dma_desc, sizeof(dma_desc));
+ if((unsigned long)addr < 0xa0000000ul)
+ discard_dcache_range(addr, size);
+
+ REG_DMA_CHN_DA(DMA_CHANNEL_RECORD) = PHYSADDR(&rec_dma_desc);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), DES8(1), NDES(0));
+ jz_set(DMA_DB, 1 << DMA_CHANNEL_RECORD);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), CTE(1));
+
+ pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
+}
+
+static void rec_dma_handle_event(int event)
+{
+ if(event == DMA_EVENT_COMPLETE) {
+ void* addr;
+ size_t size;
+ if(pcm_rec_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
+ rec_dma_start(addr, size);
+ } else if(event == DMA_EVENT_NONE) {
+ /* ignored, so callers don't need to check for this */
+ } else {
+ pcm_rec_dma_status_callback(PCM_DMAST_ERR_DMA);
+ }
+}
static void pcm_rec_dma_int_cb(int event)
{
- (void)event;
+ if(rec_lock) {
+ rec_dma_pending_event = event;
+ return;
+ } else {
+ rec_dma_handle_event(event);
+ }
}
void pcm_rec_dma_init(void)
@@ -210,40 +275,52 @@ void pcm_rec_dma_close(void)
void pcm_rec_dma_start(void* addr, size_t size)
{
- (void)addr;
- (void)size;
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_RECORDING;
+
+ rec_dma_start(addr, size);
+ jz_writef(AIC_CCR, RDMS(1), EROR(1), EREC(1));
}
void pcm_rec_dma_stop(void)
{
+ jz_writef(AIC_CCR, RDMS(0), EROR(0), EREC(0));
+ jz_writef(AIC_CCR, RFLUSH(1));
+
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_RECORDING;
}
void pcm_rec_lock(void)
{
-
+ int irq = disable_irq_save();
+ ++rec_lock;
+ restore_irq(irq);
}
void pcm_rec_unlock(void)
{
+ int irq = disable_irq_save();
+ if(--rec_lock == 0 && (aic_state & AIC_STATE_RECORDING)) {
+ rec_dma_handle_event(rec_dma_pending_event);
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ }
+ restore_irq(irq);
}
const void* pcm_rec_dma_get_peak_buffer(void)
{
- return NULL;
-}
-
-void audio_set_output_source(int source)
-{
- (void)source;
+ return (const void*)UNCACHEDADDR(REG_DMA_CHN_TA(DMA_CHANNEL_RECORD));
}
+#endif /* HAVE_RECORDING */
-void audio_input_mux(int source, unsigned flags)
+#ifdef HAVE_PCM_DMA_ADDRESS
+void* pcm_dma_addr(void* addr)
{
- (void)source;
- (void)flags;
+ return (void*)UNCACHEDADDR(addr);
}
-#endif /* HAVE_RECORDING */
+#endif
void AIC(void)
{
@@ -251,4 +328,11 @@ void AIC(void)
aic_tx_underruns += 1;
jz_writef(AIC_SR, TUR(0));
}
+
+#ifdef HAVE_RECORDING
+ if(jz_readf(AIC_SR, ROR)) {
+ aic_rx_overruns += 1;
+ jz_writef(AIC_SR, ROR(0));
+ }
+#endif /* HAVE_RECORDING */
}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
index 7314f20412..787c35c494 100644
--- a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
@@ -59,7 +59,7 @@ static void codec_stop(void)
{
es9218_mute(true);
es9218_close();
- mdelay(1);
+ mdelay(4);
}
void audiohw_init(void)
@@ -154,7 +154,7 @@ void audiohw_set_filter_roll_off(int value)
void audiohw_set_power_mode(int mode)
{
enum es9218_amp_mode new_amp_mode;
- if(mode == 0)
+ if(mode == SOUND_HIGH_POWER)
new_amp_mode = ES9218_AMP_MODE_2VRMS;
else
new_amp_mode = ES9218_AMP_MODE_1VRMS;
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
index 17fbe1cede..59a2262f25 100644
--- a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
@@ -78,6 +78,19 @@ void power_init(void)
cw2015_init();
#endif
+ /* Set lowest sample rate */
+ axp_adc_set_rate(AXP_ADC_RATE_25HZ);
+
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
+
/* Change supply voltage from the default of 1250 mV to 1200 mV,
* this matches the original firmware's settings. Didn't observe
* any obviously bad behavior at 1250 mV, but better to be safe. */
@@ -121,6 +134,16 @@ int _battery_voltage(void)
return axp_adc_read(ADC_BATTERY_VOLTAGE);
}
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
+
#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) != 0
int _battery_level(void)
{
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
deleted file mode 100644
index 33303c5e6b..0000000000
--- a/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include "system.h"
-#include "clk-x1000.h"
-#include "spl-x1000.h"
-#include "gpio-x1000.h"
-
-#define CMDLINE_COMMON \
- "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
-#define CMDLINE_NORMAL \
- " init=/linuxrc ubi.mtd=5 root=ubi0:rootfs ubi.mtd=6 rootfstype=ubifs rw"
-
-static int dualboot_setup(void)
-{
- spl_dualboot_init_clocktree();
- spl_dualboot_init_uart2();
-
- /* load PDMA MCU firmware */
- jz_writef(CPM_CLKGR, PDMA(0));
- return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
-}
-
-const struct spl_boot_option spl_boot_options[] = {
- [BOOT_OPTION_ROCKBOX] = {
- .storage_addr = 0x6800,
- .storage_size = 102 * 1024,
- .load_addr = X1000_DRAM_BASE,
- .exec_addr = X1000_DRAM_BASE,
- .flags = BOOTFLAG_UCLPACK,
- },
- [BOOT_OPTION_OFW_PLAYER] = {
- .storage_addr = 0x140000,
- .storage_size = 8 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
- [BOOT_OPTION_OFW_RECOVERY] = {
- .storage_addr = 0x940000,
- .storage_size = 10 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
-};
-
-int spl_get_boot_option(void)
-{
- /* Button debounce time in OST clock cycles */
- const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
-
- /* Buttons to poll */
- const unsigned port = GPIO_B;
- const uint32_t recov_pin = (1 << 22); /* Next */
- const uint32_t orig_fw_pin = (1 << 21); /* Prev */
-
- uint32_t pin = -1, lastpin = 0;
- uint32_t deadline = 0;
- int iter_count = 30; /* to avoid an infinite loop */
-
- /* set GPIOs to input state */
- gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
-
- /* Poll until we get a stable reading */
- do {
- lastpin = pin;
- pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
- if(pin != lastpin) {
- deadline = __ost_read32() + btn_stable_time;
- iter_count -= 1;
- }
- } while(iter_count > 0 && __ost_read32() < deadline);
-
- if(iter_count >= 0 && (pin & orig_fw_pin)) {
- if(pin & recov_pin)
- return BOOT_OPTION_OFW_RECOVERY;
- else
- return BOOT_OPTION_OFW_PLAYER;
- }
-
- return BOOT_OPTION_ROCKBOX;
-}
-
-void spl_error(void)
-{
- /* Flash the backlight */
- int level = 0;
- while(1) {
- gpio_set_function(GPIO_PC(25), GPIOF_OUTPUT(level));
- mdelay(100);
- level = 1 - level;
- }
-}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c
index 9d7a1d118a..24eb42081e 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c
@@ -19,45 +19,32 @@
*
****************************************************************************/
-#include "system.h"
-#include "clk-x1000.h"
#include "spl-x1000.h"
#include "gpio-x1000.h"
+#include "nand-x1000.h"
-/* TODO: get dual-boot working */
+static struct nand_drv* ndrv = NULL;
-const struct spl_boot_option spl_boot_options[] = {
- [BOOT_OPTION_ROCKBOX] = {
- .storage_addr = 0x6800,
- .storage_size = 102 * 1024,
- .load_addr = X1000_DRAM_BASE,
- .exec_addr = X1000_DRAM_BASE,
- .flags = BOOTFLAG_UCLPACK,
- },
-};
-
-int spl_get_boot_option(void)
+int spl_storage_open(void)
{
- return BOOT_OPTION_ROCKBOX;
-}
+ /* We need to assign the GPIOs manually */
+ gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
-void spl_error(void)
-{
- const uint32_t pin = (1 << 25);
+ /* Allocate NAND driver manually in DRAM */
+ ndrv = spl_alloc(sizeof(struct nand_drv));
+ ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
+ ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
+ ndrv->refcount = 0;
- /* Turn on backlight */
- jz_clr(GPIO_INT(GPIO_C), pin);
- jz_set(GPIO_MSK(GPIO_C), pin);
- jz_clr(GPIO_PAT1(GPIO_C), pin);
- jz_set(GPIO_PAT0(GPIO_C), pin);
+ return nand_open(ndrv);
+}
- while(1) {
- /* Turn it off */
- mdelay(100);
- jz_set(GPIO_PAT0(GPIO_C), pin);
+void spl_storage_close(void)
+{
+ nand_close(ndrv);
+}
- /* Turn it on */
- mdelay(100);
- jz_clr(GPIO_PAT0(GPIO_C), pin);
- }
+int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
+{
+ return nand_read_bytes(ndrv, addr, length, buffer);
}
diff --git a/firmware/target/mips/ingenic_x1000/spl-start.S b/firmware/target/mips/ingenic_x1000/spl-start.S
index 58346fe750..ecdc47f283 100644
--- a/firmware/target/mips/ingenic_x1000/spl-start.S
+++ b/firmware/target/mips/ingenic_x1000/spl-start.S
@@ -31,7 +31,7 @@
.set noreorder
.set noat
- .section .init.spl
+ .section .startup.spl
_spl_start:
/* Clear data watchpoint */
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index afaf5a7dd6..08f88f506c 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -34,20 +34,49 @@
#include "ucl_decompress.h"
#include <string.h>
-#if defined(FIIO_M3K) || defined(SHANLING_Q1)
-# define SPL_DDR_MEMORYSIZE 64
-# define SPL_DDR_AUTOSR_EN 1
-# define SPL_DDR_NEED_BYPASS 1
+#if defined(FIIO_M3K)
+/* Size of memory, either 64 or 32 is legal. */
+# define SPL_DDR_MEMORYSIZE 64
+/* Pin to flash on spl_error(). Should be a backlight. */
+# define SPL_ERROR_PIN GPIO_PC(24)
+/* Address and size of the bootloader on the storage medium used by the SPL */
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
+#elif defined(SHANLING_Q1)
+# define SPL_DDR_MEMORYSIZE 64
+# define SPL_ERROR_PIN GPIO_PC(25)
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
#elif defined(EROS_QN)
-# define SPL_DDR_MEMORYSIZE 32
-# define SPL_DDR_AUTOSR_EN 1
-# define SPL_DDR_NEED_BYPASS 1
+# define SPL_DDR_MEMORYSIZE 32
+# define SPL_ERROR_PIN GPIO_PC(25)
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
#else
-# error "please define DRAM settings"
+# error "please define SPL config"
+#endif
+
+/* Hardcode this since the SPL is considered part of the bootloader,
+ * and should never get built or updated separately. */
+#define BOOT_LOAD_ADDR X1000_DRAM_BASE
+#define BOOT_EXEC_ADDR BOOT_LOAD_ADDR
+
+/* Whether the bootloader is UCL-compressed */
+#ifndef SPL_USE_UCLPACK
+# define SPL_USE_UCLPACK 1
+#endif
+
+/* Whether auto-self-refresh should be enabled (seems it always should be?) */
+#ifndef SPL_DDR_AUTOSR_EN
+# define SPL_DDR_AUTOSR_EN 1
+#endif
+
+/* Whether DLL bypass is necessary (probably always?) */
+#ifndef SPL_DDR_NEED_BYPASS
+# define SPL_DDR_NEED_BYPASS 1
#endif
static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE);
-static nand_drv* ndrv = NULL;
void* spl_alloc(size_t count)
{
@@ -56,114 +85,14 @@ void* spl_alloc(size_t count)
return heap;
}
-int spl_storage_open(void)
-{
- /* We need to assign the GPIOs manually */
- gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
-
- /* Allocate NAND driver manually in DRAM */
- ndrv = spl_alloc(sizeof(nand_drv));
- ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
- ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
- ndrv->refcount = 0;
-
- return nand_open(ndrv);
-}
-
-void spl_storage_close(void)
+void spl_error(void)
{
- nand_close(ndrv);
-}
-
-int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
-{
- return nand_read_bytes(ndrv, addr, length, buffer);
-}
-
-/* Used by:
- * - FiiO M3K
- * - Shanling Q1
- *
- * Amend it and add #ifdefs for other targets if needed.
- */
-void spl_dualboot_init_clocktree(void)
-{
- /* Make sure these are gated to match the OF behavior. */
- jz_writef(CPM_CLKGR, PCM(1), MAC(1), LCD(1), MSC0(1), MSC1(1), OTG(1), CIM(1));
-
- /* Set clock sources, and make sure every clock starts out stopped */
- jz_writef(CPM_I2SCDR, CS_V(EXCLK));
- jz_writef(CPM_PCMCDR, CS_V(EXCLK));
-
- jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_MACCDR, BUSY));
-
- jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_LPCDR, BUSY));
-
- jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_MSC0CDR, BUSY));
-
- jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_MSC1CDR, BUSY));
-
- jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_CIMCDR, BUSY));
-
- jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
- while(jz_readf(CPM_USBCDR, BUSY));
-}
-
-void spl_dualboot_init_uart2(void)
-{
- /* Ungate the clock and select UART2 device function */
- jz_writef(CPM_CLKGR, UART2(0));
- gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
-
- /* Disable all interrupts */
- jz_write(UART_UIER(2), 0);
-
- /* FIFO configuration */
- jz_overwritef(UART_UFCR(2),
- RDTR(3), /* FIFO trigger level = 60? */
- UME(0), /* UART module disable */
- DME(1), /* DMA mode enable? */
- TFRT(1), /* transmit FIFO reset */
- RFRT(1), /* receive FIFO reset */
- FME(1)); /* FIFO mode enable */
-
- /* IR mode configuration */
- jz_overwritef(UART_ISR(2),
- RDPL(1), /* Zero is negative pulse for receive */
- TDPL(1), /* ... and for transmit */
- XMODE(1), /* Pulse width 1.6us */
- RCVEIR(0), /* Disable IR for recieve */
- XMITIR(0)); /* ... and for transmit */
-
- /* Line configuration */
- jz_overwritef(UART_ULCR(2), DLAB(0),
- WLS_V(8BITS), /* 8 bit words */
- SBLS_V(1_STOP_BIT), /* 1 stop bit */
- PARE(0), /* no parity */
- SBK(0)); /* don't set break */
-
- /* Set the baud rate... not too sure how this works. (Docs unclear!) */
- const unsigned divisor = 0x0004;
- jz_writef(UART_ULCR(2), DLAB(1));
- jz_write(UART_UDLHR(2), (divisor >> 8) & 0xff);
- jz_write(UART_UDLLR(2), divisor & 0xff);
- jz_write(UART_UMR(2), 16);
- jz_write(UART_UACR(2), 0);
- jz_writef(UART_ULCR(2), DLAB(0));
-
- /* Enable UART */
- jz_overwritef(UART_UFCR(2),
- RDTR(0), /* FIFO trigger level = 1 */
- DME(0), /* DMA mode disable */
- UME(1), /* UART module enable */
- TFRT(1), /* transmit FIFO reset */
- RFRT(1), /* receive FIFO reset */
- FME(1)); /* FIFO mode enable */
+ int level = 0;
+ while(1) {
+ gpio_set_function(SPL_ERROR_PIN, GPIOF_OUTPUT(level));
+ mdelay(100);
+ level = 1 - level;
+ }
}
static void init_ost(void)
@@ -346,14 +275,14 @@ static int init_dram(void)
return 0;
}
-static void* get_load_buffer(const struct spl_boot_option* opt)
+static void* get_load_buffer(void)
{
/* read to a temporary location if we need to decompress,
* otherwise simply read directly to the load address. */
- if(opt->flags & BOOTFLAG_UCLPACK)
- return spl_alloc(opt->storage_size);
+ if(SPL_USE_UCLPACK)
+ return spl_alloc(BOOT_STORAGE_SIZE);
else
- return (void*)opt->load_addr;
+ return (void*)BOOT_LOAD_ADDR;
}
/* Mapping of boot_sel[1:0] pins.
@@ -377,15 +306,10 @@ static uint32_t get_boot_sel(void)
return (*(uint32_t*)0xf40001ec) & 3;
}
-typedef void(*entry_fn)(int, char**, int, int) __attribute__((noreturn));
-
void spl_main(void)
{
- int rc, boot_option;
- const struct spl_boot_option* opt;
+ int rc;
void* load_buffer;
- char** kargv = NULL;
- int kargc = 0;
/* magic */
REG_CPM_PSWC0ST = 0x00;
@@ -393,9 +317,11 @@ void spl_main(void)
REG_CPM_PSWC2ST = 0x18;
REG_CPM_PSWC3ST = 0x08;
+ /* Save this, it's needed on some targets */
+ uint32_t saved_cpm_scratch = REG_CPM_SCRATCH;
+
/* set up boot flags */
init_boot_flags();
- set_boot_option(BOOT_OPTION_ROCKBOX);
/* early clock and DRAM init */
clk_init_early();
@@ -409,14 +335,6 @@ void spl_main(void)
return;
}
- /* find out what we should boot */
- boot_option = spl_get_boot_option();
- opt = &spl_boot_options[boot_option];
- load_buffer = get_load_buffer(opt);
-
- /* save the selection for later */
- set_boot_option(boot_option);
-
/* finish up clock init */
clk_init();
@@ -425,44 +343,29 @@ void spl_main(void)
if(rc != 0)
spl_error();
- rc = spl_storage_read(opt->storage_addr, opt->storage_size, load_buffer);
+ load_buffer = get_load_buffer();
+ rc = spl_storage_read(BOOT_STORAGE_ADDR, BOOT_STORAGE_SIZE, load_buffer);
if(rc != 0)
spl_error();
- /* handle compression */
- switch(opt->flags & BOOTFLAG_COMPRESSED) {
- case BOOTFLAG_UCLPACK: {
- uint32_t out_size = X1000_DRAM_END - opt->load_addr;
- rc = ucl_unpack((uint8_t*)load_buffer, opt->storage_size,
- (uint8_t*)opt->load_addr, &out_size);
- } break;
-
- default:
- break;
+ /* decompress */
+ if(SPL_USE_UCLPACK) {
+ uint32_t out_size = X1000_SDRAM_END - BOOT_LOAD_ADDR;
+ rc = ucl_unpack((uint8_t*)load_buffer, BOOT_STORAGE_SIZE,
+ (uint8_t*)BOOT_LOAD_ADDR, &out_size);
+ } else {
+ rc = 0;
}
if(rc != 0)
spl_error();
- /* call the setup hook */
- if(opt->setup) {
- rc = opt->setup();
- if(rc != 0)
- spl_error();
- }
-
/* close off storage access */
spl_storage_close();
- /* handle kernel command line, if specified */
- if(opt->cmdline) {
- kargv = (char**)opt->cmdline_addr;
- kargv[kargc++] = 0;
- kargv[kargc++] = (char*)opt->cmdline;
- }
-
/* jump to the entry point */
- entry_fn fn = (entry_fn)opt->exec_addr;
+ typedef void(*entry_fn)(uint32_t);
+ entry_fn fn = (entry_fn)BOOT_EXEC_ADDR;
commit_discard_idcache();
- fn(kargc, kargv, 0, 0);
+ fn(saved_cpm_scratch);
}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h
index 6d60dbf880..9ee1aa768e 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h
@@ -26,23 +26,6 @@
#include <stddef.h>
#include <stdint.h>
-#define BOOTFLAG_COMPRESSED 0x0f /* mask for compression flags */
-#define BOOTFLAG_UCLPACK 0x01 /* image is compressed with 'uclpack' */
-
-struct spl_boot_option {
- uint32_t storage_addr; /* image's location in storage */
- uint32_t storage_size; /* number of bytes to load */
- uint32_t load_addr; /* address to load image to */
- uint32_t exec_addr; /* address of the entry point */
- uint32_t flags; /* any special flags */
- const char* cmdline; /* command line; use NULL if not needed */
- uint32_t cmdline_addr; /* address to contain command line 'argv[]' */
- int(*setup)(void); /* setup hook, called before jumping to image */
-};
-
-/* array of boot option descriptions */
-extern const struct spl_boot_option spl_boot_options[];
-
/* Memory allocator. Allocation starts from the top of DRAM and counts down.
* Allocation sizes are rounded up to a multiple of the cacheline size, so
* the returned address is always suitably aligned for DMA. */
@@ -58,13 +41,6 @@ extern int spl_storage_open(void);
extern void spl_storage_close(void);
extern int spl_storage_read(uint32_t addr, uint32_t length, void* buffer);
-/* Helpers for dual-booting with the Ingenic Linux OF */
-extern void spl_dualboot_init_clocktree(void);
-extern void spl_dualboot_init_uart2(void);
-
-/* Get the boot option selected by the user, eg. by a key press */
-extern int spl_get_boot_option(void);
-
/* Called on a fatal error -- it should do something visible to the user
* like flash the backlight repeatedly. */
extern void spl_error(void) __attribute__((noreturn));
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds
index b0169ab1aa..b3e508e9c3 100644
--- a/firmware/target/mips/ingenic_x1000/spl.lds
+++ b/firmware/target/mips/ingenic_x1000/spl.lds
@@ -7,19 +7,18 @@ ENTRY(_spl_start)
STARTUP(target/mips/ingenic_x1000/spl-start.o)
MEMORY {
- /* First 4k of TCSM is used by mask ROM for stack + variables,
- * and the next 2k are occupied by SPL header */
- TCSM : ORIGIN = X1000_TCSM_BASE + 0x1800,
- LENGTH = X1000_TCSM_SIZE - 0x1800
+ TCSM : ORIGIN = X1000_SPL_EXEC_ADDR,
+ LENGTH = X1000_SPL_SIZE
}
SECTIONS
{
.text :
{
- *(.init.spl);
+ *(.startup.spl);
*(.text*);
*(.icode*);
+ *(.init*);
} > TCSM
. = ALIGN(4);
diff --git a/firmware/target/mips/ingenic_x1000/system-target.h b/firmware/target/mips/ingenic_x1000/system-target.h
index 7cea654865..ed077a3cce 100644
--- a/firmware/target/mips/ingenic_x1000/system-target.h
+++ b/firmware/target/mips/ingenic_x1000/system-target.h
@@ -31,6 +31,7 @@
#include "mmu-mips.h"
#include "mipsregs.h"
#include "mipsr2-endian.h"
+#include "system-mips.h"
#include <stdint.h>
/* Rockbox API */
@@ -96,6 +97,8 @@ extern irq_handler_t system_set_irq_handler(int irq, irq_handler_t handler);
extern void system_enable_irq(int irq);
extern void system_disable_irq(int irq);
+extern void system_early_init(void) INIT_ATTR;
+
/* Simple delay API */
#define OST_FREQUENCY (X1000_EXCLK_FREQ / 4)
#define OST_TICKS_PER_US (OST_FREQUENCY / 1000000)
diff --git a/firmware/target/mips/ingenic_x1000/system-x1000.c b/firmware/target/mips/ingenic_x1000/system-x1000.c
index d43c8e67e4..64890a6c3a 100644
--- a/firmware/target/mips/ingenic_x1000/system-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/system-x1000.c
@@ -44,6 +44,7 @@ uint32_t __cpu_idle_reftick = 0;
#endif
/* Prepare the CPU to process interrupts, but don't enable them yet */
+static void system_init_irq(void) INIT_ATTR;
static void system_init_irq(void)
{
/* Mask all interrupts */
@@ -72,7 +73,6 @@ void system_early_init(void)
* This hack should keep everything working as usual. */
if(jz_readf(CPM_MPCR, ON) == 0) {
init_boot_flags();
- set_boot_option(BOOT_OPTION_ROCKBOX);
set_boot_flag(BOOT_FLAG_CLK_INIT);
}
#endif
@@ -344,8 +344,10 @@ static int vector_irq(void)
return n;
}
-void intr_handler(unsigned cause)
+void intr_handler(void)
{
+ unsigned long cause = read_c0_cause();
+
/* OST interrupt is handled separately */
if(cause & M_CauseIP3) {
OST();
@@ -364,49 +366,6 @@ void intr_handler(unsigned cause)
irqvector[irq]();
}
-void tlb_refill_handler(void)
-{
- panicf("TLB refill handler at 0x%08lx! [0x%x]",
- read_c0_epc(), read_c0_badvaddr());
-}
-
-#define EXC(x,y) case (x): return (y);
-static char* parse_exception(unsigned cause)
-{
- switch(cause & M_CauseExcCode)
- {
- EXC(EXC_INT, "Interrupt");
- EXC(EXC_MOD, "TLB Modified");
- EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
- EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
- EXC(EXC_ADES, "Address Error (Store)");
- EXC(EXC_TLBS, "TLB Exception (Store)");
- EXC(EXC_IBE, "Instruction Bus Error");
- EXC(EXC_DBE, "Data Bus Error");
- EXC(EXC_SYS, "Syscall");
- EXC(EXC_BP, "Breakpoint");
- EXC(EXC_RI, "Reserved Instruction");
- EXC(EXC_CPU, "Coprocessor Unusable");
- EXC(EXC_OV, "Overflow");
- EXC(EXC_TR, "Trap Instruction");
- EXC(EXC_FPE, "Floating Point Exception");
- EXC(EXC_C2E, "COP2 Exception");
- EXC(EXC_MDMX, "MDMX Exception");
- EXC(EXC_WATCH, "Watch Exception");
- EXC(EXC_MCHECK, "Machine Check Exception");
- EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
- default:
- return 0;
- }
-}
-#undef EXC
-
-void exception_handler(unsigned cause, unsigned epc, unsigned stack_ptr)
-{
- panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)",
- parse_exception(cause), read_c0_badvaddr(), epc, stack_ptr);
-}
-
void system_exception_wait(void)
{
#ifdef FIIO_M3K
diff --git a/firmware/target/mips/mmu-mips.h b/firmware/target/mips/mmu-mips.h
index b8f5ff0143..ca865f9909 100644
--- a/firmware/target/mips/mmu-mips.h
+++ b/firmware/target/mips/mmu-mips.h
@@ -36,7 +36,11 @@ void map_address(unsigned long virtual, unsigned long physical,
void mmu_init(void);
/* Commits entire DCache */
-MIPS_CACHEFUNC_API(void, commit_dcache, (void));
+#if 0 /* NOTE: This is currently aliased to commit_discard_dcache. Causes compilation errors with newer GCC if we try to assign it to a section here */
+//MIPS_CACHEFUNC_API(void, commit_dcache, (void));
+#else
+void commit_dcache(void);
+#endif
/* Commit and discard entire DCache, will do writeback */
MIPS_CACHEFUNC_API(void, commit_discard_dcache, (void));
diff --git a/firmware/target/mips/system-mips.c b/firmware/target/mips/system-mips.c
new file mode 100644
index 0000000000..e6bb2d8ea6
--- /dev/null
+++ b/firmware/target/mips/system-mips.c
@@ -0,0 +1,175 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "backtrace.h"
+#include "lcd.h"
+#include "backlight-target.h"
+#include "font.h"
+#include "logf.h"
+#include "mips.h"
+#undef sp /* breaks backtrace lib */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* copies exception frame info and error pc to the backtrace context */
+static void setup_exception_bt(struct mips_exception_frame* frame,
+ unsigned long epc, struct mips_bt_context* ctx)
+{
+ ctx->pc = (void*)epc;
+ ctx->sp = (void*)frame->gpr[29 - 3];
+ ctx->depth = 0;
+ ctx->valid = (1 << MIPSBT_RA);
+ ctx->reg[MIPSBT_RA] = frame->gpr[31 - 3];
+}
+
+/* dump backtrace for an exception */
+static void exception_bt(void* frame, unsigned long epc, unsigned* line)
+{
+ struct mips_bt_context ctx;
+ setup_exception_bt(frame, epc, &ctx);
+ rb_backtrace_ctx(&ctx, line);
+}
+
+/*
+ * TODO: This should be converted into a generic panic routine that accepts
+ * a backtrace context argument but the ARM backtrace setup will need to be
+ * refactored in order to do that
+ */
+static void exception_dump(void* frame, unsigned long epc,
+ const char* fmt, ...)
+{
+ extern char panic_buf[128];
+ va_list ap;
+
+ set_irq_level(DISABLE_INTERRUPTS);
+
+ va_start(ap, fmt);
+ vsnprintf(panic_buf, sizeof(panic_buf), fmt, ap);
+ va_end(ap);
+
+ lcd_set_viewport(NULL);
+#if LCD_DEPTH > 1
+ lcd_set_backdrop(NULL);
+ lcd_set_drawmode(DRMODE_SOLID);
+ lcd_set_foreground(LCD_BLACK);
+ lcd_set_background(LCD_WHITE);
+#endif
+
+ lcd_clear_display();
+ lcd_setfont(FONT_SYSFIXED);
+
+ unsigned y = 1;
+ lcd_puts(1, y++, "*EXCEPTION*");
+
+ /* wrap panic message */
+ {
+ const int linechars = (LCD_WIDTH / SYSFONT_WIDTH) - 2;
+
+ int pos = 0, len = strlen(panic_buf);
+ while(len > 0) {
+ int idx = pos + MIN(len, linechars);
+ char c = panic_buf[idx];
+ panic_buf[idx] = 0;
+ lcd_puts(1, y++, &panic_buf[pos]);
+ panic_buf[idx] = c;
+
+ len -= linechars;
+ pos += linechars;
+ }
+ }
+
+ exception_bt(frame, epc, &y);
+#ifdef ROCKBOX_HAS_LOGF
+ logf_panic_dump(&y);
+#endif
+
+ lcd_update();
+ backlight_hw_on();
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ if (cpu_boost_lock())
+ {
+ set_cpu_frequency(0);
+ cpu_boost_unlock();
+ }
+#endif
+
+#ifdef HAVE_ATA_POWER_OFF
+ ide_power_enable(false);
+#endif
+
+ system_exception_wait();
+ system_reboot();
+ while(1);
+}
+
+#define EXC(x,y) case (x): return (y);
+static char* parse_exception(unsigned long cause)
+{
+ switch(cause & M_CauseExcCode)
+ {
+ EXC(EXC_INT, "Interrupt");
+ EXC(EXC_MOD, "TLB Modified");
+ EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
+ EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
+ EXC(EXC_ADES, "Address Error (Store)");
+ EXC(EXC_TLBS, "TLB Exception (Store)");
+ EXC(EXC_IBE, "Instruction Bus Error");
+ EXC(EXC_DBE, "Data Bus Error");
+ EXC(EXC_SYS, "Syscall");
+ EXC(EXC_BP, "Breakpoint");
+ EXC(EXC_RI, "Reserved Instruction");
+ EXC(EXC_CPU, "Coprocessor Unusable");
+ EXC(EXC_OV, "Overflow");
+ EXC(EXC_TR, "Trap Instruction");
+ EXC(EXC_FPE, "Floating Point Exception");
+ EXC(EXC_C2E, "COP2 Exception");
+ EXC(EXC_MDMX, "MDMX Exception");
+ EXC(EXC_WATCH, "Watch Exception");
+ EXC(EXC_MCHECK, "Machine Check Exception");
+ EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
+ default:
+ return 0;
+ }
+}
+#undef EXC
+
+void exception_handler(void* frame, unsigned long epc)
+{
+ unsigned long cause = read_c0_cause();
+
+ exception_dump(frame, epc, "%s [0x%08x] at %08lx",
+ parse_exception(cause), read_c0_badvaddr(), epc);
+}
+
+void cache_error_handler(void* frame, unsigned long epc)
+{
+ exception_dump(frame, epc, "Cache Error [0x%08x] at %08lx",
+ read_c0_cacheerr(), epc);
+}
+
+void tlb_refill_handler(void* frame, unsigned long epc)
+{
+ exception_dump(frame, epc, "TLB refill at %08lx [0x%x]",
+ epc, read_c0_badvaddr());
+}
diff --git a/firmware/target/mips/system-mips.h b/firmware/target/mips/system-mips.h
new file mode 100644
index 0000000000..d9108ef7c2
--- /dev/null
+++ b/firmware/target/mips/system-mips.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef SYSTEM_MIPS_H
+#define SYSTEM_MIPS_H
+
+#include <stdint.h>
+
+struct mips_exception_frame {
+ uint32_t gpr[29]; /* GPRs $1-$25, $28-$31 */
+ uint32_t lo;
+ uint32_t hi;
+ uint32_t c0_status;
+ uint32_t c0_epc;
+};
+
+void intr_handler(void);
+void exception_handler(void* frame, unsigned long epc);
+void cache_error_handler(void* frame, unsigned long epc);
+void tlb_refill_handler(void* frame, unsigned long epc);
+
+#endif /* SYSTEM_MIPS_H */
diff --git a/firmware/usb.c b/firmware/usb.c
index 4c122e8eea..32f4902c7c 100644
--- a/firmware/usb.c
+++ b/firmware/usb.c
@@ -65,6 +65,9 @@
#define USB_FULL_INIT
#endif
+/* USB detect debouncing interval (200ms taken from the usb polling code) */
+#define USB_DEBOUNCE_TIME (200*HZ/1000)
+
bool do_screendump_instead_of_usb = false;
#if !defined(SIMULATOR) && !defined(USB_NONE)
@@ -491,37 +494,12 @@ static void NORETURN_ATTR usb_thread(void)
if (button_status() & ~USBPOWER_BTN_IGNORE)
new_usbmode = USB_MODE_MASS_STORAGE;
break;
-#ifndef BOOTLOADER
- case USB_MODE_ASK:
- new_usbmode = USB_MODE_ASK;
- break;
-#endif
default:
case USB_MODE_MASS_STORAGE:
if (button_status() & ~USBPOWER_BTN_IGNORE)
new_usbmode = USB_MODE_CHARGE;
break;
}
-
-#ifndef BOOTLOADER
- if (new_usbmode == USB_MODE_ASK)
- {
- push_current_activity(ACTIVITY_USBSCREEN);
- if (yesno_pop(ID2P(LANG_ENTER_USB_STORAGE_MODE_QUERY)))
- new_usbmode = USB_MODE_MASS_STORAGE;
- else
- new_usbmode = USB_MODE_CHARGE;
- pop_current_activity();
- /* Force full redraw */
-// queue_post(&button_queue, BUTTON_REDRAW, 0);
-// Alternative approach, as above is supposedly inadequate by design.
- FOR_NB_SCREENS(i)
- {
- struct screen *screen = &screens[i];
- screen->set_viewport(NULL);
- }
- }
-#endif
#endif
#ifndef USB_DETECT_BY_REQUEST
@@ -605,8 +583,33 @@ void usb_charger_update(void)
#endif
#ifdef USB_STATUS_BY_EVENT
+static int usb_status_tmo_callback(struct timeout* tmo)
+{
+ if(usb_monitor_enabled)
+ {
+ int current_status = usb_detect();
+ int* last_status = (int*)tmo->data;
+
+ if(current_status != *last_status)
+ {
+ /* Signal changed during the timeout; wait longer */
+ *last_status = current_status;
+ return USB_DEBOUNCE_TIME;
+ }
+
+ /* Signal is stable, post the event. The thread will deal with
+ * any spurious transitions (like inserted -> inserted). */
+ queue_post(&usb_queue, current_status, 0);
+ }
+
+ return 0;
+}
+
void usb_status_event(int current_status)
{
+ static struct timeout tmo;
+ static int last_status = USB_EXTRACTED;
+
/* Caller isn't expected to filter for changes in status.
* current_status:
* USB_INSERTED, USB_EXTRACTED
@@ -614,8 +617,9 @@ void usb_status_event(int current_status)
if(usb_monitor_enabled)
{
int oldstatus = disable_irq_save(); /* Dual-use function */
- queue_remove_from_head(&usb_queue, current_status);
- queue_post(&usb_queue, current_status, 0);
+ last_status = current_status;
+ timeout_register(&tmo, usb_status_tmo_callback, USB_DEBOUNCE_TIME,
+ (intptr_t)&last_status);
restore_irq(oldstatus);
}
}
@@ -651,7 +655,6 @@ void usb_firewire_connect_event(void)
static void usb_tick(void)
{
- #define NUM_POLL_READINGS (HZ/5)
static int usb_countdown = -1;
static int last_usb_status = USB_EXTRACTED;
#ifdef USB_FIREWIRE_HANDLING
@@ -666,7 +669,7 @@ static void usb_tick(void)
if(current_firewire_status != last_firewire_status)
{
last_firewire_status = current_firewire_status;
- firewire_countdown = NUM_POLL_READINGS;
+ firewire_countdown = USB_DEBOUNCE_TIME;
}
else
{
@@ -674,8 +677,7 @@ static void usb_tick(void)
if(firewire_countdown >= 0)
firewire_countdown--;
- /* Report to the thread if we have had 3 identical status
- readings in a row */
+ /* Report status when the signal has been stable long enough */
if(firewire_countdown == 0)
{
queue_post(&usb_queue, USB_REQUEST_REBOOT, 0);
@@ -689,7 +691,7 @@ static void usb_tick(void)
if(current_status != last_usb_status)
{
last_usb_status = current_status;
- usb_countdown = NUM_POLL_READINGS;
+ usb_countdown = USB_DEBOUNCE_TIME;
}
else
{
@@ -697,8 +699,7 @@ static void usb_tick(void)
if(usb_countdown >= 0)
usb_countdown--;
- /* Report to the thread if we have had 3 identical status
- readings in a row */
+ /* Report status when the signal has been stable long enough */
if(usb_countdown == 0)
{
queue_post(&usb_queue, current_status, 0);
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
index 63df173033..9fe8b3e603 100644
--- a/firmware/usbstack/usb_core.c
+++ b/firmware/usbstack/usb_core.c
@@ -264,7 +264,9 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
};
#ifdef USB_LEGACY_CONTROL_API
+static struct usb_ctrlrequest buffered_request;
static struct usb_ctrlrequest* volatile active_request = NULL;
+static volatile unsigned int num_active_requests = 0;
static void* volatile control_write_data = NULL;
static volatile bool control_write_data_done = false;
#endif
@@ -1019,20 +1021,56 @@ void usb_core_control_complete(int status)
/* Only needed if the driver does not support the new API yet */
void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
{
- active_request = req;
- control_write_data = NULL;
- control_write_data_done = false;
+ /* Only submit non-overlapping requests */
+ if (num_active_requests++ == 0)
+ {
+ buffered_request = *req;
+ active_request = &buffered_request;
+ control_write_data = NULL;
+ control_write_data_done = false;
- usb_core_control_request(req, NULL);
+ usb_core_control_request(req, NULL);
+ }
}
void usb_drv_control_response(enum usb_control_response resp,
void* data, int length)
{
struct usb_ctrlrequest* req = active_request;
+ unsigned int num_active = num_active_requests--;
+
+ /*
+ * There should have been a prior request submission, at least.
+ * FIXME: It seems the iPod video can get here and ignoring it
+ * allows the connection to succeed??
+ */
+ if (num_active == 0)
+ {
+ //panicf("null ctrl req");
+ return;
+ }
- if(!req)
- panicf("null ctrl req");
+ /*
+ * This can happen because an active request was already pending when
+ * the driver submitted a new one in usb_core_legacy_control_request().
+ * This could mean two things: (a) a driver bug; or (b) the host sent
+ * another request because we were too slow in handling an earlier one.
+ *
+ * The USB spec requires we respond to the latest request and drop any
+ * earlier ones, but that's not easy to do with the current design of
+ * the USB stack. Thus, the host will be expecting a response for the
+ * latest request, but this response is for the _earliest_ request.
+ *
+ * Play it safe and return a STALL. At this point we've recovered from
+ * the error on our end and will be ready to handle the next request.
+ */
+ if (num_active > 1)
+ {
+ active_request = NULL;
+ num_active_requests = 0;
+ usb_drv_stall(EP_CONTROL, true, true);
+ return;
+ }
if(req->wLength == 0)
{
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index a32cf185e7..714af9d535 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -449,12 +449,10 @@ void usb_storage_init_connection(void)
#endif
#else
unsigned char * buffer;
- /* dummy ops with no callbacks, needed because by
- * default buflib buffers can be moved around which must be avoided */
- static struct buflib_callbacks dummy_ops;
// Add 31 to handle worst-case misalignment
- usb_handle = core_alloc_ex("usb storage", ALLOCATE_BUFFER_SIZE + MAX_CBW_SIZE + 31, &dummy_ops);
+ usb_handle = core_alloc_ex(ALLOCATE_BUFFER_SIZE + MAX_CBW_SIZE + 31,
+ &buflib_ops_locked);
if (usb_handle < 0)
panicf("%s(): OOM", __func__);
@@ -482,8 +480,7 @@ void usb_storage_init_connection(void)
void usb_storage_disconnect(void)
{
- if (usb_handle > 0)
- usb_handle = core_free(usb_handle);
+ usb_handle = core_free(usb_handle);
}
/* called by usb_core_transfer_complete() */