diff options
Diffstat (limited to 'firmware')
412 files changed, 19113 insertions, 11237 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES index 0a6d4d30c3..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 @@ -242,9 +221,6 @@ common/crc32.c #ifdef MODEL_NUMBER common/loader_strerror.c #endif -#ifdef MI4_FORMAT -common/crc32-mi4.c -#endif #ifdef RKW_FORMAT common/crc32-rkw.c #endif @@ -262,15 +238,51 @@ 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 +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 @@ -505,6 +517,11 @@ drivers/audio/pcm1792.c drivers/audio/cs4398.c #elif defined (HAVE_ES9018) drivers/audio/es9018.c +#elif defined (HAVE_ES9218) +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) @@ -539,6 +556,15 @@ target/hosted/sdl/pcm-sdl.c #endif /* !defined(BOOTLOADER) */ +/* WiFi */ +#if !defined(BOOTLOADER) +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) +#if defined(HAVE_W8686_SPI) +drivers/libertas/if_spi.c +#endif +#endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) */ +#endif /* !defined(BOOTLOADER) */ + /* CPU Specific - By class then particular chip if applicable */ #if defined(CPU_COLDFIRE) @@ -761,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 @@ -1267,8 +1297,10 @@ target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c +target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c +target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c target/arm/tms320dm320/dma-dm320.c #endif /* SANSA_CONNECT */ @@ -1286,14 +1318,6 @@ target/arm/olympus/mrobe-100/power-mr100.c target/arm/olympus/mrobe-100/powermgmt-mr100.c #endif /* MROBE_100 */ -#ifdef TATUNG_TPJ1022 -target/arm/tatung/tpj1022/backlight-tpj1022.c -target/arm/tatung/tpj1022/button-tpj1022.c -target/arm/tatung/tpj1022/lcd-tpj1022.c -target/arm/tatung/tpj1022/power-tpj1022.c -target/arm/tatung/tpj1022/powermgmt-tpj1022.c -#endif /* TATUNG_TPJ1022 */ - #ifdef IPOD_4G target/arm/ipod/backlight-4g_color.c target/arm/ipod/button-clickwheel.c @@ -1435,17 +1459,6 @@ target/arm/imx233/sansa-fuzeplus/debug-fuzeplus.c target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c #endif -#ifdef SAMSUNG_YPZ5 -target/arm/imx233/samsung-ypz5/backlight-ypz5.c -target/arm/imx233/samsung-ypz5/lcd-ypz5.c -target/arm/imx233/samsung-ypz5/button-ypz5.c -target/arm/imx233/samsung-ypz5/debug-ypz5.c -target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c -#ifndef BOOTLOADER -target/arm/imx233/fmradio-imx233.c -#endif -#endif - #ifdef SANSA_CLIPZIP target/arm/as3525/sansa-clipzip/lcd-clipzip.c target/arm/as3525/sansa-clipzip/button-clipzip.c @@ -1643,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 @@ -1667,9 +1680,16 @@ target/mips/ingenic_x1000/msc-x1000.c #if (CONFIG_STORAGE & STORAGE_SD) target/mips/ingenic_x1000/sd-x1000.c #endif +#ifdef BOOTLOADER +target/mips/ingenic_x1000/installer-x1000.c 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) @@ -1701,12 +1721,27 @@ 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 -#ifdef BOOTLOADER -target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c -#endif +target/mips/ingenic_x1000/spl-nand-x1000.c #endif /* FIIO_M3K */ +#if defined(SHANLING_Q1) +target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c +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/spl-nand-x1000.c +#endif /* SHANLING_Q1 */ + +#if defined(EROS_QN) +target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c +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/spl-nand-x1000.c +#endif /* EROS_QN */ + #if defined(LYRE_PROTO1) target/arm/at91sam/lyre_proto1/adc-lyre_proto1.c target/arm/at91sam/lyre_proto1/backlight-lyre_proto1.c @@ -1939,6 +1974,9 @@ drivers/axp-pmu.c #ifdef HAVE_FT6x06 drivers/ft6x06.c #endif +#ifdef HAVE_CW2015 +drivers/cw2015.c +#endif #endif /* firmware/kernel section */ 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/adler32.c b/firmware/common/adler32.c new file mode 100644 index 0000000000..419eb02e44 --- /dev/null +++ b/firmware/common/adler32.c @@ -0,0 +1,97 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 James Buren (adaptations from tinf/zlib) + * + * 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. + * + ****************************************************************************/ + +/* Copyright (c) 2003-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +#include "adler32.h" +#include "system.h" + +/* adler_32 (derived from tinf adler32 which was taken from zlib) + * Adler-32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ +uint32_t adler_32(const void *src, uint32_t len, uint32_t adler32) +{ + const unsigned char *buf = (const unsigned char *)src; + uint32_t s1 = (adler32 & 0xffff); + uint32_t s2 = (adler32 >> 16); + + enum { + A32_BASE = 65521, + A32_NMAX = 5552, + }; + + while (len > 0) { + uint32_t k = MIN(len, A32_NMAX); + uint32_t i; + + for (i = k / 16; i; --i, buf += 16) { + s2 += s1 += buf[0]; + s2 += s1 += buf[1]; + s2 += s1 += buf[2]; + s2 += s1 += buf[3]; + s2 += s1 += buf[4]; + s2 += s1 += buf[5]; + s2 += s1 += buf[6]; + s2 += s1 += buf[7]; + s2 += s1 += buf[8]; + s2 += s1 += buf[9]; + s2 += s1 += buf[10]; + s2 += s1 += buf[11]; + s2 += s1 += buf[12]; + s2 += s1 += buf[13]; + s2 += s1 += buf[14]; + s2 += s1 += buf[15]; + } + + for (i = k % 16; i; --i) { + s1 += *buf++; + s2 += s1; + } + + s1 %= A32_BASE; + s2 %= A32_BASE; + + len -= k; + } + + return (s1 | (s2 << 16)); +} diff --git a/firmware/common/crc32-mi4.c b/firmware/common/crc32-mi4.c deleted file mode 100644 index e1a5769018..0000000000 --- a/firmware/common/crc32-mi4.c +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: crc32.c 10464 2006-08-05 20:19:10Z miipekk $ - * - * Copyright (C) 2007 Barry Wardell - * - * 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. - * - ****************************************************************************/ - -/* - * We can't use the CRC32 implementation in the firmware library as it uses a - * different polynomial. The polynomial needed is 0xEDB88320L - * - * CRC32 implementation taken from: - * - * efone - Distributed internet phone system. - * - * (c) 1999,2000 Krzysztof Dabrowski - * (c) 1999,2000 ElysiuM deeZine - * - * 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. - * - */ - -/* based on implementation by Finn Yannick Jacobs */ - -#include "crc32-mi4.h" - -/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). - * so make sure, you call it before using the other - * functions! - */ -static const unsigned int crc_tab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -/* chksum_crc() -- to a given block, this one calculates the - * crc32-checksum until the length is - * reached. the crc32-checksum will be - * the result. - */ -unsigned int chksum_crc32 (unsigned char *block, unsigned int length) -{ - register unsigned long crc; - unsigned long i; - - crc = 0; - for (i = 0; i < length; i++) - { - crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF]; - } - return (crc); -} diff --git a/firmware/common/crc32.c b/firmware/common/crc32.c index 1a9bc4faea..c8ed8f5350 100644 --- a/firmware/common/crc32.c +++ b/firmware/common/crc32.c @@ -19,12 +19,88 @@ * ****************************************************************************/ -/* Code copied from firmware_flash plugin. */ +/* Copyright (c) 2003-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ #include "crc32.h" +#include "config.h" + +/* Tool function to calculate a CRC32 across some buffer */ +/* third argument is either 0xFFFFFFFF to start or value from last piece */ +/* speed optimized version */ +#ifdef CRC32_FAST +uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32) +{ + const unsigned char *buf = (const unsigned char *)src; + + /* polynomial 0x04C11DB7 - generated with help from pycrc (https://pycrc.org/index.html) */ + static const unsigned crc32_lookup[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + while (len--) { + crc32 = (crc32_lookup[((crc32 >> 24) ^ *buf)] ^ (crc32 << 8)); + buf++; + } + + return crc32; +} +#endif /* Tool function to calculate a CRC32 across some buffer */ /* third argument is either 0xFFFFFFFF to start or value from last piece */ +/* space optimized version */ +#ifndef CRC32_FAST uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32) { const unsigned char *buf = (const unsigned char *)src; @@ -60,19 +136,79 @@ uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32) return crc32; } +#endif +/* Tool function to calculate a CRC32 (reversed polynomial) across some buffer */ +/* third argument is either the starting value or value from last piece */ +/* speed optimized version */ +#ifdef CRC32R_FAST +uint32_t crc_32r(const void *src, uint32_t len, uint32_t crc32) +{ + const unsigned char *buf = (const unsigned char *)src; -/* crc_32r (derived from tinf crc32 which was taken from zlib) - * CRC32 algorithm taken from the zlib source, which is - * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler - */ + /* crc_32r (derived from recent zlib crc32) + * CRC32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + + /* reversed polynomial from other crc32 function -- 0xEDB88320 */ + static const unsigned crc32_lookup[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + while (len--) { + crc32 = (crc32_lookup[(crc32 ^ *buf) & 0xff] ^ (crc32 >> 8)); + buf++; + } + + return crc32; +} +#endif /* Tool function to calculate a CRC32 (reversed polynomial) across some buffer */ /* third argument is either the starting value or value from last piece */ +/* space optimized version */ +#ifndef CRC32R_FAST uint32_t crc_32r(const void *src, uint32_t len, uint32_t crc32) { const unsigned char* buf = src; + /* crc_32r (derived from tinf crc32 which was taken from zlib) + * CRC32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + /* reversed polynomial from other crc32 function -- 0xEDB88320 */ static const unsigned crc32_lookup[16] = { @@ -91,3 +227,4 @@ uint32_t crc_32r(const void *src, uint32_t len, uint32_t crc32) return crc32; } +#endif 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 f89129ae34..9a78d910a7 100644 --- a/firmware/common/dir.c +++ b/firmware/common/dir.c @@ -26,18 +26,16 @@ #include "debug.h" #include "dir.h" #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* */ @@ -47,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; @@ -104,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 */ @@ -165,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(); @@ -204,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); @@ -222,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); @@ -258,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); @@ -288,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); } @@ -406,9 +338,15 @@ struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry) { .attribute = entry->info.attr, .size = entry->info.size, - .mtime = fattime_mktime(entry->info.wrtdate, entry->info.wrttime), + .mtime = dostime_mktime(entry->info.wrtdate, entry->info.wrttime), }; 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 589986911c..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, @@ -2956,7 +2927,7 @@ void dircache_dump(void) FOR_EACH_CACHE_ENTRY(ce) { #ifdef DIRCACHE_NATIVE - time_t mtime = fattime_mktime(ce->wrtdate, ce->wrttime); + time_t mtime = dostime_mktime(ce->wrtdate, ce->wrttime); #else time_t mtime = ce->mtime; #endif @@ -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 51d033b678..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]; -/* 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]; +/* check if the entry points to a free volume */ +static bool is_free_volume(const struct volumeinfo *vi) +{ + return vi->drive < 0; +} + +/* 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; @@ -174,6 +307,13 @@ int disk_mount(int drive) int volume = get_free_volume(); + if (volume < 0) + { + DEBUGF("No Free Volumes\n"); + disk_writer_unlock(); + return 0; + } + if (!disk_init(IF_MD(drive))) { disk_writer_unlock(); @@ -185,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)) @@ -196,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) { @@ -217,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 */ @@ -228,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 */ } @@ -250,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)) @@ -292,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++; } } @@ -397,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) @@ -407,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; } } @@ -449,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)) { @@ -456,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 cb918c6eab..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); @@ -1123,9 +1138,44 @@ file_error: return rc; } - /** Extensions **/ +int modtime(const char *path, time_t modtime) +{ + DEBUGF("modtime(path=\"%s\",modtime=%d)\n", path, (int) modtime); + + int rc, open1rc = -1; + struct filestr_base pathstr; + struct path_component_info pathinfo; + + file_internal_lock_WRITER(); + + open1rc = open_stream_internal(path, FF_ANYTYPE | FF_PARENTINFO, + &pathstr, &pathinfo); + if (open1rc <= 0) + { + DEBUGF("Failed opening path: %d\n", open1rc); + if (open1rc == 0) + FILE_ERROR(ENOENT, -1); + else + FILE_ERROR(ERRNO, open1rc * 10 - 1); + } + + rc = fat_modtime(&pathinfo.parentinfo.fatfile, pathstr.fatstr.fatfilep, + modtime); + if (rc < 0) + { + DEBUGF("I/O error during modtime: %d\n", rc); + FILE_ERROR(ERRNO, rc * 10 - 2); + } + +file_error: + if (open1rc >= 0) + close_stream_internal(&pathstr); + file_internal_unlock_WRITER(); + return rc; +} + /* get the binary size of a file (in bytes) */ off_t filesize(int fildes) { diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c index fe18f90056..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 - @@ -75,21 +81,23 @@ void file_cache_alloc(struct filestr_cache *cachep) /* free resources attached to the cache */ void file_cache_free(struct filestr_cache *cachep) { - if (cachep && cachep->buffer) + if (cachep) { - dc_release_buffer(cachep->buffer); - cachep->buffer = NULL; + if(cachep->buffer) + { + dc_release_buffer(cachep->buffer); + cachep->buffer = NULL; + } + file_cache_reset(cachep); } - - file_cache_reset(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; @@ -153,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; } @@ -161,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); } @@ -219,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 */ @@ -227,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; @@ -249,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, @@ -293,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 @@ -397,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 */ @@ -507,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] == '.') { @@ -575,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; @@ -585,80 +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; ) - 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 new file mode 100644 index 0000000000..e39fe3a14e --- /dev/null +++ b/firmware/common/inflate.c @@ -0,0 +1,793 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by James Buren (libflate adaptations for RockBox) + * + * 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. + * + ****************************************************************************/ + +/* Copyright 2021 Plan 9 Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "inflate.h" +#include <stdbool.h> +#include <string.h> +#include "adler32.h" +#include "crc32.h" +#include "system.h" + +enum { + INFLATE_BUFFER_SIZE = 32768, + INFLATE_SYMBOL_MAX = 288, + INFLATE_OFFSET_MAX = 32, + INFLATE_CODELEN_MAX = 19, + INFLATE_HUFF_BITS = 17, + INFLATE_FLAT_BITS = 7, + INFLATE_SYMBOL_BITS = 7, + INFLATE_OFFSET_BITS = 6, + INFLATE_CODELEN_BITS = 6, +}; + +struct inflate_huff { + uint32_t max_bits; + uint32_t min_bits; + uint32_t flat_mask; + uint32_t flat[1 << INFLATE_FLAT_BITS]; + uint32_t max_code[INFLATE_HUFF_BITS]; + uint32_t last[INFLATE_HUFF_BITS]; + uint32_t decode[INFLATE_SYMBOL_MAX]; +}; + +struct inflate { + uint8_t in[INFLATE_BUFFER_SIZE]; + uint8_t out[INFLATE_BUFFER_SIZE]; + union { + struct { + uint8_t len[INFLATE_SYMBOL_MAX]; + uint8_t off[INFLATE_OFFSET_MAX]; + }; + uint8_t combo[INFLATE_SYMBOL_MAX + INFLATE_OFFSET_MAX]; + }; + uint8_t clen[INFLATE_CODELEN_MAX]; + struct inflate_huff lentab; + struct inflate_huff offtab; + struct inflate_huff clentab; + uint32_t bits[INFLATE_HUFF_BITS]; + uint32_t codes[INFLATE_HUFF_BITS]; +}; + +#define INFLATE_FILL(E) do { \ + const uint32_t _size = read(is, sizeof(it->in), rctx); \ + if (_size == 0) { \ + rv = (E); \ + goto bail; \ + } \ + ip = is; \ + ie = is + _size; \ +} while (0) + +#define INFLATE_FLUSH(E) do { \ + const uint32_t _size = (op - os); \ + if (write(os, _size, wctx) != _size) { \ + rv = (E); \ + goto bail; \ + } \ + flushed = true; \ + if (st == INFLATE_ZLIB) \ + chksum = adler_32(os, _size, chksum); \ + else if (st == INFLATE_GZIP) \ + chksum = crc_32r(os, _size, chksum); \ + op = os; \ +} while (0) + +#define INFLATE_GET_BYTE(E,C) do { \ + if (ip == ie) \ + INFLATE_FILL(E); \ + (C) = ip[0]; \ + ++ip; \ +} while (0) + +#define INFLATE_PUT_BYTE(E,B) do { \ + if (op == oe) \ + INFLATE_FLUSH(E); \ + op[0] = (B); \ + ++op; \ +} while (0) + +#define INFLATE_FILL_BITS(E,N) do { \ + while ((N) > nbits) { \ + uint8_t _byte; \ + INFLATE_GET_BYTE(E, _byte); \ + sreg |= (_byte << nbits); \ + nbits += 8; \ + } \ +} while (0) + +#define INFLATE_CONSUME_BITS(N) do { \ + sreg >>= (N); \ + nbits -= (N); \ +} while (0) + +#define INFLATE_EXTRACT_BITS(N,B) do { \ + (B) = (sreg & ((1 << (N)) - 1)); \ + INFLATE_CONSUME_BITS(N); \ +} while (0) + +#define INFLATE_CHECK_LENGTH(E,N) do { \ + if ((N) > (ie - ip)) { \ + rv = (E); \ + goto bail; \ + } \ +} while (0) + +#define INFLATE_REVERSE(C,B) ({ \ + uint32_t _c = (C); \ + _c <<= (16 - (B)); \ + ((revtab[_c >> 8]) | (revtab[_c & 0xff] << 8)); \ +}) + +#define INFLATE_DECODE(E,H) ({ \ + __label__ _found; \ + uint32_t _c = (H)->flat[sreg & (H)->flat_mask]; \ + uint32_t _b = (_c & 0xff); \ + uint32_t _code; \ + if (_b == 0xff) { \ + for (_b = (_c >> 8); _b <= (H)->max_bits; _b++) { \ + _c = (revtab[sreg & 0xff] << 8); \ + _c |= (revtab[(sreg >> 8) & 0xff]); \ + _c >>= (16 - _b); \ + if (_c <= (H)->max_code[_b]) { \ + _code = (H)->decode[(H)->last[_b] - _c]; \ + goto _found; \ + } \ + } \ + rv = (E); \ + goto bail; \ + } \ + _code = (_c >> 8); \ +_found: \ + INFLATE_CONSUME_BITS(_b); \ + _code; \ +}) + +static int inflate_blocks(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx) { + int rv = 0; + uint8_t* is = it->in; + uint8_t* ip; + uint8_t* ie; + uint8_t* os = it->out; + uint8_t* op = os; + uint8_t* oe = os + sizeof(it->out); + bool flushed = false; + uint32_t chksum; + uint32_t nbits = 0; + uint32_t sreg = 0; + uint32_t i; + uint32_t j; + bool final; + uint8_t type; + + INFLATE_FILL(-1); + + if (st == INFLATE_ZLIB) { + uint16_t header; + + INFLATE_CHECK_LENGTH(-2, 2); + + header = (ip[0] << 8 | ip[1]); + ip += 2; + + if ((header % 31) != 0) { + rv = -3; + goto bail; + } + + if (((header & 0xf000) >> 12) > 7) { + rv = -4; + goto bail; + } + + if (((header & 0x0f00) >> 8) != 8) { + rv = -5; + goto bail; + } + + if (((header & 0x0020) >> 5) != 0) { + rv = -6; + goto bail; + } + + chksum = 1; + } else if (st == INFLATE_GZIP) { + uint8_t flg; + + INFLATE_CHECK_LENGTH(-7, 10); + + if (ip[0] != 0x1f || ip[1] != 0x8b) { + rv = -8; + goto bail; + } + + if (ip[2] != 8) { + rv = -9; + goto bail; + } + + flg = ip[3]; + + if ((flg & 0xe0) != 0) { + rv = -10; + goto bail; + } + + ip += 10; + chksum = 0xffffffff; + + if ((flg & 0x04) != 0) { + uint16_t xlen; + + INFLATE_CHECK_LENGTH(-11, 2); + + xlen = (ip[0] | ip[1] << 8); + ip += 2; + + while (xlen >= (ie - ip)) { + xlen -= (ie - ip); + + if ((flg & 0x02) != 0) + chksum = crc_32r(is, ie - is, chksum); + + INFLATE_FILL(-12); + } + + ip += xlen; + } + + if ((flg & 0x08) != 0) { + while (ip++[0] != '\0') { + if (ip == ie) { + if ((flg & 0x02) != 0) + chksum = crc_32r(is, ie - is, chksum); + + INFLATE_FILL(-13); + } + } + } + + if ((flg & 0x10) != 0) { + while (ip++[0] != '\0') { + if (ip == ie) { + if ((flg & 0x02) != 0) + chksum = crc_32r(is, ie - is, chksum); + + INFLATE_FILL(-14); + } + } + } + + if ((flg & 0x02) != 0) { + uint16_t crc16; + + INFLATE_CHECK_LENGTH(-15, 2); + + crc16 = (ip[0] | ip[1] << 8); + chksum = crc_32r(is, ip - is, chksum); + chksum &= 0xffff; + chksum ^= 0xffff; + + if (crc16 != chksum) { + rv = -16; + goto bail; + } + + ip += 2; + } + + chksum = 0xffffffff; + } else { + chksum = 0; + } + + do { + INFLATE_FILL_BITS(-17, 3); + final = (sreg & 0x01); + type = ((sreg & 0x06) >> 1); + INFLATE_CONSUME_BITS(3); + + if (type == 0) { + uint8_t header[4]; + uint32_t len; + uint32_t clen; + + INFLATE_CONSUME_BITS(nbits & 0x07); + + for (i = 0; i < 4; i++) { + if (nbits != 0) + INFLATE_EXTRACT_BITS(8, header[i]); + else + INFLATE_GET_BYTE(-18, header[i]); + } + + len = (header[0] | (header[1] << 8)); + clen = (header[2] | (header[3] << 8)) ^ 0xffff; + + if (len != clen) { + rv = -19; + goto bail; + } + + while (len != 0) { + if (ip == ie) + INFLATE_FILL(-20); + + if (op == oe) + INFLATE_FLUSH(-21); + + j = MIN(len, MIN((uint32_t) (ie - ip), (uint32_t) (oe - op))); + for (i = 0; i < j; i++) + op[i] = ip[i]; + + len -= j; + ip += j; + op += j; + } + } else if (type == 3) { + rv = -22; + goto bail; + } else { + static const uint8_t revtab[256] = + { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, + }; + uint8_t* tab[3]; + uint32_t tab_lens[3]; + uint32_t tab_bits[3]; + struct inflate_huff* tab_huffs[3] = { &it->lentab, &it->offtab, &it->clentab, }; + uint32_t c; + uint32_t k; + + if (type == 2) { + static const uint32_t min_tab_sizes[3] = { 257, 1, 4, }; + static const uint32_t min_tab_bits[3] = { 5, 5, 4, }; + static const uint32_t clen_order[INFLATE_CODELEN_MAX] = { + 16, 17, 18, 0, 8, + 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, + 2, 14, 1, 15, + }; + + INFLATE_FILL_BITS(-23, 14); + for (i = 0; i < 3; i++) { + INFLATE_EXTRACT_BITS(min_tab_bits[i], tab_lens[i]); + tab_lens[i] += min_tab_sizes[i]; + } + + tab[2] = it->clen; + for (i = 0; i < INFLATE_CODELEN_MAX; i++) + tab[2][i] = 0; + for (i = 0; i < tab_lens[2]; i++) { + INFLATE_FILL_BITS(-24, 3); + INFLATE_EXTRACT_BITS(3, tab[2][clen_order[i]]); + } + + tab[0] = it->combo; + tab_bits[0] = INFLATE_SYMBOL_BITS; + + tab[1] = tab[0] + tab_lens[0]; + tab_bits[1] = INFLATE_OFFSET_BITS; + + tab_lens[2] = INFLATE_CODELEN_MAX; + tab_bits[2] = INFLATE_CODELEN_BITS; + } else { + tab[0] = it->len; + for (i = 0; i < 144; i++) + tab[0][i] = 8; + for (; i < 256; i++) + tab[0][i] = 9; + for (; i < 280; i++) + tab[0][i] = 7; + for (; i < INFLATE_SYMBOL_MAX; i++) + tab[0][i] = 8; + tab_lens[0] = INFLATE_SYMBOL_MAX; + tab_bits[0] = INFLATE_FLAT_BITS; + + tab[1] = it->off; + for (i = 0; i < INFLATE_OFFSET_MAX; i++) + tab[1][i] = 5; + tab_lens[1] = INFLATE_OFFSET_MAX; + tab_bits[1] = INFLATE_FLAT_BITS; + } + + for(; type != 0xff; --type) { + const uint8_t* hb = tab[type]; + uint32_t hb_len = tab_lens[type]; + uint32_t flat_bits = tab_bits[type]; + struct inflate_huff* h = tab_huffs[type]; + uint32_t* bits = it->bits; + uint32_t* codes = it->codes; + uint32_t max_bits = 0; + uint32_t min_bits = INFLATE_HUFF_BITS + 1; + uint32_t min_code; + uint32_t b; + uint32_t code; + uint32_t fc; + uint32_t ec; + + for (i = 0; i < INFLATE_HUFF_BITS; i++) + bits[i] = 0; + + for (i = 0; i < hb_len; i++) { + b = hb[i]; + + if (b != 0) { + bits[b]++; + + if (b < min_bits) + min_bits = b; + + if (b > max_bits) + max_bits = b; + } + } + + if (max_bits == 0) { + h->flat_mask = h->min_bits = h->max_bits = 0; + goto table_done; + } + + h->max_bits = max_bits; + + for (b = c = code = 0; b <= max_bits; b++) { + h->last[b] = c; + c += bits[b]; + + min_code = (code << 1); + codes[b] = min_code; + code = (min_code + bits[b]); + + if (code > (1U << b)) { + rv = -25; + goto bail; + } + + h->max_code[b] = (code - 1); + h->last[b] += (code - 1); + } + + if (flat_bits > max_bits) + flat_bits = max_bits; + + h->flat_mask = ((1 << flat_bits) - 1); + + if (min_bits > flat_bits) + min_bits = flat_bits; + + h->min_bits = min_bits; + + for (i = 0, b = (1 << flat_bits); i < b; i++) + h->flat[i] = 0xffffffff; + + for (b = max_bits; b > flat_bits; b--) { + code = h->max_code[b]; + + if (code == 0xffffffff) + break; + + min_code = ((code + 1) - bits[b]); + min_code >>= (b - flat_bits); + code >>= (b - flat_bits); + + for (; min_code <= code; min_code++) + h->flat[INFLATE_REVERSE(min_code, flat_bits)] = ((b << 8) | 0xff); + } + + for (i = 0; i < hb_len; i++) { + b = hb[i]; + + if (b == 0) + continue; + + c = codes[b]++; + + if (b <= flat_bits) { + code = ((i << 8) | b); + ec = ((c + 1) << (flat_bits - b)); + + if (ec > (1U << flat_bits)) { + rv = -26; + goto bail; + } + + for (fc = (c << (flat_bits - b)); fc < ec; fc++) + h->flat[INFLATE_REVERSE(fc, flat_bits)] = code; + } + + if (b > min_bits) { + c = h->last[b] - c; + + if (c >= hb_len) { + rv = -27; + goto bail; + } + + h->decode[c] = i; + } + } + + table_done: + + if (type == 2) { + static const uint32_t bits[3] = { 2, 3, 7, }; + static const uint32_t bases[3] = { 3, 3, 11, }; + + for (i = 0, j = tab_lens[0] + tab_lens[1]; i < j; ) { + uint32_t len; + uint8_t byte; + + INFLATE_FILL_BITS(-28, h->max_bits); + + c = INFLATE_DECODE(-29, h); + + if (c < 16) { + tab[0][i++] = c; + continue; + } + + c -= 16; + + if (c == 0 && i == 0) { + rv = -30; + goto bail; + } + + INFLATE_FILL_BITS(-31, bits[c]); + INFLATE_EXTRACT_BITS(bits[c], len); + len += bases[c]; + + if ((i + len) > j) { + rv = -32; + goto bail; + } + + byte = ((c == 0) ? tab[0][i - 1] : 0); + for (k = 0; k < len; k++) + tab[0][i + k] = byte; + + i += len; + } + } + } + + while (1) { + static const uint32_t lenbase[INFLATE_SYMBOL_MAX - 257] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 258, 0, 0, + }; + static const uint32_t lenextra[INFLATE_SYMBOL_MAX - 257] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 5, 0, 0, 0, + }; + static const uint32_t offbase[INFLATE_OFFSET_MAX] = { + 1, 2, 3, 4, 5, 7, 9, 13, + 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577, 0, 0, + }; + static const uint32_t offextra[INFLATE_OFFSET_MAX] = { + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13, 0, 0, + }; + uint32_t len; + uint32_t off; + uint8_t* cp; + + INFLATE_FILL_BITS(-33, tab_huffs[0]->max_bits); + c = INFLATE_DECODE(-34, tab_huffs[0]); + + if (c < 256) { + INFLATE_PUT_BYTE(-35, c); + continue; + } + + if (c == 256) + break; + + if (c > 285) { + rv = -36; + goto bail; + } + + c -= 257; + INFLATE_FILL_BITS(-37, lenextra[c]); + INFLATE_EXTRACT_BITS(lenextra[c], len); + len += lenbase[c]; + + INFLATE_FILL_BITS(-38, tab_huffs[1]->max_bits); + c = INFLATE_DECODE(-39, tab_huffs[1]); + + if (c > 29) { + rv = -40; + goto bail; + } + + INFLATE_FILL_BITS(-41, offextra[c]); + INFLATE_EXTRACT_BITS(offextra[c], off); + off += offbase[c]; + + cp = op - off; + if (cp < os) { + if (!flushed) { + rv = -42; + goto bail; + } + + cp += sizeof(it->out); + } + + while (len != 0) { + if (op == oe) + INFLATE_FLUSH(-43); + + if (cp == oe) + cp = os; + + j = MIN(len, MIN((uint32_t) (oe - op), (uint32_t) (oe - cp))); + + for (i = 0; i < j; i++) + op[i] = cp[i]; + + op += j; + cp += j; + len -= j; + } + } + } + } while (!final); + + INFLATE_FLUSH(-44); + + if (st != INFLATE_RAW) { + uint8_t header[4]; + uint32_t chksum2; + + INFLATE_CONSUME_BITS(nbits & 0x07); + + for (i = 0; i < 4; i++) { + if (nbits != 0) + INFLATE_EXTRACT_BITS(8, header[i]); + else + INFLATE_GET_BYTE(-45, header[i]); + } + + if (st == INFLATE_ZLIB) { + chksum2 = ((header[0] << 24) | (header[1] << 16) | (header[2] << 8) | (header[3] << 0)); + + if (chksum != chksum2) { + rv = -46; + goto bail; + } + } else if (st == INFLATE_GZIP) { + chksum2 = ((header[3] << 24) | (header[2] << 16) | (header[1] << 8) | (header[0] << 0)); + + if (~chksum != chksum2) { + rv = -47; + goto bail; + } + } + } + +bail: + return rv; +} + +const uint32_t inflate_size = sizeof(struct inflate); +const uint32_t inflate_align = _Alignof(struct inflate); + +int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx) { + if (it == NULL || read == NULL || write == NULL || st < 0 || st > 2 || (((uintptr_t) it) & (_Alignof(struct inflate) - 1)) != 0) + return -48; + + 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 1c48a54972..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. @@ -340,7 +396,62 @@ void path_correct_separators(char *dstpath, const char *path) strcpy(dstp, p); } +/* Remove dot segments from the path + * + * 'path' and 'dstpath' may either be the same buffer or non-overlapping + */ +void path_remove_dot_segments (char *dstpath, const char *path) +{ + char *dstp = dstpath; + char *odstp = dstpath; + const char *p = path; + + while (*p) + { + if (p[0] == '.' && p[1] == PATH_SEPCH) + p += 2; + else if (p[0] == '.' && p[1] == '.' && p[2] == PATH_SEPCH) + p += 3; + else if (p[0] == PATH_SEPCH && p[1] == '.' && p[2] == PATH_SEPCH) + p += 2; + else if (p[0] == PATH_SEPCH && p[1] == '.' && !p[2]) + { + *dstp++ = PATH_SEPCH; + break; + } + else if (p[0] == PATH_SEPCH && p[1] == '.' && + p[2] == '.' && p[3] == PATH_SEPCH) + { + dstp = odstp; + p += 3; + } + else if (p[0] == PATH_SEPCH && p[1] == '.' && p[2] == '.' && !p[3]) + { + dstp = odstp; + *dstp++ = PATH_SEPCH; + break; + } + else if (p[0] == '.' && !p[1]) + break; + else if (p[0] == '.' && p[1] == '.' && !p[2]) + break; + else + { + odstp = dstp; + if (p[0] == PATH_SEPCH) + *dstp++ = *p++; + while (p[0] && p[0] != PATH_SEPCH) + *dstp++ = *p++; + } + } + *dstp = 0; +} + /* 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. * @@ -348,9 +459,11 @@ void path_correct_separators(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 */ @@ -363,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; @@ -389,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/timefuncs.c b/firmware/common/timefuncs.c index 108431753a..1405a8d926 100644 --- a/firmware/common/timefuncs.c +++ b/firmware/common/timefuncs.c @@ -32,6 +32,37 @@ static struct tm tm; +time_t dostime_mktime(uint16_t dosdate, uint16_t dostime) +{ + /* this knows our mktime() only uses these struct tm fields */ + struct tm tm; + tm.tm_sec = ((dostime ) & 0x1f) * 2; + tm.tm_min = ((dostime >> 5) & 0x3f); + tm.tm_hour = ((dostime >> 11) ); + tm.tm_mday = ((dosdate ) & 0x1f); + tm.tm_mon = ((dosdate >> 5) & 0x0f) - 1; + tm.tm_year = ((dosdate >> 9) ) + 80; + + return mktime(&tm); +} + +void dostime_localtime(time_t time, uint16_t* dosdate, uint16_t* dostime) +{ + struct tm tm; +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) + gmtime_r(&time, &tm); +#else + localtime_r(&time, &tm); +#endif + + *dostime = ((tm.tm_sec / 2) << 0)| + ((tm.tm_min ) << 5)| + ((tm.tm_hour ) << 11); + *dosdate = ((tm.tm_mday ) << 0)| + ((tm.tm_mon + 1) << 5)| + ((tm.tm_year - 80) << 9); +} + #if !CONFIG_RTC static inline bool rtc_dirty(void) { @@ -55,13 +86,13 @@ static inline int rtc_read_datetime(struct tm *tm) #if CONFIG_RTC bool valid_time(const struct tm *tm) { - if (tm->tm_hour < 0 || tm->tm_hour > 23 || + if (!tm || (tm->tm_hour < 0 || tm->tm_hour > 23 || tm->tm_sec < 0 || tm->tm_sec > 59 || tm->tm_min < 0 || tm->tm_min > 59 || tm->tm_year < 100 || tm->tm_year > 199 || tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_wday < 0 || tm->tm_wday > 6 || - tm->tm_mday < 1 || tm->tm_mday > 31) + tm->tm_mday < 1 || tm->tm_mday > 31)) return false; else return true; 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/vuprintf.c b/firmware/common/vuprintf.c index 6a3b29388b..0566e3e37e 100644 --- a/firmware/common/vuprintf.c +++ b/firmware/common/vuprintf.c @@ -753,6 +753,7 @@ static int format_double_radix(double f, if (prec_rem) { prec_rem--; } + /* fallthrough */ case 1: /* %e, %E */ explen = 2; break; diff --git a/firmware/common/zip.c b/firmware/common/zip.c new file mode 100644 index 0000000000..2d560e472e --- /dev/null +++ b/firmware/common/zip.c @@ -0,0 +1,875 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by James Buren + * + * 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 "zip.h" +#include "string-extra.h" +#include "file.h" +#include "dir.h" +#include "system.h" +#include "errno.h" +#include "core_alloc.h" +#include "timefuncs.h" +#include "pathfuncs.h" +#include "crc32.h" +#include "rbendian.h" + +#define zip_core_alloc(N) core_alloc_ex((N),&buflib_ops_locked) + +enum { + ZIP_SIG_ED = 0x06054b50, + ZIP_SIG_CD = 0x02014b50, + ZIP_SIG_LF = 0x04034b50, + ZIP_BIT_DD = 0x0008, + ZIP_METHOD_STORE = 0x0000, + ZIP_MAX_LENGTH = 0xffff, + ZIP_BUFFER_SIZE = 4096, +}; + +enum { + ZIP_STATE_INITIAL, + ZIP_STATE_ED_ENTER, + ZIP_STATE_ED_EXIT, + ZIP_STATE_CD_ENTER, + ZIP_STATE_CD_EXIT, + ZIP_STATE_LF_ENTER, + ZIP_STATE_LF_EXIT, +}; + +struct zip_ed_disk { + uint32_t signature; + uint16_t disk_number; + uint16_t disk_number_cd; + uint16_t disk_entries_cd; + uint16_t cd_entries; + uint32_t cd_size; + uint32_t cd_offset; + uint16_t comment_length; + // comment block (variable length) +} __attribute__((packed)); + +struct zip_ed { + uint32_t cd_size; + uint32_t cd_offset; + uint16_t cd_entries; +}; + +struct zip_cd_disk { + uint32_t signature; + uint16_t version_madeby; + uint16_t version_needed; + uint16_t flags; + uint16_t method; + uint16_t time; + uint16_t date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t name_length; + uint16_t extra_length; + uint16_t comment_length; + uint16_t disk_number; + uint16_t internal_attributes; + uint32_t external_attributes; + uint32_t lf_offset; + // name block (variable length) + // extra block (variable length) + // comment block (variable length) +} __attribute__((packed)); + +struct zip_cd { + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint32_t lf_offset; +}; + +struct zip_lf_disk { + uint32_t signature; + uint16_t version_needed; + uint16_t flags; + uint16_t method; + uint16_t time; + uint16_t date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t name_length; + uint16_t extra_length; + // name block (variable length) + // extra block (variable length) +} __attribute__((packed)); + +struct zip_lf { + uint16_t flags; + uint16_t method; + uint16_t time; + uint16_t date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t name_length; + char name[MAX_PATH]; +}; + +struct zip { + ssize_t (*read) (struct zip*, void*, size_t); + off_t (*seek) (struct zip*, off_t, int); + off_t (*size) (struct zip*); + void (*close) (struct zip*); + int zip_handle; + int state; + zip_callback cb; + struct zip_args args; + void* ctx; + struct zip_ed ed; + int cds_handle; + struct zip_cd* cds; + struct zip_lf lf; +}; + +struct zip_file { + struct zip base; + int file; +}; + +struct zip_mem { + struct zip base; + int mem_handle; + const uint8_t* mem; + off_t mem_offset; + off_t mem_size; +}; + +struct zip_extract { + zip_callback cb; + void* ctx; + size_t name_offset; + size_t name_size; + char* name; + int file; + char path[MAX_PATH]; +}; + +static int zip_read_ed(struct zip* z) { + const off_t file_size = z->size(z); + const off_t max_size = sizeof(struct zip_ed_disk) + ZIP_MAX_LENGTH; + const off_t read_size = MIN(file_size, max_size); + const uint32_t sig = htole32(ZIP_SIG_ED); + int mem_handle = -1; + uint8_t* mem; + off_t i = read_size - sizeof(struct zip_ed_disk); + const struct zip_ed_disk* edd; + uint16_t disk_number; + uint16_t disk_number_cd; + uint16_t disk_entries_cd; + uint16_t cd_entries; + struct zip_ed* ed = &z->ed; + int rv; + + z->state = ZIP_STATE_ED_ENTER; + + if (file_size < (off_t) sizeof(struct zip_ed_disk)) { + rv = -2; + goto bail; + } + + if ((mem_handle = zip_core_alloc(read_size)) < 0) { + rv = -3; + goto bail; + } + + mem = core_get_data(mem_handle); + + if (z->seek(z, -read_size, SEEK_END) < 0) { + rv = -4; + goto bail; + } + + if (z->read(z, mem, read_size) != read_size) { + rv = -5; + goto bail; + } + + for (; i >= 0; i--) + if (memcmp(mem + i, &sig, sizeof(uint32_t)) == 0) + break; + + if (i < 0) { + rv = -6; + goto bail; + } + + edd = (struct zip_ed_disk*) (mem + i); + disk_number = letoh16(edd->disk_number); + disk_number_cd = letoh16(edd->disk_number_cd); + disk_entries_cd = letoh16(edd->disk_entries_cd); + cd_entries = letoh16(edd->cd_entries); + + if (disk_number != 0 || disk_number_cd != 0 || disk_entries_cd != cd_entries) { + rv = -7; + goto bail; + } + + ed->cd_size = letoh32(edd->cd_size); + ed->cd_offset = letoh32(edd->cd_offset); + ed->cd_entries = cd_entries; + + z->state = ZIP_STATE_ED_EXIT; + rv = 0; + +bail: + core_free(mem_handle); + return rv; +} + +static int zip_read_cd(struct zip* z, bool use_cb) { + const struct zip_ed* ed = &z->ed; + const uint32_t cd_size = ed->cd_size; + const uint32_t cd_offset = ed->cd_offset; + const uint16_t cd_entries = ed->cd_entries; + const uint32_t sig = htole32(ZIP_SIG_CD); + int cds_handle = -1; + int mem_handle = -1; + struct zip_cd* cds; + uint8_t* mem; + struct zip_lf* lf = &z->lf; + struct zip_args* args = &z->args; + struct zip_cd_disk* cdd; + struct zip_cd* cd; + uint16_t name_length; + int rv; + + z->state = ZIP_STATE_CD_ENTER; + + if ((cds_handle = zip_core_alloc(sizeof(struct zip_cd) * cd_entries)) < 0) { + rv = -7; + goto bail; + } + + if ((mem_handle = zip_core_alloc(cd_size)) < 0) { + rv = -8; + goto bail; + } + + cds = core_get_data(cds_handle); + mem = core_get_data(mem_handle); + + if (z->seek(z, cd_offset, SEEK_SET) < 0) { + rv = -9; + goto bail; + } + + if (z->read(z, mem, cd_size) != (ssize_t) cd_size) { + rv = -10; + goto bail; + } + + if (use_cb) { + args->entries = cd_entries; + args->name = lf->name; + args->block = NULL; + args->block_size = 0; + args->read_size = 0; + } + + cdd = (struct zip_cd_disk*) mem; + + for (uint16_t i = 0; i < cd_entries; i++) { + if (cdd->signature != sig) { + rv = -11; + goto bail; + } + + cd = &cds[i]; + + cd->crc = letoh32(cdd->crc); + cd->compressed_size = letoh32(cdd->compressed_size); + cd->uncompressed_size = letoh32(cdd->uncompressed_size); + cd->lf_offset = letoh32(cdd->lf_offset); + + mem += sizeof(struct zip_cd_disk); + name_length = letoh16(cdd->name_length); + if (use_cb) { + if (name_length >= sizeof(lf->name)) { + rv = -12; + goto bail; + } + + args->entry = i + 1; + args->file_size = cd->uncompressed_size; + args->mtime = dostime_mktime(letoh16(cdd->date), letoh16(cdd->time)); + + memcpy(lf->name, mem, name_length); + lf->name[name_length] = '\0'; + + if ((rv = z->cb(args, ZIP_PASS_SHALLOW, z->ctx)) > 0) + goto bail; + } + mem += name_length; + mem += letoh16(cdd->extra_length); + mem += letoh16(cdd->comment_length); + cdd = (struct zip_cd_disk*) mem; + } + + z->cds_handle = cds_handle; + z->cds = cds; + z->state = ZIP_STATE_CD_EXIT; + rv = 0; + +bail: + if (rv != 0) + core_free(cds_handle); + core_free(mem_handle); + return rv; +} + +static int zip_read_lf(struct zip* z, uint16_t i) { + const uint32_t sig = htole32(ZIP_SIG_LF); + const struct zip_cd* cd = &z->cds[i]; + struct zip_lf* lf = &z->lf; + struct zip_lf_disk lfd; + uint16_t name_length; + + if (z->seek(z, cd->lf_offset, SEEK_SET) < 0) + return -14; + + if (z->read(z, &lfd, sizeof(struct zip_lf_disk)) != sizeof(struct zip_lf_disk)) + return -15; + + if (lfd.signature != sig) + return -16; + + name_length = letoh16(lfd.name_length); + + if (name_length >= sizeof(lf->name)) + return -17; + + if (z->read(z, lf->name, name_length) != name_length) + return -18; + + if (z->seek(z, letoh16(lfd.extra_length), SEEK_CUR) < 0) + return -19; + + lf->flags = letoh16(lfd.flags); + lf->method = letoh16(lfd.method); + lf->time = letoh16(lfd.time); + lf->date = letoh16(lfd.date); + lf->crc = letoh32(lfd.crc); + lf->compressed_size = letoh32(lfd.compressed_size); + lf->uncompressed_size = letoh32(lfd.uncompressed_size); + lf->name_length = name_length; + lf->name[name_length] = '\0'; + + if ((lf->flags & ZIP_BIT_DD) == ZIP_BIT_DD) { + lf->crc = cd->crc; + lf->compressed_size = cd->compressed_size; + lf->uncompressed_size = cd->uncompressed_size; + } + + return 0; +} + +static int zip_read_store(struct zip* z, void* mem, uint32_t mem_size) { + const struct zip_lf* lf = &z->lf; + struct zip_args* args = &z->args; + uint32_t file_size = lf->uncompressed_size; + uint32_t block_size = mem_size; + uint32_t crc = 0xffffffff; + int rv; + + if (lf->compressed_size != lf->uncompressed_size) + return -21; + + args->block = mem; + args->block_size = block_size; + args->read_size = 0; + + do { + if (block_size > file_size) { + args->block_size = block_size = file_size; + } + + if (z->read(z, mem, block_size) != (off_t) block_size) + return -22; + + args->read_size += block_size; + crc = crc_32r(mem, block_size, crc); + + if ((rv = z->cb(args, ZIP_PASS_DATA, z->ctx)) != 0) + return (rv < 0) ? 0 : rv; + + file_size -= block_size; + } while (file_size > 0); + + if (~crc != lf->crc) + return -24; + + return 0; +} + +static int zip_read_entry(struct zip* z, uint16_t i, void* mem, uint32_t mem_size) { + const struct zip_lf* lf = &z->lf; + struct zip_args* args = &z->args; + int rv; + + if ((rv = zip_read_lf(z, i)) != 0) + return rv; + + args->entry = i + 1; + args->file_size = lf->uncompressed_size; + args->mtime = dostime_mktime(lf->date, lf->time); + args->block = NULL; + args->block_size = 0; + args->read_size = 0; + + if ((rv = z->cb(&z->args, ZIP_PASS_START, z->ctx)) != 0) + return (rv < 0) ? 0 : rv; + + if (lf->uncompressed_size == 0) + goto skip_data; + + if (lf->method == ZIP_METHOD_STORE) { + if ((rv = zip_read_store(z, mem, mem_size)) != 0) + return rv; + } else { + return -20; + } + +skip_data: + args->block = NULL; + args->block_size = 0; + args->read_size = 0; + + if ((rv = z->cb(args, ZIP_PASS_END, z->ctx)) != 0) + return (rv < 0) ? 0 : rv; + + return 0; +} + +static int zip_read_entries(struct zip* z) { + const struct zip_ed* ed = &z->ed; + const uint16_t cd_entries = ed->cd_entries; + struct zip_lf* lf = &z->lf; + struct zip_args* args = &z->args; + uint32_t mem_size = ZIP_BUFFER_SIZE; + int mem_handle; + void* mem; + int rv; + + z->state = ZIP_STATE_LF_ENTER; + + if ((mem_handle = zip_core_alloc(mem_size)) < 0) { + rv = -13; + goto bail; + } + + mem = core_get_data(mem_handle); + + args->entries = cd_entries; + args->name = lf->name; + + for (uint16_t i = 0; i < cd_entries; i++) + if ((rv = zip_read_entry(z, i, mem, mem_size)) > 0) + goto bail; + + z->state = ZIP_STATE_LF_EXIT; + rv = 0; + +bail: + core_free(mem_handle); + return rv; +} + +static void zip_init(struct zip* z, int zip_handle) { + z->zip_handle = zip_handle; + z->state = ZIP_STATE_INITIAL; + z->cb = NULL; + memset(&z->args, 0, sizeof(struct zip_args)); + z->ctx = NULL; + memset(&z->ed, 0, sizeof(struct zip_ed)); + z->cds_handle = -1; + z->cds = NULL; + memset(&z->lf, 0, sizeof(struct zip_lf)); +} + +static ssize_t zip_file_read(struct zip* zh, void* mem, size_t mem_size) { + struct zip_file* z = (struct zip_file*) zh; + + return read(z->file, mem, mem_size); +} + +static off_t zip_file_seek(struct zip* zh, off_t offset, int whence) { + struct zip_file* z = (struct zip_file*) zh; + + return lseek(z->file, offset, whence); +} + +static off_t zip_file_size(struct zip* zh) { + struct zip_file* z = (struct zip_file*) zh; + + return filesize(z->file); +} + +static void zip_file_close(struct zip* zh) { + struct zip_file* z = (struct zip_file*) zh; + + close(z->file); +} + +static void zip_file_init(struct zip_file* z, int zip_handle, int file) { + struct zip* zh = &z->base; + + zh->read = zip_file_read; + zh->seek = zip_file_seek; + zh->size = zip_file_size; + zh->close = zip_file_close; + zip_init(zh, zip_handle); + + z->file = file; +} + +static ssize_t zip_mem_read(struct zip* zh, void* mem, size_t mem_size) { + struct zip_mem* z = (struct zip_mem*) zh; + off_t bytes = z->mem_size - z->mem_offset; + off_t read_size = MIN(bytes, (off_t) mem_size); + + memcpy(mem, z->mem + z->mem_offset, read_size); + z->mem_offset += read_size; + + return read_size; +} + +static off_t zip_mem_seek(struct zip* zh, off_t offset, int whence) { + struct zip_mem* z = (struct zip_mem*) zh; + off_t new_offset; + + switch (whence) { + case SEEK_SET: + new_offset = offset; + break; + + case SEEK_CUR: + new_offset = z->mem_offset + offset; + break; + + case SEEK_END: + new_offset = z->mem_size + offset; + break; + + default: + new_offset = -1; + break; + } + + if (new_offset < 0 || new_offset > z->mem_size) + return -1; + + z->mem_offset = new_offset; + + return new_offset; +} + +static off_t zip_mem_size(struct zip* zh) { + struct zip_mem* z = (struct zip_mem*) zh; + + return z->mem_size; +} + +static void zip_mem_close(struct zip* zh) { + struct zip_mem* z = (struct zip_mem*) zh; + + core_free(z->mem_handle); +} + +static void zip_mem_init(struct zip_mem* z, int zip_handle, int mem_handle, const void* mem, off_t mem_size) { + struct zip* zh = &z->base; + + zh->read = zip_mem_read; + zh->seek = zip_mem_seek; + zh->size = zip_mem_size; + zh->close = zip_mem_close; + zip_init(zh, zip_handle); + + z->mem_handle = mem_handle; + z->mem = mem; + z->mem_offset = 0; + z->mem_size = mem_size; +} + +static int zip_extract_start(const struct zip_args* args, struct zip_extract* ze) { + size_t name_length; + const char* dir; + size_t dir_length; + + if ((name_length = strlcpy(ze->name, args->name, ze->name_size)) >= ze->name_size) + return 5; + + if ((dir_length = path_dirname(ze->name, &dir)) > 0) { + char c = ze->name[dir_length]; + + ze->name[dir_length] = '\0'; + + if (!dir_exists(ze->path)) { + const char* path = ze->name; + const char* name; + + while (parse_path_component(&path, &name) > 0) { + size_t offset = path - ze->name; + char c = ze->name[offset]; + + ze->name[offset] = '\0'; + + if (mkdir(ze->path) < 0 && errno != EEXIST) + return 6; + + ze->name[offset] = c; + } + } + + ze->name[dir_length] = c; + } + + if (ze->name[name_length - 1] == PATH_SEPCH) { + if (mkdir(ze->path) < 0 && errno != EEXIST) + return 7; + + return 0; + } + + if ((ze->file = creat(ze->path, 0666)) < 0) + return 8; + + return 0; +} + +static int zip_extract_data(const struct zip_args* args, struct zip_extract* ze) { + if (write(ze->file, args->block, args->block_size) != (ssize_t) args->block_size) { + return 9; + } + + return 0; +} + +static int zip_extract_end(const struct zip_args* args, struct zip_extract* ze) { + int rv; + + if (ze->file >= 0) { + rv = close(ze->file); + + ze->file = -1; + + if (rv < 0) + return 10; + } + + if (modtime(ze->path, args->mtime) < 0) + return 11; + + return 0; +} + +static int zip_extract_callback(const struct zip_args* args, int pass, void* ctx) { + struct zip_extract* ze = ctx; + int rv; + + if (ze->cb != NULL && (rv = ze->cb(args, pass, ze->ctx)) != 0) + return rv; + + switch (pass) { + case ZIP_PASS_START: + return zip_extract_start(args, ze); + + case ZIP_PASS_DATA: + return zip_extract_data(args, ze); + + case ZIP_PASS_END: + return zip_extract_end(args, ze); + + default: + return 1; + } +} + +struct zip* zip_open(const char* name, bool try_mem) { + int file = -1; + int mem_handle = -1; + int zip_handle = -1; + off_t mem_size; + void* mem; + void* zip; + + if (name == NULL || name[0] == '\0') + goto bail; + + if ((file = open(name, O_RDONLY)) < 0) + goto bail; + + if (try_mem && (mem_handle = zip_core_alloc(mem_size = filesize(file))) >= 0) { + if ((zip_handle = zip_core_alloc(sizeof(struct zip_mem))) < 0) + goto bail; + + mem = core_get_data(mem_handle); + + if (read(file, mem, mem_size) != mem_size) + goto bail; + + close(file); + + zip = core_get_data(zip_handle); + + zip_mem_init(zip, zip_handle, mem_handle, mem, mem_size); + } else { + if ((zip_handle = zip_core_alloc(sizeof(struct zip_file))) < 0) + goto bail; + + zip = core_get_data(zip_handle); + + zip_file_init(zip, zip_handle, file); + } + + return zip; + +bail: + if (file >= 0) + close(file); + core_free(mem_handle); + core_free(zip_handle); + return NULL; +} + +int zip_read_shallow(struct zip* z, zip_callback cb, void* ctx) { + int rv; + + if (z == NULL || z->state != ZIP_STATE_INITIAL || cb == NULL) + return -1; + + z->cb = cb; + z->ctx = ctx; + + if ((rv = zip_read_ed(z)) != 0) + return rv; + + return zip_read_cd(z, true); +} + +int zip_read_deep(struct zip* z, zip_callback cb, void* ctx) { + int rv; + + if (z == NULL || (z->state != ZIP_STATE_INITIAL && z->state != ZIP_STATE_CD_EXIT) || cb == NULL) + return -1; + + z->cb = cb; + z->ctx = ctx; + + if (z->state == ZIP_STATE_CD_EXIT) + goto read_entries; + + if ((rv = zip_read_ed(z)) != 0) + return rv; + + if ((rv = zip_read_cd(z, false)) != 0) + return rv; + +read_entries: + return zip_read_entries(z); +} + +int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx) { + int rv; + int ze_handle = -1; + struct zip_extract* ze; + char* path; + size_t size; + size_t length; + + if (root == NULL || root[0] == '\0') + root = PATH_ROOTSTR; + + if (root[0] != PATH_SEPCH) { + rv = -1; + goto bail; + } + + if (!dir_exists(root)) { + rv = 1; + goto bail; + } + + if ((ze_handle = zip_core_alloc(sizeof(struct zip_extract))) < 0) { + rv = 2; + goto bail; + } + + ze = core_get_data(ze_handle); + ze->cb = cb; + ze->ctx = ctx; + ze->file = -1; + + path = ze->path; + size = sizeof(ze->path); + length = strlcpy(path, root, size); + + if (length >= size) { + rv = 3; + goto bail; + } + + path += length; + size -= length; + + if (path[-1] != PATH_SEPCH) { + length = strlcpy(path, PATH_SEPSTR, size); + + if (length >= size) { + rv = 4; + goto bail; + } + + path += length; + size -= length; + } + + ze->name_offset = path - ze->path; + ze->name_size = size; + ze->name = path; + + rv = zip_read_deep(z, zip_extract_callback, ze); + +bail: + if (ze_handle >= 0) { + if (ze->file >= 0) + close(ze->file); + + core_free(ze_handle); + } + return rv; +} + +void zip_close(struct zip* z) { + if (z == NULL) + return; + + z->close(z); + + core_free(z->cds_handle); + + core_free(z->zip_handle); +} diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c index df1b4d3213..948911b973 100644 --- a/firmware/core_alloc.c +++ b/firmware/core_alloc.c @@ -8,24 +8,38 @@ /* not static so it can be discovered by core_get_data() */ struct buflib_context core_ctx; -/* defined in linker script */ #if (CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(__PCTOOL__) + #if defined(IPOD_VIDEO) && !defined(BOOTLOADER) +/* defined in linker script */ +extern unsigned char audiobuffer[]; extern unsigned char *audiobufend_lds[]; +/* pointer to end of audio buffer filled at runtime allocator_init */ unsigned char *audiobufend; -#else /* !IPOD_VIDEO */ -extern unsigned char audiobufend[]; -#endif +#elif defined(SANSA_E200) && defined(HAVE_BOOTLOADER_USB_MODE) +/* defined in linker script */ +extern unsigned char freebuffer[]; +extern unsigned char freebufferend[]; +/* map linker symbol to the audiobuffer in order to use core_alloc */ +unsigned char *audiobuffer = (unsigned char *)freebuffer; +unsigned char *audiobufend = (unsigned char *)freebufferend; +#else /* !IPOD_VIDEO, !SANSA_E200&&BOOTLOADERUSB */ /* defined in linker script */ extern unsigned char audiobuffer[]; +extern unsigned char audiobufend[]; +#endif + #else /* PLATFORM_HOSTED */ static unsigned char audiobuffer[((MEMORYSIZE)*1024-768)*1024]; 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)); @@ -40,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 @@ -58,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) @@ -83,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) @@ -93,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) { - const char *name = buflib_get_name(&core_ctx, handle); - return name ?: "<anonymous>"; + buflib_unpin(&core_ctx, handle); } +unsigned core_pin_count(int handle) +{ + 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) { - buflib_print_block_at(&core_ctx, block_num, buf, bufsize); + return buflib_print_block_at(&core_ctx, block_num, buf, bufsize); } -#ifdef DEBUG +bool core_test_free(void) +{ + bool ret = test_alloc > 0; + if (ret) + test_alloc = core_free(test_alloc); + + return ret; +} +#endif + +#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 11714b210d..8d2b9f44f3 100644 --- a/firmware/drivers/audio/ak4376.c +++ b/firmware/drivers/audio/ak4376.c @@ -83,11 +83,11 @@ void ak4376_open(void) ak4376_set_pdn_pin(1); mdelay(1); - static const int init_config[] = { + static const uint8_t init_config[] = { /* Ensure HPRHZ, HPLHZ are 0 */ AK4376_REG_OUTPUT_MODE, 0x00, /* Mute all volume controls */ - AK4376_REG_MIXER, 0x00, + AK4376_REG_MIXER, AK4376_MIX_LCH | (AK4376_MIX_RCH << 4), AK4376_REG_LCH_VOLUME, 0x80, AK4376_REG_RCH_VOLUME, 0x00, AK4376_REG_AMP_VOLUME, 0x00, @@ -150,14 +150,9 @@ static int round_step_up(int x, int step) return x - rem; } -static void calc_volumes(int vol, int* mix, int* dig, int* sw) +static void calc_volumes(int vol, int* dig, int* sw) { - /* Mixer can divide by 2, which gives an extra -6 dB adjustment */ - if(vol < AK4376_DIG_VOLUME_MIN) { - *mix |= AK4376_MIX_HALF; - vol += 60; - } - + /* Apply digital volume */ *dig = round_step_up(vol, AK4376_DIG_VOLUME_STEP); *dig = MIN(*dig, AK4376_DIG_VOLUME_MAX); *dig = MAX(*dig, AK4376_DIG_VOLUME_MIN); @@ -186,8 +181,8 @@ static int amp_vol_to_hw(int vol) void ak4376_set_volume(int vol_l, int vol_r) { int amp; - int mix_l = AK4376_MIX_LCH, dig_l, sw_l; - int mix_r = AK4376_MIX_RCH, dig_r, sw_r; + int dig_l, sw_l; + int dig_r, sw_r; if(vol_l <= AK4376_MIN_VOLUME && vol_r <= AK4376_MIN_VOLUME) { /* Special case for full mute */ @@ -202,11 +197,10 @@ void ak4376_set_volume(int vol_l, int vol_r) amp = MAX(amp, AK4376_AMP_VOLUME_MIN); /* Other controls are stereo */ - calc_volumes(vol_l - amp, &mix_l, &dig_l, &sw_l); - calc_volumes(vol_r - amp, &mix_r, &dig_r, &sw_r); + calc_volumes(vol_l - amp, &dig_l, &sw_l); + calc_volumes(vol_r - amp, &dig_r, &sw_r); } - ak4376_write(AK4376_REG_MIXER, (mix_l & 0xf) | ((mix_r & 0xf) << 4)); ak4376_write(AK4376_REG_LCH_VOLUME, dig_vol_to_hw(dig_l) | (1 << 7)); ak4376_write(AK4376_REG_RCH_VOLUME, dig_vol_to_hw(dig_r)); ak4376_write(AK4376_REG_AMP_VOLUME, amp_vol_to_hw(amp)); @@ -248,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/target/arm/tatung/tpj1022/power-tpj1022.c b/firmware/drivers/audio/eros_qn_codec.c index fa46f34daa..095b3b5305 100644 --- a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c +++ b/firmware/drivers/audio/eros_qn_codec.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2006 by Barry Wardell + * Copyright (C) 2021 Andrew Ryabinin, Dana Conrad * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,45 +19,44 @@ * ****************************************************************************/ -/* Created from power.c using some iPod code, and some custom stuff based on - GPIO analysis -*/ - -#include "config.h" -#include "cpu.h" -#include <stdbool.h> -#include "kernel.h" #include "system.h" -#include "power.h" -#include "logf.h" -#include "usb.h" +#include "eros_qn_codec.h" +#include "config.h" +#include "audio.h" +#include "audiohw.h" +#include "settings.h" +#include "pcm_sw_volume.h" -void power_init(void) -{ -} +#include "gpio-x1000.h" + +static long int vol_l_hw = PCM5102A_VOLUME_MIN; +static long int vol_r_hw = PCM5102A_VOLUME_MIN; +int es9018k2m_present_flag = 0; -unsigned int power_input_status(void) +void eros_qn_set_outputs(void) { - return POWER_INPUT_NONE; + audiohw_set_volume(vol_l_hw, vol_r_hw); } -void ide_power_enable(bool on) +void eros_qn_set_last_vol(long int vol_l, long int vol_r) { - (void)on; - /* We do nothing on the iPod */ + vol_l_hw = vol_l; + vol_r_hw = vol_r; } - -bool ide_powered(void) +int eros_qn_get_volume_limit(void) { - /* pretend we are always powered - we don't turn it off on the ipod */ - return true; + return (global_settings.volume_limit * 10); } -void power_off(void) +void eros_qn_switch_output(int select) { - /* Give things a second to settle before cutting power */ - sleep(HZ); - - //GPIOF_OUTPUT_VAL &=~ 0x20; -} + if (select == 0) + { + gpio_set_level(GPIO_STEREOSW_SEL, 0); + } + else + { + 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/es9218.c b/firmware/drivers/audio/es9218.c new file mode 100644 index 0000000000..76d387221a --- /dev/null +++ b/firmware/drivers/audio/es9218.c @@ -0,0 +1,226 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "audiohw.h" +#include "system.h" +#include "i2c-async.h" + +struct es9218_state { + enum es9218_clock_gear clk_gear; + uint32_t fsr; + uint32_t nco; +}; + +static struct es9218_state es9218; + +void es9218_open(void) +{ + /* Enable power supply */ + es9218_set_power_pin(1); + + /* "Wiggle" reset pin to get the internal oscillator to stabilize. + * This should also work if using an external powered oscillator, + * although in that case it's unnecessary to do this dance. */ + es9218_set_reset_pin(1); + udelay(75); + es9218_set_reset_pin(0); + udelay(50); + es9218_set_reset_pin(1); + mdelay(2); + + /* Initialize driver state */ + es9218.clk_gear = ES9218_CLK_GEAR_1; + es9218.fsr = 0; + es9218.nco = 0; +} + +void es9218_close(void) +{ + /* Turn off power supply */ + es9218_set_power_pin(0); + es9218_set_reset_pin(0); +} + +static void recalc_nco(void) +{ + /* nco * CLK * + * fsr = --------- * + * 2**32 */ + + uint32_t clk = es9218_get_mclk(); + clk >>= (int)es9218.clk_gear; + + uint64_t nco64 = es9218.fsr; + nco64 <<= 32; + nco64 /= clk; + + /* let's just ignore overflow... */ + uint32_t nco = nco64; + if(nco != es9218.nco) { + es9218.nco = nco; + + /* registers must be written in this order */ + es9218_write(ES9218_REG_PROG_NCO_BIT0_7, (nco >> 0) & 0xff); + es9218_write(ES9218_REG_PROG_NCO_BIT8_15, (nco >> 8) & 0xff); + es9218_write(ES9218_REG_PROG_NCO_BIT16_23, (nco >> 16) & 0xff); + es9218_write(ES9218_REG_PROG_NCO_BIT24_31, (nco >> 24) & 0xff); + } +} + +void es9218_set_clock_gear(enum es9218_clock_gear gear) +{ + if(gear != es9218.clk_gear) { + es9218.clk_gear = gear; + es9218_update(ES9218_REG_SYSTEM, 0x0c, (uint8_t)(gear & 3) << 2); + recalc_nco(); + } +} + +void es9218_set_nco_frequency(uint32_t fsr) +{ + if(fsr != es9218.fsr) { + es9218.fsr = fsr; + recalc_nco(); + } +} + +void es9218_recompute_nco(void) +{ + recalc_nco(); +} + +void es9218_set_amp_mode(enum es9218_amp_mode mode) +{ + es9218_update(ES9218_REG_AMP_CONFIG, 0x03, (uint8_t)mode & 3); +} + +void es9218_set_amp_powered(bool en) +{ + /* this doesn't seem to be necessary..? */ + es9218_update(ES9218_REG_ANALOG_CTRL, 0x40, en ? 0x40 : 0x00); +} + +void es9218_set_iface_role(enum es9218_iface_role role) +{ + /* asrc is used to lock onto the incoming audio frequency and is + * only used in aysnchronous slave mode. In synchronous operation, + * including master mode, it can be disabled to save power. */ + int asrc_en = (role == ES9218_IFACE_ROLE_SLAVE ? 1 : 0); + int master_mode = (role == ES9218_IFACE_ROLE_MASTER ? 1 : 0); + + es9218_update(ES9218_REG_MASTER_MODE_CONFIG, 1 << 7, master_mode << 7); + es9218_update(ES9218_REG_GENERAL_CONFIG, 1 << 7, asrc_en << 7); +} + +void es9218_set_iface_format(enum es9218_iface_format fmt, + enum es9218_iface_bits bits) +{ + uint8_t val = 0; + val |= ((uint8_t)bits & 3) << 6; + val |= ((uint8_t)fmt & 3) << 4; + /* keep low 4 bits zero -> use normal I2S mode, disable DSD mode */ + es9218_write(ES9218_REG_INPUT_SEL, val); +} + +static int dig_vol_to_hw(int x) +{ + x = MIN(x, ES9218_DIG_VOLUME_MAX); + x = MAX(x, ES9218_DIG_VOLUME_MIN); + return 0xff - (x - ES9218_DIG_VOLUME_MIN) / ES9218_DIG_VOLUME_STEP; +} + +static int amp_vol_to_hw(int x) +{ + x = MIN(x, ES9218_AMP_VOLUME_MAX); + x = MAX(x, ES9218_AMP_VOLUME_MIN); + return 24 - (x - ES9218_AMP_VOLUME_MIN) / ES9218_AMP_VOLUME_STEP; +} + +void es9218_set_dig_volume(int vol_l, int vol_r) +{ + es9218_write(ES9218_REG_VOLUME_LEFT, dig_vol_to_hw(vol_l)); + es9218_write(ES9218_REG_VOLUME_RIGHT, dig_vol_to_hw(vol_r)); +} + +void es9218_set_amp_volume(int vol) +{ + es9218_update(ES9218_REG_ANALOG_VOL, 0x1f, amp_vol_to_hw(vol)); +} + +void es9218_mute(bool en) +{ + es9218_update(ES9218_REG_FILTER_SYS_MUTE, 1, en ? 1 : 0); +} + +void es9218_set_filter(enum es9218_filter_type filt) +{ + es9218_update(ES9218_REG_FILTER_SYS_MUTE, 0xe0, ((int)filt & 7) << 5); +} + +void es9218_set_automute_time(int time) +{ + if(time < 0) time = 0; + if(time > 255) time = 255; + es9218_write(ES9218_REG_AUTOMUTE_TIME, time); +} + +void es9218_set_automute_level(int dB) +{ + es9218_update(ES9218_REG_AUTOMUTE_LEVEL, 0x7f, dB); +} + +void es9218_set_automute_fast_mode(bool en) +{ + es9218_update(ES9218_REG_MIX_AUTOMUTE, 0x10, en ? 0x10 : 0x00); +} + +void es9218_set_dpll_bandwidth(int knob) +{ + es9218_update(ES9218_REG_ASRC_DPLL_BANDWIDTH, 0xf0, (knob & 0xf) << 4); +} + +void es9218_set_thd_compensation(bool en) +{ + es9218_update(ES9218_REG_THD_COMP_BYPASS, 0x40, en ? 0x40 : 0); +} + +void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3) +{ + es9218_write(ES9218_REG_THD_COMP_C2_LO, c2 & 0xff); + es9218_write(ES9218_REG_THD_COMP_C2_HI, (c2 >> 8) & 0xff); + es9218_write(ES9218_REG_THD_COMP_C3_LO, c3 & 0xff); + es9218_write(ES9218_REG_THD_COMP_C3_HI, (c3 >> 8) & 0xff); +} + +int es9218_read(int reg) +{ + return i2c_reg_read1(ES9218_BUS, ES9218_ADDR, reg); +} + +void es9218_write(int reg, uint8_t val) +{ + i2c_reg_write1(ES9218_BUS, ES9218_ADDR, reg, val); +} + +void es9218_update(int reg, uint8_t msk, uint8_t val) +{ + i2c_reg_modify1(ES9218_BUS, ES9218_ADDR, reg, msk, val, NULL); +} 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/audio/xduoolinux_codec.c b/firmware/drivers/audio/xduoolinux_codec.c index 749d551739..3a25f6f7cf 100644 --- a/firmware/drivers/audio/xduoolinux_codec.c +++ b/firmware/drivers/audio/xduoolinux_codec.c @@ -238,7 +238,8 @@ void audiohw_set_filter_roll_off(int value) /* 0 = Sharp; 1 = Slow; 2 = Short Sharp - 3 = Short Slow */ + 3 = Short Slow + 4 = Super Slow */ #if defined(XDUOO_X3II) long int value_hw = value; alsa_controls_set_ints("AK4490 Digital Filter", 1, &value_hw); 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 5addfee3de..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 @@ -70,41 +66,40 @@ static bool enable_sw_poweroff = true; /* how long until repeat kicks in, in centiseconds */ #define REPEAT_START (30*HZ/100) -#ifndef HAVE_TOUCHSCREEN -/* the next two make repeat "accelerate", which is nice for lists +/* 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 smoothes 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 + * events even do anything sane for touchscreens??) */ /* the speed repeat starts at, in centiseconds */ #define REPEAT_INTERVAL_START (16*HZ/100) /* speed repeat finishes at, in centiseconds */ #define REPEAT_INTERVAL_FINISH (5*HZ/100) -#else -/* - * on touchscreen it's different, scrolling is done by swiping over the - * screen (potentially very quickly) and is completely different from button - * targets - * So, on touchscreen we don't want to artifically slow down early repeats, - * it'd have the contrary effect of making rockbox appear lagging - */ -#define REPEAT_INTERVAL_START (5*HZ/100) -#define REPEAT_INTERVAL_FINISH (5*HZ/100) -#endif +/* 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 int last_touchscreen_touch; +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 */ @@ -117,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 */ @@ -130,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 @@ -159,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; @@ -174,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; @@ -275,6 +304,11 @@ static void button_tick(void) /* yes we have repeat */ if (repeat_speed > REPEAT_INTERVAL_FINISH) repeat_speed--; +#ifdef HAVE_TOUCHSCREEN + if(btn & BUTTON_TOUCHSCREEN) + repeat_speed = REPEAT_INTERVAL_TOUCH; +#endif + count = repeat_speed; repeat_count++; @@ -349,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(); } } @@ -401,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 @@ -516,34 +522,27 @@ 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 - last_touchscreen_touch = 0xffff; + last_touchscreen_touch = -1; #endif /* Start polling last */ tick_add_task(button_tick); @@ -650,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 @@ -663,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; @@ -715,7 +720,7 @@ void button_clear_queue(void) } #ifdef HAVE_TOUCHSCREEN -int touchscreen_last_touch(void) +long touchscreen_last_touch(void) { return last_touchscreen_touch; } diff --git a/firmware/drivers/cw2015.c b/firmware/drivers/cw2015.c new file mode 100644 index 0000000000..705ca16e22 --- /dev/null +++ b/firmware/drivers/cw2015.c @@ -0,0 +1,191 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "cw2015.h" +#include "i2c-async.h" +#include <string.h> +#include "system.h" + +/* Headers for the debug menu */ +#ifndef BOOTLOADER +# include "action.h" +# include "list.h" +# include <stdio.h> +#endif + +/* Battery profile info is an opaque blob. According to this, + * https://lore.kernel.org/linux-pm/20200503154855.duwj2djgqfiyleq5@earth.universe/T/#u + * the blob only comes from Cellwise testing a physical battery and cannot be + * obtained any other way. It's specific to a given battery so each target has + * its own profile. + * + * Profile data seems to be retained on the chip so it's not a hard requirement + * to define this. Provided you don't lose power in the meantime, it should be + * enough to just boot the OF, then boot Rockbox and read out the battery info + * from the CW2015 debug screen. + */ +#if defined(SHANLING_Q1) +static const uint8_t device_batinfo[CW2015_SIZE_BATINFO] = { + 0x15, 0x7E, 0x61, 0x59, 0x57, 0x55, 0x56, 0x4C, + 0x4E, 0x4D, 0x50, 0x4C, 0x45, 0x3A, 0x2D, 0x27, + 0x22, 0x1E, 0x19, 0x1E, 0x2A, 0x3C, 0x48, 0x45, + 0x1D, 0x94, 0x08, 0xF6, 0x15, 0x29, 0x48, 0x51, + 0x5D, 0x60, 0x63, 0x66, 0x45, 0x1D, 0x83, 0x38, + 0x09, 0x43, 0x16, 0x42, 0x76, 0x98, 0xA5, 0x1B, + 0x41, 0x76, 0x99, 0xBF, 0x80, 0xC0, 0xEF, 0xCB, + 0x2F, 0x00, 0x64, 0xA5, 0xB5, 0x0E, 0x30, 0x29, +}; +#else +# define NO_BATINFO +#endif + +static uint8_t chip_batinfo[CW2015_SIZE_BATINFO]; + +/* TODO: Finish implementing this + * + * Although this chip might give a better battery estimate than voltage does, + * the mainline linux driver has a lot of weird hacks due to oddities like the + * SoC getting stuck during charging, and from limited testing it seems this + * may occur for the Q1 too. + */ + +static int cw2015_read_bat_info(uint8_t* data) +{ + for(int i = 0; i < CW2015_SIZE_BATINFO; ++i) { + int r = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_BATINFO + i); + if(r < 0) + return r; + + data[i] = r & 0xff; + } + + return 0; +} + +void cw2015_init(void) +{ + /* mdelay(100); */ + int rc = cw2015_read_bat_info(&chip_batinfo[0]); + if(rc < 0) + memset(chip_batinfo, 0, sizeof(chip_batinfo)); +} + +int cw2015_get_vcell(void) +{ + int vcell_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL); + int vcell_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL+1); + + if(vcell_msb < 0 || vcell_lsb < 0) + return -1; + + /* 14 bits, resolution 305 uV */ + int v_raw = ((vcell_msb & 0x3f) << 8) | vcell_lsb; + return v_raw * 61 / 200; +} + +int cw2015_get_soc(void) +{ + int soc_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_SOC); + + if(soc_msb < 0) + return -1; + + /* MSB is the state of charge in percentage. + * the LSB contains fractional information not useful to Rockbox. */ + return soc_msb & 0xff; +} + +int cw2015_get_rrt(void) +{ + int rrt_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT); + int rrt_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT+1); + + if(rrt_msb < 0 || rrt_lsb < 0) + return -1; + + /* 13 bits, resolution 1 minute */ + return ((rrt_msb & 0x1f) << 8) | rrt_lsb; +} + +const uint8_t* cw2015_get_bat_info(void) +{ + return &chip_batinfo[0]; +} + +#ifndef BOOTLOADER +enum { + CW2015_DEBUG_VCELL = 0, + CW2015_DEBUG_SOC, + CW2015_DEBUG_RRT, + CW2015_DEBUG_BATINFO, + CW2015_DEBUG_BATINFO_LAST = CW2015_DEBUG_BATINFO + 7, + CW2015_DEBUG_NUM_ENTRIES, +}; + +static int cw2015_debug_menu_cb(int action, struct gui_synclist* lists) +{ + (void)lists; + + if(action == ACTION_NONE) + action = ACTION_REDRAW; + + return action; +} + +static const char* cw2015_debug_menu_get_name(int item, void* data, + char* buf, size_t buflen) +{ + (void)data; + + /* hexdump of battery info */ + if(item >= CW2015_DEBUG_BATINFO && item <= CW2015_DEBUG_BATINFO_LAST) { + int i = item - CW2015_DEBUG_BATINFO; + const uint8_t* batinfo = cw2015_get_bat_info(); + snprintf(buf, buflen, "BatInfo%d: %02x %02x %02x %02x %02x %02x %02x %02x", i, + batinfo[8*i + 0], batinfo[8*i + 1], batinfo[8*i + 2], batinfo[8*i + 3], + batinfo[8*i + 4], batinfo[8*i + 5], batinfo[8*i + 6], batinfo[8*i + 7]); + return buf; + } + + switch(item) { + case CW2015_DEBUG_VCELL: + snprintf(buf, buflen, "VCell: %d mV", cw2015_get_vcell()); + return buf; + case CW2015_DEBUG_SOC: + snprintf(buf, buflen, "SOC: %d%%", cw2015_get_soc()); + return buf; + case CW2015_DEBUG_RRT: + snprintf(buf, buflen, "Runtime: %d min", cw2015_get_rrt()); + return buf; + default: + return "---"; + } +} + +bool cw2015_debug_menu(void) +{ + struct simplelist_info info; + simplelist_info_init(&info, "CW2015 debug", CW2015_DEBUG_NUM_ENTRIES, NULL); + info.action_callback = cw2015_debug_menu_cb; + info.get_name = cw2015_debug_menu_get_name; + return simplelist_show_list(&info); +} +#endif diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index 337e29a1bc..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)) @@ -2276,6 +2276,41 @@ fat_error: return rc; } +int fat_modtime(struct fat_file *parent, struct fat_file *file, + time_t modtime) +{ + struct bpb * const fat_bpb = FAT_BPB(parent->volume); + + if (!fat_bpb) + return -1; + + int rc; + + struct fat_filestr parentstr; + fat_filestr_init(&parentstr, parent); + + dc_lock_cache(); + + union raw_dirent *ent = cache_direntry(fat_bpb, &parentstr, file->e.entry); + if (!ent) + FAT_ERROR(-2); + + uint16_t date; + uint16_t time; + dostime_localtime(modtime, &date, &time); + + ent->wrttime = htole16(time); + ent->wrtdate = htole16(date); + ent->lstaccdate = htole16(date); + + dc_dirty_buf(ent); + + rc = 0; +fat_error: + dc_unlock_cache(); + cache_commit(fat_bpb); + return rc; +} /** File stream functions **/ @@ -2954,20 +2989,6 @@ void fat_empty_fat_direntry(struct fat_direntry *entry) entry->firstcluster = 0; } -time_t fattime_mktime(uint16_t fatdate, uint16_t fattime) -{ - /* this knows our mktime() only uses these struct tm fields */ - struct tm tm; - tm.tm_sec = ((fattime ) & 0x1f) * 2; - tm.tm_min = ((fattime >> 5) & 0x3f); - tm.tm_hour = ((fattime >> 11) ); - tm.tm_mday = ((fatdate ) & 0x1f); - tm.tm_mon = ((fatdate >> 5) & 0x0f) - 1; - tm.tm_year = ((fatdate >> 9) ) + 80; - - return mktime(&tm); -} - void fat_init(void) { dc_lock_cache(); diff --git a/firmware/drivers/ft6x06.c b/firmware/drivers/ft6x06.c index 538ca10480..c605ee0944 100644 --- a/firmware/drivers/ft6x06.c +++ b/firmware/drivers/ft6x06.c @@ -24,6 +24,16 @@ #include "i2c-async.h" #include <string.h> +#define BYTES_PER_POINT 6 + +#ifdef FT6x06_SWAP_AXES +# define POS_X pos_y +# define POS_Y pos_x +#else +# define POS_X pos_x +# define POS_Y pos_y +#endif + struct ft6x06_driver { /* i2c bus data */ int i2c_cookie; @@ -33,39 +43,42 @@ struct ft6x06_driver { ft6x06_event_cb event_cb; /* buffer for I2C transfers */ - uint8_t raw_data[6]; + uint8_t raw_data[1 + 2 + BYTES_PER_POINT * FT6x06_NUM_POINTS]; }; static struct ft6x06_driver ft_drv; struct ft6x06_state ft6x06_state; +static inline void ft6x06_convert_point(const uint8_t* raw, + struct ft6x06_point* pt) +{ + pt->event = (raw[0] >> 6) & 0x3; + pt->touch_id = (raw[2] >> 4) & 0xf; + pt->POS_X = ((raw[0] & 0xf) << 8) | raw[1]; + pt->POS_Y = ((raw[2] & 0xf) << 8) | raw[3]; + pt->weight = raw[4]; + pt->area = (raw[5] >> 4) & 0xf; +} + static void ft6x06_i2c_callback(int status, i2c_descriptor* desc) { (void)desc; if(status != I2C_STATUS_OK) return; - int evt = ft_drv.raw_data[1] >> 6; - int tx = ft_drv.raw_data[2] | ((ft_drv.raw_data[1] & 0xf) << 8); - int ty = ft_drv.raw_data[4] | ((ft_drv.raw_data[3] & 0xf) << 8); - - ft6x06_state.event = evt; -#ifdef FT6x06_SWAP_AXES - ft6x06_state.pos_x = ty; - ft6x06_state.pos_y = tx; -#else - ft6x06_state.pos_x = tx; - ft6x06_state.pos_y = ty; -#endif + ft6x06_state.gesture = ft_drv.raw_data[1]; + ft6x06_state.nr_points = ft_drv.raw_data[2] & 0xf; + for(int i = 0; i < FT6x06_NUM_POINTS; ++i) { + ft6x06_convert_point(&ft_drv.raw_data[3 + i * BYTES_PER_POINT], + &ft6x06_state.points[i]); + } - ft_drv.event_cb(evt, ft6x06_state.pos_x, ft6x06_state.pos_y); + ft_drv.event_cb(&ft6x06_state); } -static void ft6x06_dummy_event_cb(int evt, int tx, int ty) +static void ft6x06_dummy_event_cb(struct ft6x06_state* state) { - (void)evt; - (void)tx; - (void)ty; + (void)state; } void ft6x06_init(void) @@ -74,9 +87,10 @@ void ft6x06_init(void) memset(&ft_drv, 0, sizeof(ft_drv)); ft_drv.event_cb = ft6x06_dummy_event_cb; - ft6x06_state.event = FT6x06_EVT_NONE; - ft6x06_state.pos_x = 0; - ft6x06_state.pos_y = 0; + memset(&ft6x06_state, 0, sizeof(struct ft6x06_state)); + ft6x06_state.gesture = -1; + for(int i = 0; i < FT6x06_NUM_POINTS; ++i) + ft6x06_state.points[i].event = FT6x06_EVT_NONE; /* Reserve bus management cookie */ ft_drv.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1); @@ -85,16 +99,16 @@ void ft6x06_init(void) ft_drv.i2c_desc.slave_addr = FT6x06_ADDR; ft_drv.i2c_desc.bus_cond = I2C_START | I2C_STOP; ft_drv.i2c_desc.tran_mode = I2C_READ; - ft_drv.i2c_desc.buffer[0] = &ft_drv.raw_data[5]; + ft_drv.i2c_desc.buffer[0] = &ft_drv.raw_data[0]; ft_drv.i2c_desc.count[0] = 1; - ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[0]; - ft_drv.i2c_desc.count[1] = 5; + ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[1]; + ft_drv.i2c_desc.count[1] = sizeof(ft_drv.raw_data) - 1; ft_drv.i2c_desc.callback = ft6x06_i2c_callback; ft_drv.i2c_desc.arg = 0; ft_drv.i2c_desc.next = NULL; /* Set I2C register address */ - ft_drv.raw_data[5] = 0x02; + ft_drv.raw_data[0] = 0x01; } void ft6x06_set_event_cb(ft6x06_event_cb cb) diff --git a/firmware/drivers/generic_i2c.c b/firmware/drivers/generic_i2c.c index effb5372b4..9fd90b5b2c 100644 --- a/firmware/drivers/generic_i2c.c +++ b/firmware/drivers/generic_i2c.c @@ -198,6 +198,40 @@ end: return ret; } +int i2c_write_read_data(int bus_index, int bus_address, + const unsigned char* buf_write, int count_write, + unsigned char* buf_read, int count_read) +{ + int i; + int ret = 0; + const struct i2c_interface *iface = i2c_if[bus_index]; + + i2c_start(iface); + if (!i2c_outb(iface, bus_address)) + { + ret = -2; + goto end; + } + + for(i = 0;i < count_write;i++) + { + if (!i2c_outb(iface, buf_write[i])) + { + ret = -3; + goto end; + } + } + + for(i = 0;i < count_read-1;i++) + buf_read[i] = i2c_inb(iface, true); + + buf_read[i] = i2c_inb(iface, false); + +end: + i2c_stop(iface); + return ret; +} + /* returns bus index which can be used as a handle, or <0 on error */ int i2c_add_node(const struct i2c_interface *iface) { diff --git a/firmware/drivers/isp1583.c b/firmware/drivers/isp1583.c index c028d2fa01..e60339d9fc 100644 --- a/firmware/drivers/isp1583.c +++ b/firmware/drivers/isp1583.c @@ -341,7 +341,7 @@ static void usb_handle_setup_rx(void) if (len == 8) { ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STATUS; /* Acknowledge packet */ - usb_core_control_request((struct usb_ctrlrequest*)setup_pkt_buf); + usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_pkt_buf); } else { @@ -531,9 +531,9 @@ static void in_callback(int ep, unsigned char *buf, int len) usb_core_transfer_complete(ep, false, 0, len); } -int usb_drv_recv(int ep, void* ptr, int length) +int usb_drv_recv_nonblocking(int ep, void* ptr, int length) { - logf("usb_drv_recv(%d, 0x%x, %d)", ep, (int)ptr, length); + logf("usb_drv_recv_nonblocking(%d, 0x%x, %d)", ep, (int)ptr, length); if(ep == EP_CONTROL && length == 0 && ptr == NULL) { usb_status_ack(ep, DIR_TX); diff --git a/firmware/drivers/lcd-16bit-common.c b/firmware/drivers/lcd-16bit-common.c index dbfea50dd7..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)) + 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 > 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; - 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,80 +215,20 @@ 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) { - const unsigned char *src_end; - fb_data *dst, *dst_col; - unsigned dmask = 0x100; /* bit 8 == sentinel */ - int drmode = lcd_current_viewport->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)) + struct viewport *vp = lcd_current_viewport; + 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; + /* move starting point */ + src += stride * (src_y >> 3) + src_x; + src_y &= 7; - /* 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_col = FBADDR(x, y); - - /* 'Bugfix' mono_bitmap_part reads ahead in the buffer, - * if the height is <= char bit pixels other memory gets read - * the other option is to check in the hot code path but this appears - * sufficient - */ - if (height <= CHAR_BIT) - stride = 0; + unsigned dmask = 0; + int drmode = vp->drawmode; if (drmode & DRMODE_INVERSEVID) { - dmask = 0x1ff; /* bit 8 == sentinel */ + dmask = 0xff; drmode &= DRMODE_SOLID; /* mask out inversevid */ } @@ -364,107 +236,99 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x, if ((drmode & DRMODE_BG) && lcd_backdrop) drmode |= DRMODE_INT_BD; - /* go through each column and update each pixel */ - do + fb_data* dst = FBADDR(x, y); + while(height > 0) { - const unsigned char *src_col = src++; - unsigned data = (*src_col ^ dmask) >> src_y; + const unsigned char* src_col = src; + const unsigned char* src_end = src + width; + fb_data* dst_col = dst; + + unsigned data; int fg, bg; uintptr_t bo; - dst = dst_col; - dst_col += COL_INC; - row = height; - -#define UPDATE_SRC do { \ - data >>= 1; \ - if (data == 0x001) { \ - src_col += stride; \ - data = *src_col ^ dmask; \ - } \ - } while (0) + switch (drmode) { + case DRMODE_COMPLEMENT: + do { + data = (*src_col++ ^ dmask) >> src_y; + if(data & 0x01) + *dst_col = ~(*dst_col); - switch (drmode) - { - case DRMODE_COMPLEMENT: - do - { - if (data & 0x01) - *dst = ~(*dst); - - dst += ROW_INC; - UPDATE_SRC; - } - while (--row); + dst_col += COL_INC; + } while(src_col != src_end); break; - case DRMODE_BG|DRMODE_INT_BD: + case DRMODE_BG|DRMODE_INT_BD: bo = lcd_backdrop_offset; - do - { - if (!(data & 0x01)) - *dst = *PTR_ADD(dst, bo); + do { + data = (*src_col++ ^ dmask) >> src_y; + if(!(data & 0x01)) + *dst_col = *PTR_ADD(dst_col, bo); - dst += ROW_INC; - UPDATE_SRC; - } - while (--row); + dst_col += COL_INC; + } while(src_col != src_end); break; case DRMODE_BG: - bg = lcd_current_viewport->bg_pattern; - do - { - if (!(data & 0x01)) - *dst = bg; - - dst += ROW_INC; - UPDATE_SRC; - } - while (--row); + bg = vp->bg_pattern; + do { + data = (*src_col++ ^ dmask) >> src_y; + if(!(data & 0x01)) + *dst_col = bg; + + dst_col += COL_INC; + } while(src_col != src_end); break; - case DRMODE_FG: - fg = lcd_current_viewport->fg_pattern; - do - { - if (data & 0x01) - *dst = fg; + case DRMODE_FG: + fg = vp->fg_pattern; + do { + data = (*src_col++ ^ dmask) >> src_y; + if(data & 0x01) + *dst_col = fg; - dst += ROW_INC; - UPDATE_SRC; - } - while (--row); + dst_col += COL_INC; + } while(src_col != src_end); break; - case DRMODE_SOLID|DRMODE_INT_BD: - fg = lcd_current_viewport->fg_pattern; + case DRMODE_SOLID|DRMODE_INT_BD: + fg = vp->fg_pattern; bo = lcd_backdrop_offset; - do - { - *dst = (data & 0x01) ? fg - : *PTR_ADD(dst, bo); - dst += ROW_INC; - UPDATE_SRC; - } - while (--row); + do { + data = (*src_col++ ^ dmask) >> src_y; + if(data & 0x01) + *dst_col = fg; + else + *dst_col = *PTR_ADD(dst_col, bo); + + dst_col += COL_INC; + } while(src_col != src_end); break; - case DRMODE_SOLID: - fg = lcd_current_viewport->fg_pattern; - bg = lcd_current_viewport->bg_pattern; - do - { - *dst = (data & 0x01) ? fg : bg; - dst += ROW_INC; - UPDATE_SRC; - } - while (--row); + case DRMODE_SOLID: + fg = vp->fg_pattern; + bg = vp->bg_pattern; + do { + data = (*src_col++ ^ dmask) >> src_y; + if(data & 0x01) + *dst_col = fg; + else + *dst_col = bg; + + dst_col += COL_INC; + } while(src_col != src_end); break; } + + src_y = (src_y + 1) & 7; + if(src_y == 0) + src += stride; + + dst += ROW_INC; + height--; } - while (src < src_end); } + /* Draw a full monochrome bitmap */ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height) { @@ -472,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. @@ -491,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) @@ -504,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 \ @@ -515,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) @@ -526,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); @@ -535,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); @@ -546,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 + { + *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 { - 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, 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 170ad374ea..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)) { @@ -511,9 +678,10 @@ static bool LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string, x = x * (linebased ? cwidth : 1); width = vp->width - x; - if (y >= vp->height || (height + y) > (vp->height)) + if (y >= vp->height) return false; - + if ((height + y) > (vp->height)) + height = vp->height - y; s = find_scrolling_line(x, y); restart = !s; @@ -531,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) * @@ -676,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/libertas/firmware/LICENCE.Marvell b/firmware/drivers/libertas/firmware/LICENCE.Marvell new file mode 100644 index 0000000000..fdf4cda9c0 --- /dev/null +++ b/firmware/drivers/libertas/firmware/LICENCE.Marvell @@ -0,0 +1,22 @@ +Copyright © 2019. Marvell International Ltd. All rights reserved. + +Redistribution and use in binary form is permitted provided that the following +conditions are met: + +1. Redistributions must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +2. Redistribution and use shall be used only with Marvell silicon products. +Any other use, reproduction, modification, translation, or compilation of the +Software is prohibited. + +3. No reverse engineering, decompilation, or disassembly is permitted. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +“AS IS” WITHOUT WARRANTY OF ANY KIND, INCLUDING, WITHOUT LIMITATION, ANY EXPRESS +OR IMPLIED WARRANTIES OF MERCHANTABILITY, ACCURACY, FITNESS OR SUFFICIENCY FOR A +PARTICULAR PURPOSE, SATISFACTORY QUALITY, CORRESPONDENCE WITH DESCRIPTION, QUIET +ENJOYMENT OR NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS. +MARVELL, ITS AFFILIATES AND THEIR SUPPLIERS DISCLAIM ANY WARRANTY THAT THE +DELIVERABLES WILL OPERATE WITHOUT INTERRUPTION OR BE ERROR-FREE. diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9.bin b/firmware/drivers/libertas/firmware/gspi8686_v9.bin Binary files differnew file mode 100644 index 0000000000..58dc03d30e --- /dev/null +++ b/firmware/drivers/libertas/firmware/gspi8686_v9.bin diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin Binary files differnew file mode 100644 index 0000000000..424cfb386c --- /dev/null +++ b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin diff --git a/firmware/drivers/libertas/if_spi.c b/firmware/drivers/libertas/if_spi.c new file mode 100644 index 0000000000..be564cc64f --- /dev/null +++ b/firmware/drivers/libertas/if_spi.c @@ -0,0 +1,674 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by Tomasz Moń + * Ported from Linux libertas driver + * Copyright 2008 Analog Devices Inc. + * Authors: + * Andrey Yurovsky <andrey@cozybit.com> + * Colin McCabe <colin@cozybit.com> + * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman + * + * 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" +/*#define LOGF_ENABLE*/ +#include "logf.h" +#include "errno.h" +#include "file.h" +#include "panic.h" +#include "system.h" +#include "tick.h" +#include <stddef.h> +#include "if_spi.h" +#include "if_spi_drv.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1)/(d)) + +struct if_spi_card +{ + /* The card ID and card revision, as reported by the hardware. */ + uint16_t card_id; + uint8_t card_rev; + + unsigned long spu_port_delay; + unsigned long spu_reg_delay; + + uint8_t cmd_buffer[IF_SPI_CMD_BUF_SIZE]; +}; + +#define MODEL_8686 0x0b + +static const struct +{ + uint16_t model; + const char *helper; + const char *main; +} +fw_table[] = +{ + { MODEL_8686, ROCKBOX_DIR"/libertas/gspi8686_v9_helper.bin", ROCKBOX_DIR"/libertas/gspi8686_v9.bin" }, + { 0, NULL, NULL } +}; + + +/* + * SPI Interface Unit Routines + * + * The SPU sits between the host and the WLAN module. + * All communication with the firmware is through SPU transactions. + * + * First we have to put a SPU register name on the bus. Then we can + * either read from or write to that register. + * + */ + +static void spu_transaction_init(struct if_spi_card *card) +{ + (void)card; + /* Linux delays 400 ns if spu_transaction_finish() was called + * within the same jiffy. As we don't have jiffy counter nor + * nanosecond delays, simply delay for 1 us. This currently + * does not really matter as this driver simply loads firmware. + */ + udelay(1); + libertas_spi_cs(0); /* assert CS */ +} + +static void spu_transaction_finish(struct if_spi_card *card) +{ + (void)card; + libertas_spi_cs(1); /* drop CS */ +} + +/* + * Write out a byte buffer to an SPI register, + * using a series of 16-bit transfers. + */ +static int spu_write(struct if_spi_card *card, uint16_t reg, const uint8_t *buf, int len) +{ + int err = 0; + uint8_t reg_out[2]; + + /* You must give an even number of bytes to the SPU, even if it + * doesn't care about the last one. */ + if (len & 0x1) + panicf("Odd length in spu_write()"); + + reg |= IF_SPI_WRITE_OPERATION_MASK; + reg_out[0] = (reg & 0x00FF); + reg_out[1] = (reg & 0xFF00) >> 8; + + spu_transaction_init(card); + libertas_spi_tx(reg_out, sizeof(reg_out)); + libertas_spi_tx(buf, len); + spu_transaction_finish(card); + return err; +} + +static inline int spu_write_u16(struct if_spi_card *card, uint16_t reg, uint16_t val) +{ + uint8_t buf[2]; + buf[0] = (val & 0x00FF); + buf[1] = (val & 0xFF00) >> 8; + return spu_write(card, reg, buf, sizeof(buf)); +} + +static inline int spu_reg_is_port_reg(uint16_t reg) +{ + switch (reg) + { + case IF_SPI_IO_RDWRPORT_REG: + case IF_SPI_CMD_RDWRPORT_REG: + case IF_SPI_DATA_RDWRPORT_REG: + return 1; + default: + return 0; + } +} + +static int spu_read(struct if_spi_card *card, uint16_t reg, uint8_t *buf, int len) +{ + unsigned int delay; + int err = 0; + uint8_t reg_out[2]; + + /* + * You must take an even number of bytes from the SPU, even if you + * don't care about the last one. + */ + if (len & 0x1) + panicf("Odd length in spu_read()"); + + reg |= IF_SPI_READ_OPERATION_MASK; + reg_out[0] = (reg & 0x00FF); + reg_out[1] = (reg & 0xFF00) >> 8; + + spu_transaction_init(card); + libertas_spi_tx(reg_out, sizeof(reg_out)); + + delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : card->spu_reg_delay; + /* Busy-wait while the SPU fills the FIFO */ + delay = DIV_ROUND_UP((100 + (delay * 10)), 1000); + if (delay < 1000) + udelay(delay); + else + mdelay(DIV_ROUND_UP(delay, 1000)); + + libertas_spi_rx(buf, len); + spu_transaction_finish(card); + return err; +} + +/* Read 16 bits from an SPI register */ +static inline int spu_read_u16(struct if_spi_card *card, uint16_t reg, uint16_t *val) +{ + uint8_t buf[2]; + int ret; + + ret = spu_read(card, reg, buf, sizeof(buf)); + if (ret == 0) + *val = buf[0] | (buf[1] << 8); + + return ret; +} + +/* + * Read 32 bits from an SPI register. + * The low 16 bits are read first. + */ +static int spu_read_u32(struct if_spi_card *card, uint16_t reg, uint32_t *val) +{ + uint8_t buf[4]; + int err; + + err = spu_read(card, reg, buf, sizeof(buf)); + if (!err) + *val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + return err; +} + +/* + * Keep reading 16 bits from an SPI register until you get the correct result. + * + * If mask = 0, the correct result is any non-zero number. + * If mask != 0, the correct result is any number where + * number & target_mask == target + * + * Returns -ETIMEDOUT if a five seconds passes without the correct result. + */ +static int spu_wait_for_u16(struct if_spi_card *card, uint16_t reg, + uint16_t target_mask, uint16_t target) +{ + int err; + unsigned long timeout = current_tick + 5*HZ; + while (1) + { + uint16_t val; + err = spu_read_u16(card, reg, &val); + if (err) + return err; + if (target_mask) + { + if ((val & target_mask) == target) + return 0; + } + else + { + if (val) + return 0; + } + udelay(100); + if (TIME_AFTER(current_tick, timeout)) + { + logf("%s: timeout with val=%02x, target_mask=%02x, target=%02x", + __func__, val, target_mask, target); + return -ETIMEDOUT; + } + } +} + +/* + * Read 16 bits from an SPI register until you receive a specific value. + * Returns -ETIMEDOUT if a 4 tries pass without success. + */ +static int spu_wait_for_u32(struct if_spi_card *card, uint32_t reg, uint32_t target) +{ + int err, try; + for (try = 0; try < 4; ++try) + { + uint32_t val = 0; + err = spu_read_u32(card, reg, &val); + if (err) + return err; + if (val == target) + return 0; + mdelay(100); + } + return -ETIMEDOUT; +} + +static int spu_set_interrupt_mode(struct if_spi_card *card, + int suppress_host_int, + int auto_int) +{ + int err = 0; + + /* + * We can suppress a host interrupt by clearing the appropriate + * bit in the "host interrupt status mask" register + */ + if (suppress_host_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, + IF_SPI_HISM_TX_DOWNLOAD_RDY | + IF_SPI_HISM_RX_UPLOAD_RDY | + IF_SPI_HISM_CMD_DOWNLOAD_RDY | + IF_SPI_HISM_CARDEVENT | + IF_SPI_HISM_CMD_UPLOAD_RDY); + if (err) + return err; + } + + /* + * If auto-interrupts are on, the completion of certain transactions + * will trigger an interrupt automatically. If auto-interrupts + * are off, we need to set the "Card Interrupt Cause" register to + * trigger a card interrupt. + */ + if (auto_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, + IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } + return err; +} + +static int spu_get_chip_revision(struct if_spi_card *card, + uint16_t *card_id, uint8_t *card_rev) +{ + int err = 0; + uint32_t dev_ctrl; + err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); + if (err) + return err; + *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); + *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); + return err; +} + +static int spu_set_bus_mode(struct if_spi_card *card, uint16_t mode) +{ + int err = 0; + uint16_t rval; + /* set bus mode */ + err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); + if (err) + return err; + /* Check that we were able to read back what we just wrote. */ + err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); + if (err) + return err; + if ((rval & 0xF) != mode) + { + logf("Can't read bus mode register"); + return -EIO; + } + return 0; +} + +static int spu_init(struct if_spi_card *card) +{ + int err = 0; + uint32_t delay; + + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + card->spu_port_delay = 1000; + card->spu_reg_delay = 1000; + err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); + if (err) + return err; + card->spu_port_delay = delay & 0x0000ffff; + card->spu_reg_delay = (delay & 0xffff0000) >> 16; + + logf("Initialized SPU unit. " + "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx", + card->spu_port_delay, card->spu_reg_delay); + return err; +} + + +/* + * Firmware Loading + */ + +static int if_spi_prog_helper_firmware(struct if_spi_card *card, int fd) +{ + int err = 0; + int bytes_read; + uint8_t *temp = card->cmd_buffer; + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + /* Load helper firmware image */ + while ((bytes_read = read(fd, temp, HELPER_FW_LOAD_CHUNK_SZ)) > 0) + { + /* + * Scratch pad 1 should contain the number of bytes we + * want to download to the firmware + */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, + HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto out; + + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) + goto out; + + /* + * Feed the data into the command read/write port reg + * in chunks of 64 bytes + */ + memset(temp + bytes_read, 0, HELPER_FW_LOAD_CHUNK_SZ - bytes_read); + mdelay(10); + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + temp, HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto out; + + /* Interrupt the boot code */ + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto out; + } + + /* + * Once the helper / single stage firmware download is complete, + * write 0 to scratch pad 1 and interrupt the + * bootloader. This completes the helper download. + */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); +out: + if (err) + logf("failed to load helper firmware (err=%d)", err); + + return err; +} + +/* + * Returns the length of the next packet the firmware expects us to send. + * Sets crc_err if the previous transfer had a CRC error. + */ +static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, + int *crc_err) +{ + uint16_t len; + int err = 0; + + /* + * wait until the host interrupt status register indicates + * that we are ready to download + */ + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) + { + logf("timed out waiting for host_int_status"); + return err; + } + + /* Ask the device how many bytes of firmware it wants. */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + return err; + + if (len > IF_SPI_CMD_BUF_SIZE) + { + logf("firmware load device requested a larger transfer than we are prepared to handle (len = %d)", + len); + return -EIO; + } + if (len & 0x1) { + logf("%s: crc error", __func__); + len &= ~0x1; + *crc_err = 1; + } else + *crc_err = 0; + + return len; +} + +static int if_spi_prog_main_firmware(struct if_spi_card *card, int fd) +{ + int len; + int bytes_read = 0, crc_err = 0, err = 0; + uint16_t num_crc_errs; + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); + if (err) + { + logf("%s: timed out waiting for initial scratch reg = 0", __func__); + goto out; + } + + num_crc_errs = 0; + while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) + { + if (len < 0) + { + err = len; + goto out; + } + if (crc_err) + { + /* Previous transfer failed. */ + if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) + { + logf("Too many CRC errors encountered in firmware load."); + err = -EIO; + goto out; + } + + /* Rewind so we read back the data from previous transfer */ + lseek(fd, -bytes_read, SEEK_CUR); + } + + bytes_read = read(fd, card->cmd_buffer, len); + if (bytes_read < 0) + { + /* + * If there are no more bytes left, we would normally + * expect to have terminated with len = 0 + */ + logf("Firmware load wants more bytes than we have to offer."); + break; + } + else if (bytes_read < len) + { + memset(card->cmd_buffer + bytes_read, 0, len - bytes_read); + } + + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, card->cmd_buffer, len); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto out; + } + if (read(fd, card->cmd_buffer, IF_SPI_CMD_BUF_SIZE) > 0) + { + logf("firmware load wants fewer bytes than we have to offer"); + } + + /* Confirm firmware download */ + err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, + SUCCESSFUL_FW_DOWNLOAD_MAGIC); + if (err) + { + logf("failed to confirm the firmware download"); + goto out; + } + +out: + if (err) + logf("failed to load firmware (err=%d)", err); + + return err; +} + +static int if_spi_init_card(struct if_spi_card *card) +{ + int err; + size_t i; + uint32_t scratch; + int fd; + + err = spu_init(card); + if (err) + goto out; + err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); + if (err) + goto out; + + err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); + if (err) + goto out; + if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) + logf("Firmware is already loaded for Marvell WLAN 802.11 adapter"); + else { + /* Check if we support this card */ + for (i = 0; i < ARRAY_SIZE(fw_table); i++) { + if (card->card_id == fw_table[i].model) + break; + } + if (i == ARRAY_SIZE(fw_table)) { + logf("Unsupported chip_id: 0x%02x", card->card_id); + err = -ENODEV; + goto out; + } + + logf("Initializing FW for Marvell WLAN 802.11 adapter " + "(chip_id = 0x%04x, chip_rev = 0x%02x)", + card->card_id, card->card_rev); + + fd = open(fw_table[i].helper, O_RDONLY); + if (fd >= 0) + { + err = if_spi_prog_helper_firmware(card, fd); + close(fd); + if (err) + goto out; + } + else + { + logf("failed to find firmware helper (%s)", fw_table[i].helper); + err = -ENOENT; + goto out; + } + + fd = open(fw_table[i].main, O_RDONLY); + if (fd >= 0) + { + err = if_spi_prog_main_firmware(card, fd); + close(fd); + if (err) + goto out; + } + else + { + logf("failed to find firmware (%s)", fw_table[i].main); + err = -ENOENT; + goto out; + } + + logf("loaded FW for Marvell WLAN 802.11 adapter"); + } + + err = spu_set_interrupt_mode(card, 0, 1); + if (err) + goto out; + +out: + return err; +} + +void wifi_init(void) INIT_ATTR +{ +#if 0 + static struct if_spi_card card; + libertas_spi_init(); + libertas_spi_pd(1); + libertas_spi_reset(1); + mdelay(100); + if (!if_spi_init_card(&card)) + { + /* TODO: Configure card and enter deep sleep */ + } + else +#else + libertas_spi_init(); + (void)if_spi_init_card; +#endif + { + /* Keep the lines in lowest power configuration */ + libertas_spi_pd(0); + libertas_spi_reset(1); + libertas_spi_cs(1); + } +} diff --git a/firmware/drivers/libertas/if_spi.h b/firmware/drivers/libertas/if_spi.h new file mode 100644 index 0000000000..bfca12981a --- /dev/null +++ b/firmware/drivers/libertas/if_spi.h @@ -0,0 +1,215 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by Tomasz Moń + * Ported from Linux libertas driver + * Copyright 2008 Analog Devices Inc. + * Authors: + * Andrey Yurovsky <andrey@cozybit.com> + * Colin McCabe <colin@cozybit.com> + * + * 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 _LBS_IF_SPI_H_ +#define _LBS_IF_SPI_H_ + +#define IPFIELD_ALIGN_OFFSET 2 +#define IF_SPI_CMD_BUF_SIZE 2400 + +/***************** Firmware *****************/ + +#define IF_SPI_FW_NAME_MAX 30 + +#define MAX_MAIN_FW_LOAD_CRC_ERR 10 + +/* Chunk size when loading the helper firmware */ +#define HELPER_FW_LOAD_CHUNK_SZ 64 + +/* Value to write to indicate end of helper firmware dnld */ +#define FIRMWARE_DNLD_OK 0x0000 + +/* Value to check once the main firmware is downloaded */ +#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 + +/***************** SPI Interface Unit *****************/ +/* Masks used in SPI register read/write operations */ +#define IF_SPI_READ_OPERATION_MASK 0x0 +#define IF_SPI_WRITE_OPERATION_MASK 0x8000 + +/* SPI register offsets. 4-byte aligned. */ +#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ +#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ +#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ +#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ + +#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ +#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ +#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ + +#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ +#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ +#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ + +#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ +#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ +#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ +#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ + +#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ +#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ + +#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ + +#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ +#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */ +#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ +#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ + +#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ + +#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ +#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ +#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ +#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ +#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ + +#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ +#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ + +/***************** IF_SPI_DEVICEID_CTRL_REG *****************/ +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) + +/***************** IF_SPI_HOST_INT_CTRL_REG *****************/ +/* Host Interrupt Control bit : Wake up */ +#define IF_SPI_HICT_WAKE_UP (1<<0) +/* Host Interrupt Control bit : WLAN ready */ +#define IF_SPI_HICT_WLAN_READY (1<<1) +/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ +/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ +/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ +/* Host Interrupt Control bit : Tx auto download */ +#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) +/* Host Interrupt Control bit : Rx auto upload */ +#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) +/* Host Interrupt Control bit : Command auto download */ +#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) +/* Host Interrupt Control bit : Command auto upload */ +#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) + +/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ +/* Card Interrupt Case bit : Tx download over */ +#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) +/* Card Interrupt Case bit : Rx upload over */ +#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) +/* Card Interrupt Case bit : Command download over */ +#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) +/* Card Interrupt Case bit : Host event */ +#define IF_SPI_CIC_HOST_EVENT (1<<3) +/* Card Interrupt Case bit : Command upload over */ +#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) +/* Card Interrupt Case bit : Power down */ +#define IF_SPI_CIC_POWER_DOWN (1<<5) + +/***************** IF_SPI_CARD_INT_STATUS_REG *****************/ +#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) +#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) +#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) +#define IF_SPI_CIS_HOST_EVENT (1<<3) +#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) +#define IF_SPI_CIS_POWER_DOWN (1<<5) + +/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ +#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) +#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) +#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) +#define IF_SPI_HICU_CARD_EVENT (1<<3) +#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) +#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) +#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) +#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) +#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) +#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) +#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_REG *****************/ +/* Host Interrupt Status bit : Tx download ready */ +#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) +/* Host Interrupt Status bit : Rx upload ready */ +#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) +/* Host Interrupt Status bit : Command download ready */ +#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) +/* Host Interrupt Status bit : Card event */ +#define IF_SPI_HIST_CARD_EVENT (1<<3) +/* Host Interrupt Status bit : Command upload ready */ +#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) +/* Host Interrupt Status bit : I/O write FIFO overflow */ +#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) +/* Host Interrupt Status bit : I/O read FIFO underflow */ +#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) +/* Host Interrupt Status bit : Data write FIFO overflow */ +#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) +/* Host Interrupt Status bit : Data read FIFO underflow */ +#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) +/* Host Interrupt Status bit : Command write FIFO overflow */ +#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) +/* Host Interrupt Status bit : Command read FIFO underflow */ +#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ +/* Host Interrupt Status Mask bit : Tx download ready */ +#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) +/* Host Interrupt Status Mask bit : Rx upload ready */ +#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) +/* Host Interrupt Status Mask bit : Command download ready */ +#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) +/* Host Interrupt Status Mask bit : Card event */ +#define IF_SPI_HISM_CARDEVENT (1<<3) +/* Host Interrupt Status Mask bit : Command upload ready */ +#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) +/* Host Interrupt Status Mask bit : I/O write FIFO overflow */ +#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) +/* Host Interrupt Status Mask bit : I/O read FIFO underflow */ +#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) +/* Host Interrupt Status Mask bit : Data write FIFO overflow */ +#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) +/* Host Interrupt Status Mask bit : Data write FIFO underflow */ +#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) +/* Host Interrupt Status Mask bit : Command write FIFO overflow */ +#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) +/* Host Interrupt Status Mask bit : Command write FIFO underflow */ +#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_SPU_BUS_MODE_REG *****************/ +/* SCK edge on which the WLAN module outputs data on MISO */ +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 + +/* In a SPU read operation, there is a delay between writing the SPU + * register name and getting back data from the WLAN module. + * This can be specified in terms of nanoseconds or in terms of dummy + * clock cycles which the master must output before receiving a response. */ +#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 +#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 + +/* Some different modes of SPI operation */ +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 + +#endif diff --git a/firmware/target/arm/tatung/tpj1022/adc-target.h b/firmware/drivers/libertas/if_spi_drv.h index 026bb58089..6c8b5c3d92 100644 --- a/firmware/target/arm/tatung/tpj1022/adc-target.h +++ b/firmware/drivers/libertas/if_spi_drv.h @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2006 by Barry Wardell + * Copyright (C) 2021 by Tomasz Moń * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,18 +18,17 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef _ADC_TARGET_H_ -#define _ADC_TARGET_H_ -#define NUM_ADC_CHANNELS 4 +#ifndef LIBERTAS_IF_SPI_DRV +#define LIBERTAS_IF_SPI_DRV -#define ADC_BATTERY 0 -#define ADC_UNKNOWN_1 1 -#define ADC_UNKNOWN_2 2 -#define ADC_SCROLLPAD 3 -#define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */ +#include <stdint.h> -/* Force a scan now */ -unsigned short adc_scan(int channel); +void libertas_spi_init(void); +void libertas_spi_reset(int high); +void libertas_spi_pd(int high); +void libertas_spi_cs(int high); +void libertas_spi_tx(const uint8_t *buf, int len); +void libertas_spi_rx(uint8_t *buf, int len); #endif diff --git a/firmware/drivers/m66591.c b/firmware/drivers/m66591.c index d09b269f33..822585d882 100644 --- a/firmware/drivers/m66591.c +++ b/firmware/drivers/m66591.c @@ -208,7 +208,7 @@ static void control_received(void) { /* acknowledge packet recieved (clear valid) */ M66591_INTSTAT_MAIN &= ~(1<<3); - usb_core_control_request(&temp); + usb_core_legacy_control_request(&temp); } /* This is a helper function, it is used to notife the stack that a transfer is @@ -869,7 +869,7 @@ int usb_drv_send(int endpoint, void* ptr, int length) /* This function begins a receive (on an OUT endpoint), it should not block * so the actual receive is done in the interrupt handler. */ -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { return mxx_queue(endpoint, ptr, length, false, false); } 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 88ff6c69f7..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,13 +560,67 @@ 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 */ + + static bool old_rdsr = false; + bool rdsr = (cache[STATUSRSSI] & STATUSRSSI_RDSR); + if (rdsr && !old_rdsr) + rds_process(&cache[RDSA]); + old_rdsr = rdsr; +#else rds_process(&cache[RDSA]); +#endif /* !(CONFIG_RDS & RDS_CFG_POLL) */ } mutex_unlock(&fmr_mutex); } -#endif /* (CONFIG_RDS & RDS_CFG_ISR) */ + +#if (CONFIG_RDS & RDS_CFG_POLL) +static struct event_queue rds_queue; +static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)]; + +enum { + Q_POWERUP, +}; + +static void NORETURN_ATTR rds_thread(void) +{ + /* start up frozen */ + int timeout = TIMEOUT_BLOCK; + struct queue_event ev; + + while (true) { + queue_wait_w_tmo(&rds_queue, &ev, timeout); + switch (ev.id) { + case Q_POWERUP: + /* power up: timeout after 1 tick, else block indefinitely */ + timeout = ev.data ? CONFIG_RDS_POLL_TICKS : TIMEOUT_BLOCK; + break; + case SYS_TIMEOUT: + /* Captures RDS data and processes it */ + si4700_rds_process(); + break; + } + } +} + +/* true after full radio power up, and false before powering down */ +void si4700_rds_powerup(bool on) +{ + queue_post(&rds_queue, Q_POWERUP, on); +} + +/* One-time RDS init at startup */ +void si4700_rds_init(void) +{ + queue_init(&rds_queue, false); + create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" + IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); +} +#endif /* !(CONFIG_RDS & RDS_CFG_POLL) */ #endif /* HAVE_RDS_CAP */ diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c index 375fd8be74..862445f2e8 100644 --- a/firmware/drivers/usb-designware.c +++ b/firmware/drivers/usb-designware.c @@ -53,13 +53,31 @@ #define COMMIT_DCACHE_RANGE(b,s) commit_dcache_range(b,s) #endif -/* On some platforms, virtual addresses must be mangled to - * get a physical address for DMA +/* USB_DW_PHYSADDR(x) converts the address of buffer x to one usable with DMA. + * For example, converting a virtual address to a physical address. + * + * USB_DW_UNCACHEDADDR(x) is used to get an uncached pointer to a buffer. + * If the platform doesn't support this, define NO_UNCACHED_ADDR instead. + * + * Define POST_DMA_FLUSH if the driver should discard DMA RX buffers after a + * transfer completes. Needed if the CPU can speculatively fetch cache lines + * in any way, eg. due to speculative execution / prefetching. */ #if CONFIG_CPU == X1000 -# define DMA_ADDR2PHYS(x) PHYSADDR(x) -#else -# define DMA_ADDR2PHYS(x) x +# define USB_DW_PHYSADDR(x) PHYSADDR(x) +# define USB_DW_UNCACHEDADDR(x) ((typeof(x))UNCACHEDADDR(x)) +# define POST_DMA_FLUSH +#elif CONFIG_CPU == AS3525v2 +# define USB_DW_PHYSADDR(x) AS3525_PHYSICAL_ADDR(x) +# define USB_DW_UNCACHEDADDR(x) AS3525_UNCACHED_ADDR(x) +#elif CONFIG_CPU == S5L8701 +# define USB_DW_PHYSADDR(x) x +# define NO_UNCACHED_ADDR /* Not known how to form uncached addresses */ +#elif CONFIG_CPU == S5L8702 +# define USB_DW_PHYSADDR(x) S5L8702_PHYSICAL_ADDR(x) +# define USB_DW_UNCACHEDADDR(x) S5L8702_UNCACHED_ADDR(x) +#elif !defined(USB_DW_ARCH_SLAVE) +# error "Must define USB_DW_PHYSADDR / USB_DW_UNCACHEDADDR!" #endif #ifndef USB_DW_TOUTCAL @@ -77,19 +95,35 @@ enum usb_dw_epdir USB_DW_EPDIR_OUT = 1, }; -union usb_ep0_buffer +enum usb_dw_ep0_state { - struct usb_ctrlrequest setup; - uint8_t raw[64]; + /* Waiting for a setup packet to arrive. This is the default state. */ + EP0_SETUP, + + /* Request wait states -- after submitting a request, we enter EP0_REQ + * (or EP0_REQ_CTRLWRITE for control writes). EP0_REQ is also used for + * the 2nd phase of a control write. EP0_REQ_CANCELLED is entered if we + * receive a setup packet before getting a response from the USB stack. */ + EP0_REQ, + EP0_REQ_CTRLWRITE, + EP0_REQ_CANCELLED, + + /* Waiting for a data phase to complete. */ + EP0_DATA_IN, + EP0_DATA_OUT, + + /* Waiting for the status phase */ + EP0_STATUS_IN, + EP0_STATUS_OUT, + + EP0_NUM_STATES }; -static union usb_ep0_buffer ep0_buffer USB_DEVBSS_ATTR; - /* Internal EP state/info */ struct usb_dw_ep { struct semaphore complete; - uint32_t* req_addr; + void* req_addr; uint32_t req_size; uint32_t* addr; uint32_t sizeleft; @@ -99,7 +133,42 @@ struct usb_dw_ep uint8_t busy; }; +/* Additional state for EP0 */ +struct usb_dw_ep0 +{ + enum usb_dw_ep0_state state; + struct usb_ctrlrequest active_req; + struct usb_ctrlrequest pending_req; +}; + +static const char* const dw_dir_str[USB_DW_NUM_DIRS] = +{ + [USB_DW_EPDIR_IN] = "IN", + [USB_DW_EPDIR_OUT] = "OUT", +}; + +static const char* const dw_state_str[EP0_NUM_STATES] = +{ + [EP0_SETUP] = "setup", + [EP0_REQ] = "req", + [EP0_REQ_CTRLWRITE] = "req_cw", + [EP0_DATA_IN] = "dat_in", + [EP0_DATA_OUT] = "dat_out", + [EP0_STATUS_IN] = "sts_in", + [EP0_STATUS_OUT] = "sts_out", +}; + +static const char* const dw_resp_str[3] = +{ + [USB_CONTROL_ACK] = "ACK", + [USB_CONTROL_RECEIVE] = "RECV", + [USB_CONTROL_STALL] = "STALL", +}; + static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS]; +static struct usb_dw_ep0 ep0; +uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32))); +uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */ static uint32_t usb_endpoints; /* available EPs mask */ @@ -117,35 +186,30 @@ static uint32_t epmis_msk; static uint32_t ep_periodic_msk; #endif -static const char *dw_dir_str[USB_DW_NUM_DIRS] = -{ - [USB_DW_EPDIR_IN] = "IN", - [USB_DW_EPDIR_OUT] = "OUT", -}; - - static struct usb_dw_ep *usb_dw_get_ep(int epnum, enum usb_dw_epdir epdir) { return &usb_dw_ep_list[epnum][epdir]; } -static int usb_dw_maxpktsize(int epnum, enum usb_dw_epdir epdir) +static uint32_t usb_dw_maxpktsize(int epnum, enum usb_dw_epdir epdir) { return epnum ? DWC_EPCTL(epnum, epdir) & 0x3ff : 64; } -static int usb_dw_maxxfersize(int epnum, enum usb_dw_epdir epdir) +static uint32_t usb_dw_maxxfersize(int epnum, enum usb_dw_epdir epdir) { - return epnum ? ALIGN_DOWN_P2(MIN(hw_maxbytes, - hw_maxpackets*usb_dw_maxpktsize(epnum, epdir)), CACHEALIGN_BITS) : 64; + /* EP0 can only transfer one packet at a time. */ + if(epnum == 0) + return 64; + + uint32_t maxpktsize = usb_dw_maxpktsize(epnum, epdir); + return CACHEALIGN_DOWN(MIN(hw_maxbytes, hw_maxpackets * maxpktsize)); } /* Calculate number of packets (if size == 0 an empty packet will be sent) */ -static int usb_dw_calc_packets(uint32_t size, uint32_t maxpktsize) +static uint32_t usb_dw_calc_packets(uint32_t size, uint32_t maxpktsize) { - int packets = (size + maxpktsize - 1) / maxpktsize; - if (!packets) packets = 1; - return packets; + return MAX(1, (size + maxpktsize - 1) / maxpktsize); } static int usb_dw_get_stall(int epnum, enum usb_dw_epdir epdir) @@ -184,8 +248,8 @@ static unsigned usb_dw_bytes_in_txfifo(int epnum, uint32_t *sentbytes) uint32_t dieptsiz = DWC_DIEPTSIZ(epnum); uint32_t packetsleft = (dieptsiz >> 19) & 0x3ff; if (!packetsleft) return 0; - int maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN); - int packets = usb_dw_calc_packets(size, maxpktsize); + uint32_t maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN); + uint32_t packets = usb_dw_calc_packets(size, maxpktsize); uint32_t bytesleft = dieptsiz & 0x7ffff; uint32_t bytespushed = size - bytesleft; uint32_t bytespulled = (packets - packetsleft) * maxpktsize; @@ -200,7 +264,7 @@ static unsigned usb_dw_bytes_in_txfifo(int epnum, uint32_t *sentbytes) static void usb_dw_handle_rxfifo(void) { uint32_t rxsts = DWC_GRXSTSP; - int pktsts = (rxsts >> 17) & 0xf; + uint32_t pktsts = (rxsts >> 17) & 0xf; switch (pktsts) { @@ -208,19 +272,31 @@ static void usb_dw_handle_rxfifo(void) case PKTSTS_SETUPRX: { int ep = rxsts & 0xf; - int words = (((rxsts >> 4) & 0x7ff) + 3) >> 2; - struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_OUT); - if (dw_ep->busy) + uint32_t words = (((rxsts >> 4) & 0x7ff) + 3) >> 2; + + /* Annoyingly, we need to special-case EP0. */ + if(ep == 0) { + uint32_t* addr = (uint32_t*)ep0_buffer; while (words--) - *dw_ep->addr++ = DWC_DFIFO(0); + *addr++ = DWC_DFIFO(0); } else { - /* Discard data */ - while (words--) - (void) DWC_DFIFO(0); + struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_OUT); + if (dw_ep->busy) + { + while (words--) + *dw_ep->addr++ = DWC_DFIFO(0); + } + else + { + /* Discard data */ + while (words--) + (void) DWC_DFIFO(0); + } } + break; } case PKTSTS_OUTDONE: @@ -292,7 +368,7 @@ static void usb_dw_handle_dtxfifo(int epnum) { /* We push whole packets to read consistent info on DIEPTSIZ (i.e. when FIFO size is not maxpktsize multiplo). */ - int maxpktwords = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN) >> 2; + uint32_t maxpktwords = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN) >> 2; words = (fifospace / maxpktwords) * maxpktwords; } @@ -458,7 +534,7 @@ static void usb_dw_nptx_unqueue(int epnum) dw_ep->addr -= (bytesinfifo + 3) >> 2; #else (void) bytesinfifo; - DWC_DIEPDMA(ep) = DMA_ADDR2PHYS((uint32_t)(dw_ep->addr) + sentbytes); + DWC_DIEPDMA(ep) = USB_DW_PHYSADDR((uint32_t)(dw_ep->addr) + sentbytes); #endif DWC_DIEPTSIZ(ep) = PKTCNT(packetsleft) | (dw_ep->size - sentbytes); @@ -664,57 +740,72 @@ static void usb_dw_reset_endpoints(void) #endif } -static void usb_dw_start_xfer(int epnum, - enum usb_dw_epdir epdir, const void* buf, int size) +static void usb_dw_epstart(int epnum, enum usb_dw_epdir epdir, + void* buf, uint32_t size) { if ((uint32_t)buf & ((epdir == USB_DW_EPDIR_IN) ? 3 : CACHEALIGN_SIZE-1)) logf("%s: %s%d %p unaligned", __func__, dw_dir_str[epdir], epnum, buf); struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); + uint32_t xfersize = MIN(size, usb_dw_maxxfersize(epnum, epdir)); - dw_ep->busy = true; - dw_ep->status = -1; + dw_ep->addr = (uint32_t*)buf; + dw_ep->size = xfersize; dw_ep->sizeleft = size; - size = MIN(size, usb_dw_maxxfersize(epnum, epdir)); - dw_ep->size = size; + dw_ep->status = -1; + dw_ep->busy = true; - int packets = usb_dw_calc_packets(size, usb_dw_maxpktsize(epnum, epdir)); - uint32_t eptsiz = PKTCNT(packets) | size; - uint32_t nak; + if (epnum == 0 && epdir == USB_DW_EPDIR_OUT) + { + /* FIXME: there's an extremely rare race condition here. + * + * 1. Host sends a control write. + * 2. We process the request. + * 3. (time passes) + * 4. This function is called via USB_CONTROL_RECEIVE response. + * 5. Right before we set CNAK, host sends another control write. + * + * So we may unintentionally receive data from the second request. + * It's possible to detect this when we see a setup packet because + * EP0 OUT will be busy. In principle it should even be possible to + * handle the 2nd request correctly. Currently we don't attempt to + * detect or recover from this error. + */ + DWC_DOEPCTL(0) |= CNAK; + return; + } - /* Set up data source */ - dw_ep->addr = (uint32_t*)buf; -#ifndef USB_DW_ARCH_SLAVE - DWC_EPDMA(epnum, epdir) = DMA_ADDR2PHYS((uint32_t)buf); -#endif + uint32_t maxpktsize = usb_dw_maxpktsize(epnum, epdir); + uint32_t packets = usb_dw_calc_packets(xfersize, maxpktsize); + uint32_t eptsiz = PKTCNT(packets) | xfersize; + uint32_t nak = CNAK; if (epdir == USB_DW_EPDIR_IN) { #ifndef USB_DW_ARCH_SLAVE - COMMIT_DCACHE_RANGE(buf, size); + COMMIT_DCACHE_RANGE(buf, xfersize); #endif #ifdef USB_DW_SHARED_FIFO eptsiz |= MCCNT((ep_periodic_msk >> epnum) & 1); #endif - nak = CNAK; + } else { #ifndef USB_DW_ARCH_SLAVE - DISCARD_DCACHE_RANGE(buf, size); + DISCARD_DCACHE_RANGE(buf, xfersize); #endif - eptsiz |= STUPCNT(!epnum); - nak = epnum ? CNAK : SNAK; } +#ifndef USB_DW_ARCH_SLAVE + DWC_EPDMA(epnum, epdir) = USB_DW_PHYSADDR((uint32_t)buf); +#endif DWC_EPTSIZ(epnum, epdir) = eptsiz; - - /* Enable the endpoint */ DWC_EPCTL(epnum, epdir) |= EPENA | nak; #ifdef USB_DW_ARCH_SLAVE /* Enable interrupts to start pushing data into the FIFO */ - if ((epdir == USB_DW_EPDIR_IN) && size) + if ((epdir == USB_DW_EPDIR_IN) && dw_ep->size > 0) #ifdef USB_DW_SHARED_FIFO DWC_GINTMSK |= ((ep_periodic_msk & (1 << epnum)) ? PTXFE : NPTXFE); #else @@ -723,25 +814,31 @@ static void usb_dw_start_xfer(int epnum, #endif } -static void usb_dw_ep0_wait_setup(void) -{ - usb_dw_start_xfer(0, USB_DW_EPDIR_OUT, ep0_buffer.raw, 64); -} - -static void usb_dw_handle_setup_received(void) +static void usb_dw_transfer(int epnum, enum usb_dw_epdir epdir, + void* buf, uint32_t size) { - static struct usb_ctrlrequest usb_ctrlsetup; - - usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN); + struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); - memcpy(&usb_ctrlsetup, ep0_buffer.raw, sizeof(usb_ctrlsetup)); + if (!dw_ep->active) + logf("%s: %s%d inactive", __func__, dw_dir_str[epdir], epnum); + if (dw_ep->busy) + logf("%s: %s%d busy", __func__, dw_dir_str[epdir], epnum); - if (((usb_ctrlsetup.bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) - && ((usb_ctrlsetup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) - && (usb_ctrlsetup.bRequest == USB_REQ_SET_ADDRESS)) - usb_dw_set_address(usb_ctrlsetup.wValue); + dw_ep->req_addr = buf; + dw_ep->req_size = size; + usb_dw_epstart(epnum, epdir, buf, size); +} - usb_core_control_request(&usb_ctrlsetup); +static void usb_dw_ep0_recv(void) +{ +#ifndef USB_DW_ARCH_SLAVE +#ifdef NO_UNCACHED_ADDR + DISCARD_DCACHE_RANGE(&_ep0_buffer[0], 64); +#endif + DWC_DOEPDMA(0) = USB_DW_PHYSADDR((uint32_t)&_ep0_buffer[0]); +#endif + DWC_DOEPTSIZ(0) = STUPCNT(1) | PKTCNT(1) | 64; + DWC_DOEPCTL(0) |= EPENA | SNAK; } static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir) @@ -755,60 +852,239 @@ static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir) } } +static void usb_dw_control_received(struct usb_ctrlrequest* req) +{ + logf("%s(%p) state=%s", __func__, req, dw_state_str[ep0.state]); + logf(" bRequestType=%02x bRequest=%02x", req->bRequestType, req->bRequest); + logf(" wValue=%04x wIndex=%u wLength=%u", req->wValue, req->wIndex, req->wLength); + + switch(ep0.state) { + case EP0_REQ: + case EP0_REQ_CTRLWRITE: + case EP0_REQ_CANCELLED: + /* Save the request for later */ + memcpy(&ep0.pending_req, req, sizeof(*req)); + ep0.state = EP0_REQ_CANCELLED; + break; + + case EP0_DATA_IN: + case EP0_STATUS_IN: + case EP0_DATA_OUT: + case EP0_STATUS_OUT: + usb_core_control_complete(-1); + /* fallthrough */ + + case EP0_SETUP: + /* Save the request */ + memcpy(&ep0.active_req, req, sizeof(*req)); + req = &ep0.active_req; + + /* Check for a SET ADDRESS request, which we must handle here */ + if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE && + (req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD && + (req->bRequest == USB_REQ_SET_ADDRESS)) + usb_dw_set_address(req->wValue); + + /* Check for control writes */ + if (req->wLength > 0 && !(req->bRequestType & USB_DIR_IN)) + ep0.state = EP0_REQ_CTRLWRITE; + else + ep0.state = EP0_REQ; + + usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN); + usb_core_control_request(req, NULL); + break; + + default: + panicf("%s: bad state=%s", __func__, ep0.state >= EP0_NUM_STATES ? "unk" : dw_state_str[ep0.state]); + } +} + +/* note: must be called with IRQs disabled */ +static void usb_dw_control_response(enum usb_control_response resp, + void* data, int length) +{ + struct usb_ctrlrequest* req = &ep0.active_req; + + switch(ep0.state) { + case EP0_REQ: + case EP0_REQ_CTRLWRITE: + switch(resp) { + case USB_CONTROL_ACK: + if(req->wLength > 0 && (req->bRequestType & USB_DIR_IN)) + ep0.state = EP0_DATA_IN; /* control read */ + else + ep0.state = EP0_STATUS_IN; /* non-data or write */ + + usb_dw_transfer(0, USB_DW_EPDIR_IN, data, length); + break; + + case USB_CONTROL_RECEIVE: + if(ep0.state != EP0_REQ_CTRLWRITE) + panicf("%s: bad response", __func__); + + ep0.state = EP0_DATA_OUT; + usb_dw_transfer(0, USB_DW_EPDIR_OUT, data, length); + break; + + case USB_CONTROL_STALL: + if(ep0.state == EP0_REQ_CTRLWRITE) + usb_dw_set_stall(0, USB_DW_EPDIR_OUT, 1); + else + usb_dw_set_stall(0, USB_DW_EPDIR_IN, 1); + + ep0.state = EP0_SETUP; + break; + } + break; + + case EP0_REQ_CANCELLED: + /* Terminate the old request */ + usb_core_control_complete(-3); + + /* Submit the pending request */ + ep0.state = EP0_SETUP; + usb_dw_control_received(&ep0.pending_req); + break; + + default: + panicf("%s: bad state=%s", __func__, dw_state_str[ep0.state]); + } +} + +static void usb_dw_ep0_xfer_complete(enum usb_dw_epdir epdir, + int status, int transferred) +{ + struct usb_dw_ep* dw_ep = usb_dw_get_ep(0, epdir); + + switch((ep0.state << 1) | epdir) + { + case (EP0_DATA_IN << 1) | USB_DW_EPDIR_IN: + ep0.state = EP0_STATUS_OUT; + usb_dw_transfer(0, USB_DW_EPDIR_OUT, NULL, 0); + break; + + case (EP0_DATA_OUT << 1) | USB_DW_EPDIR_OUT: + ep0.state = EP0_REQ; + usb_core_control_request(&ep0.active_req, dw_ep->req_addr); + break; + + case (EP0_STATUS_IN << 1) | USB_DW_EPDIR_IN: + case (EP0_STATUS_OUT << 1) | USB_DW_EPDIR_OUT: + if(status != 0 || transferred != 0) + usb_core_control_complete(-2); + else + usb_core_control_complete(0); + + ep0.state = EP0_SETUP; + break; + + default: + panicf("%s: state=%s dir=%s", __func__, + dw_state_str[ep0.state], dw_dir_str[epdir]); + } +} + static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir) { struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir); + bool is_ep0out = (epnum == 0 && epdir == USB_DW_EPDIR_OUT); if (!dw_ep->busy) + { + if(is_ep0out) + usb_dw_ep0_recv(); return; + } - uint32_t bytesleft = DWC_EPTSIZ(epnum, epdir) & 0x7ffff; + uint32_t bytes_left = DWC_EPTSIZ(epnum, epdir) & 0x7ffff; + uint32_t transferred = (is_ep0out ? 64 : dw_ep->size) - bytes_left; - if (!epnum && (epdir == USB_DW_EPDIR_OUT)) /* OUT0 */ + if(transferred > dw_ep->sizeleft) { - int recvbytes = 64 - bytesleft; - dw_ep->sizeleft = dw_ep->req_size - recvbytes; - if (dw_ep->req_addr) - memcpy(dw_ep->req_addr, ep0_buffer.raw, dw_ep->req_size); + /* Host sent more data than expected. + * Shouldn't happen for IN endpoints. */ + dw_ep->status = -2; + goto complete; } - else + + if(is_ep0out) + { +#if defined(NO_UNCACHED_ADDR) && defined(POST_DMA_FLUSH) + DISCARD_DCACHE_RANGE(ep0_buffer, 64); +#endif + memcpy(dw_ep->addr, ep0_buffer, transferred); + usb_dw_ep0_recv(); + } + + dw_ep->sizeleft -= transferred; + + /* Start a new transfer if there is still more to go */ + if(bytes_left == 0 && dw_ep->sizeleft > 0) { - dw_ep->sizeleft -= (dw_ep->size - bytesleft); - if (!bytesleft && dw_ep->sizeleft) - { #ifndef USB_DW_ARCH_SLAVE - dw_ep->addr += (dw_ep->size >> 2); /* words */ + dw_ep->addr += (dw_ep->size >> 2); /* offset in words */ #endif - usb_dw_start_xfer(epnum, epdir, dw_ep->addr, dw_ep->sizeleft); - return; - } + usb_dw_epstart(epnum, epdir, dw_ep->addr, dw_ep->sizeleft); + return; + } - if (epdir == USB_DW_EPDIR_IN) - { - /* SNAK the disabled EP, otherwise IN tokens for this - EP could raise unwanted EPMIS interrupts. Useful for - usbserial when there is no data to send. */ - DWC_DIEPCTL(epnum) |= SNAK; + if(epdir == USB_DW_EPDIR_IN) + { + /* SNAK the disabled EP, otherwise IN tokens for this + EP could raise unwanted EPMIS interrupts. Useful for + usbserial when there is no data to send. */ + DWC_DIEPCTL(epnum) |= SNAK; #ifdef USB_DW_SHARED_FIFO - /* See usb-s5l8701.c */ - if (usb_dw_config.use_ptxfifo_as_plain_buffer) - { - int dtxfnum = GET_DTXFNUM(epnum); - if (dtxfnum) - usb_dw_flush_fifo(TXFFLSH, dtxfnum); - } -#endif + /* See usb-s5l8701.c */ + if (usb_dw_config.use_ptxfifo_as_plain_buffer) + { + int dtxfnum = GET_DTXFNUM(epnum); + if (dtxfnum) + usb_dw_flush_fifo(TXFFLSH, dtxfnum); } +#endif + } + else + { +#if !defined(USB_DW_ARCH_SLAVE) && defined(POST_DMA_FLUSH) + /* On EP0 OUT we do not DMA into the request buffer, + * so do not discard the cache in this case. */ + if(!is_ep0out) + DISCARD_DCACHE_RANGE(dw_ep->req_addr, dw_ep->req_size); +#endif } - dw_ep->busy = false; dw_ep->status = 0; + + complete: + dw_ep->busy = false; semaphore_release(&dw_ep->complete); - int transfered = dw_ep->req_size - dw_ep->sizeleft; - usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ? - USB_DIR_OUT : USB_DIR_IN, dw_ep->status, transfered); + int total_bytes = dw_ep->req_size - dw_ep->sizeleft; + if (epnum == 0) + { + usb_dw_ep0_xfer_complete(epdir, dw_ep->status, total_bytes); + } + else + { + usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ? + USB_DIR_OUT : USB_DIR_IN, dw_ep->status, total_bytes); + } +} + +static void usb_dw_handle_setup_received(void) +{ +#if defined(NO_UNCACHED_ADDR) && defined(POST_DMA_FLUSH) + DISCARD_DCACHE_RANGE(ep0_buffer, 64); +#endif + struct usb_ctrlrequest req; + memcpy(&req, ep0_buffer, sizeof(struct usb_ctrlrequest)); + + usb_dw_ep0_recv(); + + usb_dw_control_received(&req); } #ifdef USB_DW_SHARED_FIFO @@ -822,7 +1098,7 @@ static int usb_dw_get_epmis(void) /* Get the EP on the top of the queue, 0 < idx < number of available IN endpoints */ - int idx = (gnptxsts >> 27) & 0xf; + uint32_t idx = (gnptxsts >> 27) & 0xf; for (epmis = 0; epmis < USB_NUM_ENDPOINTS; epmis++) if ((usb_endpoints & (1 << epmis)) && !idx--) break; @@ -960,6 +1236,7 @@ static void usb_dw_irq(void) if (daint & (1 << (ep + 16))) { uint32_t epints = DWC_DOEPINT(ep); + DWC_DOEPINT(ep) = epints; if (!ep) { @@ -967,17 +1244,31 @@ static void usb_dw_irq(void) { usb_dw_handle_setup_received(); } - else if (epints & XFRC) + + if (epints & XFRC) { - usb_dw_handle_xfer_complete(0, USB_DW_EPDIR_OUT); + if(epints & STATUSRECVD) + { + /* At the end of a control write's data phase, the + * controller writes a spurious OUTDONE token to the + * FIFO and raises StatusRecvd | XferCompl. + * + * We do not need or want this -- we've already handled + * the data phase by this point -- but EP0 is stoppped + * as a side effect of XferCompl, so we need to restart + * it to keep receiving packets. */ + usb_dw_ep0_recv(); + } + else if(!(epints & SETUPRECVD)) + { + /* Only call this for normal data packets. Setup + * packets use the STUP interrupt handler instead. */ + usb_dw_handle_xfer_complete(0, USB_DW_EPDIR_OUT); + } } - usb_dw_ep0_wait_setup(); - /* Clear interrupt after the current EP0 packet is handled */ - DWC_DOEPINT(0) = epints; } else { - DWC_DOEPINT(ep) = epints; if (epints & XFRC) { usb_dw_handle_xfer_complete(ep, USB_DW_EPDIR_OUT); @@ -991,14 +1282,14 @@ static void usb_dw_irq(void) DWC_GINTSTS = USBRST; usb_dw_set_address(0); usb_dw_reset_endpoints(); - usb_dw_ep0_wait_setup(); usb_core_bus_reset(); } if (DWC_GINTSTS & ENUMDNE) { DWC_GINTSTS = ENUMDNE; - /* Nothing to do? */ + ep0.state = EP0_SETUP; + usb_dw_ep0_recv(); } } @@ -1083,9 +1374,17 @@ static void usb_dw_init(void) if (!initialized) { +#if !defined(USB_DW_ARCH_SLAVE) && !defined(NO_UNCACHED_ADDR) + ep0_buffer = USB_DW_UNCACHEDADDR(&_ep0_buffer[0]); +#else + /* DMA is not used so we can operate on cached addresses */ + ep0_buffer = &_ep0_buffer[0]; +#endif + for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++) for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++) semaphore_init(&usb_dw_get_ep(ep, dir)->complete, 1, 0); + initialized = true; } @@ -1333,39 +1632,18 @@ void usb_drv_release_endpoint(int endpoint) usb_dw_target_enable_irq(); } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { - int epnum = EP_NUM(endpoint); - struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_OUT); - usb_dw_target_disable_irq(); - if (dw_ep->active) - { - dw_ep->req_addr = ptr; - dw_ep->req_size = length; - /* OUT0 is always launched waiting for SETUP packet, - it is CNAKed to receive app data */ - if (epnum == 0) - DWC_DOEPCTL(0) |= CNAK; - else - usb_dw_start_xfer(epnum, USB_DW_EPDIR_OUT, ptr, length); - } + usb_dw_transfer(EP_NUM(endpoint), USB_DW_EPDIR_OUT, ptr, length); usb_dw_target_enable_irq(); return 0; } int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) { - int epnum = EP_NUM(endpoint); - struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN); - usb_dw_target_disable_irq(); - if (dw_ep->active) - { - dw_ep->req_addr = ptr; - dw_ep->req_size = length; - usb_dw_start_xfer(epnum, USB_DW_EPDIR_IN, ptr, length); - } + usb_dw_transfer(EP_NUM(endpoint), USB_DW_EPDIR_IN, ptr, length); usb_dw_target_enable_irq(); return 0; } @@ -1388,3 +1666,11 @@ int usb_drv_send(int endpoint, void *ptr, int length) return dw_ep->status; } + +void usb_drv_control_response(enum usb_control_response resp, + void* data, int length) +{ + usb_dw_target_disable_irq(); + usb_dw_control_response(resp, data, length); + usb_dw_target_enable_irq(); +} 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 ceafc6ebf7..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) @@ -210,12 +212,17 @@ struct sound_settings_info #include "df1704.h" #elif defined(HAVE_PCM1792_CODEC) #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) #include "cs4398.h" #elif defined(HAVE_ES9018) #include "es9018.h" +#elif defined(HAVE_ES9218) +#include "es9218.h" #elif (CONFIG_PLATFORM & (PLATFORM_ANDROID | PLATFORM_MAEMO \ | PLATFORM_PANDORA | PLATFORM_SDL)) #include "hosted_codec.h" @@ -233,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 */ @@ -594,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 @@ -688,4 +706,9 @@ AUDIOHW_SETTING(BALANCE, "%", 0, 1, -100, 100, 0) AUDIOHW_SETTING(CHANNELS, "", 0, 1, 0, 5, 0) AUDIOHW_SETTING(STEREO_WIDTH, "%", 0, 5, 0, 250, 100) +/* if not otherwise defined, set to 16 */ +#if !defined(PCM_NATIVE_BITDEPTH) +# define PCM_NATIVE_BITDEPTH 16 +#endif + #endif /* _AUDIOHW_H_ */ 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/button.h b/firmware/export/button.h index 1745947230..3abad2d4f0 100644 --- a/firmware/export/button.h +++ b/firmware/export/button.h @@ -107,7 +107,7 @@ int button_apply_acceleration(const unsigned int data); BUTTON_MULTIMEDIA_FFWD) #ifdef HAVE_TOUCHSCREEN -int touchscreen_last_touch(void); +long touchscreen_last_touch(void); #if (!defined(BUTTON_TOPLEFT) || !defined(BUTTON_TOPMIDDLE) \ || !defined(BUTTON_TOPRIGHT) || !defined(BUTTON_MIDLEFT) \ diff --git a/firmware/export/config.h b/firmware/export/config.h index 623ef8b4ff..26ed7395ff 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -105,7 +105,6 @@ #define IRIVER_H10_PAD 12 #define SANSA_E200_PAD 13 #define SANSA_C200_PAD 14 -#define TATUNG_TPJ1022_PAD 15 #define MROBE100_PAD 17 #define MROBE500_PAD 18 #define GIGABEAT_S_PAD 19 @@ -145,7 +144,6 @@ #define MA_PAD 56 #define SONY_NWZ_PAD 57 #define CREATIVE_ZEN_PAD 58 -#define SAMSUNG_YPZ5_PAD 59 #define IHIFI_PAD 60 #define SAMSUNG_YPR1_PAD 61 #define SAMSUNG_YH92X_PAD 62 @@ -160,6 +158,7 @@ #define FIIO_M3K_LINUX_PAD 71 #define EROSQ_PAD 72 #define FIIO_M3K_PAD 73 +#define SHANLING_Q1_PAD 74 /* CONFIG_REMOTE_KEYPAD */ #define H100_REMOTE 1 @@ -209,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 */ @@ -224,7 +225,6 @@ #define LCD_GIGABEAT 12 #define LCD_H10_20GB 13 /* as used by iriver H10 20Gb */ #define LCD_H10_5GB 14 /* as used by iriver H10 5Gb */ -#define LCD_TPJ1022 15 /* as used by Tatung Elio TPJ-1022 */ #define LCD_C200 17 /* as used by Sandisk Sansa c200 */ #define LCD_MROBE500 18 /* as used by Olympus M:Robe 500i */ #define LCD_MROBE100 19 /* as used by Olympus M:Robe 100 */ @@ -263,7 +263,6 @@ #define LCD_CREATIVEZENMOZAIC 56 /* as used by the Creative ZEN Mozaic (FGD0801) */ #define LCD_ILI9342C 57 /* another type of lcd used by HiFi E.T MA9/MA8 */ #define LCD_CREATIVEZENV 58 /* as used by the Creative Zen V (Plus) */ -#define LCD_SAMSUNGYPZ5 59 /* as used by Samsung YP-Z5 */ #define LCD_IHIFI 60 /* as used by IHIFI 760/960 */ #define LCD_CREATIVEZENXFISTYLE 61 /* as used by Creative Zen X-Fi Style */ #define LCD_SAMSUNGYPR1 62 /* as used by Samsung YP-R1 */ @@ -274,6 +273,8 @@ #define LCD_IHIFI770C 67 /* as used by IHIFI 770C */ #define LCD_IHIFI800 68 /* as used by IHIFI 800 */ #define LCD_FIIOM3K 69 /* as used by the FiiO M3K */ +#define LCD_SHANLING_Q1 70 /* as used by the Shanling Q1 */ +#define LCD_EROSQ 71 /* as used by the ErosQ (native) */ /* LCD_PIXELFORMAT */ #define HORIZONTAL_PACKING 1 @@ -348,6 +349,7 @@ Lyre prototype 1 */ #define RTC_STM41T62 21 /* ST M41T62 */ #define RTC_JZ4760 22 /* Ingenic Jz4760 */ #define RTC_X1000 23 /* Ingenic X1000 */ +#define RTC_CONNECT 24 /* Sansa Connect AVR */ /* USB On-the-go */ #define USBOTG_M66591 6591 /* M:Robe 500 */ @@ -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" @@ -423,8 +429,6 @@ Lyre prototype 1 */ #include "config/sansae200.h" #elif defined(SANSA_C200) #include "config/sansac200.h" -#elif defined(TATUNG_TPJ1022) -#include "config/tatungtpj1022.h" #elif defined(MROBE_100) #include "config/mrobe100.h" #elif defined(MROBE_500) @@ -539,8 +543,6 @@ Lyre prototype 1 */ #include "config/sonynwze370.h" #elif defined(SONY_NWZE360) #include "config/sonynwze360.h" -#elif defined(SAMSUNG_YPZ5) -#include "config/samsungypz5.h" #elif defined(IHIFI760) #include "config/ihifi760.h" #elif defined(IHIFI770) @@ -591,10 +593,23 @@ Lyre prototype 1 */ #include "config/fiiom3k.h" #elif defined(EROS_Q) #include "config/aigoerosq.h" +#elif defined(SHANLING_Q1) +#include "config/shanlingq1.h" +#elif defined(EROS_QN) +#include "config/erosqnative.h" #else //#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 @@ -709,15 +731,24 @@ Lyre prototype 1 */ #define BATTERY_CAPACITY_DEFAULT 0 #endif +#ifndef BATTERY_CAPACITY_MIN +#define BATTERY_CAPACITY_MIN BATTERY_CAPACITY_DEFAULT +#endif + +#ifndef BATTERY_CAPACITY_MAX +#define BATTERY_CAPACITY_MAX BATTERY_CAPACITY_DEFAULT +#endif + #ifndef BATTERY_CAPACITY_INC #define BATTERY_CAPACITY_INC 0 #endif #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 */ #ifndef CONFIG_RDS #define CONFIG_RDS RDS_CFG_PROCESS /* thread processing+raw processing */ #endif /* CONFIG_RDS */ @@ -758,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 @@ -855,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 @@ -906,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 */ @@ -955,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 @@ -970,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="" @@ -1043,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 @@ -1055,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 @@ -1158,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 @@ -1172,20 +1240,26 @@ Lyre prototype 1 */ /* Define the implemented USB transport classes */ #if CONFIG_USBOTG == USBOTG_ISP1583 #define USB_HAS_BULK -#elif (CONFIG_USBOTG == USBOTG_ARC) || \ +#define USB_LEGACY_CONTROL_API +#elif (CONFIG_USBOTG == USBOTG_DESIGNWARE) +#define USB_HAS_BULK +#define USB_HAS_INTERRUPT +#elif (CONFIG_USBOTG == USBOTG_ARC) || \ (CONFIG_USBOTG == USBOTG_JZ4740) || \ (CONFIG_USBOTG == USBOTG_JZ4760) || \ (CONFIG_USBOTG == USBOTG_M66591) || \ - (CONFIG_USBOTG == USBOTG_DESIGNWARE) || \ (CONFIG_USBOTG == USBOTG_AS3525) || \ (CONFIG_USBOTG == USBOTG_RK27XX) || \ (CONFIG_USBOTG == USBOTG_TNETV105) #define USB_HAS_BULK #define USB_HAS_INTERRUPT +#define USB_LEGACY_CONTROL_API #elif defined(CPU_TCC780X) #define USB_HAS_BULK +#define USB_LEGACY_CONTROL_API #elif CONFIG_USBOTG == USBOTG_S3C6400X #define USB_HAS_BULK +#define USB_LEGACY_CONTROL_API //#define USB_HAS_INTERRUPT -- seems to be broken #endif /* CONFIG_USBOTG */ @@ -1261,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/android.h b/firmware/export/config/android.h index 7d3355ef9d..ac0e2141a1 100644 --- a/firmware/export/config/android.h +++ b/firmware/export/config/android.h @@ -51,9 +51,6 @@ #define HAVE_TOUCHSCREEN #define HAVE_BUTTON_DATA -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* define this if you have a real-time clock */ #define CONFIG_RTC APPLICATION diff --git a/firmware/export/config/cowond2.h b/firmware/export/config/cowond2.h index 81a2a85018..51b5495680 100644 --- a/firmware/export/config/cowond2.h +++ b/firmware/export/config/cowond2.h @@ -90,9 +90,6 @@ /* The D2 has either a PCF50606 or PCF50635, RTC_D2 handles both */ #define CONFIG_RTC RTC_D2 -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* Define this if you have a software controlled poweroff */ #define HAVE_SW_POWEROFF diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h new file mode 100644 index 0000000000..4f87282f1f --- /dev/null +++ b/firmware/export/config/erosqnative.h @@ -0,0 +1,146 @@ +/* + * This config file is for the AIGO EROS Q / EROS K (and its clones) + */ + +/* For Rolo and boot loader */ +#define MODEL_NAME "AIGO Eros Q Native" +#define MODEL_NUMBER 116 +#define BOOTFILE_EXT "erosq" +#define BOOTFILE "rockbox." BOOTFILE_EXT +#define BOOTDIR "/.rockbox" +#define FIRMWARE_OFFSET_FILE_CRC 0 +#define FIRMWARE_OFFSET_FILE_DATA 8 + +/* CPU defines */ +#define CONFIG_CPU X1000 +#define X1000_EXCLK_FREQ 24000000 +#define CPU_FREQ 1008000000 +#define HAVE_FPU + +#ifndef SIMULATOR +#define TIMER_FREQ X1000_EXCLK_FREQ +#endif + +/* kernel defines */ +#define INCLUDE_TIMEOUT_API +#define HAVE_SEMAPHORE_OBJECTS + +/* drivers */ +#define HAVE_I2C_ASYNC + +/* Buffers for plugsins and codecs */ +#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */ +#define CODEC_SIZE 0x100000 /* 1 MiB */ + +/* LCD defines */ +#define CONFIG_LCD LCD_EROSQ +#define LCD_WIDTH 320 +#define LCD_HEIGHT 240 +#define LCD_DEPTH 16 /* Future Improvement: 18 or 24 bpp if display supports it */ +#define LCD_PIXELFORMAT RGB565 +/* sqrt(240^2 + 320^2) / 2.0 = 200 */ +#define LCD_DPI 200 +#define HAVE_LCD_COLOR +#define HAVE_LCD_BITMAP +#define HAVE_LCD_ENABLE +#define HAVE_LCD_SHUTDOWN +#define LCD_X1000_FASTSLEEP +//#define LCD_X1000_DMA_WAITFORFRAME + +#define HAVE_BACKLIGHT +#define HAVE_BACKLIGHT_BRIGHTNESS +#define MIN_BRIGHTNESS_SETTING 1 +#define MAX_BRIGHTNESS_SETTING 255 +#define BRIGHTNESS_STEP 5 +#define DEFAULT_BRIGHTNESS_SETTING 70 +#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING + +/* RTC settings */ +#define CONFIG_RTC RTC_X1000 + +/* Codec / audio hardware defines */ +#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192 +#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 + +/* Button defines */ +#define CONFIG_KEYPAD EROSQ_PAD +#define HAVE_SCROLLWHEEL +#define HAVE_HEADPHONE_DETECTION +#define HAVE_LINEOUT_DETECTION + +/* Storage defines */ +#define CONFIG_STORAGE STORAGE_SD +#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/*|CURRENT_MEASURE*/) +#define CONFIG_CHARGING CHARGING_MONITOR +#define HAVE_SW_POWEROFF + +#ifndef SIMULATOR +#define HAVE_AXP_PMU 192 +#define HAVE_POWEROFF_WHILE_CHARGING +#endif + +/* Battery */ +#define BATTERY_TYPES_COUNT 1 +#define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */ +#define BATTERY_CAPACITY_MIN 1300 /* min. capacity selectable */ +#define BATTERY_CAPACITY_MAX 1300 /* max. capacity selectable */ +#define BATTERY_CAPACITY_INC 0 /* capacity increment */ + +#define CURRENT_NORMAL 100 // 1.7mA * 60s +#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 +#define USB_DW_TURNAROUND 5 +#define HAVE_USBSTACK +#define USB_VENDOR_ID 0xc502 +#define USB_PRODUCT_ID 0x0023 +#define USB_DEVBSS_ATTR __attribute__((aligned(32))) +#define HAVE_USB_POWER +#define HAVE_USB_CHARGING_ENABLE +#define HAVE_USB_CHARGING_IN_THREAD +#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE +#define HAVE_BOOTLOADER_USB_MODE +/* This appears to improve transfer performance (the default is 64 KiB). + * Going any higher doesn't help but we're still slower than the OF. */ +#define USB_READ_BUFFER_SIZE (128 * 1024) +#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 +#define HAVE_ALBUMART +#define HAVE_BMP_SCALING +#define HAVE_JPEG +#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 849aa9c0a6..86a9b05402 100644 --- a/firmware/export/config/fiiom3k.h +++ b/firmware/export/config/fiiom3k.h @@ -24,6 +24,7 @@ #define HAVE_I2C_ASYNC #define HAVE_FT6x06 #define FT6x06_SWAP_AXES +#define FT6x06_NUM_POINTS 1 /* Buffer for plugins and codecs. */ #define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */ @@ -54,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 @@ -78,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 @@ -87,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 @@ -103,10 +110,13 @@ #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 -#define USB_DW_ARCH_SLAVE #define USB_DW_TURNAROUND 5 #define HAVE_USBSTACK #define USB_VENDOR_ID 0x2972 @@ -114,7 +124,18 @@ #define USB_DEVBSS_ATTR __attribute__((aligned(32))) #define HAVE_USB_POWER #define HAVE_USB_CHARGING_ENABLE +#define HAVE_USB_CHARGING_IN_THREAD +#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE #define HAVE_BOOTLOADER_USB_MODE +/* This appears to improve transfer performance (the default is 64 KiB). + * Going any higher doesn't help but we're still slower than the OF. */ +#define USB_READ_BUFFER_SIZE (128 * 1024) +#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 */ @@ -126,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/iaudiox5.h b/firmware/export/config/iaudiox5.h index 0164cd20fa..cae83dd952 100644 --- a/firmware/export/config/iaudiox5.h +++ b/firmware/export/config/iaudiox5.h @@ -151,6 +151,7 @@ #define CURRENT_NORMAL 65 /*2250mah/35h = 65 ma*/ #define CURRENT_BACKLIGHT 25 #define CURRENT_REMOTE 8 /* additional current when remote connected */ +#define CURRENT_RECORD 2 /* FIXME: placeholder value */ /* Define this if your LCD can set contrast */ #define HAVE_LCD_CONTRAST diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h index fdf7e8e516..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. @@ -158,6 +156,7 @@ /* define current usage levels */ #define CURRENT_NORMAL 18 /* playback @48MHz clock, backlight off */ #define CURRENT_BACKLIGHT 23 /* maximum brightness */ +#define CURRENT_RECORD 2 /* FIXME: placeholder value */ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER diff --git a/firmware/export/config/mrobe500.h b/firmware/export/config/mrobe500.h index a7d72f76ac..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 @@ -198,7 +195,7 @@ /* define current usage levels */ #define CURRENT_NORMAL 85 /* Measured */ #define CURRENT_BACKLIGHT 200 /* Over 200 mA total measured when on */ -#define CURRENT_RECORD 0 /* no recording */ +#define CURRENT_REMOTE 2 /* FIXME: placeholder value */ /* Hardware controlled charging with monitoring */ #define CONFIG_CHARGING CHARGING_MONITOR @@ -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/nokian8xx.h b/firmware/export/config/nokian8xx.h index 550ee112d1..5cdf42a11f 100644 --- a/firmware/export/config/nokian8xx.h +++ b/firmware/export/config/nokian8xx.h @@ -43,9 +43,6 @@ #define HAVE_TOUCHSCREEN #define HAVE_BUTTON_DATA -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* define this if you have a real-time clock */ #define CONFIG_RTC APPLICATION diff --git a/firmware/export/config/nokian900.h b/firmware/export/config/nokian900.h index ebbe5e2cc8..e7084f9acb 100644 --- a/firmware/export/config/nokian900.h +++ b/firmware/export/config/nokian900.h @@ -42,9 +42,6 @@ #define HAVE_TOUCHSCREEN #define HAVE_BUTTON_DATA -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* define this if you have a real-time clock */ #define CONFIG_RTC APPLICATION diff --git a/firmware/export/config/pandora.h b/firmware/export/config/pandora.h index 3b26ad9b7b..41b2b44dad 100644 --- a/firmware/export/config/pandora.h +++ b/firmware/export/config/pandora.h @@ -43,9 +43,6 @@ #define HAVE_TOUCHSCREEN #define HAVE_BUTTON_DATA -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* define this if you have a real-time clock */ #define CONFIG_RTC APPLICATION diff --git a/firmware/export/config/samsungypr0.h b/firmware/export/config/samsungypr0.h index ba5f02ad74..88ba96504d 100644 --- a/firmware/export/config/samsungypr0.h +++ b/firmware/export/config/samsungypr0.h @@ -68,10 +68,6 @@ /* TODO: ASCODEC has an auto dim feature, so disabling the supply to leds should do the trick. But for now I tested SW fading only */ #define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING -/* define this if you have RTC RAM available for settings */ -/* TODO: in theory we could use that, ascodec offers us such a ram. we have also a small device, part of the nand of 1 MB size, that Samsung uses to store region code etc and it's almost unused space */ -//#define HAVE_RTC_RAM - /* define this if you have a real-time clock */ #define CONFIG_RTC RTC_AS3514 #define HAVE_RTC_ALARM @@ -109,6 +105,8 @@ #define CONFIG_TUNER SI4700 #define HAVE_TUNER_PWR_CTRL #define HAVE_RDS_CAP +#define CONFIG_RDS (RDS_CFG_POLL | RDS_CFG_PROCESS) +#define CONFIG_RDS_POLL_TICKS 4 /* Define this for FM radio input available */ #define HAVE_FMRADIO_IN @@ -123,6 +121,7 @@ /* Define current usage levels. */ #define CURRENT_NORMAL 24 /* ~25h, on 600mAh that's about 24mA */ #define CURRENT_BACKLIGHT 62 /* ~6,5h -> 92mA. Minus 24mA normal that gives us 68mA */ +#define CURRENT_RECORD 2 /* FIXME: placeholder value */ #endif /* SIMULATOR */ diff --git a/firmware/export/config/samsungypr1.h b/firmware/export/config/samsungypr1.h index d091e3ed1e..8ef76d60af 100644 --- a/firmware/export/config/samsungypr1.h +++ b/firmware/export/config/samsungypr1.h @@ -150,6 +150,8 @@ #define CONFIG_TUNER SI4700 #define HAVE_TUNER_PWR_CTRL #define HAVE_RDS_CAP +#define CONFIG_RDS (RDS_CFG_POLL | RDS_CFG_PROCESS) +#define CONFIG_RDS_POLL_TICKS 4 /* Define this for FM radio input available */ #define HAVE_FMRADIO_IN @@ -161,5 +163,6 @@ /* Define current usage levels. */ #define CURRENT_NORMAL 24 /* ~25h, on 600mAh that's about 24mA */ #define CURRENT_BACKLIGHT 62 /* ~6,5h -> 92mA. Minus 24mA normal that gives us 68mA */ +#define CURRENT_RECORD 2 /* FIXME: placeholder value */ #endif /* SIMULATOR */ diff --git a/firmware/export/config/samsungypz5.h b/firmware/export/config/samsungypz5.h deleted file mode 100644 index 2af62c1cbe..0000000000 --- a/firmware/export/config/samsungypz5.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * This config file is for the Samsung YP-Z5 - */ -#define IMX233_SUBTARGET 3600 -#define IMX233_PACKAGE IMX233_BGA169 -#define IMX233_PARTITIONS IMX233_FREESCALE - -/* For Rolo and boot loader */ -#define MODEL_NUMBER 84 -#define MODEL_NAME "Samsung YP-Z5" -/* Define if boot data from bootloader has been enabled for the target */ -#define HAVE_BOOTDATA - -#define HW_SAMPR_CAPS SAMPR_CAP_ALL_48 - -/* define this if you have recording possibility */ -#define HAVE_RECORDING - -#define REC_SAMPR_CAPS SAMPR_CAP_ALL_48 - -/* Default recording levels */ -#define DEFAULT_REC_MIC_GAIN 23 -#define DEFAULT_REC_LEFT_GAIN 23 -#define DEFAULT_REC_RIGHT_GAIN 23 - -/* Define bitmask of input sources - recordable bitmask can be defined - explicitly if different */ -#define HAVE_FMRADIO_IN -#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) - - - -/* define this if you have a colour LCD */ -#define HAVE_LCD_COLOR - -#ifndef BOOTLOADER -#define HAVE_ALBUMART - -/* define this to enable bitmap scaling */ -#define HAVE_BMP_SCALING - -/* define this to enable JPEG decoding */ -#define HAVE_JPEG - -/* Define this if a programmable hotkey is mapped */ -#define HAVE_HOTKEY - -/* define this if you have access to the quickscreen */ -#define HAVE_QUICKSCREEN - -/* define this if you would like tagcache to build on this target */ -#define HAVE_TAGCACHE - -/* define this if the target has volume keys which can be used in the lists */ -#define HAVE_VOLUME_IN_LIST - -/* define this if you have LCD enable function */ -//#define HAVE_LCD_ENABLE - -/* Define this if your LCD can be put to sleep. HAVE_LCD_ENABLE - should be defined as well. -#define HAVE_LCD_SLEEP -#define HAVE_LCD_SLEEP_SETTING -*/ - -/* define this if you can flip your LCD -#define HAVE_LCD_FLIP -*/ - -/* define this if you can invert the colours on your LCD -#define HAVE_LCD_INVERT -*/ - -/* define this if you have a real-time clock */ -#define CONFIG_RTC RTC_IMX233 - -/* define this if you have a real-time clock with alarm facilities */ -#define HAVE_RTC_ALARM - -#endif /* !BOOTLOADER */ - -/* define this if you have an i.MX23 codec */ -#define HAVE_IMX233_CODEC - -#define CONFIG_TUNER TEA5767 -#define CONFIG_TUNER_XTAL 32768 - -/* There is no hardware tone control */ -#define HAVE_SW_TONE_CONTROLS - -#define CONFIG_KEYPAD SAMSUNG_YPZ5_PAD -#define HAVE_TOUCHPAD_IMX233 - -/* Define this to enable morse code input */ -#define HAVE_MORSE_INPUT - - - - -/* LCD dimensions */ -#define LCD_WIDTH 128 -#define LCD_HEIGHT 160 -/* sqrt(128^2 + 160^2) / 1.8 = 113.8 */ -#define LCD_DPI 114 -#define LCD_DEPTH 16 /* 65536 colours */ -#define LCD_PIXELFORMAT RGB565 /* rgb565 */ - -/* Define this if you have a software controlled poweroff */ -#define HAVE_SW_POWEROFF - -/* Some devices seem to be FAT16 formatted */ -#define HAVE_FAT16SUPPORT - -/* 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 - -#define AB_REPEAT_ENABLE - -/* Define this for LCD backlight available */ -#define HAVE_BACKLIGHT -#define HAVE_BACKLIGHT_BRIGHTNESS - -/* Main LCD backlight brightness range and defaults */ -#define MIN_BRIGHTNESS_SETTING 1 -#define MAX_BRIGHTNESS_SETTING 18 -#define DEFAULT_BRIGHTNESS_SETTING 5 - -/* Which backlight fading type? */ -#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING - -/* define this if you have a flash memory storage */ -//#define HAVE_FLASH_STORAGE - -#define CONFIG_STORAGE (STORAGE_RAMDISK) -//#define CONFIG_NAND NAND_IMX233 -//#define NUM_DRIVES 0 - -/* Extra threads: touchpad and rds */ -//#define TARGET_EXTRA_THREADS 2 - -/* todo */ -#define BATTERY_CAPACITY_DEFAULT 550 /* default battery capacity */ -#define BATTERY_CAPACITY_MIN 550 /* min. capacity selectable */ -#define BATTERY_CAPACITY_MAX 550 /* max. capacity selectable */ -#define BATTERY_CAPACITY_INC 0 /* capacity increment */ -#define BATTERY_TYPES_COUNT 1 /* only one type */ - -#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE - -/* Charging implemented in a target-specific algorithm */ -#define CONFIG_CHARGING CHARGING_TARGET - -/* define this if the unit can be powered or charged via USB */ -#define HAVE_USB_POWER - -/* Define this if you have an IMX233*/ -#define CONFIG_CPU IMX233 - -/* Define this if you want to use the IMX233 i2c interface */ -#define CONFIG_I2C I2C_IMX233 - -/* define current usage levels (based on battery bench) */ -#define CURRENT_NORMAL 35 -#define CURRENT_BACKLIGHT 30 -#define CURRENT_RECORD CURRENT_NORMAL - -/* maximum charging current */ -#define CURRENT_MAX_CHG 200 - -/* Define this to the CPU frequency */ -#define CPU_FREQ 454000000 - -/* Type of LCD */ -#define CONFIG_LCD LCD_SAMSUNGYPZ5 - -/* Offset ( in the firmware file's header ) to the file CRC and data. These are - only used when loading the old format rockbox.e200 file */ -#define FIRMWARE_OFFSET_FILE_CRC 0x0 -#define FIRMWARE_OFFSET_FILE_DATA 0x8 - -/* USB On-the-go */ -#define CONFIG_USBOTG USBOTG_ARC - -/* enable these for the experimental usb stack */ -#define HAVE_USBSTACK -#define USB_VENDOR_ID 0x04e8 -#define USB_PRODUCT_ID 0x5041 -#define HAVE_USB_HID_MOUSE -#define HAVE_BOOTLOADER_USB_MODE - -/* Define this if you have adjustable CPU frequency */ -//#define HAVE_ADJUSTABLE_CPU_FREQ - -/* Virtual LED (icon) */ -#define CONFIG_LED LED_VIRTUAL - -#define BOOTFILE_EXT "samsung" -#define BOOTFILE "rockbox." BOOTFILE_EXT -#define BOOTDIR "/.rockbox" - -#define INCLUDE_TIMEOUT_API diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h index 3108bace61..99eb1d8832 100644 --- a/firmware/export/config/sansaclipplus.h +++ b/firmware/export/config/sansaclipplus.h @@ -11,12 +11,16 @@ #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 #ifndef BOOTLOADER #define HAVE_HOTSWAP +#define HAVE_RDS_CAP +#define CONFIG_RDS (RDS_CFG_POLL | RDS_CFG_PROCESS) +#define CONFIG_RDS_POLL_TICKS 4 #endif #define HW_SAMPR_CAPS SAMPR_CAP_ALL_96 @@ -146,6 +150,9 @@ /* define this if the flash memory uses the SecureDigital Memory Card protocol */ #define CONFIG_STORAGE STORAGE_SD +/* Define this if target has an additional number of threads specific to it */ +#define TARGET_EXTRA_THREADS 1 /* RDS thread */ + #define BATTERY_CAPACITY_DEFAULT 290 /* default battery capacity */ #define BATTERY_CAPACITY_MIN 290 /* min. capacity selectable */ #define BATTERY_CAPACITY_MAX 290 /* max. capacity selectable */ @@ -195,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 5909d3bf84..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 @@ -81,9 +78,8 @@ #define LCD_PIXELFORMAT RGB565 /* rgb565 */ #define HAVE_LCD_ENABLE -#ifndef BOOTLOADER #define HAVE_LCD_SLEEP -#endif +#define HAVE_LCD_SHUTDOWN #define LCD_SLEEP_TIMEOUT (2*HZ) @@ -114,7 +110,7 @@ //#define HW_SAMPR_CAPS SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 | SAMPR_CAP_8 /* define this if you have a real-time clock */ -//#define CONFIG_RTC RTC_STM41T62 +#define CONFIG_RTC RTC_CONNECT /* define this if the unit uses a scrollwheel for navigation */ #define HAVE_SCROLLWHEEL @@ -135,6 +131,12 @@ /* Define this if you have a software controlled poweroff */ #define HAVE_SW_POWEROFF +#ifndef BOOTLOADER +#define HAVE_WIFI +/* define this if the target has Marvell 88W8686 interfaced over SPI */ +#define HAVE_W8686_SPI +#endif + /* The number of bytes reserved for loadable codecs */ #define CODEC_SIZE 0x100000 @@ -147,7 +149,15 @@ #define BATTERY_CAPACITY_INC 100 /* capacity increment */ #define BATTERY_TYPES_COUNT 1 /* only one type */ -#define CONFIG_BATTERY_MEASURE PERCENTAGE_MEASURE +/* bq27000 provides voltage, percentage and time measure. + * Voltage reading is available every 2.56 seconds and does not need filtering. + * Read the measured voltage every 3 seconds so we are guaranteed to not read + * the same value twice (do not try to read every 2.56 seconds as clocks are + * not synchronized). + */ +#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|PERCENTAGE_MEASURE|TIME_MEASURE) +#define BATT_AVE_SAMPLES 1 +#define POWER_THREAD_STEP_TICKS (3*HZ) /* define current usage levels */ #if 0 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/sansam200v4.h b/firmware/export/config/sansam200v4.h index 97462dc6e2..37f0931079 100644 --- a/firmware/export/config/sansam200v4.h +++ b/firmware/export/config/sansam200v4.h @@ -75,9 +75,6 @@ #define CONFIG_RTC RTC_AS3514 #endif -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* Define this if you have a software controlled poweroff */ #define HAVE_SW_POWEROFF diff --git a/firmware/export/config/sdlapp.h b/firmware/export/config/sdlapp.h index d8c1266f51..3286e377ce 100644 --- a/firmware/export/config/sdlapp.h +++ b/firmware/export/config/sdlapp.h @@ -52,9 +52,6 @@ #define HAVE_TOUCHSCREEN #define HAVE_BUTTON_DATA -/* define this if you have RTC RAM available for settings */ -//#define HAVE_RTC_RAM - /* define this if you have a real-time clock */ #define CONFIG_RTC APPLICATION diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h new file mode 100644 index 0000000000..6f5365a97e --- /dev/null +++ b/firmware/export/config/shanlingq1.h @@ -0,0 +1,136 @@ +/* RoLo-related defines */ +#define MODEL_NAME "Shanling Q1" +#define MODEL_NUMBER 115 +#define BOOTFILE_EXT "q1" +#define BOOTFILE "rockbox." BOOTFILE_EXT +#define BOOTDIR "/.rockbox" +#define FIRMWARE_OFFSET_FILE_CRC 0 +#define FIRMWARE_OFFSET_FILE_DATA 8 + +/* CPU defines */ +#define CONFIG_CPU X1000 +#define X1000_EXCLK_FREQ 24000000 +#define CPU_FREQ 1008000000 + +#ifndef SIMULATOR +#define TIMER_FREQ X1000_EXCLK_FREQ +#endif + +/* Kernel defines */ +#define INCLUDE_TIMEOUT_API +#define HAVE_SEMAPHORE_OBJECTS + +/* Drivers */ +#define HAVE_I2C_ASYNC + +/* Buffer for plugins and codecs. */ +#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */ +#define CODEC_SIZE 0x100000 /* 1 MiB */ + +/* LCD defines */ +#define CONFIG_LCD LCD_SHANLING_Q1 +#define LCD_WIDTH 360 +#define LCD_HEIGHT 400 +#define LCD_DEPTH 16 +#define LCD_PIXELFORMAT RGB565 +#define LCD_DPI 200 +#define HAVE_LCD_COLOR +#define HAVE_LCD_BITMAP +#define HAVE_LCD_ENABLE +#define LCD_X1000_FASTSLEEP +#define LCD_X1000_DMA_WAIT_FOR_FRAME + +/* Backlight defines */ +#define HAVE_BACKLIGHT +#define HAVE_BACKLIGHT_BRIGHTNESS +#define MIN_BRIGHTNESS_SETTING 1 +#define MAX_BRIGHTNESS_SETTING 100 +#define BRIGHTNESS_STEP 5 +#define DEFAULT_BRIGHTNESS_SETTING 70 +#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING + +/* Codec / audio hardware defines */ +#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192 +#define HAVE_ES9218 +#define HAVE_SW_TONE_CONTROLS + +/* Button defines */ +#define CONFIG_KEYPAD SHANLING_Q1_PAD +#define HAVE_TOUCHSCREEN +#define HAVE_BUTTON_DATA +#define HAVE_FT6x06 +#define FT6x06_NUM_POINTS 5 +#define HAVE_HEADPHONE_DETECTION + +/* Storage defines */ +#define CONFIG_STORAGE STORAGE_SD +#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 + +/* RTC settings */ +#define CONFIG_RTC RTC_X1000 +/* TODO: implement HAVE_RTC_ALARM */ + +/* Power management */ +#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE) +#define CONFIG_CHARGING CHARGING_MONITOR +#define HAVE_SW_POWEROFF + +#ifndef SIMULATOR +/* TODO: get the CW2015 driver working correctly */ +/* #define HAVE_CW2015 */ +#define HAVE_AXP_PMU 192 /* Presumed */ +#define HAVE_POWEROFF_WHILE_CHARGING +#endif + +/* Only one battery type */ +#define BATTERY_CAPACITY_DEFAULT 1100 +#define BATTERY_CAPACITY_MIN 1100 +#define BATTERY_CAPACITY_MAX 1100 +#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 +#define USB_DW_TURNAROUND 5 +#define HAVE_USBSTACK +#define USB_VENDOR_ID 0x0525 /* Same as the xDuuo X3, for some reason. */ +#define USB_PRODUCT_ID 0xa4a5 /* Nb. DAC mode uses 20b1:301f 'XMOS Ltd' */ +#define USB_DEVBSS_ATTR __attribute__((aligned(32))) +#define HAVE_USB_POWER +#define HAVE_USB_CHARGING_ENABLE +#define HAVE_USB_CHARGING_IN_THREAD +#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE +#define HAVE_BOOTLOADER_USB_MODE +/* This appears to improve transfer performance (the default is 64 KiB). + * Going any higher doesn't help but we're still slower than the OF. */ +#define USB_READ_BUFFER_SIZE (128 * 1024) +#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 +#define HAVE_BMP_SCALING +#define HAVE_JPEG +#define HAVE_TAGCACHE +#define HAVE_VOLUME_IN_LIST +#define HAVE_QUICKSCREEN +#define HAVE_HOTKEY +#define AB_REPEAT_ENABLE +#define HAVE_BOOTLOADER_SCREENDUMP diff --git a/firmware/export/config/tatungtpj1022.h b/firmware/export/config/tatungtpj1022.h deleted file mode 100644 index 3865e8f87f..0000000000 --- a/firmware/export/config/tatungtpj1022.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This config file is for the Tatung Elio TPJ-1022 - */ - -#define MODEL_NAME "Tatung Elio TPJ-1022" - -/* For Rolo and boot loader */ -#define MODEL_NUMBER 15 - -/* define this if you use an ATA controller */ -#define CONFIG_STORAGE STORAGE_ATA - -/*define this if the ATA controller and method of USB access support LBA48 */ -#define HAVE_LBA48 - -/* define this if you have recording possibility */ -/*#define HAVE_RECORDING*/ /* TODO: add support for this */ - -/* define the bitmask of hardware sample rates */ -#define HW_SAMPR_CAPS (SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_48 | \ - SAMPR_CAP_44 | SAMPR_CAP_32 | SAMPR_CAP_8) - -/* define the bitmask of recording sample rates -#define REC_SAMPR_CAPS (SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_48 | \ - SAMPR_CAP_44 | SAMPR_CAP_32 | SAMPR_CAP_8) */ - - - - -/* define this if you have a colour LCD */ -#define HAVE_LCD_COLOR - -/* define this if you have access to the quickscreen */ -#define HAVE_QUICKSCREEN - -/* LCD dimensions */ -#define LCD_WIDTH 220 -#define LCD_HEIGHT 176 -/* sqrt(220^2 + 176^2) / 2.2 = 128.1 */ -#define LCD_DPI 128 -#define LCD_DEPTH 16 /* 65536 colours */ -#define LCD_PIXELFORMAT RGB565 - -/* #define IRAM_LCDFRAMEBUFFER IDATA_ATTR *//* put the lcd frame buffer in IRAM */ - -#define CONFIG_KEYPAD TATUNG_TPJ1022_PAD - - - - -/* define this if you have a real-time clock */ -#ifndef BOOTLOADER -//#define CONFIG_RTC RTC_E8564 -#endif - -/* 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 - -/* Define this if you have the WM8731 audio codec */ -#define HAVE_WM8731 - -#define AB_REPEAT_ENABLE - -/* define this if you have a disk storage, i.e. something - that needs spinups and can cause skips when shaked */ -#define HAVE_DISK_STORAGE - -/* Define this for LCD backlight available */ -#define HAVE_BACKLIGHT - -#define BATTERY_CAPACITY_DEFAULT 1550 /* default battery capacity - TODO: check this, probably different - for different models too */ -#define BATTERY_CAPACITY_MIN 1500 /* min. capacity selectable */ -#define BATTERY_CAPACITY_MAX 1600 /* max. capacity selectable */ -#define BATTERY_CAPACITY_INC 10 /* capacity increment */ -#define BATTERY_TYPES_COUNT 1 /* only one type */ - -#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE - -/* Hardware controlled charging? FIXME */ -#define CONFIG_CHARGING CHARGING_SIMPLE - -/* define this if the unit can be powered or charged via USB */ -/*#define HAVE_USB_POWER*/ - -/* Define this if you have a PortalPlayer PP5020 */ -#define CONFIG_CPU PP5020 - -/* Define this if you want to use the PP5020 i2c interface */ -#define CONFIG_I2C I2C_PP5020 - -/* define this if the hardware can be powered off while charging */ -/* TODO: should this be set for the H10? */ -//#define HAVE_POWEROFF_WHILE_CHARGING - -/* The start address index for ROM builds */ -#define ROM_START 0x00000000 - -/* Define this to the CPU frequency */ -/* TODO: this is probably wrong */ -#define CPU_FREQ 11289600 - -/* Type of LCD */ -#define CONFIG_LCD LCD_TPJ1022 - -#define DEFAULT_CONTRAST_SETTING 19 - -/* Offset ( in the firmware file's header ) to the file length */ -#define FIRMWARE_OFFSET_FILE_LENGTH 0 - -/* Offset ( in the firmware file's header ) to the file CRC */ -#define FIRMWARE_OFFSET_FILE_CRC 0 - -/* Offset ( in the firmware file's header ) to the real data */ -#define FIRMWARE_OFFSET_FILE_DATA 8 - -/* USB On-the-go */ -#define CONFIG_USBOTG USBOTG_ARC - -/* define this if the unit can be powered or charged via USB */ -#define HAVE_USB_POWER - -/* enable these for the experimental usb stack ROOLKU */ -#define HAVE_USBSTACK -#define USB_VENDOR_ID 0x07B4 -#define USB_PRODUCT_ID 0x0280 - -/* Define this if you have adjustable CPU frequency */ -#define HAVE_ADJUSTABLE_CPU_FREQ - -#define BOOTFILE_EXT "elio" -#define BOOTFILE "rockbox." BOOTFILE_EXT -#define BOOTDIR "/.rockbox" - - -/* DMA is used only for reading on PP502x because although reads are ~8x faster - * writes appear to be ~25% slower. - */ -#ifndef BOOTLOADER -#define HAVE_ATA_DMA -#endif - -/* Define this if a programmable hotkey is mapped */ -//#define HAVE_HOTKEY diff --git a/firmware/export/config/xduoox3.h b/firmware/export/config/xduoox3.h index 6cc1aa2e57..ea86e60d59 100644 --- a/firmware/export/config/xduoox3.h +++ b/firmware/export/config/xduoox3.h @@ -185,7 +185,7 @@ /* enable these for the experimental usb stack */ #define HAVE_USBSTACK -#define HAVE_BOOTLOADER_USB_MODE +//#define HAVE_BOOTLOADER_USB_MODE /* Connect by events, not by tick polling */ #define USB_STATUS_BY_EVENT diff --git a/firmware/export/cw2015.h b/firmware/export/cw2015.h new file mode 100644 index 0000000000..c810d1b7b5 --- /dev/null +++ b/firmware/export/cw2015.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __CW2015_H__ +#define __CW2015_H__ + +#include <stdint.h> +#include <stdbool.h> + +/* Driver for I2C battery fuel gauge IC CW2015. */ + +#define CW2015_REG_VERSION 0x00 +#define CW2015_REG_VCELL 0x02 /* 14 bits, registers 0x02 - 0x03 */ +#define CW2015_REG_SOC 0x04 /* 16 bits, registers 0x04 - 0x05 */ +#define CW2015_REG_RRT_ALERT 0x06 /* 13 bit RRT + alert flag, 0x06-0x07 */ +#define CW2015_REG_CONFIG 0x08 +#define CW2015_REG_MODE 0x0a +#define CW2015_REG_BATINFO 0x10 /* cf. mainline Linux CW2015 driver */ +#define CW2015_SIZE_BATINFO 64 + +extern void cw2015_init(void); + +/* Read the battery terminal voltage, converted to millivolts. */ +extern int cw2015_get_vcell(void); + +/* Read the SOC, in percent (0-100%). */ +extern int cw2015_get_soc(void); + +/* Get the estimated remaining run time, in minutes. + * Not a linearly varying quantity, according to the datasheet. */ +extern int cw2015_get_rrt(void); + +/* Read the current battery profile */ +extern const uint8_t* cw2015_get_bat_info(void); + +/* Debug screen */ +extern bool cw2015_debug_menu(void); + +#endif /* __CW2015_H__ */ 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/dm320.h b/firmware/export/dm320.h index 01f206bfc9..bd6ca15407 100644 --- a/firmware/export/dm320.h +++ b/firmware/export/dm320.h @@ -30,13 +30,17 @@ #if !defined(__ASSEMBLER__) && !defined(__LD__) /* These variables are created during linking (app/boot.lds) */ extern unsigned long _lcdbuf; +#ifdef MROBE_500 extern unsigned long _lcdbuf2; +#endif extern unsigned long _ttbstart; #endif #define TTB_BASE_ADDR (_ttbstart) /* End of memory */ #define FRAME ((short *) (&_lcdbuf)) /* Right after TTB */ +#ifdef MROBE_500 #define FRAME2 ((short *) (&_lcdbuf2)) /* Right after FRAME */ +#endif #define PHY_IO_BASE 0x00030000 #define DM320_REG(addr) (*(volatile unsigned short *)(PHY_IO_BASE + (addr))) diff --git a/firmware/export/eros_qn_codec.h b/firmware/export/eros_qn_codec.h new file mode 100644 index 0000000000..223ef06779 --- /dev/null +++ b/firmware/export/eros_qn_codec.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * + * Copyright (c) 2021 Andrew Ryabinin, 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 _EROS_QN_CODEC_H +#define _EROS_QN_CODEC_H + +/* + * Note: Maximum volume is set one step below unity in order to + * avoid overflowing pcm samples due to our DC Offset. + * + * The DAC's output is hot enough this should not be an issue. + */ +#define PCM5102A_VOLUME_MIN -740 +#define PCM5102A_VOLUME_MAX -20 + +/* 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 eros_qn_set_outputs(void); + +/* returns (global_settings.volume_limit * 10) */ +int eros_qn_get_volume_limit(void); + +#endif diff --git a/firmware/export/erosqlinux_codec.h b/firmware/export/erosqlinux_codec.h index b6ab58fa74..ecc10be924 100644 --- a/firmware/export/erosqlinux_codec.h +++ b/firmware/export/erosqlinux_codec.h @@ -3,7 +3,16 @@ #define AUDIOHW_CAPS (LINEOUT_CAP) -AUDIOHW_SETTING(VOLUME, "dB", 0, 2, -74, 0, -40) +/* a small DC offset prevents play/pause clicking due to the DAC auto-muting */ +#define PCM_DC_OFFSET_VALUE -1 + +/* + * Note: Maximum volume is set one step below unity in order to + * avoid overflowing pcm samples due to our DC Offset. + * + * The DAC's output is hot enough this should not be an issue. + */ +AUDIOHW_SETTING(VOLUME, "dB", 0, 2, -74, -2, -40) //#define AUDIOHW_NEEDS_INITIAL_UNMUTE 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/es9218.h b/firmware/export/es9218.h new file mode 100644 index 0000000000..1492304c67 --- /dev/null +++ b/firmware/export/es9218.h @@ -0,0 +1,230 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __ES9218_H__ +#define __ES9218_H__ + +#include <stdbool.h> +#include <stdint.h> + +#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP) +#define AUDIOHW_HAVE_ES9218_ROLL_OFF + +#define ES9218_DIG_VOLUME_MIN (-1275) +#define ES9218_DIG_VOLUME_MAX 0 +#define ES9218_DIG_VOLUME_STEP 5 + +#define ES9218_AMP_VOLUME_MIN (-240) +#define ES9218_AMP_VOLUME_MAX 0 +#define ES9218_AMP_VOLUME_STEP 10 + +AUDIOHW_SETTING(VOLUME, "dB", 1, ES9218_DIG_VOLUME_STEP, + ES9218_DIG_VOLUME_MIN, ES9218_DIG_VOLUME_MAX, -200) +AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 7, 0) +AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0) + +/* Register addresses */ +#define ES9218_REG_SYSTEM 0x00 +#define ES9218_REG_INPUT_SEL 0x01 +#define ES9218_REG_MIX_AUTOMUTE 0x02 +#define ES9218_REG_ANALOG_VOL 0x03 +#define ES9218_REG_AUTOMUTE_TIME 0x04 +#define ES9218_REG_AUTOMUTE_LEVEL 0x05 +#define ES9218_REG_DOP_VOLUME_RAMP 0x06 +#define ES9218_REG_FILTER_SYS_MUTE 0x07 +#define ES9218_REG_GPIO1_2_CONFIG 0x08 +#define ES9218_REG_RESERVED_1 0x09 +#define ES9218_REG_MASTER_MODE_CONFIG 0x0a +#define ES9218_REG_OVERCURRENT_PROT 0x0b +#define ES9218_REG_ASRC_DPLL_BANDWIDTH 0x0c +#define ES9218_REG_THD_COMP_BYPASS 0x0d +#define ES9218_REG_SOFT_START_CONFIG 0x0e +#define ES9218_REG_VOLUME_LEFT 0x0f +#define ES9218_REG_VOLUME_RIGHT 0x10 +#define ES9218_REG_MASTER_TRIM_BIT0_7 0x11 +#define ES9218_REG_MASTER_TRIM_BIT8_15 0x12 +#define ES9218_REG_MASTER_TRIM_BIT16_23 0x13 +#define ES9218_REG_MASTER_TRIM_BIT24_31 0x14 +#define ES9218_REG_GPIO_INPUT_SEL 0x15 +#define ES9218_REG_THD_COMP_C2_LO 0x16 +#define ES9218_REG_THD_COMP_C2_HI 0x17 +#define ES9218_REG_THD_COMP_C3_LO 0x18 +#define ES9218_REG_THD_COMP_C3_HI 0x19 +#define ES9218_REG_CHARGE_PUMP_SS_DELAY 0x1a +#define ES9218_REG_GENERAL_CONFIG 0x1b +#define ES9218_REG_RESERVED_2 0x1c +#define ES9218_REG_GPIO_INV_CLOCK_GEAR 0x1d +#define ES9218_REG_CHARGE_PUMP_CLK_LO 0x1e +#define ES9218_REG_CHARGE_PUMP_CLK_HI 0x1f +#define ES9218_REG_AMP_CONFIG 0x20 +#define ES9218_REG_INTERRUPT_MASK 0x21 +#define ES9218_REG_PROG_NCO_BIT0_7 0x22 +#define ES9218_REG_PROG_NCO_BIT8_15 0x23 +#define ES9218_REG_PROG_NCO_BIT16_23 0x24 +#define ES9218_REG_PROG_NCO_BIT24_31 0x25 +#define ES9218_REG_RESERVED_3 0x27 +#define ES9218_REG_FIR_RAM_ADDR 0x28 +#define ES9218_REG_FIR_DATA_BIT0_7 0x29 +#define ES9218_REG_FIR_DATA_BIT8_15 0x2a +#define ES9218_REG_FIR_DATA_BIT16_23 0x2b +#define ES9218_REG_PROG_FIR_CONFIG 0x2c +#define ES9218_REG_ANALOG_OVERRIDE_1 0x2d +#define ES9218_REG_ANALOG_OVERRIDE_2 0x2e +#define ES9218_REG_ANALOG_OVERRIDE_3 0x2f +#define ES9218_REG_ANALOG_CTRL 0x30 +#define ES9218_REG_CLKGEAR_CFG_BIT0_7 0x31 +#define ES9218_REG_CLKGEAR_CFG_BIT8_15 0x32 +#define ES9218_REG_CLKGEAR_CFG_BIT16_23 0x33 +#define ES9218_REG_RESERVED_4 0x34 +#define ES9218_REG_THD_COMP_C2_CH2_LO 0x35 +#define ES9218_REG_THD_COMP_C2_CH2_HI 0x36 +#define ES9218_REG_THD_COMP_C3_CH2_LO 0x37 +#define ES9218_REG_THD_COMP_C3_CH2_HI 0x38 +#define ES9218_REG_RESERVED_5 0x39 +#define ES9218_REG_RESERVED_6 0x3a +#define ES9218_REG_RESERVED_7 0x3b +#define ES9218_REG_RESERVED_8 0x3c +#define ES9218_REG_CHIP_ID_AND_STATUS 0x40 +#define ES9218_REG_GPIO_AND_CLOCK_GEAR 0x41 +#define ES9218_REG_DPLL_NUMBER_BIT0_7 0x42 +#define ES9218_REG_DPLL_NUMBER_BIT8_15 0x43 +#define ES9218_REG_DPLL_NUMBER_BIT16_23 0x44 +#define ES9218_REG_DPLL_NUMBER_BIT24_31 0x45 +#define ES9218_REG_INPUT_MUTE_STATUS 0x48 +#define ES9218_REG_FIR_READ_BIT0_7 0x49 +#define ES9218_REG_FIR_READ_BIT8_15 0x4a +#define ES9218_REG_FIR_READ_BIT16_23 0x4b + +enum es9218_clock_gear { + ES9218_CLK_GEAR_1 = 0, /* CLK = XI/1 */ + ES9218_CLK_GEAR_2 = 1, /* CLK = XI/2 */ + ES9218_CLK_GEAR_4 = 2, /* CLK = XI/4 */ + ES9218_CLK_GEAR_8 = 3, /* CLK = XI/8 */ +}; + +enum es9218_amp_mode { + ES9218_AMP_MODE_CORE_ON = 0, + ES9218_AMP_MODE_LOWFI = 1, + ES9218_AMP_MODE_1VRMS = 2, + ES9218_AMP_MODE_2VRMS = 3, +}; + +enum es9218_iface_role { + ES9218_IFACE_ROLE_SLAVE = 0, + ES9218_IFACE_ROLE_MASTER = 1, +}; + +enum es9218_iface_format { + ES9218_IFACE_FORMAT_I2S = 0, + ES9218_IFACE_FORMAT_LJUST = 1, + ES9218_IFACE_FORMAT_RJUST = 2, +}; + +enum es9218_iface_bits { + ES9218_IFACE_BITS_16 = 0, + ES9218_IFACE_BITS_24 = 1, + ES9218_IFACE_BITS_32 = 2, +}; + +enum es9218_filter_type { + ES9218_FILTER_LINEAR_FAST = 0, + ES9218_FILTER_LINEAR_SLOW = 1, + ES9218_FILTER_MINIMUM_FAST = 2, + ES9218_FILTER_MINIMUM_SLOW = 3, + ES9218_FILTER_APODIZING_1 = 4, + ES9218_FILTER_APODIZING_2 = 5, + ES9218_FILTER_HYBRID_FAST = 6, + ES9218_FILTER_BRICK_WALL = 7, +}; + +/* Power DAC on or off */ +extern void es9218_open(void); +extern void es9218_close(void); + +/* Clock controls + * + * - Clock gear divides the input master clock to produce the DAC's clock. + * Frequency can be lowered to save power when using lower sample rates. + * + * - NCO (numerically controller oscillator), according to the datasheet, + * defines the ratio between the DAC's clock and the FSR (for PCM modes, + * this is I2S frame clock = sample rate). In master mode it effectively + * controls the sampling frequency by setting the I2S frame clock output. + * It can also be used in slave mode, but other parts of the datasheet + * say contradictory things about synchronous operation in slave mode. + * + * - If using NCO mode and a varying MCLK input (eg. input from the SoC) then + * you will need to call es9218_recompute_nco() when changing MCLK in order + * to refresh the NCO setting. + */ +extern void es9218_set_clock_gear(enum es9218_clock_gear gear); +extern void es9218_set_nco_frequency(uint32_t fsr); +extern void es9218_recompute_nco(void); + +/* Amplifier controls */ +extern void es9218_set_amp_mode(enum es9218_amp_mode mode); +extern void es9218_set_amp_powered(bool en); + +/* Interface selection */ +extern void es9218_set_iface_role(enum es9218_iface_role role); +extern void es9218_set_iface_format(enum es9218_iface_format fmt, + enum es9218_iface_bits bits); + +/* Volume controls, all volumes given in units of dB/10 */ +extern void es9218_set_dig_volume(int vol_l, int vol_r); +extern void es9218_set_amp_volume(int vol); + +/* System mute */ +extern void es9218_mute(bool muted); + +/* Oversampling filter */ +extern void es9218_set_filter(enum es9218_filter_type filt); + +/* Automute settings */ +extern void es9218_set_automute_time(int time); +extern void es9218_set_automute_level(int dB); +extern void es9218_set_automute_fast_mode(bool en); + +/* DPLL bandwidth setting (knob = 0-15) */ +extern void es9218_set_dpll_bandwidth(int knob); + +/* THD compensation */ +extern void es9218_set_thd_compensation(bool en); +extern void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3); + +/* Direct register read/write/update operations */ +extern int es9218_read(int reg); +extern void es9218_write(int reg, uint8_t val); +extern void es9218_update(int reg, uint8_t msk, uint8_t val); + +/* GPIO pin setting callbacks */ +extern void es9218_set_power_pin(int level); +extern void es9218_set_reset_pin(int level); + +/* XI(MCLK) getter -- supplied by the target. + * + * Note: when changing the supplied MCLK frequency, the NCO will need to be + * reprogrammed for the new master clock. Call es9218_recompute_nco() to + * force this. Not necessary if you're not using NCO mode. + */ +extern uint32_t es9218_get_mclk(void); + +#endif /* __ES9218_H__ */ diff --git a/firmware/export/events.h b/firmware/export/events.h index 4591058d4f..8bdf1b55e2 100644 --- a/firmware/export/events.h +++ b/firmware/export/events.h @@ -52,7 +52,7 @@ #define EVENT_CLASS_RECORDING 0x1000 #define EVENT_CLASS_LCD 0x2000 #define EVENT_CLASS_VOICE 0x4000 - +#define EVENT_CLASS_SYSTEM 0x8000 /*LAST ONE */ /** * Subscribe to an event with a simple callback. The callback will be called * synchronously everytime the event fires, passing the event id and data to @@ -99,4 +99,14 @@ void remove_event_ex(unsigned short id, void (*handler)(unsigned short id, void */ void send_event(unsigned short id, void *data); +/** System events **/ +enum { + /* USB_INSERTED + data = &usbmode */ + SYS_EVENT_USB_INSERTED = (EVENT_CLASS_SYSTEM|1), + /* USB_EXTRACTED + data = NULL */ + SYS_EVENT_USB_EXTRACTED, +}; + #endif diff --git a/firmware/export/fat.h b/firmware/export/fat.h index b8092290e6..70152985b5 100644 --- a/firmware/export/fat.h +++ b/firmware/export/fat.h @@ -140,6 +140,8 @@ enum fat_remove_op /* what should fat_remove(), remove? */ int fat_remove(struct fat_file *file, enum fat_remove_op what); int fat_rename(struct fat_file *parent, struct fat_file *file, const unsigned char *newname); +int fat_modtime(struct fat_file *parent, struct fat_file *file, + time_t modtime); /** File stream functions **/ int fat_closewrite(struct fat_filestr *filestr, uint32_t size, @@ -174,7 +176,6 @@ void fat_recalc_free(IF_MV_NONVOID(int volume)); bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free); /** Misc. **/ -time_t fattime_mktime(uint16_t fatdate, uint16_t fattime); void fat_empty_fat_direntry(struct fat_direntry *entry); void fat_init(void); 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/ft6x06.h b/firmware/export/ft6x06.h index de1fdd0979..6596f89272 100644 --- a/firmware/export/ft6x06.h +++ b/firmware/export/ft6x06.h @@ -25,23 +25,32 @@ #include "config.h" #include <stdbool.h> -typedef void(*ft6x06_event_cb)(int, int, int); +enum ft6x06_event { + FT6x06_EVT_NONE = -1, + FT6x06_EVT_PRESS = 0, + FT6x06_EVT_RELEASE = 1, + FT6x06_EVT_CONTACT = 2, +}; -struct ft6x06_state { +struct ft6x06_point { int event; + int touch_id; int pos_x; int pos_y; + int weight; + int area; }; -enum ft6x06_event { - FT6x06_EVT_NONE = -1, - FT6x06_EVT_PRESS = 0, - FT6x06_EVT_RELEASE = 1, - FT6x06_EVT_CONTACT = 2, +struct ft6x06_state { + int gesture; + int nr_points; + struct ft6x06_point points[FT6x06_NUM_POINTS]; }; extern struct ft6x06_state ft6x06_state; +typedef void(*ft6x06_event_cb)(struct ft6x06_state* state); + void ft6x06_init(void); void ft6x06_set_event_cb(ft6x06_event_cb fn); void ft6x06_enable(bool en); diff --git a/firmware/export/generic_i2c.h b/firmware/export/generic_i2c.h index f71736acf0..9fdba26836 100644 --- a/firmware/export/generic_i2c.h +++ b/firmware/export/generic_i2c.h @@ -49,5 +49,15 @@ int i2c_write_data(int bus_index, int bus_address, int address, int i2c_read_data(int bus_index, int bus_address, int address, unsigned char* buf, int count); +/* Special function for devices that can appear on I2C bus but do not + * comply to I2C specification. Such devices include AT88SC6416C crypto + * memory. To read data from AT88SC6416C, a write I2C transaction starts, + * 3 bytes are written and then, in the middle of transaction, the device + * starts sending data. + */ +int i2c_write_read_data(int bus_index, int bus_address, + const unsigned char* buf_write, int count_write, + unsigned char* buf_read, int count_read); + #endif /* _GEN_I2C_ */ diff --git a/firmware/export/i2c-async.h b/firmware/export/i2c-async.h index 2877d1875c..f31a73452b 100644 --- a/firmware/export/i2c-async.h +++ b/firmware/export/i2c-async.h @@ -247,14 +247,15 @@ extern int i2c_reg_modify1(int bus, uint8_t addr, uint8_t reg, uint8_t clr, uint8_t set, uint8_t* val); /* Variant to write a single 8-bit value to a register */ -inline int i2c_reg_write1(int bus, uint8_t addr, uint8_t reg, uint8_t val) +static inline int i2c_reg_write1(int bus, uint8_t addr, + uint8_t reg, uint8_t val) { return i2c_reg_write(bus, addr, reg, 1, &val); } /* Variant to read an 8-bit value from a register; returns the value * directly, or returns -1 on any error. */ -inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg) +static inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg) { uint8_t v; int i = i2c_reg_read(bus, addr, reg, 1, &v); @@ -265,8 +266,8 @@ inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg) } /* Variant to set or clear one bit in an 8-bit register */ -inline int i2c_reg_setbit1(int bus, uint8_t addr, uint8_t reg, - int bit, int value, uint8_t* val) +static inline int i2c_reg_setbit1(int bus, uint8_t addr, uint8_t reg, + int bit, int value, uint8_t* val) { uint8_t clr = 0, set = 0; if(value) 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/jz4760b.h b/firmware/export/jz4760b.h index 589f67800a..af1f36270b 100644 --- a/firmware/export/jz4760b.h +++ b/firmware/export/jz4760b.h @@ -7017,6 +7017,11 @@ do { \ #define USB_INTR_SUSPEND 0x01 #define USB_INTR_RESUME 0x02 #define USB_INTR_RESET 0x04 +#define USB_INTR_SOF 0x08 +#define USB_INTR_CONNECT 0x10 +#define USB_INTR_DISCONNECT 0x20 +#define USB_INTR_SESS_REQ 0x40 +#define USB_INTR_VBUS_ERR 0x80 #define USB_INTR_EP(n) (1 << (n)) 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/logf.h b/firmware/export/logf.h index c8aaad06b4..7fbe5976a4 100644 --- a/firmware/export/logf.h +++ b/firmware/export/logf.h @@ -31,7 +31,7 @@ #define MAX_LOGF_SIZE 16384 -extern unsigned char logfbuffer[MAX_LOGF_SIZE]; +extern unsigned char logfbuffer[MAX_LOGF_SIZE + 1]; extern int logfindex; extern bool logfwrap; extern bool logfenabled; 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 92539c54c1..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); @@ -82,10 +89,13 @@ size_t path_basename(const char *name, const char **nameptr); size_t path_dirname(const char *name, const char **nameptr); size_t path_strip_trailing_separators(const char *name, const char **nameptr); void path_correct_separators(char *dstpath, const char *path); +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-internal.h b/firmware/export/pcm-internal.h index d5d46c33e1..e6400004a0 100644 --- a/firmware/export/pcm-internal.h +++ b/firmware/export/pcm-internal.h @@ -121,7 +121,6 @@ pcm_play_dma_status_callback(enum pcm_dma_status status) void pcm_play_dma_start_int(const void *addr, size_t size); void pcm_play_dma_stop_int(void); void pcm_play_stop_int(void); -const void *pcm_play_dma_get_peak_buffer_int(int *count); #endif /* HAVE_SW_VOLUME_CONTROL && !PCM_SW_VOLUME_UNBUFFERED */ /* Called by the bottom layer ISR when more data is needed. Returns true @@ -145,7 +144,6 @@ void pcm_play_dma_init(void) INIT_ATTR; void pcm_play_dma_postinit(void); void pcm_play_dma_start(const void *addr, size_t size); void pcm_play_dma_stop(void); -const void * pcm_play_dma_get_peak_buffer(int *count); void pcm_dma_apply_settings(void); 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 235f5302f9..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,43 +84,20 @@ 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_BACKLIGHT -#define CURRENT_BACKLIGHT 5 /* additional current when backlight always on */ -#endif - -#if defined(HAVE_RECORDING) && !defined(CURRENT_RECORD) -#define CURRENT_RECORD 2 /* additional recording current */ -#endif /* HAVE_RECORDING && !CURRENT_RECORD*/ - -#ifndef CURRENT_USB -#define CURRENT_USB 2 /* usual current in mA in USB mode */ -#endif - -#if defined(HAVE_REMOTE_LCD) && !defined(CURRENT_REMOTE) -#define CURRENT_REMOTE 2 /* additional current when remote connected */ -#endif /* CURRENT_REMOTE && !HAVE_REMOTE_LCD */ - -#if CONFIG_CHARGING -#ifndef CURRENT_MAX_CHG +#if CONFIG_CHARGING && !defined(CURRENT_MAX_CHG) #define CURRENT_MAX_CHG 350 /* maximum charging current */ #endif -#endif /* CONFIG_CHARGING */ - -#ifdef CHARGING_DEBUG_FILE -#define POWERMGMT_DEBUG_STACK ((0x1000)/sizeof(long)) -#else -#define POWERMGMT_DEBUG_STACK 0 -#endif #ifndef BATT_AVE_SAMPLES /* slw filter constant unless otherwise specified */ #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) @@ -132,11 +115,15 @@ extern const unsigned short percent_to_volt_charge[11]; int battery_level(void); /* percent */ int battery_time(void); /* minutes */ int battery_voltage(void); /* filtered batt. voltage in millivolts */ +int battery_current(void); /* battery current in milliamps + * (may just be a rough estimate) */ /* Implemented by the target, unfiltered */ 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); @@ -163,19 +150,23 @@ void battery_read_info(int *voltage, int *level); bool battery_level_safe(void); void set_poweroff_timeout(int timeout); +#if BATTERY_CAPACITY_INC > 0 void set_battery_capacity(int capacity); /* set local battery capacity value */ -int get_battery_capacity(void); /* get local battery capacity value */ +#endif +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 bd75bf0817..cf9e0f077b 100644 --- a/firmware/export/si4700.h +++ b/firmware/export/si4700.h @@ -49,13 +49,11 @@ 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 */ +/* Read raw RDS info for processing. + * - 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 + * new. */ void si4700_rds_process(void); #endif /* HAVE_RDS_CAP */ 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-designware.h b/firmware/export/usb-designware.h index 428733b4f5..9e5496f0db 100644 --- a/firmware/export/usb-designware.h +++ b/firmware/export/usb-designware.h @@ -216,8 +216,11 @@ #define DWC_DIEPINT(x) (*((REG32_PTR_T)(OTGBASE + 0x908 + 0x20*(x)))) #define DWC_DOEPINT(x) (*((REG32_PTR_T)(OTGBASE + 0xb08 + 0x20*(x)))) + #define SETUPRECVD (1<<15) /* control OUT */ #define TXFE (1<<7) /* IN */ + #define BACK2BACKSETUP (1<<6) /* control OUT */ #define INEPNE (1<<6) /* IN */ + #define STATUSRECVD (1<<5) /* control OUT */ #define ITEPMIS (1<<5) /* IN */ #define ITTXFE (1<<4) /* IN */ #define OTEPDIS (1<<4) /* OUT */ diff --git a/firmware/export/usb.h b/firmware/export/usb.h index 2bcd95ef81..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 @@ -178,7 +177,7 @@ struct usb_transfer_completion_event_data int dir; int status; int length; - void* data; + void* data[2]; }; #endif /* HAVE_USBSTACK */ diff --git a/firmware/export/usb_core.h b/firmware/export/usb_core.h index 75fafc06a8..fe1f7459cf 100644 --- a/firmware/export/usb_core.h +++ b/firmware/export/usb_core.h @@ -39,11 +39,21 @@ extern int usb_max_pkt_size; +enum { + USB_STRING_INDEX_LANGUAGE, + USB_STRING_INDEX_MANUFACTURER, + USB_STRING_INDEX_PRODUCT, + USB_STRING_INDEX_SERIAL, + USB_STRING_INDEX_MAX, +}; + struct usb_class_driver; void usb_core_init(void); void usb_core_exit(void); -void usb_core_control_request(struct usb_ctrlrequest* req); +void usb_core_control_request(struct usb_ctrlrequest* req, void* data); +void usb_core_control_complete(int status); +void usb_core_legacy_control_request(struct usb_ctrlrequest* req); void usb_core_transfer_complete(int endpoint,int dir,int status,int length); void usb_core_bus_reset(void); bool usb_core_any_exclusive_storage(void); diff --git a/firmware/export/usb_drv.h b/firmware/export/usb_drv.h index 7ef7c8b7ee..3ef4db3c9c 100644 --- a/firmware/export/usb_drv.h +++ b/firmware/export/usb_drv.h @@ -56,6 +56,12 @@ * -> usb_drv_int_enable(false) [ditto] * -> soc specific controller/clock deinit */ +enum usb_control_response { + USB_CONTROL_ACK, + USB_CONTROL_STALL, + USB_CONTROL_RECEIVE, +}; + /* one-time initialisation of the USB driver */ void usb_drv_startup(void); void usb_drv_int_enable(bool enable); /* Target implemented */ @@ -68,8 +74,9 @@ void usb_drv_stall(int endpoint, bool stall,bool in); bool usb_drv_stalled(int endpoint,bool in); int usb_drv_send(int endpoint, void* ptr, int length); int usb_drv_send_nonblocking(int endpoint, void* ptr, int length); -int usb_drv_recv(int endpoint, void* ptr, int length); -void usb_drv_ack(struct usb_ctrlrequest* req); +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length); +void usb_drv_control_response(enum usb_control_response resp, + void* data, int length); void usb_drv_set_address(int address); void usb_drv_reset_endpoint(int endpoint, bool send); bool usb_drv_powered(void); diff --git a/firmware/target/arm/imx233/samsung-ypz5/lcd-target.h b/firmware/export/wifi.h index 9993b7d731..a70e062cb0 100644 --- a/firmware/target/arm/imx233/samsung-ypz5/lcd-target.h +++ b/firmware/export/wifi.h @@ -5,8 +5,9 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ + * $Id$ * - * Copyright (c) 2013 by Amaury Pouly + * Copyright (C) 2021 by Tomasz Moń * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,9 +18,12 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef LCD_TARGET_H -#define LCD_TARGET_H -bool lcd_debug_screen(void); +#ifndef WIFI_H +#define WIFI_H -#endif /* LCD_TARGET_H */ +#include "config.h" + +void wifi_init(void) INIT_ATTR; + +#endif 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/export/xduoolinux_codec.h b/firmware/export/xduoolinux_codec.h index 301a341f15..a5bdab0661 100644 --- a/firmware/export/xduoolinux_codec.h +++ b/firmware/export/xduoolinux_codec.h @@ -3,7 +3,6 @@ #define AUDIOHW_CAPS (LINEOUT_CAP | FILTER_ROLL_OFF_CAP) AUDIOHW_SETTING(VOLUME, "dB", 1, 5, -102*10, 0, -30*10) -AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 4, 0) #endif // We want this, but the codec takes over a second to unmute! @@ -12,10 +11,14 @@ AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 4, 0) #if defined(XDUOO_X3II) /* The AK4490 glitches when switching sample rates */ #define AUDIOHW_MUTE_ON_SRATE_CHANGE +AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 5, 0) +#define AUDIOHW_HAVE_SS_ROLL_OFF #endif #if defined(XDUOO_X20) //#define AUDIOHW_NEEDS_INITIAL_UNMUTE +AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 4, 0) +#define AUDIOHW_HAVE_SHORT2_ROLL_OFF #endif void audiohw_mute(int mute); 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/target/arm/imx233/samsung-ypz5/debug-ypz5.c b/firmware/include/adler32.h index c125eac973..cd5302e869 100644 --- a/firmware/target/arm/imx233/samsung-ypz5/debug-ypz5.c +++ b/firmware/include/adler32.h @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2013 by Amaury Pouly + * Copyright (C) 2021 James Buren (adaptations from tinf/zlib) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,11 +19,11 @@ * ****************************************************************************/ -#include "system.h" -#include "button-target.h" -#include "lcd-target.h" +#include <stdint.h> -bool dbg_hw_target_info(void) -{ - return button_debug_screen() && lcd_debug_screen(); -} +#ifndef _ADLER32_H +#define _ADLER32_H + +uint32_t adler_32(const void *src, uint32_t len, uint32_t adler32); + +#endif 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.h b/firmware/include/file.h index 040f48dfc5..02d2077977 100644 --- a/firmware/include/file.h +++ b/firmware/include/file.h @@ -85,6 +85,9 @@ int fdprintf(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); #ifndef rename #define rename FS_PREFIX(rename) #endif +#ifndef modtime +#define modtime FS_PREFIX(modtime) +#endif #ifndef filesize #define filesize FS_PREFIX(filesize) #endif 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/filesystem-native.h b/firmware/include/filesystem-native.h index 640e179890..5bd61eea76 100644 --- a/firmware/include/filesystem-native.h +++ b/firmware/include/filesystem-native.h @@ -43,6 +43,8 @@ #define __OPEN_MODE_ARG #define __CREAT_MODE_ARG +#include <time.h> + int open(const char *name, int oflag); int creat(const char *name); int close(int fildes); @@ -53,6 +55,7 @@ ssize_t read(int fildes, void *buf, size_t nbyte); ssize_t write(int fildes, const void *buf, size_t nbyte); int remove(const char *path); int rename(const char *old, const char *new); +int modtime(const char *path, time_t modtime); off_t filesize(int fildes); int fsamefile(int fildes1, int fildes2); int relate(const char *path1, const char *path2); 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 new file mode 100644 index 0000000000..1fce186f34 --- /dev/null +++ b/firmware/include/inflate.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by James Buren (libflate adaptations for RockBox) + * + * 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 _INFLATE_H_ +#define _INFLATE_H_ + +#include <stdint.h> +#include <stddef.h> + +enum { + INFLATE_RAW, + INFLATE_ZLIB, + INFLATE_GZIP, +}; + +struct inflate; + +typedef uint32_t (*inflate_reader) (void* block, uint32_t block_size, void* ctx); +typedef uint32_t (*inflate_writer) (const void* block, uint32_t block_size, void* ctx); + +extern const uint32_t inflate_size; +extern const uint32_t inflate_align; + +// 'it' must be allocated by the caller. it must be at least 'inflate_size' bytes +// and aligned at 'inflate_align' bytes. 'st' is the type of stream to decompress; +// 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/crc32-mi4.h b/firmware/include/strptokspn_r.h index 431757271a..d565118190 100644 --- a/firmware/include/crc32-mi4.h +++ b/firmware/include/strptokspn_r.h @@ -5,9 +5,9 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id: crc32.h 10464 2006-08-05 20:19:10Z miipekk $ + * $Id$ * - * Copyright (C) 2007 Barry Wardell + * 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 @@ -18,8 +18,9 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef _CRC32_MI4_H -#define _CRC32_MI4_H -unsigned int chksum_crc32 (unsigned char *block, unsigned int length); + +#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/include/timefuncs.h b/firmware/include/timefuncs.h index c72508e862..25e041b576 100644 --- a/firmware/include/timefuncs.h +++ b/firmware/include/timefuncs.h @@ -24,8 +24,11 @@ #include "config.h" #include <stdbool.h> +#include <stdint.h> #include "time.h" +time_t dostime_mktime(uint16_t dosdate, uint16_t dostime); +void dostime_localtime(time_t time, uint16_t* dosdate, uint16_t* dostime); struct tm *get_time(void); int set_time(const struct tm *tm); #if CONFIG_RTC diff --git a/firmware/include/zip.h b/firmware/include/zip.h new file mode 100644 index 0000000000..70d225cfd7 --- /dev/null +++ b/firmware/include/zip.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by James Buren + * + * 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 _ZIP_H_ +#define _ZIP_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <time.h> + +enum { + ZIP_PASS_SHALLOW, + ZIP_PASS_START, + ZIP_PASS_DATA, + ZIP_PASS_END, +}; + +struct zip; + +struct zip_args { + uint16_t entry; + uint16_t entries; + char* name; + uint32_t file_size; + time_t mtime; + void* block; + uint32_t block_size; + uint32_t read_size; +}; + +typedef int (*zip_callback) (const struct zip_args* args, int pass, void* ctx); + +// open a handle for the given full file name path +struct zip* zip_open(const char* name, bool try_mem); + +// quick read of only directory index +int zip_read_shallow(struct zip* z, zip_callback cb, void* ctx); + +// slow read of whole archive +// this can also pickup where a successful shallow read leftoff +int zip_read_deep(struct zip* z, zip_callback cb, void* ctx); + +// extract the contents to an existing directory +// this can also pickup where a successful shallow read leftoff +int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx); + +// returns system resources +void zip_close(struct zip* z); + +#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 307be7116a..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 @@ -1501,7 +1501,14 @@ static inline void boost_thread(struct thread_entry *thread, bool boost) if ((thread->cpu_boost != 0) != boost) { thread->cpu_boost = boost; +#ifdef CPU_BOOST_LOGGING + const char fmt[] = __FILE__" thread[%s]"; + char pathbuf[sizeof(fmt) + 32]; /* thread name 32 */ + snprintf(pathbuf, sizeof(pathbuf), fmt, thread->name); + cpu_boost_(boost, pathbuf, __LINE__); +#else cpu_boost(boost); +#endif } } diff --git a/firmware/libc/atoi.c b/firmware/libc/atoi.c index 3393839b27..00617c3d13 100644 --- a/firmware/libc/atoi.c +++ b/firmware/libc/atoi.c @@ -26,7 +26,10 @@ int atoi (const char *str) { int value = 0; int sign = 1; - + + if (!str) + return 0; + while (isspace(*str)) { str++; 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/logf.c b/firmware/logf.c index 11ffa4e15c..971c2d3a35 100644 --- a/firmware/logf.c +++ b/firmware/logf.c @@ -61,7 +61,7 @@ static int logdiskfindex; #ifdef ROCKBOX_HAS_LOGF #ifndef __PCTOOL__ -unsigned char logfbuffer[MAX_LOGF_SIZE]; +unsigned char logfbuffer[MAX_LOGF_SIZE + 1]; int logfindex; bool logfwrap; bool logfenabled = true; @@ -272,10 +272,14 @@ void logf_panic_dump(int *y) return; } + /* Explicitly null-terminate our buffer */ + logfbuffer[MAX_LOGF_SIZE] = 0; + lcd_puts(1, (*y)++, "start of logf data"); lcd_update(); - i = logfindex - 2; /* The last actual characer (i.e. not '\0') */ + /* The intent is to dump the newest log entries first! */ + i = logfindex - 2; /* The last actual characer (i.e. not '\0') */ while(i >= 0) { while(logfbuffer[i] != 0 && i>=0) @@ -300,12 +304,13 @@ void logf_panic_dump(int *y) } if(strlen( &logfbuffer[i + 1]) > 0) { - lcd_putsf(1, (*y)++, "%*s", (MAX_LOGF_SIZE-i) &logfbuffer[i + 1]); + lcd_putsf(1, (*y)++, "%*s", (MAX_LOGF_SIZE-i), &logfbuffer[i + 1]); lcd_update(); } } i--; } + lcd_puts(1, (*y)++, "end of logf data"); lcd_update(); } 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/pcm_sw_volume.c b/firmware/pcm_sw_volume.c index e972896321..bee1559bb8 100644 --- a/firmware/pcm_sw_volume.c +++ b/firmware/pcm_sw_volume.c @@ -26,6 +26,16 @@ #include "fixedpoint.h" #include "pcm_sw_volume.h" +/* + * NOTE: With the addition of 32-bit software scaling to this + * file, sometimes the "size" variable gets a little confusing. + * + * The source buffer (as of right now) is always 16-bit, and the + * destination buffer can potentially be 32-bit. I've tried to + * make it consistent: when passed in a function call, try to use + * the source buffer (16-bit) size. + */ + /* volume factors set by pcm_set_master_volume */ static uint32_t vol_factor_l = 0, vol_factor_r = 0; @@ -39,6 +49,25 @@ static uint32_t pcm_new_factor_l = 0, pcm_new_factor_r = 0; static uint32_t pcm_factor_l = 0, pcm_factor_r = 0; static typeof (memcpy) *pcm_scaling_fn = NULL; +/* take care of some defines for 32-bit software vol */ +#if (PCM_NATIVE_BITDEPTH > 16) /* >16-bit */ + +# define HAVE_SWVOL_32 +# define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int32_t)) +# define PCM_DBL_BUF_SIZE_T int32_t + +# if !defined(PCM_DC_OFFSET_VALUE) +/* PCM_DC_OFFSET_VALUE is only needed due to hardware quirk on Eros Q */ +# define PCM_DC_OFFSET_VALUE 0 +# endif + +#else /* 16-BIT */ + +# define PCM_VOL_SAMPLE_SIZE PCM_SAMPLE_SIZE +# define PCM_DBL_BUF_SIZE_T int16_t + +#endif /* 16-BIT */ + /*** ** Volume scaling routines ** If unbuffered, called externally by pcm driver @@ -51,53 +80,56 @@ static typeof (memcpy) *pcm_scaling_fn = NULL; #define PCM_F_T int64_t /* Requires large integer math */ #endif /* PCM_SW_VOLUME_FRACBITS */ -/* Scale and round sample by PCM factor */ +/* Scale sample by PCM factor */ static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s) { - return (f * s + (PCM_F_T)PCM_FACTOR_UNITY/2) >> PCM_SW_VOLUME_FRACBITS; +#if defined(HAVE_SWVOL_32) + return (f * s + PCM_DC_OFFSET_VALUE) >> (32 - PCM_NATIVE_BITDEPTH); +#else + return (f * s) >> PCM_SW_VOLUME_FRACBITS; +#endif } -/* Both UNITY, use direct copy */ -/* static void * memcpy(void *dst, const void *src, size_t size); */ - /* Either cut (both <= UNITY), no clipping needed */ -static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t size) +static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t src_size) { - int16_t *d = dst; + PCM_DBL_BUF_SIZE_T *d = dst; const int16_t *s = src; uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r; - while (size) + while (src_size) { *d++ = pcm_scale_sample(factor_l, *s++); *d++ = pcm_scale_sample(factor_r, *s++); - size -= PCM_SAMPLE_SIZE; + src_size -= PCM_SAMPLE_SIZE; } return dst; } +#if !defined(HAVE_SWVOL_32) /* NOTE: 32-bit scaling is hardcoded to the cut function! */ /* Either boost (any > UNITY) requires clipping */ -static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t size) +static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t src_size) { int16_t *d = dst; const int16_t *s = src; uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r; - while (size) + while (src_size) { *d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++)); *d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++)); - size -= PCM_SAMPLE_SIZE; + src_size -= PCM_SAMPLE_SIZE; } return dst; } +#endif /* Transition the volume change smoothly across a frame */ -static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size) +static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t src_size) { - int16_t *d = dst; + PCM_DBL_BUF_SIZE_T *d = dst; const int16_t *s = src; uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r; @@ -110,13 +142,19 @@ static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size) int32_t diff_l = (int32_t)new_factor_l - (int32_t)factor_l; int32_t diff_r = (int32_t)new_factor_r - (int32_t)factor_r; - for (size_t done = 0; done < size; done += PCM_SAMPLE_SIZE) + for (size_t done = 0; done < src_size; done += PCM_SAMPLE_SIZE) { - int32_t sweep = (1 << 14) - fp14_cos(180*done / size); /* 0.0..2.0 */ + int32_t sweep = (1 << 14) - fp14_cos(180*done / src_size); /* 0.0..2.0 */ uint32_t f_l = fp_mul(sweep, diff_l, 15) + factor_l; uint32_t f_r = fp_mul(sweep, diff_r, 15) + factor_r; +#if defined(HAVE_SWVOL_32) + /* do not clip to 16 bits */ + *d++ = pcm_scale_sample(f_l, *s++); + *d++ = pcm_scale_sample(f_r, *s++); +#else *d++ = clip_sample_16(pcm_scale_sample(f_l, *s++)); *d++ = clip_sample_16(pcm_scale_sample(f_r, *s++)); +#endif } /* Select steady-state operation */ @@ -129,9 +167,9 @@ static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size) #ifndef PCM_SW_VOLUME_UNBUFFERED static inline #endif -void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size) +void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t src_size) { - pcm_scaling_fn(dst, src, size); + pcm_scaling_fn(dst, src, src_size); } /* Assign the new scaling function for normal steady-state operation */ @@ -143,6 +181,13 @@ void pcm_sync_pcm_factors(void) pcm_factor_l = new_factor_l; pcm_factor_r = new_factor_r; +/* NOTE: 32-bit scaling is limited to 0 db <--> -74 db, we will hardcode to cut. + * MEMCPY CANNOT BE USED, because we do need to at minimum multiply each + * sample up to 32-bit size. */ +#if defined(HAVE_SWVOL_32) + pcm_scaling_fn = pcm_scale_buffer_cut; +#else + if (new_factor_l == PCM_FACTOR_UNITY && new_factor_r == PCM_FACTOR_UNITY) { @@ -157,6 +202,7 @@ void pcm_sync_pcm_factors(void) { pcm_scaling_fn = pcm_scale_buffer_boost; } +#endif } #ifndef PCM_SW_VOLUME_UNBUFFERED @@ -164,10 +210,10 @@ void pcm_sync_pcm_factors(void) static const void * volatile src_buf_addr = NULL; static size_t volatile src_buf_rem = 0; -#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE) +#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_VOL_SAMPLE_SIZE) /* double buffer and frame length control */ -static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] +static PCM_DBL_BUF_SIZE_T pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2] PCM_DBL_BUF_BSS MEM_ALIGN_ATTR; static size_t pcm_dbl_buf_size[2]; static int pcm_dbl_buf_num = 0; @@ -205,11 +251,12 @@ bool pcm_play_dma_complete_callback(enum pcm_dma_status status, in one chunk */ static void update_frame_params(size_t size) { - int count = size / PCM_SAMPLE_SIZE; + /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */ + int count = (size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t))) / PCM_VOL_SAMPLE_SIZE; frame_count = (count + PCM_PLAY_DBL_BUF_SAMPLES - 1) / PCM_PLAY_DBL_BUF_SAMPLES; int perframe = count / frame_count; - frame_size = perframe * PCM_SAMPLE_SIZE; + frame_size = perframe * PCM_VOL_SAMPLE_SIZE; frame_frac = count - perframe * frame_count; frame_err = 0; } @@ -221,7 +268,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status) if (status != PCM_DMAST_STARTED) return status; - size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num]; + /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */ + size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num] / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t)); const void *addr = src_buf_addr + size; size = src_buf_rem - size; @@ -237,7 +285,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status) if (size != 0) { - size = frame_size; + /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */ + size = frame_size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t)); if ((frame_err += frame_frac) >= frame_count) { @@ -247,7 +296,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status) } pcm_dbl_buf_num ^= 1; - pcm_dbl_buf_size[pcm_dbl_buf_num] = size; + /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */ + pcm_dbl_buf_size[pcm_dbl_buf_num] = size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t)); pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size); return PCM_DMAST_OK; @@ -265,6 +315,7 @@ static void start_pcm(bool reframe) if (reframe) update_frame_params(src_buf_rem); + pcm_play_dma_status_callback(PCM_DMAST_STARTED); pcm_play_dma_status_callback(PCM_DMAST_STARTED); @@ -274,7 +325,8 @@ static void start_pcm(bool reframe) void pcm_play_dma_start_int(const void *addr, size_t size) { src_buf_addr = addr; - src_buf_rem = size; + /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */ + src_buf_rem = size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t)); start_pcm(true); } @@ -285,23 +337,6 @@ void pcm_play_dma_stop_int(void) src_buf_rem = 0; } -/* Return playing buffer from the source buffer */ -const void * pcm_play_dma_get_peak_buffer_int(int *count) -{ - const void *addr = src_buf_addr; - size_t size = src_buf_rem; - const void *addr2 = src_buf_addr; - - if (addr == addr2 && size) - { - *count = size / PCM_SAMPLE_SIZE; - return addr; - } - - *count = 0; - return NULL; -} - #endif /* PCM_SW_VOLUME_UNBUFFERED */ @@ -312,13 +347,32 @@ static uint32_t pcm_centibels_to_factor(int volume) { if (volume == PCM_MUTE_LEVEL) return 0; /* mute */ - +#if defined(HAVE_SWVOL_32) + /* + * 32-bit software volume taken from pcm-alsa.c + */ + volume += 48; /* -42dB .. 0dB => 5dB .. 48dB */ + /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0 + * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */ + int vol_shift = volume / 3; + int r = volume % 3; + int32_t dig_vol_mult; + if(r == 0) + dig_vol_mult = 1 << vol_shift; + else if(r == 1) + dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 2); + else + dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 1); + return dig_vol_mult; +#else /* standard software volume */ /* Centibels -> fixedpoint */ return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS), PCM_SW_VOLUME_FRACBITS); +#endif /* HAVE_SWVOL_32 */ } + /** Public functions **/ /* Produce final pcm scale factor */ diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index a05e0aeb68..95763dc950 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c @@ -24,9 +24,7 @@ #include "kernel.h" #include "thread.h" #include "debug.h" -#if !defined(DX50) && !defined(DX90) #include "adc.h" -#endif #include "string.h" #include "storage.h" #include "power.h" @@ -54,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; @@ -85,43 +79,12 @@ void handle_auto_poweroff(void); static int poweroff_timeout = 0; static long last_event_tick = 0; -#if (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) == PERCENTAGE_MEASURE -#ifdef SIMULATOR -int _battery_level(void) { return -1; } -int _battery_voltage(void); -extern const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11]; -extern const unsigned short percent_to_volt_charge[11]; +#if BATTERY_CAPACITY_INC > 0 +static int battery_capacity = BATTERY_CAPACITY_DEFAULT; #else -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 -#elif (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE) == VOLTAGE_MEASURE -int _battery_level(void) { return -1; } -/* - * 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; -#elif (CONFIG_BATTERY_MEASURE == 0) -int _battery_voltage(void) { return -1; } -int _battery_level(void) { return -1; } - -const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11]; -const unsigned short percent_to_volt_charge[11]; +# define battery_capacity BATTERY_CAPACITY_DEFAULT #endif -#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) -static int powermgmt_est_runningtime_min; -int _battery_time(void) { return powermgmt_est_runningtime_min; } -#endif - -/* default value, mAh */ -static int battery_capacity = BATTERY_CAPACITY_DEFAULT; - #if BATTERY_TYPES_COUNT > 1 static int battery_type = 0; #else @@ -133,109 +96,215 @@ unsigned short power_history[POWER_HISTORY_LEN] = {0}; #if (CONFIG_CPU == JZ4732) || (CONFIG_CPU == JZ4760B) || \ (CONFIG_CPU == X1000) || (CONFIG_PLATFORM & PLATFORM_HOSTED) -static char power_stack[DEFAULT_STACK_SIZE + POWERMGMT_DEBUG_STACK]; +static char power_stack[DEFAULT_STACK_SIZE]; #else -static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK]; +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 */ -#ifdef CURRENT_NORMAL /*only used if we have run current*/ -static int runcurrent(void); +#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) +int _battery_time(void) { return -1; } +#else +static int time_now; /* Cached to avoid polling too often */ #endif -void battery_read_info(int *voltage, int *level) -{ - int millivolts = _battery_voltage(); - int percent; +#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 (voltage) - *voltage = millivolts; +#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 - if (level) { - percent = voltage_to_battery_level(millivolts); - if (percent < 0) - percent = _battery_level(); - *level = percent; - } -} +#if !(CONFIG_BATTERY_MEASURE & CURRENT_MEASURE) +int _battery_current(void) { return -1; } +#else +static int current_avg, current_now; +#endif -#if BATTERY_TYPES_COUNT > 1 -void set_battery_type(int type) +/* 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) { - if (type != battery_type) { - if ((unsigned)type >= BATTERY_TYPES_COUNT) - type = 0; +#ifdef HAVE_BATTERY_SWITCH + if ((power_input_status() & POWER_INPUT_BATTERY) == 0) + return -1; +#endif - battery_type = type; - battery_status_update(); /* recalculate the battery status */ - } + return percent_now; } -#endif -#ifdef BATTERY_CAPACITY_MIN -void set_battery_capacity(int capacity) +/* 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 (capacity > BATTERY_CAPACITY_MAX) - capacity = BATTERY_CAPACITY_MAX; - if (capacity < BATTERY_CAPACITY_MIN) - capacity = BATTERY_CAPACITY_MIN; - - battery_capacity = capacity; - - battery_status_update(); /* recalculate the battery status */ -} +#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) == 0) +#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE + return current_now; +#elif defined(CURRENT_NORMAL) + int current = CURRENT_NORMAL; -#ifndef CURRENT_NORMAL /* no estimation without current */ - return -1; +#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 + +#if defined(HAVE_SPDIF_POWER) && defined(CURRENT_SPDIF_OUT) + if (spdif_powered()) + current += CURRENT_SPDIF_OUT; #endif - if (battery_capacity <= 0) /* nor without capacity */ - return -1; +#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 - return _battery_time(); } -/* 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 +} + +/* 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++; + } } -/* look into the percent_to_volt_* table and get a realistic battery 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]) { @@ -257,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; } @@ -278,102 +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 /*don't try to estimate run or charge - time without normal current defined*/ - /* calculate estimated remaining running time */ + 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 */ - powermgmt_est_runningtime_min = (100 - level)*battery_capacity*60 - / 100 / (CURRENT_MAX_CHG - runcurrent()); - } - 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; - /* discharging: remaining running time */ - if (level > 0 && (millivolt > percent_to_volt_discharge[battery_type][0] - || millivolt < 0)) { - /* linear extrapolation */ - powermgmt_est_runningtime_min = (level + battery_percent)*60 - * battery_capacity / 200 / runcurrent(); + /* 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; } - if (0 > powermgmt_est_runningtime_min) { - powermgmt_est_runningtime_min = 0; + + 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); } + + /* 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 /*check that we have a current defined in a config file*/ - -/* - * Estimate how much current we are drawing just to run. - */ -static int runcurrent(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 (voltage) + *voltage = millivolts; -#if defined(HAVE_BACKLIGHT) - if (backlight_get_current_timeout() == 0) /* LED always on */ - current += CURRENT_BACKLIGHT; + 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_RECORDING) && defined(CURRENT_RECORD) - if (audio_status() & AUDIO_STATUS_RECORD) - current += CURRENT_RECORD; -#endif +#if BATTERY_TYPES_COUNT > 1 +void set_battery_type(int type) +{ + if(type < 0 || type > BATTERY_TYPES_COUNT) + type = 0; -#ifdef HAVE_SPDIF_POWER - if (spdif_powered()) - current += CURRENT_SPDIF_OUT; + if (type != battery_type) { + battery_type = type; + battery_status_update(); /* recalculate the battery status */ + } +} #endif -#ifdef HAVE_REMOTE_LCD - if (remote_detect()) - current += CURRENT_REMOTE; -#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 defined(HAVE_ATA_POWER_OFF) && defined(CURRENT_ATA) - if (ide_powered()) - current += CURRENT_ATA; + if (capacity != battery_capacity) { + battery_capacity = capacity; + battery_status_update(); /* recalculate the battery status */ + } +} #endif -#endif /* BOOTLOADER */ - - return current; +int get_battery_capacity(void) +{ + return battery_capacity; } -#endif /* CURRENT_NORMAL */ +/* 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 +} /* Check to see whether or not we've received an alarm in the last second */ #ifdef HAVE_RTC_ALARM @@ -387,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 } @@ -407,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 */ @@ -543,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 @@ -667,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(); /* @@ -694,10 +741,18 @@ static void power_thread(void) /* Delay reading the first battery level */ #ifdef MROBE_100 while (_battery_voltage() > 4200) /* gives false readings initially */ + { #elif defined(DX50) || defined(DX90) while (_battery_voltage() < 1) /* can give false readings initially */ -#endif { +#elif defined(EROS_QN) || defined(FIIO_M3K) || defined(SHANLING_Q1) + + /* wait until the first battery read is ready */ + while (_battery_voltage() <= 0) + { +#else + { +#endif sleep(HZ/100); } @@ -711,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(); @@ -762,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(); @@ -802,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) @@ -817,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) { @@ -838,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) @@ -856,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); @@ -893,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 3496545cac..a3e6d5c2b9 100644 --- a/firmware/rolo.c +++ b/firmware/rolo.c @@ -40,25 +40,20 @@ #include "loader_strerror.h" #if defined(MI4_FORMAT) -#include "crc32-mi4.h" #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 @@ -134,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 @@ -243,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/screendump.c b/firmware/screendump.c index 1acaaafba6..7d09b0cd5e 100644 --- a/firmware/screendump.c +++ b/firmware/screendump.c @@ -102,7 +102,7 @@ static void (*screen_dump_hook)(int fh) = NULL; void screen_dump(void) { int fd, y; - char filename[32]; + char filename[MAX_PATH]; fb_data *src; #if LCD_DEPTH == 1 @@ -306,7 +306,7 @@ static const unsigned char rbmpheader[] = void remote_screen_dump(void) { int fd, y; - char filename[32]; + char filename[MAX_PATH]; fb_remote_data *src; #if LCD_REMOTE_DEPTH == 1 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/system.c b/firmware/system.c index 537e901b05..e2fdff0e59 100644 --- a/firmware/system.c +++ b/firmware/system.c @@ -89,6 +89,7 @@ char * cpu_boost_log_getlog_next(void) void cpu_boost_(bool on_off, char* location, int line) { + int item = cpu_boost_calls_count; if (!cpu_boost_lock()) return; @@ -98,12 +99,13 @@ void cpu_boost_(bool on_off, char* location, int line) cpu_boost_calls_count--; if (cpu_boost_calls_count < 0) cpu_boost_calls_count = 0; + item += cpu_boost_first; } if (cpu_boost_calls_count < MAX_BOOST_LOG) { int message = (cpu_boost_first+cpu_boost_calls_count)%MAX_BOOST_LOG; - snprintf(cpu_boost_calls[message], MAX_PATH, - "%c %s:%d",on_off?'B':'U',location,line); + snprintf(cpu_boost_calls[message], MAX_PATH,"%d.) %c %d %s:%d", + item,on_off?'B':'U',boost_counter,location,line); cpu_boost_calls_count++; } #else diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c index e4bb39b406..691ccaa028 100644 --- a/firmware/target/arm/as3525/audio-as3525.c +++ b/firmware/target/arm/as3525/audio-as3525.c @@ -67,6 +67,7 @@ void audio_input_mux(int source, unsigned flags) { default: /* playback - no recording */ source = AUDIO_SRC_PLAYBACK; + /*fallthrough*/ case AUDIO_SRC_PLAYBACK: if (source != last_source) { diff --git a/firmware/target/arm/as3525/debug-as3525.c b/firmware/target/arm/as3525/debug-as3525.c index c4c4a7f4dc..4848f0e5c5 100644 --- a/firmware/target/arm/as3525/debug-as3525.c +++ b/firmware/target/arm/as3525/debug-as3525.c @@ -288,14 +288,19 @@ static bool dbg_btn(bool *done, int *x) { case DEBUG_CANCEL: *done = true; + /*fallthrough*/ case DEBUG_NEXT: cont = false; + /*fallthrough*/ case DEBUG_LEFT_JUSTIFY: (*x) = 0; sleep(HZ/5); break; case DEBUG_LEFT_SCROLL: (*x)--; + break; + default: + break; } } lcd_clear_display(); @@ -361,7 +366,7 @@ bool dbg_hw_info(void) calc_freq(CLK_IDE)/D_MHZ); lcd_putsf(x, line++, "%s:%3dMHz %3dMHz", "DBOP", AS3525_DBOP_FREQ/D_MHZ, calc_freq(CLK_DBOP)/D_MHZ); - lcd_putsf(x, line++, "%s:%3dMHz %3dMHz", "I2C ", AS3525_I2C_FREQ/D_KHZ, + lcd_putsf(x, line++, "%s:%3dkHz %3dkHz", "I2C ", AS3525_I2C_FREQ/D_KHZ, calc_freq(CLK_I2C)/D_KHZ); lcd_putsf(x, line++, "I2SI: %s %3dMHz", (CGU_AUDIO & (1<<23)) ? "on " : "off" , calc_freq(CLK_I2SI)/D_MHZ); @@ -390,7 +395,7 @@ bool dbg_hw_info(void) lcd_putsf(x, line++, "%s:%3dMHz %3dMHz", "SSP ", AS3525_SSP_FREQ/D_MHZ, calc_freq(CLK_SSP)/D_MHZ); else - lcd_putsf(x, line++, "SSP :%3dMHz %3dKHz", AS3525_SSP_FREQ/D_MHZ, + lcd_putsf(x, line++, "SSP :%3dMHz %3dkHz", AS3525_SSP_FREQ/D_MHZ, calc_freq(CLK_SSP)/D_KHZ); #endif /* CONFIG_CPU == AS3525 */ lcd_putsf(x, line++, "USB : %3dMHz", calc_freq(CLK_USB)/D_MHZ); @@ -610,4 +615,4 @@ int get_cpu_voltage_setting(void) return value; } -#endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */
\ No newline at end of file +#endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */ diff --git a/firmware/target/arm/as3525/fmradio-i2c-as3525.c b/firmware/target/arm/as3525/fmradio-i2c-as3525.c index 5b629f5ad4..7f6cb8366b 100644 --- a/firmware/target/arm/as3525/fmradio-i2c-as3525.c +++ b/firmware/target/arm/as3525/fmradio-i2c-as3525.c @@ -185,6 +185,11 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count) } #ifdef HAVE_RDS_CAP +/* On the Sansa Clip Zip, the tuner interrupt line is routed to the SoC so we + * can use to detect when a RDS packet is ready. On the Clip+, we have to + * regularly poll. */ + +#if !(CONFIG_RDS & RDS_CFG_POLL) /* Low-level RDS Support */ static struct semaphore rds_sema; static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)]; @@ -231,4 +236,5 @@ void si4700_rds_init(void) create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); } +#endif /* !(CONFIG_RDS & RDS_CFG_POLL) */ #endif /* HAVE_RDS_CAP */ 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/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c index dfbda388be..e480140497 100644 --- a/firmware/target/arm/as3525/pcm-as3525.c +++ b/firmware/target/arm/as3525/pcm-as3525.c @@ -200,18 +200,6 @@ void pcm_dma_apply_settings(void) 0x01ffffff); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - int oldstatus = disable_irq_save(); - size_t addr = DMAC_CH_SRC_ADDR(0); - size_t start_addr = (size_t)dma_start_addr; - size_t start_size = dma_start_size; - restore_interrupt(oldstatus); - - *count = (start_size - addr + start_addr) >> 2; - return (void*)AS3525_UNCACHED_ADDR(addr); -} - #ifdef HAVE_PCM_DMA_ADDRESS void * pcm_dma_addr(void *addr) { 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/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c index 83ccb55f79..0450c8b410 100644 --- a/firmware/target/arm/as3525/system-as3525.c +++ b/firmware/target/arm/as3525/system-as3525.c @@ -180,7 +180,7 @@ void INT_GPIOA(void) void button_gpioa_isr(void); button_gpioa_isr(); #endif -#ifdef HAVE_RDS_CAP +#if defined(HAVE_RDS_CAP) && !(CONFIG_RDS & RDS_CFG_POLL) void tuner_isr(void); tuner_isr(); #endif diff --git a/firmware/target/arm/as3525/usb-as3525.c b/firmware/target/arm/as3525/usb-as3525.c index d798d4da83..3f636256a8 100644 --- a/firmware/target/arm/as3525/usb-as3525.c +++ b/firmware/target/arm/as3525/usb-as3525.c @@ -53,12 +53,21 @@ static int usb_status = USB_EXTRACTED; void usb_enable(bool on) { #if defined(HAVE_USBSTACK) + static int boosted = 0; if (on){ - cpu_boost(1); + if (boosted == 0) + { + cpu_boost(1); + boosted = 1; + } usb_core_init(); } else { usb_core_exit(); - cpu_boost(0); + if(boosted == 1) + { + cpu_boost(0); + boosted = 0; + } } #else (void)on; diff --git a/firmware/target/arm/as3525/usb-drv-as3525.c b/firmware/target/arm/as3525/usb-drv-as3525.c index 01fae5f0ab..d0875ed48c 100644 --- a/firmware/target/arm/as3525/usb-drv-as3525.c +++ b/firmware/target/arm/as3525/usb-drv-as3525.c @@ -412,12 +412,12 @@ void usb_drv_cancel_all_transfers(void) restore_irq(flags); } -int usb_drv_recv(int ep, void *ptr, int len) +int usb_drv_recv_nonblocking(int ep, void *ptr, int len) { struct usb_dev_dma_desc *uc_desc = endpoints[ep][1].uc_desc; ep &= 0x7f; - logf("usb_drv_recv(%d,%x,%d)\n", ep, (int)ptr, len); + logf("usb_drv_recv_nonblocking(%d,%x,%d)\n", ep, (int)ptr, len); if (len > USB_DMA_DESC_RXTX_BYTES) panicf("usb_recv: len=%d > %d", len, USB_DMA_DESC_RXTX_BYTES); @@ -655,7 +655,7 @@ static void handle_out_ep(int ep) req->wIndex, req->wLength); - usb_core_control_request(&req_copy); + usb_core_legacy_control_request(&req_copy); setup_desc_init(setup_desc); ep_sts &= ~USB_EP_STAT_SETUP_RCVD; @@ -674,7 +674,8 @@ static void handle_out_ep(int ep) * The usb_storage buffer is 63KB, but Linux sends 120KB. * We get the first part, but upon re-enabling receive dma we * get a 'buffer not available' error from the hardware, since - * we haven't gotten the next usb_drv_recv() from the stack yet. + * we haven't gotten the next usb_drv_recv_nonblocking() from + * the stack yet. * It seems the NAK bit is ignored here and the HW tries to dma * the incoming data anyway. * In theory I think the BNA error should be recoverable, but @@ -759,7 +760,7 @@ void INT_USB_FUNC(void) got_set_configuration = 1; set_config.wValue = USB_DEV_STS & USB_DEV_STS_MASK_CFG; - usb_core_control_request(&set_config); + usb_core_legacy_control_request(&set_config); intr &= ~USB_DEV_INTR_SET_CONFIG; } if (intr & USB_DEV_INTR_EARLY_SUSPEND) {/* idle >3ms detected */ 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/pcm-imx233.c b/firmware/target/arm/imx233/pcm-imx233.c index c06120e4aa..1588d2c874 100644 --- a/firmware/target/arm/imx233/pcm-imx233.c +++ b/firmware/target/arm/imx233/pcm-imx233.c @@ -42,7 +42,6 @@ __ENSURE_STRUCT_CACHE_FRIENDLY(struct pcm_dma_command_t) static int dac_locked = 0; static struct pcm_dma_command_t dac_dma; -static bool dac_freezed = false; static const void *dac_buf; /* current buffer */ static size_t dac_size; /* remaining size */ @@ -175,17 +174,6 @@ void pcm_dma_apply_settings(void) pcm_play_unlock(); } -const void *pcm_play_dma_get_peak_buffer(int *count) -{ - if(!dac_freezed) - imx233_dma_freeze_channel(APB_AUDIO_DAC, true); - struct imx233_dma_info_t info = imx233_dma_get_info(APB_AUDIO_DAC, DMA_INFO_AHB_BYTES | DMA_INFO_BAR); - if(!dac_freezed) - imx233_dma_freeze_channel(APB_AUDIO_DAC, false); - *count = info.ahb_bytes; - return (void *)info.bar; -} - /* * Recording */ diff --git a/firmware/target/arm/imx233/samsung-ypz5/backlight-target.h b/firmware/target/arm/imx233/samsung-ypz5/backlight-target.h deleted file mode 100644 index e26f83811d..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/backlight-target.h +++ /dev/null @@ -1,28 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * - * Copyright (C) 2013 by Lorenzo Miori - * - * 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 BACKLIGHT_TARGET_H -#define BACKLIGHT_TARGET_H - -bool backlight_hw_init(void); -void backlight_hw_on(void); -void backlight_hw_off(void); -void backlight_hw_brightness(int brightness); - -#endif /* BACKLIGHT_TARGET_H */ diff --git a/firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c deleted file mode 100644 index b02c3a1d16..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c +++ /dev/null @@ -1,149 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * - * Copyright (C) 2013 by Lorenzo Miori - * - * 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 "system.h" -#include "lcd.h" -#include "backlight.h" -#include "backlight-target.h" -#include "pinctrl-imx233.h" - -/** - * AAT3151 Backlight Controller - */ - -/* Timings */ -#define TIME_OFF 500 -#define TIME_LOW 50 -#define TIME_HI 50 -#define TIME_LAT 500 - -/* Number of raising edges to select the particular register */ -#define D1_D4_CURRENT_E 17 -#define D1_D3_CURRENT_E 18 -#define D4_CURRENT_E 19 -#define MAX_CURRENT_E 20 -#define LOW_CURRENT_E 21 - -/* The actual register address / number */ -#define D1_D4_CURRENT 1 -#define D1_D3_CURRENT 2 -#define D4_CURRENT 3 -#define MAX_CURRENT 4 -#define LOW_CURRENT 5 - -/* Valid values for LOW_CURRENT register */ -#define MAX_CURRENT_20 1 -#define MAX_CURRENT_30 2 -#define MAX_CURRENT_15 3 -#define MAX_CURRENT_LOW_CURRENT 4 - -static int current_level = -1; - -static void create_raising_edges(int num) -{ - while (num--) - { - /* Setting a register takes a sufficient small amount of time, - * in the order of 50 ns. Thus the necessary 2 delays TIME_LOW/TIME_HI - * are not strictly necessary */ - imx233_pinctrl_set_gpio(3, 13, false); - imx233_pinctrl_set_gpio(3, 13, true); - } -} - -static void aat3151_write(int addr, int data) -{ - create_raising_edges(16 + addr); - udelay(TIME_LAT); - create_raising_edges(data); - udelay(TIME_LAT); -} - -void backlight_hw_brightness(int level) -{ - /* Don't try to reset backlight if not necessary - * Moreover this helps to avoid flickering when - * being in some screens like USB mode and - * pressing some keys / touchpad... - */ - if (current_level == level) return; - - /* Check for limits and adjust in case */ - level = MIN(MAX_BRIGHTNESS_SETTING, MAX(0, level)); - - if (level == 0) - { - /* Set pin low for a sufficient time, puts the device into low-power consumption state - * In other words backlight goes off - */ - imx233_pinctrl_set_gpio(3, 13, false); - udelay(TIME_OFF); - } - else - { - if (level > 3) { - /* This enables 16 levels of backlight */ - aat3151_write(MAX_CURRENT, MAX_CURRENT_15); - /* Set the value according Table 1 in datasheet - * For MAX_CURRENT_15, the scale is from 0 mA to 15 mA in 16 steps - */ - aat3151_write(D1_D3_CURRENT, 19 - level); - } - else { - /* This enables other 4 levels of backlight */ - aat3151_write(MAX_CURRENT, MAX_CURRENT_LOW_CURRENT); - /* Set the value according Table 1 in datasheet - * For LOW_CURRENT, there is no "real" scale. We have scattered values. - * We are interested in the last 3 -> 0.5 mA; 1 mA; 2 mA - */ - aat3151_write(LOW_CURRENT, 13 + level); - } - } - current_level = level; -} - -bool backlight_hw_init(void) -{ - imx233_pinctrl_acquire(3, 13, "backlight"); - imx233_pinctrl_set_function(3, 13, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_set_drive(3, 13, PINCTRL_DRIVE_4mA); - imx233_pinctrl_enable_gpio(3, 13, true); - imx233_pinctrl_set_gpio(3, 13, false); - return true; -} - -void backlight_hw_on(void) -{ -#ifdef HAVE_LCD_ENABLE - lcd_enable(true); /* power on lcd + visible display */ -#endif - /* restore the previous backlight level */ - backlight_hw_brightness(backlight_brightness); -} - -void backlight_hw_off(void) -{ - /* there is no real on/off but we can set to 0 brightness */ - backlight_hw_brightness(0); -#ifdef HAVE_LCD_ENABLE - lcd_enable(false); /* power off visible display */ -#endif -} diff --git a/firmware/target/arm/imx233/samsung-ypz5/button-target.h b/firmware/target/arm/imx233/samsung-ypz5/button-target.h deleted file mode 100644 index 2f94f5fb31..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/button-target.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2013 by Lorenzo Miori - * - * 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 _BUTTON_TARGET_H_ -#define _BUTTON_TARGET_H_ - -#include <stdbool.h> - -#define HAS_BUTTON_HOLD -#define IMX233_BUTTON_LRADC_CHANNEL 0 -#define IMX233_BUTTON_LRADC_HOLD_DET BLH_GPIO -#define BLH_GPIO_BANK 0 -#define BLH_GPIO_PIN 13 - -#define IMX233_BUTTON_LRADC_CHANNEL 0 - -/* Main unit's buttons */ -#define BUTTON_POWER 0x00000001 -#define BUTTON_VOL_UP 0x00000002 -#define BUTTON_VOL_DOWN 0x00000004 -/* Directional buttons by touchpad */ -#define BUTTON_LEFT 0x00000008 -#define BUTTON_UP 0x00000010 -#define BUTTON_RIGHT 0x00000020 -#define BUTTON_DOWN 0x00000040 -#define BUTTON_SELECT 0x00000080 -#define BUTTON_BACK 0x00000100 -#define BUTTON_REW 0x00000200 -#define BUTTON_FF 0x00000400 - - -#define BUTTON_MAIN (BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_POWER | BUTTON_LEFT | \ - BUTTON_UP | BUTTON_RIGHT | BUTTON_DOWN | BUTTON_SELECT | \ - BUTTON_BACK | BUTTON_REW | BUTTON_FF) - -/* Software power-off */ -#define POWEROFF_BUTTON BUTTON_POWER -#define POWEROFF_COUNT 10 - -bool button_debug_screen(void); - -#endif /* _BUTTON_TARGET_H_ */ diff --git a/firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c deleted file mode 100644 index 91fe059c31..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c +++ /dev/null @@ -1,273 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * - * Copyright (C) 2013 by Lorenzo Miori - * - * 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 "lcd.h" -#include "string.h" -#include "kernel.h" -#include "pinctrl-imx233.h" -#include "power-imx233.h" -#include "button-lradc-imx233.h" -#include "button-target.h" -#include "button-imx233.h" - -#ifndef BOOTLOADER -#include "touchscreen.h" -#include "touchscreen-imx233.h" -#include "button.h" -#include "font.h" -#include "action.h" -#endif - -#define CHAN 0 /* ADC channel for the buttons */ -#define I_VDDIO 0 /* Mock button to define the relative voltage to compute voltage from ADC steps */ - -struct imx233_button_map_t imx233_button_map[] = -{ - [I_VDDIO] = IMX233_BUTTON_(VDDIO, VDDIO(3760), "vddio"), /* we need VDDIO for relative */ - IMX233_BUTTON_(HOLD, GPIO(0, 13), "hold"), - IMX233_BUTTON(POWER, PSWITCH(1), "power"), - IMX233_BUTTON(SELECT, PSWITCH(3), "select"), - IMX233_BUTTON(VOL_UP, LRADC_REL(CHAN, 485, I_VDDIO), "vol up"), - IMX233_BUTTON(VOL_DOWN, LRADC_REL(CHAN, 975, I_VDDIO), "vol down"), - IMX233_BUTTON(BACK, LRADC_REL(CHAN, 1521, I_VDDIO), "back"), - IMX233_BUTTON(FF, LRADC_REL(CHAN, 2000, I_VDDIO), "ff"), - IMX233_BUTTON(REW, LRADC_REL(CHAN, 2480, I_VDDIO), "rew"), - IMX233_BUTTON_(END, END(), "") -}; - -#ifndef BOOTLOADER -static int last_x = 0; -static int last_y = 0; -static bool touching = false; -#endif /* BOOTLOADER */ - -#ifndef BOOTLOADER - -/* Touchpad extra pin initialization - * Strange facts: - * 1. In the fully working sample I have, it seems that pins - * must be all set to low - * 2. In the other sample without LCD, it seems (by measurement) that - * not all the pins are set to low! Actually, I still need to see if - * touchpad works in this other sample. -*/ -void touchpad_pin_setup(void) -{ - /* TX+ */ - imx233_pinctrl_acquire(0, 25, "touchpad X+ power low"); - imx233_pinctrl_set_function(0, 25, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_set_drive(0, 25, PINCTRL_DRIVE_4mA); - imx233_pinctrl_enable_gpio(0, 25, true); - - /* TY+ */ - imx233_pinctrl_acquire(0, 26, "touchpad Y+ power high"); - imx233_pinctrl_set_function(0, 26, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_set_drive(0, 26, PINCTRL_DRIVE_4mA); - imx233_pinctrl_enable_gpio(0, 26, true); - - /* TY- */ - imx233_pinctrl_acquire(1, 21, "touchpad Y- power low"); - imx233_pinctrl_set_function(1, 21, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_set_drive(1, 21, PINCTRL_DRIVE_4mA); - imx233_pinctrl_enable_gpio(1, 21, true); - - /* TX- */ - imx233_pinctrl_acquire(3, 15, "touchpad X- power high"); - imx233_pinctrl_set_function(3, 15, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_set_drive(3, 15, PINCTRL_DRIVE_4mA); - imx233_pinctrl_enable_gpio(3, 15, true); - -} -#endif /* BOOTLOADER */ - -void button_init_device(void) -{ - /* init button subsystem */ - imx233_button_init(); -#ifndef BOOTLOADER - touchpad_pin_setup(); - /* Now that is powered up, proceed with touchpad initialization */ - imx233_touchscreen_init(); - imx233_touchscreen_enable(true); -#endif /* BOOTLOADER */ -} - -/* X, Y, RadiusX, RadiusY */ -#define TOUCH_UP 2400, 1050, 650, 250 -#define TOUCH_DOWN 2057, 3320, 500, 350 -#define TOUCH_LEFT 3581, 2297, 300, 350 -#define TOUCH_RIGHT 1000, 2100, 400, 700 -#define TOUCH_CENTER 2682, 2167, 335, 276 - -bool coord_in_radius(int x, int y, int cx, int cy, int rx, int ry) -{ - return ((x >= cx - rx && x <= cx + rx) && (y >= cy - ry && y <= cy + ry)); -} - -int button_read_device(void) -{ - int res = 0; - -#ifndef BOOTLOADER - /* handle the touchpad events */ - touching = imx233_touchscreen_get_touch(&last_x, &last_y); - if(touching) - { - if (coord_in_radius(last_x, last_y, TOUCH_LEFT)) - { - res |= BUTTON_LEFT; - } - else if (coord_in_radius(last_x, last_y, TOUCH_RIGHT)) - { - res |= BUTTON_RIGHT; - } - else if (coord_in_radius(last_x, last_y, TOUCH_DOWN)) - { - res |= BUTTON_DOWN; - } - else if (coord_in_radius(last_x, last_y, TOUCH_UP)) - { - res |= BUTTON_UP; - } - } -#endif /* BOOTLOADER */ - /* handle the generic events */ - return imx233_button_read(res); -} - -#ifndef BOOTLOADER - -#define MAX_ENTRIES 100 -#define VIEWPORT_HEIGHT 100 -#define VIEWPORT_WIDTH 100 -#define MAX_X 3700 -#define MAX_Y 3700 -#define ADAPT_TO_VIEWPORT(cx, cy, rx, ry) ((float)(cx) / MAX_X) * VIEWPORT_WIDTH, \ - ((float)(cy) / MAX_Y) * VIEWPORT_HEIGHT, \ - ((float)(rx) / MAX_X) * VIEWPORT_WIDTH, \ - ((float)(ry) / MAX_Y) * VIEWPORT_HEIGHT -static void draw_calibration_rect(int cx, int cy, int rx, int ry) -{ - if (coord_in_radius(last_x, last_y, cx, cy, rx, ry)) - lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0xff, 0xff), LCD_BLACK); - else - lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0, 0), LCD_BLACK); - lcd_drawrect(ADAPT_TO_VIEWPORT(cx - rx, cy - ry, 2 * rx, 2 * ry)); -} - -bool button_debug_screen(void) -{ - int last = 0; - struct point_t - { - int x; - int y; - }; - struct point_t last_entries[MAX_ENTRIES]; - struct viewport report_vp; - - memset(&report_vp, 0, sizeof(report_vp)); - report_vp.x = (LCD_WIDTH - VIEWPORT_WIDTH) / 2; - report_vp.y = (LCD_HEIGHT - VIEWPORT_HEIGHT) / 2; - report_vp.width = VIEWPORT_WIDTH; - report_vp.height = VIEWPORT_HEIGHT; - - lcd_setfont(FONT_SYSFIXED); - lcd_clear_display(); - - while(1) - { - int button = get_action(CONTEXT_STD, HZ / 10); - switch(button) - { - case ACTION_STD_OK: - case ACTION_STD_MENU: - lcd_set_viewport(NULL); - lcd_setfont(FONT_UI); - lcd_clear_display(); - return true; - case ACTION_STD_CANCEL: - lcd_set_viewport(NULL); - lcd_setfont(FONT_UI); - lcd_clear_display(); - return false; - } - - lcd_set_viewport(NULL); - lcd_putsf(0, 1, "(%d,%d) %s", last_x, last_y, touching ? "touching!" : ""); - lcd_putsf(0, 0, "Type %s", imx233_pinctrl_get_gpio(0, 31) ? "CAP" : "REG"); - lcd_set_viewport(&report_vp); - lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0, 0, 0xff), LCD_BLACK); - lcd_drawrect(0, 0, 100, 100); - float percent_x = ((float)(last_x) / MAX_X); - float percent_y = ((float)(last_y) / MAX_Y); - if (touching) - { - lcd_set_viewport(NULL); - if (last < MAX_ENTRIES) - { - last_entries[last].x = last_x; - last_entries[last].y = last_y; - last++; - lcd_putsf(0, 17, "Recording: %d captures left", MAX_ENTRIES - last); - } - else - { - int min_x = 9999; - int min_y = 9999; - int max_x = -1; - int max_y = -1; - int median_x = 0; - int median_y = 0; - for (int i = 0; i < MAX_ENTRIES; i++) - { - min_x = MIN(min_x, last_entries[i].x); - min_y = MIN(min_y, last_entries[i].y); - max_x = MAX(max_x, last_entries[i].x); - max_y = MAX(max_y, last_entries[i].y); - median_x += last_entries[i].x; - median_y += last_entries[i].y; - } - median_x /= MAX_ENTRIES; - median_y /= MAX_ENTRIES; - lcd_putsf(0, 17, "center(%d,%d)", median_x, median_y); - lcd_putsf(0, 18, "radius(%d,%d)", median_x / 2, median_y / 2); - } - lcd_set_viewport(&report_vp); - lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0x8c, 0), LCD_BLACK); - lcd_fillrect(VIEWPORT_WIDTH * percent_x, VIEWPORT_HEIGHT * percent_y, 2, 2); - } - - /* Draw current calibration settings */ - lcd_set_viewport(&report_vp); - draw_calibration_rect(TOUCH_UP); - draw_calibration_rect(TOUCH_DOWN); - draw_calibration_rect(TOUCH_CENTER); - draw_calibration_rect(TOUCH_LEFT); - draw_calibration_rect(TOUCH_RIGHT); - - lcd_update(); - yield(); - } - - return true; -} -#endif diff --git a/firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c deleted file mode 100644 index 99fe0b2aef..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c +++ /dev/null @@ -1,295 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * - * Copyright (c) 2013 by Lorenzo Miori - * - * 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 <sys/types.h> /* off_t */ -#include <string.h> -#include "cpu.h" -#include "system.h" -#include "backlight-target.h" -#include "lcd.h" -#include "lcdif-imx233.h" -#include "clkctrl-imx233.h" -#include "pinctrl-imx233.h" -#include "dcp-imx233.h" -#include "logf.h" -#ifndef BOOTLOADER -#include "button.h" -#include "font.h" -#include "action.h" -#endif -#include "dma-imx233.h" -#include "kernel.h" - -#include "regs/lcdif.h" - -/** - * NOTE - * We don't know exact LCD models nor we have datasheets for them - * Register function are partly guessed from the values, others are guessed from other LCD - * drivers and others have been confirmed studying their values - */ - -static enum lcd_type_t -{ - LCD_TYPE_ZERO = 0, - LCD_TYPE_ONE = 1 -} lcd_type = LCD_TYPE_ZERO; - -static void lcd_write_reg(uint16_t reg, uint16_t data) -{ - imx233_lcdif_pio_send(false, 1, ®); - if(reg != 0x22) - imx233_lcdif_pio_send(true, 1, &data); -} - -/* - * The two LCD types require different initialization sequences - */ -void lcd_init_seq(void) -{ - switch (lcd_type) - { - case LCD_TYPE_ZERO: - { - lcd_write_reg(0x11, 0x1f1e); - lcd_write_reg(0x38, 0xf0f); - lcd_write_reg(0x12, 0x1101); - lcd_write_reg(0x13, 0x808); - lcd_write_reg(0x14, 0x3119); - lcd_write_reg(0x10, 0x1a10); - udelay(0xc350); - lcd_write_reg(0x13, 0x83b); - udelay(0x30d40); - lcd_write_reg(1, 0x90c); /* Display mode */ - lcd_write_reg(2, 0x200); - lcd_write_reg(3, 0x1030); - lcd_write_reg(7, 5); - lcd_write_reg(8, 0x503); - lcd_write_reg(11, 0); - lcd_write_reg(12, 0); - /* Gamma control */ - lcd_write_reg(0x30, 0x606); - lcd_write_reg(0x31, 0x606); - lcd_write_reg(0x32, 0x305); - lcd_write_reg(0x33, 2); - lcd_write_reg(0x34, 0x503); - lcd_write_reg(0x35, 0x606); - lcd_write_reg(0x36, 0x606); - lcd_write_reg(0x37, 0x200); - - lcd_write_reg(0x11, 0x1f1e); - lcd_write_reg(0x38, 0xf0f); - /* Set initial LCD limits and RAM settings */ - lcd_write_reg(0x40, 0); //BPP ? - lcd_write_reg(0x42, 0x9f00); - lcd_write_reg(0x43, 0); - lcd_write_reg(0x44, 0x7f00); /* Horizontal initial refresh zone [0 - 127] */ - lcd_write_reg(0x45, 0x9f00); /* Vertical initial refresh zone [0 - 159] */ - - lcd_write_reg(14, 0x13); - lcd_write_reg(0xa9, 0x14); - lcd_write_reg(0xa7, 0x30); - lcd_write_reg(0xa8, 0x124); - lcd_write_reg(0x6f, 0x1d00); - lcd_write_reg(0x70, 3); - lcd_write_reg(7, 1); - lcd_write_reg(0x10, 0x1a10); - udelay(0x9c40); - lcd_write_reg(7, 0x21); - lcd_write_reg(7, 0x23); - udelay(0x9c40); - lcd_write_reg(7, 0x37); /* Seems to be "power on" */ - break; - } - case LCD_TYPE_ONE: - { - lcd_write_reg(0, 1); - udelay(0x2710); - lcd_write_reg(0x11, 0x171b); - lcd_write_reg(0x12, 0); - lcd_write_reg(0x13, 0x80d); - lcd_write_reg(0x14, 0x18); - lcd_write_reg(0x10, 0x1a10); - udelay(0xc350); - lcd_write_reg(0x13, 0x81d); - udelay(0xc350); - lcd_write_reg(1, 0x90c); /* Display mode */ - lcd_write_reg(2, 0x200); - lcd_write_reg(3, 0x1030); - lcd_write_reg(7, 5); - lcd_write_reg(8, 0x30a); - lcd_write_reg(11, 4); - lcd_write_reg(12, 0); - /* Gamma control */ - lcd_write_reg(0x30, 0x300); - lcd_write_reg(0x31, 0); - lcd_write_reg(0x32, 0); - lcd_write_reg(0x33, 0x404); - lcd_write_reg(0x34, 0x707); - lcd_write_reg(0x35, 0x700); - lcd_write_reg(0x36, 0x703); - lcd_write_reg(0x37, 4); - - lcd_write_reg(0x38, 0); - /* Set initial LCD limits and RAM settings */ - lcd_write_reg(0x40, 0); - lcd_write_reg(0x42, 0x9f00); /* LCD Display Start Address Register 0 */ - lcd_write_reg(0x43, 0); /* LCD Display Start Address Register 1 */ - lcd_write_reg(0x44, 0x7f00); /* Horizontal initial refresh zone [0 - 127] */ - lcd_write_reg(0x45, 0x9f00); /* Vertical initial refresh zone [0 - 159] */ - - lcd_write_reg(7, 1); - udelay(0x2710); - lcd_write_reg(7, 0x21); - lcd_write_reg(7, 0x23); - udelay(0x2710); - lcd_write_reg(7, 0x1037); - udelay(0x2710); - lcd_write_reg(7, 0x35); - lcd_write_reg(7, 0x36); - lcd_write_reg(7, 0x37); - udelay(10000); - break; - } - default: - break; - } -} - -static void send_update_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h) -{ - /* Set horizontal refresh zone */ - lcd_write_reg(0x44, (x | (y + w - 1) << 0x8)); - /* Set vertical refresh zone */ - lcd_write_reg(0x45, (y | (y + h - 1) << 0x8)); - lcd_write_reg(0x21, x | y << 8); - /* Set register index to 0x22 to write screen data. 0 is mock value */ - lcd_write_reg(0x22, 0); -} - -static void setup_lcd_pins(void) -{ - imx233_lcdif_setup_system_pins(16); - /* lcd_rd */ - imx233_pinctrl_acquire(0, 9, "lcd rd"); - imx233_pinctrl_set_function(0, 9, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_set_gpio(0, 9, false); - /* - * This pin is important to know the LCD type - * There are two types that require two different initialization sequences - */ - /* lcd_tp */ - imx233_pinctrl_acquire(3, 12, "lcd type"); - imx233_pinctrl_set_function(3, 12, PINCTRL_FUNCTION_GPIO); - imx233_pinctrl_enable_gpio(3, 12, false); - /* Sense LCD Type */ - lcd_type = imx233_pinctrl_get_gpio(3, 12) ? LCD_TYPE_ONE : LCD_TYPE_ZERO; -} - -static void setup_parameters(void) -{ - imx233_lcdif_init(); - imx233_lcdif_enable(true); - imx233_lcdif_set_word_length(16); - imx233_lcdif_set_data_swizzle(false); - imx233_lcdif_set_timings(2, 1, 1, 1); - BF_WR(LCDIF_CTRL, MODE86_V(8080_MODE)); - - imx233_lcdif_reset_lcd(true); - udelay(50); - imx233_lcdif_reset_lcd(false); - udelay(10); - imx233_lcdif_reset_lcd(true); -} - -void lcd_init_device(void) -{ - /* Setup interface pins */ - setup_lcd_pins(); - /* Set LCD parameters */ - setup_parameters(); - /* Send initialization sequence to LCD */ - lcd_init_seq(); -} - -struct lcdif_cmd_t -{ - struct apb_dma_command_t dma; - uint32_t ctrl0; - uint32_t pad[4]; -} __attribute__((packed)) CACHEALIGN_ATTR; - -struct lcdif_cmd_t lcdif_dma; -void lcd_update(void) -{ - unsigned size = LCD_WIDTH * LCD_HEIGHT * sizeof(fb_data); - - send_update_rect(0,0,LCD_WIDTH,LCD_HEIGHT); - /* We can safely do the transfer in a single shot, since 160 * 128 * 2 < 65k, - * the maximum transfer size! - */ - lcdif_dma.dma.cmd |= BF_OR(APB_CHx_CMD, CMDWORDS(1), XFER_COUNT(size), COMMAND(2)); - lcdif_dma.ctrl0 = HW_LCDIF_CTRL & ~BM_LCDIF_CTRL_COUNT; - lcdif_dma.ctrl0 |= BF_OR(LCDIF_CTRL, COUNT(size/2), DATA_SELECT(1)); - lcdif_dma.dma.buffer = FBADDR(0,0); - lcdif_dma.dma.cmd |= BM_APB_CHx_CMD_SEMAPHORE; - - imx233_dma_start_command(APB_LCDIF, &lcdif_dma.dma); - imx233_dma_wait_completion(APB_LCDIF, HZ); -} - -void lcd_update_rect(int x, int y, int w, int h) -{ - (void)x; - (void)y; - (void)w; - (void)h; - lcd_update(); -} - -#ifndef BOOTLOADER -bool lcd_debug_screen(void) -{ - lcd_setfont(FONT_SYSFIXED); - - while(1) - { - int button = get_action(CONTEXT_STD, HZ / 10); - switch(button) - { - case ACTION_STD_NEXT: - case ACTION_STD_PREV: - case ACTION_STD_OK: - case ACTION_STD_MENU: - lcd_setfont(FONT_UI); - return true; - case ACTION_STD_CANCEL: - lcd_setfont(FONT_UI); - return false; - } - - lcd_clear_display(); - lcd_putsf(0, 0, "LCD type: %d", lcd_type); - lcd_update(); - yield(); - } - - return true; -} -#endif diff --git a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h b/firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h deleted file mode 100644 index 5d32bc5e9c..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2013 by Amaury Pouly - * - * 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 POWERMGMT_TARGET_H -#define POWERMGMT_TARGET_H - -#include "config.h" -#include "powermgmt-imx233.h" - -#define IMX233_CHARGE_CURRENT 100 -#define IMX233_STOP_CURRENT 10 -#define IMX233_TOPOFF_TIMEOUT (30 * 60 * HZ) -#define IMX233_CHARGING_TIMEOUT (4 * 3600 * HZ) -#define IMX233_DIE_TEMP_HIGH 71 -#define IMX233_DIE_TEMP_LOW 56 -#define IMX233_BATT_TEMP_SENSOR 0 -#define IMX233_BATT_TEMP_HIGH 1100 -#define IMX233_BATT_TEMP_LOW 220 - -#endif /* POWERMGMT_TARGET_H */ diff --git a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c deleted file mode 100644 index 9d9ed7de99..0000000000 --- a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2013 by Amaury Pouly - * - * 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 "powermgmt-target.h" -#include "power-imx233.h" - - -const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = -{ - 3400 -}; - -const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = -{ - 3300 -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ -const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = -{ - /* Sansa Fuze+ Li Ion 600mAH figured from discharge curve */ - { 3100, 3650, 3720, 3750, 3780, 3820, 3880, 4000, 4040, 4125, 4230 }, -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ -const unsigned short percent_to_volt_charge[11] = -{ - /* Sansa Fuze+ Li Ion 600mAH figured from charge curve */ - 3480, 3790, 3845, 3880, 3900, 3935, 4005, 4070, 4150, 4250, 4335 -}; 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/imx233/touchscreen-imx233.c b/firmware/target/arm/imx233/touchscreen-imx233.c index f98dc9b738..561b2c750a 100644 --- a/firmware/target/arm/imx233/touchscreen-imx233.c +++ b/firmware/target/arm/imx233/touchscreen-imx233.c @@ -23,9 +23,6 @@ #include "kernel.h" #include "touchscreen-imx233.h" #include "stdlib.h" -#ifdef SAMSUNG_YPZ5 -#include "pinctrl-imx233.h" -#endif /* Description: * the driver basically has 2 modes: @@ -82,24 +79,6 @@ static void touch_channel_irq(int chan) process(); } -#ifdef SAMSUNG_YPZ5 -/* On this target we need to manually setup pulldown pins, - * using specific GPIO lines - */ -static void pulldown_setup(bool xminus_enable, bool yminus_enable, - bool xplus_enable, bool yplus_enable) -{ - /* TX+ */ - imx233_pinctrl_set_gpio(0, 25, xplus_enable); - /* TX- */ - imx233_pinctrl_set_gpio(3, 15, xminus_enable); - /* TY+ */ - imx233_pinctrl_set_gpio(0, 26, yplus_enable); - /* TY- */ - imx233_pinctrl_set_gpio(1, 21, yminus_enable); -} -#endif - static void kick_measure(bool pull_x, bool pull_y, bool detect, int src) { #if IMX233_SUBTARGET < 3700 @@ -116,9 +95,6 @@ static void kick_measure(bool pull_x, bool pull_y, bool detect, int src) imx233_icoll_enable_interrupt(INT_SRC_LRADC_CHx(touch_chan), true); imx233_lradc_enable_channel_irq(touch_chan, true); /* setup measurement: x- pull down and x+ pull up */ -#ifdef SAMSUNG_YPZ5 - pulldown_setup(pull_x, pull_y, pull_x, pull_y); -#endif imx233_lradc_setup_touch(pull_x, pull_y, pull_x, pull_y, detect); imx233_lradc_enable_touch_detect_irq(false); imx233_lradc_enable_channel_irq(touch_chan, true); @@ -138,9 +114,6 @@ static void enter_state(enum touch_state_t state) switch(state) { case TOUCH_STATE_WAIT: -#ifdef SAMSUNG_YPZ5 - pulldown_setup(false, false, false, false); -#endif imx233_lradc_setup_touch(false, false, false, false, true); imx233_lradc_enable_touch_detect_irq(true); break; 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/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c index 13dff6ecb5..955301b4da 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c @@ -224,34 +224,6 @@ void pcm_play_dma_stop(void) play_stop_pcm(); } -/* Return a pointer to the samples and the number of them in *count */ -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - static unsigned long dsa NOCACHEBSS_ATTR; - unsigned long addr; - long offs, size; - int oldstatus; - - /* read burst dma source address register in channel context */ - sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1); - - oldstatus = disable_irq_save(); - addr = dsa; - offs = addr - (unsigned long)dma_play_bd.buf_addr; - size = dma_play_bd.mode.count; - restore_irq(oldstatus); - - /* Be addresses are coherent (no buffer change during read) */ - if (offs >= 0 && offs < size) - { - *count = (size - offs) >> 2; - return (void *)((addr + 2) & ~3); - } - - *count = 0; - return NULL; -} - void * pcm_dma_addr(void *addr) { return (void *)addr_virt_to_phys((unsigned long)addr); 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/backlight-mini1g_mini2g.c b/firmware/target/arm/ipod/backlight-mini1g_mini2g.c index 8dd7ef87bd..143be32e91 100644 --- a/firmware/target/arm/ipod/backlight-mini1g_mini2g.c +++ b/firmware/target/arm/ipod/backlight-mini1g_mini2g.c @@ -43,3 +43,10 @@ void backlight_hw_off(void) { GPIO_CLEAR_BITWISE(GPIOB_OUTPUT_VAL, 0x08); } + +bool backlight_hw_init(void) +{ + GPIO_SET_BITWISE(GPIOB_ENABLE, 0x0c); /* B02 and B03 enable */ + GPIO_SET_BITWISE(GPIOB_OUTPUT_VAL, 0x08); /* B03 = 1 */ + return true; +} diff --git a/firmware/target/arm/ipod/backlight-target.h b/firmware/target/arm/ipod/backlight-target.h index 299a1cb6e7..133663d279 100644 --- a/firmware/target/arm/ipod/backlight-target.h +++ b/firmware/target/arm/ipod/backlight-target.h @@ -57,7 +57,7 @@ void backlight_hw_brightness(int val); #elif defined(IPOD_MINI) || defined(IPOD_MINI2G) -#define backlight_hw_init() true +bool backlight_hw_init(void); void backlight_hw_on(void); void backlight_hw_off(void); 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 ff550b14d6..45044bc664 100644 --- a/firmware/target/arm/pcm-telechips.c +++ b/firmware/target/arm/pcm-telechips.c @@ -62,14 +62,6 @@ struct dma_data dma_play_data SHAREDBSS_ATTR = .state = 0 }; -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - unsigned long addr = (unsigned long)dma_play_data.p; - size_t cnt = dma_play_data.size; - *count = cnt >> 2; - return (void *)((addr + 2) & ~3); -} - void pcm_play_dma_init(void) { DAVC = 0x0; /* Digital Volume = max */ @@ -226,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? */ @@ -259,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" @@ -276,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 31b0065888..f609e3ff7a 100644 --- a/firmware/target/arm/pp/mi4-loader.c +++ b/firmware/target/arm/pp/mi4-loader.c @@ -27,100 +27,12 @@ #include "config.h" #include "mi4-loader.h" #include "loader_strerror.h" -#include "crc32-mi4.h" +#include "crc32.h" #include "file.h" #if defined(HAVE_BOOTDATA) -#include "system.h" -#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; -} +#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]; @@ -293,7 +205,7 @@ static int load_mi4_filename(unsigned char* buf, return EREAD_IMAGE_FAILED; /* Check CRC32 to see if we have a valid file */ - sum = chksum_crc32 (buf, mi4header.mi4size - MI4_HEADER_SIZE); + sum = crc_32r (buf, mi4header.mi4size - MI4_HEADER_SIZE, 0); if(sum != mi4header.crc32) return EBAD_CHKSUM; diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c index 8434123a92..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)); } @@ -512,19 +514,6 @@ void pcm_play_dma_postinit(void) audiohw_postinit(); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - unsigned long addr, size; - - int status = disable_fiq_save(); - addr = dma_play_data.addr; - size = dma_play_data.size; - restore_fiq(status); - - *count = size >> 2; - return (void *)((addr + 2) & ~3); -} - /**************************************************************************** ** Recording DMA transfer **/ diff --git a/firmware/target/arm/pp/system-pp502x.c b/firmware/target/arm/pp/system-pp502x.c index d6dfad1b24..fb6fe28d91 100644 --- a/firmware/target/arm/pp/system-pp502x.c +++ b/firmware/target/arm/pp/system-pp502x.c @@ -559,8 +559,6 @@ void system_init(void) /* to be done */ #elif defined (MROBE_100) /* to be done */ -#elif defined (TATUNG_TPJ1022) - /* to be done */ #elif defined(PBELL_VIBE500) /* reset all allowed devices */ DEV_RS = 0x3ffffef8; diff --git a/firmware/target/arm/pp/system-target.h b/firmware/target/arm/pp/system-target.h index 1e947195bd..c26e9e1d06 100644 --- a/firmware/target/arm/pp/system-target.h +++ b/firmware/target/arm/pp/system-target.h @@ -175,13 +175,7 @@ extern unsigned char probed_ramsize; int battery_default_capacity(void); #endif - #ifdef BOOTLOADER -#if defined(TATUNG_TPJ1022) - /* Some targets don't like yielding in the bootloader - force - * yield() to return without a context switch. */ -#define YIELD_KERNEL_HOOK() true -#endif #ifdef HAVE_BOOTLOADER_USB_MODE void tick_stop(void); diff --git a/firmware/target/arm/pp/usb-fw-pp502x.c b/firmware/target/arm/pp/usb-fw-pp502x.c index b0fcfb83a4..a4d8e28a06 100644 --- a/firmware/target/arm/pp/usb-fw-pp502x.c +++ b/firmware/target/arm/pp/usb-fw-pp502x.c @@ -81,12 +81,6 @@ #define USB_GPIO_MASK 0x10 #define USB_GPIO_VAL 0x10 -#elif defined(TATUNG_TPJ1022) - /* GPIO ? bit ? is usb detect (dummy value)*/ -#define USB_GPIO GPIOD -#define USB_GPIO_MASK 0x10 -#define USB_GPIO_VAL 0x10 - #elif defined(PBELL_VIBE500) /* GPIO L bit 3 is usb detect */ #define USB_GPIO GPIOL 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/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c index c0b4c45805..26e3ad87ba 100644 --- a/firmware/target/arm/rk27xx/pcm-rk27xx.c +++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c @@ -263,18 +263,6 @@ void INT_HDMA(void) } } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - uint32_t addr; - - int old = disable_irq_save(); - addr = HDMA_CSRC0; - *count = ((HDMA_CCNT0 & 0xffff)<<2); - restore_interrupt(old); - - return (void*)addr; -} - /**************************************************************************** ** Recording DMA transfer **/ diff --git a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c index 057ecf6ebc..77860b5494 100644 --- a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c +++ b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c @@ -117,7 +117,7 @@ static void setup_received(void) setup_data[1] = SETUP2; /* pass setup data to the upper layer */ - usb_core_control_request((struct usb_ctrlrequest*)setup_data); + usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_data); } static int max_pkt_size(struct endpoint_t *endp) @@ -355,7 +355,7 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) } /* Setup a receive transfer. (non blocking) */ -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { logf("udc: recv(%x)", endpoint); struct endpoint_t *ep; diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c index d784180e77..e75c3fe7c6 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c @@ -219,11 +219,3 @@ void fiq_handler(void) pcm_play_dma_status_callback(PCM_DMAST_STARTED); } - -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - unsigned long addr = DCSRC2; - int cnt = DSTAT2; - *count = (cnt & 0xFFFFF) >> 1; - return (void *)((addr + 2) & ~3); -} 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/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c index 9574522586..999a53e3f9 100644 --- a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c +++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c @@ -259,11 +259,3 @@ void fiq_handler(void) pcm_play_dma_status_callback(PCM_DMAST_STARTED); } - -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - unsigned long addr = DCSRC2; - int cnt = DSTAT2; - *count = (cnt & 0xFFFFF) >> 1; - return (void *)((addr + 2) & ~3); -} 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/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c index 050e14451d..c7b6ed9a03 100644 --- a/firmware/target/arm/s5l8700/pcm-s5l8700.c +++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c @@ -262,12 +262,6 @@ void pcm_dma_apply_settings(void) pcm_dma_set_freq(pcm_fsel); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - *count = DMACTCNT0 >> 1; - return (void *)(((DMACADDR0 + 2) & ~3) | 0x40000000); -} - #ifdef HAVE_PCM_DMA_ADDRESS void * pcm_dma_addr(void *addr) { 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/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c index 33fe637770..d4570f3a7f 100644 --- a/firmware/target/arm/s5l8702/pcm-s5l8702.c +++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c @@ -216,13 +216,6 @@ void pcm_play_dma_postinit(void) audiohw_postinit(); } -const void* pcm_play_dma_get_peak_buffer(int *count) -{ - void *addr = dmac_ch_get_info(&dma_play_ch, count, NULL); - *count >>= 2; /* bytes to samples */ - return addr; /* aligned to dest burst */ -} - #ifdef HAVE_PCM_DMA_ADDRESS void * pcm_dma_addr(void *addr) { 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/tatung/app.lds b/firmware/target/arm/tatung/app.lds deleted file mode 100644 index 856cc52bb0..0000000000 --- a/firmware/target/arm/tatung/app.lds +++ /dev/null @@ -1,2 +0,0 @@ -#include "config.h" -#include "../pp/app-pp.lds" diff --git a/firmware/target/arm/tatung/boot.lds b/firmware/target/arm/tatung/boot.lds deleted file mode 100644 index 33b826bec8..0000000000 --- a/firmware/target/arm/tatung/boot.lds +++ /dev/null @@ -1,2 +0,0 @@ -#include "config.h" -#include "../pp/boot-pp.lds" diff --git a/firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c b/firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c deleted file mode 100644 index bf29ba092e..0000000000 --- a/firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c +++ /dev/null @@ -1,85 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006 by Barry Wardell - * - * 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 "cpu.h" -#include "lcd.h" -#include "kernel.h" -#include "system.h" - -/*** hardware configuration ***/ - -void lcd_set_contrast(int val) -{ - /* TODO: Implement lcd_set_contrast() */ - (void)val; -} - -void lcd_set_invert_display(bool yesno) -{ - /* TODO: Implement lcd_set_invert_display() */ - (void)yesno; -} - -/* turn the display upside down (call lcd_update() afterwards) */ -void lcd_set_flip(bool yesno) -{ - /* TODO: Implement lcd_set_flip() */ - (void)yesno; -} - -/* LCD init */ -void lcd_init_device(void) -{ - -} - -/*** update functions ***/ - -/* Performance function to blit a YUV bitmap directly to the LCD */ -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) -{ - (void)src; - (void)src_x; - (void)src_y; - (void)stride; - (void)x; - (void)y; - (void)width; - (void)height; -} - -/* Update a fraction of the display. */ -void lcd_update_rect(int x0, int y0, int width, int height) -{ - (void)x0; - (void)y0; - (void)width; - (void)height; -} - -/* Update the display. - This must be called after all other LCD functions that change the display. */ -void lcd_update(void) -{ - lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); -} diff --git a/firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c b/firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c deleted file mode 100644 index cc91012fcd..0000000000 --- a/firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese - * Revisions copyright (C) 2005 by Gerald Van Baren - * - * 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 "adc.h" -#include "powermgmt.h" - -/* FIXME: All voltages copied from H10 according to the battery type given - * in config-tpj1022.h at the time of splitting. This probably needs changing - * if that port ever gets up to speed. */ - -const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = -{ - 3760 -}; - -const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = -{ - 3650 -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ -const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = -{ - { 3760, 3800, 3850, 3870, 3900, 3950, 4020, 4070, 4110, 4180, 4240 } -}; - -#if CONFIG_CHARGING -/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ -const unsigned short percent_to_volt_charge[11] = -{ - 3990, 4030, 4060, 4080, 4100, 4120, 4150, 4180, 4220, 4260, 4310 -}; -#endif /* CONFIG_CHARGING */ - -#define BATTERY_SCALE_FACTOR 6000 -/* full-scale ADC readout (2^10) in millivolt */ - -/* Returns battery voltage from ADC [millivolts] */ -int _battery_voltage(void) -{ - return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; -} diff --git a/firmware/target/arm/tms320dm320/app.lds b/firmware/target/arm/tms320dm320/app.lds index 6ceb512d21..b35ace1ce6 100644 --- a/firmware/target/arm/tms320dm320/app.lds +++ b/firmware/target/arm/tms320dm320/app.lds @@ -14,22 +14,29 @@ STARTUP(target/arm/tms320dm320/crt0.o) #define LCD_NATIVE_HEIGHT LCD_HEIGHT #endif -#define LCD_FUDGE LCD_NATIVE_WIDTH%32 - -#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2) - /* must be 16Kb (0x4000) aligned */ #define TTB_SIZE 0x4000 +#define DRAMSIZE (MEMORYSIZE * 0x100000) +#define DRAMORIG CONFIG_SDRAM_START + + +#ifdef MROBE_500 +#define LCD_FUDGE LCD_NATIVE_WIDTH%32 +#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2) /* Give this some memory to allow it to align to the MMU boundary. * Note that since there are two buffers (YUV/RGB) it calculates the approximate * memory needed in steps of 1 Meg. */ -#define LCD_TTB_AREA 0x100000*((LCD_BUFFER_SIZE>>19)+1) - -#define DRAMSIZE (MEMORYSIZE * 0x100000) +#define LCD_TTB_AREA (0x100000*((LCD_BUFFER_SIZE>>19)+1)) +#else +#define LCD_BUFFER_SIZE (LCD_NATIVE_WIDTH*LCD_NATIVE_HEIGHT*2) +#define LCD_TTB_AREA (TTB_SIZE + LCD_BUFFER_SIZE) +#endif -#define DRAMORIG CONFIG_SDRAM_START +/* End of the audio buffer, where the codec buffer starts */ +#define ENDAUDIOADDR \ + (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE - LCD_TTB_AREA) #define FLASHORIG 0x00100000 #define FLASHSIZE 0x00800000 @@ -44,10 +51,6 @@ PRO_STACK_SIZE = 0x2000; IRQ_STACK_SIZE = 0x600; FIQ_STACK_SIZE = 0x400; -/* End of the audio buffer, where the codec buffer starts */ -#define ENDAUDIOADDR \ - (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE - LCD_TTB_AREA) - MEMORY { DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE @@ -105,7 +108,7 @@ SECTIONS *(.rodata*) } > DRAM - .data : + .data : { . = ALIGN(0x4); *(.data*) @@ -142,12 +145,12 @@ SECTIONS } > ITCM /* Program stack space */ - .pro_stack (NOLOAD): + .pro_stack (NOLOAD): { . = ALIGN(0x4); *(.stack) stackbegin = .; /* Variable for thread.c */ - _pro_stack_end = .; + _pro_stack_end = .; . += PRO_STACK_SIZE; _pro_stack_start = .; stackend = .; /* Variable for tread.c */ @@ -193,31 +196,48 @@ SECTIONS pluginbuf = .; . += PLUGIN_BUFFER_SIZE; } > DRAM - + _endsdram = .; +#ifdef MROBE_500 .ttbtable (NOLOAD) : { . = ALIGN (0x4000); _ttbstart = .; . += TTB_SIZE; } > DRAM - + /* The LCD buffer should be at the end of memory to protect against - * overflowing something else when the YUV blitter is fudging the screen + * overflowing something else when the YUV blitter is fudging the screen * size. */ - + .lcdbuffer (NOLOAD) : { _lcdbuf = .; . += LCD_BUFFER_SIZE; } > DRAM - + .lcdbuffer2 (NOLOAD) : { _lcdbuf2 = .; . += LCD_BUFFER_SIZE; } > DRAM +#else + .lcdbuffer (NOLOAD) : + { + . = ALIGN(32); + _lcdbuf = .; + . += LCD_BUFFER_SIZE; + } > DRAM + + /* Place TTB at the end of RAM to minimize alignment losses */ + .ttbtable (NOLOAD) : + { + . = ALIGN (0x4000); + _ttbstart = .; + . += TTB_SIZE; + } > DRAM +#endif } diff --git a/firmware/target/arm/tms320dm320/boot.lds b/firmware/target/arm/tms320dm320/boot.lds index 2db687d533..8b075f43df 100644 --- a/firmware/target/arm/tms320dm320/boot.lds +++ b/firmware/target/arm/tms320dm320/boot.lds @@ -14,22 +14,28 @@ STARTUP(target/arm/tms320dm320/crt0.o) #define LCD_NATIVE_HEIGHT LCD_HEIGHT #endif -#define LCD_FUDGE LCD_NATIVE_WIDTH%32 - -#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2) - /* must be 16Kb (0x4000) aligned */ #define TTB_SIZE (0x4000) +/* Bootloader only uses/knows about the upper 32 M */ +#define DRAMSIZE (MEMORYSIZE * 0x100000 / 2) +#define DRAMORIG CONFIG_SDRAM_START+DRAMSIZE + +#ifdef MROBE_500 +#define LCD_FUDGE LCD_NATIVE_WIDTH%32 +#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2) /* Give this some memory to allow it to align to the MMU boundary. * Note that since there are two buffers (YUV/RGB) it calculates the approximate * memory needed in steps of 1 Meg. */ -#define LCD_TTB_AREA 0x100000*((LCD_BUFFER_SIZE>>19)+1) - -/* Bootloader only uses/knows about the upper 32 M */ -#define DRAMORIG CONFIG_SDRAM_START+0x02000000 -#define DRAMSIZE (MEMORYSIZE * 0x80000) +#define LCD_TTB_AREA (0x100000*((LCD_BUFFER_SIZE>>19)+1)) +/* End of the audio buffer, where the codec buffer starts */ +#define TTB_BEGIN (DRAMORIG + DRAMSIZE - LCD_TTB_AREA) +#else +#define LCD_BUFFER_SIZE (LCD_NATIVE_WIDTH*LCD_NATIVE_HEIGHT*2) +#define LCD_TTB_AREA (TTB_SIZE + LCD_BUFFER_SIZE) +#define LCD_BEGIN (DRAMORIG + DRAMSIZE - LCD_TTB_AREA) +#endif #define IRAMORIG 0x00000000 #define IRAMSIZE 0x4000 @@ -59,9 +65,6 @@ PRO_STACK_SIZE = 0x2000; IRQ_STACK_SIZE = 0x400; FIQ_STACK_SIZE = 0x400; -/* End of the audio buffer, where the codec buffer starts */ -#define TTB_BEGIN (DRAMORIG + DRAMSIZE - LCD_TTB_AREA) - MEMORY { DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE @@ -148,7 +151,7 @@ SECTIONS } > IRAM AT> FLASH _iramcopy = LOADADDR(.iram); - + .ibss (NOLOAD) : { . = ALIGN(0x4); @@ -158,12 +161,12 @@ SECTIONS } > IRAM /* Program stack space */ - .pro_stack (NOLOAD): + .pro_stack (NOLOAD): { . = ALIGN(0x4); *(.stack) stackbegin = .; /* Variable for thread.c */ - _pro_stack_end = .; + _pro_stack_end = .; . += PRO_STACK_SIZE; _pro_stack_start = .; stackend = .; /* Variable for tread.c */ @@ -186,28 +189,45 @@ SECTIONS . += FIQ_STACK_SIZE; _fiq_stack_start = .; } > IRAM - + +#ifdef MROBE_500 .ttbtable TTB_BEGIN (NOLOAD) : { . = ALIGN (0x4000); _ttbstart = .; . += TTB_SIZE; } > DRAM - + /* The LCD buffer should be at the end of memory to protect against - * overflowing something else when the YUV blitter is fudging the screen + * overflowing something else when the YUV blitter is fudging the screen * size. */ - + .lcdbuffer (NOLOAD) : { _lcdbuf = .; . += LCD_BUFFER_SIZE; } > DRAM - + .lcdbuffer2 (NOLOAD) : { _lcdbuf2 = .; . += LCD_BUFFER_SIZE; } > DRAM +#else + .lcdbuffer LCD_BEGIN (NOLOAD) : + { + . = ALIGN(32); + _lcdbuf = .; + . += LCD_BUFFER_SIZE; + } > DRAM + + /* Place TTB at the end of RAM to minimize alignment losses */ + .ttbtable (NOLOAD) : + { + . = ALIGN (0x4000); + _ttbstart = .; + . += TTB_SIZE; + } > DRAM +#endif } 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/creative-zvm/pcm-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c index d5e30ad73b..fa1eb2481f 100644 --- a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c +++ b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c @@ -52,12 +52,6 @@ void pcm_play_dma_postinit(void) // dsp_wake(); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - (void) count; - return 0; -} - void pcm_dma_apply_settings(void) { audiohw_set_frequency(pcm_fsel); diff --git a/firmware/target/arm/tms320dm320/i2c-dm320.c b/firmware/target/arm/tms320dm320/i2c-dm320.c index 629dae394c..364d3b5c17 100644 --- a/firmware/target/arm/tms320dm320/i2c-dm320.c +++ b/firmware/target/arm/tms320dm320/i2c-dm320.c @@ -294,4 +294,12 @@ int i2c_read_bytes(unsigned short address, unsigned short reg, return i2c_read_data(dm320_i2c_bus, address, reg, buf, count); } +int i2c_write_read_bytes(unsigned short address, + const unsigned char* buf_write, int count_write, + unsigned char* buf_read, int count_read) +{ + return i2c_write_read_data(dm320_i2c_bus, address, buf_write, count_write, + buf_read, count_read); +} + #endif diff --git a/firmware/target/arm/tms320dm320/i2c-dm320.h b/firmware/target/arm/tms320dm320/i2c-dm320.h index 7dfc19f046..89a8e6f1a4 100644 --- a/firmware/target/arm/tms320dm320/i2c-dm320.h +++ b/firmware/target/arm/tms320dm320/i2c-dm320.h @@ -24,13 +24,16 @@ #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); #ifdef HAVE_SOFTWARE_I2C int i2c_read_bytes(unsigned short address, unsigned short reg, unsigned char* buf, int count); +int i2c_write_read_bytes(unsigned short address, + const unsigned char* buf_write, int count_write, + unsigned char* buf_read, int count_read); #endif #endif 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/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c index 5ce5a8f399..fda8615e5a 100644 --- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c +++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c @@ -46,22 +46,6 @@ void pcm_play_dma_postinit(void) audiohw_postinit(); } -/* Return the current location in the SDRAM to SARAM transfer along with the - * number of bytes read in the current buffer (count). There is latency with - * this method equivalent to ~ the size of the SARAM buffer since there is - * another buffer between your ears and this calculation, but this works for - * key clicks and an approximate peak meter. - */ -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - int cnt = DSP_(_sdem_level); - - unsigned long addr = (unsigned long) start + cnt; - - *count = (cnt & 0xFFFFF) >> 1; - return (void *)((addr + 2) & ~3); -} - void pcm_play_dma_init(void) { IO_INTC_IRQ0 = INTR_IRQ0_IMGBUF; diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c index 9d91093a02..6fd504a0f8 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c @@ -21,9 +21,13 @@ #include <stdio.h> #include "config.h" +#include "file.h" #include "system.h" +#include "time.h" #include "power.h" #include "kernel.h" +#include "panic.h" +/*#define LOGF_ENABLE*/ #include "logf.h" #include "avr-sansaconnect.h" #include "uart-target.h" @@ -48,38 +52,92 @@ #define dbgprintf(...) #endif -#define CMD_SYNC 0xAA -#define CMD_CLOSE 0xCC -#define CMD_LCM_POWER 0xC9 -#define LCM_POWER_OFF 0x00 -#define LCM_POWER_ON 0x01 -#define LCM_POWER_SLEEP 0x02 -#define LCM_POWER_WAKE 0x03 -#define LCM_REPOWER_ON 0x04 - -#define CMD_STATE 0xBB -#define CMD_VER 0xBC -#define CMD_WHEEL_EN 0xD0 -#define CMD_SET_INTCHRG 0xD1 -#define CMD_CODEC_RESET 0xD7 -#define CMD_AMP_ENABLE 0xCA -#define CMD_FILL 0xFF - -#define CMD_SYS_CTRL 0xDA -#define SYS_CTRL_POWEROFF 0x00 +#define AVR_DELAY_US 200 +#define AVR_MAX_RETRIES 10 + +#define CMD_SYNC 0xAA +#define CMD_CLOSE 0xCC +#define CMD_FILL 0xFF + +/* Actual command opcodes handled by AVR */ +#define CMD_STATE 0xBB +#define CMD_VER 0xBC +#define CMD_MONOTIME 0xBD +#define CMD_PGMWAKE 0xBF +#define CMD_HDQ_READ 0xC0 +#define CMD_HDQ_WRITE 0xC1 +#define CMD_HDQ_STATUS 0xC2 +#define CMD_GET_LAST_RESET_TYPE 0xC4 +#define CMD_UNKNOWN_C5 0xC5 +#define CMD_GET_BATTERY_TEMP 0xC8 +#define CMD_LCM_POWER 0xC9 +#define CMD_AMP_ENABLE 0xCA +#define CMD_WHEEL_EN 0xD0 +#define CMD_SET_INTCHRG 0xD1 +#define CMD_GET_INTCHRG 0xD2 +#define CMD_WIFI_PD 0xD3 +#define CMD_UNKNOWN_D4 0xD4 +#define CMD_UNKNOWN_D5 0xD5 +#define CMD_UNKNOWN_D6 0xD6 +#define CMD_CODEC_RESET 0xD7 +#define CMD_ADC_START 0xD8 +#define CMD_ADC_RESULT 0xD9 +#define CMD_SYS_CTRL 0xDA +#define CMD_SET_USBCHRG 0xE2 +#define CMD_GET_USBCHRG 0xE3 +#define CMD_MONORSTCNT 0xE4 + +/* CMD_LCM_POWER parameters */ +#define LCM_POWER_OFF 0x00 +#define LCM_POWER_ON 0x01 +#define LCM_POWER_SLEEP 0x02 +#define LCM_POWER_WAKE 0x03 +#define LCM_REPOWER_ON 0x04 + +/* CMD_SYS_CTRL parameters */ +#define SYS_CTRL_POWEROFF 0x00 +#define SYS_CTRL_RESET 0x01 +#define SYS_CTRL_SLEEP 0x02 +#define SYS_CTRL_DISABLE_WD 0x03 +#define SYS_CTRL_KICK_WD 0x04 +#define SYS_CTRL_EN_HDQ_THERM 0x05 +#define SYS_CTRL_EN_TS_THERM 0x06 +#define SYS_CTRL_FRESET 0x80 + +/* HDQ status codes */ +#define HDQ_STATUS_OK 0x00 +#define HDQ_STATUS_NOT_READY 0x01 +#define HDQ_STATUS_TIMEOUT 0x02 /* protects spi avr commands from concurrent access */ static struct mutex avr_mtx; +/* serializes hdq read/write and status retrieval */ +static struct mutex hdq_mtx; -/* buttons thread */ -#define BTN_INTERRUPT 1 +/* AVR thread events */ +#define INPUT_INTERRUPT 1 +#define MONOTIME_OFFSET_UPDATE 2 +#define POWER_OFF_REQUEST 3 static int btn = 0; static bool hold_switch; -#ifndef BOOTLOADER +static bool input_interrupt_pending; +/* AVR implements 32-bit counter incremented every second. + * The counter value cannot be modified to arbitrary value, + * so the epoch offset needs to be stored in a file. + */ +#define MONOTIME_OFFSET_FILE ROCKBOX_DIR "/monotime_offset.dat" +static uint32_t monotime_offset; +/* Buffer last read monotime value. Reading monotime takes + * atleast 1400 us so the tick counter is used together with + * last read monotime value to return current time. + */ +static bool monotime_available; +static uint32_t monotime_value; +static unsigned long monotime_value_tick; + static long avr_stack[DEFAULT_STACK_SIZE/sizeof(long)]; static const char avr_thread_name[] = "avr"; -static struct semaphore avr_thread_trigger; -#endif +static struct event_queue avr_queue; /* OF bootloader will refuse to start software if low power is set * Bits 3, 4, 5, 6 and 7 are unknown. @@ -93,21 +151,20 @@ static uint8_t avr_battery_status; #define BATTERY_LEVEL_PERCENTAGE_MASK 0x7F static uint8_t avr_battery_level = 100; -static inline unsigned short be2short(unsigned char* buf) +static inline uint16_t be2short(uint8_t *buf) { - return (unsigned short)((buf[0] << 8) | buf[1]); + return (uint16_t)((buf[0] << 8) | buf[1]); } #define BUTTON_DIRECT_MASK (BUTTON_LEFT | BUTTON_UP | BUTTON_RIGHT | BUTTON_DOWN | BUTTON_SELECT | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_NEXT | BUTTON_PREV) -#ifndef BOOTLOADER -static void handle_wheel(unsigned char wheel) +static void handle_wheel(uint8_t wheel) { static int key = 0; - static unsigned char velocity = 0; - static unsigned long wheel_delta = 1ul << 24; - static unsigned char wheel_prev = 0; - static long nextbacklight_hw_on = 0; + static uint8_t velocity = 0; + static uint32_t wheel_delta = 1ul << 24; + static uint8_t wheel_prev = 0; + static unsigned long nextbacklight_hw_on = 0; static int prev_key = -1; static int prev_key_post = 0; @@ -166,49 +223,45 @@ static void handle_wheel(unsigned char wheel) prev_key = key; } -#endif -/* buf must be 11-byte array of byte (reply from avr_hid_get_state() */ -static void parse_button_state(unsigned char *buf) +/* buf must be 8-byte state array (reply from avr_hid_get_state() */ +static void parse_button_state(uint8_t *state) { - unsigned short main_btns_state = be2short(&buf[4]); + uint16_t main_btns_state = be2short(&state[2]); #ifdef BUTTON_DEBUG - unsigned short main_btns_changed = be2short(&buf[6]); + uint16_t main_btns_changed = be2short(&state[4]); #endif /* make sure other bits doesn't conflict with our "free bits" buttons */ main_btns_state &= BUTTON_DIRECT_MASK; - if (buf[3] & 0x01) /* is power button pressed? */ + if (state[1] & 0x01) /* is power button pressed? */ { main_btns_state |= BUTTON_POWER; } btn = main_btns_state; -#ifndef BOOTLOADER /* check if stored hold_switch state changed (prevents lost changes) */ - if ((buf[3] & 0x20) /* hold change notification */ || - (hold_switch != ((buf[3] & 0x02) >> 1))) + if ((state[1] & 0x20) /* hold change notification */ || + (hold_switch != ((state[1] & 0x02) >> 1))) { -#endif - hold_switch = (buf[3] & 0x02) >> 1; + hold_switch = (state[1] & 0x02) >> 1; #ifdef BUTTON_DEBUG dbgprintf("HOLD changed (%d)", hold_switch); #endif #ifndef BOOTLOADER backlight_hold_changed(hold_switch); - } #endif -#ifndef BOOTLOADER - if ((hold_switch == false) && (buf[3] & 0x80)) /* scrollwheel change */ + } + + if ((hold_switch == false) && (state[1] & 0x80)) /* scrollwheel change */ { - handle_wheel(buf[2]); + handle_wheel(state[0]); } -#endif #ifdef BUTTON_DEBUG - if (buf[3] & 0x10) /* power button change */ + if (state[1] & 0x10) /* power button change */ { /* power button state has changed */ main_btns_changed |= BUTTON_POWER; @@ -230,72 +283,232 @@ static void parse_button_state(unsigned char *buf) #endif } -static void spi_txrx(unsigned char *buf_tx, unsigned char *buf_rx, int n) +static bool avr_command_reads_data(uint8_t opcode) +{ + switch (opcode) + { + case CMD_STATE: + case CMD_VER: + case CMD_GET_LAST_RESET_TYPE: + case CMD_GET_INTCHRG: + case CMD_MONOTIME: + case CMD_UNKNOWN_C5: + case CMD_MONORSTCNT: + case CMD_HDQ_STATUS: + case CMD_GET_BATTERY_TEMP: + case CMD_GET_USBCHRG: + case CMD_ADC_RESULT: + return true; + default: + return false; + } +} + +static size_t avr_command_data_size(uint8_t opcode) { - int i; - unsigned short rxdata; + switch (opcode) + { + case CMD_STATE: return 8; + case CMD_VER: return 1; + case CMD_MONOTIME: return 4; + case CMD_PGMWAKE: return 4; + case CMD_HDQ_READ: return 1; + case CMD_HDQ_WRITE: return 2; + case CMD_HDQ_STATUS: return 2; + case CMD_GET_LAST_RESET_TYPE: return 1; + case CMD_UNKNOWN_C5: return 1; + case CMD_GET_BATTERY_TEMP: return 2; + case CMD_LCM_POWER: return 1; + case CMD_AMP_ENABLE: return 1; + case CMD_WHEEL_EN: return 1; + case CMD_SET_INTCHRG: return 1; + case CMD_GET_INTCHRG: return 1; + case CMD_WIFI_PD: return 1; + case CMD_UNKNOWN_D4: return 1; + case CMD_UNKNOWN_D5: return 2; + case CMD_UNKNOWN_D6: return 2; + case CMD_CODEC_RESET: return 0; + case CMD_ADC_START: return 1; + case CMD_ADC_RESULT: return 2; + case CMD_SYS_CTRL: return 1; + case CMD_SET_USBCHRG: return 1; + case CMD_GET_USBCHRG: return 1; + case CMD_MONORSTCNT: return 2; + default: + panicf("Invalid AVR opcode %02X", opcode); + return 0; + } +} + +static uint8_t spi_read_byte(void) +{ + uint16_t rxdata; + + do + { + rxdata = IO_SERIAL1_RX_DATA; + } + while (rxdata & (1<<8)); + + return rxdata & 0xFF; +} + +static void avr_hid_select(void) +{ + /* Drive GIO29 (AVR SS) low */ + IO_GIO_BITCLR1 = (1 << 13); +} + +static void avr_hid_release(void) +{ + /* Drive GIO29 (AVR SS) high */ + IO_GIO_BITSET1 = (1 << 13); +} + +static bool avr_run_command(uint8_t opcode, uint8_t *data, size_t data_length) +{ + bool success = true; + const bool is_read = avr_command_reads_data(opcode); + size_t i; + uint8_t rx; + + /* Verify command data size and also make sure command is valid */ + if (avr_command_data_size(opcode) != data_length) + { + panicf("AVR %02x invalid data length", opcode); + } mutex_lock(&avr_mtx); bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF1); IO_SERIAL1_TX_ENABLE = 0x0001; - for (i = 0; i<n; i++) - { - IO_SERIAL1_TX_DATA = buf_tx[i]; + avr_hid_select(); + udelay(10); - /* 100 us wait for AVR */ - udelay(100); + IO_SERIAL1_TX_DATA = CMD_SYNC; + spi_read_byte(); + /* Allow AVR to process CMD_SYNC */ + udelay(AVR_DELAY_US); - do - { - rxdata = IO_SERIAL1_RX_DATA; - } while (rxdata & (1<<8)); + IO_SERIAL1_TX_DATA = opcode; + rx = spi_read_byte(); + if (rx != CMD_SYNC) + { + /* AVR failed to register CMD_SYNC */ + success = false; + } + /* Allow AVR to process opcode */ + udelay(AVR_DELAY_US); - if (buf_rx != NULL) - buf_rx[i] = rxdata & 0xFF; + if (is_read) + { + for (i = 0; i < data_length; i++) + { + IO_SERIAL1_TX_DATA = CMD_FILL; + data[i] = spi_read_byte(); + udelay(AVR_DELAY_US); + } + } + else + { + for (i = 0; i < data_length; i++) + { + IO_SERIAL1_TX_DATA = data[i]; + spi_read_byte(); + udelay(AVR_DELAY_US); + } + } - /* 100 us wait to give AVR time to process data */ - udelay(100); + IO_SERIAL1_TX_DATA = CMD_CLOSE; + rx = spi_read_byte(); + udelay(AVR_DELAY_US); + if (is_read) + { + success = success && (rx == CMD_CLOSE); } + avr_hid_release(); + IO_SERIAL1_TX_ENABLE = 0; bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF1); mutex_unlock(&avr_mtx); + + return success; +} + +static bool avr_hid_get_state(void) +{ + uint8_t state[8]; + if (avr_run_command(CMD_STATE, state, sizeof(state))) + { + avr_battery_status = state[6]; + avr_battery_level = state[7]; + parse_button_state(state); + return true; + } + return false; } -void avr_hid_sync(void) +static bool avr_hid_sync(void) { - int i; - unsigned char prg[4] = {CMD_SYNC, CMD_VER, CMD_FILL, CMD_CLOSE}; + int retry; + for (retry = 0; retry < AVR_MAX_RETRIES; retry++) + { + if (avr_hid_get_state()) + { + return true; + } + mdelay(100); + } + /* TODO: Program HID as it appears to be not programmed. + * To do so, unfortunately, AVR firmware would have to be written + * from scratch as OF blob cannot be used due to licensing. + */ + return false; +} - /* Send SYNC three times */ - for (i = 0; i<3; i++) +static bool avr_execute_command(uint8_t opcode, uint8_t *data, size_t data_length) +{ + int retry; + for (retry = 0; retry < AVR_MAX_RETRIES; retry++) { - spi_txrx(prg, NULL, sizeof(prg)); + if (avr_run_command(opcode, data, data_length)) + { + return true; + } + + /* Resync and try again */ + avr_hid_sync(); } + return false; } void avr_hid_init(void) { /* setup alternate GIO functions: - GIO29 - SIF1 Enable (Directly connected to AVR's SS) GIO30 - SIF1 Clock GIO31 - SIF1 Data In GIO32 - SIF1 Data Out + Manually drive GIO29 output (directly connected to AVR's SS). */ - IO_GIO_FSEL2 = (IO_GIO_FSEL2 & 0x00FF) | 0xAA00; + IO_GIO_FSEL2 = (IO_GIO_FSEL2 & 0x00FF) | 0xA800; /* GIO29, GIO30 - outputs, GIO31 - input */ IO_GIO_DIR1 = (IO_GIO_DIR1 & ~((1 << 13) | (1 << 14))) | (1 << 15); /* GIO32 - output */ bitclr16(&IO_GIO_DIR2, (1 << 0)); + avr_hid_release(); - /* RATE = 219 (0xDB) -> 200 kHz */ + /* Master, MSB first, RATE = 219 (Bit rate = ARM clock / 2*(RATE + 1))) + * Boosted 148.5 MHz / 440 = 337.5 kHz + * Default 74.25 MHz / 440 = 168.75 kHz + */ IO_SERIAL1_MODE = 0x6DB; mutex_init(&avr_mtx); + mutex_init(&hdq_mtx); } int _battery_level(void) @@ -308,6 +521,17 @@ int _battery_level(void) return avr_battery_level & BATTERY_LEVEL_PERCENTAGE_MASK; } +int _battery_voltage(void) +{ + return avr_hid_hdq_read_short(HDQ_REG_VOLT); +} + +int _battery_time(void) +{ + /* HDQ_REG_TTE reads as 65535 when charging */ + return avr_hid_hdq_read_short(HDQ_REG_TTE); +} + unsigned int power_input_status(void) { if (avr_battery_status & BATTERY_STATUS_CHARGER_CONNECTED) @@ -322,92 +546,137 @@ bool charging_state(void) return (avr_battery_status & BATTERY_STATUS_CHARGING) != 0; } -static void avr_hid_get_state(void) +static int avr_hid_hdq_read_byte_internal(uint8_t address) { - static unsigned char cmd[11] = {CMD_SYNC, CMD_STATE, - CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, - CMD_CLOSE}; + uint8_t result[2]; - static unsigned char buf[11]; + if (!avr_execute_command(CMD_HDQ_READ, &address, sizeof(address))) + { + return -1; + } - /* In very unlikely case the command has to be repeated */ do { - spi_txrx(cmd, buf, sizeof(cmd)); + mdelay(10); + if (!avr_execute_command(CMD_HDQ_STATUS, result, sizeof(result))) + { + return -1; + } + } + while (result[0] == HDQ_STATUS_NOT_READY); + + if (result[0] != HDQ_STATUS_OK) + { + logf("HDQ read %d status %d", address, result[0]); + return -1; } - while ((buf[1] != CMD_SYNC) || (buf[10] != CMD_CLOSE)); - avr_battery_status = buf[8]; - avr_battery_level = buf[9]; - parse_button_state(buf); + return result[1]; } -static void avr_hid_enable_wheel(void) +int avr_hid_hdq_read_byte(uint8_t address) { - unsigned char wheel_en[4] = {CMD_SYNC, CMD_WHEEL_EN, 0x01, CMD_CLOSE}; + int retry; + int value = -1; + for (retry = 0; (retry < 3) && (value < 0); retry++) + { + mutex_lock(&hdq_mtx); + value = avr_hid_hdq_read_byte_internal(address); + mutex_unlock(&hdq_mtx); + } + return value; +} + +int avr_hid_hdq_read_short(uint8_t address) +{ + int old_hi = -1, old_lo = -1, hi = -2, lo = -2; + /* Keep reading until we read the same value twice. + * There's no atomic 16-bit value retrieval, so keep reading + * until we read the same value twice. HDQ registers update + * no more than once per 2.56 seconds so usually there will + * be 4 reads and sometimes 6 reads. + */ + while ((old_hi != hi) || (old_lo != lo)) + { + old_hi = hi; + old_lo = lo; + hi = avr_hid_hdq_read_byte(address + 1); + lo = avr_hid_hdq_read_byte(address); + } + if ((hi < 0) || (lo < 0)) + { + return -1; + } + return (hi << 8) | lo; +} - spi_txrx(wheel_en, NULL, sizeof(wheel_en)); +static void avr_hid_enable_wheel(void) +{ + uint8_t enable = 0x01; + avr_execute_command(CMD_WHEEL_EN, &enable, sizeof(enable)); } /* command that is sent by "hidtool -J 1" issued on every OF boot */ void avr_hid_enable_charger(void) { - unsigned char charger_en[4] = {CMD_SYNC, CMD_SET_INTCHRG, 0x01, CMD_CLOSE}; - - spi_txrx(charger_en, NULL, sizeof(charger_en)); + uint8_t enable = 0x01; + avr_execute_command(CMD_SET_INTCHRG, &enable, sizeof(enable)); } -void avr_hid_lcm_sleep(void) +void avr_hid_wifi_pd(int high) { - unsigned char lcm_sleep[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_SLEEP, CMD_CLOSE}; + uint8_t state = high ? 0x01 : 0x00; + avr_execute_command(CMD_WIFI_PD, &state, sizeof(state)); +} - spi_txrx(lcm_sleep, NULL, sizeof(lcm_sleep)); +static void avr_hid_lcm_power(uint8_t parameter) +{ + avr_execute_command(CMD_LCM_POWER, ¶meter, sizeof(parameter)); } +void avr_hid_lcm_sleep(void) +{ + avr_hid_lcm_power(LCM_POWER_SLEEP); +} void avr_hid_lcm_wake(void) { - unsigned char lcm_wake[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_WAKE, CMD_CLOSE}; - - spi_txrx(lcm_wake, NULL, sizeof(lcm_wake)); + avr_hid_lcm_power(LCM_POWER_WAKE); } void avr_hid_lcm_power_on(void) { - unsigned char lcm_power_on[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_ON, CMD_CLOSE}; - - spi_txrx(lcm_power_on, NULL, sizeof(lcm_power_on)); + avr_hid_lcm_power(LCM_POWER_ON); } void avr_hid_lcm_power_off(void) { - unsigned char lcm_power_off[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_OFF, CMD_CLOSE}; - - spi_txrx(lcm_power_off, NULL, sizeof(lcm_power_off)); + avr_hid_lcm_power(LCM_POWER_OFF); } void avr_hid_reset_codec(void) { - unsigned char codec_reset[4] = {CMD_SYNC, CMD_CODEC_RESET, CMD_CLOSE, CMD_FILL}; - - spi_txrx(codec_reset, NULL, sizeof(codec_reset)); + avr_execute_command(CMD_CODEC_RESET, NULL, 0); } -void avr_hid_set_amp_enable(unsigned char enable) +void avr_hid_set_amp_enable(uint8_t enable) { - unsigned char amp_enable[4] = {CMD_SYNC, CMD_AMP_ENABLE, enable, CMD_CLOSE}; + avr_execute_command(CMD_AMP_ENABLE, &enable, sizeof(enable)); +} - spi_txrx(amp_enable, NULL, sizeof(amp_enable)); +static void avr_hid_sys_ctrl(uint8_t parameter) +{ + avr_execute_command(CMD_SYS_CTRL, ¶meter, sizeof(parameter)); } void avr_hid_power_off(void) { - unsigned char prg[4] = {CMD_SYNC, CMD_SYS_CTRL, SYS_CTRL_POWEROFF, CMD_CLOSE}; - - spi_txrx(prg, NULL, sizeof(prg)); + /* Do not execute command directly here because we can get called inside + * tick task context that must not acquire mutex. + */ + queue_post(&avr_queue, POWER_OFF_REQUEST, 0); } -#ifndef BOOTLOADER static bool avr_state_changed(void) { return (IO_GIO_BITSET0 & 0x1) ? false : true; @@ -420,6 +689,9 @@ static bool headphones_inserted(void) static void set_audio_output(bool headphones) { +#ifdef BOOTLOADER + (void)headphones; +#else if (headphones) { /* Stereo output on headphones */ @@ -432,19 +704,158 @@ static void set_audio_output(bool headphones) aic3x_switch_output(false); avr_hid_set_amp_enable(1); } +#endif +} + +static void read_monotime_offset(void) +{ + int fd = open(MONOTIME_OFFSET_FILE, O_RDONLY); + if (fd >= 0) + { + uint32_t offset; + if (sizeof(offset) == read(fd, &offset, sizeof(offset))) + { + monotime_offset = offset; + } + close(fd); + } +} + +static bool write_monotime_offset(void) +{ + bool success = false; + int fd = open(MONOTIME_OFFSET_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd >= 0) + { + uint32_t offset = monotime_offset; + if (sizeof(monotime_offset) == write(fd, &offset, sizeof(offset))) + { + success = true; + } + close(fd); + } + return success; +} + +static void read_monotime(void) +{ + uint8_t tmp[4]; + uint32_t t1, t2; + + if (!avr_execute_command(CMD_MONOTIME, tmp, sizeof(tmp))) + { + return; + } + t1 = (tmp[0]) | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24); + + if (!avr_execute_command(CMD_MONOTIME, tmp, sizeof(tmp))) + { + return; + } + t2 = (tmp[0]) | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24); + + if ((t1 == t2) || (t1 + 1 == t2)) + { + int flags = disable_irq_save(); + monotime_value = t2; + monotime_value_tick = current_tick; + restore_irq(flags); + monotime_available = true; + } +} + +static time_t get_timestamp(void) +{ + time_t timestamp; + if (!monotime_available) + { + read_monotime(); + } + if (monotime_available) + { + int flags = disable_irq_save(); + timestamp = monotime_value; + timestamp += monotime_offset; + timestamp += ((current_tick - monotime_value_tick) / HZ); + restore_irq(flags); + return timestamp; + } + return 0; +} + +void rtc_init(void) +{ + /* This is called before disk is mounted */ +} + +int rtc_read_datetime(struct tm *tm) +{ + time_t time = get_timestamp(); + gmtime_r(&time, tm); + return 1; +} + +int rtc_write_datetime(const struct tm *tm) +{ + time_t offset = mktime((struct tm *)tm); + int flags = disable_irq_save(); + offset -= monotime_value; + offset -= ((current_tick - monotime_value_tick) / HZ); + monotime_offset = offset; + restore_irq(flags); + queue_post(&avr_queue, MONOTIME_OFFSET_UPDATE, 0); + return 1; } void avr_thread(void) { + struct queue_event ev; bool headphones_active_state = headphones_inserted(); bool headphones_state; + bool disk_access_available = true; + bool monotime_offset_update_pending = false; set_audio_output(headphones_active_state); + read_monotime_offset(); + read_monotime(); while (1) { - semaphore_wait(&avr_thread_trigger, TIMEOUT_BLOCK); + if (avr_state_changed()) + { + /* We have to read AVR state, simply check if there's any event + * pending but do not block. It is possible that AVR interrupt + * line is held active even though we read the state (change + * occured during read). + */ + queue_wait_w_tmo(&avr_queue, &ev, 0); + } + else + { + queue_wait(&avr_queue, &ev); + } + + if (ev.id == SYS_USB_CONNECTED) + { + /* Allow USB to gain exclusive storage access */ + usb_acknowledge(SYS_USB_CONNECTED_ACK); + disk_access_available = false; + } + else if (ev.id == SYS_USB_DISCONNECTED) + { + disk_access_available = true; + } + else if (ev.id == MONOTIME_OFFSET_UPDATE) + { + monotime_offset_update_pending = true; + } + else if (ev.id == POWER_OFF_REQUEST) + { + avr_hid_reset_codec(); + avr_hid_sys_ctrl(SYS_CTRL_POWEROFF); + } + input_interrupt_pending = false; if (avr_state_changed()) { /* Read buttons state */ @@ -457,6 +868,20 @@ void avr_thread(void) set_audio_output(headphones_state); headphones_active_state = headphones_state; } + + if (disk_access_available) + { + if (monotime_offset_update_pending && write_monotime_offset()) + { + monotime_offset_update_pending = false; + } + } + + /* Update buffered monotime value every hour */ + if (TIME_AFTER(current_tick, monotime_value_tick + 3600 * HZ)) + { + read_monotime(); + } } } @@ -466,7 +891,11 @@ void GIO0(void) /* Clear interrupt */ IO_INTC_IRQ1 = (1 << 5); - semaphore_release(&avr_thread_trigger); + if (!input_interrupt_pending) + { + input_interrupt_pending = true; + queue_post(&avr_queue, INPUT_INTERRUPT, 0); + } } void GIO2(void) __attribute__ ((section(".icode"))); @@ -475,28 +904,36 @@ void GIO2(void) /* Clear interrupt */ IO_INTC_IRQ1 = (1 << 7); - semaphore_release(&avr_thread_trigger); + /* Prevent event queue overflow by allowing just one pending event */ + if (!input_interrupt_pending) + { + input_interrupt_pending = true; + queue_post(&avr_queue, INPUT_INTERRUPT, 0); + } } -#endif void button_init_device(void) { btn = 0; hold_switch = false; -#ifndef BOOTLOADER - semaphore_init(&avr_thread_trigger, 1, 1); + + queue_init(&avr_queue, true); + input_interrupt_pending = true; + queue_post(&avr_queue, INPUT_INTERRUPT, 0); create_thread(avr_thread, avr_stack, sizeof(avr_stack), 0, avr_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU)); -#endif + IO_GIO_DIR0 |= 0x01; /* Set GIO0 as input */ + /* Get in sync with AVR */ + avr_hid_sync(); + /* Enable wheel */ avr_hid_enable_wheel(); /* Read button status and tell avr we want interrupt on next change */ avr_hid_get_state(); -#ifndef BOOTLOADER IO_GIO_IRQPORT |= 0x05; /* Enable GIO0/GIO2 external interrupt */ IO_GIO_INV0 &= ~0x05; /* Clear INV for GIO0/GIO2 */ /* falling edge detection on GIO0, any edge on GIO2 */ @@ -504,7 +941,6 @@ void button_init_device(void) /* Enable GIO0 and GIO2 interrupts */ IO_INTC_EINT1 |= INTR_EINT1_EXT0 | INTR_EINT1_EXT2; -#endif } int button_read_device(void) @@ -519,9 +955,3 @@ bool button_hold(void) { return hold_switch; } - -void lcd_enable(bool on) -{ - (void)on; -} - diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h index 06fd2b2d72..ed23e84936 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h +++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h @@ -24,11 +24,42 @@ #include "config.h" -void avr_hid_sync(void); +/* HDQ (bq27000) RAM registers */ +#define HDQ_REG_CTRL 0x00 +#define HDQ_REG_MODE 0x01 +#define HDQ_REG_AR 0x02 +#define HDQ_REG_ARTTE 0x04 +#define HDQ_REG_TEMP 0x06 +#define HDQ_REG_VOLT 0x08 +#define HDQ_REG_FLAGS 0x0A +#define HDQ_REG_RSOC 0x0B +#define HDQ_REG_NAC 0x0C +#define HDQ_REG_CACD 0x0E +#define HDQ_REG_CACT 0x10 +#define HDQ_REG_LMD 0x12 +#define HDQ_REG_AI 0x14 +#define HDQ_REG_TTE 0x16 +#define HDQ_REG_TTF 0x18 +#define HDQ_REG_SI 0x1A +#define HDQ_REG_STTE 0x1C +#define HDQ_REG_MLI 0x1E +#define HDQ_REG_MLTTE 0x20 +#define HDQ_REG_SAE 0x22 +#define HDQ_REG_AP 0x24 +#define HDQ_REG_TTECP 0x26 +#define HDQ_REG_CYCL 0x28 +#define HDQ_REG_CYCT 0x2A +#define HDQ_REG_CSOC 0x2C + void avr_hid_init(void); +int avr_hid_hdq_read_byte(uint8_t address); +int avr_hid_hdq_read_short(uint8_t address); + void avr_hid_enable_charger(void); +void avr_hid_wifi_pd(int high); + void avr_hid_lcm_sleep(void); void avr_hid_lcm_wake(void); void avr_hid_lcm_power_on(void); diff --git a/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c index c65d806635..37cb672738 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c @@ -52,13 +52,12 @@ static void _backlight_write_brightness(int brightness) void backlight_hw_on(void) { -#ifdef HAVE_LCD_SLEEP if (!lcd_active()) { lcd_awake(); lcd_update(); } -#endif + /* set GIO34 as PWM1 */ IO_GIO_FSEL3 = (IO_GIO_FSEL3 & 0xFFF3) | (1 << 2); diff --git a/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S b/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S index a356016fb4..1848143fc1 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S +++ b/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S @@ -58,10 +58,10 @@ _init_board: /* Setup the EMIF interface timings */ /* FLASH interface: - * These are based on the OF setup + * These are based on the OF setup */ /* IO_EMIF_CS0CTRL1 and - * IO_EMIF_CS0CTRL2 + * IO_EMIF_CS0CTRL2 */ mwh 0x30A00, 0x889A mwh 0x30A02, 0x1110 @@ -75,7 +75,7 @@ _init_board: mwh 0x30A0E, 0x0222 /* IO_EMIF_CS3CTRL1 and - * IO_EMIF_CS3CTRL2 + * IO_EMIF_CS3CTRL2 */ mwh 0x30A10, 0x8899 mwh 0x30A12, 0x5110 @@ -96,37 +96,49 @@ _init_board: _clock_setup: /* Clock initialization */ + /* Disable peripheral clocks */ + mwhm 0x3089A, 0 + mwhm 0x3089C, 0 + /* IO_CLK_BYP: Bypass the PLLs for the following changes */ mwh 0x30894, 0x1111 /* - * IO_CLK_PLLA - * IO_CLK_PLLB + * IO_CLK_PLLA: 27 MHz * 11 / 1 = 297 MHz + * IO_CLK_PLLB: 27 MHz */ - mwhm 0x30880, 0x00A0 + mwh 0x30880, 0x10A0 mwhm 0x30882, 0x1000 - /* IO_CLK_SEL0 */ + /* IO_CLK_SEL0: Timer 0 and 1, UART 0 and 1 from PLLIN (27 MHz) */ mwh 0x30884, 0x0066 - /* IO_CLK_SEL1 */ + /* IO_CLK_SEL1: VENC from PLLA, OSD clock = VENC clock / 2 */ mwhm 0x30886, 0x0003 - # IO_CLK_SEL2: ARM, AXL, SDRAM and DSP are from PLLA */ + /* IO_CLK_SEL2: ARM, AXL, SDRAM and DSP are from PLLA */ mwh 0x30888, 0 - /* IO_CLK_DIV0: Set the slow clock speed for the ARM/AHB */ + /* IO_CLK_DIV0: Set the fast clock speed for the ARM/AHB + * ARM = PLLA / 2 = 148.5 MHz + * AHB = ARM / 2 = 74.25 MHz + */ mwh 0x3088A, 0x0101 - /* IO_CLK_DIV1: Accelerator, SDRAM */ + /* IO_CLK_DIV1: Accelerator, SDRAM + * AXL = PLLA / 2 = 148.5 MHz + * SDRAM = PLLA / 3 = 99 MHz + */ mwh 0x3088C, 0x0102 /* IO_CLK_DIV2: DSP, MS Clock * OF must be booted with this value + * DSP = PLLA / 3 = 99 MHz + * MS = PLLA / 1 = 297 MHz */ mwhm 0x3088E, 0x0200 - # PLLA &= ~0x1000 (BIC #0x1000) + /* PLLA &= ~0x1000 (BIC #0x1000) */ mrh 0x30880 bic r0, r0, #0x1000 strh r0, [r1] @@ -141,25 +153,27 @@ _plla_wait: /* IO_CLK_BYP: Enable PLL feeds */ mwhm 0x30894, 0x0 - /* IO_CLK_MOD0 */ + /* IO_CLK_MOD0 + * Enable clocks: + * ARM, Bus Controller, AHB, ARM internal memory, EMIF, SDRAM + * Disable clocks: + * ETM, E2ICE, INTC, EXTHOST, DSP, HPIB + */ mwh 0x30898, 0x01A7 - /* IO_CLK_MOD1 */ - mwhm 0x3089A, 0x18 - - /* IO_CLK_MOD2 */ - mwhm 0x3089C, 0x4A0 + /* IO_CLK_MOD2: Enable GIO and SIF1 clocks */ + mwhm 0x3089C, 0x0420 /* Setup the SDRAM range on the AHB bus */ /* SDRAMSA */ mov r0, #0x60000 mov r1, #0x1000000 str r1, [r0, #0xF00] - + /* SDRAMEA: 64MB */ mov r1, #0x5000000 str r1, [r0, #0xF04] - + /* SDRC_REFCTL */ mwh 0x309A8, 0 @@ -183,8 +197,11 @@ _plla_wait: mwhm 0x309A8, 0x0140 + /* IMGBUF SDRAM priority bit 2 set */ mwhm 0x309BE, 0x4 + /* SDRAM refresh priority bit 1 set */ mwhm 0x309BC, 0x2 + /* Use defined priority bits */ ldr r0, =0x309C4 ldr r1, [r0] orr r1, r1, #1 @@ -198,10 +215,11 @@ _plla_wait: orr r1, r1, #0x40 strh r1, [r0] + /* Enable auto refresh with interval (64 + 1) * 8 SDRAM clocks */ mwhm 0x309A8, 0x0140 /* Go through the GPIO initialization */ - /* Warning: setting some of the functions wrong will make OF unable + /* Warning: setting some of the functions wrong will make OF unable to boot (freeze during startup) */ /* IO_GIO_FSEL0: Set up the GPIO pin functions 0-16 */ mwhm 0x305A4, 0xC000 @@ -223,7 +241,7 @@ _plla_wait: /* IO_GIO_DIR2 */ mwh 0x30584, 0x01FD - + /* IO_GIO_INV0 */ mwh 0x30586, 0x0000 @@ -235,6 +253,6 @@ _plla_wait: bx lr -.ltorg +.ltorg .size _init_board, .-_init_board diff --git a/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c new file mode 100644 index 0000000000..ac01525500 --- /dev/null +++ b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c @@ -0,0 +1,80 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id: $ +* +* Copyright (C) 2021 by Tomasz Moń +* +* 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 "cryptomem-sansaconnect.h" +#include "i2c-dm320.h" + +/* Command values */ +#define WRITE_USER_ZONE 0xB0 +#define READ_USER_ZONE 0xB2 +#define SYSTEM_WRITE 0xB4 +#define SYSTEM_READ 0xB6 +#define VERIFY_CRYPTO 0xB8 +#define VERIFY_PASSWORD 0xBA + +/* SYSTEM_WRITE ADDR 1 values */ +#define WRITE_CONFIG 0x00 +#define WRITE_FUSES 0x01 +#define SEND_CHECKSUM 0x02 +#define SET_USER_ZONE 0x03 + +int cryptomem_read_deviceid(char deviceid[32]) +{ + int ret; + unsigned int i; + unsigned char cmd_data[3]; + + /* It is assumed that other I2C communication has already taken place + * (e.g. power_init()) before this function is called and thus we don't + * have to send atleast 5 dummy clock cycles here. + */ + + cmd_data[0] = SET_USER_ZONE; + cmd_data[1] = 0; + cmd_data[2] = 0; + ret = i2c_write(SYSTEM_WRITE, cmd_data, sizeof(cmd_data)); + if (ret < 0) + { + return ret; + } + + cmd_data[0] = 0; + cmd_data[1] = 0; + cmd_data[2] = 32; + ret = i2c_write_read_bytes(READ_USER_ZONE, cmd_data, sizeof(cmd_data), + deviceid, 32); + if (ret < 0) + { + return ret; + } + + /* Verify that deviceid contains only printable ASCII characters */ + for (i = 0; i < 32; i++) + { + if (!isprint(deviceid[i])) + { + return -1; + } + } + + return 0; +} diff --git a/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.h b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.h new file mode 100644 index 0000000000..b6b52d3b2c --- /dev/null +++ b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.h @@ -0,0 +1,27 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id: $ +* +* Copyright (C) 2021 by Tomasz Moń +* +* 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 _CRYPTOMEM_SANSACONNECT_H_ +#define _CRYPTOMEM_SANSACONNECT_H_ + +int cryptomem_read_deviceid(char deviceid[32]); + +#endif /* _CRYPTOMEM_SANSACONNECT_H_ */ diff --git a/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c index 27eb0b407a..9674f07a64 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c @@ -29,60 +29,110 @@ #include "lcd.h" #include "lcd-target.h" #include "avr-sansaconnect.h" +#include "backlight-target.h" -extern bool lcd_on; /* lcd-memframe.c */ +/* See lcd-memframe.c */ +extern void lcd_set_active(bool active); + +static bool lcd_powered; -#if defined(HAVE_LCD_SLEEP) void lcd_sleep(void) { - if (lcd_on) + if (lcd_powered) { - lcd_on = false; + lcd_set_active(false); - avr_hid_lcm_sleep(); - sleep(HZ/20); + avr_hid_lcm_power_off(); + mdelay(100); + /* Disable OSD window */ + bitclr16(&IO_OSD_OSDWINMD0, 0x01); /* disable video encoder */ - bitclr16(&IO_VID_ENC_VMOD, 0x01); + bitclr16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC); + mdelay(66); - sleep(HZ/20); + /* disable video encoder and OSD clocks */ + bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD); - /* disable video encoder clock */ - bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC); + lcd_powered = false; } } void lcd_awake(void) { - if (!lcd_on) + if (!lcd_powered) { - lcd_on = true; + /* Enable Video Encoder and OSD clocks */ + bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD); + /* Enable OSD window */ + bitset16(&IO_OSD_OSDWINMD0, 0x01); + /* Enable video encoder */ + bitset16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC); + + avr_hid_lcm_power_on(); + mdelay(66); + lcd_set_active(true); + avr_hid_lcm_wake(); - /* enable video encoder clock */ - bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC); + lcd_powered = true; + send_event(LCD_EVENT_ACTIVATION, NULL); + } +} - /* enable video encoder */ - bitset16(&IO_VID_ENC_VMOD, 0x01); +void lcd_shutdown(void) +{ + backlight_hw_off(); + lcd_sleep(); +} + +void lcd_enable(bool enable) +{ + if (lcd_active() == enable) + return; + + lcd_set_active(enable); + + if (enable) + { + /* Enable Video Encoder and OSD clocks */ + bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD); + /* Enable OSD window */ + bitset16(&IO_OSD_OSDWINMD0, 0x01); + /* Enable video encoder */ + bitset16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC); avr_hid_lcm_wake(); + mdelay(30); send_event(LCD_EVENT_ACTIVATION, NULL); + } + else + { + mdelay(30); + avr_hid_lcm_sleep(); + mdelay(10); - lcd_update(); + /* Disable OSD window */ + bitclr16(&IO_OSD_OSDWINMD0, 0x01); + /* disable video encoder */ + bitclr16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC); + mdelay(66); + + /* disable video encoder and OSD clocks */ + bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD); } } -#endif void lcd_init_device(void) { unsigned int addr; - + /* Disable Video Encoder clock */ bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC); /* configure GIO39, GIO34 as outputs */ IO_GIO_DIR2 &= ~((1 << 7) /* GIO39 */ | (1 << 2) /* GIO34 */); - + IO_GIO_FSEL3 = (IO_GIO_FSEL3 & ~(0x300F)) | (0x1000) /* GIO39 - FIELD_VENC */ | (0x4); /* GIO34 - PWM1 (brightness control) */ @@ -92,7 +142,7 @@ void lcd_init_device(void) VENC Clock from PLLA */ IO_CLK_SEL1 = 0x3; - /* Set VENC Clock Division to 11 + /* Set VENC Clock Division to 11 (PLLA / 11 = 297 MHz / 11 = 27 MHz) OF bootloader sets division to 8, vmlinux sets it to 11 */ IO_CLK_DIV3 = (IO_CLK_DIV3 & ~(0x1F00)) | 0xB00; @@ -121,11 +171,9 @@ void lcd_init_device(void) IO_VID_ENC_VMOD = 0x2015; /* OF sets 0x2011 */ /* Copy Rockbox frame buffer to the second framebuffer */ + lcd_set_active(true); lcd_update(); - avr_hid_sync(); - avr_hid_lcm_power_on(); - /* set framebuffer address - OF sets RAM start address to 0x1000000 */ addr = ((int)FRAME-CONFIG_SDRAM_START)/32; @@ -150,8 +198,9 @@ void lcd_init_device(void) /* Enable Video Encoder - RGB666, custom timing */ IO_VID_ENC_VMOD = 0x2015; + + lcd_powered = true; avr_hid_lcm_wake(); - lcd_on = true; } #ifdef LCD_USE_DMA @@ -168,14 +217,14 @@ static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height) /* Set source and destination addresses */ dst = (char*)(FRAME + LCD_WIDTH*y + x); src = (char*)(FBADDR(x,y)); - + /* Flush cache to memory */ commit_dcache(); /* Addresses are relative to start of SDRAM */ src -= CONFIG_SDRAM_START; dst -= CONFIG_SDRAM_START; - + /* Enable Image Buffer clock */ bitset16(&IO_CLK_MOD1, CLK_MOD1_IMGBUF); @@ -192,7 +241,7 @@ static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height) /* Set the start address of buffer */ COP_BUF_ADDR = 0x0000; - + /* Setup SDRAM stride */ COP_SDEM_LOFST = LCD_WIDTH; @@ -201,28 +250,28 @@ static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height) addr = (int)src; addr >>= 1; /* Addresses are in 16-bit words */ - + /* Setup the registers to initiate the read from SDRAM */ COP_SDEM_ADDRH = addr >> 16; COP_SDEM_ADDRL = addr & 0xFFFF; - + /* Set direction and start */ COP_DMA_CTRL = 0x0001; COP_DMA_CTRL |= 0x0002; /* Wait for read to finish */ while (COP_DMA_CTRL & 0x02) {}; - + addr = (int)dst; addr >>= 1; - + COP_SDEM_ADDRH = addr >> 16; COP_SDEM_ADDRL = addr & 0xFFFF; - + /* Set direction and start transfer */ COP_DMA_CTRL = 0x0000; COP_DMA_CTRL |= 0x0002; - + /* Wait for the transfer to complete */ while (COP_DMA_CTRL & 0x02) {}; @@ -247,7 +296,7 @@ void lcd_update_rect(int x, int y, int width, int height) __attribute__ ((section(".icode"))); void lcd_update_rect(int x, int y, int width, int height) { - if (!lcd_on) + if (!lcd_active()) return; if ((width | height) < 0) @@ -271,7 +320,7 @@ void lcd_update_rect(int x, int y, int width, int height) void lcd_update(void) __attribute__ ((section(".icode"))); void lcd_update(void) { - if (!lcd_on) + if (!lcd_active()) return; lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); diff --git a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c index f9f19a8045..bda000e68f 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c @@ -42,22 +42,6 @@ void pcm_play_dma_postinit(void) audiohw_postinit(); } -/* Return the current location in the SDRAM to SARAM transfer along with the - * number of bytes read in the current buffer (count). There is latency with - * this method equivalent to ~ the size of the SARAM buffer since there is - * another buffer between your ears and this calculation, but this works for - * key clicks and an approximate peak meter. - */ -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - int cnt = DSP_(_sdem_level); - - unsigned long addr = (unsigned long) start + cnt; - - *count = (cnt & 0xFFFFF) >> 1; - return (void *)((addr + 2) & ~3); -} - void pcm_play_dma_init(void) { /* GIO16 is DSP/AIC3X CLK */ diff --git a/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c index b139572605..597fb6b7e0 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c @@ -31,6 +31,29 @@ #include "i2c-dm320.h" #include "logf.h" + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3450 +}; + +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3400 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + { 3400, 3508, 3630, 3703, 3727, 3750, 3803, 3870, 3941, 4026, 4142 } +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + 3540, 3788, 3860, 3890, 3916, 3956, 4016, 4085, 4164, 4180, 4190 +}; + /* (7-bit) address is 0x48, the LSB is read/write flag */ #define TPS65021_ADDR (0x48 << 1) @@ -54,7 +77,7 @@ void power_init(void) /* PWM mode */ tps65021_write_reg(0x04, 0xB2); - + /* Set core voltage to 1.5V */ tps65021_write_reg(0x06, 0x1C); @@ -70,7 +93,6 @@ void power_off(void) /* Disable GIO0 and GIO2 interrupts */ IO_INTC_EINT1 &= ~(INTR_EINT1_EXT2 | INTR_EINT1_EXT0); - avr_hid_reset_codec(); avr_hid_power_off(); } diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c index 93477bed9e..1ce6e4a640 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c @@ -27,9 +27,12 @@ #include "system.h" #include "kernel.h" #include "panic.h" -#include "logf.h" #include "tnetv105_usb_drv.h" #include "tnetv105_cppi.h" +#if USB_CPPI_LOGGING +#define LOGF_ENABLE +#endif +#include "logf.h" /* This file is pretty much directly copied from the Sansa Connect * Linux kernel source code. This is because the functionality is @@ -71,11 +74,11 @@ static uint8_t ch0_rx_buf[CPPI_DMA_RX_BUF_SIZE]; static uint8_t ch1_rx_buf[CPPI_DMA_RX_BUF_SIZE]; #if USB_CPPI_LOGGING -#define cppi_log_event0(msg) usb_log_event(msg, LOG_EVENT_D0, 0, 0, 0, 0) -#define cppi_log_event1(msg, data0) usb_log_event(msg, LOG_EVENT_D1, data0, 0, 0, 0) -#define cppi_log_event2(msg, data0, data1) usb_log_event(msg, LOG_EVENT_D2, data0, data1, 0, 0) -#define cppi_log_event3(msg, data0, data1, data2) usb_log_event(msg, LOG_EVENT_D3, data0, data1, data2, 0) -#define cppi_log_event4(msg, data0, data1, data2, data3) usb_log_event(msg, LOG_EVENT_D4, data0, data1, data2, data3) +#define cppi_log_event0(msg) logf(msg) +#define cppi_log_event1(msg, data0) logf(msg " %lx", (uint32_t)data0) +#define cppi_log_event2(msg, data0, data1) logf(msg " %lx %lx", (uint32_t)data0, (uint32_t)data1) +#define cppi_log_event3(msg, data0, data1, data2) logf(msg " %lx %lx %lx", (uint32_t)data0, (uint32_t)data1, (uint32_t)data2) +#define cppi_log_event4(msg, data0, data1, data2, data3) logf(msg " %lx %lx %lx %lx", (uint32_t)data0, (uint32_t)data1, (uint32_t)data2, (uint32_t)data3) #else #define cppi_log_event0(x) #define cppi_log_event1(x, y) @@ -439,7 +442,7 @@ void tnetv_cppi_dump_info(struct cppi_info *cppi) cppi_rcb *rcb; logf("CPPI struct:\n"); - logf("Buf mem: %x Buf size: %d int: %x %x\n\n", (uint32_t) cppi->dma_mem, cppi->dma_size, tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS), tnetv_usb_reg_read(DM320_VLYNQ_INTST)); + logf("Buf mem: %lx Buf size: %d int: %lx %lx\n\n", (uint32_t) cppi->dma_mem, cppi->dma_size, tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS), tnetv_usb_reg_read(VL_INTST)); for (ch = 0; ch < CPPI_NUM_CHANNELS; ch++) { @@ -447,9 +450,9 @@ void tnetv_cppi_dump_info(struct cppi_info *cppi) pTxCtl = &cppi->tx_ctl[ch]; logf("ch: %d\n", ch); - logf(" rx_numbufs: %d active %d free_buf_cnt %d\n", pRxCtl->RxNumBuffers, pRxCtl->RxActive, tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch))); - logf(" q_cnt %d head %x tail %x\n", pRxCtl->RxActQueueCount, (uint32_t) pRxCtl->RxActQueueHead, (uint32_t) pRxCtl->RxActQueueTail); - logf(" fake_head: %x fake_tail: %x\n", (uint32_t) pRxCtl->RxFakeRcvHead, (uint32_t) pRxCtl->RxFakeRcvTail); + logf(" rx_numbufs: %d active %ld free_buf_cnt %ld\n", pRxCtl->RxNumBuffers, pRxCtl->RxActive, tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch))); + logf(" q_cnt %ld head %lx tail %lx\n", pRxCtl->RxActQueueCount, (uint32_t) pRxCtl->RxActQueueHead, (uint32_t) pRxCtl->RxActQueueTail); + logf(" fake_head: %lx fake_tail: %lx\n", (uint32_t) pRxCtl->RxFakeRcvHead, (uint32_t) pRxCtl->RxFakeRcvTail); rcb = (cppi_rcb *) pRxCtl->RcbStart; do @@ -457,16 +460,16 @@ void tnetv_cppi_dump_info(struct cppi_info *cppi) if (!rcb) break; - logf(" Rcb: %x\n", (uint32_t) rcb); - logf(" HNext %x BufPtr %x Off_BLen %x mode %x\n", rcb->HNext, rcb->BufPtr, rcb->Off_BLen, rcb->mode); - logf(" Next %x Eop %x dma_handle %x fake_bytes %x\n", (uint32_t) rcb->Next, (uint32_t) rcb->Eop, rcb->dma_handle, rcb->fake_bytes); + logf(" Rcb: %lx\n", (uint32_t) rcb); + logf(" HNext %lx BufPtr %lx Off_BLen %lx mode %lx\n", rcb->HNext, rcb->BufPtr, rcb->Off_BLen, rcb->mode); + logf(" Next %lx Eop %lx dma_handle %lx fake_bytes %lx\n", (uint32_t) rcb->Next, (uint32_t) rcb->Eop, rcb->dma_handle, rcb->fake_bytes); rcb = rcb->Next; } while (rcb && rcb != (cppi_rcb *) pRxCtl->RcbStart); logf("\n"); - logf(" tx_numbufs: %d active %d\n", pTxCtl->TxNumBuffers, pTxCtl->TxActive); - logf(" q_cnt %d head %x tail %x\n", pTxCtl->TxActQueueCount, (uint32_t) pTxCtl->TxActQueueHead, (uint32_t) pTxCtl->TxActQueueTail); + logf(" tx_numbufs: %d active %ld\n", pTxCtl->TxNumBuffers, pTxCtl->TxActive); + logf(" q_cnt %ld head %lx tail %lx\n", pTxCtl->TxActQueueCount, (uint32_t) pTxCtl->TxActQueueHead, (uint32_t) pTxCtl->TxActQueueTail); tcb = (cppi_tcb *) pTxCtl->TcbPool; do @@ -474,9 +477,9 @@ void tnetv_cppi_dump_info(struct cppi_info *cppi) if (!tcb) break; - logf(" Tcb (pool): %x\n", (uint32_t) tcb); - logf(" HNext %x BufPtr %x Off_BLen %x mode %x\n", tcb->HNext, tcb->BufPtr, tcb->Off_BLen, tcb->mode); - logf(" Next %x Eop %x dma_handle %x\n", (uint32_t) tcb->Next, (uint32_t) tcb->Eop, tcb->dma_handle); + logf(" Tcb (pool): %lx\n", (uint32_t) tcb); + logf(" HNext %lx BufPtr %lx Off_BLen %lx mode %lx\n", tcb->HNext, tcb->BufPtr, tcb->Off_BLen, tcb->mode); + logf(" Next %lx Eop %lx dma_handle %lx\n", (uint32_t) tcb->Next, (uint32_t) tcb->Eop, tcb->dma_handle); tcb = tcb->Next; } while (tcb && tcb != (cppi_tcb *) pTxCtl->TcbPool); @@ -487,9 +490,9 @@ void tnetv_cppi_dump_info(struct cppi_info *cppi) if (!tcb) break; - logf(" Tcb (act): %x\n", (uint32_t) tcb); - logf(" HNext %x BufPtr %x Off_BLen %x mode %x\n", tcb->HNext, tcb->BufPtr, tcb->Off_BLen, tcb->mode); - logf(" Next %x Eop %x dma_handle %x\n", (uint32_t) tcb->Next, (uint32_t) tcb->Eop, tcb->dma_handle); + logf(" Tcb (act): %lx\n", (uint32_t) tcb); + logf(" HNext %lx BufPtr %lx Off_BLen %lx mode %lx\n", tcb->HNext, tcb->BufPtr, tcb->Off_BLen, tcb->mode); + logf(" Next %lx Eop %lx dma_handle %lx\n", (uint32_t) tcb->Next, (uint32_t) tcb->Eop, tcb->dma_handle); tcb = tcb->Next; } while (tcb && tcb != (cppi_tcb *) pTxCtl->TxActQueueTail); @@ -556,7 +559,7 @@ int tnetv_cppi_rx_int_recv(struct cppi_info *cppi, int ch, int *buf_size, void * CurrentRcb = pRxCtl->RxFakeRcvHead; if (!CurrentRcb) { - cppi_log_event2("[cppi] rx_int recv: nothing in q", tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS), tnetv_usb_reg_read(DM320_VLYNQ_INTST)); + cppi_log_event2("[cppi] rx_int recv: nothing in q", tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS), tnetv_usb_reg_read(VL_INTST)); return -1; } diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h index 9d0ac37cd0..b240f9593d 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h +++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h @@ -26,11 +26,11 @@ #define TNETV105_CPPI_H #include <stdint.h> +#include "errno.h" typedef uint32_t dma_addr_t; #define USB_CPPI_LOGGING 0 -#define EAGAIN 11 /* Try again */ -#define CPPI_RX_NUM_BUFS 129 +#define CPPI_RX_NUM_BUFS 2 #define CPPI_INFO_MEM_SIZE (2 * CPPI_RX_NUM_BUFS * sizeof(cppi_rcb) + 4 * 2 * sizeof(cppi_tcb)) #define CPPI_NUM_CHANNELS 4 diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c index 5563a40814..e5a9000f40 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c @@ -1026,8 +1026,8 @@ static bool tnetv_handle_cppi(void) if (rx_intstatus || tx_intstatus || rcv_sched) { /* Request calling again after short delay - * Needed when for example when OUT endpoint has pending - * data but the USB task did not call usb_drv_recv() yet. + * Needed when for example when OUT endpoint has pending data + * but the USB task did not call usb_drv_recv_nonblocking() yet. */ return true; } @@ -1181,7 +1181,7 @@ void VLYNQ(void) } /* Process control packet */ - usb_core_control_request(&setup); + usb_core_legacy_control_request(&setup); } if (sysIntrStatus.f.ep0_in_ack) @@ -1371,7 +1371,7 @@ int usb_drv_send_nonblocking(int endpoint, void* ptr, int length) return _usb_drv_send(endpoint, ptr, length, false); } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { int epn = EP_NUM(endpoint); struct ep_runtime_t *ep; @@ -1388,8 +1388,6 @@ int usb_drv_recv(int endpoint, void* ptr, int length) return 0; } -void usb_drv_ack(struct usb_ctrlrequest* req); - void usb_drv_set_address(int address) { UsbCtrlType usbCtrl; diff --git a/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c new file mode 100644 index 0000000000..1f201ca1c9 --- /dev/null +++ b/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c @@ -0,0 +1,132 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2021 by Tomasz Moń +* +* 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 "kernel.h" +#include "system.h" +#include "spi.h" +#include "avr-sansaconnect.h" +#include "libertas/if_spi_drv.h" + +#define IO_SERIAL0_XMIT (0x100) + +void libertas_spi_init(void) +{ + IO_GIO_DIR0 &= ~((1 << 4) /* CS */ | (1 << 3) /* reset */); + libertas_spi_reset(1); + libertas_spi_cs(1); + + /* Enable the clock */ + bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + + /* Disable transmitter */ + IO_SERIAL0_TX_ENABLE = 0x0001; + + /* SELSDEN = 0, SLVEN = 0, SIOCLR = 0, SCLKM = 1, MSB = 1, MSSEL = 0, + * RATE = 1 (Bit rate = ARM clock / 2*(RATE + 1))) + * Boosted 148.5 MHz / 4 = 37.125 MHz + * Default 74.25 MHz / 4 = 18.5625 MHz + */ + IO_SERIAL0_MODE = 0x0601; + + /* Disable the clock */ + bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + + /* Make sure the SPI clock is not inverted */ + bitclr16(&IO_CLK_INV, CLK_INV_SIF0); +} + +void libertas_spi_reset(int high) +{ + if (high) + { + IO_GIO_BITSET0 = (1 << 3); + } + else + { + IO_GIO_BITCLR0 = (1 << 3); + } +} + +void libertas_spi_pd(int high) +{ + avr_hid_wifi_pd(high); +} + +void libertas_spi_cs(int high) +{ + if (high) + { + IO_GIO_BITSET0 = (1 << 4); + } + else + { + IO_GIO_BITCLR0 = (1 << 4); + } +} + +void libertas_spi_tx(const uint8_t *buf, int len) +{ + /* Enable the clock */ + bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + IO_SERIAL0_TX_ENABLE = 0x0001; + + while (len > 0) + { + IO_SERIAL0_TX_DATA = *(buf + 1); + while (IO_SERIAL0_RX_DATA & IO_SERIAL0_XMIT) {}; + IO_SERIAL0_TX_DATA = *buf; + while (IO_SERIAL0_RX_DATA & IO_SERIAL0_XMIT) {}; + + buf += 2; + len -= 2; + } + + IO_SERIAL0_TX_ENABLE = 0x0000; + + /* Disable the clock */ + bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0); +} + +void libertas_spi_rx(uint8_t *buf, int len) +{ + /* Enable the clock */ + bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + IO_SERIAL0_TX_ENABLE = 0x0001; + + while (len > 0) + { + uint16_t data; + IO_SERIAL0_TX_DATA = 0; + while ((data = IO_SERIAL0_RX_DATA) & IO_SERIAL0_XMIT) {}; + *(buf + 1) = data & 0xFF; + IO_SERIAL0_TX_DATA = 0; + while ((data = IO_SERIAL0_RX_DATA) & IO_SERIAL0_XMIT) {}; + *buf = data & 0xFF; + + buf += 2; + len -= 2; + } + + IO_SERIAL0_TX_ENABLE = 0x0000; + + /* Disable the clock */ + bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0); +} diff --git a/firmware/target/arm/tms320dm320/sdmmc-dm320.c b/firmware/target/arm/tms320dm320/sdmmc-dm320.c index 33354a3146..e66a4cb3c7 100644 --- a/firmware/target/arm/tms320dm320/sdmmc-dm320.c +++ b/firmware/target/arm/tms320dm320/sdmmc-dm320.c @@ -805,8 +805,9 @@ int sd_init(void) bitclr16(&IO_CLK_MOD2, CLK_MOD2_MMC); bitset16(&IO_CLK_INV, CLK_INV_MMC); - /* mmc module clock: 75 Mhz (AHB) / 2 = ~37.5 Mhz - * (Frequencies above are taken from Sansa Connect's OF source code) */ + /* mmc module clock when boosted 74.25 Mhz (AHB) / 2 = 37.125 Mhz + * default 37.125 (AHB) / 2 = 18.5625 MHz + * (Frequencies above are valid for Sansa Connect) */ IO_CLK_DIV3 = (IO_CLK_DIV3 & 0xFF00) | 0x01; bitset16(&IO_CLK_MOD2, CLK_MOD2_MMC); diff --git a/firmware/target/arm/tms320dm320/system-dm320.c b/firmware/target/arm/tms320dm320/system-dm320.c index 935f3609a6..6cf616184d 100644 --- a/firmware/target/arm/tms320dm320/system-dm320.c +++ b/firmware/target/arm/tms320dm320/system-dm320.c @@ -312,7 +312,14 @@ void system_init(void) #endif { #ifdef SANSA_CONNECT - /* Setting AHB divisor to 0 increases power consumption */ + /* Setting AHB divisor to 0 increases power consumption + * Slow Setup: + * ARM div = 4 ( 74.25 MHz ) + * AHB div = 2 ( 37.125 MHz ) + * Fast Setup: + * ARM div = 2 ( 148.5 MHz ) + * AHB div = 2 ( 74.25 MHz ) + */ clock_arm_slow = (1 << 8) | 3; clock_arm_fast = (1 << 8) | 1; #else @@ -369,9 +376,11 @@ void system_init(void) #endif #ifdef SANSA_CONNECT +#ifndef HAVE_WIFI /* keep WIFI CS and reset high to save power */ IO_GIO_DIR0 &= ~((1 << 4) /* CS */ | (1 << 3) /* reset */); IO_GIO_BITSET0 = (1 << 4) | (1 << 3); +#endif i2c_init(); avr_hid_init(); diff --git a/firmware/target/arm/tms320dm320/system-target.h b/firmware/target/arm/tms320dm320/system-target.h index 1c46e909ed..9aa8b3e213 100644 --- a/firmware/target/arm/tms320dm320/system-target.h +++ b/firmware/target/arm/tms320dm320/system-target.h @@ -24,10 +24,15 @@ #include "system-arm.h" #include "mmu-arm.h" -#define CPUFREQ_SLEEP 32768 +#ifdef SANSA_CONNECT +#define CPUFREQ_DEFAULT 74250000 +#define CPUFREQ_NORMAL 74250000 +#define CPUFREQ_MAX 148500000 +#else #define CPUFREQ_DEFAULT 87500000 #define CPUFREQ_NORMAL 87500000 #define CPUFREQ_MAX 175000000 +#endif void udelay(int usec); void mdelay(int msec); diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c index b19b635923..22751f27f0 100644 --- a/firmware/target/arm/usb-drv-arc.c +++ b/firmware/target/arm/usb-drv-arc.c @@ -593,7 +593,7 @@ int usb_drv_send(int endpoint, void* ptr, int length) return prime_transfer(EP_NUM(endpoint), ptr, length, true, true); } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { //logf("usbrecv(%x, %d)", ptr, length); return prime_transfer(EP_NUM(endpoint), ptr, length, false, false); @@ -877,7 +877,7 @@ static void control_received(void) } } - usb_core_control_request((struct usb_ctrlrequest*)tmp); + usb_core_legacy_control_request((struct usb_ctrlrequest*)tmp); } static void transfer_completed(void) diff --git a/firmware/target/arm/usb-s3c6400x.c b/firmware/target/arm/usb-s3c6400x.c index 920d1a6286..0f3ecf8c00 100644 --- a/firmware/target/arm/usb-s3c6400x.c +++ b/firmware/target/arm/usb-s3c6400x.c @@ -166,7 +166,7 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) return 0; } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { ep_transfer(EP_NUM(endpoint), ptr, length, true); return 0; @@ -522,7 +522,7 @@ static void handle_ep_int(int ep, bool out) ep0_setup_pkt->bRequest == USB_REQ_SET_ADDRESS) DCFG = (DCFG & ~bitm(DCFG, devadr)) | (ep0_setup_pkt->wValue << DCFG_devadr_bitp); - usb_core_control_request(ep0_setup_pkt); + usb_core_legacy_control_request(ep0_setup_pkt); } } diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c index 1b5f16c223..53f101c471 100644 --- a/firmware/target/arm/usb-tcc.c +++ b/firmware/target/arm/usb-tcc.c @@ -251,7 +251,7 @@ void handle_control(void) DEBUG(2, "req: %02x %02d", req->bRequestType, req->bRequest); } - usb_core_control_request(req); + usb_core_legacy_control_request(req); } static @@ -609,7 +609,7 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) return rc; } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { volatile struct tcc_ep *tcc_ep = &tcc_endpoints[endpoint & 0x7f]; int flags; 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/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c index c45219297c..36eb00c31b 100644 --- a/firmware/target/coldfire/pcm-coldfire.c +++ b/firmware/target/coldfire/pcm-coldfire.c @@ -301,21 +301,6 @@ void DMA0(void) /* else inished playing */ } /* DMA0 */ -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - unsigned long addr, cnt; - - /* Make sure interrupt doesn't change the second value after we read the - * first value. */ - int level = set_irq_level(DMA_IRQ_LEVEL); - addr = SAR0; - cnt = BCR0; - restore_irq(level); - - *count = (cnt & 0xffffff) >> 2; - return (void *)((addr + 2) & ~3); -} /* pcm_play_dma_get_peak_buffer */ - #ifdef HAVE_RECORDING /**************************************************************************** ** Recording DMA transfer diff --git a/firmware/target/hosted/android/pcm-android.c b/firmware/target/hosted/android/pcm-android.c index b078f92698..c23b802b2d 100644 --- a/firmware/target/hosted/android/pcm-android.c +++ b/firmware/target/hosted/android/pcm-android.c @@ -171,13 +171,6 @@ void pcm_play_dma_stop(void) stop_method); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - uintptr_t addr = (uintptr_t)pcm_data_start; - *count = pcm_data_size / 4; - return (void *)((addr + 3) & ~3); -} - void pcm_play_dma_init(void) { /* in order to have background music playing after leaving the activity, diff --git a/firmware/target/hosted/backtrace-glibc.c b/firmware/target/hosted/backtrace-glibc.c index 19becda38b..3a63499187 100644 --- a/firmware/target/hosted/backtrace-glibc.c +++ b/firmware/target/hosted/backtrace-glibc.c @@ -52,7 +52,7 @@ void rb_backtrace(int pc, int sp, unsigned *line) for(int i = 0; i < count; i++) { - lcd_putsf(0, (*line)++, " %s", buffer[i], strings[i]); + lcd_putsf(0, (*line)++, " %s", strings[i]); lcd_update(); } 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/filesystem-app.h b/firmware/target/hosted/filesystem-app.h index 68b3f13b6e..b35b63e95f 100644 --- a/firmware/target/hosted/filesystem-app.h +++ b/firmware/target/hosted/filesystem-app.h @@ -82,6 +82,7 @@ ssize_t app_write(int fildes, const void *buf, size_t nbyte); #endif /* HAVE_SDL_THREADS */ int app_remove(const char *path); int app_rename(const char *old, const char *new); +#define app_modtime os_modtime #define app_filesize os_filesize #define app_fsamefile os_fsamefile int app_relate(const char *path1, const char *path2); diff --git a/firmware/target/hosted/filesystem-hosted.h b/firmware/target/hosted/filesystem-hosted.h index 5147ef6db6..b9c7ce648a 100644 --- a/firmware/target/hosted/filesystem-hosted.h +++ b/firmware/target/hosted/filesystem-hosted.h @@ -33,6 +33,7 @@ void * os_lc_open(const char *ospath); #define _FILESYSTEM_HOSTED__FILE_H_ #ifndef OSFUNCTIONS_DECLARED +int os_modtime(const char *path, time_t modtime); off_t os_filesize(int osfd); int os_fsamefile(int osfd1, int osfd2); int os_relate(const char *path1, const char *path2); diff --git a/firmware/target/hosted/filesystem-unix.c b/firmware/target/hosted/filesystem-unix.c index 177cb574e0..f0d7de640d 100644 --- a/firmware/target/hosted/filesystem-unix.c +++ b/firmware/target/hosted/filesystem-unix.c @@ -23,6 +23,7 @@ #include <sys/stat.h> #include <string.h> #include <errno.h> +#include <utime.h> #include "config.h" #include "system.h" #include "file.h" @@ -35,6 +36,17 @@ #define SAME_FILE_INFO(sb1p, sb2p) \ ((sb1p)->st_dev == (sb2p)->st_dev && (sb1p)->st_ino == (sb2p)->st_ino) +int os_modtime(const char *path, time_t modtime) +{ + struct utimbuf times = + { + .actime = modtime, + .modtime = modtime, + }; + + return utime(path, ×); +} + off_t os_filesize(int osfd) { struct stat sb; diff --git a/firmware/target/hosted/filesystem-win32.c b/firmware/target/hosted/filesystem-win32.c index be95134cc9..fac10d003b 100644 --- a/firmware/target/hosted/filesystem-win32.c +++ b/firmware/target/hosted/filesystem-win32.c @@ -462,6 +462,13 @@ int os_relate(const char *ospath1, const char *ospath2) return rc; } +int os_modtime(const char *path, time_t modtime) +{ + (void)path; + (void)modtime; + return 0; +} + void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep) { ULARGE_INTEGER free = { .QuadPart = 0 }, diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h b/firmware/target/hosted/ibasso/dx50/adc-target.h index e69de29bb2..e69de29bb2 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h +++ b/firmware/target/hosted/ibasso/dx50/adc-target.h diff --git a/firmware/target/hosted/ibasso/dx90/adc-target.h b/firmware/target/hosted/ibasso/dx90/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/firmware/target/hosted/ibasso/dx90/adc-target.h diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.c b/firmware/target/hosted/ibasso/pcm-ibasso.c index e31732431b..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", @@ -439,17 +439,6 @@ void pcm_dma_apply_settings(void) } } -/* TODO: WTF */ -const void* pcm_play_dma_get_peak_buffer(int* count) -{ - TRACE; - - uintptr_t addr = (uintptr_t) _pcm_buffer; - *count = _pcm_buffer_size / 4; - return (void*) ((addr + 3) & ~3); -} - - void pcm_close_device(void) { TRACE; 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/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c index e2d57f9fab..33fa6d343f 100644 --- a/firmware/target/hosted/maemo/pcm-gstreamer.c +++ b/firmware/target/hosted/maemo/pcm-gstreamer.c @@ -191,14 +191,6 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused) unlock_audio(); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - uintptr_t addr = (uintptr_t)pcm_data; - *count = pcm_data_size / 4; - return (void *)((addr + 2) & ~3); -} - - static gboolean gst_bus_message (GstBus * bus, GstMessage * message, void *unused) { diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c index 81c0a19a45..68002d8158 100644 --- a/firmware/target/hosted/pcm-alsa.c +++ b/firmware/target/hosted/pcm-alsa.c @@ -68,6 +68,11 @@ #warning "MIX_FRAME_SAMPLES <1024 may cause dropouts!" #endif +/* PCM_DC_OFFSET_VALUE is a workaround for eros q hardware quirk */ +#if !defined(PCM_DC_OFFSET_VALUE) +# define PCM_DC_OFFSET_VALUE 0 +#endif + static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */ #if defined(HAVE_ALSA_32BIT) static const snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE; /* sample format */ @@ -359,8 +364,8 @@ static bool copy_frames(bool first) sample_t *sample_ptr = &frames[2*(period_size-frames_left)]; for (int i = 0; i < nframes; i++) { - *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_l; - *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_r; + *sample_ptr++ = (*pcm_ptr++ * dig_vol_mult_l) + PCM_DC_OFFSET_VALUE; + *sample_ptr++ = (*pcm_ptr++ * dig_vol_mult_r) + PCM_DC_OFFSET_VALUE; } } else @@ -775,13 +780,6 @@ void pcm_play_dma_start(const void *addr, size_t size) } } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - uintptr_t addr = (uintptr_t)pcm_data; - *count = pcm_size / 4; - return (void *)((addr + 3) & ~3); -} - void pcm_play_dma_postinit(void) { audiohw_postinit(); 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/rtc.c b/firmware/target/hosted/rtc.c index e747aece38..05c8f75ef7 100644 --- a/firmware/target/hosted/rtc.c +++ b/firmware/target/hosted/rtc.c @@ -54,6 +54,9 @@ int rtc_write_datetime(const struct tm *tm) tv.tv_sec = mktime((struct tm *)tm); tv.tv_usec = 0; + if ((int)tv.tv_sec == -1) + return -1; + /* set system clock */ settimeofday(&tv, NULL); diff --git a/firmware/target/hosted/samsungypr/radio-ypr.c b/firmware/target/hosted/samsungypr/radio-ypr.c index 42d485231d..ef7fb84aa0 100644 --- a/firmware/target/hosted/samsungypr/radio-ypr.c +++ b/firmware/target/hosted/samsungypr/radio-ypr.c @@ -126,62 +126,3 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count) (void)address; return read(radio_dev, buf, count); } - -#ifdef HAVE_RDS_CAP - -/* Register we are going to poll */ -#define STATUSRSSI (0xA) -#define STATUSRSSI_RDSR (0x1 << 15) - -/* Low-level RDS Support */ -static struct event_queue rds_queue; -static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)]; - -enum { - Q_POWERUP, -}; - -static void NORETURN_ATTR rds_thread(void) -{ - /* start up frozen */ - int timeout = TIMEOUT_BLOCK; - struct queue_event ev; - bool rds_rdy = false; - struct si4700_dbg_info radio_regs; - - while (true) { - queue_wait_w_tmo(&rds_queue, &ev, timeout); - switch (ev.id) { - case Q_POWERUP: - /* power up: timeout after 1 tick, else block indefinitely */ - timeout = ev.data ? 1 : TIMEOUT_BLOCK; - break; - case SYS_TIMEOUT: - /* Captures RDS data and processes it */ - si4700_dbg_info(&radio_regs); - bool rdsr = radio_regs.regs[STATUSRSSI] & STATUSRSSI_RDSR; - if (rdsr != rds_rdy) { - rds_rdy = rdsr; - if (rdsr) { - si4700_rds_process(); - } - } - break; - } - } -} - -/* true after full radio power up, and false before powering down */ -void si4700_rds_powerup(bool on) -{ - queue_post(&rds_queue, Q_POWERUP, on); -} - -/* One-time RDS init at startup */ -void si4700_rds_init(void) -{ - queue_init(&rds_queue, false); - create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" - IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); -} -#endif /* HAVE_RDS_CAP */ 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/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c index 7297e94340..940403002f 100644 --- a/firmware/target/hosted/sdl/pcm-sdl.c +++ b/firmware/target/hosted/sdl/pcm-sdl.c @@ -276,13 +276,6 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len) SDL_UnlockMutex(audio_lock); } -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - uintptr_t addr = (uintptr_t)pcm_data; - *count = pcm_data_size / 4; - return (void *)((addr + 2) & ~3); -} - #ifdef HAVE_RECORDING void pcm_rec_lock(void) { diff --git a/firmware/target/hosted/sdl/sim-ui-defines.h b/firmware/target/hosted/sdl/sim-ui-defines.h index 5b4030bd37..7d60deac34 100644 --- a/firmware/target/hosted/sdl/sim-ui-defines.h +++ b/firmware/target/hosted/sdl/sim-ui-defines.h @@ -514,7 +514,7 @@ #define UI_LCD_POSX 26 #define UI_LCD_POSY 40 -#elif defined(EROS_Q) +#elif defined(EROS_Q) || defined(EROS_QN) #define UI_TITLE "AIGO EROS Q" #define UI_WIDTH 400 /* width of GUI window */ #define UI_HEIGHT 653 /* height of GUI window */ @@ -523,10 +523,18 @@ #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) +#define UI_TITLE "Shanling Q1" +#define UI_WIDTH 466 +#define UI_HEIGHT 526 +#define UI_LCD_POSX 46 +#define UI_LCD_POSY 61 #elif defined(SIMULATOR) 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 a8b7dc82d3..c4ae5a404f 100644 --- a/firmware/target/hosted/system-hosted.c +++ b/firmware/target/hosted/system-hosted.c @@ -67,7 +67,7 @@ static void sig_handler(int sig, siginfo_t *siginfo, void *context) lcd_putsf(0, line++, "%s at %08x", strsignal(sig), pc); if(sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS || sig == SIGTRAP) - lcd_putsf(0, line++, "address 0x%08x", siginfo->si_addr); + lcd_putsf(0, line++, "address %p", siginfo->si_addr); if(!triggered) { @@ -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/hosted/xduoo/button-xduoo.c b/firmware/target/hosted/xduoo/button-xduoo.c index 568ad2614d..c06b60bd9f 100644 --- a/firmware/target/hosted/xduoo/button-xduoo.c +++ b/firmware/target/hosted/xduoo/button-xduoo.c @@ -58,13 +58,13 @@ int button_map(int keycode) #if defined(XDUOO_X3II) && defined(USE_REMOTE) /* Headphone remote */ case KEY_NEXTSONG: - return (BUTTON_NEXT | BUTTON_DELAY_RELEASE); + return headphones_inserted()? (BUTTON_NEXT | BUTTON_DELAY_RELEASE) : 0; case KEY_PLAYPAUSE: - return (BUTTON_PLAY | BUTTON_DELAY_RELEASE); + return headphones_inserted()? (BUTTON_PLAY | BUTTON_DELAY_RELEASE) : 0; case KEY_PREVIOUSSONG: - return (BUTTON_PREV | BUTTON_DELAY_RELEASE); + return headphones_inserted()? (BUTTON_PREV | BUTTON_DELAY_RELEASE) : 0; #endif default: 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/pcm-jz4740.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c index 83325e09ef..178da0b153 100644 --- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c @@ -184,47 +184,6 @@ void pcm_play_unlock(void) restore_irq(flags); } -static int get_dma_count(void) -{ - int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL); - switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK) - { - case DMAC_DCMD_DS_16BIT: - count *= 2; - break; - case DMAC_DCMD_DS_32BIT: - count *= 4; - break; - case DMAC_DCMD_DS_16BYTE: - count *= 16; - break; - } - - return count; -} - -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - int flags = disable_irq_save(); - - const void* addr; - if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN) - { - int bytes = get_dma_count(); - *count = bytes >> 2; - addr = (const void*)((int)(playback_address + bytes + 2) & ~3); - } - else - { - *count = 0; - addr = NULL; - } - - restore_irq(flags); - - return addr; -} - void audiohw_close(void) { /* TODO: prevent pop */ diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c index 401d568a97..72bcbeccd0 100644 --- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c +++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c @@ -172,44 +172,3 @@ void pcm_play_unlock(void) restore_irq(flags); } - -static int get_dma_count(void) -{ - int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL); - switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK) - { - case DMAC_DCMD_DS_16BIT: - count *= 2; - break; - case DMAC_DCMD_DS_32BIT: - count *= 4; - break; - case DMAC_DCMD_DS_16BYTE: - count *= 16; - break; - } - - return count; -} - -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - int flags = disable_irq_save(); - - const void* addr; - if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN) - { - int bytes = get_dma_count(); - *count = bytes >> 2; - addr = (const void*)((int)(playback_address + bytes + 2) & ~3); - } - else - { - *count = 0; - addr = NULL; - } - - restore_irq(flags); - - return addr; -} 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-jz4740.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c index 162aeec97a..07697da723 100644 --- a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c @@ -75,7 +75,7 @@ static unsigned char ep0_rx_buf[64]; static struct usb_endpoint endpoints[] = { { .type = ep_control, .fifo_addr = USB_FIFO_EP0, .fifo_size = 64 }, - { .type = ep_control, .fifo_addr = USB_FIFO_EP0, .buf = &ep0_rx_buf }, + { .type = ep_control, .fifo_addr = USB_FIFO_EP0, .buf = ep0_rx_buf }, { .type = ep_bulk, .fifo_addr = USB_FIFO_EP1, .fifo_size = 512 }, { .type = ep_bulk, .fifo_addr = USB_FIFO_EP1, .fifo_size = 512 }, { .type = ep_interrupt, .fifo_addr = USB_FIFO_EP2, .fifo_size = 64 }, @@ -193,7 +193,7 @@ static void EP0_send(void) if(ep->sent >= ep->length) { REG_USB_REG_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */ - usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent); + usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, 0, ep->sent); ep_transfer_completed(ep); } else @@ -239,7 +239,7 @@ static void EP0_handler(void) { readFIFO(ep_recv, REG_USB_REG_COUNT0); REG_USB_REG_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */ - usb_core_control_request((struct usb_ctrlrequest*)ep_recv->buf); + usb_core_legacy_control_request((struct usb_ctrlrequest*)ep_recv->buf); } } @@ -743,7 +743,7 @@ int usb_drv_send(int endpoint, void* ptr, int length) return 0; } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { int flags; struct usb_endpoint *ep; diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c index 37df1b3bb6..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) @@ -230,27 +215,28 @@ static void EP0_send(void) select_endpoint(0); csr0 = REG_USB_CSR0; - if(ep->sent == 0) - { + logf("%s(): 0x%x %d %d", __func__, csr0, ep->sent, ep->length); + + 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) - usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent); + 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) @@ -263,13 +249,12 @@ static void EP0_handler(void) select_endpoint(0); csr0 = REG_USB_CSR0; - logf("%s(): 0x%x", __func__, csr0); + logf("%s(): 0x%x %d", __func__, csr0, ep_send->busy); /* 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; } @@ -279,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(0, USB_DIR_IN, -1, 0); + usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0); ep_transfer_completed(ep_send); } - if (ep_recv->busy) - { - usb_core_transfer_complete(0, USB_DIR_OUT, -1, 0); + 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) - usb_core_transfer_complete(0, USB_DIR_IN, -1, 0); + 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(0, USB_DIR_OUT, 0, ep_recv->received); + 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_control_request(&ep0_rx.request); + 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 */ @@ -427,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); @@ -492,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; @@ -514,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; @@ -591,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"); @@ -611,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; } @@ -715,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; @@ -731,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; @@ -752,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; @@ -811,11 +786,11 @@ 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 usb_core_transfer_complete(0, USB_DIR_IN, -1, 0); + else + usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0); } endpoints[0].busy = false; @@ -825,7 +800,7 @@ static void udc_reset(void) endpoints[0].allocated = true; if (endpoints[1].busy) - usb_core_transfer_complete(0, USB_DIR_OUT, -1, 0); + usb_core_transfer_complete(EP_CONTROL, USB_DIR_OUT, -1, 0); endpoints[1].busy = false; endpoints[1].wait = false; @@ -834,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; @@ -861,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 } @@ -893,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; @@ -912,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 @@ -962,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); } @@ -976,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); @@ -993,7 +961,7 @@ void GPIO_USB_DET(void) void usb_enable(bool on) { - if(on) + if (on) usb_core_init(); else usb_core_exit(); @@ -1067,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 { @@ -1082,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; } @@ -1094,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; } @@ -1109,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; } @@ -1118,7 +1084,7 @@ int usb_drv_send(int endpoint, void* ptr, int length) return -1; } -int usb_drv_recv(int endpoint, void* ptr, int length) +int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length) { int flags; struct usb_endpoint *ep; @@ -1185,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 */ @@ -1201,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/aic-x1000.c b/firmware/target/mips/ingenic_x1000/aic-x1000.c index 1d1768d4f9..ff9802501d 100644 --- a/firmware/target/mips/ingenic_x1000/aic-x1000.c +++ b/firmware/target/mips/ingenic_x1000/aic-x1000.c @@ -84,8 +84,10 @@ static int calc_i2s_clock_params(x1000_clk_t clksrc, *i2sdiv = X1000_EXCLK_FREQ / 64 / fs; /* clamp to maximum value */ - if(*i2sdiv > 0x200) - *i2sdiv = 0x200; + if(*i2sdiv > 512) + *i2sdiv = 512; + if(*i2sdiv == 0) + *i2sdiv = 1; return 0; } 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 new file mode 100644 index 0000000000..eb476c513d --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/boot-x1000.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __BOOT_X1000_H__ +#define __BOOT_X1000_H__ + +#include "x1000/cpm.h" +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> + +enum { + /* Set after running clk_init() and setting up system clocks */ + BOOT_FLAG_CLK_INIT = (1 << 31), + + /* Set by the SPL if it was loaded over USB boot */ + 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... */ + +static inline void cpm_scratch_set(uint32_t value) +{ + /* TODO: see if this holds its state over a WDT reset */ + REG_CPM_SCRATCH_PROT = 0x5a5a; + REG_CPM_SCRATCH = value; + REG_CPM_SCRATCH_PROT = 0xa5a5; +} + +static inline void init_boot_flags(void) +{ + cpm_scratch_set(0); +} + +static inline bool get_boot_flag(uint32_t bit) +{ + return (REG_CPM_SCRATCH & bit) != 0; +} + +static inline void set_boot_flag(uint32_t bit) +{ + cpm_scratch_set(REG_CPM_SCRATCH | bit); +} + +static inline void clr_boot_flag(uint32_t bit) +{ + cpm_scratch_set(REG_CPM_SCRATCH & ~bit); +} + +#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 6cc18fb113..e3b0f792bb 100644 --- a/firmware/target/mips/ingenic_x1000/clk-x1000.c +++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c @@ -21,6 +21,7 @@ #include "system.h" #include "clk-x1000.h" +#include "boot-x1000.h" #include "x1000/cpm.h" #include "x1000/msc.h" #include "x1000/aic.h" @@ -225,7 +226,86 @@ const char* clk_get_name(x1000_clk_t clk) } } +/* At present we've only got 24 MHz targets, and they are all using + * the same "standard" configuration. */ +#if X1000_EXCLK_FREQ == 24000000 +void clk_init_early(void) +{ + jz_writef(CPM_MPCR, ENABLE(0)); + while(jz_readf(CPM_MPCR, ON)); + + /* 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); +} + +void clk_init(void) +{ + /* make sure we only initialize the clocks once */ + if(get_boot_flag(BOOT_FLAG_CLK_INIT)) + return; + + clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) | + CLKMUX_CPU(SCLK_A) | + CLKMUX_AHB0(SCLK_A) | + CLKMUX_AHB2(SCLK_A)); + clk_set_ccr_div(CLKDIV_CPU(1) | + CLKDIV_L2(1) | + CLKDIV_AHB0(1) | + CLKDIV_AHB2(1) | + CLKDIV_PCLK(1)); + + jz_writef(CPM_APCR, ENABLE(0)); + while(jz_readf(CPM_APCR, ON)); + + /* 24 MHz * 42 = 1008 MHz */ + 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) + /* TODO: Allow targets to define their clock frequencies in their config, + * instead of having this be a random special case. */ + 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 */ + set_boot_flag(BOOT_FLAG_CLK_INIT); +} +#else +# error "please write a new clk_init() for this EXCLK frequency" +#endif + #define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL) +#define CCR_DIV_BITS jz_orm(CPM_CCR, CDIV, L2DIV, H0DIV, H2DIV, PDIV) #define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX) #define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY) @@ -241,12 +321,14 @@ void clk_set_ccr_mux(uint32_t muxbits) while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS); } -void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk) +void clk_set_ccr_div(uint32_t divbits) { /* Set new divider configuration */ - jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1), - H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1), - CE_CPU(1), CE_AHB0(1), CE_AHB2(1)); + uint32_t reg = REG_CPM_CCR; + reg &= ~CCR_DIV_BITS; + reg |= divbits & CCR_DIV_BITS; + reg |= jz_orm(CPM_CCR, CE_CPU, CE_AHB0, CE_AHB2); + REG_CPM_CCR = reg; /* Wait until divider change completes */ while(REG_CPM_CSR & CSR_DIV_BITS); diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h index 2ff602db9f..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)) @@ -31,6 +32,13 @@ #define CLKMUX_AHB0(x) jz_orf(CPM_CCR, SEL_H0PLL_V(x)) #define CLKMUX_AHB2(x) jz_orf(CPM_CCR, SEL_H2PLL_V(x)) +/* Arguments to clk_set_ccr_div() */ +#define CLKDIV_CPU(x) jz_orf(CPM_CCR, CDIV((x) - 1)) +#define CLKDIV_L2(x) jz_orf(CPM_CCR, L2DIV((x) - 1)) +#define CLKDIV_AHB0(x) jz_orf(CPM_CCR, H0DIV((x) - 1)) +#define CLKDIV_AHB2(x) jz_orf(CPM_CCR, H2DIV((x) - 1)) +#define CLKDIV_PCLK(x) jz_orf(CPM_CCR, PDIV((x) - 1)) + typedef enum x1000_clk_t { X1000_CLK_EXCLK, X1000_CLK_APLL, @@ -59,23 +67,27 @@ extern uint32_t clk_get(x1000_clk_t clk); /* Get the name of a clock for debug purposes */ extern const char* clk_get_name(x1000_clk_t clk); +/* Clock initialization */ +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(int cpu, int l2, int ahb0, int ahb2, int pclk); +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 */ -inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq) +static inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq) { return (infreq + (outfreq - 1)) / outfreq; } /* Returns the smallest n such that (infreq >> n) <= outfreq */ -inline uint32_t clk_calc_shift(uint32_t infreq, uint32_t outfreq) +static inline uint32_t clk_calc_shift(uint32_t infreq, uint32_t outfreq) { uint32_t div = clk_calc_div(infreq, outfreq); return __builtin_clz(div) ^ 31; diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S index b36500b462..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,9 +21,12 @@ #include "config.h" #include "mips.h" +#include "bootdata.h" .text .extern main + .extern system_early_init + .extern _loadaddress .global _start .set push @@ -31,202 +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 */ - 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 fe469b1a72..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); @@ -149,9 +168,15 @@ static bool dbg_cpuidle(void) #ifdef FIIO_M3K extern bool dbg_fiiom3k_touchpad(void); #endif +#ifdef SHANLING_Q1 +extern bool dbg_shanlingq1_touchscreen(void); +#endif #ifdef HAVE_AXP_PMU extern bool axp_debug_menu(void); #endif +#ifdef HAVE_CW2015 +extern bool cw2015_debug_menu(void); +#endif /* Menu definition */ static const struct { @@ -167,9 +192,15 @@ static const struct { #ifdef FIIO_M3K {"Touchpad", &dbg_fiiom3k_touchpad}, #endif +#ifdef SHANLING_Q1 + {"Touchscreen", &dbg_shanlingq1_touchscreen}, +#endif #ifdef HAVE_AXP_PMU {"Power stats", &axp_debug_menu}, #endif +#ifdef HAVE_CW2015 + {"CW2015 debug", &cw2015_debug_menu}, +#endif }; static int hw_info_menu_action_cb(int btn, struct gui_synclist* lists) 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/adc-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c new file mode 100644 index 0000000000..df97aba0c8 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c @@ -0,0 +1,182 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, 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 "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" + +/* + * Earlier devices audio path appears to be: + * DAC \--> HP Amp --> Stereo Switch --> HP OUT + * \-> LO OUT + * + * 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_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); + aic_set_i2s_mode(AIC_I2S_MASTER_MODE); + audiohw_set_frequency(HW_FREQ_48); + + aic_enable_i2s_master_clock(true); + aic_enable_i2s_bit_clock(true); + + mdelay(10); + + /* power on DAC and HP Amp */ + gpio_set_level(GPIO_DAC_ANALOG_PWR, 1); + gpio_set_level(GPIO_HPAMP_POWER, 1); +} + +void audiohw_postinit(void) +{ + /* + * enable playback, fill FIFO buffer with -1 to prevent + * the DAC from auto-muting, wait, and then stop playback. + * This seems to completely prevent power-on or first-track + * clicking. + */ + jz_writef(AIC_CCR, ERPL(1)); + for (int i = 0; i < 32; i++) + { + jz_write(AIC_DR, 0xFFFFFF); + } + /* Wait until all samples are through the FIFO. */ + while(jz_readf(AIC_SR, TFL) != 0); + mdelay(20); /* This seems to silence the power-on click */ + jz_writef(AIC_CCR, ERPL(0)); + + /* unmute - attempt to make power-on pop-free */ + gpio_set_level(GPIO_STEREOSW_SEL, 0); + gpio_set_level(GPIO_HPAMP_SHDN, 1); + mdelay(10); + gpio_set_level(GPIO_DAC_PWR, 1); + mdelay(10); + 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); + } +} + +void audiohw_close(void) +{ + /* mute - attempt to make power-off pop-free */ + gpio_set_level(GPIO_STEREOSW_MUTE, 1); + mdelay(10); + gpio_set_level(GPIO_DAC_PWR, 0); + mdelay(10); + gpio_set_level(GPIO_HPAMP_SHDN, 0); +} + +void audiohw_set_frequency(int fsel) +{ + int sampr = hw_freq_sampr[fsel]; + int mult = 256; + + aic_enable_i2s_bit_clock(false); + aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, mult); + 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/backlight-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c new file mode 100644 index 0000000000..2c86a995db --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c @@ -0,0 +1,63 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, 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 "backlight.h" +#include "backlight-target.h" +#include "lcd.h" +#include "pwm-x1000.h" + +#define BL_LCD_CHN 0 +#define BL_LCD_PERIOD 30000 + +static int backlight_calc_duty(int period, int min_duty, int brightness) +{ + return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING; +} + +bool backlight_hw_init(void) +{ + pwm_init(BL_LCD_CHN); + pwm_enable(BL_LCD_CHN); + backlight_hw_brightness(MAX_BRIGHTNESS_SETTING); + return true; +} + +void backlight_hw_on(void) +{ + pwm_enable(BL_LCD_CHN); +#ifdef HAVE_LCD_ENABLE + lcd_enable(true); +#endif +} + +void backlight_hw_off(void) +{ + pwm_disable(BL_LCD_CHN); +#ifdef HAVE_LCD_ENABLE + lcd_enable(false); +#endif +} + +void backlight_hw_brightness(int brightness) +{ + int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness); + pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns); +} diff --git a/firmware/target/arm/imx233/samsung-ypz5/audio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h index c0bee26e45..05d6e36679 100644 --- a/firmware/target/arm/imx233/samsung-ypz5/audio-target.h +++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2013 by Lorenzo Miori + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,14 +18,16 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef __audio_target__ -#define __audio_target__ -/* MUTE_ON toggles a transistor that in turns toggles a mosfet... */ -#define IMX233_AUDIO_HP_GATE_BANK 1 -#define IMX233_AUDIO_HP_GATE_PIN 22 -#define IMX233_AUDIO_HP_GATE_INVERTED +#ifndef __BACKLIGHT_TARGET_H__ +#define __BACKLIGHT_TARGET_H__ -#define IMX233_AUDIO_COUPLING_MODE ACM_CAP +#include <stdbool.h> -#endif /* __audio_target__ */ +extern bool backlight_hw_init(void); + +extern void backlight_hw_on(void); +extern void backlight_hw_off(void); +extern void backlight_hw_brightness(int brightness); + +#endif /* __BACKLIGHT_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c new file mode 100644 index 0000000000..0d2207af2a --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c @@ -0,0 +1,251 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, 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 "button.h" +#include "kernel.h" +#include "backlight.h" +#include "powermgmt.h" +#include "panic.h" +#include "axp-pmu.h" +#include "gpio-x1000.h" +#include "irq-x1000.h" +#include "i2c-x1000.h" +#include "eros_qn_codec.h" +#include <string.h> +#include <stdbool.h> + +#ifndef BOOTLOADER +# include "lcd.h" +# include "font.h" +#endif + +/* =========================================== + * | OLD STATE | NEW STATE | DIRECTION | + * | 0 0 | 0 0 | 0: NO CHANGE | + * | 0 0 | 0 1 | -1: CCW | + * | 0 0 | 1 0 | 1: CW | + * | 0 0 | 1 1 | 0: INVALID | + * | 0 1 | 0 0 | 1: CW | + * | 0 1 | 0 1 | 0: NO CHANGE | + * | 0 1 | 1 0 | 0: INVALID | + * | 0 1 | 1 1 | -1: CCW | + * | 1 0 | 0 0 | -1: CCW | + * | 1 0 | 0 1 | 0: INVALID | + * | 1 0 | 1 0 | 0: NO CHANGE | + * | 1 0 | 1 1 | 1: CW | + * | 1 1 | 0 0 | 0: INVALID | + * | 1 1 | 0 1 | 1: CW | + * | 1 1 | 1 0 | -1: CCW | + * | 1 1 | 1 1 | 0: NO CHANGE | + * =========================================== + * + * Quadrature explanation since it's not plainly obvious how this works: + * + * If either of the quadrature lines change, we can look up the combination + * of previous state and new state in the table above (enc_state[] below) + * and it tells us whether to add 1, subtract 1, or no change from the sum (enc_position). + * This also gives us a nice debounce, since each state can only have 1 pin change + * at a time. I didn't come up with this, but I've used it before and it works well. + * + * Old state is 2 higher bits, new state is 2 lower bits of enc_current_state. */ + +/* list of valid quadrature states and their directions */ +signed char enc_state[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}; +volatile unsigned char enc_current_state = 0; +volatile signed int enc_position = 0; + +/* Value of headphone detect register */ +static uint8_t hp_detect_reg = 0x00; +static uint8_t hp_detect_reg_old = 0x00; + +/* Interval to poll the register */ +#define HPD_POLL_TIME (HZ/2) + +static int hp_detect_tmo_cb(struct timeout* tmo) +{ + i2c_descriptor* d = (i2c_descriptor*)tmo->data; + i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); + return HPD_POLL_TIME; +} + +static void hp_detect_init(void) +{ + static struct timeout tmo; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; + static i2c_descriptor desc = { + .slave_addr = AXP_PMU_ADDR, + .bus_cond = I2C_START | I2C_STOP, + .tran_mode = I2C_READ, + .buffer[0] = (void*)&gpio_reg, + .count[0] = 1, + .buffer[1] = &hp_detect_reg, + .count[1] = 1, + .callback = NULL, + .arg = 0, + .next = NULL, + }; + + /* Headphone and LO detects are wired to AXP192 GPIOs 0 and 1, + * set them to inputs. */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO0FUNCTION, 0x01); /* HP detect */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* LO detect */ + + /* Get an initial reading before startup */ + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + if(r >= 0) + { + hp_detect_reg = r; + hp_detect_reg_old = hp_detect_reg; + } + + /* Poll the register every second */ + timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc); +} + +bool headphones_inserted(void) +{ + /* if the status has changed, set the output volume accordingly */ + if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30)) + { + hp_detect_reg_old = hp_detect_reg; +#if !defined(BOOTLOADER) + eros_qn_set_outputs(); +#endif + } + return hp_detect_reg & 0x10 ? false : true; +} + +bool lineout_inserted(void) +{ + /* if the status has changed, set the output volume accordingly */ + if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30)) + { + hp_detect_reg_old = hp_detect_reg; +#if !defined(BOOTLOADER) + eros_qn_set_outputs(); +#endif + } + return hp_detect_reg & 0x20 ? false : true; +} + +/* Rockbox interface */ +void button_init_device(void) +{ + /* set both quadrature lines to interrupts */ + gpio_set_function(GPIO_BTN_SCROLL_A, GPIOF_IRQ_EDGE(1)); + gpio_set_function(GPIO_BTN_SCROLL_B, GPIOF_IRQ_EDGE(1)); + + /* set interrupts to fire on the next edge based on current state */ + gpio_flip_edge_irq(GPIO_BTN_SCROLL_A); + gpio_flip_edge_irq(GPIO_BTN_SCROLL_B); + + /* get current state of both encoder gpios */ + enc_current_state = (REG_GPIO_PIN(GPIO_B)>>21) & 0x0c; + + /* enable quadrature interrupts */ + gpio_enable_irq(GPIO_BTN_SCROLL_A); + gpio_enable_irq(GPIO_BTN_SCROLL_B); + + /* Set up headphone and line out detect polling */ + hp_detect_init(); +} + +/* wheel Quadrature line A interrupt */ +void GPIOB24(void) +{ + /* fill state with previous (2 higher bits) and current (2 lower bits) */ + enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03); + + /* look up in table */ + enc_position = enc_position + enc_state[(enc_current_state)]; + + /* move current state to previous state if valid data */ + if (enc_state[(enc_current_state)] != 0) + enc_current_state = (enc_current_state << 2); + + /* we want the other edge next time */ + gpio_flip_edge_irq(GPIO_BTN_SCROLL_A); +} + +/* wheel Quadrature line B interrupt */ +void GPIOB23(void) +{ + /* fill state with previous (2 higher bits) and current (2 lower bits) */ + enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03); + + /* look up in table */ + enc_position = enc_position + enc_state[(enc_current_state)]; + + /* move current state to previous state if valid data */ + if (enc_state[(enc_current_state)] != 0) + enc_current_state = (enc_current_state << 2); + + /* we want the other edge next time */ + gpio_flip_edge_irq(GPIO_BTN_SCROLL_B); +} + +int button_read_device(void) +{ + int r = 0; + + /* Read GPIOs for normal buttons */ + uint32_t a = REG_GPIO_PIN(GPIO_A); + uint32_t b = REG_GPIO_PIN(GPIO_B); + uint32_t c = REG_GPIO_PIN(GPIO_C); + uint32_t d = REG_GPIO_PIN(GPIO_D); + + /* All buttons are active low */ + if((a & (1 << 16)) == 0) r |= BUTTON_PLAY; + if((a & (1 << 17)) == 0) r |= BUTTON_VOL_UP; + if((a & (1 << 19)) == 0) r |= BUTTON_VOL_DOWN; + + if((b & (1 << 7)) == 0) r |= BUTTON_POWER; + if((b & (1 << 28)) == 0) r |= BUTTON_MENU; + if((b & (1 << 28)) == 0) r |= BUTTON_MENU; + + if((d & (1 << 4)) == 0) r |= BUTTON_PREV; + + if((d & (1 << 5)) == 0) r |= BUTTON_BACK; + if((c & (1 << 24)) == 0) r |= BUTTON_NEXT; + + /* check encoder - from testing, each indent is 2 state changes or so */ + if (enc_position > 1) + { + /* need to use queue_post() in order to do BUTTON_SCROLL_*, + * 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) + { + /* need to use queue_post() in order to do BUTTON_SCROLL_*, + * 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/arm/tatung/tpj1022/button-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h index 1143c1b1da..9c39a40296 100644 --- a/firmware/target/arm/tatung/tpj1022/button-target.h +++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h @@ -5,9 +5,8 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id$ * - * Copyright (C) 2006 by Robert Kukla + * Copyright (C) 2021 Solomon Peachy, Dana Conrad * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,34 +17,29 @@ * KIND, either express or implied. * ****************************************************************************/ - #ifndef _BUTTON_TARGET_H_ #define _BUTTON_TARGET_H_ -#define HAS_BUTTON_HOLD - /* Main unit's buttons */ - -#define BUTTON_VOL_DOWN 0x00000001 - -/* bit position in GPIOA */ -#define BUTTON_REW 0x00000002 -#define BUTTON_FF 0x00000004 -#define BUTTON_POWER 0x00000008 -#define BUTTON_UP 0x00000010 -#define BUTTON_DOWN 0x00000020 -#define BUTTON_AB 0x00000040 -#define BUTTON_RIGHT 0x00000080 - -/* still unknown */ -#define BUTTON_MENU 0x00000100 -#define BUTTON_REC 0x00000200 -#define BUTTON_VOL_UP 0x00000400 -#define BUTTON_LEFT 0x00000800 - -#define BUTTON_MAIN 0x00000FFF - +#define BUTTON_POWER 0x00000001 +#define BUTTON_MENU 0x00000002 +#define BUTTON_BACK 0x00000004 +#define BUTTON_PLAY 0x00000008 +#define BUTTON_NEXT 0x00000010 +#define BUTTON_PREV 0x00000020 +#define BUTTON_VOL_UP 0x00000040 +#define BUTTON_VOL_DOWN 0x00000080 +#define BUTTON_SCROLL_BACK 0x00000100 +#define BUTTON_SCROLL_FWD 0x00000200 + +#define BUTTON_MAIN (BUTTON_POWER | BUTTON_MENU | BUTTON_BACK | BUTTON_PREV | \ + BUTTON_NEXT | BUTTON_PLAY | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD) + +#define BUTTON_LEFT BUTTON_PREV +#define BUTTON_RIGHT BUTTON_NEXT + +/* Software power-off */ #define POWEROFF_BUTTON BUTTON_POWER -#define POWEROFF_COUNT 10 +#define POWEROFF_COUNT 25 #endif /* _BUTTON_TARGET_H_ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h new file mode 100644 index 0000000000..72052c261f --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h @@ -0,0 +1,75 @@ +/* -------------------- NOTES ------------------- */ + +/* I don't think we have any devices on I2C1, the pins /may/ be reused. */ +/* DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0)) */ + +/* OF has SD Card power listed as 0x2a - PB10, but it seems to work without. */ + +/* I think BT power reg is pin 0x53 - C19 */ + +/* USB_DETECT D3 chosen by trial-and-error. */ + +/* I have a suspicion this isn't right for AXP_IRQ, + * and it's not used right now anyway. copied from m3k. */ +/* DEFINE_GPIO(AXP_IRQ, GPIO_PB(10), GPIOF_INPUT) */ + +/* ---------------------------------------------- */ + + +/* 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 */ +/* 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: 0 - mute, 1 - play */ +DEFINE_GPIO(HPAMP_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0)) + +/* mute audio mux: 0 - play, 1 - mute */ +DEFINE_GPIO(STEREOSW_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1)) + +/* + * 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(DAC_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0)) + +/* Headphone Amp power */ +DEFINE_GPIO(HPAMP_POWER, GPIO_PB(6), GPIOF_OUTPUT(0)) + +/* SD card */ +DEFINE_GPIO(MSC0_CD, GPIO_PB(11), GPIOF_INPUT) + +/* USB */ +DEFINE_GPIO(USB_DETECT, GPIO_PD(3), GPIOF_INPUT) +DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0)) + +/* LCD */ +DEFINE_GPIO(LCD_PWR, GPIO_PB(14), GPIOF_OUTPUT(0)) +DEFINE_GPIO(LCD_RESET, GPIO_PB(13), GPIOF_OUTPUT(0)) +DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1)) +DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1)) + +/* Buttons */ +DEFINE_GPIO(BTN_PLAY, GPIO_PA(16), GPIOF_INPUT) +DEFINE_GPIO(BTN_VOL_UP, GPIO_PA(17), GPIOF_INPUT) +DEFINE_GPIO(BTN_VOL_DOWN, GPIO_PA(19), GPIOF_INPUT) +DEFINE_GPIO(BTN_POWER, GPIO_PB(7), GPIOF_INPUT) +DEFINE_GPIO(BTN_MENU, GPIO_PB(28), GPIOF_INPUT) +DEFINE_GPIO(BTN_BACK, GPIO_PD(5), GPIOF_INPUT) +DEFINE_GPIO(BTN_PREV, GPIO_PD(4), GPIOF_INPUT) +DEFINE_GPIO(BTN_NEXT, GPIO_PC(24), GPIOF_INPUT) +DEFINE_GPIO(BTN_SCROLL_A, GPIO_PB(24), GPIOF_INPUT) +DEFINE_GPIO(BTN_SCROLL_B, GPIO_PB(23), GPIOF_INPUT) diff --git a/firmware/target/arm/tatung/tpj1022/backlight-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h index 4a5e677599..89d995f33a 100644 --- a/firmware/target/arm/tatung/tpj1022/backlight-target.h +++ b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2006 by Barry Wardell + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,13 +19,16 @@ * ****************************************************************************/ -/* Taken from the x5's implementation */ +#ifndef __I2C_TARGET_H__ +#define __I2C_TARGET_H__ -#ifndef BACKLIGHT_TARGET_H -#define BACKLIGHT_TARGET_H +#define I2C_ASYNC_BUS_COUNT 3 +#define I2C_ASYNC_QUEUE_SIZE 4 -#define backlight_hw_init() true -void backlight_hw_on(void); -void backlight_hw_off(void); +#define ES9018K2M_BUS 1 +#define ES9018K2M_ADDR 0x48 -#endif +#define AXP_PMU_BUS 2 +#define AXP_PMU_ADDR 0x34 + +#endif /* __I2C_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c new file mode 100644 index 0000000000..0d43a3f010 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c @@ -0,0 +1,211 @@ + +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, 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 "lcd.h" +#include "kernel.h" +#include "lcd-x1000.h" +#include "gpio-x1000.h" +#include "system.h" + +/* for reference on these command/data hex values, see the mipi dcs lcd spec. * + * Not everything here is there, but all the standard stuff is. */ + +static const uint32_t erosqnative_lcd_cmd_enable[] = { + /* Set EXTC? */ + LCD_INSTR_CMD, 0xc8, + LCD_INSTR_DAT, 0xff, + LCD_INSTR_DAT, 0x93, + LCD_INSTR_DAT, 0x42, + /* Set Address Mode */ + LCD_INSTR_CMD, 0x36, + LCD_INSTR_DAT, 0xd8, + /* Pixel Format Set */ + LCD_INSTR_CMD, 0x3a, + //LCD_INSTR_DAT, 0x66, /* OF specified 18 bpp */ + LCD_INSTR_DAT, 0x05, /* RB seems to be happier dealing with 16 bits/pixel */ + /* Power Control 1? */ + LCD_INSTR_CMD, 0xc0, + LCD_INSTR_DAT, 0x15, + LCD_INSTR_DAT, 0x15, + /* Power Control 2? */ + LCD_INSTR_CMD, 0xc1, + LCD_INSTR_DAT, 0x01, + /* VCOM? */ + LCD_INSTR_CMD, 0xc5, + LCD_INSTR_DAT, 0xda, + /* ?? */ + LCD_INSTR_CMD, 0xb1, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x1b, + /* ?? */ + LCD_INSTR_CMD, 0xb4, + LCD_INSTR_DAT, 0x02, + /* Positive gamma correction? */ + LCD_INSTR_CMD, 0xe0, + LCD_INSTR_DAT, 0x0f, + LCD_INSTR_DAT, 0x13, + LCD_INSTR_DAT, 0x17, + LCD_INSTR_DAT, 0x04, + LCD_INSTR_DAT, 0x13, + LCD_INSTR_DAT, 0x07, + LCD_INSTR_DAT, 0x40, + LCD_INSTR_DAT, 0x39, + LCD_INSTR_DAT, 0x4f, + LCD_INSTR_DAT, 0x06, + LCD_INSTR_DAT, 0x0d, + LCD_INSTR_DAT, 0x0a, + LCD_INSTR_DAT, 0x1f, + LCD_INSTR_DAT, 0x22, + LCD_INSTR_DAT, 0x00, + /* Negative gamma correction? */ + LCD_INSTR_CMD, 0xe1, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x21, + LCD_INSTR_DAT, 0x24, + LCD_INSTR_DAT, 0x03, + LCD_INSTR_DAT, 0x0f, + LCD_INSTR_DAT, 0x05, + LCD_INSTR_DAT, 0x38, + LCD_INSTR_DAT, 0x32, + LCD_INSTR_DAT, 0x49, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x09, + LCD_INSTR_DAT, 0x08, + LCD_INSTR_DAT, 0x32, + LCD_INSTR_DAT, 0x35, + LCD_INSTR_DAT, 0x0f, + /* Exit Sleep */ + LCD_INSTR_CMD, 0x11, + LCD_INSTR_UDELAY, 120000, + /* 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 */ +static const uint32_t erosqnative_lcd_cmd_sleep[] = { + /* Display OFF */ + LCD_INSTR_CMD, 0x28, + /* Sleep IN */ + LCD_INSTR_CMD, 0x10, + LCD_INSTR_UDELAY, 5000, + LCD_INSTR_END, +}; + +static const uint32_t erosqnative_lcd_cmd_wake[] = { + /* Sleep OUT */ + LCD_INSTR_CMD, 0x11, + LCD_INSTR_UDELAY, 5000, + /* Display ON */ + LCD_INSTR_CMD, 0x29, + LCD_INSTR_END, +}; + +/* As far as I can tell, this is a sequence of commands sent before each + * DMA set. Original in OF was: + * {0x2c, 0x2c, 0x2c, 0x2c} + * But this set from the m3k seems to work the same, and makes more sense + * to me: + * {0x00, 0x00, 0x00, 0x2c} + * This command is more than likely going to be the same + * for any old mipi lcd on the market, maybe. I really don't think we need + * to send "write_memory_start four times in a row. */ +static const uint8_t __attribute__((aligned(64))) + erosqnative_lcd_dma_wr_cmd[] = {0x2c, 0x2c, 0x2c, 0x2c}; + +const struct lcd_tgt_config lcd_tgt_config = { + .bus_width = 8, + .cmd_width = 8, + .use_6800_mode = 0, + .use_serial = 0, + .clk_polarity = 0, + .dc_polarity = 0, + .wr_polarity = 1, + .te_enable = 0, /* OF had TE enabled (1) */ + .te_polarity = 1, + .te_narrow = 0, + .dma_wr_cmd_buf = &erosqnative_lcd_dma_wr_cmd, + .dma_wr_cmd_size = sizeof(erosqnative_lcd_dma_wr_cmd), +}; + +void lcd_tgt_enable(bool enable) +{ + if(enable) { + /* power up the panel */ + gpio_set_level(GPIO_LCD_PWR, 1); + mdelay(20); + gpio_set_level(GPIO_LCD_RESET, 1); + mdelay(12); + + /* set the clock */ + lcd_set_clock(X1000_CLK_SCLK_A, 20000000); + + /* toggle chip select low (active) */ + gpio_set_level(GPIO_LCD_RD, 1); + gpio_set_level(GPIO_LCD_CE, 1); + mdelay(5); + gpio_set_level(GPIO_LCD_CE, 0); + + lcd_exec_commands(&erosqnative_lcd_cmd_enable[0]); + } else { + /* doesn't flash white if we don't do anything... */ +#if 0 + lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]); + + mdelay(115); // copied from m3k + + gpio_set_level(GPIO_LCD_PWR, 0); + gpio_set_level(GPIO_LCD_RESET, 0); +#endif + } +} + +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) + lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]); + else + lcd_exec_commands(&erosqnative_lcd_cmd_wake[0]); +} diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c new file mode 100644 index 0000000000..ab6393a9fe --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c @@ -0,0 +1,130 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, 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. + * + ****************************************************************************/ + +// whole file copied from m3k + +#include "power.h" +#include "adc.h" +#include "system.h" +#include "kernel.h" +#ifdef HAVE_USB_CHARGING_ENABLE +# include "usb_core.h" +#endif +#include "axp-pmu.h" +#include "i2c-x1000.h" + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3470 +}; + +/* The OF shuts down at this voltage */ +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3400 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + { 3400, 3477, 3540, 3578, 3617, 3674, 3771, 3856, 3936, 4016, 4117 } +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + 3400, 3477, 3540, 3578, 3617, 3674, 3771, 3856, 3936, 4016, 4117 +}; + +void power_init(void) +{ + /* Initialize driver */ + i2c_x1000_set_freq(2, I2C_FREQ_400K); + axp_init(); + + /* 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)); + + /* Turn on all power outputs */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); + + /* Set the default charging current. This is the same as the + * 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 +void usb_charging_maxcurrent_change(int maxcurrent) +{ + axp_set_charge_current(maxcurrent); +} +#endif + +void adc_init(void) +{ +} + +void power_off(void) +{ + axp_power_off(); + while(1); +} + +bool charging_state(void) +{ + return axp_battery_status() == AXP_BATT_CHARGING; +} + +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/boot.make b/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make deleted file mode 100644 index 77b23167c2..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make +++ /dev/null @@ -1,30 +0,0 @@ -# __________ __ ___. -# Open \______ \ ____ ____ | | _\_ |__ _______ ___ -# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / -# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < -# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ -# \/ \/ \/ \/ \/ -# $Id$ -# - -include $(ROOTDIR)/lib/microtar/microtar.make - -.SECONDEXPANSION: - -$(BUILDDIR)/spl.m3k: $(BUILDDIR)/spl.bin - $(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@ - -$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin - $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null - -.PHONY: $(BUILDDIR)/bootloader-info.txt -$(BUILDDIR)/bootloader-info.txt: - $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@ - -$(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.m3k \ - $(BUILDDIR)/bootloader.ucl \ - $(BUILDDIR)/bootloader-info.txt - $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \ - --numeric-owner --no-acls --no-xattrs --no-selinux \ - --mode=0644 --owner=0 --group=0 \ - -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^) diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c index 4354257f7b..24daf2ef69 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c @@ -45,11 +45,11 @@ #define STATE_SCROLLING 4 /* Assume there's no active touch if no event is reported in this time */ -#define AUTORELEASE_TIME (10000 * OST_TICKS_PER_US) +#define AUTORELEASE_TIME (40 * 1000 * OST_TICKS_PER_US) /* If there's no significant motion on the scrollbar for this time, * then report it as a button press instead */ -#define SCROLL_PRESS_TIME (100000 * OST_TICKS_PER_US) +#define SCROLL_PRESS_TIME (400 * 1000 * OST_TICKS_PER_US) /* If a press on the scrollbar moves more than this during SCROLL_PRESS_TIME, * then we enter scrolling mode. */ @@ -318,7 +318,7 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty) } } -static void ft_event_cb(int evt, int tx, int ty) +static void ft_event_cb(struct ft6x06_state* state) { /* TODO: convert the touch positions to linear positions. * @@ -327,7 +327,8 @@ static void ft_event_cb(int evt, int tx, int ty) * the middle of the touchpad than on the edges, so scrolling feels slow * in the middle and faster near the edge. */ - ft_step_state(__ost_read32(), evt, tx, ty); + struct ft6x06_point* pt = &state->points[0]; + ft_step_state(__ost_read32(), pt->event, pt->pos_x, pt->pos_y); } static void ft_init(void) diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c deleted file mode 100644 index 10a58ace38..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c +++ /dev/null @@ -1,283 +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 "installer-fiiom3k.h" -#include "nand-x1000.h" -#include "system.h" -#include "core_alloc.h" -#include "file.h" -#include "microtar.h" -#include <stdint.h> -#include <string.h> -#include <stdio.h> - -#define IMAGE_SIZE (128 * 1024) -#define TAR_SIZE (256 * 1024) - -static int flash_prepare(void) -{ - int mf_id, dev_id; - int rc; - - rc = nand_open(); - if(rc < 0) - return INSTALL_ERR_FLASH(NAND_OPEN, rc); - - rc = nand_identify(&mf_id, &dev_id); - if(rc < 0) { - nand_close(); - return INSTALL_ERR_FLASH(NAND_IDENTIFY, rc); - } - - return INSTALL_SUCCESS; -} - -static void flash_finish(void) -{ - /* Ensure writes are always disabled when we finish. - * Errors are safe to ignore here, there's nothing we could do anyway. */ - nand_enable_writes(false); - nand_close(); -} - -static int flash_img_read(uint8_t* buffer) -{ - int rc = flash_prepare(); - if(rc < 0) - goto error; - - rc = nand_read(0, IMAGE_SIZE, buffer); - if(rc < 0) { - rc = INSTALL_ERR_FLASH(NAND_READ, rc); - goto error; - } - - error: - flash_finish(); - return rc; -} - -static int flash_img_write(const uint8_t* buffer) -{ - int rc = flash_prepare(); - if(rc < 0) - goto error; - - rc = nand_enable_writes(true); - if(rc < 0) { - rc = INSTALL_ERR_FLASH(NAND_ENABLE_WRITES, rc); - goto error; - } - - rc = nand_erase(0, IMAGE_SIZE); - if(rc < 0) { - rc = INSTALL_ERR_FLASH(NAND_ERASE, rc); - goto error; - } - - rc = nand_write(0, IMAGE_SIZE, buffer); - if(rc < 0) { - rc = INSTALL_ERR_FLASH(NAND_WRITE, rc); - goto error; - } - - error: - flash_finish(); - return rc; -} - -static int patch_img(mtar_t* tar, uint8_t* buffer, const char* filename, - size_t patch_offset, size_t patch_size) -{ - /* Seek to file */ - mtar_header_t h; - int rc = mtar_find(tar, filename, &h); - if(rc != MTAR_ESUCCESS) { - rc = INSTALL_ERR_MTAR(TAR_FIND, rc); - return rc; - } - - /* We need a normal file */ - if(h.type != 0 && h.type != MTAR_TREG) - return INSTALL_ERR_BAD_FORMAT; - - /* Check size does not exceed patch area */ - if(h.size > patch_size) - return INSTALL_ERR_BAD_FORMAT; - - /* Read data directly into patch area, fill unused bytes with 0xff */ - memset(&buffer[patch_offset], 0xff, patch_size); - rc = mtar_read_data(tar, &buffer[patch_offset], h.size); - if(rc != MTAR_ESUCCESS) { - rc = INSTALL_ERR_MTAR(TAR_READ, rc); - return rc; - } - - return INSTALL_SUCCESS; -} - -int install_boot(const char* srcfile) -{ - int rc; - mtar_t* tar = NULL; - int handle = -1; - - /* Allocate enough memory for image and tar state */ - size_t bufsize = IMAGE_SIZE + sizeof(mtar_t) + 2*CACHEALIGN_SIZE; - handle = core_alloc("boot_image", bufsize); - if(handle < 0) { - rc = INSTALL_ERR_OUT_OF_MEMORY; - goto error; - } - - uint8_t* buffer = core_get_data(handle); - - /* Tar state alloc */ - CACHEALIGN_BUFFER(buffer, bufsize); - tar = (mtar_t*)buffer; - memset(tar, 0, sizeof(tar)); - - /* Image buffer alloc */ - buffer += sizeof(mtar_t); - CACHEALIGN_BUFFER(buffer, bufsize); - - /* Read the flash -- we need an existing image to patch */ - rc = flash_img_read(buffer); - if(rc < 0) - goto error; - - /* Open the tarball */ - rc = mtar_open(tar, srcfile, "r"); - if(rc != MTAR_ESUCCESS) { - rc = INSTALL_ERR_MTAR(TAR_OPEN, rc); - goto error; - } - - /* Extract the needed files & patch 'em in */ - rc = patch_img(tar, buffer, "spl.m3k", 0, 12 * 1024); - if(rc < 0) - goto error; - - rc = patch_img(tar, buffer, "bootloader.ucl", 0x6800, 102 * 1024); - if(rc < 0) - goto error; - - /* Flash the new image */ - rc = flash_img_write(buffer); - if(rc < 0) - goto error; - - rc = INSTALL_SUCCESS; - - error: - if(tar && tar->close) - mtar_close(tar); - if(handle >= 0) - core_free(handle); - return rc; -} - -int backup_boot(const char* destfile) -{ - int rc; - int handle = -1; - int fd = -1; - size_t bufsize = IMAGE_SIZE + CACHEALIGN_SIZE - 1; - handle = core_alloc("boot_image", bufsize); - if(handle < 0) { - rc = INSTALL_ERR_OUT_OF_MEMORY; - goto error; - } - - uint8_t* buffer = core_get_data(handle); - CACHEALIGN_BUFFER(buffer, bufsize); - - rc = flash_img_read(buffer); - if(rc < 0) - goto error; - - fd = open(destfile, O_CREAT|O_TRUNC|O_WRONLY); - if(fd < 0) { - rc = INSTALL_ERR_FILE_IO; - goto error; - } - - ssize_t cnt = write(fd, buffer, IMAGE_SIZE); - if(cnt != IMAGE_SIZE) { - rc = INSTALL_ERR_FILE_IO; - goto error; - } - - error: - if(fd >= 0) - close(fd); - if(handle >= 0) - core_free(handle); - return rc; -} - -int restore_boot(const char* srcfile) -{ - int rc; - int handle = -1; - int fd = -1; - size_t bufsize = IMAGE_SIZE + CACHEALIGN_SIZE - 1; - handle = core_alloc("boot_image", bufsize); - if(handle < 0) { - rc = INSTALL_ERR_OUT_OF_MEMORY; - goto error; - } - - uint8_t* buffer = core_get_data(handle); - CACHEALIGN_BUFFER(buffer, bufsize); - - fd = open(srcfile, O_RDONLY); - if(fd < 0) { - rc = INSTALL_ERR_FILE_NOT_FOUND; - goto error; - } - - off_t fsize = filesize(fd); - if(fsize != IMAGE_SIZE) { - rc = INSTALL_ERR_BAD_FORMAT; - goto error; - } - - ssize_t cnt = read(fd, buffer, IMAGE_SIZE); - if(cnt != IMAGE_SIZE) { - rc = INSTALL_ERR_FILE_IO; - goto error; - } - - close(fd); - fd = -1; - - rc = flash_img_write(buffer); - if(rc < 0) - goto error; - - error: - if(fd >= 0) - close(fd); - if(handle >= 0) - core_free(handle); - return rc; -} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h deleted file mode 100644 index eb700e6689..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h +++ /dev/null @@ -1,51 +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. - * - ****************************************************************************/ - -#ifndef __INSTALLER_FIIOM3K_H__ -#define __INSTALLER_FIIOM3K_H__ - -#include <stddef.h> - -#define INSTALL_SUCCESS 0 -#define INSTALL_ERR_OUT_OF_MEMORY (-1) -#define INSTALL_ERR_FILE_NOT_FOUND (-2) -#define INSTALL_ERR_FILE_IO (-3) -#define INSTALL_ERR_BAD_FORMAT (-4) -#define INSTALL_ERR_NAND_OPEN (-5) -#define INSTALL_ERR_NAND_IDENTIFY (-6) -#define INSTALL_ERR_NAND_READ (-7) -#define INSTALL_ERR_NAND_ENABLE_WRITES (-8) -#define INSTALL_ERR_NAND_ERASE (-9) -#define INSTALL_ERR_NAND_WRITE (-10) -#define INSTALL_ERR_TAR_OPEN (-11) -#define INSTALL_ERR_TAR_FIND (-12) -#define INSTALL_ERR_TAR_READ (-13) -#define INSTALL_ERR_MTAR(x,y) ((INSTALL_ERR_##x)*100 + (y)) -#define INSTALL_ERR_FLASH(x,y) ((INSTALL_ERR_##x)*100 + (y)) - -/* Install the Rockbox bootloader from a bootloader.m3k image */ -extern int install_boot(const char* srcfile); - -/* Backup or restore the bootloader from a raw NAND image */ -extern int backup_boot(const char* destfile); -extern int restore_boot(const char* srcfile); - -#endif /* __INSTALLER_FIIOM3K_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c index 6b1ad2dbb5..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, @@ -77,7 +82,7 @@ void power_init(void) axp_set_charge_current(780); /* Short delay to give power outputs time to stabilize */ - mdelay(5); + mdelay(200); } #ifdef HAVE_USB_CHARGING_ENABLE @@ -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 7c56e4ac19..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c +++ /dev/null @@ -1,309 +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 "ucl_decompress.h" -#include "spl-x1000.h" -#include "gpio-x1000.h" -#include "clk-x1000.h" -#include "nand-x1000.h" -#include "x1000/cpm.h" -#include <string.h> -#include <stdint.h> - -/* Available boot options */ -#define BOOTOPTION_ROCKBOX 0 -#define BOOTOPTION_ORIG_FW 1 -#define BOOTOPTION_RECOVERY 2 - -/* Boot select button state must remain stable for this duration - * before the choice will be accepted. Currently 100ms. - */ -#define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000)) - -static const char normal_cmdline[] = "mem=64M@0x0\ - no_console_suspend\ - console=ttyS2,115200n8\ - lpj=5009408\ - ip=off\ - init=/linuxrc\ - ubi.mtd=3\ - root=ubi0:rootfs\ - ubi.mtd=4\ - rootfstype=ubifs\ - rw\ - loglevel=8"; - -static const char recovery_cmdline[] = "mem=64M@0x0\ - no_console_suspend\ - console=ttyS2,115200n8\ - lpj=5009408\ - ip=off"; - -/* Entry point function type, defined to be Linux compatible. */ -typedef void(*entry_fn)(int, char**, int, int); - -struct spl_boot_option { - uint32_t nand_addr; - uint32_t nand_size; - uint32_t load_addr; - uint32_t exec_addr; - const char* cmdline; /* for Linux */ -}; - -const struct spl_boot_option spl_boot_options[] = { - { - /* Rockbox: the first unused NAND page is 26 KiB in, and the - * remainder of the block is unused, giving us 102 KiB to use. - */ - .nand_addr = 0x6800, - .nand_size = 0x19800, - .load_addr = X1000_DRAM_END - 0x19800, - .exec_addr = X1000_DRAM_BASE, - .cmdline = NULL, - }, - { - /* Original firmware */ - .nand_addr = 0x20000, - .nand_size = 0x400000, - .load_addr = 0x80efffc0, - .exec_addr = 0x80f00000, - .cmdline = normal_cmdline, - }, - { - /* Recovery image */ - .nand_addr = 0x420000, - .nand_size = 0x500000, - .load_addr = 0x80efffc0, - .exec_addr = 0x80f00000, - .cmdline = recovery_cmdline, - }, -}; - -void spl_error(void) -{ - const uint32_t pin = (1 << 24); - - /* Turn on button light */ - 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); - - while(1) { - /* Turn it off */ - mdelay(100); - jz_set(GPIO_PAT0(GPIO_C), pin); - - /* Turn it on */ - mdelay(100); - jz_clr(GPIO_PAT0(GPIO_C), pin); - } -} - -void spl_target_boot(void) -{ - int opt_index = spl_get_boot_option(); - const struct spl_boot_option* opt = &spl_boot_options[opt_index]; - uint8_t* load_addr = (uint8_t*)opt->load_addr; - - /* Clock setup etc. */ - spl_handle_pre_boot(opt_index); - - /* Since the GPIO refactor, the SFC driver no longer assigns its own pins. - * We don't want to call gpio_init(), to keep code size down. Assign them - * here manually, we shouldn't need any other pins. */ - gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1)); - - /* Open NAND chip */ - int rc = nand_open(); - if(rc) - spl_error(); - - int mf_id, dev_id; - rc = nand_identify(&mf_id, &dev_id); - if(rc) - goto nand_err; - - /* For OF only: load DMA coprocessor's firmware from flash */ - if(opt_index != BOOTOPTION_ROCKBOX) { - rc = nand_read(0x4000, 0x2000, (uint8_t*)0xb3422000); - if(rc) - goto nand_err; - } - - /* Read the firmware */ - rc = nand_read(opt->nand_addr, opt->nand_size, load_addr); - if(rc) - goto nand_err; - - /* Rockbox doesn't need the NAND; for the OF, we should leave it open - * and also make sure to turn off the write protect bits. */ - if(opt_index == BOOTOPTION_ROCKBOX) - nand_close(); - else - nand_enable_writes(true); - - /* Kernel arguments pointer, for Linux only */ - char** kargv = (char**)0x80004000; - - if(!opt->cmdline) { - /* Uncompress the rockbox bootloader */ - uint32_t out_size = X1000_DRAM_END - opt->exec_addr; - int rc = ucl_unpack(load_addr, opt->nand_size, - (uint8_t*)opt->exec_addr, &out_size); - if(rc != UCL_E_OK) - spl_error(); - } else { - /* Set kernel args */ - kargv[0] = 0; - kargv[1] = (char*)opt->cmdline; - } - - entry_fn entry = (entry_fn)opt->exec_addr; - commit_discard_idcache(); - entry(2, kargv, 0, 0); - __builtin_unreachable(); - - nand_err: - nand_close(); - spl_error(); -} - -int spl_get_boot_option(void) -{ - const uint32_t pinmask = (1 << 17) | (1 << 19); - - uint32_t pin = 1, lastpin = 0; - uint32_t deadline = 0; - /* Iteration count guards against unlikely case of broken buttons - * which never stabilize; if this occurs, we always boot Rockbox. */ - int iter_count = 0; - const int max_iter_count = 30; - - /* Configure the button GPIOs as inputs */ - gpioz_configure(GPIO_A, pinmask, GPIOF_INPUT); - - /* Poll the pins for a short duration to detect a keypress */ - do { - lastpin = pin; - pin = ~REG_GPIO_PIN(GPIO_A) & pinmask; - if(pin != lastpin) { - /* This will always be set on the first iteration */ - deadline = __ost_read32() + BTN_STABLE_TIME; - iter_count += 1; - } - } while(iter_count < max_iter_count && __ost_read32() < deadline); - - if(iter_count < max_iter_count && (pin & (1 << 17))) { - if(pin & (1 << 19)) - return BOOTOPTION_RECOVERY; /* Play+Volume Up */ - else - return BOOTOPTION_ORIG_FW; /* Play */ - } else { - return BOOTOPTION_ROCKBOX; /* Volume Up or no buttons */ - } -} - -void spl_handle_pre_boot(int bootopt) -{ - /* Move system to EXCLK so we can manipulate the PLLs */ - clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) | CLKMUX_CPU(SCLK_A) | - CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); - clk_set_ccr_div(1, 1, 1, 1, 1); - - /* Enable APLL @ 1008 MHz (24 MHz EXCLK * 42 = 1008 MHz) */ - jz_writef(CPM_APCR, BS(1), PLLM(41), PLLN(0), PLLOD(0), ENABLE(1)); - while(jz_readf(CPM_APCR, ON) == 0); - - /* System clock setup -- common to Rockbox and FiiO firmware - * ---- - * CPU at 1 GHz, L2 cache at 500 MHz - * AHB0 and AHB2 at 200 MHz - * PCLK at 100 MHz - * DDR at 200 MHz - */ - - if(bootopt == BOOTOPTION_ROCKBOX) { - /* We don't use MPLL in Rockbox, so run everything from APLL. */ - clk_set_ccr_div(1, 2, 5, 5, 10); - clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) | - CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); - clk_set_ddr(X1000_CLK_SCLK_A, 5); - - /* Turn off MPLL */ - jz_writef(CPM_MPCR, ENABLE(0)); - } else { - /* Typical ingenic setup -- 1008 MHz APLL, 600 MHz MPLL - * with APLL driving the CPU/L2 and MPLL driving busses. */ - clk_set_ccr_div(1, 2, 3, 3, 6); - clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) | - CLKMUX_AHB0(MPLL) | CLKMUX_AHB2(MPLL)); - - /* Mimic OF's clock gating setup */ - jz_writef(CPM_CLKGR, PDMA(0), PCM(1), MAC(1), LCD(1), - MSC0(1), MSC1(1), OTG(1), CIM(1)); - - /* We now need to define the clock tree by fiddling with - * various CPM registers. Although the kernel has code to - * set up the clock tree itself, it isn't used and relies - * on the SPL for this. */ - 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)); - - /* Handle UART initialization */ - gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1)); - jz_writef(CPM_CLKGR, UART2(0)); - - /* TODO: Stop being lazy and make this human readable */ - volatile uint8_t* ub = (volatile uint8_t*)0xb0032000; - ub[0x04] = 0; - ub[0x08] = 0xef; - ub[0x20] = 0xfc; - ub[0x0c] = 3; - uint8_t uv = ub[0x0c]; - ub[0x0c] = uv | 0x80; - ub[0x04] = 0; - ub[0x00] = 0x0d; - ub[0x24] = 0x10; - ub[0x28] = 0; - ub[0x0c] = uv & 0x7f; - ub[0x08] = 0x17; - } -} 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 new file mode 100644 index 0000000000..d2f9e4e5e0 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/installer-x1000.c @@ -0,0 +1,341 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "installer-x1000.h" +#include "nand-x1000.h" +#include "core_alloc.h" +#include "file.h" +#include "microtar-rockbox.h" +#include <stddef.h> + +struct update_part { + const char* filename; + size_t offset; + size_t length; +}; + +/* Parts of the flash to update. The offset and length are given in bytes, + * offset relative to start of flash. The region's new contents are given + * by the named file inside the update archive. If any file is missing, the + * update will fail. (gracefully! nothing is written unless the package has + * all its components) + * + * If the update file is smaller than the region size, unused space at the + * end of the region is padded with 0xff. + * + * NOTE: The current code assumes all parts are contiguous. The current + * update map fits in one eraseblock, but if it ever needs extending beyond + * that, better implement a bitmap to indicate which blocks need updating + * and which can be skipped. We don't want to erase and reprogram blocks + * for no good reason, it's bad for the flash lifespan. + */ +static const struct update_part updates[] = { + { + .filename = "spl." BOOTFILE_EXT, + .offset = 0, + .length = 12 * 1024, + }, + { + .filename = "bootloader.ucl", + .offset = 0x6800, + .length = 102 * 1024, + }, +}; + +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(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; + size_t img_len = 0; + + /* calculate minimal image needed to contain all update blocks */ + for(int i = 0; i < num_updates; ++i) { + img_len = MAX(img_len, updates[i].offset + updates[i].length); + img_off = MIN(img_off, updates[i].offset); + } + + /* round everything to a multiple of the block size */ + size_t r_off = blk_size * (img_off / blk_size); + size_t r_len = blk_size * ((img_len + img_off - r_off + blk_size - 1) / blk_size); + + *offptr = r_off; + *lenptr = r_len; +} + +/* Read in a single part of the update from the tarball, and patch it + * into the image */ +static int patch_part(mtar_t* tar, const struct update_part* part, + uint8_t* img_buf, size_t img_off) +{ + int rc = mtar_find(tar, part->filename); + if(rc != MTAR_ESUCCESS) + return IERR_BAD_FORMAT; + + const mtar_header_t* h = mtar_get_header(tar); + if(h->type != 0 && h->type != MTAR_TREG) + return IERR_BAD_FORMAT; + + if(h->size > part->length) + return IERR_BAD_FORMAT; + + /* wipe the patched area, and read in the new data */ + memset(&img_buf[part->offset - img_off], 0xff, part->length); + rc = mtar_read_data(tar, &img_buf[part->offset - img_off], h->size); + if(rc < 0 || (unsigned)rc != h->size) + return IERR_FILE_IO; + + return IERR_SUCCESS; +} + +struct updater { + int buf_hnd; /* core_alloc handle for our memory buffer */ + size_t buf_len; /* sizeof the buffer */ + + uint8_t* img_buf; + size_t img_off; /* image address in flash */ + size_t img_len; /* image length in flash = size of the buffer */ + + mtar_t* tar; + struct nand_drv* ndrv; +}; + +static int updater_init(struct updater* u) +{ + int rc; + + /* initialize stuff correctly */ + u->buf_hnd = -1; + u->buf_len = 0; + u->img_buf = NULL; + u->img_off = 0; + u->img_len = 0; + u->tar = NULL; + u->ndrv = NULL; + + /* open NAND */ + u->ndrv = nand_init(); + nand_lock(u->ndrv); + rc = nand_open(u->ndrv); + if(rc != NAND_SUCCESS) { + rc = IERR_NAND_OPEN; + goto error; + } + + get_image_loc(u->ndrv, &u->img_off, &u->img_len); + + /* 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_ex(u->buf_len, &buflib_ops_locked); + if(u->buf_hnd < 0) { + rc = IERR_OUT_OF_MEMORY; + goto error; + } + + /* allocate from the buffer */ + uint8_t* buffer = (uint8_t*)core_get_data(u->buf_hnd); + size_t buf_len = u->buf_len; + + CACHEALIGN_BUFFER(buffer, buf_len); + u->img_buf = buffer; + buffer += u->img_len; + buf_len -= u->img_len; + + CACHEALIGN_BUFFER(buffer, buf_len); + u->tar = (mtar_t*)buffer; + memset(u->tar, 0, sizeof(mtar_t)); + + rc = IERR_SUCCESS; + + error: + return rc; +} + +static void updater_cleanup(struct updater* u) +{ + if(u->tar && mtar_is_open(u->tar)) + mtar_close(u->tar); + + core_free(u->buf_hnd); + + if(u->ndrv) { + nand_close(u->ndrv); + nand_unlock(u->ndrv); + } +} + +int install_bootloader(const char* filename) +{ + struct updater u; + int rc = updater_init(&u); + if(rc != IERR_SUCCESS) + goto error; + + /* get the image */ + rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf); + if(rc != NAND_SUCCESS) { + rc = IERR_NAND_READ; + goto error; + } + + /* get the tarball */ + rc = mtar_open(u.tar, filename, O_RDONLY); + if(rc != MTAR_ESUCCESS) { + if(rc == MTAR_EOPENFAIL) + rc = IERR_FILE_NOT_FOUND; + else if(rc == MTAR_EREADFAIL) + rc = IERR_FILE_IO; + else + rc = IERR_BAD_FORMAT; + goto error; + } + + /* patch stuff */ + for(int i = 0; i < num_updates; ++i) { + rc = patch_part(u.tar, &updates[i], u.img_buf, u.img_off); + if(rc != IERR_SUCCESS) + goto error; + } + + /* write back the patched image */ + rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf); + if(rc != NAND_SUCCESS) { + rc = IERR_NAND_WRITE; + goto error; + } + + rc = IERR_SUCCESS; + + error: + updater_cleanup(&u); + return rc; +} + +int backup_bootloader(const char* filename) +{ + int rc, fd = 0; + struct updater u; + + rc = updater_init(&u); + if(rc != IERR_SUCCESS) + goto error; + + /* read image */ + rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf); + if(rc != NAND_SUCCESS) { + rc = IERR_NAND_READ; + 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) { + rc = IERR_FILE_IO; + goto error; + } + + ssize_t cnt = write(fd, u.img_buf, u.img_len); + if(cnt < 0 || (size_t)cnt != u.img_len) { + rc = IERR_FILE_IO; + goto error; + } + + rc = IERR_SUCCESS; + + error: + if(fd >= 0) + close(fd); + updater_cleanup(&u); + return rc; +} + +int restore_bootloader(const char* filename) +{ + int rc, fd = 0; + struct updater u; + + rc = updater_init(&u); + if(rc != IERR_SUCCESS) + goto error; + + /* read from file */ + fd = open(filename, O_RDONLY); + if(fd < 0) { + rc = IERR_FILE_NOT_FOUND; + goto error; + } + + ssize_t cnt = read(fd, u.img_buf, u.img_len); + if(cnt < 0 || (size_t)cnt != u.img_len) { + rc = IERR_FILE_IO; + 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) { + rc = IERR_NAND_WRITE; + goto error; + } + + rc = IERR_SUCCESS; + + error: + if(fd >= 0) + close(fd); + updater_cleanup(&u); + return rc; +} + +const char* installer_strerror(int rc) +{ + switch(rc) { + case IERR_SUCCESS: return "Success"; + case IERR_OUT_OF_MEMORY: return "Out of memory"; + case IERR_FILE_NOT_FOUND: return "File not found"; + case IERR_FILE_IO: return "Disk I/O error"; + case IERR_BAD_FORMAT: return "Bad archive"; + 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 new file mode 100644 index 0000000000..9b0f1e4bd6 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/installer-x1000.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __INSTALLER_X1000_H__ +#define __INSTALLER_X1000_H__ + +/* This API is for the bootloader recovery menu and Rockbox utility to handle + * bootloader installation, backup, and restore. + * + * Currently the installer can only handle NAND flash, although the X1000 can + * boot from NOR flash or SD/MMC. Support for other storage media can be added + * when there is a target that needs it. + * + * Bootloader updates are tarballs, and they can "monkey patch" the flash in + * a customizable way (but fixed at compile time). + * + * Backup and restore simply takes the range of eraseblocks touched by the + * monkey patch and copies them to or from a regular file. + */ + +enum { + IERR_SUCCESS = 0, + IERR_OUT_OF_MEMORY, + IERR_FILE_NOT_FOUND, + IERR_FILE_IO, + IERR_BAD_FORMAT, + IERR_NAND_OPEN, + IERR_NAND_READ, + IERR_NAND_WRITE, + IERR_CORRUPTED_BACKUP, +}; + +extern int install_bootloader(const char* filename); +extern int backup_bootloader(const char* filename); +extern int restore_bootloader(const char* filename); +extern const char* installer_strerror(int rc); + +#endif /* __INSTALLER_X1000_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c index 193ff082e0..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 @@ -457,6 +460,10 @@ void lcd_update(void) jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1)); } +/* We can do partial updates even though the DMA doesn't seem to handle it well, + * due to the fact that this is actually putting it into a buffer, and then + * it gets transferred via DMA to a secondary buffer, which gets transferred in + * its entirety to the LCD through a different DMA process. */ void lcd_update_rect(int x, int y, int width, int height) { /* Clamp the coordinates */ 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/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c index 27929cced5..62172fa213 100644 --- a/firmware/target/mips/ingenic_x1000/msc-x1000.c +++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c @@ -21,6 +21,7 @@ #include "system.h" #include "panic.h" +#include "led.h" #include "msc-x1000.h" #include "gpio-x1000.h" #include "irq-x1000.h" @@ -39,8 +40,31 @@ * ensure that removing the card always resets the driver to a sane state. */ +#define DEBOUNCE_TIME (HZ/10) + static const msc_config msc_configs[] = { -#ifdef FIIO_M3K +#if defined(FIIO_M3K) +#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A + { + .msc_nr = 0, + .msc_type = MSC_TYPE_SD, + .bus_width = 4, + .label = "microSD", + .cd_gpio = GPIO_MSC0_CD, + .cd_active_level = 0, + }, +#elif defined(SHANLING_Q1) +#define MSC_CLOCK_SOURCE X1000_CLK_MPLL + { + .msc_nr = 0, + .msc_type = MSC_TYPE_SD, + .bus_width = 4, + .label = "microSD", + .cd_gpio = GPIO_MSC0_CD, + .cd_active_level = 0, + }, + /* NOTE: SDIO wifi card is on msc1 */ +#elif defined(EROS_QN) #define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A { .msc_nr = 0, @@ -105,6 +129,7 @@ static void msc_init_one(msc_drv* d, int msc) d->req = NULL; d->iflag_done = 0; d->card_present = 1; + d->card_present_last = 1; d->req_running = 0; mutex_init(&d->lock); semaphore_init(&d->cmd_done, 1, 0); @@ -122,8 +147,10 @@ static void msc_init_one(msc_drv* d, int msc) /* Setup the card detect IRQ */ if(d->config->cd_gpio != GPIO_NONE) { - if(gpio_get_level(d->config->cd_gpio) != d->config->cd_active_level) + if(gpio_get_level(d->config->cd_gpio) != d->config->cd_active_level) { d->card_present = 0; + d->card_present_last = 0; + } system_set_irq_handler(GPIO_TO_IRQ(d->config->cd_gpio), msc == 0 ? msc0_cd_interrupt : msc1_cd_interrupt); @@ -197,6 +224,16 @@ bool msc_card_detect(msc_drv* d) return gpio_get_level(d->config->cd_gpio) == d->config->cd_active_level; } +void msc_led_trigger(void) +{ + bool state = false; + for(int i = 0; i < MSC_COUNT; ++i) + if(msc_drivers[i].req_running) + state = true; + + led(state); +} + /* --------------------------------------------------------------------------- * Controller API */ @@ -351,6 +388,8 @@ static void msc_finish_request(msc_drv* d, int status) d->req->status = status; d->req_running = 0; d->iflag_done = 0; + + msc_led_trigger(); timeout_cancel(&d->cmd_tmo); semaphore_release(&d->cmd_done); } @@ -438,6 +477,7 @@ void msc_async_start(msc_drv* d, msc_req* r) /* Begin processing */ d->req = r; d->req_running = 1; + msc_led_trigger(); jz_writef(MSC_CTRL(d->msc_nr), START_OP(1)); if(r->flags & MSC_RF_DATA) jz_writef(MSC_DMAC(d->msc_nr), ENABLE(1)); @@ -613,11 +653,25 @@ static void msc_interrupt(msc_drv* d) static int msc_cd_callback(struct timeout* tmo) { msc_drv* d = (msc_drv*)tmo->data; + int now_present = msc_card_detect(d) ? 1 : 0; + + /* If the CD pin level changed during the timeout interval, then the + * signal is not yet stable and we need to wait longer. */ + if(now_present != d->card_present_last) { + d->card_present_last = now_present; + return DEBOUNCE_TIME; + } - /* If card is still present we assume the card is properly inserted */ - if(msc_card_detect(d)) { - d->card_present = 1; - queue_broadcast(SYS_HOTSWAP_INSERTED, d->drive_nr); + /* If there is a change, then broadcast the hotswap event */ + if(now_present != d->card_present) { + if(now_present) { + d->card_present = 1; + queue_broadcast(SYS_HOTSWAP_INSERTED, d->drive_nr); + } else { + msc_async_abort(d, MSC_REQ_EXTRACTED); + d->card_present = 0; + queue_broadcast(SYS_HOTSWAP_EXTRACTED, d->drive_nr); + } } return 0; @@ -625,17 +679,9 @@ static int msc_cd_callback(struct timeout* tmo) static void msc_cd_interrupt(msc_drv* d) { - if(!msc_card_detect(d)) { - /* Immediately abort and notify when removing a card */ - msc_async_abort(d, MSC_REQ_EXTRACTED); - if(d->card_present) { - d->card_present = 0; - queue_broadcast(SYS_HOTSWAP_EXTRACTED, d->drive_nr); - } - } else { - /* Timer to debounce input */ - timeout_register(&d->cd_tmo, msc_cd_callback, HZ/4, (intptr_t)d); - } + /* Timer to debounce input */ + d->card_present_last = msc_card_detect(d) ? 1 : 0; + timeout_register(&d->cd_tmo, msc_cd_callback, DEBOUNCE_TIME, (intptr_t)d); /* Invert the IRQ */ gpio_flip_edge_irq(d->config->cd_gpio); diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.h b/firmware/target/mips/ingenic_x1000/msc-x1000.h index 70f67a70d6..3ebdd3adc3 100644 --- a/firmware/target/mips/ingenic_x1000/msc-x1000.h +++ b/firmware/target/mips/ingenic_x1000/msc-x1000.h @@ -126,7 +126,8 @@ typedef struct msc_drv { unsigned iflag_done; volatile int req_running; - volatile int card_present; + volatile int card_present; /* Debounced status */ + volatile int card_present_last; /* Status when we last polled it */ struct mutex lock; struct semaphore cmd_done; @@ -147,6 +148,8 @@ extern void msc_unlock(msc_drv* d); extern void msc_full_reset(msc_drv* d); extern bool msc_card_detect(msc_drv* d); +extern void msc_led_trigger(void); + /* Controller API */ extern void msc_ctl_reset(msc_drv* d); extern void msc_set_clock_mode(msc_drv* d, int mode); diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index 5a21b1f8c2..af0f972eae 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c @@ -22,480 +22,447 @@ #include "nand-x1000.h" #include "sfc-x1000.h" #include "system.h" -#include <stddef.h> - -/* NAND command numbers */ -#define NAND_CMD_READ_ID 0x9f -#define NAND_CMD_WRITE_ENABLE 0x06 -#define NAND_CMD_GET_FEATURE 0x0f -#define NAND_CMD_SET_FEATURE 0x1f -#define NAND_CMD_PAGE_READ_TO_CACHE 0x13 -#define NAND_CMD_READ_FROM_CACHE 0x0b -#define NAND_CMD_READ_FROM_CACHEx4 0x6b -#define NAND_CMD_PROGRAM_LOAD 0x02 -#define NAND_CMD_PROGRAM_LOADx4 0x32 -#define NAND_CMD_PROGRAM_EXECUTE 0x10 -#define NAND_CMD_BLOCK_ERASE 0xd8 - -/* NAND device register addresses for GET_FEATURE / SET_FEATURE */ -#define NAND_FREG_PROTECTION 0xa0 -#define NAND_FREG_FEATURE 0xb0 -#define NAND_FREG_STATUS 0xc0 - -/* Protection register bits */ -#define NAND_FREG_PROTECTION_BRWD 0x80 -#define NAND_FREG_PROTECTION_BP2 0x20 -#define NAND_FREG_PROTECTION_BP1 0x10 -#define NAND_FREG_PROTECTION_BP0 0x08 -/* Mask of BP bits 0-2 */ -#define NAND_FREG_PROTECTION_ALLBP 0x38 - -/* Feature register bits */ -#define NAND_FREG_FEATURE_QE 0x01 - -/* Status register bits */ -#define NAND_FREG_STATUS_OIP 0x01 -#define NAND_FREG_STATUS_WEL 0x02 -#define NAND_FREG_STATUS_E_FAIL 0x04 -#define NAND_FREG_STATUS_P_FAIL 0x08 - -/* NAND chip config */ -const nand_chip_data target_nand_chip_data[] = { -#ifdef FIIO_M3K - { - /* ATO25D1GA */ - .mf_id = 0x9b, - .dev_id = 0x12, - .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)), - .clock_freq = 150000000, - .log2_page_size = 11, /* = 2048 bytes */ - .log2_block_size = 6, /* = 64 pages */ - .rowaddr_width = 3, - .coladdr_width = 2, - .flags = NANDCHIP_FLAG_QUAD, - } -#else - /* Nobody will use this anyway if the device has no NAND flash */ - { 0 }, -#endif +#include "logf.h" +#include <string.h> + +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 target_nand_chip_count = - sizeof(target_nand_chip_data) / sizeof(nand_chip_data); - -/* NAND ops -- high level primitives used by the driver */ -static int nandop_wait_status(int errbit); -static int nandop_read_page(uint32_t row_addr, uint8_t* buf); -static int nandop_write_page(uint32_t row_addr, const uint8_t* buf); -static int nandop_erase_block(uint32_t block_addr); -static int nandop_set_write_protect(bool en); - -/* NAND commands -- 1-to-1 mapping between chip commands and functions */ -static int nandcmd_read_id(int* mf_id, int* dev_id); -static int nandcmd_write_enable(void); -static int nandcmd_get_feature(uint8_t reg); -static int nandcmd_set_feature(uint8_t reg, uint8_t val); -static int nandcmd_page_read_to_cache(uint32_t row_addr); -static int nandcmd_read_from_cache(uint8_t* buf); -static int nandcmd_program_load(const uint8_t* buf); -static int nandcmd_program_execute(uint32_t row_addr); -static int nandcmd_block_erase(uint32_t block_addr); - -struct nand_drv { - const nand_chip_data* chip_data; - bool write_enabled; +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 struct nand_drv nand_drv; -static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; +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, +}; -static void nand_drv_reset(void) -{ - nand_drv.chip_data = NULL; - nand_drv.write_enabled = false; -} +#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 */ +}; -int nand_open(void) -{ - sfc_init(); - sfc_lock(); +const size_t nr_supported_nand_chips = ARRAYLEN(supported_nand_chips); - nand_drv_reset(); - sfc_open(); +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; - const nand_chip_data* chip_data = &target_nand_chip_data[0]; - sfc_set_dev_conf(chip_data->dev_conf); - sfc_set_clock(chip_data->clock_freq); +struct nand_drv* nand_init(void) +{ + static bool inited = false; + if(!inited) { + mutex_init(&static_nand_drv.mutex); + static_nand_drv.scratch_buf = static_scratch_buf; + static_nand_drv.page_buf = static_page_buf; + static_nand_drv.refcount = 0; + } - sfc_unlock(); - return NAND_SUCCESS; + return &static_nand_drv; } -void nand_close(void) +static uint8_t nand_get_reg(struct nand_drv* drv, uint8_t reg) { - sfc_lock(); - sfc_close(); - nand_drv_reset(); - sfc_unlock(); + sfc_exec(NANDCMD_GET_FEATURE, reg, drv->scratch_buf, 1|SFC_READ); + return drv->scratch_buf[0]; } -int nand_identify(int* mf_id, int* dev_id) +static void nand_set_reg(struct nand_drv* drv, uint8_t reg, uint8_t val) { - sfc_lock(); - - int status = nandcmd_read_id(mf_id, dev_id); - if(status < 0) - goto error; - - for(size_t i = 0; i < target_nand_chip_count; ++i) { - const nand_chip_data* data = &target_nand_chip_data[i]; - if(data->mf_id == *mf_id && data->dev_id == *dev_id) { - nand_drv.chip_data = data; - break; - } - } - - if(!nand_drv.chip_data) { - status = NAND_ERR_UNKNOWN_CHIP; - goto error; - } - - /* Set parameters according to new chip data */ - sfc_set_dev_conf(nand_drv.chip_data->dev_conf); - sfc_set_clock(nand_drv.chip_data->clock_freq); - status = NAND_SUCCESS; - - error: - sfc_unlock(); - return status; + drv->scratch_buf[0] = val; + sfc_exec(NANDCMD_SET_FEATURE, reg, drv->scratch_buf, 1|SFC_WRITE); } -const nand_chip_data* nand_get_chip_data(void) +static void nand_upd_reg(struct nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val) { - return nand_drv.chip_data; + uint8_t x = nand_get_reg(drv, reg); + x &= ~msk; + x |= val; + nand_set_reg(drv, reg, x); } -extern int nand_enable_writes(bool en) +static const struct nand_chip* identify_chip_method(uint8_t method, + const uint8_t* id_buf) { - if(en == nand_drv.write_enabled) - return NAND_SUCCESS; - - int rc = nandop_set_write_protect(!en); - if(rc == NAND_SUCCESS) - nand_drv.write_enabled = en; + 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 rc; + return NULL; } -static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) +static bool identify_chip(struct nand_drv* drv) { - const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size); - - if(addr & (page_size - 1)) - return NAND_ERR_UNALIGNED; - if(size & (page_size - 1)) - return NAND_ERR_UNALIGNED; - if(size <= 0) - return NAND_SUCCESS; - if(write && !nand_drv.write_enabled) - return NAND_ERR_WRITE_PROTECT; - if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) - return NAND_ERR_UNALIGNED; - - addr >>= nand_drv.chip_data->log2_page_size; - size >>= nand_drv.chip_data->log2_page_size; - - int rc = NAND_SUCCESS; - sfc_lock(); - - for(; size > 0; --size, ++addr, buf += page_size) { - if(write) - rc = nandop_write_page(addr, buf); - else - rc = nandop_read_page(addr, buf); - - if(rc) - break; - } + /* 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 + * + * Currently we use the 2nd method, aka. address read ID, the + * other methods can be added when needed. + */ + 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; - sfc_unlock(); - return rc; + return false; } -int nand_read(uint32_t addr, uint32_t size, uint8_t* buf) +static void setup_chip_data(struct nand_drv* drv) { - return nand_rdwr(false, addr, size, buf); + drv->ppb = 1 << drv->chip->log2_ppb; + drv->fpage_size = drv->chip->page_size + drv->chip->oob_size; } -int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf) +static void winbond_setup_chip(struct nand_drv* drv) { - return nand_rdwr(true, addr, size, (uint8_t*)buf); + /* Ensure we are in buffered read mode. */ + nand_upd_reg(drv, FREG_CFG, FREG_CFG_WINBOND_BUF, FREG_CFG_WINBOND_BUF); } -int nand_erase(uint32_t addr, uint32_t size) +static void setup_chip_registers(struct nand_drv* drv) { - const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size; - const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size; - const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_block_size; - - if(addr & (block_size - 1)) - return NAND_ERR_UNALIGNED; - if(size & (block_size - 1)) - return NAND_ERR_UNALIGNED; - if(size <= 0) - return NAND_SUCCESS; - if(!nand_drv.write_enabled) - return NAND_ERR_WRITE_PROTECT; + /* Set chip registers to enter normal operation */ + if(drv->chip->flags & NAND_CHIPFLAG_HAS_QE_BIT) { + bool en = (drv->chip->flags & NAND_CHIPFLAG_QUAD) != 0; + nand_upd_reg(drv, FREG_CFG, FREG_CFG_QUAD_ENABLE, + en ? FREG_CFG_QUAD_ENABLE : 0); + } - addr >>= nand_drv.chip_data->log2_page_size; - size >>= nand_drv.chip_data->log2_page_size; - size >>= nand_drv.chip_data->log2_block_size; + 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); + } - int rc = NAND_SUCCESS; - sfc_lock(); + /* Clear OTP bit to access the main data array */ + nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE, 0); - for(; size > 0; --size, addr += pages_per_block) - if((rc = nandop_erase_block(addr))) - break; + /* Clear write protection bits */ + nand_set_reg(drv, FREG_PROT, FREG_PROT_UNLOCK); - sfc_unlock(); - return rc; + /* Call any chip-specific hooks */ + if(drv->chip->setup_chip) + drv->chip->setup_chip(drv); } -/* - * NAND ops - */ - -static int nandop_wait_status(int errbit) +int nand_open(struct nand_drv* drv) { - int reg; - do { - reg = nandcmd_get_feature(NAND_FREG_STATUS); - if(reg < 0) - return reg; - } while(reg & NAND_FREG_STATUS_OIP); - - if(reg & errbit) - return NAND_ERR_COMMAND; + if(drv->refcount > 0) { + drv->refcount++; + return NAND_SUCCESS; + } - return reg; -} + /* Initialize the controller */ + sfc_open(); + 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); -static int nandop_read_page(uint32_t row_addr, uint8_t* buf) -{ - int status; + /* Send the software reset command */ + sfc_exec(NANDCMD_RESET, 0, NULL, 0); + mdelay(10); - if((status = nandcmd_page_read_to_cache(row_addr)) < 0) - return status; - if((status = nandop_wait_status(0)) < 0) - return status; - if((status = nandcmd_read_from_cache(buf)) < 0) - return status; + /* Chip identification and setup */ + if(!identify_chip(drv)) + return NAND_ERR_UNKNOWN_CHIP; - return NAND_SUCCESS; -} + setup_chip_data(drv); -static int nandop_write_page(uint32_t row_addr, const uint8_t* buf) -{ - int status; + /* Set new SFC parameters */ + sfc_set_dev_conf(drv->chip->dev_conf); + sfc_set_clock(drv->chip->clock_freq); - if((status = nandcmd_write_enable()) < 0) - return status; - if((status = nandcmd_program_load(buf)) < 0) - return status; - if((status = nandcmd_program_execute(row_addr)) < 0) - return status; - if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0) - return status; + /* Enter normal operating mode */ + setup_chip_registers(drv); + drv->refcount++; return NAND_SUCCESS; } -static int nandop_erase_block(uint32_t block_addr) +void nand_close(struct nand_drv* drv) { - int status; + --drv->refcount; + if(drv->refcount > 0) + return; - if((status = nandcmd_write_enable()) < 0) - return status; - if((status = nandcmd_block_erase(block_addr)) < 0) - return status; - if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0) - return status; + /* Let's reset the chip... the idea is to restore the registers + * to whatever they should "normally" be */ + sfc_exec(NANDCMD_RESET, 0, NULL, 0); + mdelay(10); - return NAND_SUCCESS; + sfc_close(); } -static int nandop_set_write_protect(bool en) +void nand_enable_otp(struct nand_drv* drv, bool enable) { - int val = nandcmd_get_feature(NAND_FREG_PROTECTION); - if(val < 0) - return val; - - if(en) { - val |= NAND_FREG_PROTECTION_ALLBP; - if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) - val |= NAND_FREG_PROTECTION_BRWD; - } else { - val &= ~NAND_FREG_PROTECTION_ALLBP; - if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) - val &= ~NAND_FREG_PROTECTION_BRWD; - } - - /* NOTE: The WP pin typically only protects changes to the protection - * register -- it doesn't actually prevent writing to the chip. That's - * why it should be re-enabled after setting the new protection status. - */ - sfc_set_wp_enable(false); - int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val); - sfc_set_wp_enable(true); - - if(status < 0) - return status; - - return NAND_SUCCESS; + nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE, + enable ? FREG_CFG_OTP_ENABLE : 0); } -/* - * Low-level NAND commands - */ - -static int nandcmd_read_id(int* mf_id, int* dev_id) +static uint8_t nand_wait_busy(struct nand_drv* drv) { - sfc_op op = {0}; - op.command = NAND_CMD_READ_ID; - op.flags = SFC_FLAG_READ; - op.addr_bytes = 1; - op.addr_lo = 0; - op.data_bytes = 2; - op.buffer = nand_auxbuf; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; - - *mf_id = nand_auxbuf[0]; - *dev_id = nand_auxbuf[1]; - return NAND_SUCCESS; + uint8_t reg; + do { + reg = nand_get_reg(drv, FREG_STATUS); + } while(reg & FREG_STATUS_BUSY); + return reg; } -static int nandcmd_write_enable(void) +int nand_block_erase(struct nand_drv* drv, nand_block_t block) { - sfc_op op = {0}; - op.command = NAND_CMD_WRITE_ENABLE; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + sfc_exec(NANDCMD_WR_EN, 0, NULL, 0); + sfc_exec(drv->chip->cmd_block_erase, block, NULL, 0); - return NAND_SUCCESS; + uint8_t status = nand_wait_busy(drv); + if(status & FREG_STATUS_EFAIL) + return NAND_ERR_ERASE_FAIL; + else + return NAND_SUCCESS; } -static int nandcmd_get_feature(uint8_t reg) +int nand_page_program(struct nand_drv* drv, nand_page_t page, const void* buffer) { - sfc_op op = {0}; - op.command = NAND_CMD_GET_FEATURE; - op.flags = SFC_FLAG_READ; - op.addr_bytes = 1; - op.addr_lo = reg; - op.data_bytes = 1; - op.buffer = nand_auxbuf; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; - - return nand_auxbuf[0]; + sfc_exec(NANDCMD_WR_EN, 0, 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) + return NAND_ERR_PROGRAM_FAIL; + else + return NAND_SUCCESS; } -static int nandcmd_set_feature(uint8_t reg, uint8_t val) +int nand_page_read(struct nand_drv* drv, nand_page_t page, void* buffer) { - sfc_op op = {0}; - op.command = NAND_CMD_SET_FEATURE; - op.flags = SFC_FLAG_WRITE; - op.addr_bytes = 1; - op.addr_lo = reg; - op.data_bytes = 1; - op.buffer = nand_auxbuf; - nand_auxbuf[0] = val; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + sfc_exec(drv->chip->cmd_page_read, page, NULL, 0); + nand_wait_busy(drv); + sfc_exec(drv->chip->cmd_read_cache, 0, buffer, drv->fpage_size|SFC_READ); - return NAND_SUCCESS; -} + if(drv->chip->flags & NAND_CHIPFLAG_ON_DIE_ECC) { + uint8_t status = nand_get_reg(drv, FREG_STATUS); -static int nandcmd_page_read_to_cache(uint32_t row_addr) -{ - sfc_op op = {0}; - op.command = NAND_CMD_PAGE_READ_TO_CACHE; - op.addr_bytes = nand_drv.chip_data->rowaddr_width; - op.addr_lo = row_addr; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + 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; } -static int nandcmd_read_from_cache(uint8_t* buf) +int nand_read_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer) { - sfc_op op = {0}; - if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { - op.command = NAND_CMD_READ_FROM_CACHEx4; - op.mode = SFC_MODE_QUAD_IO; - } else { - op.command = NAND_CMD_READ_FROM_CACHE; - op.mode = SFC_MODE_STANDARD; - } + if(byte_len == 0) + return NAND_SUCCESS; - op.flags = SFC_FLAG_READ; - op.addr_bytes = nand_drv.chip_data->coladdr_width; - op.addr_lo = 0; - op.dummy_bits = 8; // NOTE: this may need a chip_data parameter - op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); - op.buffer = buf; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + int rc; + unsigned pg_size = drv->chip->page_size; + nand_page_t page = byte_addr / pg_size; + unsigned offset = byte_addr % pg_size; + while(1) { + rc = nand_page_read(drv, page, drv->page_buf); + if(rc < 0) + return rc; - return NAND_SUCCESS; -} + memcpy(buffer, &drv->page_buf[offset], MIN(pg_size - offset, byte_len)); -static int nandcmd_program_load(const uint8_t* buf) -{ - sfc_op op = {0}; - if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { - op.command = NAND_CMD_PROGRAM_LOADx4; - op.mode = SFC_MODE_QUAD_IO; - } else { - op.command = NAND_CMD_PROGRAM_LOAD; - op.mode = SFC_MODE_STANDARD; - } + if(byte_len <= pg_size - offset) + break; - op.flags = SFC_FLAG_WRITE; - op.addr_bytes = nand_drv.chip_data->coladdr_width; - op.addr_lo = 0; - op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); - op.buffer = (void*)buf; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + byte_len -= pg_size - offset; + buffer += pg_size - offset; + offset = 0; + page++; + } return NAND_SUCCESS; } -static int nandcmd_program_execute(uint32_t row_addr) +int nand_write_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer) { - sfc_op op = {0}; - op.command = NAND_CMD_PROGRAM_EXECUTE; - op.addr_bytes = nand_drv.chip_data->rowaddr_width; - op.addr_lo = row_addr; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + if(byte_len == 0) + return NAND_SUCCESS; - return NAND_SUCCESS; -} + int rc; + unsigned pg_size = drv->chip->page_size; + unsigned blk_size = pg_size << drv->chip->log2_ppb; -static int nandcmd_block_erase(uint32_t block_addr) -{ - sfc_op op = {0}; - op.command = NAND_CMD_BLOCK_ERASE; - op.addr_bytes = nand_drv.chip_data->rowaddr_width; - op.addr_lo = block_addr; - if(sfc_exec(&op)) - return NAND_ERR_CONTROLLER; + if(byte_addr % blk_size != 0) + return NAND_ERR_UNALIGNED; + if(byte_len % blk_size != 0) + return NAND_ERR_UNALIGNED; + + nand_page_t page = byte_addr / pg_size; + nand_page_t end_page = page + (byte_len / pg_size); + + for(nand_block_t blk = page; blk < end_page; blk += drv->ppb) { + rc = nand_block_erase(drv, blk); + if(rc < 0) + return rc; + } + + for(; page != end_page; ++page) { + memcpy(drv->page_buf, buffer, pg_size); + memset(&drv->page_buf[pg_size], 0xff, drv->chip->oob_size); + buffer += pg_size; + + rc = nand_page_program(drv, page, drv->page_buf); + if(rc < 0) + return rc; + } return NAND_SUCCESS; } + +/* TODO - NAND driver future improvements + * + * 1. Support sofware or on-die ECC transparently. Support debug ECC bypass. + * + * It's probably best to add an API call to turn ECC on or off. Software + * ECC and most or all on-die ECC implementations require some OOB bytes + * to function; which leads us to the next problem... + * + * 2. Allow safe access to OOB areas + * + * The OOB data area is not fully available to users; it is also occupied + * by ECC data and bad block markings. The NAND driver needs to provide a + * mapping which allows OOB data users to map around those reserved areas, + * otherwise it's not really possible to use OOB data. + * + * 3. Support partial page programming. + * + * This might already work. My understanding of NAND flash is that bits are + * represented by charge deposited on flash cells. In the case of SLC flash, + * cells are one bit. For MLC flash, cells can store more than one bit; but + * MLC flash is much less reliable than SLC. We probably don't have to be + * concerned about MLC flash, and its does not support partial programming + * anyway due to the cell characteristics, so I will only consider SLC here. + * + * For SLC there are two cell states -- an uncharged cell represents a "1" + * and a charged cell represents "0". Programming can only deposit charge + * on a cell and erasing can only remove charge. Therefore, "programming" a + * cell to 1 is actually a no-op. + * + * So, there's no datasheet which spells this out, but I suspect you just + * set the areas you're not interested in programming to 0xff. Programming + * can never change a written 0 back to a 1, so programming a 1 bit works + * more like a "don't care" (= keep whatever value is already there). + * + * What _is_ given by the datasheets is limits on how many times you can + * reprogram the same page without erasing it. This is an overall limit + * called NOP (number of programs) in many datasheets. In addition to this, + * sub-regions of the page have further limits: it's common for a 2048+64 + * byte page to be split into 8 regions, with four 512-byte main areas and + * four 16-byte OOB areas. Usually, each subregion can only be programmed + * once. However, you can write multiple subregions with a single program. + * + * Violating programming constraints could cause data loss, so we need to + * communicate to upper layers what the limitations are here if they want + * to use partial programming safely. + * + * Programming the same page more than once increases the overall stress + * on the flash cells and can cause bitflips. For this reason, it's best + * to keep the number of programs as low as possible. Some sources suggest + * that programming the pages in a block in linear order is also better to + * reduce stress, although I don't know why this would be. + * + * These program/read stresses can flip bits, but it's only due to residual + * charge building up on uncharged cells; cells are not permanently damaged + * by these kind of stresses. Erasing the block will remove the charge and + * restore all the cells to a clean state. + * + * These slides are fairly informative on this subject: + * - https://cushychicken.github.io/assets/cooke_inconvenient_truths.pdf + * + * 4. Bad block management + * + * This probably doesn't belong in the NAND layer but it seems wise to keep + * at least a bad block table at the level of the NAND driver. Factory bad + * block marks are usually some non-0xFF byte in the OOB area, but bad blocks + * which develop over the device lifetime usually won't be marked; after all + * they are unreliable, so we can't program a marking on them and expect it + * to stick. So, most FTL systems keep a bad block table somewhere in flash + * and update it whenever a block goes bad. + * + * So, in addition to a bad block marker scan, we should try to gather bad + * block information from such tables. + */ diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h index cc56b836f8..0ccd075079 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.h +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h @@ -22,86 +22,221 @@ #ifndef __NAND_X1000_H__ #define __NAND_X1000_H__ -/* NOTE: this is a very minimal API designed only to support a bootloader. - * Not suitable for general data storage. It doesn't have proper support for - * partial page writes, access to spare area, etc, which are all necessary - * for an effective flash translation layer. +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include "kernel.h" + +#define NAND_SUCCESS 0 +#define NAND_ERR_UNKNOWN_CHIP (-1) +#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 +#define NAND_DRV_MAXPAGESIZE 2112 + +/* Quad I/O support bit */ +#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. * - * There's no ECC support. This can be added if necessary, but it's unlikely - * the boot area on any X1000 device uses software ECC as Ingenic's SPL simply - * doesn't have much room for more code (theirs programmed to work on multiple - * hardware configurations, so it's bigger than ours). + * BIT 31 log2_ppb bits + * +-------------------------------+---------------+ + * nand_page_t = | block nr | page nr | + * +-------------------------------+---------------+ + * BIT 0 + * + * The page address is split into block and page numbers. Page numbers occupy + * the lower log2_ppb bits, and the block number occupies the upper bits. + * + * Block addresses are structured the same as page addresses, but with a page + * number of 0. So block number N has address N << log2_ppb. */ +typedef uint32_t nand_block_t; +typedef uint32_t nand_page_t; -#include <stdint.h> -#include <stdbool.h> -#include <stddef.h> +struct nand_drv; -/* Error codes which can be returned by the NAND API */ -#define NAND_SUCCESS 0 -#define NAND_ERR_UNKNOWN_CHIP (-1) -#define NAND_ERR_UNALIGNED (-2) -#define NAND_ERR_WRITE_PROTECT (-3) -#define NAND_ERR_CONTROLLER (-4) -#define NAND_ERR_COMMAND (-5) +struct nand_chip { + /* Base2 logarithm of the number of pages per block */ + unsigned log2_ppb; -/* Chip supports quad I/O for page read/write */ -#define NANDCHIP_FLAG_QUAD 0x01 + /* Size of a page's main / oob areas, in bytes. */ + unsigned page_size; + unsigned oob_size; -/* Set/clear the BRWD bit when enabling/disabling write protection */ -#define NANDCHIP_FLAG_USE_BRWD 0x02 + /* Total number of blocks in the chip */ + unsigned nr_blocks; -typedef struct nand_chip_data { - /* Chip manufacturer / device ID */ - uint8_t mf_id; - uint8_t dev_id; + /* Bad block marker offset within the 1st page of a bad block */ + unsigned bbm_pos; - /* Width of row/column addresses in bytes */ - uint8_t rowaddr_width; - uint8_t coladdr_width; + /* Clock frequency to use */ + uint32_t clock_freq; - /* SFC dev conf and clock frequency to use for this device */ + /* Value of sfc_dev_conf */ uint32_t dev_conf; - uint32_t clock_freq; - /* Page size in bytes = 1 << log2_page_size */ - uint32_t log2_page_size; + /* Chip specific flags */ + uint32_t flags; - /* Block size in number of pages = 1 << log2_block_size */ - uint32_t log2_block_size; + /* 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; + + /* Reference count for open/close operations */ + unsigned refcount; + + /* Scratch and page buffers. Both need to be cacheline-aligned and are + * provided externally by the caller prior to nand_open(). + * + * - The scratch buffer is NAND_DRV_SCRATCHSIZE bytes long and is used + * for small data transfers associated with commands. It must not be + * disturbed while any NAND operation is in progress. + * + * - The page buffer is used by certain functions like nand_read_bytes(), + * but it's main purpose is to provide a common temporary buffer for + * driver users to perform I/O with. Must be fpage_size bytes long. + */ + uint8_t* scratch_buf; + uint8_t* page_buf; + + /* Pointer to the chip data. */ + 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; +}; + +extern const struct nand_chip_id supported_nand_chips[]; +extern const size_t nr_supported_nand_chips; + +/* Return the static NAND driver instance. + * + * 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 struct nand_drv* nand_init(void); - /* Chip flags */ - uint32_t flags; -} nand_chip_data; +static inline void nand_lock(struct nand_drv* drv) +{ + mutex_lock(&drv->mutex); +} + +static inline void nand_unlock(struct nand_drv* drv) +{ + mutex_unlock(&drv->mutex); +} + +/* Open or close the NAND driver + * + * The NAND driver is reference counted, and opening / closing it will + * increment and decrement the reference count. The hardware is only + * controlled when the reference count rises above or falls to 0, else + * these functions are no-ops which always succeed. + * + * These functions require the lock to be held. + */ +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. + * + * 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(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); -/* Open or close the NAND driver. The NAND driver takes control of the SFC, - * so that driver must be in the closed state before opening the NAND driver. +/* 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_open(void); -extern void nand_close(void); - -/* Identify the NAND chip. This must be done after opening the driver and - * prior to any data access, in order to set the chip parameters. */ -extern int nand_identify(int* mf_id, int* dev_id); - -/* Return the chip data for the identified NAND chip. - * Returns NULL if the chip is not identified. */ -const nand_chip_data* nand_get_chip_data(void); - -/* Controls the chip's write protect features. The driver also keeps track of - * this flag and refuses to perform write or erase operations unless you have - * enabled writes. Writes should be disabled again when you finish writing. */ -extern int nand_enable_writes(bool en); - -/* Reading and writing operates on whole pages at a time. If the address or - * size is not aligned to a multiple of the page size, no data will be read - * or written and an error code is returned. */ -extern int nand_read(uint32_t addr, uint32_t size, uint8_t* buf); -extern int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf); - -/* Erase operates on whole blocks. Like the page read/write operations, - * the address and size must be aligned to a multiple of the block size. - * If not, no blocks are erased and an error code is returned. */ -extern int nand_erase(uint32_t addr, uint32_t size); +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 a3da3411f2..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,34 +31,37 @@ #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 */ jz_writef(AIC_CFG, RST(1)); jz_writef(AIC_I2SCR, STPBK(1)); - jz_writef(AIC_CFG, MSB(0), LSMP(1), ICDC(0), AUSEL(1), BCKD(0), SYNCD(0)); + jz_writef(AIC_CFG, MSB(0), LSMP(0), ICDC(0), AUSEL(1), BCKD(0), SYNCD(0)); jz_writef(AIC_CCR, ENDSW(0), ASVTSU(0)); jz_writef(AIC_I2SCR, RFIRST(0), ESCLK(0), AMSL(0)); jz_write(AIC_SPENA, 0); @@ -66,13 +69,20 @@ void pcm_play_dma_init(void) /* Let the target initialize its hardware and setup the AIC */ audiohw_init(); +#if (PCM_NATIVE_BITDEPTH > 16) + /* Program audio format (stereo, 24 bit samples) */ + jz_writef(AIC_CCR, PACK16(0), CHANNEL_V(STEREO), + OSS_V(24BIT), ISS_V(24BIT), M2S(0)); + jz_writef(AIC_I2SCR, SWLH(0)); +#else /* Program audio format (stereo, packed 16 bit samples) */ jz_writef(AIC_CCR, PACK16(1), CHANNEL_V(STEREO), OSS_V(16BIT), ISS_V(16BIT), M2S(0)); jz_writef(AIC_I2SCR, SWLH(0)); +#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); @@ -99,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)); @@ -123,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 { @@ -139,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!"); + } - aic_dma_pending_event = DMA_EVENT_NONE; - aic_state = AIC_STATE_STOPPED; + /* disable playback */ + jz_writef(AIC_CCR, ERPL(0)); + + 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); @@ -186,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) @@ -203,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) { @@ -244,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/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c index c1fde89b70..5ade6bcc64 100644 --- a/firmware/target/mips/ingenic_x1000/sfc-x1000.c +++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c @@ -21,86 +21,71 @@ #include "system.h" #include "kernel.h" -#include "panic.h" #include "sfc-x1000.h" +#include "clk-x1000.h" #include "irq-x1000.h" -#include "x1000/sfc.h" -#include "x1000/cpm.h" - -/* DMA works, but not in the SPL due to some hardware not being set up right. - * Only the SPL and bootloader actually require flash access, so to keep it - * simple, DMA is unconditionally disabled. */ -//#define NEED_SFC_DMA +/* #define USE_DMA */ #define FIFO_THRESH 31 -#define SFC_STATUS_PENDING (-1) +static void sfc_poll_wait(void); +#ifdef USE_DMA +static void sfc_irq_wait(void); -#ifdef NEED_SFC_DMA -static struct mutex sfc_mutex; static struct semaphore sfc_sema; -static struct timeout sfc_lockup_tmo; -static bool sfc_inited = false; -static volatile int sfc_status; -#else -# define sfc_status SFC_STATUS_OK -#endif - -void sfc_init(void) -{ -#ifdef NEED_SFC_DMA - if(sfc_inited) - return; - - mutex_init(&sfc_mutex); - semaphore_init(&sfc_sema, 1, 0); - sfc_inited = true; -#endif -} - -void sfc_lock(void) -{ -#ifdef NEED_SFC_DMA - mutex_lock(&sfc_mutex); -#endif -} -void sfc_unlock(void) -{ -#ifdef NEED_SFC_DMA - mutex_unlock(&sfc_mutex); +/* This function pointer thing is a hack for the SPL, since it has to use + * the NAND driver directly and we can't afford to drag in the whole kernel + * just to wait on a semaphore. */ +static void(*sfc_wait)(void) = sfc_poll_wait; #endif -} void sfc_open(void) { jz_writef(CPM_CLKGR, SFC(0)); +#ifdef USE_DMA + jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32), + PHASE_NUM(1), THRESHOLD(FIFO_THRESH), WP_EN(1)); +#else jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1), THRESHOLD(FIFO_THRESH), WP_EN(1)); +#endif REG_SFC_CGE = 0; REG_SFC_INTC = 0x1f; REG_SFC_MEM_ADDR = 0; +} + +void sfc_close(void) +{ + REG_SFC_CGE = 0x1f; + jz_writef(CPM_CLKGR, SFC(1)); +} + +void sfc_irq_begin(void) +{ +#ifdef USE_DMA + static bool inited = false; + if(!inited) { + semaphore_init(&sfc_sema, 1, 0); + inited = true; + } -#ifdef NEED_SFC_DMA - jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32)); system_enable_irq(IRQ_SFC); + sfc_wait = sfc_irq_wait; #endif } -void sfc_close(void) +void sfc_irq_end(void) { -#ifdef NEED_SFC_DMA +#ifdef USE_DMA system_disable_irq(IRQ_SFC); + sfc_wait = sfc_poll_wait; #endif - - REG_SFC_CGE = 0x1f; - jz_writef(CPM_CLKGR, SFC(1)); } void sfc_set_clock(uint32_t freq) { - /* TODO: This is a hack so we can use MPLL in the SPL. - * There must be a better way to do this... */ + /* FIXME: Get rid of this hack & allow defining a real clock tree... */ x1000_clk_t clksrc = X1000_CLK_MPLL; uint32_t in_freq = clk_get(clksrc); if(in_freq < freq) { @@ -115,170 +100,99 @@ void sfc_set_clock(uint32_t freq) jz_writef(CPM_SSICDR, CE(0)); } -#ifdef NEED_SFC_DMA -static int sfc_lockup_tmo_cb(struct timeout* tmo) -{ - (void)tmo; - - int irq = disable_irq_save(); - if(sfc_status == SFC_STATUS_PENDING) { - sfc_status = SFC_STATUS_LOCKUP; - jz_overwritef(SFC_TRIG, STOP(1)); - semaphore_release(&sfc_sema); - } - - restore_irq(irq); - return 0; -} - -static void sfc_wait_end(void) +#ifndef USE_DMA +static void sfc_fifo_rdwr(bool write, void* buffer, uint32_t data_bytes) { - semaphore_wait(&sfc_sema, TIMEOUT_BLOCK); -} - -void SFC(void) -{ - unsigned sr = REG_SFC_SR & ~REG_SFC_INTC; - - if(jz_vreadf(sr, SFC_SR, OVER)) { - jz_overwritef(SFC_SCR, CLR_OVER(1)); - sfc_status = SFC_STATUS_OVERFLOW; - } else if(jz_vreadf(sr, SFC_SR, UNDER)) { - jz_overwritef(SFC_SCR, CLR_UNDER(1)); - sfc_status = SFC_STATUS_UNDERFLOW; - } else if(jz_vreadf(sr, SFC_SR, END)) { - jz_overwritef(SFC_SCR, CLR_END(1)); - sfc_status = SFC_STATUS_OK; - } else { - panicf("SFC IRQ bug"); - return; - } - - /* Not sure this is wholly correct */ - if(sfc_status != SFC_STATUS_OK) - jz_overwritef(SFC_TRIG, STOP(1)); - - REG_SFC_INTC = 0x1f; - semaphore_release(&sfc_sema); -} -#else -/* Note the X1000 is *very* picky about how the SFC FIFOs are accessed - * so please do NOT try to rearrange the code without testing it first! - */ - -static void sfc_fifo_read(unsigned* buffer, int data_bytes) -{ - int data_words = (data_bytes + 3) / 4; + uint32_t* word_buf = (uint32_t*)buffer; + uint32_t sr_bit = write ? BM_SFC_SR_TREQ : BM_SFC_SR_RREQ; + uint32_t clr_bit = write ? BM_SFC_SCR_CLR_TREQ : BM_SFC_SCR_CLR_RREQ; + uint32_t data_words = (data_bytes + 3) / 4; while(data_words > 0) { - if(jz_readf(SFC_SR, RREQ)) { - jz_overwritef(SFC_SCR, CLR_RREQ(1)); + if(REG_SFC_SR & sr_bit) { + REG_SFC_SCR = clr_bit; - int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; + /* We need to read/write in bursts equal to FIFO threshold amount + * X1000 PM, 10.8.5, SFC > software guidelines > slave mode */ + uint32_t amount = MIN(data_words, FIFO_THRESH); data_words -= amount; - while(amount > 0) { - *buffer++ = REG_SFC_DATA; - amount -= 1; - } - } - } -} -static void sfc_fifo_write(const unsigned* buffer, int data_bytes) -{ - int data_words = (data_bytes + 3) / 4; - while(data_words > 0) { - if(jz_readf(SFC_SR, TREQ)) { - jz_overwritef(SFC_SCR, CLR_TREQ(1)); - - int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; - data_words -= amount; - while(amount > 0) { - REG_SFC_DATA = *buffer++; - amount -= 1; + uint32_t* endptr = word_buf + amount; + for(; word_buf != endptr; ++word_buf) { + if(write) + REG_SFC_DATA = *word_buf; + else + *word_buf = REG_SFC_DATA; } } } } - -static void sfc_wait_end(void) -{ - while(jz_readf(SFC_SR, END) == 0); - jz_overwritef(SFC_SCR, CLR_TREQ(1)); -} - -#endif /* NEED_SFC_DMA */ - -int sfc_exec(const sfc_op* op) -{ -#ifdef NEED_SFC_DMA - uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END); #endif - if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) { - jz_writef(SFC_TRAN_CONF(0), DATA_EN(1)); - REG_SFC_TRAN_LENGTH = op->data_bytes; -#ifdef NEED_SFC_DMA - REG_SFC_MEM_ADDR = PHYSADDR(op->buffer); -#endif - - if(op->flags & SFC_FLAG_READ) - { - jz_writef(SFC_GLB, TRAN_DIR_V(READ)); -#ifdef NEED_SFC_DMA - discard_dcache_range(op->buffer, op->data_bytes); - intc_clear |= jz_orm(SFC_INTC, MSK_OVER); +void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size) +{ + /* Deal with transfer direction */ + bool write = (size & SFC_WRITE) != 0; + uint32_t glb = REG_SFC_GLB; + if(data) { + if(write) { + jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(WRITE)); + size &= ~SFC_WRITE; +#ifdef USE_DMA + commit_dcache_range(data, size); #endif - } - else - { - jz_writef(SFC_GLB, TRAN_DIR_V(WRITE)); -#ifdef NEED_SFC_DMA - commit_dcache_range(op->buffer, op->data_bytes); - intc_clear |= jz_orm(SFC_INTC, MSK_UNDER); + } else { + jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(READ)); +#ifdef USE_DMA + discard_dcache_range(data, size); #endif } - } else { - jz_writef(SFC_TRAN_CONF(0), DATA_EN(0)); - REG_SFC_TRAN_LENGTH = 0; -#ifdef NEED_SFC_DMA - REG_SFC_MEM_ADDR = 0; -#endif } - bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0; - jz_writef(SFC_TRAN_CONF(0), - MODE(op->mode), POLL_EN(0), - ADDR_WIDTH(op->addr_bytes), - PHASE_FMT(dummy_first ? 1 : 0), - DUMMY_BITS(op->dummy_bits), - COMMAND(op->command), CMD_EN(1)); - - REG_SFC_DEV_ADDR(0) = op->addr_lo; - REG_SFC_DEV_PLUS(0) = op->addr_hi; + /* Program transfer configuration */ + REG_SFC_GLB = glb; + REG_SFC_TRAN_LENGTH = size; +#ifdef USE_DMA + REG_SFC_MEM_ADDR = PHYSADDR(data); +#endif + REG_SFC_TRAN_CONF(0) = cmd; + REG_SFC_DEV_ADDR(0) = addr; + REG_SFC_DEV_PLUS(0) = 0; -#ifdef NEED_SFC_DMA - sfc_status = SFC_STATUS_PENDING; - timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0); + /* Clear old interrupts */ REG_SFC_SCR = 0x1f; - REG_SFC_INTC &= ~intc_clear; -#endif + jz_writef(SFC_INTC, MSK_END(0)); + /* Start the command */ jz_overwritef(SFC_TRIG, FLUSH(1)); jz_overwritef(SFC_TRIG, START(1)); -#ifndef NEED_SFC_DMA - if(op->flags & SFC_FLAG_READ) - sfc_fifo_read((unsigned*)op->buffer, op->data_bytes); - if(op->flags & SFC_FLAG_WRITE) - sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes); + /* Data transfer by PIO or DMA, and wait for completion */ +#ifndef USE_DMA + sfc_fifo_rdwr(write, data, size); + sfc_poll_wait(); +#else + sfc_wait(); #endif +} - sfc_wait_end(); +static void sfc_poll_wait(void) +{ + while(jz_readf(SFC_SR, END) == 0); + jz_overwritef(SFC_SCR, CLR_END(1)); +} -#ifdef NEED_SFC_DMA - if(op->flags & SFC_FLAG_READ) - discard_dcache_range(op->buffer, op->data_bytes); -#endif +#ifdef USE_DMA +static void sfc_irq_wait(void) +{ + semaphore_wait(&sfc_sema, TIMEOUT_BLOCK); +} - return sfc_status; +void SFC(void) +{ + /* the only interrupt we use is END; errors are basically not + * possible with the SPI interface... */ + semaphore_release(&sfc_sema); + jz_overwritef(SFC_SCR, CLR_END(1)); + jz_writef(SFC_INTC, MSK_END(1)); } +#endif diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.h b/firmware/target/mips/ingenic_x1000/sfc-x1000.h index 5784198b93..afb4aa3ce6 100644 --- a/firmware/target/mips/ingenic_x1000/sfc-x1000.h +++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.h @@ -19,87 +19,107 @@ * ****************************************************************************/ +#ifndef __SFC_X1000_H__ +#define __SFC_X1000_H__ + +#include "x1000/sfc.h" #include <stdint.h> #include <stdbool.h> -#include "clk-x1000.h" -#include "x1000/sfc.h" -/* SPI flash controller interface -- this is a low-level driver upon which - * you can build NAND/NOR flash drivers. The main function is sfc_exec(), - * used to issue commands, transfer data, etc. +/* SPI transfer mode. SFC_TMODE_X_Y_Z means: + * + * - X lines for command phase + * - Y lines for address+dummy phase + * - Z lines for data phase */ +#define SFC_TMODE_1_1_1 0 +#define SFC_TMODE_1_1_2 1 +#define SFC_TMODE_1_2_2 2 +#define SFC_TMODE_2_2_2 3 +#define SFC_TMODE_1_1_4 4 +#define SFC_TMODE_1_4_4 5 +#define SFC_TMODE_4_4_4 6 -#define SFC_FLAG_READ 0x01 /* Read data */ -#define SFC_FLAG_WRITE 0x02 /* Write data */ -#define SFC_FLAG_DUMMYFIRST 0x04 /* Do dummy bits before sending address. - * Default is dummy bits after address. - */ +/* Phase format + * _____________________ + * / SFC_PFMT_ADDR_FIRST \ + * +-----+-------+-------+------+ + * | cmd | addr | dummy | data | + * +-----+-------+-------+------+ + * ______________________ + * / SFC_PFMT_DUMMY_FIRST \ + * +-----+-------+-------+------+ + * | cmd | dummy | addr | data | + * +-----+-------+-------+------+ + */ +#define SFC_PFMT_ADDR_FIRST 0 +#define SFC_PFMT_DUMMY_FIRST 1 -/* SPI transfer mode. If in doubt, check with the X1000 manual and confirm - * the transfer format is what you expect. +/* Direction of transfer flag */ +#define SFC_READ 0 +#define SFC_WRITE (1 << 31) + +/** \brief Macro to generate an SFC command for use with sfc_exec() + * \param cmd Command number (up to 16 bits) + * \param tmode SPI transfer mode + * \param awidth Number of address bytes + * \param dwidth Number of dummy cycles (1 cycle = 1 bit) + * \param pfmt Phase format (address first or dummy first) + * \param data_en 1 to enable data phase, 0 to omit it */ -#define SFC_MODE_STANDARD 0 -#define SFC_MODE_DUAL_IN_DUAL_OUT 1 -#define SFC_MODE_DUAL_IO 2 -#define SFC_MODE_FULL_DUAL_IO 3 -#define SFC_MODE_QUAD_IN_QUAD_OUT 4 -#define SFC_MODE_QUAD_IO 5 -#define SFC_MODE_FULL_QUAD_IO 6 +#define SFC_CMD(cmd, tmode, awidth, dwidth, pfmt, data_en) \ + jz_orf(SFC_TRAN_CONF, COMMAND(cmd), CMD_EN(1), \ + MODE(tmode), ADDR_WIDTH(awidth), DUMMY_BITS(dwidth), \ + PHASE_FMT(pfmt), DATA_EN(data_en)) -/* Return status codes for sfc_exec() */ -#define SFC_STATUS_OK 0 -#define SFC_STATUS_OVERFLOW 1 -#define SFC_STATUS_UNDERFLOW 2 -#define SFC_STATUS_LOCKUP 3 +/* Open/close SFC hardware */ +extern void sfc_open(void); +extern void sfc_close(void); -typedef struct sfc_op { - int command; /* Command number */ - int mode; /* SPI transfer mode */ - int flags; /* Flags for this op */ - int addr_bytes; /* Number of address bytes */ - int dummy_bits; /* Number of dummy bits (yes: bits, not bytes) */ - uint32_t addr_lo; /* Lower 32 bits of address */ - uint32_t addr_hi; /* Upper 32 bits of address */ - int data_bytes; /* Number of data bytes to read/write */ - void* buffer; /* Data buffer -- MUST be word-aligned */ -} sfc_op; +/* Enable IRQ mode, instead of busy waiting for operations to complete. + * Needs to be called separately after sfc_open(), because the SPL has to + * use busy waiting, but we cannot #ifdef it for the SPL due to limitations + * of the build system. */ +extern void sfc_irq_begin(void); +extern void sfc_irq_end(void); -/* One-time driver init for mutexes/etc needed for handling interrupts. - * This can be safely called multiple times; only the first call will - * actually perform the init. - */ -extern void sfc_init(void); +/* Change the SFC clock frequency */ +extern void sfc_set_clock(uint32_t freq); + +/* Set the device configuration register */ +static inline void sfc_set_dev_conf(uint32_t conf) +{ + REG_SFC_DEV_CONF = conf; +} -/* Controller mutex -- lock before touching the driver */ -extern void sfc_lock(void); -extern void sfc_unlock(void); +/* Control the state of the write protect pin */ +static inline void sfc_set_wp_enable(bool en) +{ + jz_writef(SFC_GLB, WP_EN(en ? 1 : 0)); +} -/* Open/close the driver. The driver must be open in order to do operations. - * Closing the driver shuts off the hardware; the driver can be re-opened at - * a later time when it's needed again. +/** \brief Execute a command + * \param cmd Command encoded by `SFC_CMD` macro. + * \param addr Address up to 32 bits; pass 0 if the command doesn't need it + * \param data Buffer for data transfer commands, must be cache-aligned + * \param size Number of data bytes / direction of transfer flag + * \returns SFC status code: 0 on success and < 0 on failure. * - * After opening the driver, you must also program a valid device configuration - * and clock rate using sfc_set_dev_conf() and sfc_set_clock(). + * - Non-data commands must pass `data = NULL` and `size = 0` in order to + * get correct results. + * + * - Data commands must specify a direction of transfer using the high bit + * of the `size` argument by OR'ing in `SFC_READ` or `SFC_WRITE`. */ -extern void sfc_open(void); -extern void sfc_close(void); +extern void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size); -/* These functions can be called at any time while the driver is open, but - * must not be called while there is an operation in progress. It's the - * caller's job to ensure the configuration will work with the device and - * be capable of reading back data correctly. +/* NOTE: the above will need to be changed if we need better performance + * The hardware can do multiple commands in a sequence, including polling, + * and emit an interrupt only at the end. * - * - sfc_set_dev_conf() writes its argument to the SFC_DEV_CONF register. - * - sfc_set_wp_enable() sets the state of the write-protect pin (WP). - * - sfc_set_clock() sets the controller clock frequency (in Hz). + * Also, some chips need more than 4 address bytes even though the block + * and page numbers would still fit in 32 bits; the current API cannot + * handle this. */ -#define sfc_set_dev_conf(dev_conf) \ - do { REG_SFC_DEV_CONF = (dev_conf); } while(0) - -#define sfc_set_wp_enable(en) \ - jz_writef(SFC_GLB, WP_EN((en) ? 1 : 0)) - -extern void sfc_set_clock(uint32_t freq); -/* Execute an operation. Returns zero on success, nonzero on failure. */ -extern int sfc_exec(const sfc_op* op); +#endif /* __SFC_X1000_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c new file mode 100644 index 0000000000..787c35c494 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c @@ -0,0 +1,191 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "audiohw.h" +#include "system.h" +#include "pcm_sampr.h" +#include "aic-x1000.h" +#include "i2c-x1000.h" +#include "gpio-x1000.h" +#include "x1000/aic.h" +#include "x1000/cpm.h" + +/* Codec has an dedicated oscillator connected, so it can operate + * as i2s master or slave. I can't distinguish any difference in + * terms of audio quality or power consumption. Code is left here + * for reference in case it proves useful to change it. */ +#define CODEC_MASTER_MODE 0 + +static int cur_fsel = HW_FREQ_48; +static int cur_vol_l = 0, cur_vol_r = 0; +static int cur_filter = 0; +static enum es9218_amp_mode cur_amp_mode = ES9218_AMP_MODE_1VRMS; + +static void codec_start(void) +{ + es9218_open(); + es9218_mute(true); + es9218_set_iface_role(CODEC_MASTER_MODE ? ES9218_IFACE_ROLE_MASTER + : ES9218_IFACE_ROLE_SLAVE); + es9218_set_iface_format(ES9218_IFACE_FORMAT_I2S, ES9218_IFACE_BITS_32); + es9218_set_dpll_bandwidth(10); + es9218_set_thd_compensation(true); + es9218_set_thd_coeffs(0, 0); + audiohw_set_filter_roll_off(cur_filter); + audiohw_set_frequency(cur_fsel); + audiohw_set_volume(cur_vol_l, cur_vol_r); + es9218_set_amp_mode(cur_amp_mode); +} + +static void codec_stop(void) +{ + es9218_mute(true); + es9218_close(); + mdelay(4); +} + +void audiohw_init(void) +{ + /* Configure AIC */ + aic_set_external_codec(true); + aic_set_i2s_mode(CODEC_MASTER_MODE ? AIC_I2S_SLAVE_MODE + : AIC_I2S_MASTER_MODE); + aic_enable_i2s_bit_clock(true); + + /* Open DAC driver */ + i2c_x1000_set_freq(1, I2C_FREQ_400K); + codec_start(); +} + +void audiohw_postinit(void) +{ + es9218_mute(false); +} + +void audiohw_close(void) +{ + codec_stop(); +} + +void audiohw_set_frequency(int fsel) +{ + int sampr = hw_freq_sampr[fsel]; + + /* choose clock gear setting, in line with the OF */ + enum es9218_clock_gear clkgear; + if(sampr <= 48000) + clkgear = ES9218_CLK_GEAR_4; + else if(sampr <= 96000) + clkgear = ES9218_CLK_GEAR_2; + else + clkgear = ES9218_CLK_GEAR_1; + + aic_enable_i2s_bit_clock(false); + es9218_set_clock_gear(clkgear); + + if(CODEC_MASTER_MODE) + es9218_set_nco_frequency(sampr); + else + aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, 64); + + aic_enable_i2s_bit_clock(true); + + /* save frequency selection */ + cur_fsel = fsel; +} + +static int round_step_up(int x, int step) +{ + int rem = x % step; + if(rem > 0) + rem -= step; + return x - rem; +} + +void audiohw_set_volume(int vol_l, int vol_r) +{ + /* save volume */ + cur_vol_l = vol_l; + cur_vol_r = vol_r; + + /* adjust the amp setting first */ + int amp = round_step_up(MAX(vol_l, vol_r), ES9218_AMP_VOLUME_STEP); + amp = MIN(amp, ES9218_AMP_VOLUME_MAX); + amp = MAX(amp, ES9218_AMP_VOLUME_MIN); + + /* adjust digital volumes */ + vol_l -= amp; + vol_l = MIN(vol_l, ES9218_DIG_VOLUME_MAX); + vol_l = MAX(vol_l, ES9218_DIG_VOLUME_MIN); + + vol_r -= amp; + vol_r = MIN(vol_r, ES9218_DIG_VOLUME_MAX); + vol_r = MAX(vol_r, ES9218_DIG_VOLUME_MIN); + + /* program DAC */ + es9218_set_amp_volume(amp); + es9218_set_dig_volume(vol_l, vol_r); +} + +void audiohw_set_filter_roll_off(int value) +{ + cur_filter = value; + es9218_set_filter(value); +} + +void audiohw_set_power_mode(int mode) +{ + enum es9218_amp_mode new_amp_mode; + if(mode == SOUND_HIGH_POWER) + new_amp_mode = ES9218_AMP_MODE_2VRMS; + else + new_amp_mode = ES9218_AMP_MODE_1VRMS; + + if(new_amp_mode != cur_amp_mode) { + codec_stop(); + cur_amp_mode = new_amp_mode; + codec_start(); + es9218_mute(false); + } +} + +void es9218_set_power_pin(int level) +{ + gpio_set_level(GPIO_ES9218_POWER, level ? 1 : 0); +} + +void es9218_set_reset_pin(int level) +{ + gpio_set_level(GPIO_ES9218_RESET, level ? 1 : 0); +} + +uint32_t es9218_get_mclk(void) +{ + /* Measured by running the DAC in asynchronous I2S slave mode, + * and reading back the DPLL number from regs 0x42-0x45 while + * playing back 44.1 KHz audio. + * + * CLK = (44_100 * 2**32) / 0x4b46e5 + * = 38_393_403.29532737 + * ~ 38.4 Mhz + */ + return 38400000; +} diff --git a/firmware/target/arm/tatung/tpj1022/backlight-tpj1022.c b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c index d63387728b..32c1b902aa 100644 --- a/firmware/target/arm/tatung/tpj1022/backlight-tpj1022.c +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2006 by Barry Wardell + * 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 @@ -19,28 +19,45 @@ * ****************************************************************************/ -/* The H10 display (and hence backlight) possibly identical to that of the X5, - so that code was used here but left #if 0'ed out for the moment */ - -#include "config.h" -#include "cpu.h" -#include "system.h" #include "backlight.h" +#include "backlight-target.h" +#include "lcd.h" +#include "pwm-x1000.h" + +#define BL_LCD_CHN 0 +#define BL_LCD_PERIOD 10000 + +static int backlight_calc_duty(int period, int min_duty, int brightness) +{ + return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING; +} + +bool backlight_hw_init(void) +{ + pwm_init(BL_LCD_CHN); + pwm_enable(BL_LCD_CHN); + backlight_hw_brightness(MAX_BRIGHTNESS_SETTING); + return true; +} void backlight_hw_on(void) { -#if 0 - int level = disable_irq_save(); - pcf50606_write(0x38, 0xb0); /* Backlight ON, GPO1INV=1, GPO1ACT=011 */ - restore_irq(level); + pwm_enable(BL_LCD_CHN); +#ifdef HAVE_LCD_ENABLE + lcd_enable(true); #endif } void backlight_hw_off(void) { -#if 0 - int level = disable_irq_save(); - pcf50606_write(0x38, 0x80); /* Backlight OFF, GPO1INV=1, GPO1ACT=000 */ - restore_irq(level); + pwm_disable(BL_LCD_CHN); +#ifdef HAVE_LCD_ENABLE + lcd_enable(false); #endif } + +void backlight_hw_brightness(int brightness) +{ + int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness); + pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns); +} diff --git a/firmware/target/arm/imx233/samsung-ypz5/fmradio-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h index 66bceb4071..7298c1c06a 100644 --- a/firmware/target/arm/imx233/samsung-ypz5/fmradio-target.h +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2013 by Amaury Pouly + * 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 @@ -18,14 +18,16 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef _FMRADIO_TARGET_H_ -#define _FMRADIO_TARGET_H_ -#define IMX233_FMRADIO_I2C FMI_HW +#ifndef __BACKLIGHT_TARGET_H__ +#define __BACKLIGHT_TARGET_H__ -#define IMX233_FMRADIO_POWER FMP_GPIO -#define FMP_GPIO_BANK 0 -#define FMP_GPIO_PIN 10 -#define FMP_GPIO_DELAY (HZ / 5) +#include <stdbool.h> -#endif /* _FMRADIO_TARGET_H_ */ +extern bool backlight_hw_init(void); + +extern void backlight_hw_on(void); +extern void backlight_hw_off(void); +extern void backlight_hw_brightness(int brightness); + +#endif /* __BACKLIGHT_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c new file mode 100644 index 0000000000..13b0cdd078 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c @@ -0,0 +1,258 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * Copyright (C) 2021 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 "button.h" +#include "touchscreen.h" +#include "ft6x06.h" +#include "axp-pmu.h" +#include "kernel.h" +#include "backlight.h" +#include "powermgmt.h" +#include "gpio-x1000.h" +#include "irq-x1000.h" +#include "i2c-x1000.h" +#include <stdbool.h> + +#ifndef BOOTLOADER +# include "lcd.h" +# include "font.h" +#endif + +/* Volume wheel rotation */ +static volatile int wheel_pos = 0; + +/* Value of headphone detect register */ +static uint8_t hp_detect_reg = 0x00; + +/* Interval to poll the register */ +#define HPD_POLL_TIME (HZ/2) + +static int hp_detect_tmo_cb(struct timeout* tmo) +{ + i2c_descriptor* d = (i2c_descriptor*)tmo->data; + i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); + return HPD_POLL_TIME; +} + +static void hp_detect_init(void) +{ + /* TODO: replace this copy paste cruft with an API in axp-pmu */ + static struct timeout tmo; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; + static i2c_descriptor desc = { + .slave_addr = AXP_PMU_ADDR, + .bus_cond = I2C_START | I2C_STOP, + .tran_mode = I2C_READ, + .buffer[0] = (void*)&gpio_reg, + .count[0] = 1, + .buffer[1] = &hp_detect_reg, + .count[1] = 1, + .callback = NULL, + .arg = 0, + .next = NULL, + }; + + /* Headphone detect is wired to AXP192 GPIO: set it to input state */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); + + /* Get an initial reading before startup */ + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + if(r >= 0) + hp_detect_reg = r; + + /* Poll the register every second */ + timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc); +} + +void button_init_device(void) +{ + /* Setup interrupts for the volume wheel */ + gpio_set_function(GPIO_WHEEL1, GPIOF_IRQ_EDGE(0)); + gpio_set_function(GPIO_WHEEL2, GPIOF_IRQ_EDGE(0)); + gpio_flip_edge_irq(GPIO_WHEEL1); + gpio_flip_edge_irq(GPIO_WHEEL2); + gpio_enable_irq(GPIO_WHEEL1); + gpio_enable_irq(GPIO_WHEEL2); + + /* Init touchscreen driver */ + i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K); + ft6x06_init(); + + /* Reset touch controller */ + gpio_set_level(GPIO_FT6x06_POWER, 1); + gpio_set_level(GPIO_FT6x06_RESET, 0); + mdelay(5); + gpio_set_level(GPIO_FT6x06_RESET, 1); + + /* Enable ft6x06 interrupt */ + system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT), ft6x06_irq_handler); + gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0)); + gpio_enable_irq(GPIO_FT6x06_INTERRUPT); + + /* Headphone detection */ + hp_detect_init(); +} + +int button_read_device(int* data) +{ + const struct ft6x06_point* point; + int r = 0; + + /* Read GPIO buttons, these are all active low */ + uint32_t b = REG_GPIO_PIN(GPIO_B); + if((b & (1 << 21)) == 0) r |= BUTTON_PREV; + if((b & (1 << 22)) == 0) r |= BUTTON_NEXT; + if((b & (1 << 28)) == 0) r |= BUTTON_PLAY; + if((b & (1 << 31)) == 0) r |= BUTTON_POWER; + + /* Check the wheel */ + int wheel_btn = 0; + int whpos = wheel_pos; + if(whpos > 3) + wheel_btn = BUTTON_VOL_DOWN; + else if(whpos < -3) + wheel_btn = BUTTON_VOL_UP; + + if(wheel_btn) { + wheel_pos = 0; + + /* Post the event (rapid motion is more reliable this way) */ + queue_post(&button_queue, wheel_btn, 0); + queue_post(&button_queue, wheel_btn|BUTTON_REL, 0); + + /* Poke the backlight */ + backlight_on(); + reset_poweroff_timer(); + } + + if(touchscreen_get_mode() == TOUCHSCREEN_POINT) { + /* Pointing mode can't use multitouch since we can only pass + * along coordinates for one touch event at a time */ + point = &ft6x06_state.points[0]; + int t = touchscreen_to_pixels(point->pos_x, point->pos_y, data); + if(point->event == FT6x06_EVT_PRESS || + point->event == FT6x06_EVT_CONTACT) + r |= t; + } else { + /* 3x3 mode can have simultaneous 'button' presses via multitouch */ + for(int i = 0; i < ft6x06_state.nr_points; ++i) { + point = &ft6x06_state.points[i]; + if(point->event == FT6x06_EVT_PRESS || + point->event == FT6x06_EVT_CONTACT) + r |= touchscreen_to_pixels(point->pos_x, point->pos_y, NULL); + } + } + + return r; +} + +void touchscreen_enable_device(bool en) +{ + ft6x06_enable(en); + /* TODO: check if it's worth shutting off the power pin */ +} + +bool headphones_inserted(void) +{ + /* TODO: Also check if the headset button is detectable via an ADC. + * The AXP driver should probably get proper interrupt handling, + * that would be useful for more things than just GPIO polling. */ + return hp_detect_reg & 0x20 ? true : false; +} + +static void handle_wheel_irq(void) +{ + /* Wheel stuff adapted from button-erosqnative.c */ + static const int delta[16] = { 0, -1, 1, 0, + 1, 0, 0, -1, + -1, 0, 0, 1, + 0, 1, -1, 0 }; + static uint32_t state = 0; + state <<= 2; + state |= (REG_GPIO_PIN(GPIO_D) >> 2) & 3; + state &= 0xf; + + wheel_pos += delta[state]; +} + +void GPIOD02(void) +{ + handle_wheel_irq(); + gpio_flip_edge_irq(GPIO_WHEEL1); +} + +void GPIOD03(void) +{ + handle_wheel_irq(); + gpio_flip_edge_irq(GPIO_WHEEL2); +} + +#ifndef BOOTLOADER +static int getbtn(void) +{ + int btn; + do { + btn = button_get_w_tmo(1); + } while(btn & (BUTTON_REL|BUTTON_REPEAT)); + return btn; +} + +bool dbg_shanlingq1_touchscreen(void) +{ + /* definition of box used to represent the touchpad */ + const int pad_w = LCD_WIDTH; + const int pad_h = LCD_HEIGHT; + const int box_h = pad_h - SYSFONT_HEIGHT*5; + const int box_w = pad_w * box_h / pad_h; + const int box_x = (LCD_WIDTH - box_w) / 2; + const int box_y = SYSFONT_HEIGHT * 9 / 2; + + bool draw_border = true; + + do { + int line = 0; + lcd_clear_display(); + lcd_putsf(0, line++, "nr_points: %d gesture: %d", + ft6x06_state.nr_points, ft6x06_state.gesture); + + /* draw touchpad box borders */ + if(draw_border) + lcd_drawrect(box_x, box_y, box_w, box_h); + + for(int i = 0; i < ft6x06_state.nr_points; ++i) { + const struct ft6x06_point* point = &ft6x06_state.points[i]; + lcd_putsf(0, line++, "pt%d id:%d pos: %d,%d wgt: %d area:%d", + i, point->touch_id, point->pos_x, point->pos_y, + point->weight, point->area); + + /* draw crosshair */ + int tx = box_x + point->pos_x * box_w / pad_w; + int ty = box_y + point->pos_y * box_h / pad_h; + lcd_hline(tx-2, tx+2, ty); + lcd_vline(tx, ty-2, ty+2); + } + + lcd_update(); + } while(getbtn() != BUTTON_POWER); + return false; +} +#endif diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h new file mode 100644 index 0000000000..905d148afa --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __BUTTON_TARGET_H__ +#define __BUTTON_TARGET_H__ + +#include <stdbool.h> + +/* physical buttons */ +#define BUTTON_POWER 0x00000001 +#define BUTTON_VOL_UP 0x00000002 /* up = wheel clockwise */ +#define BUTTON_VOL_DOWN 0x00000004 +#define BUTTON_PLAY 0x00000008 /* circle */ +#define BUTTON_NEXT 0x00000010 /* down */ +#define BUTTON_PREV 0x00000020 /* up */ + +/* compatibility hacks */ +#define BUTTON_LEFT BUTTON_MIDLEFT +#define BUTTON_RIGHT BUTTON_MIDRIGHT + +/* touchscreen "buttons" */ +#define BUTTON_TOPLEFT 0x00000040 +#define BUTTON_TOPMIDDLE 0x00000080 +#define BUTTON_TOPRIGHT 0x00000100 +#define BUTTON_MIDLEFT 0x00000200 +#define BUTTON_CENTER 0x00000400 +#define BUTTON_MIDRIGHT 0x00000800 +#define BUTTON_BOTTOMLEFT 0x00001000 +#define BUTTON_BOTTOMMIDDLE 0x00002000 +#define BUTTON_BOTTOMRIGHT 0x00004000 + +#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\ + BUTTON_PLAY|BUTTON_NEXT|BUTTON_PREV) + +#define POWEROFF_BUTTON BUTTON_POWER +#define POWEROFF_COUNT 30 + +#endif /* __BUTTON_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h new file mode 100644 index 0000000000..7c71d12888 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h @@ -0,0 +1,32 @@ +/* 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(I2C0, GPIO_B, 3 << 23, GPIOF_DEVICE(0)) +DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0)) +DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1)) + +/* Name Pin Function */ +DEFINE_GPIO(FT6x06_INTERRUPT, GPIO_PA(16), GPIOF_INPUT) +DEFINE_GPIO(USB_DETECT, GPIO_PA(17), GPIOF_INPUT) +DEFINE_GPIO(FT6x06_RESET, GPIO_PA(19), GPIOF_OUTPUT(0)) +DEFINE_GPIO(LCD_PWR, GPIO_PB(6), GPIOF_OUTPUT(1)) +DEFINE_GPIO(FT6x06_POWER, GPIO_PB(8), GPIOF_OUTPUT(0)) +DEFINE_GPIO(MSC0_CD, GPIO_PB(9), GPIOF_INPUT) +DEFINE_GPIO(ES9218_POWER, GPIO_PB(13), GPIOF_OUTPUT(0)) +DEFINE_GPIO(LCD_RST, GPIO_PB(15), GPIOF_OUTPUT(1)) +DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1)) +DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1)) +DEFINE_GPIO(BTN_PREV, GPIO_PB(21), GPIOF_INPUT) +DEFINE_GPIO(BTN_NEXT, GPIO_PB(22), GPIOF_INPUT) +DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0)) +DEFINE_GPIO(BTN_PLAY, GPIO_PB(28), GPIOF_INPUT) +DEFINE_GPIO(BTN_POWER, GPIO_PB(31), GPIOF_INPUT) +DEFINE_GPIO(AXP_IRQ, GPIO_PC(21), GPIOF_INPUT) +DEFINE_GPIO(USB_ID, GPIO_PC(23), GPIOF_INPUT) +DEFINE_GPIO(WHEEL1, GPIO_PD(2), GPIOF_INPUT) +DEFINE_GPIO(WHEEL2, GPIO_PD(3), GPIOF_INPUT) +DEFINE_GPIO(ES9218_GPIO2, GPIO_PD(4), GPIOF_OUTPUT(0)) +DEFINE_GPIO(ES9218_RESET, GPIO_PD(5), GPIOF_OUTPUT(0)) diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h new file mode 100644 index 0000000000..af19aeb28c --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __I2C_TARGET_H__ +#define __I2C_TARGET_H__ + +#define I2C_ASYNC_BUS_COUNT 3 +#define I2C_ASYNC_QUEUE_SIZE 4 + +#define FT6x06_BUS 0 +#define FT6x06_ADDR 0x38 + +#define ES9218_BUS 1 +#define ES9218_ADDR 0x48 + +#define AXP_PMU_BUS 2 +#define AXP_PMU_ADDR 0x34 + +#define CW2015_BUS 2 +#define CW2015_ADDR 0x62 + +#endif /* __I2C_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c new file mode 100644 index 0000000000..532a149185 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c @@ -0,0 +1,399 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "lcd.h" +#include "system.h" +#include "lcd-x1000.h" +#include "gpio-x1000.h" + +/* LCD controller is probably an RM68090. + */ + +static const uint32_t q1_lcd_cmd_enable[] = { + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0xbe, + LCD_INSTR_DAT, 0xc3, + LCD_INSTR_DAT, 0x29, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x01, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x04, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x02, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x03, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x10, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x05, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x06, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x07, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x03, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x08, + LCD_INSTR_DAT, 0x03, + LCD_INSTR_DAT, 0x03, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x0d, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x10, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0xc1, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x11, + LCD_INSTR_DAT, 0xb1, + LCD_INSTR_DAT, 0x08, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x12, + LCD_INSTR_DAT, 0xb1, + LCD_INSTR_DAT, 0x08, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x13, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x0f, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x14, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x14, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x15, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x04, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x16, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x22, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x23, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x30, + LCD_INSTR_DAT, 0x7c, + LCD_INSTR_DAT, 0x3f, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x32, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x70, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x91, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0xe0, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0xe1, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x61, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x00, + LCD_INSTR_DAT, 0x10, + LCD_INSTR_DAT, 0x30, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x01, + LCD_INSTR_DAT, 0xf6, + LCD_INSTR_DAT, 0x3f, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x02, + LCD_INSTR_DAT, 0x50, + LCD_INSTR_DAT, 0x1f, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x03, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x30, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x08, + LCD_INSTR_DAT, 0x03, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x11, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x35, + LCD_INSTR_DAT, 0x76, + LCD_INSTR_DAT, 0x66, + + LCD_INSTR_CMD, 0x01, + LCD_INSTR_CMD, 0x39, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x26, + + LCD_INSTR_CMD, 0x04, + LCD_INSTR_CMD, 0x00, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0xc7, + + LCD_INSTR_CMD, 0x04, + LCD_INSTR_CMD, 0x01, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x06, + LCD_INSTR_CMD, 0x06, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x00, + LCD_INSTR_DAT, 0x0d, + LCD_INSTR_DAT, 0x0e, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x01, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x03, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x02, + LCD_INSTR_DAT, 0x08, + LCD_INSTR_DAT, 0x08, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x03, + LCD_INSTR_DAT, 0x02, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x04, + LCD_INSTR_DAT, 0x03, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x05, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x04, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x06, + LCD_INSTR_DAT, 0x1b, + LCD_INSTR_DAT, 0x21, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x07, + LCD_INSTR_DAT, 0x0f, + LCD_INSTR_DAT, 0x0e, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x08, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x04, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x09, + LCD_INSTR_DAT, 0x08, + LCD_INSTR_DAT, 0x08, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x0a, + LCD_INSTR_DAT, 0x02, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x0b, + LCD_INSTR_DAT, 0x03, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x0c, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x03, + + LCD_INSTR_CMD, 0x03, + LCD_INSTR_CMD, 0x0d, + LCD_INSTR_DAT, 0x31, + LCD_INSTR_DAT, 0x34, + + /* X start */ + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x10, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x1e, /* 30 */ + + /* X end */ + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x11, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x85, /* 389 */ + + /* Y start */ + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x12, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, /* 0 */ + + /* Y end */ + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x13, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x8f, /* 399 */ + + /* RAM write start X? */ + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x00, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x1e, + + /* RAM write start Y? */ + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x01, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x00, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x03, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x30, + + LCD_INSTR_CMD, 0x02, + LCD_INSTR_CMD, 0x02, + LCD_INSTR_END, +}; + +/* NOTE this sleep mode may not be saving power, but it gets rid of the + * ghost image that would otherwise remain on the display */ +static const uint32_t q1_lcd_cmd_sleep[] = { + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x10, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x03, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x07, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x01, + + LCD_INSTR_END, +}; + +static const uint32_t q1_lcd_cmd_wake[] = { + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x07, + LCD_INSTR_DAT, 0x01, + LCD_INSTR_DAT, 0x03, + + LCD_INSTR_CMD, 0x00, + LCD_INSTR_CMD, 0x10, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0xc1, + + LCD_INSTR_END, +}; + +static const uint8_t __attribute__((aligned(64))) + q1_lcd_dma_wr_cmd[] = {0x02, 0x02, 0x02, 0x02}; + +const struct lcd_tgt_config lcd_tgt_config = { + .bus_width = 8, + .cmd_width = 8, + .use_6800_mode = 0, + .use_serial = 0, + .clk_polarity = 0, + .dc_polarity = 0, + .wr_polarity = 1, + .te_enable = 0, + .big_endian = 1, + .dma_wr_cmd_buf = &q1_lcd_dma_wr_cmd, + .dma_wr_cmd_size = sizeof(q1_lcd_dma_wr_cmd), +}; + +void lcd_tgt_enable(bool enable) +{ + if(enable) { + /* power on the panel */ + gpio_set_level(GPIO_LCD_PWR, 1); + gpio_set_level(GPIO_LCD_RST, 1); + gpio_set_level(GPIO_LCD_CE, 1); + gpio_set_level(GPIO_LCD_RD, 1); + mdelay(50); + gpio_set_level(GPIO_LCD_RST, 0); + mdelay(100); + gpio_set_level(GPIO_LCD_RST, 1); + mdelay(50); + gpio_set_level(GPIO_LCD_CE, 0); + + /* Start the controller */ + lcd_set_clock(X1000_CLK_MPLL, 50000000); + lcd_exec_commands(q1_lcd_cmd_enable); + } else { + /* FIXME: Shanling Q1 LCD power down sequence + * not important because we don't use it but it'd be nice to know */ + } +} + +void lcd_tgt_sleep(bool sleep) +{ + if(sleep) + lcd_exec_commands(q1_lcd_cmd_sleep); + else + lcd_exec_commands(q1_lcd_cmd_wake); +} diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c new file mode 100644 index 0000000000..59a2262f25 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c @@ -0,0 +1,163 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "power.h" +#include "adc.h" +#include "system.h" +#include "axp-pmu.h" +#ifdef HAVE_CW2015 +# include "cw2015.h" +#endif +#ifdef HAVE_USB_CHARGING_ENABLE +# include "usb_core.h" +#endif + +#include "i2c-x1000.h" + +/* TODO: Better(?) battery reporting for Q1 using CW2015 driver + * + * The CW2015 has its own quirks so the driver has to be more complicated + * than "read stuff from I2C," unfortunately. Without fixing the quirks it + * is probably worse than the simple voltage-based method. + * + * A bigger problem is that it shares an I2C bus with the AXP192, but when + * we attempt to communicate with both chips, they start returning bogus + * data intermittently. Ususally, reads will return 0 but sometimes they + * can return other nonzero bogus data. It could be that one or the other is + * pulling the bus line down inappropriately, or maybe the hardware does not + * respect the bus free time between start/stop conditions and one of the + * devices is getting confused. + */ + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3470 +}; + +/* the OF shuts down at this voltage */ +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3400 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 } +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196 +}; + +void power_init(void) +{ + i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); + axp_init(); +#ifdef HAVE_CW2015 + 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. */ + axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200); + + /* For now, just turn everything on... definitely the touchscreen + * is powered by one of the outputs */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); + + /* Delay to give power output time to stabilize */ + mdelay(20); +} + +#ifdef HAVE_USB_CHARGING_ENABLE +void usb_charging_maxcurrent_change(int maxcurrent) +{ + axp_set_charge_current(maxcurrent); +} +#endif + +void power_off(void) +{ + axp_power_off(); + while(1); +} + +bool charging_state(void) +{ + return axp_battery_status() == AXP_BATT_CHARGING; +} + +int _battery_voltage(void) +{ + /* CW2015 can also read battery voltage, but the AXP consistently + * reads ~20-30 mV higher so I suspect it's the "real" voltage. */ + 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) +{ + return cw2015_get_soc(); +} +#endif + +#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & TIME_MEASURE) != 0 +int _battery_time(void) +{ + return cw2015_get_rrt(); +} +#endif + +void adc_init(void) +{ +} diff --git a/firmware/target/arm/tatung/tpj1022/button-tpj1022.c b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c index ce9d7ab24d..24eb42081e 100644 --- a/firmware/target/arm/tatung/tpj1022/button-tpj1022.c +++ b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2006 by Robert Kukla + * 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 @@ -19,31 +19,32 @@ * ****************************************************************************/ -#include "system.h" -#include "button.h" +#include "spl-x1000.h" +#include "gpio-x1000.h" +#include "nand-x1000.h" -bool button_hold(void) -{ - return (GPIOK_INPUT_VAL & 0x40) ? true : false; -} +static struct nand_drv* ndrv = NULL; -int button_read_device(void) +int spl_storage_open(void) { - int btn = BUTTON_NONE; + /* We need to assign the GPIOs manually */ + gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1)); - if (!button_hold()) - { - btn = (GPIOA_INPUT_VAL & 0xfe) ^ 0xfe; + /* 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; - if ((GPIOK_INPUT_VAL & 0x20) == 0) btn |= BUTTON_VOL_DOWN; + return nand_open(ndrv); +} - /* to be found - if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_MENU; - if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_REC; - if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_VOL_UP; - if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_LEFT; - */ - } - - return btn; +void spl_storage_close(void) +{ + nand_close(ndrv); +} + +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 fd664e231d..08f88f506c 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.c +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c @@ -19,41 +19,121 @@ * ****************************************************************************/ +#include "system.h" #include "spl-x1000.h" #include "clk-x1000.h" -#include "system.h" +#include "nand-x1000.h" +#include "gpio-x1000.h" +#include "boot-x1000.h" #include "x1000/cpm.h" #include "x1000/ost.h" +#include "x1000/uart.h" #include "x1000/ddrc.h" #include "x1000/ddrc_apb.h" #include "x1000/ddrphy.h" +#include "ucl_decompress.h" +#include <string.h> -#ifdef FIIO_M3K -# 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_ERROR_PIN GPIO_PC(25) +# define BOOT_STORAGE_ADDR 0x6800 +# define BOOT_STORAGE_SIZE (102 * 1024) #else -# error "please add SPL memory definitions" +# 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 -/* Note: This is based purely on disassembly of the SPL from the FiiO M3K. - * The code there is somewhat generic and corresponds roughly to Ingenic's - * U-Boot code, but isn't entirely the same. +static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE); + +void* spl_alloc(size_t count) +{ + heap -= CACHEALIGN_UP(count); + memset(heap, 0, CACHEALIGN_UP(count)); + return heap; +} + +void spl_error(void) +{ + int level = 0; + while(1) { + gpio_set_function(SPL_ERROR_PIN, GPIOF_OUTPUT(level)); + mdelay(100); + level = 1 - level; + } +} + +static void init_ost(void) +{ + /* NOTE: the prescaler needs to be the same as in system-x1000.c */ + jz_writef(CPM_CLKGR, OST(0)); + jz_writef(OST_CTRL, PRESCALE2_V(BY_4)); + jz_overwritef(OST_CLEAR, OST2(1)); + jz_write(OST_2CNTH, 0); + jz_write(OST_2CNTL, 0); + jz_setf(OST_ENABLE, OST2); +} + +/* NOTE: This is originally based on disassembly of the FiiO M3K SPL, which + * is in fact the GPL'd Ingenic X-Loader SPL. Similar stuff can be found in + * Ingenic's U-boot code. + * + * The source code for the Ingenic X-Loader SPL can be found in these repos: + * - https://github.com/JaminCheung/x-loader + * - https://github.com/YuanhuanLiang/X1000 * - * I converted all the runtime conditionals to compile-time ones in order to - * save code space, since they should be constant for any given target. + * Runtime conditionals based on the SoC type, looked up by OTP bits in EFUSE, + * are converted to compile-time conditionals here, as they are constant for a + * given target and there is no point in wasting precious space on dead code. * - * I haven't bothered to decode all the register fields. Some of the values - * written are going to bits documented as "Reserved" by Ingenic, but their - * documentation doesn't seem completely reliable, so either these are bits - * which _do_ have a purpose, or they're only defined on other Ingenic CPUs. + * I didn't decode the register fields; note that some values are documented as + * "reserved" in the X1000 PM. The X-Loader source might shed more light on it; + * it's likely these bits have meaning only on other Ingenic SoCs. * - * The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite". - * These aren't documented by Ingenic, but the addresses and names can be found - * in their U-Boot code. + * The DDR PHY registers match Synopsys's "PHY Utility Block Lite." The names + * of those registers & their fields can also be found in the X-Loader code, + * but they're not documented by Ingenic. */ -static void ddr_init(void) +static int init_dram(void) { +#if SPL_DDR_MEMORYSIZE != 64 && SPL_DDR_MEMORYSIZE != 32 +# error "bad memory size" +#endif + + jz_writef(CPM_CLKGR, DDR(0)); + REG_CPM_DRCG = 0x73; mdelay(3); REG_CPM_DRCG = 0x71; @@ -67,7 +147,11 @@ static void ddr_init(void) REG_DDRC_CTRL = 0; mdelay(3); +#if SPL_DDR_MEMORYSIZE == 64 REG_DDRC_CFG = 0xa468a6c; +#elif SPL_DDR_MEMORYSIZE == 32 + REG_DDRC_CFG = 0xa46896c; +#endif REG_DDRC_CTRL = 2; REG_DDRPHY_DTAR = 0x150000; REG_DDRPHY_DCR = 0; @@ -91,7 +175,7 @@ static void ddr_init(void) while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f) i -= 1; if(i == 0) - spl_error(); + return 1; #if SPL_DDR_NEED_BYPASS REG_DDRPHY_ACDLLCR = 0x80000000; @@ -105,14 +189,18 @@ static void ddr_init(void) while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f) i -= 1; if(i == 0) - spl_error(); + return 2; REG_DDRC_APB_PHYRST_CFG = 0x400000; mdelay(3); REG_DDRC_APB_PHYRST_CFG = 0; mdelay(3); +#if SPL_DDR_MEMORYSIZE == 64 REG_DDRC_CFG = 0xa468aec; +#elif SPL_DDR_MEMORYSIZE == 32 + REG_DDRC_CFG = 0xa4689ec; +#endif REG_DDRC_CTRL = 2; #if SPL_DDR_NEED_BYPASS REG_DDRPHY_PIR = 0x20020081; @@ -128,15 +216,19 @@ static void ddr_init(void) } if(i == 0) - spl_error(); + return 3; if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0) - spl_error(); + return 4; REG_DDRC_CTRL = 0; REG_DDRC_CTRL = 10; REG_DDRC_CTRL = 0; +#if SPL_DDR_MEMORYSIZE == 64 REG_DDRC_CFG = 0xa468a6c; +#elif SPL_DDR_MEMORYSIZE == 32 + REG_DDRC_CFG = 0xa46896c; +#endif REG_DDRC_TIMING1 = 0x2050501; REG_DDRC_TIMING2 = 0x4090404; REG_DDRC_TIMING3 = 0x2704030d; @@ -149,11 +241,9 @@ static void ddr_init(void) #elif SPL_DDR_MEMORYSIZE == 32 REG_DDRC_MMAP0 = 0x20fe; REG_DDRC_MMAP1 = 0x2200; -#else -# error "Unsupported DDR_MEMORYSIZE" #endif REG_DDRC_CTRL = 10; - REG_DDRC_REFCNT = 0x2f0003; + REG_DDRC_REFCNT = 0x2f0003; /* is this adjustable for 32M? */ REG_DDRC_CTRL = 0xc91e; #if SPL_DDR_MEMORYSIZE == 64 @@ -168,8 +258,6 @@ static void ddr_init(void) REG_DDRC_REMAP3 = 0x01000908; REG_DDRC_REMAP4 = 0x0f0e0d0c; REG_DDRC_REMAP5 = 0x13121110; -#else -# error "Unsupported DDR_MEMORYSIZE" #endif REG_DDRC_STATUS &= ~0x40; @@ -184,69 +272,100 @@ static void ddr_init(void) #endif REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN; + return 0; +} + +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(SPL_USE_UCLPACK) + return spl_alloc(BOOT_STORAGE_SIZE); + else + return (void*)BOOT_LOAD_ADDR; } -static void init(void) +/* Mapping of boot_sel[1:0] pins. + * See X1000 PM pg. 687, "XBurst Boot ROM Specification" */ +enum { + BSEL_MSC = 1, + BSEL_USB = 2, + BSEL_SFC = 3, +}; + +static uint32_t get_boot_sel(void) +{ + /* This variable holds the level of the boot_sel[2:0] pins at boot time, + * and is defined by the maskrom. + * + * We use it to detect when we're USB booting, but this isn't totally + * accurate because it only reflects the selected boot mode at reset and + * not the current mode -- if the original selection fails and we fall + * back to USB, this variable will only return the original selection. + */ + return (*(uint32_t*)0xf40001ec) & 3; +} + +void spl_main(void) { - /* from original firmware SPL */ + int rc; + void* load_buffer; + + /* magic */ REG_CPM_PSWC0ST = 0x00; REG_CPM_PSWC1ST = 0x10; REG_CPM_PSWC2ST = 0x18; REG_CPM_PSWC3ST = 0x08; - /* enable MPLL */ -#if X1000_EXCLK_FREQ == 24000000 - /* 24 * (24+1) = 600 MHz */ - jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0)); -#elif X1000_EXCLK_FREQ == 26000000 - /* 26 * (22+1) = 598 MHz */ - jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0)); -#else -# error "unknown EXCLK frequency" -#endif - while(jz_readf(CPM_MPCR, ON) == 0); + /* Save this, it's needed on some targets */ + uint32_t saved_cpm_scratch = REG_CPM_SCRATCH; - /* set DDR clock to MPLL/3 = 200 MHz */ - jz_writef(CPM_CLKGR, DDR(0)); - clk_set_ddr(X1000_CLK_MPLL, 3); + /* set up boot flags */ + init_boot_flags(); - /* start OST so we can use mdelay/udelay */ - jz_writef(CPM_CLKGR, OST(0)); - jz_writef(OST_CTRL, PRESCALE2_V(BY_4)); - jz_writef(OST_CLEAR, OST2(1)); - jz_write(OST_2CNTH, 0); - jz_write(OST_2CNTL, 0); - jz_setf(OST_ENABLE, OST2); + /* early clock and DRAM init */ + clk_init_early(); + init_ost(); + if(init_dram() != 0) + spl_error(); - /* init DDR memory */ - ddr_init(); -} + /* USB boot stops here */ + if(get_boot_sel() == BSEL_USB) { + set_boot_flag(BOOT_FLAG_USB_BOOT); + return; + } -/* This variable is defined by the maskrom. It's simply the level of the - * boot_sel[2:0] pins (GPIOs B28-30) at boot time. Meaning of the bits: - * - * boot_sel[2] boot_sel[1] boot_sel[0] Description - * ----------------------------------------------------------------- - * 1 X X EXCLK is 26 MHz - * 0 X X EXCLK is 24 MHz - * X 1 1 Boot from SFC0 - * X 0 1 Boot from MSC0 - * X 1 0 Boot from USB 2.0 device - * ----------------------------------------------------------------- - * Source: X1000 PM pg. 687, "XBurst Boot ROM Specification" - */ -extern const uint32_t boot_sel; + /* finish up clock init */ + clk_init(); -void spl_main(void) -{ - /* Basic hardware init */ - init(); + /* load the image from storage */ + rc = spl_storage_open(); + if(rc != 0) + spl_error(); - /* If doing a USB boot, host PC will upload 2nd stage itself, - * we should not load anything from flash or change clocks. */ - if((boot_sel & 3) == 2) - return; + load_buffer = get_load_buffer(); + rc = spl_storage_read(BOOT_STORAGE_ADDR, BOOT_STORAGE_SIZE, load_buffer); + if(rc != 0) + spl_error(); + + /* 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(); + + /* close off storage access */ + spl_storage_close(); - /* Just pass control to the target... */ - spl_target_boot(); + /* jump to the entry point */ + typedef void(*entry_fn)(uint32_t); + entry_fn fn = (entry_fn)BOOT_EXEC_ADDR; + commit_discard_idcache(); + 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 81714a3ed2..9ee1aa768e 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.h +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h @@ -22,21 +22,27 @@ #ifndef __SPL_X1000_H__ #define __SPL_X1000_H__ -/* TODO: this needs some refactoring... */ +#include "boot-x1000.h" +#include <stddef.h> +#include <stdint.h> -/* Called on a fatal error */ -extern void spl_error(void) __attribute__((noreturn)); - -/* Called by SPL to handle a main boot */ -extern void spl_target_boot(void); - -/* Invoked by SPL main routine to determine the boot option */ -extern int spl_get_boot_option(void); +/* 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. */ +extern void* spl_alloc(size_t count); -/* Do any setup/initialization needed for the given boot option, this - * will be called right before flushing caches + jumping to the image. - * Typical use is to set up system clocks, etc. +/* Access to boot storage medium, eg. flash or MMC/SD card. + * + * Read address and length is given in bytes. To make life easier, no + * alignment restrictions are placed on the buffer, length, or address. + * The buffer doesn't even need to be in DRAM. */ -extern void spl_handle_pre_boot(int bootopt); +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); + +/* 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)); #endif /* __SPL_X1000_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds index e932bd9c2e..b3e508e9c3 100644 --- a/firmware/target/mips/ingenic_x1000/spl.lds +++ b/firmware/target/mips/ingenic_x1000/spl.lds @@ -7,22 +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 { - /* Mask ROM variables, addresses found by disassembly */ - boot_sel = X1000_TCSM_BASE + 0x1ec; - .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 779bb2055c..64890a6c3a 100644 --- a/firmware/target/mips/ingenic_x1000/system-x1000.c +++ b/firmware/target/mips/ingenic_x1000/system-x1000.c @@ -27,6 +27,7 @@ #include "dma-x1000.h" #include "irq-x1000.h" #include "clk-x1000.h" +#include "boot-x1000.h" #include "x1000/cpm.h" #include "x1000/ost.h" #include "x1000/tcu.h" @@ -43,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 */ @@ -59,6 +61,26 @@ static void system_init_irq(void) write_c0_cause(M_CauseIV); } +/* First function called by crt0.S */ +void system_early_init(void) +{ +#if defined(FIIO_M3K) && !defined(BOOTLOADER) + /* HACK for compatibility: CPM scratchpad has undefined contents at + * time of reset and old bootloader revisions don't initialize it. + * Therefore we can't rely on its contents on the FiiO M3K. This does + * kind of break the entire point of boot flags, but right now they + * are really only used by the bootloader so it's not a huge issue. + * This hack should keep everything working as usual. */ + if(jz_readf(CPM_MPCR, ON) == 0) { + init_boot_flags(); + set_boot_flag(BOOT_FLAG_CLK_INIT); + } +#endif + + /* Finish up clock init */ + clk_init(); +} + /* First thing called from Rockbox main() */ void system_init(void) { @@ -322,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(); @@ -342,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/ingenic_x1000/usb-x1000.c b/firmware/target/mips/ingenic_x1000/usb-x1000.c index 1a31d8db2e..398528c6c4 100644 --- a/firmware/target/mips/ingenic_x1000/usb-x1000.c +++ b/firmware/target/mips/ingenic_x1000/usb-x1000.c @@ -35,10 +35,13 @@ const struct usb_dw_config usb_dw_config = { .phytype = DWC_PHYTYPE_UTMI_16, - /* Available FIFO memory: 3576 words */ - .rx_fifosz = 1024, - .nptx_fifosz = 128, /* 1 dedicated FIFO for EP0 */ - .ptx_fifosz = 768, /* 3 dedicated FIFOs */ + /* Available FIFO memory: 3576 words + * Number of endpoints: 9 + * Max packet size: 512 bytes + */ + .rx_fifosz = 816, /* shared RxFIFO */ + .nptx_fifosz = 32, /* only used for EP0 IN */ + .ptx_fifosz = 384, /* room for 7 IN EPs */ #ifndef USB_DW_ARCH_SLAVE .ahb_burst_len = HBSTLEN_INCR16, diff --git a/firmware/target/mips/ingenic_x1000/x1000/cpm.h b/firmware/target/mips/ingenic_x1000/x1000/cpm.h index 30750195ce..3d59fd6fc7 100644 --- a/firmware/target/mips/ingenic_x1000/x1000/cpm.h +++ b/firmware/target/mips/ingenic_x1000/x1000/cpm.h @@ -674,6 +674,18 @@ #define JN_CPM_DRCG CPM_DRCG #define JI_CPM_DRCG +#define REG_CPM_SCRATCH_PROT jz_reg(CPM_SCRATCH_PROT) +#define JA_CPM_SCRATCH_PROT (0xb0000000 + 0x38) +#define JT_CPM_SCRATCH_PROT JIO_32_RW +#define JN_CPM_SCRATCH_PROT CPM_SCRATCH_PROT +#define JI_CPM_SCRATCH_PROT + +#define REG_CPM_SCRATCH jz_reg(CPM_SCRATCH) +#define JA_CPM_SCRATCH (0xb0000000 + 0x34) +#define JT_CPM_SCRATCH JIO_32_RW +#define JN_CPM_SCRATCH CPM_SCRATCH +#define JI_CPM_SCRATCH + #define REG_CPM_USBPCR jz_reg(CPM_USBPCR) #define JA_CPM_USBPCR (0xb0000000 + 0x3c) #define JT_CPM_USBPCR JIO_32_RW @@ -1427,4 +1439,34 @@ #define BF_CPM_OPCR_BUS_MODE_V(e) BF_CPM_OPCR_BUS_MODE(BV_CPM_OPCR_BUS_MODE__##e) #define BFM_CPM_OPCR_BUS_MODE_V(v) BM_CPM_OPCR_BUS_MODE +#define REG_CPM_RSR jz_reg(CPM_RSR) +#define JA_CPM_RSR (0xb0000000 + 0x8) +#define JT_CPM_RSR JIO_32_RW +#define JN_CPM_RSR CPM_RSR +#define JI_CPM_RSR +#define BP_CPM_RSR_HR 3 +#define BM_CPM_RSR_HR 0x8 +#define BF_CPM_RSR_HR(v) (((v) & 0x1) << 3) +#define BFM_CPM_RSR_HR(v) BM_CPM_RSR_HR +#define BF_CPM_RSR_HR_V(e) BF_CPM_RSR_HR(BV_CPM_RSR_HR__##e) +#define BFM_CPM_RSR_HR_V(v) BM_CPM_RSR_HR +#define BP_CPM_RSR_P0R 2 +#define BM_CPM_RSR_P0R 0x4 +#define BF_CPM_RSR_P0R(v) (((v) & 0x1) << 2) +#define BFM_CPM_RSR_P0R(v) BM_CPM_RSR_P0R +#define BF_CPM_RSR_P0R_V(e) BF_CPM_RSR_P0R(BV_CPM_RSR_P0R__##e) +#define BFM_CPM_RSR_P0R_V(v) BM_CPM_RSR_P0R +#define BP_CPM_RSR_WR 1 +#define BM_CPM_RSR_WR 0x2 +#define BF_CPM_RSR_WR(v) (((v) & 0x1) << 1) +#define BFM_CPM_RSR_WR(v) BM_CPM_RSR_WR +#define BF_CPM_RSR_WR_V(e) BF_CPM_RSR_WR(BV_CPM_RSR_WR__##e) +#define BFM_CPM_RSR_WR_V(v) BM_CPM_RSR_WR +#define BP_CPM_RSR_PR 0 +#define BM_CPM_RSR_PR 0x1 +#define BF_CPM_RSR_PR(v) (((v) & 0x1) << 0) +#define BFM_CPM_RSR_PR(v) BM_CPM_RSR_PR +#define BF_CPM_RSR_PR_V(e) BF_CPM_RSR_PR(BV_CPM_RSR_PR__##e) +#define BFM_CPM_RSR_PR_V(v) BM_CPM_RSR_PR + #endif /* __HEADERGEN_CPM_H__*/ diff --git a/firmware/target/mips/ingenic_x1000/x1000/ost.h b/firmware/target/mips/ingenic_x1000/x1000/ost.h index 8f2619e0e7..9444712e86 100644 --- a/firmware/target/mips/ingenic_x1000/x1000/ost.h +++ b/firmware/target/mips/ingenic_x1000/x1000/ost.h @@ -31,21 +31,21 @@ #define JT_OST_CTRL JIO_32_RW #define JN_OST_CTRL OST_CTRL #define JI_OST_CTRL -#define BP_OST_CTRL_PRESCALE2 3 -#define BM_OST_CTRL_PRESCALE2 0x38 +#define BP_OST_CTRL_PRESCALE2 2 +#define BM_OST_CTRL_PRESCALE2 0xc #define BV_OST_CTRL_PRESCALE2__BY_1 0x0 #define BV_OST_CTRL_PRESCALE2__BY_4 0x1 #define BV_OST_CTRL_PRESCALE2__BY_16 0x2 -#define BF_OST_CTRL_PRESCALE2(v) (((v) & 0x7) << 3) +#define BF_OST_CTRL_PRESCALE2(v) (((v) & 0x3) << 2) #define BFM_OST_CTRL_PRESCALE2(v) BM_OST_CTRL_PRESCALE2 #define BF_OST_CTRL_PRESCALE2_V(e) BF_OST_CTRL_PRESCALE2(BV_OST_CTRL_PRESCALE2__##e) #define BFM_OST_CTRL_PRESCALE2_V(v) BM_OST_CTRL_PRESCALE2 #define BP_OST_CTRL_PRESCALE1 0 -#define BM_OST_CTRL_PRESCALE1 0x7 +#define BM_OST_CTRL_PRESCALE1 0x3 #define BV_OST_CTRL_PRESCALE1__BY_1 0x0 #define BV_OST_CTRL_PRESCALE1__BY_4 0x1 #define BV_OST_CTRL_PRESCALE1__BY_16 0x2 -#define BF_OST_CTRL_PRESCALE1(v) (((v) & 0x7) << 0) +#define BF_OST_CTRL_PRESCALE1(v) (((v) & 0x3) << 0) #define BFM_OST_CTRL_PRESCALE1(v) BM_OST_CTRL_PRESCALE1 #define BF_OST_CTRL_PRESCALE1_V(e) BF_OST_CTRL_PRESCALE1(BV_OST_CTRL_PRESCALE1__##e) #define BFM_OST_CTRL_PRESCALE1_V(v) BM_OST_CTRL_PRESCALE1 diff --git a/firmware/target/mips/ingenic_x1000/x1000boot.make b/firmware/target/mips/ingenic_x1000/x1000boot.make index e69b6a1439..0bdf5cf7b4 100644 --- a/firmware/target/mips/ingenic_x1000/x1000boot.make +++ b/firmware/target/mips/ingenic_x1000/x1000boot.make @@ -7,6 +7,8 @@ # $Id$ # +include $(ROOTDIR)/lib/microtar/microtar.make + INCLUDES += -I$(APPSDIR) SRC += $(call preprocess, $(APPSDIR)/SOURCES) @@ -14,16 +16,23 @@ LDSDEP := $(FIRMDIR)/export/cpu.h $(FIRMDIR)/export/config/$(MODELNAME).h BOOTLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/boot.lds BOOTLINK := $(BUILDDIR)/boot.link +BOOTEXT := $(suffix $(BINARY)) SPLLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/spl.lds SPLLINK := $(BUILDDIR)/spl.link +SPLBINARY := spl$(BOOTEXT) + +BLINFO = $(BUILDDIR)/bootloader-info.txt CLEANOBJS += $(BUILDDIR)/bootloader.* $(BUILDDIR)/spl.* -include $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/$(MODELNAME)/boot.make +# Currently not needed +#include $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/$(MODELNAME)/boot.make .SECONDEXPANSION: +### Bootloader + $(BOOTLINK): $(BOOTLDS) $(LDSDEP) $(call PRINTS,PP $(@F)) $(call preprocess2file,$<,$@,) @@ -38,6 +47,12 @@ $(BUILDDIR)/bootloader.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(BOOTLINK) $(BUILDDIR)/bootloader.bin: $(BUILDDIR)/bootloader.elf $(call PRINTS,OC $(@F))$(call objcopy,$<,$@) +$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin + $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null + + +### SPL + $(SPLLINK): $(SPLLDS) $(LDSDEP) $(call PRINTS,PP $(@F)) $(call preprocess2file,$<,$@,) @@ -51,3 +66,29 @@ $(BUILDDIR)/spl.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(SPLLINK) $(BUILDDIR)/spl.bin: $(BUILDDIR)/spl.elf $(call PRINTS,OC $(@F))$(call objcopy,$<,$@) + +$(BUILDDIR)/$(SPLBINARY): $(BUILDDIR)/spl.bin + $(call PRINTS,MKSPL $(@F))$(MKFIRMWARE) $< $@ + + +### Generating the update package + +# suppress regenerating bootloader-info if nothing has changed +BLVERSION:=$(SVNVERSION) +OLDBLVERSION:=$(shell head -n1 $(BLINFO) 2>/dev/null || echo "NOREVISION") + +ifneq ($(BLVERSION),$(OLDBLVERSION)) +.PHONY: $(BLINFO) +endif + +$(BLINFO): + $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@ + +# The "binary" is actually an update package which is just a tar archive +$(BUILDDIR)/$(BINARY): $(BUILDDIR)/$(SPLBINARY) \ + $(BUILDDIR)/bootloader.ucl \ + $(BLINFO) + $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \ + --numeric-owner --no-acls --no-xattrs --no-selinux \ + --mode=0644 --owner=0 --group=0 \ + -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^) 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/arm/imx233/samsung-ypz5/ftl-target.h b/firmware/target/mips/system-mips.h index cb943e34f2..d9108ef7c2 100644 --- a/firmware/target/arm/imx233/samsung-ypz5/ftl-target.h +++ b/firmware/target/mips/system-mips.h @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2009 by Michael Sparmann + * 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 @@ -19,21 +19,22 @@ * ****************************************************************************/ -#ifndef __FTL_TARGET_H__ -#define __FTL_TARGET_H__ +#ifndef SYSTEM_MIPS_H +#define SYSTEM_MIPS_H -#include "config.h" -#include "inttypes.h" +#include <stdint.h> -#ifdef BOOTLOADER -/* Bootloaders don't need write access */ -#define FTL_READONLY -#endif +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; +}; -uint32_t ftl_init(void); -uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer); -uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer); -uint32_t ftl_sync(void); +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 +#endif /* SYSTEM_MIPS_H */ diff --git a/firmware/usb.c b/firmware/usb.c index b919fe468d..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) @@ -476,7 +479,12 @@ static void NORETURN_ATTR usb_thread(void) usb_state = USB_POWERED; usb_stack_enable(true); - +#ifndef BOOTLOADER +#ifndef HAVE_USB_POWER + int usb_mode = -1; +#endif + send_event(SYS_EVENT_USB_INSERTED, &usb_mode); +#endif /* Power (charging-only) button */ #ifdef HAVE_USB_POWER new_usbmode = usb_mode; @@ -486,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 @@ -547,7 +530,9 @@ static void NORETURN_ATTR usb_thread(void) #ifdef HAVE_USB_POWER new_usbmode = usb_mode; #endif - +#ifndef BOOTLOADER + send_event(SYS_EVENT_USB_EXTRACTED, NULL); +#endif usb_set_host_present(false); break; /* USB_EXTRACTED: */ @@ -598,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 @@ -607,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); } } @@ -644,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 @@ -659,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 { @@ -667,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); @@ -682,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 { @@ -690,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_class_driver.h b/firmware/usbstack/usb_class_driver.h index 20ee26a3d0..bffc994d9e 100644 --- a/firmware/usbstack/usb_class_driver.h +++ b/firmware/usbstack/usb_class_driver.h @@ -22,6 +22,11 @@ #ifndef _USB_CLASS_DRIVER_H_ #define _USB_CLASS_DRIVER_H_ +#include "usb_ch9.h" +#include <stdbool.h> +#include <stddef.h> +#include <string.h> + /* Common api, implemented by all class drivers */ struct usb_class_driver { @@ -75,7 +80,7 @@ struct usb_class_driver { able to handle it, it should ack the request, and return true. Otherwise it should return false. Optional function */ - bool (*control_request)(struct usb_ctrlrequest* req, unsigned char *dest); + bool (*control_request)(struct usb_ctrlrequest* req, void* reqdata, unsigned char *dest); #ifdef HAVE_HOTSWAP /* Tells the driver that a hotswappable disk/card was inserted or diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index 3d187c8cab..9fe8b3e603 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -22,6 +22,7 @@ #include "thread.h" #include "kernel.h" #include "string.h" +#include "panic.h" /*#define LOGF_ENABLE*/ #include "logf.h" @@ -62,6 +63,10 @@ #include "ocotp-imx233.h" #endif +#ifdef SANSA_CONNECT +#include "cryptomem-sansaconnect.h" +#endif + #ifndef USB_MAX_CURRENT #define USB_MAX_CURRENT 500 #endif @@ -86,9 +91,9 @@ static struct usb_device_descriptor __attribute__((aligned(2))) .idVendor = USB_VENDOR_ID, .idProduct = USB_PRODUCT_ID, .bcdDevice = 0x0100, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, + .iManufacturer = USB_STRING_INDEX_MANUFACTURER, + .iProduct = USB_STRING_INDEX_PRODUCT, + .iSerialNumber = USB_STRING_INDEX_SERIAL, .bNumConfigurations = 1 } ; @@ -137,12 +142,12 @@ static const struct usb_string_descriptor __attribute__((aligned(2))) lang_descriptor = USB_STRING_INITIALIZER(u"\x0409"); /* LANGID US English */ -static const struct usb_string_descriptor* const usb_strings[] = +static const struct usb_string_descriptor* const usb_strings[USB_STRING_INDEX_MAX] = { - &lang_descriptor, - &usb_string_iManufacturer, - &usb_string_iProduct, - &usb_string_iSerial + [USB_STRING_INDEX_LANGUAGE] = &lang_descriptor, + [USB_STRING_INDEX_MANUFACTURER] = &usb_string_iManufacturer, + [USB_STRING_INDEX_PRODUCT] = &usb_string_iProduct, + [USB_STRING_INDEX_SERIAL] = &usb_string_iSerial, }; static int usb_address = 0; @@ -168,7 +173,7 @@ static int usb_no_host_callback(struct timeout *tmo) static int usb_core_num_interfaces; typedef void (*completion_handler_t)(int ep, int dir, int status, int length); -typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, +typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); static struct @@ -258,7 +263,15 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] = #endif }; -static void usb_core_control_request_handler(struct usb_ctrlrequest* req); +#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 + +static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata); static unsigned char response_data[256] USB_DEVBSS_ATTR; @@ -327,6 +340,22 @@ static void set_serial_descriptor(void) } usb_string_iSerial.bLength = 2 + 2 * (1 + IMX233_NUM_OCOTP_OPS * 8); } +#elif defined(SANSA_CONNECT) +static void set_serial_descriptor(void) +{ + char deviceid[32]; + short* p = &usb_string_iSerial.wString[1]; + int i; + + if(!cryptomem_read_deviceid(deviceid)) { + for(i = 0; i < 32; i++) { + *p++ = deviceid[i]; + } + usb_string_iSerial.bLength = 2 + 2 * (1 + 32); + } else { + device_descriptor.iSerialNumber = 0; + } +} #elif (CONFIG_STORAGE & STORAGE_ATA) /* If we don't know the device serial number, use the one * from the disk */ @@ -426,10 +455,10 @@ void usb_core_handle_transfer_completion( case EP_CONTROL: logf("ctrl handled %ld req=0x%x", current_tick, - ((struct usb_ctrlrequest*)event->data)->bRequest); + ((struct usb_ctrlrequest*)event->data[0])->bRequest); usb_core_control_request_handler( - (struct usb_ctrlrequest*)event->data); + (struct usb_ctrlrequest*)event->data[0], event->data[1]); break; default: handler = ep_data[ep].completion_handler[EP_DIR(event->dir)]; @@ -540,7 +569,7 @@ static void allocate_interfaces_and_endpoints(void) } -static void control_request_handler_drivers(struct usb_ctrlrequest* req) +static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* reqdata) { int i, interface = req->wIndex & 0xff; bool handled = false; @@ -551,7 +580,7 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req) drivers[i].first_interface <= interface && drivers[i].last_interface > interface) { - handled = drivers[i].control_request(req, response_data); + handled = drivers[i].control_request(req, reqdata, response_data); if(handled) break; } @@ -559,11 +588,11 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req) if(!handled) { /* nope. flag error */ logf("bad req:desc %d:%d", req->bRequest, req->wValue >> 8); - usb_drv_stall(EP_CONTROL, true, true); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); } } -static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req) +static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, void* reqdata) { int size; const void* ptr = NULL; @@ -617,8 +646,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req) case USB_DT_STRING: logf("STRING %d", index); - if((unsigned)index < (sizeof(usb_strings) / - sizeof(struct usb_string_descriptor*))) { + if((unsigned)index < USB_STRING_INDEX_MAX) { size = usb_strings[index]->bLength; ptr = usb_strings[index]; } @@ -632,7 +660,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req) } else { logf("bad string id %d", index); - usb_drv_stall(EP_CONTROL, true, true); + ptr = NULL; } break; @@ -643,8 +671,8 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req) default: logf("ctrl desc."); - control_request_handler_drivers(req); - break; + control_request_handler_drivers(req, reqdata); + return; } if(ptr) { @@ -654,8 +682,9 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req) if (ptr != response_data) memcpy(response_data, ptr, length); - usb_drv_recv(EP_CONTROL, NULL, 0); - usb_drv_send(EP_CONTROL, response_data, length); + usb_drv_control_response(USB_CONTROL_ACK, response_data, length); + } else { + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); } } @@ -699,105 +728,105 @@ static void usb_core_do_clear_feature(int recip, int recip_nr, int feature) } } -static void request_handler_device(struct usb_ctrlrequest* req) +static void request_handler_device(struct usb_ctrlrequest* req, void* reqdata) { + unsigned address; + switch(req->bRequest) { - case USB_REQ_GET_CONFIGURATION: { - logf("usb_core: GET_CONFIG"); - response_data[0] = (usb_state == ADDRESS ? 0 : 1); - usb_drv_recv(EP_CONTROL, NULL, 0); - usb_drv_send(EP_CONTROL, response_data, 1); - break; - } - case USB_REQ_SET_CONFIGURATION: { - usb_drv_cancel_all_transfers(); - usb_core_do_set_config(req->wValue); - usb_drv_send(EP_CONTROL, NULL, 0); - break; - } - case USB_REQ_SET_ADDRESS: { - unsigned char address = req->wValue; - usb_drv_send(EP_CONTROL, NULL, 0); - usb_drv_cancel_all_transfers(); - usb_drv_set_address(address); - usb_core_do_set_addr(address); - break; - } + case USB_REQ_GET_CONFIGURATION: + logf("usb_core: GET_CONFIG"); + response_data[0] = (usb_state == ADDRESS ? 0 : 1); + usb_drv_control_response(USB_CONTROL_ACK, response_data, 1); + break; + case USB_REQ_SET_CONFIGURATION: + usb_drv_cancel_all_transfers(); + usb_core_do_set_config(req->wValue); + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + break; + case USB_REQ_SET_ADDRESS: + /* NOTE: We really have no business handling this and drivers + * should just handle it themselves. We don't care beyond + * knowing if we've been assigned an address yet, or not. */ + address = req->wValue; + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + usb_drv_cancel_all_transfers(); + usb_drv_set_address(address); + usb_core_do_set_addr(address); + break; case USB_REQ_GET_DESCRIPTOR: logf("usb_core: GET_DESC %d", req->wValue >> 8); - request_handler_device_get_descriptor(req); - break; - case USB_REQ_CLEAR_FEATURE: + request_handler_device_get_descriptor(req, reqdata); break; case USB_REQ_SET_FEATURE: if(req->wValue==USB_DEVICE_TEST_MODE) { int mode = req->wIndex >> 8; - usb_drv_send(EP_CONTROL, NULL, 0); + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); usb_drv_set_test_mode(mode); + } else { + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); } break; case USB_REQ_GET_STATUS: response_data[0] = 0; response_data[1] = 0; - usb_drv_recv(EP_CONTROL, NULL, 0); - usb_drv_send(EP_CONTROL, response_data, 2); + usb_drv_control_response(USB_CONTROL_ACK, response_data, 2); break; default: logf("bad req:desc %d:%d", req->bRequest, req->wValue); - usb_drv_stall(EP_CONTROL, true, true); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); break; } } -static void request_handler_interface_standard(struct usb_ctrlrequest* req) +static void request_handler_interface_standard(struct usb_ctrlrequest* req, void* reqdata) { switch (req->bRequest) { case USB_REQ_SET_INTERFACE: logf("usb_core: SET_INTERFACE"); - usb_drv_send(EP_CONTROL, NULL, 0); + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); break; case USB_REQ_GET_INTERFACE: logf("usb_core: GET_INTERFACE"); response_data[0] = 0; - usb_drv_recv(EP_CONTROL, NULL, 0); - usb_drv_send(EP_CONTROL, response_data, 1); - break; - case USB_REQ_CLEAR_FEATURE: - break; - case USB_REQ_SET_FEATURE: + usb_drv_control_response(USB_CONTROL_ACK, response_data, 1); break; case USB_REQ_GET_STATUS: response_data[0] = 0; response_data[1] = 0; - usb_drv_recv(EP_CONTROL, NULL, 0); - usb_drv_send(EP_CONTROL, response_data, 2); + usb_drv_control_response(USB_CONTROL_ACK, response_data, 2); + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* TODO: These used to be ignored (erroneously). + * Should they be passed to the drivers instead? */ + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); break; default: - control_request_handler_drivers(req); + control_request_handler_drivers(req, reqdata); break; } } -static void request_handler_interface(struct usb_ctrlrequest* req) +static void request_handler_interface(struct usb_ctrlrequest* req, void* reqdata) { switch(req->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: - request_handler_interface_standard(req); + request_handler_interface_standard(req, reqdata); break; case USB_TYPE_CLASS: - control_request_handler_drivers(req); + control_request_handler_drivers(req, reqdata); break; case USB_TYPE_VENDOR: default: logf("bad req:desc %d", req->bRequest); - usb_drv_stall(EP_CONTROL, true, true); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); break; } } -static void request_handler_endoint_drivers(struct usb_ctrlrequest* req) +static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void* reqdata) { bool handled = false; control_handler_t control_handler = NULL; @@ -807,30 +836,30 @@ static void request_handler_endoint_drivers(struct usb_ctrlrequest* req) ep_data[EP_NUM(req->wIndex)].control_handler[EP_DIR(req->wIndex)]; if(control_handler) - handled = control_handler(req, response_data); + handled = control_handler(req, reqdata, response_data); if(!handled) { /* nope. flag error */ logf("usb bad req %d", req->bRequest); - usb_drv_stall(EP_CONTROL, true, true); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); } } -static void request_handler_endpoint_standard(struct usb_ctrlrequest* req) +static void request_handler_endpoint_standard(struct usb_ctrlrequest* req, void* reqdata) { switch (req->bRequest) { case USB_REQ_CLEAR_FEATURE: usb_core_do_clear_feature(USB_RECIP_ENDPOINT, req->wIndex, req->wValue); - usb_drv_send(EP_CONTROL, NULL, 0); + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); break; case USB_REQ_SET_FEATURE: logf("usb_core: SET FEATURE (%d)", req->wValue); if(req->wValue == USB_ENDPOINT_HALT) usb_drv_stall(EP_NUM(req->wIndex), true, EP_DIR(req->wIndex)); - - usb_drv_send(EP_CONTROL, NULL, 0); + + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); break; case USB_REQ_GET_STATUS: response_data[0] = 0; @@ -839,35 +868,34 @@ static void request_handler_endpoint_standard(struct usb_ctrlrequest* req) if(req->wIndex > 0) response_data[0] = usb_drv_stalled(EP_NUM(req->wIndex), EP_DIR(req->wIndex)); - - usb_drv_recv(EP_CONTROL, NULL, 0); - usb_drv_send(EP_CONTROL, response_data, 2); + + usb_drv_control_response(USB_CONTROL_ACK, response_data, 2); break; default: - request_handler_endoint_drivers(req); + request_handler_endpoint_drivers(req, reqdata); break; } } -static void request_handler_endpoint(struct usb_ctrlrequest* req) +static void request_handler_endpoint(struct usb_ctrlrequest* req, void* reqdata) { switch(req->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: - request_handler_endpoint_standard(req); + request_handler_endpoint_standard(req, reqdata); break; case USB_TYPE_CLASS: - request_handler_endoint_drivers(req); + request_handler_endpoint_drivers(req, reqdata); break; case USB_TYPE_VENDOR: default: logf("bad req:desc %d", req->bRequest); - usb_drv_stall(EP_CONTROL, true, true); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); break; } } /* Handling USB requests starts here */ -static void usb_core_control_request_handler(struct usb_ctrlrequest* req) +static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata) { #ifdef HAVE_USB_CHARGING_ENABLE timeout_cancel(&usb_no_host_timeout); @@ -885,20 +913,19 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req) switch(req->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: - request_handler_device(req); + request_handler_device(req, reqdata); break; case USB_RECIP_INTERFACE: - request_handler_interface(req); + request_handler_interface(req, reqdata); break; case USB_RECIP_ENDPOINT: - request_handler_endpoint(req); + request_handler_endpoint(req, reqdata); break; - case USB_RECIP_OTHER: + default: logf("unsupported recipient"); - usb_drv_stall(EP_CONTROL, true, true); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); break; } - //logf("control handled"); } /* called by usb_drv_int() */ @@ -908,32 +935,47 @@ void usb_core_bus_reset(void) usb_address = 0; usb_state = DEFAULT; #ifdef HAVE_USB_CHARGING_ENABLE +#ifdef HAVE_USB_CHARGING_IN_THREAD + /* On some targets usb_charging_maxcurrent_change() cannot be called + * from an interrupt handler; get the USB thread to do it instead. */ + usb_charger_update(); +#else usb_charging_maxcurrent_change(usb_charging_maxcurrent()); #endif +#endif } /* called by usb_drv_transfer_completed() */ void usb_core_transfer_complete(int endpoint, int dir, int status, int length) { - struct usb_transfer_completion_event_data *completion_event; + struct usb_transfer_completion_event_data* completion_event = + &ep_data[endpoint].completion_event[EP_DIR(dir)]; - switch (endpoint) { - case EP_CONTROL: - /* already handled */ - break; + void* data0 = NULL; + void* data1 = NULL; - default: - completion_event = &ep_data[endpoint].completion_event[EP_DIR(dir)]; +#ifdef USB_LEGACY_CONTROL_API + if(endpoint == EP_CONTROL) { + bool cwdd = control_write_data_done; + struct usb_ctrlrequest* req = active_request; - completion_event->endpoint = endpoint; - completion_event->dir = dir; - completion_event->data = 0; - completion_event->status = status; - completion_event->length = length; - /* All other endpoints. Let the thread deal with it */ - usb_signal_transfer_completion(completion_event); - break; + if(dir == USB_DIR_OUT && req && cwdd) { + data0 = req; + data1 = control_write_data; + } else { + return; + } } +#endif + + completion_event->endpoint = endpoint; + completion_event->dir = dir; + completion_event->data[0] = data0; + completion_event->data[1] = data1; + completion_event->status = status; + completion_event->length = length; + + usb_signal_transfer_completion(completion_event); } void usb_core_handle_notify(long id, intptr_t data) @@ -951,21 +993,154 @@ void usb_core_handle_notify(long id, intptr_t data) } } -/* called by usb_drv_int() */ -void usb_core_control_request(struct usb_ctrlrequest* req) +void usb_core_control_request(struct usb_ctrlrequest* req, void* reqdata) { struct usb_transfer_completion_event_data* completion_event = &ep_data[EP_CONTROL].completion_event[EP_DIR(USB_DIR_IN)]; completion_event->endpoint = EP_CONTROL; completion_event->dir = 0; - completion_event->data = (void*)req; + completion_event->data[0] = (void*)req; + completion_event->data[1] = reqdata; completion_event->status = 0; completion_event->length = 0; logf("ctrl received %ld, req=0x%x", current_tick, req->bRequest); usb_signal_transfer_completion(completion_event); } +void usb_core_control_complete(int status) +{ + /* We currently don't use this, it's here to make the API look good ;) + * It makes sense to #define it away on normal builds. + */ + (void)status; + logf("ctrl complete %ld, %d", current_tick, status); +} + +#ifdef USB_LEGACY_CONTROL_API +/* Only needed if the driver does not support the new API yet */ +void usb_core_legacy_control_request(struct usb_ctrlrequest* req) +{ + /* 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); + } +} + +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; + } + + /* + * 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) + { + active_request = NULL; + + /* No-data request */ + if(resp == USB_CONTROL_ACK) + usb_drv_send(EP_CONTROL, data, length); + else if(resp == USB_CONTROL_STALL) + usb_drv_stall(EP_CONTROL, true, true); + else + panicf("RECEIVE on non-data req"); + } + else if(req->bRequestType & USB_DIR_IN) + { + /* Control read request */ + if(resp == USB_CONTROL_ACK) + { + active_request = NULL; + usb_drv_recv_nonblocking(EP_CONTROL, NULL, 0); + usb_drv_send(EP_CONTROL, data, length); + } + else if(resp == USB_CONTROL_STALL) + { + active_request = NULL; + usb_drv_stall(EP_CONTROL, true, true); + } + else + { + panicf("RECEIVE on ctrl read req"); + } + } + else if(!control_write_data_done) + { + /* Control write request, data phase */ + if(resp == USB_CONTROL_RECEIVE) + { + control_write_data = data; + control_write_data_done = true; + usb_drv_recv_nonblocking(EP_CONTROL, data, length); + } + else if(resp == USB_CONTROL_STALL) + { + /* We should stall the OUT endpoint here, but the old code did + * not do so and some drivers may not handle it correctly. */ + active_request = NULL; + usb_drv_stall(EP_CONTROL, true, true); + } + else + { + panicf("ACK on ctrl write data"); + } + } + else + { + active_request = NULL; + control_write_data = NULL; + control_write_data_done = false; + + /* Control write request, status phase */ + if(resp == USB_CONTROL_ACK) + usb_drv_send(EP_CONTROL, NULL, 0); + else if(resp == USB_CONTROL_STALL) + usb_drv_stall(EP_CONTROL, true, true); + else + panicf("RECEIVE on ctrl write status"); + } +} +#endif + void usb_core_notify_set_address(uint8_t addr) { logf("notify set addr received %ld", current_tick); diff --git a/firmware/usbstack/usb_hid.c b/firmware/usbstack/usb_hid.c index 5885b60e32..64aa123ced 100644 --- a/firmware/usbstack/usb_hid.c +++ b/firmware/usbstack/usb_hid.c @@ -664,10 +664,9 @@ void usb_hid_transfer_complete(int ep, int dir, int status, int length) * In order to allow sending info to the DAP, the Set Report mechanism can be * used by defining vendor specific output reports and send them from the host * to the DAP using the host's custom driver */ -static int usb_hid_set_report(struct usb_ctrlrequest *req) +static int usb_hid_set_report(struct usb_ctrlrequest *req, void *reqdata) { - static unsigned char buf[SET_REPORT_BUF_LEN] USB_DEVBSS_ATTR - __attribute__((aligned(32))); + static unsigned char buf[64] USB_DEVBSS_ATTR __attribute__((aligned(32))); int length; if ((req->wValue >> 8) != REPORT_TYPE_OUTPUT) @@ -692,8 +691,11 @@ static int usb_hid_set_report(struct usb_ctrlrequest *req) return 4; } - memset(buf, 0, length); - usb_drv_recv(EP_CONTROL, buf, length); + if(!reqdata) { + memset(buf, 0, length); + usb_drv_control_response(USB_CONTROL_RECEIVE, buf, length); + return 0; + } #ifdef LOGF_ENABLE if (buf[1] & 0x01) @@ -710,10 +712,11 @@ static int usb_hid_set_report(struct usb_ctrlrequest *req) /* Defining other LEDs and setting them from the USB host (OS) can be used * to send messages to the DAP */ + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); return 0; } -static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char** dest) +static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char* dest) { if ((req->wValue >> 8) != REPORT_TYPE_FEATURE) { @@ -739,16 +742,17 @@ static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char** dest) return 4; } - (*dest)[0] = 0; - (*dest)[1] = battery_level(); - *dest += GET_REPORT_BUF_LEN; - + dest[0] = 0; + dest[1] = battery_level(); + usb_drv_control_response(USB_CONTROL_ACK, dest, 2); return 0; } /* called by usb_core_control_request() */ -bool usb_hid_control_request(struct usb_ctrlrequest *req, unsigned char *dest) +bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata, unsigned char *dest) { + (void)reqdata; + unsigned char *orig_dest = dest; switch (req->bRequestType & USB_TYPE_MASK) { @@ -772,8 +776,7 @@ bool usb_hid_control_request(struct usb_ctrlrequest *req, unsigned char *dest) if (dest != orig_dest) { - usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */ - usb_drv_send(EP_CONTROL, orig_dest, dest - orig_dest); + usb_drv_control_response(USB_CONTROL_ACK, orig_dest, dest - orig_dest); return true; } break; @@ -785,27 +788,26 @@ bool usb_hid_control_request(struct usb_ctrlrequest *req, unsigned char *dest) (req->bRequest == USB_HID_SET_IDLE) ? "set idle" : ((req->bRequest == USB_HID_SET_REPORT) ? "set report" : ((req->bRequest == USB_HID_GET_REPORT) ? "get report" : ""))); + + int rc; switch (req->bRequest) { case USB_HID_SET_REPORT: - if (usb_hid_set_report(req)) - break; + rc = usb_hid_set_report(req, reqdata); + break; case USB_HID_GET_REPORT: - if (usb_hid_get_report(req, &dest)) - break; + rc = usb_hid_get_report(req, dest); + break; case USB_HID_SET_IDLE: - if (dest != orig_dest) - { - usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */ - usb_drv_send(EP_CONTROL, orig_dest, dest - orig_dest); - } - else - { - usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ - } + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); return true; + default: + /* all other requests are errors */ + return false; } - break; + + if(rc == 0) + return true; } case USB_TYPE_VENDOR: diff --git a/firmware/usbstack/usb_hid.h b/firmware/usbstack/usb_hid.h index 917992cd35..41e6662e29 100644 --- a/firmware/usbstack/usb_hid.h +++ b/firmware/usbstack/usb_hid.h @@ -32,7 +32,7 @@ void usb_hid_init_connection(void); void usb_hid_init(void); void usb_hid_disconnect(void); void usb_hid_transfer_complete(int ep, int dir, int status, int length); -bool usb_hid_control_request(struct usb_ctrlrequest* req, unsigned char* dest); +bool usb_hid_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); void usb_hid_send(usage_page_t usage_page, int id); diff --git a/firmware/usbstack/usb_serial.c b/firmware/usbstack/usb_serial.c index a08394c0a8..ae90b57078 100644 --- a/firmware/usbstack/usb_serial.c +++ b/firmware/usbstack/usb_serial.c @@ -174,7 +174,13 @@ static struct usb_endpoint_descriptor .bInterval = 0 }; -static struct cdc_line_coding line_coding; +union line_coding_buffer +{ + struct cdc_line_coding data; + unsigned char raw[64]; +}; + +static union line_coding_buffer line_coding USB_DEVBSS_ATTR; /* send_buffer: local ring buffer. * transit_buffer: used to store aligned data that will be sent by the USB @@ -184,10 +190,11 @@ static struct cdc_line_coding line_coding; */ #define BUFFER_SIZE 512 #define TRANSIT_BUFFER_SIZE 32 +#define RECV_BUFFER_SIZE 32 static unsigned char send_buffer[BUFFER_SIZE]; static unsigned char transit_buffer[TRANSIT_BUFFER_SIZE] USB_DEVBSS_ATTR __attribute__((aligned(4))); -static unsigned char receive_buffer[32] +static unsigned char receive_buffer[512] USB_DEVBSS_ATTR __attribute__((aligned(32))); static void sendout(void); @@ -277,11 +284,13 @@ int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size) } /* called by usb_core_control_request() */ -bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest) +bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { bool handled = false; (void)dest; + (void)reqdata; + if (req->wIndex != control_interface) { return false; @@ -291,11 +300,19 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest { if (req->bRequest == SET_LINE_CODING) { - if (req->wLength == sizeof(line_coding)) + if (req->wLength == sizeof(struct cdc_line_coding)) { /* Receive line coding into local copy */ - usb_drv_recv(EP_CONTROL, &line_coding, sizeof(line_coding)); - usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + if (!reqdata) + { + usb_drv_control_response(USB_CONTROL_RECEIVE, line_coding.raw, + sizeof(struct cdc_line_coding)); + } + else + { + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + } + handled = true; } } @@ -304,7 +321,7 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest if (req->wLength == 0) { /* wValue holds Control Signal Bitmap that is simply ignored here */ - usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); handled = true; } } @@ -313,11 +330,11 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest { if (req->bRequest == GET_LINE_CODING) { - if (req->wLength == sizeof(line_coding)) + if (req->wLength == sizeof(struct cdc_line_coding)) { /* Send back line coding so host is happy */ - usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */ - usb_drv_send(EP_CONTROL, &line_coding, sizeof(line_coding)); + usb_drv_control_response(USB_CONTROL_ACK, line_coding.raw, + sizeof(struct cdc_line_coding)); handled = true; } } @@ -329,7 +346,7 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest void usb_serial_init_connection(void) { /* prime rx endpoint */ - usb_drv_recv(ep_out, receive_buffer, sizeof receive_buffer); + usb_drv_recv_nonblocking(ep_out, receive_buffer, RECV_BUFFER_SIZE); /* we come here too after a bus reset, so reset some data */ buffer_transitlength = 0; @@ -420,7 +437,7 @@ void usb_serial_transfer_complete(int ep,int dir, int status, int length) /* Data received. TODO : Do something with it ? */ /* Get the next bit */ - usb_drv_recv(ep_out, receive_buffer, sizeof receive_buffer); + usb_drv_recv_nonblocking(ep_out, receive_buffer, RECV_BUFFER_SIZE); break; case USB_DIR_IN: diff --git a/firmware/usbstack/usb_serial.h b/firmware/usbstack/usb_serial.h index f1a603c4a3..c4c0e78724 100644 --- a/firmware/usbstack/usb_serial.h +++ b/firmware/usbstack/usb_serial.h @@ -30,7 +30,7 @@ void usb_serial_init_connection(void); void usb_serial_init(void); void usb_serial_disconnect(void); void usb_serial_transfer_complete(int ep,int dir, int status, int length); -bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char *dest); +bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char *dest); void usb_serial_send(const unsigned char *data, int length); diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c index 39ccb9a6e5..714af9d535 100644 --- a/firmware/usbstack/usb_storage.c +++ b/firmware/usbstack/usb_storage.c @@ -71,7 +71,7 @@ #endif /* USB_READ_BUFFER_SIZE */ /* We don't use sizeof() here, because we *need* a multiple of 32 */ -#define MAX_CBW_SIZE 32 +#define MAX_CBW_SIZE 512 #ifdef USB_WRITE_BUFFER_SIZE #define WRITE_BUFFER_SIZE USB_WRITE_BUFFER_SIZE @@ -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__); @@ -470,7 +468,7 @@ void usb_storage_init_connection(void) ramdisk_buffer = tb.transfer_buffer + ALLOCATE_BUFFER_SIZE; #endif #endif - usb_drv_recv(ep_out, cbw_buffer, MAX_CBW_SIZE); + usb_drv_recv_nonblocking(ep_out, cbw_buffer, MAX_CBW_SIZE); int i; for(i=0;i<storage_num_drives();i++) { @@ -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() */ @@ -589,17 +586,6 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length) } handle_scsi(cbw); break; -#if 0 - if(cur_cmd.cur_cmd == SCSI_WRITE_10) - { - queue_broadcast(SYS_USB_WRITE_DATA, (cur_cmd.lun<<16)+cur_cmd.orig_count); - } - else if(cur_cmd.cur_cmd == SCSI_READ_10) - { - queue_broadcast(SYS_USB_READ_DATA, (cur_cmd.lun<<16)+cur_cmd.orig_count); - } -#endif - break; case SENDING_RESULT: if(dir==USB_DIR_OUT) { logf("OUT received in SENDING"); @@ -673,11 +659,13 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length) } /* called by usb_core_control_request() */ -bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* dest) +bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { bool handled = false; (void)dest; + (void)reqdata; + switch (req->bRequest) { case USB_BULK_GET_MAX_LUN: { *tb.max_lun = storage_num_drives() - 1; @@ -685,8 +673,7 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* des if(skip_first) (*tb.max_lun) --; #endif logf("ums: getmaxlun"); - usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */ - usb_drv_send(EP_CONTROL, tb.max_lun, 1); + usb_drv_control_response(USB_CONTROL_ACK, tb.max_lun, 1); handled = true; break; } @@ -701,7 +688,7 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* des usb_drv_reset_endpoint(ep_in, false); usb_drv_reset_endpoint(ep_out, true); #endif - usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); handled = true; break; } @@ -1187,14 +1174,14 @@ static void send_command_failed_result(void) #if CONFIG_RTC static void receive_time(void) { - usb_drv_recv(ep_out, tb.transfer_buffer, 12); + usb_drv_recv_nonblocking(ep_out, tb.transfer_buffer, 12); state = RECEIVING_TIME; } #endif /* CONFIG_RTC */ static void receive_block_data(void *data,int size) { - usb_drv_recv(ep_out, data, size); + usb_drv_recv_nonblocking(ep_out, data, size); state = RECEIVING_BLOCKS; } @@ -1210,7 +1197,7 @@ static void send_csw(int status) state = WAITING_FOR_CSW_COMPLETION_OR_COMMAND; //logf("CSW: %X",status); /* Already start waiting for the next command */ - usb_drv_recv(ep_out, cbw_buffer, MAX_CBW_SIZE); + usb_drv_recv_nonblocking(ep_out, cbw_buffer, MAX_CBW_SIZE); /* The next completed transfer will be either the CSW one * or the new command */ @@ -1245,7 +1232,7 @@ static void fill_inquiry(IF_MD_NONVOID(int lun)) tb.inquiry->DeviceType = DIRECT_ACCESS_DEVICE; tb.inquiry->AdditionalLength = 0x1f; - memset(tb.inquiry->Reserved, 0, 3); +// memset(tb.inquiry->Reserved, 0, sizeof(tb.inquiry->Reserved)); // Redundant tb.inquiry->Versions = 4; /* SPC-2 */ tb.inquiry->Format = 2; /* SPC-2/3 inquiry format */ diff --git a/firmware/usbstack/usb_storage.h b/firmware/usbstack/usb_storage.h index 3591d285d8..af17689110 100644 --- a/firmware/usbstack/usb_storage.h +++ b/firmware/usbstack/usb_storage.h @@ -30,7 +30,7 @@ void usb_storage_init_connection(void); void usb_storage_disconnect(void); void usb_storage_init(void); void usb_storage_transfer_complete(int ep,int dir,int state,int length); -bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* dest); +bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); #ifdef HAVE_HOTSWAP void usb_storage_notify_hotswap(int volume,bool inserted); #endif |