diff options
Diffstat (limited to 'firmware')
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] = ®15_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] = ®16_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, ®s[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, ®s[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, §or); inode = (struct cfs_inode*)§or; #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() */ |