summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES20
-rw-r--r--firmware/asm/thread.h10
-rw-r--r--firmware/backlight.c10
-rw-r--r--firmware/buflib.c6
-rw-r--r--firmware/common/dir.c93
-rw-r--r--firmware/common/dircache.c13
-rw-r--r--firmware/common/disk.c18
-rw-r--r--firmware/common/file.c21
-rw-r--r--firmware/common/file_internal.c108
-rw-r--r--firmware/common/fileobj_mgr.c103
-rw-r--r--firmware/common/inflate.c34
-rw-r--r--firmware/common/multiboot.c113
-rw-r--r--firmware/common/pathfuncs.c78
-rw-r--r--firmware/common/rb-loader.c95
-rw-r--r--firmware/common/rb_namespace.c341
-rw-r--r--firmware/common/zip.c4
-rw-r--r--firmware/drivers/audio/ak4376.c22
-rw-r--r--firmware/drivers/audio/x1000-codec.c286
-rw-r--r--firmware/drivers/axp-pmu.c202
-rw-r--r--firmware/drivers/fat.c2
-rw-r--r--firmware/drivers/lcd-16bit-common.c168
-rw-r--r--firmware/drivers/lcd-1bit-vert.c1
-rw-r--r--firmware/drivers/lcd-2bit-horz.c1
-rw-r--r--firmware/drivers/lcd-2bit-vert.c1
-rw-r--r--firmware/drivers/lcd-2bit-vi.c1
-rw-r--r--firmware/drivers/lcd-bitmap-common.c34
-rw-r--r--firmware/drivers/lcd-color-common.c19
-rw-r--r--firmware/drivers/rds.c37
-rw-r--r--firmware/drivers/tuner/si4700.c29
-rw-r--r--firmware/export/ak4376.h8
-rw-r--r--firmware/export/audiohw.h13
-rw-r--r--firmware/export/axp-pmu.h6
-rw-r--r--firmware/export/config.h26
-rw-r--r--firmware/export/config/erosqnative.h13
-rw-r--r--firmware/export/config/fiiom3k.h22
-rw-r--r--firmware/export/config/gigabeats.h3
-rw-r--r--firmware/export/config/iaudiox5.h1
-rw-r--r--firmware/export/config/ipod6g.h1
-rw-r--r--firmware/export/config/mrobe500.h2
-rw-r--r--firmware/export/config/samsungypr0.h1
-rw-r--r--firmware/export/config/samsungypr1.h1
-rw-r--r--firmware/export/config/sansaclipplus.h1
-rw-r--r--firmware/export/config/sansaclipzip.h1
-rw-r--r--firmware/export/config/sansae200.h1
-rw-r--r--firmware/export/config/sansafuze.h1
-rw-r--r--firmware/export/config/sansafuzeplus.h1
-rw-r--r--firmware/export/config/sansafuzev2.h1
-rw-r--r--firmware/export/config/shanlingq1.h13
-rw-r--r--firmware/export/i2c-async.h9
-rw-r--r--firmware/export/lcd.h11
-rw-r--r--firmware/export/linuxboot.h192
-rw-r--r--firmware/export/mi4-loader.h22
-rw-r--r--firmware/export/multiboot.h30
-rw-r--r--firmware/export/mv.h4
-rw-r--r--firmware/export/pathfuncs.h7
-rw-r--r--firmware/export/powermgmt.h45
-rw-r--r--firmware/export/rbpaths.h3
-rw-r--r--firmware/export/screendump.h14
-rw-r--r--firmware/export/si4700.h8
-rw-r--r--firmware/export/system.h4
-rw-r--r--firmware/export/x1000-codec.h184
-rw-r--r--firmware/export/x1000.h60
-rw-r--r--firmware/include/buflib.h6
-rw-r--r--firmware/include/dircache_redirect.h69
-rw-r--r--firmware/include/file_internal.h32
-rw-r--r--firmware/include/fileobj_mgr.h5
-rw-r--r--firmware/include/fs_defines.h6
-rw-r--r--firmware/include/inflate.h24
-rw-r--r--firmware/include/rb-loader.h20
-rw-r--r--firmware/include/rb_namespace.h79
-rw-r--r--firmware/kernel/include/mutex.h5
-rw-r--r--firmware/kernel/include/queue.h3
-rw-r--r--firmware/kernel/pthread/corelock.c18
-rw-r--r--firmware/kernel/pthread/mutex.c21
-rw-r--r--firmware/kernel/pthread/thread.c204
-rw-r--r--firmware/linuxboot.c336
-rw-r--r--firmware/powermgmt.c633
-rw-r--r--firmware/rolo.c17
-rw-r--r--firmware/target/arm/as3525/lcd-ssd1303.c3
-rw-r--r--firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c3
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c4
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenv.c3
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c3
-rw-r--r--firmware/target/arm/imx233/debug-imx233.c6
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c3
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c49
-rw-r--r--firmware/target/arm/ipod/lcd-gray.c9
-rw-r--r--firmware/target/arm/lcd-ssd1815.c6
-rw-r--r--firmware/target/arm/olympus/mrobe-100/lcd-mr100.c6
-rw-r--r--firmware/target/arm/pp/mi4-loader.c89
-rw-r--r--firmware/target/arm/rk27xx/lcdif-rk27xx.c3
-rw-r--r--firmware/target/arm/s5l8700/debug-s5l8700.c4
-rw-r--r--firmware/target/arm/s5l8700/yps3/lcd-yps3.c11
-rw-r--r--firmware/target/arm/s5l8702/debug-s5l8702.c4
-rw-r--r--firmware/target/arm/samsung/yh920/lcd-yh920.c3
-rw-r--r--firmware/target/coldfire/iaudio/m3/lcd-m3.c6
-rw-r--r--firmware/target/coldfire/iaudio/m5/lcd-m5.c6
-rw-r--r--firmware/target/coldfire/iriver/h100/lcd-h100.c6
-rw-r--r--firmware/target/coldfire/mpio/hd300/lcd-hd300.c3
-rw-r--r--firmware/target/hosted/ibasso/dx50/adc-target.h (renamed from firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h)0
-rw-r--r--firmware/target/hosted/ibasso/dx90/adc-target.h0
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.c2
-rw-r--r--firmware/target/hosted/ibasso/sysfs-ibasso.c2
-rw-r--r--firmware/target/hosted/rolo.c16
-rw-r--r--firmware/target/hosted/sdl/sim-ui-defines.h8
-rw-r--r--firmware/target/hosted/system-hosted.c1
-rw-r--r--firmware/target/hosted/usb-hiby.c4
-rw-r--r--firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/app.lds18
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.c280
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.h38
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c59
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.h4
-rw-r--r--firmware/target/mips/ingenic_x1000/crt0.S126
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c25
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c129
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.c2
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.h8
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c17
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h3
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c179
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.h4
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c4
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-nand-x1000.c (renamed from firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c)51
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c224
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.h24
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds6
-rw-r--r--firmware/target/mips/ingenic_x1000/system-x1000.c1
-rw-r--r--firmware/usbstack/usb_storage.c7
139 files changed, 3716 insertions, 2138 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 87db67d8fd..49d1d9ac95 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -33,6 +33,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 +47,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
@@ -259,6 +263,7 @@ common/dircache.c
common/pathfuncs.c
common/fdprintf.c
common/linked_list.c
+common/rb_namespace.c
common/strcasecmp.c
common/strcasestr.c
common/strnatcmp.c
@@ -281,7 +286,7 @@ font_cache.c
font.c
hangul.c
lru.c
-#ifndef BOOTLOADER
+#ifdef HAVE_SCREENDUMP
screendump.c
#endif
#if LCD_DEPTH == 1
@@ -1641,6 +1646,7 @@ drivers/nand_id.c
#if CONFIG_CPU == X1000
target/mips/ingenic_x1000/crt0.S
target/mips/ingenic_x1000/aic-x1000.c
+target/mips/ingenic_x1000/boot-x1000.c
target/mips/ingenic_x1000/clk-x1000.c
target/mips/ingenic_x1000/debug-x1000.c
target/mips/ingenic_x1000/dma-x1000.c
@@ -1669,6 +1675,10 @@ target/mips/ingenic_x1000/spl-start.S
target/mips/ingenic_x1000/spl-x1000.c
common/ucl_decompress.c
#endif
+#if (defined(HAVE_X1000_ICODEC_PLAY) || defined(HAVE_X1000_ICODEC_REC)) \
+ && !defined(BOOTLOADER)
+drivers/audio/x1000-codec.c
+#endif
#endif /* CONFIG_CPU == X1000 */
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
@@ -1700,7 +1710,7 @@ target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
-target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* FIIO_M3K */
#if defined(SHANLING_Q1)
@@ -1709,7 +1719,7 @@ target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
-target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* SHANLING_Q1 */
#if defined(EROS_QN)
@@ -1718,7 +1728,7 @@ target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
-target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* EROS_QN */
#if defined(LYRE_PROTO1)
diff --git a/firmware/asm/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..9575ecdc22 100644
--- a/firmware/backlight.c
+++ b/firmware/backlight.c
@@ -21,6 +21,9 @@
*
****************************************************************************/
#include "config.h"
+#if !defined(BOOTLOADER)
+#include "settings.h"
+#endif
#include <stdlib.h>
#include "cpu.h"
#include "kernel.h"
@@ -665,9 +668,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:
diff --git a/firmware/buflib.c b/firmware/buflib.c
index 0e90e7fe72..c6ec011653 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -97,6 +97,12 @@
#define BPANICF panicf
+struct buflib_callbacks buflib_ops_locked = {
+ .move_callback = NULL,
+ .shrink_callback = NULL,
+ .sync_callback = NULL,
+};
+
#define IS_MOVABLE(a) (!a[2].ops || a[2].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,
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index 245947b134..45749b8474 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -28,17 +28,14 @@
#include "pathfuncs.h"
#include "timefuncs.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/* structure used for open directory streams */
static struct dirstr_desc
{
struct filestr_base stream; /* basic stream info (first!) */
- struct dirscan_info scan; /* directory scan cursor */
+ struct ns_scan_info scan; /* directory scan cursor */
struct dirent entry; /* current parsed entry information */
-#ifdef HAVE_MULTIVOLUME
- int volumecounter; /* counter for root volume entries */
-#endif
} open_streams[MAX_OPEN_DIRS];
/* check and return a struct dirstr_desc* from a DIR* */
@@ -48,7 +45,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
dir = NULL;
- else if (dir->stream.flags & FDO_BUSY)
+ else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
return dir;
int errnum;
@@ -105,50 +102,6 @@ static struct dirstr_desc * alloc_dirstr(void)
return NULL;
}
-#ifdef HAVE_MULTIVOLUME
-static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
-{
- /* Volumes (secondary file systems) get inserted into the system root
- * directory. If the path specified volume 0, enumeration will not
- * include other volumes, but just its own files and directories.
- *
- * Fake special directories, which don't really exist, that will get
- * redirected upon opendir()
- */
- while (++dir->volumecounter < NUM_VOLUMES)
- {
- /* on the system root */
- if (!fat_ismounted(dir->volumecounter))
- continue;
-
- get_volume_name(dir->volumecounter, entry->d_name);
- dir->entry.info.attr = ATTR_MOUNT_POINT;
- dir->entry.info.size = 0;
- dir->entry.info.wrtdate = 0;
- dir->entry.info.wrttime = 0;
- return 1;
- }
-
- /* do normal directory entry fetching */
- return 0;
-}
-#endif /* HAVE_MULTIVOLUME */
-
-static inline int readdir_volume(struct dirstr_desc *dir,
- struct dirent *entry)
-{
-#ifdef HAVE_MULTIVOLUME
- /* fetch virtual volume entries? */
- if (dir->volumecounter < NUM_VOLUMES)
- return readdir_volume_inner(dir, entry);
-#endif /* HAVE_MULTIVOLUME */
-
- /* do normal directory entry fetching */
- return 0;
- (void)dir; (void)entry;
-}
-
-
/** POSIX interface **/
/* open a directory */
@@ -166,21 +119,13 @@ DIR * opendir(const char *dirname)
if (!dir)
FILE_ERROR(EMFILE, RC);
- rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL);
+ rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
if (rc < 0)
{
DEBUGF("Open failed: %d\n", rc);
FILE_ERROR(ERRNO, RC);
}
-#ifdef HAVE_MULTIVOLUME
- /* volume counter is relevant only to the system root */
- dir->volumecounter = rc > 1 ? 0 : INT_MAX;
-#endif /* HAVE_MULTIVOLUME */
-
- fat_rewind(&dir->stream.fatstr);
- rewinddir_dirent(&dir->scan);
-
dirp = (DIR *)dir;
file_error:
file_internal_unlock_WRITER();
@@ -205,7 +150,7 @@ int closedir(DIR *dirp)
FILE_ERROR(EBADF, -2);
}
- rc = close_stream_internal(&dir->stream);
+ rc = ns_close_stream(&dir->stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 3);
@@ -223,16 +168,11 @@ struct dirent * readdir(DIR *dirp)
struct dirent *res = NULL;
- int rc = readdir_volume(dir, &dir->entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
- if (rc < 0)
- FILE_ERROR(EIO, RC);
- }
-
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
if (rc > 0)
res = &dir->entry;
+ else if (rc < 0)
+ FILE_ERROR(EIO, RC);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -259,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
if (!dir)
FILE_ERROR_RETURN(ERRNO, -1);
- int rc = readdir_volume(dir, entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, entry);
- if (rc < 0)
- FILE_ERROR(EIO, rc * 10 - 4);
- }
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(EIO, rc * 10 - 4);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -289,12 +225,7 @@ void rewinddir(DIR *dirp)
if (!dir)
FILE_ERROR_RETURN(ERRNO);
- rewinddir_dirent(&dir->scan);
-
-#ifdef HAVE_MULTIVOLUME
- if (dir->volumecounter != INT_MAX)
- dir->volumecounter = 0;
-#endif /* HAVE_MULTIVOLUME */
+ ns_dirscan_rewind(&dir->scan);
RELEASE_DIRSTR(READER, dir);
}
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 3b880d3382..7a84b761a0 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -1473,7 +1473,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;
@@ -1760,7 +1760,7 @@ static int sab_process_volume(IF_MV(int volume,) struct dircache_entry *ce)
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);
@@ -2541,13 +2541,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,
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index c096878e86..e94e161d44 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)
@@ -260,23 +260,7 @@ int disk_mount_all(void)
for (int i = 0; i < NUM_VOLUMES; i++)
vol_drive[i] = -1; /* mark all as unassigned */
-#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))
diff --git a/firmware/common/file.c b/firmware/common/file.c
index c048d182f4..202410db81 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -28,9 +28,18 @@
#include "file.h"
#include "fileobj_mgr.h"
#include "disk_cache.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "string-extra.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#ifdef LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#define DEBUGF logf
+#endif
+
+
/**
* These functions provide a roughly POSIX-compatible file I/O API.
*/
@@ -494,6 +503,8 @@ static int open_internal_inner1(const char *path, int oflag,
return fildes;
file_error:
+ if (fildes >= 0)
+ close(fildes);
return rc;
}
@@ -598,8 +609,10 @@ static inline ssize_t readwrite_partial(struct filestr_desc *file,
static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
bool write)
{
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("readwrite(%p,%lx,%lu,%s)\n",
file, (long)buf, (unsigned long)nbyte, write ? "write" : "read");
+#endif
const file_size_t size = *file->sizep;
file_size_t filerem;
@@ -764,8 +777,9 @@ file_error:;
/* error or not, update the file offset and size if anything was
transferred */
file->offset += done;
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("file offset: %ld\n", file->offset);
-
+#endif
/* adjust file size to length written */
if (write && file->offset > size)
*file->sizep = file->offset;
@@ -899,8 +913,9 @@ file_error:
/* move the read/write file offset */
off_t lseek(int fildes, off_t offset, int whence)
{
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("lseek(fd=%d,ofs=%ld,wh=%d)\n", fildes, (long)offset, whence);
-
+#endif
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
if (!file)
FILE_ERROR_RETURN(ERRNO, -1);
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index b92c4ea115..9ddbb232b4 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -26,12 +26,18 @@
#include "pathfuncs.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dir.h"
-#include "dircache_redirect.h"
-#include "dircache.h"
+#include "rb_namespace.h"
#include "string-extra.h"
#include "rbunicode.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#ifdef LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#define DEBUGF logf
+#endif
+
/** Internal common filesystem service functions **/
/* for internal functions' scanning use to save quite a bit of stack space -
@@ -89,9 +95,9 @@ void file_cache_free(struct filestr_cache *cachep)
/** Stream base APIs **/
-static inline void filestr_clear(struct filestr_base *stream)
+static inline void filestr_clear(struct filestr_base *stream, unsigned int flags)
{
- stream->flags = 0;
+ stream->flags = flags;
stream->bindp = NULL;
#if 0
stream->mtx = NULL;
@@ -155,7 +161,7 @@ void filestr_discard_cache(struct filestr_base *stream)
/* Initialize the base descriptor */
void filestr_base_init(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, FD_VALID);
file_cache_init(&stream->cache);
stream->cachep = &stream->cache;
}
@@ -163,7 +169,7 @@ void filestr_base_init(struct filestr_base *stream)
/* free base descriptor resources */
void filestr_base_destroy(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, 0);
filestr_free_cache(stream);
}
@@ -229,7 +235,7 @@ void iso_decode_d_name(char *d_name)
#ifdef HAVE_DIRCACHE
/* nullify all the fields of the struct dirent */
-void empty_dirent(struct dirent *entry)
+void empty_dirent(struct DIRENT *entry)
{
entry->d_name[0] = '\0';
entry->info.attr = 0;
@@ -251,7 +257,7 @@ void fill_dirinfo_native(struct dirinfo_native *dinp)
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
struct fat_direntry fatent;
int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
@@ -295,7 +301,7 @@ struct pathwalk_component
#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
#define WALK_RC_FOUND 1 /* found and opened */
-#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
+#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
/* return another struct pathwalk_component from the pool, or NULL if the
@@ -399,10 +405,9 @@ static int walk_open_info(struct pathwalk *walkp,
/* make open official if not simply probing for presence - must do it here
or compp->info on stack will get destroyed before it was copied */
- if (!(callflags & FF_PROBE))
+ if (!(callflags & (FF_PROBE|FF_NOFS)))
fileop_onopen_internal(stream, &compp->info, callflags);
-
- return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
+ return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
}
/* check the component against the prefix test info */
@@ -509,6 +514,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
if (len > MAX_COMPNAME)
return -ENAMETOOLONG;
+ /* no filesystem is mounted here */
+ if (walkp->callflags & FF_NOFS)
+ return -ENOENT;
+
/* check for "." and ".." */
if (name[0] == '.')
{
@@ -577,7 +586,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
/* This lets it be passed quietly to directory scanning */
- stream->flags = callflags & FF_MASK;
+ stream->flags |= callflags & FF_MASK;
struct pathwalk walk;
walk.path = path;
@@ -587,81 +596,36 @@ int open_stream_internal(const char *path, unsigned int callflags,
struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
rootp->nextp = NULL;
- rootp->attr = ATTR_SYSTEM_ROOT;
-
-#ifdef HAVE_MULTIVOLUME
- int volume = 0, rootrc = WALK_RC_FOUND;
-#endif /* HAVE_MULTIVOLUME */
while (1)
{
- const char *pathptr = walk.path;
-
- #ifdef HAVE_MULTIVOLUME
- /* this seamlessly integrates secondary filesystems into the
- root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
- const char *p;
- volume = path_strip_volume(pathptr, &p, false);
- if (!CHECK_VOL(volume))
- {
- DEBUGF("No such device or address: %d\n", volume);
- FILE_ERROR(ENXIO, -2);
- }
-
- if (p == pathptr)
- {
- /* the root of this subpath is the system root */
- rootp->attr = ATTR_SYSTEM_ROOT;
- rootrc = WALK_RC_FOUND_ROOT;
- }
- else
- {
- /* this subpath specifies a mount point */
- rootp->attr = ATTR_MOUNT_POINT;
- rootrc = WALK_RC_FOUND;
- }
-
- walk.path = p;
- #endif /* HAVE_MULTIVOLUME */
-
- /* set name to start at last leading separator; names of volume
- specifiers will be returned as "/<fooN>" */
- rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
- rootp->length =
- IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
+ rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
+ if (rc < 0)
+ break;
- rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
+ rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
if (rc < 0)
- {
- /* not mounted */
- DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
- rc = -ENXIO;
break;
- }
- get_rootinfo_internal(&rootp->info);
+ walk.path = rootp->name + rootp->length;
+
rc = walk_path(&walk, rootp, stream);
if (rc != WALK_RC_CONT_AT_ROOT)
break;
}
- switch (rc)
+ if (rc >= 0)
{
- case WALK_RC_FOUND_ROOT:
- IF_MV( rc = rootrc; )
- /* fallthrough */
- case WALK_RC_NOT_FOUND:
- case WALK_RC_FOUND:
/* FF_PROBE leaves nothing for caller to clean up */
- if (callflags & FF_PROBE)
+ if (walk.callflags & FF_PROBE)
filestr_base_destroy(stream);
-
- break;
-
- default: /* utter, abject failure :`( */
+ }
+ else
+ {
+ /* utter, abject failure :`( */
DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
filestr_base_destroy(stream);
- FILE_ERROR(-rc, -3);
+ FILE_ERROR(-rc, -1);
}
file_error:
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index e34a460e10..da82681acc 100644
--- a/firmware/common/fileobj_mgr.c
+++ b/firmware/common/fileobj_mgr.c
@@ -20,12 +20,13 @@
****************************************************************************/
#include "config.h"
#include "system.h"
+#include <errno.h>
#include "debug.h"
#include "file.h"
#include "dir.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/**
* Manages file and directory streams on all volumes
@@ -34,8 +35,8 @@
*/
-/* there will always be enough of these for all user handles, thus these
- functions don't return failure codes */
+/* there will always be enough of these for all user handles, thus most of
+ these functions don't return failure codes */
#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
/* describes the file as an image on the storage medium */
@@ -84,6 +85,14 @@ static struct ll_head busy_bindings[NUM_VOLUMES];
for (struct filestr_base *s = STREAM_##what(start); \
s; s = STREAM_NEXT(s))
+/* once a file/directory, always a file/directory; such a change
+ is a bug */
+#define CHECK_FO_DIRECTORY(callflags, fobp) \
+ if (((callflags) ^ (fobp)->flags) & FO_DIRECTORY) \
+ { \
+ DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", \
+ __func__, (fobp), (callflags)); \
+ }
/* syncs information for the stream's old and new parent directory if any are
currently opened */
@@ -96,6 +105,9 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
continue; /* not directory or removed can't be parent of anything */
struct filestr_base *parentstrp = STREAM_FIRST(fobp);
+ if (!parentstrp)
+ continue;
+
struct fat_file *parentfilep = &parentstrp->infop->fatfile;
for (int i = 0; i < count; i++)
@@ -111,8 +123,8 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
}
/* see if this file has open streams and return that fileobj_binding if so,
- else grab a new one from the free list; returns true if this stream is
- the only open one */
+ else grab a new one from the free list; returns true if this is new */
+
static bool binding_assign(const struct file_base_info *srcinfop,
struct fileobj_binding **fobpp)
{
@@ -123,7 +135,7 @@ static bool binding_assign(const struct file_base_info *srcinfop,
if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
{
- /* already has open streams */
+ /* already has open streams/mounts*/
*fobpp = fobp;
return false;
}
@@ -143,6 +155,22 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp)
ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
}
+static void bind_source_info(const struct file_base_info *srcinfop,
+ struct fileobj_binding **fobpp)
+{
+ if (!binding_assign(srcinfop, fobpp))
+ return; /* already in use */
+ /* is new */
+ (*fobpp)->bind.info = *srcinfop;
+ fileobj_bind_file(&(*fobpp)->bind);
+}
+
+static void release_binding(struct fileobj_binding *fobp)
+{
+ fileobj_unbind_file(&fobp->bind);
+ binding_add_to_free_list(fobp);
+}
+
/** File and directory internal interface **/
void file_binding_insert_last(struct file_base_binding *bindp)
@@ -169,6 +197,33 @@ void file_binding_remove_next(struct file_base_binding *prevp,
}
#endif /* HAVE_DIRCACHE */
+/* mounts a file object as a target from elsewhere */
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp)
+{
+ struct fileobj_binding *fobp;
+ bind_source_info(srcinfop, &fobp);
+ CHECK_FO_DIRECTORY(callflags, fobp);
+ if (fobp->flags & FO_MOUNTTARGET)
+ return false; /* already mounted */
+ fobp->flags |= FDO_BUSY | FO_MOUNTTARGET |
+ (callflags & FO_DIRECTORY);
+ *bindpp = &fobp->bind;
+ return true;
+}
+/* unmounts the file object and frees it if now unusued */
+void fileobj_unmount(struct file_base_binding *bindp)
+{
+ struct fileobj_binding *fobp = (struct fileobj_binding *)bindp;
+ if (!(fobp->flags & FO_MOUNTTARGET))
+ return; /* not mounted */
+ if (STREAM_FIRST(fobp) == NULL)
+ release_binding(fobp); /* no longer in use */
+ else
+ fobp->flags &= ~FO_MOUNTTARGET;
+}
+
/* opens the file object for a new stream and sets up the caches;
* the stream must already be opened at the FS driver level and *stream
* initialized.
@@ -180,10 +235,13 @@ void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags)
{
+ /* assign base file information */
struct fileobj_binding *fobp;
- bool first = binding_assign(srcinfop, &fobp);
+ bind_source_info(srcinfop, &fobp);
+ unsigned int foflags = fobp->flags;
/* add stream to this file's list */
+ bool first = STREAM_FIRST(fobp) == NULL;
ll_insert_last(&fobp->list, &stream->node);
/* initiate the new stream into the enclave */
@@ -197,27 +255,16 @@ void fileobj_fileop_open(struct filestr_base *stream,
if (first)
{
/* first stream for file */
- fobp->bind.info = *srcinfop;
- fobp->flags = FDO_BUSY | FO_SINGLE |
- (callflags & (FO_DIRECTORY|FO_TRUNC));
- fobp->writers = 0;
- fobp->size = 0;
-
- fileobj_bind_file(&fobp->bind);
+ fobp->flags = foflags | FDO_BUSY | FO_SINGLE |
+ (callflags & (FO_DIRECTORY|FO_TRUNC));
+ fobp->writers = 0;
+ fobp->size = 0;
}
else
{
/* additional stream for file */
- fobp->flags &= ~FO_SINGLE;
- fobp->flags |= callflags & FO_TRUNC;
-
- /* once a file/directory, always a file/directory; such a change
- is a bug */
- if ((callflags ^ fobp->flags) & FO_DIRECTORY)
- {
- DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
- __func__, stream, callflags);
- }
+ fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC);
+ CHECK_FO_DIRECTORY(callflags, fobp);
}
if ((callflags & FD_WRITE) && ++fobp->writers == 1)
@@ -257,12 +304,14 @@ void fileobj_fileop_close(struct filestr_base *stream)
if (foflags & FO_SINGLE)
{
/* last stream for file; close everything */
- fileobj_unbind_file(&fobp->bind);
-
if (fobp->writers)
file_cache_free(&fobp->cache);
- binding_add_to_free_list(fobp);
+ /* binding must stay valid if something is mounted to here */
+ if (foflags & FO_MOUNTTARGET)
+ fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET);
+ else
+ release_binding(fobp);
}
else
{
diff --git a/firmware/common/inflate.c b/firmware/common/inflate.c
index 26fd191690..e39fe3a14e 100644
--- a/firmware/common/inflate.c
+++ b/firmware/common/inflate.c
@@ -43,6 +43,7 @@
#include "inflate.h"
#include <stdbool.h>
+#include <string.h>
#include "adler32.h"
#include "crc32.h"
#include "system.h"
@@ -757,3 +758,36 @@ int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate
return inflate_blocks(it, st, read, rctx, write, wctx);
}
+
+static uint32_t inflate_buffer_rw(struct inflate_bufferctx* c,
+ void* dst, const void* src, uint32_t block_size)
+{
+ size_t size_left = c->end - c->buf;
+ size_t copy_size = MIN((size_t)block_size, size_left);
+
+ memcpy(dst, src, copy_size);
+ c->buf += copy_size;
+
+ return copy_size;
+}
+
+uint32_t inflate_buffer_reader(void* block, uint32_t block_size, void* ctx)
+{
+ struct inflate_bufferctx* c = ctx;
+ return inflate_buffer_rw(c, block, c->buf, block_size);
+}
+
+uint32_t inflate_buffer_writer(const void* block, uint32_t block_size, void* ctx)
+{
+ struct inflate_bufferctx* c = ctx;
+ return inflate_buffer_rw(c, c->buf, block, block_size);
+}
+
+uint32_t inflate_getsize_writer(const void* block, uint32_t block_size, void* ctx)
+{
+ (void)block;
+
+ size_t* size = ctx;
+ *size += block_size;
+ return block_size;
+}
diff --git a/firmware/common/multiboot.c b/firmware/common/multiboot.c
new file mode 100644
index 0000000000..dfa6556be3
--- /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;
+ 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
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c
index 2b4e6a8eb0..b942fdf022 100644
--- a/firmware/common/pathfuncs.c
+++ b/firmware/common/pathfuncs.c
@@ -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.
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..ff5ad0f6db
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,341 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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"
+
+/* 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);
+}
+
+/* mount the directory that enumerates into the root namespace */
+int root_mount_path(const char *path, unsigned int flags)
+{
+#ifdef HAVE_MULTIVOLUME
+ int volume = path_strip_volume(path, NULL, false);
+ if (volume == ROOT_VOLUME)
+ return -EINVAL;
+
+ if (!CHECK_VOL(volume))
+ return -ENOENT;
+#else
+ if (!path_is_absolute(path))
+ {
+ logf("Path not absolute %s", path);
+ return -ENOENT;
+ }
+#endif /* HAVE_MULTIVOLUME */
+
+ bool contents = flags & NSITEM_CONTENTS;
+ int item = contents ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+
+ logf("%s: item:%d, st:%u, %s", __func__, item, state, path);
+
+ if (state)
+ return -EBUSY;
+
+ if (contents)
+ {
+ /* 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;
+ }
+
+ state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
+ set_root_item_state(item, state);
+
+ return 0;
+}
+
+/* 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);
+}
+
+/* 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)
+ {
+ logf("Found mounted item: %d %s", item, entry->d_name);
+ break;
+ }
+
+ 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/zip.c b/firmware/common/zip.c
index 9512d6c239..36b90a9223 100644
--- a/firmware/common/zip.c
+++ b/firmware/common/zip.c
@@ -32,9 +32,7 @@
#include "crc32.h"
#include "rbendian.h"
-#define zip_core_alloc(N) core_alloc_ex("zip",(N),&dummy_ops)
-
-static struct buflib_callbacks dummy_ops;
+#define zip_core_alloc(N) core_alloc_ex("zip",(N),&buflib_ops_locked)
enum {
ZIP_SIG_ED = 0x06054b50,
diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c
index 8206bcf7e0..8d2b9f44f3 100644
--- a/firmware/drivers/audio/ak4376.c
+++ b/firmware/drivers/audio/ak4376.c
@@ -242,11 +242,29 @@ void ak4376_set_freqmode(int fsel, int mult, int power_mode)
/* Handle the DSMLP bit in the MODE_CTRL register */
int mode_ctrl = 0x00;
- if(power_mode || hw_freq_sampr[fsel] <= SAMPR_12)
+ if(power_mode == SOUND_LOW_POWER || hw_freq_sampr[fsel] <= SAMPR_12)
mode_ctrl |= 0x40;
+ /* Handle the LPMODE bit */
+ int pwr3 = power_mode == SOUND_LOW_POWER ? 0x11 : 0x01;
+
+ /* The datasheet says the HP amp must be powered down before changing
+ * the operating mode of the DAC or HP amp. I'm assuming this means
+ * the amp must be shut down when changing DSMLP or LPMODE. */
+ int cur_mode_ctrl = ak4376_read(AK4376_REG_MODE_CTRL);
+ int cur_pwr3 = ak4376_read(AK4376_REG_PWR3);
+ bool disable_amp = mode_ctrl != cur_mode_ctrl || pwr3 != cur_pwr3;
+
/* Program the new settings */
+ if(disable_amp)
+ ak4376_write(AK4376_REG_PWR4, 0x00);
+
ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
- ak4376_write(AK4376_REG_PWR3, power_mode ? 0x11 : 0x01);
+ ak4376_write(AK4376_REG_PWR3, pwr3);
+
+ if(disable_amp) {
+ ak4376_write(AK4376_REG_PWR4, 0x03);
+ mdelay(26);
+ }
}
diff --git a/firmware/drivers/audio/x1000-codec.c b/firmware/drivers/audio/x1000-codec.c
new file mode 100644
index 0000000000..c083882dab
--- /dev/null
+++ b/firmware/drivers/audio/x1000-codec.c
@@ -0,0 +1,286 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021-2022 Aidan MacDonald
+ * Copyright 2014 Ingenic Semiconductor Co.,Ltd
+ * cscheng <shicheng.cheng@ingenic.com>
+ * sound/soc/ingenic/icodec/icdc_d3.c
+ * ALSA SoC Audio driver -- ingenic internal codec (icdc_d3) driver
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "x1000-codec.h"
+#include "audiohw.h"
+#include "pcm_sampr.h"
+#include "kernel.h"
+#include "x1000/aic.h"
+
+static const uint8_t fsel_to_hw[HW_NUM_FREQ] = {
+ [0 ... HW_NUM_FREQ-1] = 0,
+ HW_HAVE_8_([HW_FREQ_8] = 0,)
+ HW_HAVE_11_([HW_FREQ_11] = 1,)
+ HW_HAVE_12_([HW_FREQ_12] = 2,)
+ HW_HAVE_16_([HW_FREQ_16] = 3,)
+ HW_HAVE_22_([HW_FREQ_22] = 4,)
+ HW_HAVE_24_([HW_FREQ_24] = 5,)
+ HW_HAVE_32_([HW_FREQ_32] = 6,)
+ HW_HAVE_44_([HW_FREQ_44] = 7,)
+ HW_HAVE_48_([HW_FREQ_48] = 8,)
+ HW_HAVE_88_([HW_FREQ_88] = 9,)
+ HW_HAVE_96_([HW_FREQ_96] = 10,)
+ HW_HAVE_176_([HW_FREQ_176] = 11,)
+ HW_HAVE_192_([HW_FREQ_192] = 12,)
+};
+
+void x1000_icodec_open(void)
+{
+ /* Ingenic does not specify any timing constraints for reset,
+ * let's do a 1ms delay for fun */
+ jz_writef(AIC_RGADW, ICRST(1));
+ mdelay(1);
+ jz_writef(AIC_RGADW, ICRST(0));
+
+ /* Power-up and initial config sequence */
+ static const uint8_t init_config[] = {
+ JZCODEC_CR_VIC, 0x03, /* ensure codec is powered off */
+ JZCODEC_CR_CK, 0x40, /* MCLK_DIV=1, SHUTDOWN_CLK=0, CRYSTAL=12Mhz */
+ JZCODEC_AICR_DAC, 0x13, /* ADWL=0 (16bit word length)
+ * SLAVE=0 (i2s master mode)
+ * SB_DAC=1 (power down DAC)
+ * AUDIOIF=3 (i2s mode) */
+ JZCODEC_AICR_ADC, 0x13, /* ADWL=0 (16bit word length)
+ * SB_ADC=1 (power down ADC)
+ * AUDIOIF=3 (i2s mode)
+ */
+ JZCODEC_CR_DAC, 0x91, /* DAC mute, power down */
+ JZCODEC_CR_DAC2, 0x38, /* DAC power down */
+ JZCODEC_CR_DMIC, 0x00, /* DMIC clock off */
+ JZCODEC_CR_MIC1, 0x30, /* MIC1 power down */
+ JZCODEC_CR_MIC2, 0x30, /* MIC2 power down */
+ JZCODEC_CR_ADC, 0x90, /* ADC mute, power down */
+ JZCODEC_ICR, 0x00, /* INT_FORM=0 (high level IRQ) */
+ JZCODEC_IMR, 0xff, /* Mask all interrupts */
+ JZCODEC_IMR2, 0xff,
+ JZCODEC_IFR, 0xff, /* Clear all interrupt flags */
+ JZCODEC_IFR2, 0xff,
+ };
+
+ for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
+ x1000_icodec_write(init_config[i], init_config[i+1]);
+
+ /* SB -> 0 (power up) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x02);
+ mdelay(250);
+
+ /* Initial gain setting. Apparently we need to set one gain and
+ * then set another after 10ms; afterward it can be changed freely. */
+ static const uint8_t gain_regs[] = {
+ JZCODEC_GCR_DACL,
+ JZCODEC_GCR_DACR,
+ JZCODEC_GCR_DACL2,
+ JZCODEC_GCR_DACR2,
+ JZCODEC_GCR_MIC1,
+ JZCODEC_GCR_MIC2,
+ JZCODEC_GCR_ADCL,
+ JZCODEC_GCR_ADCR,
+ };
+
+ for(size_t i = 0; i < ARRAYLEN(gain_regs); ++i)
+ x1000_icodec_write(gain_regs[i], 0);
+
+ mdelay(10);
+
+ for(size_t i = 0; i < ARRAYLEN(gain_regs); ++i)
+ x1000_icodec_write(gain_regs[i], 1);
+
+ /* SB_SLEEP -> 0 (exit sleep/standby mode) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x00);
+ mdelay(200);
+}
+
+void x1000_icodec_close(void)
+{
+ /* SB_SLEEP -> 1 (enable sleep mode) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x02);
+
+ /* SB -> 1 (power down) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x03);
+}
+
+/*
+ * DAC configuration
+ */
+
+void x1000_icodec_dac_frequency(int fsel)
+{
+ x1000_icodec_update(JZCODEC_FCR_DAC, 0x0f, fsel_to_hw[fsel]);
+}
+
+/*
+ * ADC configuration
+ */
+
+void x1000_icodec_adc_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_AICR_ADC, 0x10, en ? 0x00 : 0x10);
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x10, en ? 0x00 : 0x10);
+}
+
+void x1000_icodec_adc_mute(bool muted)
+{
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x80, muted ? 0x80 : 0x00);
+}
+
+void x1000_icodec_adc_mic_sel(int sel)
+{
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x40,
+ sel == JZCODEC_MIC_SEL_DIGITAL ? 0x40 : 0x00);
+}
+
+void x1000_icodec_adc_frequency(int fsel)
+{
+ x1000_icodec_update(JZCODEC_FCR_ADC, 0x0f, fsel_to_hw[fsel]);
+}
+
+void x1000_icodec_adc_highpass_filter(bool en)
+{
+ x1000_icodec_update(JZCODEC_FCR_ADC, 0x40, en ? 0x40 : 0x00);
+}
+
+void x1000_icodec_adc_gain(int gain_dB)
+{
+ if(gain_dB < X1000_ICODEC_ADC_GAIN_MIN)
+ gain_dB = X1000_ICODEC_ADC_GAIN_MIN;
+ else if(gain_dB > X1000_ICODEC_ADC_GAIN_MAX)
+ gain_dB = X1000_ICODEC_ADC_GAIN_MAX;
+
+ /* bit 7 = use the same gain for both channels */
+ x1000_icodec_write(JZCODEC_GCR_ADCL, 0x80 | gain_dB);
+}
+
+/*
+ * MIC1 configuration
+ */
+
+void x1000_icodec_mic1_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, 0x10, en ? 0x00 : 0x10);
+}
+
+void x1000_icodec_mic1_bias_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, 0x20, en ? 0x00 : 0x20);
+}
+
+void x1000_icodec_mic1_configure(int settings)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, JZCODEC_MIC1_CONFIGURE_MASK,
+ settings & JZCODEC_MIC1_CONFIGURE_MASK);
+}
+
+void x1000_icodec_mic1_gain(int gain_dB)
+{
+ if(gain_dB < X1000_ICODEC_MIC_GAIN_MIN)
+ gain_dB = X1000_ICODEC_MIC_GAIN_MIN;
+ else if(gain_dB > X1000_ICODEC_MIC_GAIN_MAX)
+ gain_dB = X1000_ICODEC_MIC_GAIN_MAX;
+
+ x1000_icodec_write(JZCODEC_GCR_MIC1, gain_dB/X1000_ICODEC_MIC_GAIN_STEP);
+}
+
+/*
+ * Mixer configuration
+ */
+
+void x1000_icodec_mixer_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIX, 0x80, en ? 0x80 : 0x00);
+}
+
+/*
+ * Register access
+ */
+
+static int x1000_icodec_read_direct(int reg)
+{
+ jz_writef(AIC_RGADW, ADDR(reg));
+ return jz_readf(AIC_RGDATA, DATA);
+}
+
+static void x1000_icodec_write_direct(int reg, int value)
+{
+ jz_writef(AIC_RGADW, ADDR(reg), DATA(value));
+ jz_writef(AIC_RGADW, RGWR(1));
+ while(jz_readf(AIC_RGADW, RGWR));
+}
+
+static void x1000_icodec_update_direct(int reg, int mask, int value)
+{
+ int x = x1000_icodec_read_direct(reg) & ~mask;
+ x |= value;
+ x1000_icodec_write_direct(reg, x);
+}
+
+static int x1000_icodec_read_indirect(int c_reg, int index)
+{
+ x1000_icodec_update_direct(c_reg, 0x7f, index & 0x3f);
+ return x1000_icodec_read_direct(c_reg+1);
+}
+
+static void x1000_icodec_write_indirect(int c_reg, int index, int value)
+{
+ /* NB: The X1000 programming manual says we should write the data
+ * register first, but in fact the control register needs to be
+ * written first (following Ingenic's Linux driver). */
+ x1000_icodec_update_direct(c_reg, 0x7f, 0x40 | (index & 0x3f));
+ x1000_icodec_write_direct(c_reg+1, value);
+}
+
+static void x1000_icodec_update_indirect(int c_reg, int index, int mask, int value)
+{
+ int x = x1000_icodec_read_indirect(c_reg, index) & ~mask;
+ x |= value;
+ x1000_icodec_write_indirect(c_reg, index, x);
+}
+
+int x1000_icodec_read(int reg)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_read_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg));
+ else
+ return x1000_icodec_read_direct(reg);
+}
+
+void x1000_icodec_write(int reg, int value)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_write_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg), value);
+ else
+ return x1000_icodec_write_direct(reg, value);
+}
+
+void x1000_icodec_update(int reg, int mask, int value)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_update_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg),
+ mask, value);
+ else
+ return x1000_icodec_update_direct(reg, mask, value);
+}
diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c
index fd1126dbbf..d59fbb2e3f 100644
--- a/firmware/drivers/axp-pmu.c
+++ b/firmware/drivers/axp-pmu.c
@@ -36,6 +36,8 @@ struct axp_adc_info {
uint8_t reg;
uint8_t en_reg;
uint8_t en_bit;
+ int8_t num;
+ int8_t den;
};
struct axp_supply_info {
@@ -49,17 +51,16 @@ struct axp_supply_info {
};
static const struct axp_adc_info axp_adc_info[NUM_ADC_CHANNELS] = {
- {0x56, AXP_REG_ADCENABLE1, 5}, /* ACIN_VOLTAGE */
- {0x58, AXP_REG_ADCENABLE1, 4}, /* ACIN_CURRENT */
- {0x5a, AXP_REG_ADCENABLE1, 3}, /* VBUS_VOLTAGE */
- {0x5c, AXP_REG_ADCENABLE1, 2}, /* VBUS_CURRENT */
- {0x5e, AXP_REG_ADCENABLE2, 7}, /* INTERNAL_TEMP */
- {0x62, AXP_REG_ADCENABLE1, 1}, /* TS_INPUT */
- {0x78, AXP_REG_ADCENABLE1, 7}, /* BATTERY_VOLTAGE */
- {0x7a, AXP_REG_ADCENABLE1, 6}, /* CHARGE_CURRENT */
- {0x7c, AXP_REG_ADCENABLE1, 6}, /* DISCHARGE_CURRENT */
- {0x7e, AXP_REG_ADCENABLE1, 1}, /* APS_VOLTAGE */
- {0x70, 0xff, 0}, /* BATTERY_POWER */
+ [ADC_ACIN_VOLTAGE] = {0x56, AXP_REG_ADCENABLE1, 1 << 5, 17, 10},
+ [ADC_ACIN_CURRENT] = {0x58, AXP_REG_ADCENABLE1, 1 << 4, 5, 8},
+ [ADC_VBUS_VOLTAGE] = {0x5a, AXP_REG_ADCENABLE1, 1 << 3, 17, 10},
+ [ADC_VBUS_CURRENT] = {0x5c, AXP_REG_ADCENABLE1, 1 << 2, 3, 8},
+ [ADC_INTERNAL_TEMP] = {0x5e, AXP_REG_ADCENABLE2, 1 << 7, 0, 0},
+ [ADC_TS_INPUT] = {0x62, AXP_REG_ADCENABLE1, 1 << 1, 4, 5},
+ [ADC_BATTERY_VOLTAGE] = {0x78, AXP_REG_ADCENABLE1, 1 << 7, 11, 10},
+ [ADC_CHARGE_CURRENT] = {0x7a, AXP_REG_ADCENABLE1, 1 << 6, 1, 2},
+ [ADC_DISCHARGE_CURRENT] = {0x7c, AXP_REG_ADCENABLE1, 1 << 6, 1, 2},
+ [ADC_APS_VOLTAGE] = {0x7e, AXP_REG_ADCENABLE1, 1 << 1, 7, 5},
};
static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = {
@@ -126,57 +127,8 @@ static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = {
#endif
};
-static struct axp_driver {
- int adc_enable;
- int chargecurrent_setting;
- int chip_id;
-} axp;
-
-static void axp_init_enabled_adcs(void)
-{
- axp.adc_enable = 0;
-
- /* Read chip ID, so we can display it on the debug screen.
- * This is undocumented but there's Linux driver code floating around
- * which suggests this should work for many AXP chips. */
- axp.chip_id = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHIP_ID);
-
- /* Read enabled ADCs from the hardware */
- uint8_t regs[2];
- int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR,
- AXP_REG_ADCENABLE1, 2, &regs[0]);
- if(rc != I2C_STATUS_OK)
- return;
-
- /* Parse registers to set ADC enable bits */
- const struct axp_adc_info* info = axp_adc_info;
- for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
- if(info[i].en_reg == 0xff)
- continue;
-
- if(regs[info[i].en_reg - AXP_REG_ADCENABLE1] & info[i].en_bit)
- axp.adc_enable |= 1 << i;
- }
-
- /* Handle battery power ADC */
- if((axp.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
- (axp.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
- axp.adc_enable |= (1 << ADC_BATTERY_POWER);
- }
-}
-
void axp_init(void)
{
- axp_init_enabled_adcs();
-
- /* We need discharge current ADC to reliably poll for a full battery */
- int bits = axp.adc_enable;
- bits |= (1 << ADC_DISCHARGE_CURRENT);
- axp_adc_set_enabled(bits);
-
- /* Read the maximum charging current */
- int value = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGECONTROL1);
- axp.chargecurrent_setting = (value < 0) ? -1 : (value & 0xf);
}
void axp_supply_set_voltage(int supply, int voltage)
@@ -305,22 +257,15 @@ int axp_adc_read(int adc)
int axp_adc_read_raw(int adc)
{
- /* Don't give a reading if the ADC is not enabled */
- if((axp.adc_enable & (1 << adc)) == 0)
- return INT_MIN;
-
/* Read the ADC */
- uint8_t buf[3];
- int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
+ uint8_t buf[2];
uint8_t reg = axp_adc_info[adc].reg;
- int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, count, &buf[0]);
+ int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, 2, &buf[0]);
if(rc != I2C_STATUS_OK)
return INT_MIN;
/* Parse the value */
- if(adc == ADC_BATTERY_POWER)
- return (buf[0] << 16) | (buf[1] << 8) | buf[2];
- else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
+ if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
return (buf[0] << 5) | (buf[1] & 0x1f);
else
return (buf[0] << 4) | (buf[1] & 0xf);
@@ -328,79 +273,33 @@ int axp_adc_read_raw(int adc)
int axp_adc_conv_raw(int adc, int value)
{
- switch(adc) {
- case ADC_ACIN_VOLTAGE:
- case ADC_VBUS_VOLTAGE:
- /* 0 mV ... 6.9615 mV, step 1.7 mV */
- return value * 17 / 10;
- case ADC_ACIN_CURRENT:
- /* 0 mA ... 2.5594 A, step 0.625 mA */
- return value * 5 / 8;
- case ADC_VBUS_CURRENT:
- /* 0 mA ... 1.5356 A, step 0.375 mA */
- return value * 3 / 8;
- case ADC_INTERNAL_TEMP:
- /* -144.7 C ... 264.8 C, step 0.1 C */
+ if(adc == ADC_INTERNAL_TEMP)
return value - 1447;
- case ADC_TS_INPUT:
- /* 0 mV ... 3.276 V, step 0.8 mV */
- return value * 4 / 5;
- case ADC_BATTERY_VOLTAGE:
- /* 0 mV ... 4.5045 V, step 1.1 mV */
- return value * 11 / 10;
- case ADC_CHARGE_CURRENT:
- case ADC_DISCHARGE_CURRENT:
- /* 0 mA to 4.095 A, step 0.5 mA */
- return value / 2;
- case ADC_APS_VOLTAGE:
- /* 0 mV to 5.733 V, step 1.4 mV */
- return value * 7 / 5;
- case ADC_BATTERY_POWER:
- /* 0 uW to 23.6404 W, step 0.55 uW */
- return value * 11 / 20;
- default:
- /* Shouldn't happen */
- return INT_MIN;
- }
-}
-
-int axp_adc_get_enabled(void)
-{
- return axp.adc_enable;
+ else
+ return axp_adc_info[adc].num * value / axp_adc_info[adc].den;
}
void axp_adc_set_enabled(int adc_bits)
{
- /* Ignore no-op */
- if(adc_bits == axp.adc_enable)
- return;
+ uint8_t xfer[3];
+ xfer[0] = 0;
+ xfer[1] = AXP_REG_ADCENABLE2;
+ xfer[2] = 0;
/* Compute the new register values */
const struct axp_adc_info* info = axp_adc_info;
- uint8_t regs[2] = {0, 0};
for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
- if(info[i].en_reg == 0xff)
+ if(!(adc_bits & (1 << i)))
continue;
- if(adc_bits & (1 << i))
- regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
- }
-
- /* These ADCs share an enable bit */
- if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
- adc_bits |= (1 << ADC_CHARGE_CURRENT);
- adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
- }
-
- /* Enable required bits for battery power ADC */
- if(adc_bits & (1 << ADC_BATTERY_POWER)) {
- regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
- regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
+ if(info[i].en_reg == AXP_REG_ADCENABLE1)
+ xfer[0] |= info[i].en_bit;
+ else
+ xfer[2] |= info[i].en_bit;
}
/* Update the configuration */
- i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 2, &regs[0]);
- axp.adc_enable = adc_bits;
+ i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 3, &xfer[0]);
}
int axp_adc_get_rate(void)
@@ -468,38 +367,26 @@ static const int chargecurrent_tbl[] = {
1080, 1160, 1240, 1320,
};
-static const int chargecurrent_tblsz = sizeof(chargecurrent_tbl)/sizeof(int);
-
-void axp_set_charge_current(int maxcurrent)
+void axp_set_charge_current(int current_mA)
{
- /* Find the charge current just higher than maxcurrent */
- int value = 0;
- while(value < chargecurrent_tblsz &&
- chargecurrent_tbl[value] <= maxcurrent)
- ++value;
-
- /* Select the next lower current, the greatest current <= maxcurrent */
- if(value >= chargecurrent_tblsz)
- value = chargecurrent_tblsz - 1;
- else if(value > 0)
- --value;
-
- /* Don't issue i2c write if desired setting is already in use */
- if(value == axp.chargecurrent_setting)
- return;
+ /* find greatest charging current not exceeding requested current */
+ unsigned int index = 0;
+ while(index < ARRAYLEN(chargecurrent_tbl)-1 &&
+ chargecurrent_tbl[index+1] <= current_mA)
+ ++index;
- /* Update register */
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
- AXP_REG_CHARGECONTROL1, 0x0f, value, NULL);
- axp.chargecurrent_setting = value;
+ AXP_REG_CHARGECONTROL1, 0x0f, index, NULL);
}
int axp_get_charge_current(void)
{
- if(axp.chargecurrent_setting < 0)
- return chargecurrent_tbl[0];
- else
- return chargecurrent_tbl[axp.chargecurrent_setting];
+ int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_CHARGECONTROL1);
+ if(ret < 0)
+ ret = 0;
+
+ return chargecurrent_tbl[ret & 0x0f];
}
void axp_power_off(void)
@@ -511,7 +398,6 @@ void axp_power_off(void)
#ifndef BOOTLOADER
enum {
- AXP_DEBUG_CHIP_ID,
AXP_DEBUG_BATTERY_STATUS,
AXP_DEBUG_INPUT_STATUS,
AXP_DEBUG_CHARGE_CURRENT,
@@ -585,12 +471,6 @@ static const char* axp_debug_menu_get_name(int item, void* data,
}
switch(item) {
- case AXP_DEBUG_CHIP_ID: {
- snprintf(buf, buflen, "Chip ID: %d (%02x) [Driver: AXP%d]",
- axp.chip_id, axp.chip_id, HAVE_AXP_PMU);
- return buf;
- } break;
-
case AXP_DEBUG_BATTERY_STATUS: {
switch(axp_battery_status()) {
case AXP_BATT_FULL:
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index 0c02c8224f..30d16f3666 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -1698,8 +1698,8 @@ static int add_dir_entry(struct bpb *fat_bpb, struct fat_filestr *parentstr,
int rc;
unsigned char basisname[11], shortname[11];
- int n;
int entries_needed;
+ int n = -1;
unsigned long ucslen = 0;
if (is_dotdir_name(name) && (attr & ATTR_DIRECTORY))
diff --git a/firmware/drivers/lcd-16bit-common.c b/firmware/drivers/lcd-16bit-common.c
index b9c9060216..1b84847929 100644
--- a/firmware/drivers/lcd-16bit-common.c
+++ b/firmware/drivers/lcd-16bit-common.c
@@ -283,12 +283,6 @@ 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) ||
@@ -312,22 +306,10 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
if (y + height > lcd_current_viewport->height)
height = lcd_current_viewport->height - y;
- /* adjust for viewport */
+ /* convert to viewport coordinates */
x += lcd_current_viewport->x;
y += lcd_current_viewport->y;
- /* 'Bugfix' mono_bitmap_part reads ahead in the buffer, While this is a bug
- * if the height is <= char bit pixels other memory gets read but is not used
- * the other option is to check in the hot code path but this appears
- * sufficient, limit to the sim to stop Address Sanitizer errors
- */
-#if defined(SIMULATOR) && \
- (!defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE)
- /* vertical stride targets don't seem affected by this */
- if (height <= CHAR_BIT)
- stride = 0;
-#endif
-
#if defined(HAVE_VIEWPORT_CLIP)
/********************* Viewport on screen clipping ********************/
/* nothing to draw? */
@@ -342,7 +324,6 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
src_x -= x;
x = 0;
}
-
if (y < 0)
{
height += y;
@@ -354,14 +335,17 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_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);
+
+ /* move starting point */
+ src += stride * (src_y >> 3) + src_x;
+ src_y &= 7;
+
+ unsigned dmask = 0;
+ int drmode = lcd_current_viewport->drawmode;
if (drmode & DRMODE_INVERSEVID)
{
- dmask = 0x1ff; /* bit 8 == sentinel */
+ dmask = 0xff;
drmode &= DRMODE_SOLID; /* mask out inversevid */
}
@@ -369,107 +353,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
- {
- if (data & 0x01)
- *dst = ~(*dst);
+ switch (drmode) {
+ case DRMODE_COMPLEMENT:
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(data & 0x01)
+ *dst_col = ~(*dst_col);
- 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;
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(!(data & 0x01))
+ *dst_col = bg;
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
- case DRMODE_FG:
+ case DRMODE_FG:
fg = lcd_current_viewport->fg_pattern;
- do
- {
- if (data & 0x01)
- *dst = fg;
+ 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:
+ case DRMODE_SOLID|DRMODE_INT_BD:
fg = lcd_current_viewport->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:
+ 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);
+ 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)
{
diff --git a/firmware/drivers/lcd-1bit-vert.c b/firmware/drivers/lcd-1bit-vert.c
index c86ced9b6c..dcf5e49504 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,
diff --git a/firmware/drivers/lcd-2bit-horz.c b/firmware/drivers/lcd-2bit-horz.c
index b2114ba830..85918a735c 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,
diff --git a/firmware/drivers/lcd-2bit-vert.c b/firmware/drivers/lcd-2bit-vert.c
index 6a476495d0..a059e3b512 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,
diff --git a/firmware/drivers/lcd-2bit-vi.c b/firmware/drivers/lcd-2bit-vi.c
index c09bbce2e0..423f4536d4 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,
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index 9a5f865f2a..c85d16cb70 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -84,6 +84,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,8 +133,9 @@ 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))
{
@@ -176,19 +178,39 @@ struct viewport *LCDFN(get_viewport)(bool *is_default)
void LCDFN(update_viewport)(void)
{
+
struct viewport* vp = LCDFN(current_viewport);
- if (vp->buffer->stride != LCDFN(framebuffer_default.stride))
+ if ((vp->flags & VP_FLAG_OWNER_UPDATE) == VP_FLAG_OWNER_UPDATE)
{
- LCDFN(update_viewport_rect)(0,0, vp->width, vp->height);
+#ifdef LOGF_ENABLE
+ logf("%s ignored - owner update", __func__);
+#endif
return;
}
- LCDFN(update_rect)(vp->x, vp->y, vp->width, vp->height);
+ int x, y;
+ if (vp->buffer->stride != LCDFN(framebuffer_default.stride))
+ {
+ x = 0;
+ y = 0;
+ }
+ else
+ {
+ x = vp->x;
+ y = vp->y;
+ }
+ LCDFN(update_rect)(x, y, 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))
{
diff --git a/firmware/drivers/lcd-color-common.c b/firmware/drivers/lcd-color-common.c
index 935f4e59dd..a867583d36 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,
@@ -214,6 +215,7 @@ void lcd_drawline(int x1, int y1, int x2, int y2)
int d, dinc1, dinc2;
int x, xinc1, xinc2;
int y, yinc1, yinc2;
+ int x_vp, y_vp, w_vp, h_vp;
lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
deltay = abs(y2 - y1);
@@ -268,16 +270,23 @@ void lcd_drawline(int x1, int y1, int x2, int y2)
x = x1;
y = y1;
+ void *(*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
+ x_vp = lcd_current_viewport->x;
+ y_vp = lcd_current_viewport->y;
+ w_vp = lcd_current_viewport->width;
+ h_vp = lcd_current_viewport->height;
+
for (i = 0; i < numpixels; i++)
{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
+ if ((x >= 0 && y >= 0)
+ && (x < w_vp)
+ && (y < h_vp)
#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
+ && (x < LCD_WIDTH)
+ && (y < LCD_HEIGHT)
#endif
)
- pfunc(FBADDR(x + lcd_current_viewport->x, y + lcd_current_viewport->y));
+ pfunc(fbaddr( x + x_vp, y + y_vp));
if (d < 0)
{
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/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index 57006b4e3c..7b54526f61 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -553,32 +553,6 @@ void si4700_dbg_info(struct si4700_dbg_info *nfo)
}
#ifdef HAVE_RDS_CAP
-
-#if (CONFIG_RDS & RDS_CFG_ISR)
-static unsigned char isr_regbuf[(RDSD - STATUSRSSI + 1) * 2];
-
-/* Called by RDS interrupt on target */
-void si4700_rds_interrupt(void)
-{
- si4700_rds_read_raw_async(isr_regbuf, sizeof (isr_regbuf));
-}
-
-/* Handle RDS event from ISR */
-void si4700_rds_process(void)
-{
- uint16_t rds_data[4];
- int index = (RDSA - STATUSRSSI) * 2;
-
- for (int i = 0; i < 4; i++) {
- rds_data[i] = isr_regbuf[index] << 8 | isr_regbuf[index + 1];
- index += 2;
- }
-
- rds_process(rds_data);
-}
-
-#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
-
/* Handle RDS event from thread */
void si4700_rds_process(void)
{
@@ -586,7 +560,7 @@ void si4700_rds_process(void)
if (tuner_powered())
{
- si4700_read_reg(RDSD);
+ si4700_read(6);
#if (CONFIG_RDS & RDS_CFG_POLL)
/* we need to keep track of the ready bit because it stays set for 80ms
* and we must avoid processing it twice */
@@ -603,7 +577,6 @@ void si4700_rds_process(void)
mutex_unlock(&fmr_mutex);
}
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
#if (CONFIG_RDS & RDS_CFG_POLL)
static struct event_queue rds_queue;
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/audiohw.h b/firmware/export/audiohw.h
index 3f1bcb6feb..3f8b48d750 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)
@@ -237,6 +239,11 @@ struct sound_settings_info
#include "erosqlinux_codec.h"
#endif
+#if defined(HAVE_X1000_ICODEC_REC) && !defined(HAVE_X1000_ICODEC_PLAY)
+/* Targets may have an external DAC above, but use icodec for recording only */
+#include "x1000-codec.h"
+#endif
+
/* convert caps into defines */
#ifdef AUDIOHW_CAPS
/* Tone controls */
@@ -598,6 +605,12 @@ void audiohw_set_filter_roll_off(int val);
#endif
#ifdef AUDIOHW_HAVE_POWER_MODE
+enum audiohw_power_mode
+{
+ SOUND_HIGH_POWER = 0,
+ SOUND_LOW_POWER,
+};
+
/**
* Set DAC's power saving mode.
* @param enable 0 - highest performance, 1 - battery saving
diff --git a/firmware/export/axp-pmu.h b/firmware/export/axp-pmu.h
index 457f746e8c..24c992dea3 100644
--- a/firmware/export/axp-pmu.h
+++ b/firmware/export/axp-pmu.h
@@ -37,8 +37,7 @@
#define ADC_CHARGE_CURRENT 7
#define ADC_DISCHARGE_CURRENT 8
#define ADC_APS_VOLTAGE 9
-#define ADC_BATTERY_POWER 10
-#define NUM_ADC_CHANNELS 11
+#define NUM_ADC_CHANNELS 10
/* ADC sampling rates */
#define AXP_ADC_RATE_25HZ 0
@@ -123,7 +122,6 @@ extern int axp_input_status(void);
extern int axp_adc_read(int adc);
extern int axp_adc_read_raw(int adc);
extern int axp_adc_conv_raw(int adc, int value);
-extern int axp_adc_get_enabled(void);
extern void axp_adc_set_enabled(int adc_bits);
extern int axp_adc_get_rate(void);
extern void axp_adc_set_rate(int rate);
@@ -139,7 +137,7 @@ extern void axp_cc_enable(bool en);
extern bool axp_cc_is_enabled(void);
/* Set/get maximum charging current in milliamps */
-extern void axp_set_charge_current(int maxcurrent);
+extern void axp_set_charge_current(int current_mA);
extern int axp_get_charge_current(void);
/* Set the shutdown bit */
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 52f647f2fa..6465bdcb0e 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -208,6 +208,8 @@
Usually application ports, and only
if the estimation is better that ours
(which it probably is) */
+#define CURRENT_MEASURE 8 /* Target can report battery charge and/or
+ * discharge current */
/* CONFIG_LCD */
#define LCD_SSD1815 1 /* as used by Sansa M200 and others */
#define LCD_S1D15E06 3 /* as used by iRiver H100 series */
@@ -611,6 +613,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,13 +714,21 @@ 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 */
@@ -856,6 +869,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
@@ -1181,7 +1200,6 @@ Lyre prototype 1 */
(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)
@@ -1269,6 +1287,10 @@ Lyre prototype 1 */
#endif /* SIMULATOR */
#endif /* default SDL SW volume conditions */
+#if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_SCREENDUMP)
+# define HAVE_SCREENDUMP
+#endif
+
/* null audiohw setting macro for when codec header is included for reasons
other than audio support */
#define AUDIOHW_SETTING(name, us, nd, st, minv, maxv, defv, expr...)
diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h
index f68830e211..c71f7ade49 100644
--- a/firmware/export/config/erosqnative.h
+++ b/firmware/export/config/erosqnative.h
@@ -78,12 +78,13 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
/* Power management */
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE/*|CURRENT_MEASURE*/)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
@@ -103,6 +104,10 @@
#define CURRENT_BACKLIGHT 180
#define CURRENT_MAX_CHG 500 // bursts higher if needed
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.aigo_erosqn"
+
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
@@ -122,6 +127,11 @@
#define USB_WRITE_BUFFER_SIZE (128 * 1024)
#endif
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_PREV|BUTTON_NEXT))
+#endif
+
/* Rockbox capabilities */
#define HAVE_VOLUME_IN_LIST
#define HAVE_FAT16SUPPORT
@@ -131,3 +141,4 @@
#define HAVE_TAGCACHE
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h
index 61b6123a67..ab3e274243 100644
--- a/firmware/export/config/fiiom3k.h
+++ b/firmware/export/config/fiiom3k.h
@@ -55,11 +55,16 @@
/* Codec / audio hardware defines */
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define REC_SAMPR_CAPS (SAMPR_CAP_ALL_96 & ~SAMPR_CAP_64)
+#define INPUT_SRC_CAPS SRC_CAP_MIC
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP|MIC_GAIN_CAP)
+#define HAVE_RECORDING
+#define HAVE_RECORDING_WITHOUT_MONITORING
#define HAVE_AK4376
+#define HAVE_X1000_ICODEC_REC
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
-
-/* TODO: Need to implement recording */
+#define DEFAULT_REC_MIC_GAIN 12
/* Button defines */
#define CONFIG_KEYPAD FIIO_M3K_PAD
@@ -79,6 +84,7 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
@@ -88,7 +94,7 @@
/* TODO: implement HAVE_RTC_ALARM */
/* Power management */
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
@@ -104,6 +110,10 @@
#define BATTERY_CAPACITY_INC 0
#define BATTERY_TYPES_COUNT 1
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.fiio_m3k"
+
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
@@ -123,6 +133,11 @@
#define USB_WRITE_BUFFER_SIZE (128 * 1024)
#endif
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_VOL_UP|BUTTON_VOL_DOWN))
+#endif
+
/* Rockbox capabilities */
#define HAVE_FAT16SUPPORT
#define HAVE_ALBUMART
@@ -134,3 +149,4 @@
#define HAVE_HOTKEY
#define HAVE_LOCKED_ACTIONS
#define AB_REPEAT_ENABLE
+#define HAVE_BOOTLOADER_SCREENDUMP
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/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..2e4afbdf63 100644
--- a/firmware/export/config/ipod6g.h
+++ b/firmware/export/config/ipod6g.h
@@ -158,6 +158,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..ffc8a6bfb8 100644
--- a/firmware/export/config/mrobe500.h
+++ b/firmware/export/config/mrobe500.h
@@ -198,7 +198,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
diff --git a/firmware/export/config/samsungypr0.h b/firmware/export/config/samsungypr0.h
index 520eb08d01..88ba96504d 100644
--- a/firmware/export/config/samsungypr0.h
+++ b/firmware/export/config/samsungypr0.h
@@ -121,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 50abfa323e..8ef76d60af 100644
--- a/firmware/export/config/samsungypr1.h
+++ b/firmware/export/config/samsungypr1.h
@@ -163,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/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index e0df0c28c3..e78063ef02 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -11,6 +11,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.clip+"
+#define MULTIBOOT_MIN_VOLUME 1
#define HAVE_MULTIDRIVE
#define NUM_DRIVES 2
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/sansae200.h b/firmware/export/config/sansae200.h
index 59e531241f..6bb2df9bc8 100644
--- a/firmware/export/config/sansae200.h
+++ b/firmware/export/config/sansae200.h
@@ -13,6 +13,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.e200"
+#define MULTIBOOT_MIN_VOLUME 1
/* define this if you have recording possibility */
#define HAVE_RECORDING
diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h
index b72f5b5198..e8f7157deb 100644
--- a/firmware/export/config/sansafuze.h
+++ b/firmware/export/config/sansafuze.h
@@ -10,6 +10,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze"
+#define MULTIBOOT_MIN_VOLUME 1
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h
index a853cb13b5..9b91e114bc 100644
--- a/firmware/export/config/sansafuzeplus.h
+++ b/firmware/export/config/sansafuzeplus.h
@@ -12,6 +12,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze+"
+#define MULTIBOOT_MIN_VOLUME 1
// HW can do it but we don't have the IRAM for mix buffers
//#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h
index fc2ff68257..96d3b2deee 100644
--- a/firmware/export/config/sansafuzev2.h
+++ b/firmware/export/config/sansafuzev2.h
@@ -10,6 +10,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze2"
+#define MULTIBOOT_MIN_VOLUME 1
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h
index 16ce888958..6f5365a97e 100644
--- a/firmware/export/config/shanlingq1.h
+++ b/firmware/export/config/shanlingq1.h
@@ -67,6 +67,7 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
@@ -76,7 +77,7 @@
/* TODO: implement HAVE_RTC_ALARM */
/* Power management */
-#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE)
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
@@ -94,6 +95,10 @@
#define BATTERY_CAPACITY_INC 0
#define BATTERY_TYPES_COUNT 1
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.shanling_q1"
+
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
@@ -113,6 +118,11 @@
#define USB_WRITE_BUFFER_SIZE (128 * 1024)
#endif
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_PREV|BUTTON_NEXT))
+#endif
+
/* Rockbox capabilities */
#define HAVE_FAT16SUPPORT
#define HAVE_ALBUMART
@@ -123,3 +133,4 @@
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
#define AB_REPEAT_ENABLE
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/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/lcd.h b/firmware/export/lcd.h
index ffaf1a63d2..0edef7b866 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -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;
@@ -507,7 +510,9 @@ typedef void lcd_blockfunc_type(fb_data *address, unsigned mask, unsigned bits);
extern struct viewport* lcd_current_viewport;
-#define FBADDR(x,y) ((fb_data*) lcd_current_viewport->buffer->get_address_fn(x, y))
+#define FB_CURRENTVP_BUFFER (lcd_current_viewport->buffer)
+#define FBADDRBUF(buffer,x,y) ((fb_data*) buffer->get_address_fn(x,y))
+#define FBADDR(x,y) (FBADDRBUF(lcd_current_viewport->buffer,x,y))
#define FRAMEBUFFER_SIZE (sizeof(fb_data)*LCD_FBWIDTH*LCD_FBHEIGHT)
diff --git a/firmware/export/linuxboot.h b/firmware/export/linuxboot.h
new file mode 100644
index 0000000000..de6f24bf57
--- /dev/null
+++ b/firmware/export/linuxboot.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __LINUXBOOT_H__
+#define __LINUXBOOT_H__
+
+#include "system.h"
+#include <stddef.h>
+#include <sys/types.h>
+
+/*
+ * From u-boot's include/image.h
+ * SPDX-License-Identifier: GPL-2.0+
+ * (C) Copyright 2008 Semihalf
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#define IH_MAGIC 0x27051956
+#define IH_NMLEN 32
+
+enum
+{
+ IH_ARCH_INVALID,
+ IH_ARCH_ALPHA,
+ IH_ARCH_ARM,
+ IH_ARCH_I386,
+ IH_ARCH_IA64,
+ IH_ARCH_MIPS,
+ IH_ARCH_MIPS64,
+ IH_ARCH_PPC,
+ IH_ARCH_S390,
+ IH_ARCH_SH,
+ IH_ARCH_SPARC,
+ IH_ARCH_SPARC64,
+ IH_ARCH_M68K,
+ /* NOTE: Other archs not relevant and omitted here, can add if needed */
+ IH_ARCH_COUNT,
+};
+
+enum
+{
+ IH_TYPE_INVALID = 0,
+ IN_TYPE_STANDALONE,
+ IH_TYPE_KERNEL,
+ IH_TYPE_RAMDISK,
+ IH_TYPE_MULTI,
+ IH_TYPE_FIRMWARE,
+ IH_TYPE_SCRIPT,
+ IH_TYPE_FILESYSTEM,
+ IH_TYPE_FLATDT,
+ /* NOTE: Other types not relevant and omitted here, can add if needed */
+ IH_TYPE_COUNT,
+};
+
+enum
+{
+ IH_COMP_NONE = 0,
+ IH_COMP_GZIP,
+ IH_COMP_BZIP2,
+ IH_COMP_LZMA,
+ IH_COMP_LZO,
+ IH_COMP_LZ4,
+ IH_COMP_ZSTD,
+ IH_COMP_COUNT,
+};
+
+/* Legacy U-Boot image header as produced by mkimage(1).
+ *
+ * WARNING: all fields are big-endian so you usually do not want to
+ * access them directly, use the accessor functions instead.
+ */
+struct uimage_header
+{
+ uint32_t ih_magic;
+ uint32_t ih_hcrc;
+ uint32_t ih_time;
+ uint32_t ih_size;
+ uint32_t ih_load;
+ uint32_t ih_ep;
+ uint32_t ih_dcrc;
+ uint8_t ih_os;
+ uint8_t ih_arch;
+ uint8_t ih_type;
+ uint8_t ih_comp;
+ uint8_t ih_name[IH_NMLEN];
+};
+
+#define _uimage_get32_f(name) \
+ static inline uint32_t uimage_get_##name(const struct uimage_header* uh) \
+ { return betoh32(uh->ih_##name); }
+_uimage_get32_f(magic)
+_uimage_get32_f(hcrc)
+_uimage_get32_f(time)
+_uimage_get32_f(size)
+_uimage_get32_f(load)
+_uimage_get32_f(ep)
+_uimage_get32_f(dcrc)
+#undef _uimage_get32_f
+
+#define _uimage_set32_f(name) \
+ static inline void uimage_set_##name(struct uimage_header* uh, uint32_t val) \
+ { uh->ih_##name = htobe32(val); }
+_uimage_set32_f(magic)
+_uimage_set32_f(hcrc)
+_uimage_set32_f(time)
+_uimage_set32_f(size)
+_uimage_set32_f(load)
+_uimage_set32_f(ep)
+_uimage_set32_f(dcrc)
+#undef _uimage_set32_f
+
+#define _uimage_get8_f(name) \
+ static inline uint8_t uimage_get_##name(const struct uimage_header* uh) \
+ { return uh->ih_##name; }
+_uimage_get8_f(os)
+_uimage_get8_f(arch)
+_uimage_get8_f(type)
+_uimage_get8_f(comp)
+#undef _uimage_get8_f
+
+#define _uimage_set8_f(name) \
+ static inline void uimage_set_##name(struct uimage_header* uh, uint8_t val) \
+ { uh->ih_##name = val; }
+_uimage_set8_f(os)
+_uimage_set8_f(arch)
+_uimage_set8_f(type)
+_uimage_set8_f(comp)
+#undef _uimage_set8_f
+
+/*
+ * uImage utilities
+ */
+
+/** Reader callback for use with `uimage_load`
+ *
+ * \param buf Buffer to write into
+ * \param size Number of bytes to read
+ * \param ctx State argument
+ * \return Number of bytes actually read, or -1 on error.
+ */
+typedef ssize_t(*uimage_reader)(void* buf, size_t size, void* ctx);
+
+/** Calculate U-Boot style CRC */
+uint32_t uimage_crc(uint32_t crc, const void* data, size_t size);
+
+/** Calculate CRC of a uImage header */
+uint32_t uimage_calc_hcrc(const struct uimage_header* uh);
+
+/** Load and decompress a uImage
+ *
+ * \param uh Returned header struct (will be filled with read data)
+ * \param out_size Returned size of the decompressed data
+ * \param reader Data reader function
+ * \param rctx Context argument for the reader function
+ * \return Buflib handle containing the decompressed data, or negative on error
+ *
+ * This function will read a uImage, verify checksums and decompress the image
+ * data into a non-moveable buflib allocation. The length of the compressed
+ * data will be taken from the uImage header.
+ */
+int uimage_load(struct uimage_header* uh, size_t* out_size,
+ uimage_reader reader, void* rctx);
+
+/** File reader for use with `uimage_load`
+ *
+ * \param ctx File descriptor, casted to `void*`
+ */
+ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx);
+
+/* helper for patching broken self-extracting kernels on MIPS */
+uint32_t mips_linux_stub_get_entry(void** code_start, size_t code_size);
+
+#endif /* __LINUXBOOT_H__ */
diff --git a/firmware/export/mi4-loader.h b/firmware/export/mi4-loader.h
index f66164ec2c..adc43ebf64 100644
--- a/firmware/export/mi4-loader.h
+++ b/firmware/export/mi4-loader.h
@@ -21,6 +21,9 @@
*
****************************************************************************/
+#ifndef __MI4_LOADER_H__
+#define __MI4_LOADER_H__
+
#include <stdint.h>
#define MI4_HEADER_SIZE 0x200
@@ -50,21 +53,4 @@ struct tea_key {
int load_mi4(unsigned char* buf, const char* firmware, unsigned int buffer_size);
const char *mi4_strerror(int8_t errno);
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-
-/* TODO needs mapped back to debug_menu if root redirect ever becomes a reality */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware);
-#endif
+#endif /* __MI4_LOADER_H__ */
diff --git a/firmware/export/multiboot.h b/firmware/export/multiboot.h
new file mode 100644
index 0000000000..0132b8531f
--- /dev/null
+++ b/firmware/export/multiboot.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2017, 2020 by William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __MULTIBOOT_H__
+#define __MULTIBOOT_H__
+
+extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume);
+#ifdef HAVE_MULTIBOOT
+extern int get_redirect_dir(char* buf, int buffer_size, int volume,
+ const char* rootdir, const char* firmware);
+#endif
+
+#endif /* __MULTIBOOT_H__ */
diff --git a/firmware/export/mv.h b/firmware/export/mv.h
index 3657ef6c98..08daf50b34 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
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h
index 350dd4e548..d4fa4eb460 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);
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index 235f5302f9..6ae5ccfd13 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"
@@ -81,40 +87,24 @@ 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 +122,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,8 +157,10 @@ 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);
@@ -174,8 +170,9 @@ 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/screendump.h b/firmware/export/screendump.h
index cd7793b9d3..b8fc27047d 100644
--- a/firmware/export/screendump.h
+++ b/firmware/export/screendump.h
@@ -52,12 +52,7 @@
#define DUMP_BMP_LINESIZE ((LCD_WIDTH*3 + 3) & ~3)
#endif
-#ifdef BOOTLOADER
-
-#define screen_dump()
-#define remote_screen_dump()
-
-#else /* !BOOTLOADER */
+#ifdef HAVE_SCREENDUMP
/* Save a .BMP file containing the current screen contents. */
void screen_dump(void);
@@ -69,6 +64,11 @@ void screen_dump_set_hook(void (*hook)(int fd));
void remote_screen_dump(void);
#endif
-#endif /* !BOOTLOADER */
+#else /* !HAVE_SCREENDUMP */
+
+#define screen_dump() do { } while(0)
+#define remote_screen_dump() do { } while(0)
+
+#endif /* HAVE_SCREENDUMP */
#endif /* __SCREENDUMP_H__ */
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index 033b435f2a..cf9e0f077b 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -49,15 +49,7 @@ void si4700_rds_init(void) INIT_ATTR;
/* Radio is fully powered up or about to be powered down */
void si4700_rds_powerup(bool on);
-#if (CONFIG_RDS & RDS_CFG_ISR)
-/* Read raw RDS info for processing - asynchronously */
-void si4700_rds_read_raw_async(unsigned char *buf, int count); /* implemented by target */
-void si4700_rds_interrupt(void);
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
-
/* Read raw RDS info for processing.
- * - If RDS_CFG_ISR is set, the tuner driver will call si4700_rds_read_raw_async() which should
- * perform an asynchronous read and call this function when the data has been read.
* - If RDS_CFG_POLL is set, this function will read status and RDS data and process it if a new
* packet is available.
* - Otherwise this function will read a RDS packet and process it under the assumption that it is
diff --git a/firmware/export/system.h b/firmware/export/system.h
index f554ac7cf1..9558be559a 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -307,8 +307,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/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/include/buflib.h b/firmware/include/buflib.h
index 7f534c6ce0..e805ebbf1b 100644
--- a/firmware/include/buflib.h
+++ b/firmware/include/buflib.h
@@ -129,6 +129,12 @@ struct buflib_callbacks {
void (*sync_callback)(int handle, bool sync_on);
};
+/** 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. */
+extern struct buflib_callbacks buflib_ops_locked;
+
#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK)
#define BUFLIB_SHRINK_POS_FRONT (1u<<31)
#define BUFLIB_SHRINK_POS_BACK (1u<<30)
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 9fae16b551..5c3f630d74 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,58 @@ static inline void fileop_onsync_internal(struct filestr_base *stream)
static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
{
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER)
+ char path[VOL_MAX_LEN+2];
+ 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 */
+
+ if (volume == boot_data.boot_volume) /* boot volume contained in uint8_t payload */
+ {
+ 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)
+ char path[VOL_MAX_LEN+2];
+ 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 +199,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 +217,7 @@ static inline void fileop_onunmount_internal(struct filestr_base *stream)
static inline int readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
#ifdef HAVE_DIRCACHE
return dircache_readdir_dirent(stream, scanp, entry);
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d62b5a8541..ec0c5d28f0 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -30,6 +30,7 @@
#include "fs_attr.h"
#include "fs_defines.h"
#include "fat.h"
+#include "dir.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
@@ -72,17 +73,20 @@ enum fildes_and_obj_flags
/* used in descriptor and common */
FDO_BUSY = 0x0001, /* descriptor/object is in use */
/* only used in individual stream descriptor */
- FD_WRITE = 0x0002, /* descriptor has write mode */
- FD_WRONLY = 0x0004, /* descriptor is write mode only */
- FD_APPEND = 0x0008, /* descriptor is append mode */
+ FD_VALID = 0x0002, /* descriptor is valid but not registered */
+ FD_WRITE = 0x0004, /* descriptor has write mode */
+ FD_WRONLY = 0x0008, /* descriptor is write mode only */
+ FD_APPEND = 0x0010, /* descriptor is append mode */
FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
/* only used as common flags */
- FO_DIRECTORY = 0x0010, /* fileobj is a directory */
- FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */
- FO_REMOVED = 0x0040, /* fileobj was deleted while open */
- FO_SINGLE = 0x0080, /* fileobj has only one stream open */
- FDO_MASK = 0x00ff,
- FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
+ FO_DIRECTORY = 0x0020, /* fileobj is a directory */
+ FO_TRUNC = 0x0040, /* fileobj is opened to be truncated */
+ FO_REMOVED = 0x0080, /* fileobj was deleted while open */
+ FO_SINGLE = 0x0100, /* fileobj has only one stream open */
+ FO_MOUNTTARGET = 0x0200, /* fileobj kept open as a mount target */
+ FDO_MASK = 0x03ff,
+ FDO_CHG_MASK = FO_TRUNC,
+ /* fileobj permitted external change */ /* fileobj permitted external change */
/* bitflags that instruct various 'open' functions how to behave;
* saved in stream flags (only) but not used by manager */
FF_FILE = 0x00000000, /* expect file; accept file only */
@@ -95,7 +99,9 @@ enum fildes_and_obj_flags
FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */
FF_INFO = 0x00400000, /* return info on self */
FF_PARENTINFO = 0x00800000, /* return info on parent */
- FF_MASK = 0x00ff0000,
+ FF_DEVPATH = 0x01000000, /* path is a device path, not root-based */
+ FF_NOFS = 0x02000000, /* no filesystem mounted here */
+ FF_MASK = 0x03ff0000,
};
/** Common data structures used throughout **/
@@ -229,10 +235,10 @@ int test_stream_exists_internal(const char *path, unsigned int callflags);
int open_noiso_internal(const char *path, int oflag); /* file.c */
void force_close_writer_internal(struct filestr_base *stream); /* file.c */
-struct dirent;
+struct DIRENT;
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry);
+ struct DIRENT *entry);
void uncached_rewinddir_dirent(struct dirscan_info *scanp);
int uncached_readdir_internal(struct filestr_base *stream,
@@ -333,7 +339,7 @@ static inline struct fat_direntry *get_dir_fatent(void)
void iso_decode_d_name(char *d_name);
#ifdef HAVE_DIRCACHE
-void empty_dirent(struct dirent *entry);
+void empty_dirent(struct DIRENT *entry);
void fill_dirinfo_native(struct dirinfo_native *din);
#endif /* HAVE_DIRCACHE */
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h
index 627d2df341..0db3520d34 100644
--- a/firmware/include/fileobj_mgr.h
+++ b/firmware/include/fileobj_mgr.h
@@ -29,6 +29,11 @@ void file_binding_remove(struct file_base_binding *bindp);
void file_binding_remove_next(struct file_base_binding *prevp,
struct file_base_binding *bindp);
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp);
+void fileobj_unmount(struct file_base_binding *bindp);
+
void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags);
diff --git a/firmware/include/fs_defines.h b/firmware/include/fs_defines.h
index 538c4b36cd..a1938c56b8 100644
--- a/firmware/include/fs_defines.h
+++ b/firmware/include/fs_defines.h
@@ -51,11 +51,13 @@
/* 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,
diff --git a/firmware/include/inflate.h b/firmware/include/inflate.h
index b56ccf181a..1fce186f34 100644
--- a/firmware/include/inflate.h
+++ b/firmware/include/inflate.h
@@ -23,6 +23,7 @@
#define _INFLATE_H_
#include <stdint.h>
+#include <stddef.h>
enum {
INFLATE_RAW,
@@ -43,4 +44,27 @@ extern const uint32_t inflate_align;
// see above enum for possible options.
int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx);
+struct inflate_bufferctx {
+ // initialize this with your input/output buffer.
+ // the pointer is updated as data is read or written.
+ void* buf;
+
+ // buffer end marker (= buf + buf_size).
+ void* end;
+};
+
+// reader and writer for using an in-memory buffer.
+// Use 'inflate_bufferctx' as the context argument.
+uint32_t inflate_buffer_reader(void* block, uint32_t block_size, void* ctx);
+uint32_t inflate_buffer_writer(const void* block, uint32_t block_size, void* ctx);
+
+// dummy writer used if you just want to figure out how big the decompressed
+// data will be. It does not actually write any data. Example usage:
+//
+// size_t size = 0;
+// inflate(it, st, read, rctx, inflate_getsize_writer, &size);
+//
+// Now 'size' will be the size of the decompressed data (assuming no errors).
+uint32_t inflate_getsize_writer(const void* block, uint32_t block_size, void* ctx);
+
#endif
diff --git a/firmware/include/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..7bc711b5a6
--- /dev/null
+++ b/firmware/include/rb_namespace.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 */
+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);
+
+/* 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/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/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/linuxboot.c b/firmware/linuxboot.c
new file mode 100644
index 0000000000..f9732f6ace
--- /dev/null
+++ b/firmware/linuxboot.c
@@ -0,0 +1,336 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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("inflate", 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("uimage", 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, core_get_data(out_h), *out_size);
+ ret = 0;
+
+ err:
+ if(state_h > 0)
+ 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/powermgmt.c b/firmware/powermgmt.c
index 519823611f..c33ad387ae 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,39 +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; }
-#endif
+#if BATTERY_CAPACITY_INC > 0
+static int battery_capacity = BATTERY_CAPACITY_DEFAULT;
#else
-int _battery_level(void) { return -1; }
+# define battery_capacity BATTERY_CAPACITY_DEFAULT
#endif
-#if (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE) == VOLTAGE_MEASURE
-/*
- * Average battery voltage and charger voltage, filtered via a digital
- * exponential filter (aka. exponential moving average, scaled):
- * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N.
- */
-static unsigned int avgbat;
-/* filtered battery voltage, millivolts */
-static unsigned int battery_millivolts;
-#else
-#ifndef SIMULATOR
-int _battery_voltage(void) { return -1; }
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11];
-const unsigned short percent_to_volt_charge[11];
-#endif
-#endif
-
-#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
-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
@@ -129,112 +96,227 @@ 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 = _battery_level();
- if (percent < 0)
- percent = voltage_to_battery_level(millivolts);
- *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;
-#else
- if (battery_capacity <= 0) /* nor without capacity */
- return -1;
- return _battery_time();
+#ifndef BOOTLOADER
+ if (usb_inserted()
+#ifdef HAVE_USB_POWER
+ #if (CURRENT_USB < CURRENT_NORMAL)
+ || usb_powered_only()
+ #else
+ && !usb_powered_only()
+ #endif
+#endif
+ ) {
+ current = CURRENT_USB;
+ }
+
+#if defined(HAVE_BACKLIGHT) && defined(CURRENT_BACKLIGHT)
+ if (backlight_get_current_timeout() == 0) /* LED always on */
+ current += CURRENT_BACKLIGHT;
+#endif
+
+#if 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 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 _battery_time();
+ return -1;
#endif
}
-/* Returns battery level in percent */
-int battery_level(void)
+/* Initialize the battery voltage/current filters. This is called
+ * once by the power thread before entering the main polling loop. */
+static void average_init(void)
{
-#ifdef HAVE_BATTERY_SWITCH
- if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
- return -1;
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ voltage_now = _battery_voltage() + 15;
+
+ /* The battery voltage is usually a little lower directly after
+ turning on, because the disk was used heavily. Raise it by 5% */
+#ifdef HAVE_DISK_STORAGE
+#if CONFIG_CHARGING
+ if(!charger_inserted())
+#endif
+ {
+ voltage_now += (percent_to_volt_discharge[battery_type][6] -
+ percent_to_volt_discharge[battery_type][5]) / 2;
+ }
+#endif /* HAVE_DISK_STORAGE */
+
+ voltage_avg = voltage_now * BATT_AVE_SAMPLES;
+#endif /* CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE */
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ current_now = _battery_current();
+ current_avg = current_now * BATT_CURRENT_AVE_SAMPLES;
#endif
- return battery_percent;
}
-/* Tells if the battery level is safe for disk writes */
-bool battery_level_safe(void)
+/* Sample the battery voltage/current and update the filters.
+ * Updated once every POWER_THREAD_STEP_TICKS. */
+static void average_step(bool low_battery)
{
-#if defined(NO_LOW_BATTERY_SHUTDOWN)
- return true;
-#elif (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
- return (battery_percent > 0);
-#elif defined(HAVE_BATTERY_SWITCH)
- /* Cannot rely upon the battery reading to be valid and the
- * device could be powered externally. */
- return input_millivolts() > battery_level_dangerous[battery_type];
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ int millivolts = _battery_voltage();
+ if(low_battery) {
+ voltage_now = (millivolts + voltage_now + 1) / 2;
+ voltage_avg += voltage_now - voltage_avg / BATT_AVE_SAMPLES;
+ } else {
+ voltage_avg += millivolts - voltage_avg / BATT_AVE_SAMPLES;
+ voltage_now = voltage_avg / BATT_AVE_SAMPLES;
+ }
#else
- return battery_millivolts > battery_level_dangerous[battery_type];
+ (void)low_battery;
+#endif
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ current_avg += _battery_current() - current_avg / BATT_CURRENT_AVE_SAMPLES;
+ current_now = current_avg / BATT_CURRENT_AVE_SAMPLES;
#endif
}
-/* look into the percent_to_volt_* table and get a realistic battery level */
+/* Send system battery level update events on reaching certain significant
+ * levels. This is called by battery_status_update() and does not have to
+ * be called separately. */
+static void send_battery_level_event(int percent)
+{
+ static const int levels[] = { 5, 15, 30, 50, 0 };
+ const int *level = levels;
+
+ while (*level)
+ {
+ if (percent <= *level && last_sent_battery_level > *level) {
+ last_sent_battery_level = *level;
+ queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
+ break;
+ }
+
+ level++;
+ }
+}
+
+#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) && \
+ (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+/* Look into the percent_to_volt_* table and estimate the battery level. */
static int voltage_to_percent(int voltage, const short* table)
{
if (voltage <= table[0]) {
@@ -256,20 +338,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;
}
@@ -277,102 +358,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 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. */
- if (level < 0)
- level = voltage_to_battery_level(millivolt);
+ int current = battery_current();
+ int resolution = battery_capacity * 36;
-#ifdef CURRENT_NORMAL /*don't try to estimate run or charge
- time without normal current defined*/
- /* calculate estimated remaining running time */
+ 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);
+ }
- /* 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();
+ /* The second term decrements the counter due to elapsed time. */
+ time_err -= resolution;
+
+ /* Arbitrary cutoff to ensure we don't get too far out
+ * of sync. Seems to work well on synthetic tests. */
+ if(time_err > resolution * 12 ||
+ time_err < -resolution * 13) {
+ time_cnt = time_est;
+ time_err = 0;
}
- 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
@@ -389,13 +515,13 @@ bool query_force_shutdown(void)
#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
}
@@ -406,8 +532,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 */
@@ -542,90 +673,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
@@ -666,13 +722,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();
/*
@@ -718,7 +772,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();
@@ -769,7 +823,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();
@@ -809,7 +863,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)
@@ -824,10 +888,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) {
@@ -845,9 +908,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)
@@ -863,25 +943,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);
diff --git a/firmware/rolo.c b/firmware/rolo.c
index 5f936c95f4..d280e39243 100644
--- a/firmware/rolo.c
+++ b/firmware/rolo.c
@@ -41,23 +41,19 @@
#include "loader_strerror.h"
#if defined(MI4_FORMAT)
#include "mi4-loader.h"
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
-#include "bootdata.h"
-#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*mi4-loader.c*/
-#endif
#define LOAD_FIRMWARE(a,b,c) load_mi4(a,b,c)
#elif defined(RKW_FORMAT)
#include "rkw-loader.h"
#define LOAD_FIRMWARE(a,b,c) load_rkw(a,b,c)
#else
#include "rb-loader.h"
+#define LOAD_FIRMWARE(a,b,c) load_firmware(a,b,c)
+#endif
+
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
+#include "multiboot.h"
#include "bootdata.h"
#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*rb-loader.c*/
-#endif
-#define LOAD_FIRMWARE(a,b,c) load_firmware(a,b,c)
#endif
#if CONFIG_CPU == AS3525v2
@@ -133,9 +129,10 @@ static void rolo_error(const char *text)
lcd_scroll_stop();
}
-#if CONFIG_CPU == IMX31L || CONFIG_CPU == RK27XX
+#if CONFIG_CPU == IMX31L || CONFIG_CPU == RK27XX || CONFIG_CPU == X1000
/* this is in firmware/target/arm/imx31/rolo_restart.c for IMX31 */
/* this is in firmware/target/arm/rk27xx/rolo_restart.c for rk27xx */
+/* this is in firmware/target/mips/ingenic_x1000/boot-x1000.c for X1000 */
extern void rolo_restart(const unsigned char* source, unsigned char* dest,
int length);
#else
@@ -242,7 +239,7 @@ int rolo_load(const char* filename)
/* get the system buffer. release only in case of error, otherwise
* we don't return anyway */
- rolo_handle = core_alloc_maximum("rolo", &filebuf_size, NULL);
+ rolo_handle = core_alloc_maximum("rolo", &filebuf_size, &buflib_ops_locked);
if (rolo_handle < 0)
{
rolo_error("OOM");
diff --git a/firmware/target/arm/as3525/lcd-ssd1303.c b/firmware/target/arm/as3525/lcd-ssd1303.c
index 2aa0b844c5..186fdcacbe 100644
--- a/firmware/target/arm/as3525/lcd-ssd1303.c
+++ b/firmware/target/arm/as3525/lcd-ssd1303.c
@@ -310,6 +310,7 @@ static void internal_update_rect(int x, int y, int width, int height)
const int column_high = get_column_high_byte(x);
const int column_low = get_column_low_byte(x);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= height; y++)
{
@@ -320,7 +321,7 @@ static void internal_update_rect(int x, int y, int width, int height)
(column_low)
);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
lcd_write_command (LCD_NOP); /* return to command mode */
diff --git a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
index 03ed1de5d5..48594a2ac9 100644
--- a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
+++ b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
@@ -440,9 +440,10 @@ void lcd_update_rect(int x, int y, int width, int height)
/* setup GRAM write window */
lcd_setup_rect(x, x_end - 1, y, y_end - 1);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* write to GRAM */
for (row = y; row < y_end; row++) {
- lcd_write_data(FBADDR(x,row), width);
+ lcd_write_data(fbaddr(x,row), width);
}
}
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c b/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
index c1bc379a49..ab4466300b 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
@@ -149,6 +149,8 @@ void lcd_update_rect(int x, int y, int w, int h)
lcd_write_reg(0x17, y | (y + h - 1) << 8);
lcd_write_reg(0x21, y * LCD_WIDTH + x);
lcd_write_reg(0x22, 0);
+
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int yy = y; yy < y + h; yy++)
- imx233_lcdif_pio_send(true, 2 * w, FBADDR(x, yy));
+ imx233_lcdif_pio_send(true, 2 * w, fbaddr(x,yy));
}
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenv.c b/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
index 06b0f158f3..75d2775814 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
@@ -172,8 +172,9 @@ void lcd_update_rect(int x, int y, int w, int h)
lcd_send(false, 0x75); lcd_send(true, y); lcd_send(true, y + h - 1);
lcd_send(false, 0x5c);
imx233_lcdif_set_word_length(16);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int yy = y; yy < y + h; yy++)
- imx233_lcdif_pio_send(true, w, FBADDR(x, yy));
+ imx233_lcdif_pio_send(true, w, fbaddr(x,yy));
}
#ifndef BOOTLOADER
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c b/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
index ce0bcc3885..825b0072a3 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
@@ -282,8 +282,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
index 890ff0b586..d0084900e7 100644
--- a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
+++ b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
@@ -241,8 +241,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
index 59496f2d24..d5f25a523c 100644
--- a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
+++ b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
@@ -188,8 +188,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/debug-imx233.c b/firmware/target/arm/imx233/debug-imx233.c
index bfc38b20dc..4487952162 100644
--- a/firmware/target/arm/imx233/debug-imx233.c
+++ b/firmware/target/arm/imx233/debug-imx233.c
@@ -940,8 +940,8 @@ bool dbg_hw_info_emi(void)
bool dbg_hw_info_audio(void)
{
- static const char *hp_sel[2] = {"DAC", "Line1"};
- static const char *mux_sel[4] = {"Mic", "Line1", "HP", "Line2"};
+ static const char * const hp_sel[2] = {"DAC", "Line1"};
+ static const char * const mux_sel[4] = {"Mic", "Line1", "HP", "Line2"};
lcd_setfont(FONT_SYSFIXED);
while(1)
@@ -1171,7 +1171,7 @@ bool dbg_hw_info_button(void)
}
else if(MAP[i].periph == IMX233_BUTTON_LRADC)
{
- static const char *op_name[] =
+ static const char * const op_name[] =
{
[IMX233_BUTTON_EQ] = "eq",
[IMX233_BUTTON_GT] = "gt",
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
index 92864c9ed7..ceb7b4e090 100644
--- a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
@@ -637,8 +637,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c b/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
index cfcf85bfc0..8f49bfa3eb 100644
--- a/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
+++ b/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
@@ -228,8 +228,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c b/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
index 999f4ee525..862522da15 100644
--- a/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
+++ b/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
@@ -189,8 +189,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
index 5b0c71110d..c84f1cf41c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
@@ -125,53 +125,30 @@ bool si4700_st(void)
return (GPIO1_DR & (1 << 28)) >> 28;
}
-
/* Low-level RDS Support */
-static bool int_restore;
-
-/* Called after I2C read cycle completes */
-static void si4700_rds_read_raw_async_callback(struct i2c_transfer_desc *xfer)
-{
- if (xfer->rxcount == 0)
- si4700_rds_process();
- /* else read didn't finish */
-
- if (int_restore)
- gpio_int_enable(SI4700_EVENT_ID);
-}
-
-/* Called to read registers from ISR context */
-void si4700_rds_read_raw_async(unsigned char *buf, int count)
-{
- /* transfer descriptor for RDS async operations */
- static struct i2c_transfer_desc xfer = { .node = &si4700_i2c_node };
-
- xfer.txdata = NULL;
- xfer.txcount = 0;
- xfer.rxdata = buf;
- xfer.rxcount = count;
- xfer.callback = si4700_rds_read_raw_async_callback;
- xfer.next = NULL;
-
- i2c_transfer(&xfer);
-}
+static struct semaphore rds_sema;
+static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
/* RDS GPIO interrupt handler - start RDS data read */
void INT_SI4700_RDS(void)
{
- /* mask and clear the interrupt until we're done */
- gpio_int_disable(SI4700_EVENT_ID);
gpio_int_clear(SI4700_EVENT_ID);
+ semaphore_release(&rds_sema);
+}
- /* tell radio driver about it */
- si4700_rds_interrupt();
+/* Captures RDS data and processes it */
+static void NORETURN_ATTR rds_thread(void)
+{
+ while (true) {
+ semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
+ si4700_rds_process();
+ }
}
/* Called with on=true after full radio power up, and with on=false before
powering down */
void si4700_rds_powerup(bool on)
{
- int_restore = on;
gpio_int_disable(SI4700_EVENT_ID);
gpio_int_clear(SI4700_EVENT_ID);
gpio_enable_event(SI4700_EVENT_ID, on);
@@ -180,5 +157,7 @@ void si4700_rds_powerup(bool on)
/* One-time RDS init at startup */
void si4700_rds_init(void)
{
- /* nothing to do */
+ semaphore_init(&rds_sema, 1, 0);
+ create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
+ IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
}
diff --git a/firmware/target/arm/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/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/pp/mi4-loader.c b/firmware/target/arm/pp/mi4-loader.c
index 0104496e9d..f609e3ff7a 100644
--- a/firmware/target/arm/pp/mi4-loader.c
+++ b/firmware/target/arm/pp/mi4-loader.c
@@ -30,96 +30,9 @@
#include "crc32.h"
#include "file.h"
#if defined(HAVE_BOOTDATA)
-#include "system.h"
-#include "bootdata.h"
-
-/* Write bootdata into location in FIRMWARE marked by magic header
- * Assumes buffer is already loaded with the firmware image
- * We just need to find the location and write data into the
- * payload region along with the crc for later verification and use.
- * Returns payload len on success,
- * On error returns EKEY_NOT_FOUND
- */
-int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
-{
- struct boot_data_t bl_boot_data;
- struct boot_data_t *fw_boot_data = NULL;
- int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
- int payload_len = EKEY_NOT_FOUND;
-
- /* search for boot data header prior to search_len */
- for(int i = 0;i < search_len;i++)
- {
- fw_boot_data = (struct boot_data_t*) &buf[i];
- if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
- fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
- continue;
-
- memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
- bl_boot_data.boot_volume = boot_volume;
-
- memset(fw_boot_data->payload, 0, fw_boot_data->length);
- /* determine maximum bytes we can write to firmware
- BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
- payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
- fw_boot_data->length = payload_len;
- /* copy data to FIRMWARE bootdata struct */
- memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
- /* crc will be used within the firmware to check validity of bootdata */
- fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
- break;
-
- }
- return payload_len;
-}
+#include "multiboot.h"
#endif /* HAVE_BOOTDATA */
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware)
-{
- int fd;
- int f_offset;
- char add_path[MAX_PATH];
- /* Check in root of volume for rockbox_main.<playername> redirect */
- snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
- fd = open(add_path, O_RDONLY);
- if (fd < 0)
- return EFILE_NOT_FOUND;
-
- /*clear add_path for re-use*/
- memset(add_path, 0, sizeof(add_path));
- f_offset = read(fd, add_path,sizeof(add_path));
- close(fd);
-
- for(int i = f_offset - 1;i > 0; i--)
- {
- /* strip control chars < SPACE or all if path doesn't start with '/' */
- if (add_path[i] < 0x20 || add_path[0] != '/')
- add_path[i] = '\0';
- }
- /* if '/add_path' is specified in rockbox_main.<playername>
- path is /<vol#>/add_path/rootdir/firmwarename
- if add_path is empty or '/' is missing from beginning
- path is /<vol#>/rootdir/firmwarename
- */
- return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
- rootdir, firmware);
-}
-#endif /* HAVE_MULTIBOOT */
-
static inline unsigned int le2int(unsigned char* buf)
{
int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
diff --git a/firmware/target/arm/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/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/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/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/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/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 5c68ded3e1..889386b7c6 100644
--- a/firmware/target/hosted/ibasso/pcm-ibasso.c
+++ b/firmware/target/hosted/ibasso/pcm-ibasso.c
@@ -123,7 +123,7 @@ static void* pcm_thread_run(void* nothing)
/* https://github.com/tinyalsa/tinyalsa/blob/master/tinypcminfo.c */
-static const char* format_lookup[] =
+static const char* const format_lookup[] =
{
/*[0] =*/ "S8",
"U8",
diff --git a/firmware/target/hosted/ibasso/sysfs-ibasso.c b/firmware/target/hosted/ibasso/sysfs-ibasso.c
index 8f62e3fec2..e3a0f911af 100644
--- a/firmware/target/hosted/ibasso/sysfs-ibasso.c
+++ b/firmware/target/hosted/ibasso/sysfs-ibasso.c
@@ -32,7 +32,7 @@
#include "sysfs-ibasso.h"
-static const char* SYSFS_PATHS[] =
+static const char* const SYSFS_PATHS[] =
{
/* SYSFS_DX50_CODEC_VOLUME */
"/dev/codec_volume",
diff --git a/firmware/target/hosted/rolo.c b/firmware/target/hosted/rolo.c
index 04b21d553e..dfe483c964 100644
--- a/firmware/target/hosted/rolo.c
+++ b/firmware/target/hosted/rolo.c
@@ -37,12 +37,6 @@
//#define LOGF_ENABLE
#include "logf.h"
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
-#include "bootdata.h"
-#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*rb-loader.c*/
-#endif
-
static void rolo_error(const char *text, const char *text2)
{
lcd_clear_display();
@@ -72,16 +66,6 @@ int rolo_load(const char* filename)
audio_stop();
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
- /* write the bootdata as if rolo were the bootloader */
- unsigned int crc = 0;
- if (strcmp(filename, BOOTDIR "/" BOOTFILE) == 0)
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
-
- if(crc > 0 && crc == boot_data.crc)
- write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */
-#endif
-
#ifdef HAVE_STORAGE_FLUSH
lcd_puts(0, 2, "Flushing storage buffers");
lcd_update();
diff --git a/firmware/target/hosted/sdl/sim-ui-defines.h b/firmware/target/hosted/sdl/sim-ui-defines.h
index baba357ccc..7d60deac34 100644
--- a/firmware/target/hosted/sdl/sim-ui-defines.h
+++ b/firmware/target/hosted/sdl/sim-ui-defines.h
@@ -523,10 +523,10 @@
#elif defined(FIIO_M3K) || defined(FIIO_M3K_LINUX)
#define UI_TITLE "FiiO M3K"
-#define UI_WIDTH 287 /* width of GUI window */
-#define UI_HEIGHT 589 /* height of GUI window */
-#define UI_LCD_POSX 25
-#define UI_LCD_POSY 15
+#define UI_WIDTH 334 /* width of GUI window */
+#define UI_HEIGHT 684 /* height of GUI window */
+#define UI_LCD_POSX 49
+#define UI_LCD_POSY 37
#elif defined(SHANLING_Q1)
diff --git a/firmware/target/hosted/system-hosted.c b/firmware/target/hosted/system-hosted.c
index ce47fd5f5c..c4ae5a404f 100644
--- a/firmware/target/hosted/system-hosted.c
+++ b/firmware/target/hosted/system-hosted.c
@@ -90,6 +90,7 @@ static void sig_handler(int sig, siginfo_t *siginfo, void *context)
void power_off(void)
{
backlight_hw_off();
+ sync();
system("/sbin/poweroff");
while (1); /* halt */
}
diff --git a/firmware/target/hosted/usb-hiby.c b/firmware/target/hosted/usb-hiby.c
index b82fa5c4ce..050c86e446 100644
--- a/firmware/target/hosted/usb-hiby.c
+++ b/firmware/target/hosted/usb-hiby.c
@@ -63,8 +63,8 @@ void usb_enable(bool on)
*/
int disk_mount_all(void)
{
- const char *dev[] = {"/dev/mmcblk0p1", "/dev/mmcblk0"};
- const char *fs[] = {"vfat", "exfat"};
+ const char * const dev[] = {"/dev/mmcblk0p1", "/dev/mmcblk0"};
+ const char * const fs[] = {"vfat", "exfat"};
sysfs_set_string("/sys/class/android_usb/android0/f_mass_storage/lun/file", "");
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
index 47b646be34..2c1c3a226e 100644
--- a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
+++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
@@ -532,6 +532,7 @@ void lcd_update(void)
const int column_high = get_column_high_byte(0);
const int column_low = get_column_low_byte(0);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -542,7 +543,7 @@ void lcd_update(void)
(column_low)
);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -586,6 +587,7 @@ void lcd_update_rect(int x, int y, int width, int height)
ymax = (y + height-1) >> 3;
y >>= 3;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -596,6 +598,6 @@ void lcd_update_rect(int x, int y, int width, int height)
(column_low)
);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/mips/ingenic_x1000/app.lds b/firmware/target/mips/ingenic_x1000/app.lds
index 26b2854728..2943329ba7 100644
--- a/firmware/target/mips/ingenic_x1000/app.lds
+++ b/firmware/target/mips/ingenic_x1000/app.lds
@@ -6,6 +6,13 @@ OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_x1000/crt0.o)
+#ifdef BOOTLOADER
+# undef PLUGIN_BUFFER_SIZE
+# undef CODEC_SIZE
+# define PLUGIN_BUFFER_SIZE 0
+# define CODEC_SIZE 0
+#endif
+
/* End of the audio buffer, where the codec buffer starts */
#define ENDAUDIOADDR (X1000_DRAM_END - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
@@ -16,6 +23,7 @@ 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
}
SECTIONS
@@ -61,6 +69,14 @@ SECTIONS
} > IRAM
_iramcopy = LOADADDR(.iram);
+ .tcsm X1000_TCSM_BASE: AT (_bssbegin + SIZEOF(.iram))
+ {
+ _tcsmstart = .;
+ KEEP(*(.tcsm*));
+ _tcsmend = .;
+ } > TCSM
+ _tcsmcopy = LOADADDR(.tcsm);
+
. = ALIGN(4);
.stack (NOLOAD) :
{
@@ -88,10 +104,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..aa97bfcd85
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.c
@@ -0,0 +1,280 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "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);
+
+ 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);
+
+ /* 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));
+
+ /* 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));
+
+#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)
+{
+ nand_drv* n = nand_init();
+ nand_lock(n);
+
+ int ret = nand_open(n);
+ if(ret != NAND_SUCCESS)
+ goto err_unlock;
+
+ /* NOTE: hardcoded address is used by all current targets */
+ jz_writef(CPM_CLKGR, PDMA(0));
+ ret = nand_read_bytes(n, 0x4000, 0x2000, (void*)0xb3422000);
+
+ nand_close(n);
+ err_unlock:
+ nand_unlock(n);
+ return ret;
+}
diff --git a/firmware/target/mips/ingenic_x1000/boot-x1000.h b/firmware/target/mips/ingenic_x1000/boot-x1000.h
index 400eb93dc1..1b7a0db1e9 100644
--- a/firmware/target/mips/ingenic_x1000/boot-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.h
@@ -24,18 +24,10 @@
#include "x1000/cpm.h"
#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
enum {
- BOOT_OPTION_ROCKBOX = 0,
- BOOT_OPTION_OFW_PLAYER,
- BOOT_OPTION_OFW_RECOVERY,
-};
-
-enum {
- /* 3 bits to store the boot option selected by the SPL */
- BOOT_OPTION_MASK = 0x7,
- BOOT_OPTION_SHIFT = 0,
-
/* Set after running clk_init() and setting up system clocks */
BOOT_FLAG_CLK_INIT = (1 << 31),
@@ -43,6 +35,18 @@ enum {
BOOT_FLAG_USB_BOOT = (1 << 30),
};
+void x1000_boot_rockbox(const void* source, size_t length)
+ __attribute__((section(".icode")));
+void x1000_boot_linux(const void* source, size_t length,
+ void* load, void* entry, const char* args)
+ __attribute__((section(".icode")));
+
+/* dual boot support code */
+void x1000_dualboot_cleanup(void);
+void x1000_dualboot_init_clocktree(void);
+void x1000_dualboot_init_uart2(void);
+int x1000_dualboot_load_pdma_fw(void);
+
/* Note: these functions are inlined to minimize SPL code size.
* They are private to the X1000 early boot code anyway... */
@@ -74,18 +78,4 @@ static inline void clr_boot_flag(uint32_t bit)
cpm_scratch_set(REG_CPM_SCRATCH & ~bit);
}
-static inline void set_boot_option(int opt)
-{
- uint32_t r = REG_CPM_SCRATCH;
- r &= ~(BOOT_OPTION_MASK << BOOT_OPTION_SHIFT);
- r |= (opt & BOOT_OPTION_MASK) << BOOT_OPTION_SHIFT;
- cpm_scratch_set(r);
-}
-
-static inline int get_boot_option(void)
-{
- uint32_t r = REG_CPM_SCRATCH;
- return (r >> BOOT_OPTION_SHIFT) & BOOT_OPTION_MASK;
-}
-
#endif /* __BOOT_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
index 4988e7c3bf..e3b0f792bb 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -265,39 +265,36 @@ void clk_init(void)
jz_writef(CPM_APCR, BS(1), PLLM(42 - 1), PLLN(0), PLLOD(0), ENABLE(1));
while(jz_readf(CPM_APCR, ON) == 0);
-#if (defined(FIIO_M3K) || defined(EROS_QN))
+#if defined(FIIO_M3K) || defined(EROS_QN)
/* TODO: Allow targets to define their clock frequencies in their config,
* instead of having this be a random special case. */
- if(get_boot_option() == BOOT_OPTION_ROCKBOX) {
- clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
- CLKDIV_L2(2) | /* 504 MHz */
- CLKDIV_AHB0(5) | /* 201.6 MHz */
- CLKDIV_AHB2(5) | /* 201.6 MHz */
- CLKDIV_PCLK(10)); /* 100.8 MHz */
- clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
- CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(SCLK_A) |
- CLKMUX_AHB2(SCLK_A));
-
- /* DDR to 201.6 MHz */
- clk_set_ddr(X1000_CLK_SCLK_A, 5);
-
- /* Disable MPLL */
- jz_writef(CPM_MPCR, ENABLE(0));
- while(jz_readf(CPM_MPCR, ON));
- } else {
-#endif
- clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
- CLKDIV_L2(2) | /* 504 MHz */
- CLKDIV_AHB0(3) | /* 200 MHz */
- CLKDIV_AHB2(3) | /* 200 MHz */
- CLKDIV_PCLK(6)); /* 100 MHz */
- clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
- CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(MPLL) |
- CLKMUX_AHB2(MPLL));
-#if (defined(FIIO_M3K) || defined(EROS_QN))
- }
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(5) | /* 201.6 MHz */
+ CLKDIV_AHB2(5) | /* 201.6 MHz */
+ CLKDIV_PCLK(10)); /* 100.8 MHz */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) |
+ CLKMUX_AHB2(SCLK_A));
+
+ /* DDR to 201.6 MHz */
+ clk_set_ddr(X1000_CLK_SCLK_A, 5);
+
+ /* Disable MPLL */
+ jz_writef(CPM_MPCR, ENABLE(0));
+ while(jz_readf(CPM_MPCR, ON));
+#else
+ /* Default configuration matching the Ingenic OF */
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(3) | /* 200 MHz */
+ CLKDIV_AHB2(3) | /* 200 MHz */
+ CLKDIV_PCLK(6)); /* 100 MHz */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(MPLL) |
+ CLKMUX_AHB2(MPLL));
#endif
/* mark that clocks have been initialized */
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h
index e19c56d0ba..f7153da564 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.h
@@ -80,13 +80,13 @@ extern void clk_set_ccr_div(uint32_t divbits);
extern void clk_set_ddr(x1000_clk_t src, uint32_t div);
/* 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 304f8d682f..d079b01e2a 100644
--- a/firmware/target/mips/ingenic_x1000/crt0.S
+++ b/firmware/target/mips/ingenic_x1000/crt0.S
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,10 +21,12 @@
#include "config.h"
#include "mips.h"
+#include "bootdata.h"
.text
.extern main
.extern system_early_init
+ .extern _loadaddress
.global _start
.set push
@@ -35,62 +37,74 @@
.section .init.text
_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
+ b _realstart
nop
- /* Invalidate BTB */
- mfc0 v0, C0_Config, 7
- nop
- ori v0, v0, 2
- mtc0 v0, C0_Config, 7
- 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:
/* 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)
+ la a0, _iramcopy
+ la a1, _iramstart
+ la a2, _iramend
+ bal _copy
+ nop
+
+ /* Copy TCSM from BSS */
+ la a0, _tcsmcopy
+ la a1, _tcsmstart
+ la a2, _tcsmend
+ bal _copy
+ nop
/* 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
+
+ /* 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
+
+ /* Invalidate BTB */
+ mfc0 v0, C0_Config, 7
+ nop
+ ori v0, v0, 2
+ mtc0 v0, C0_Config, 7
+ nop
/* Jump to C code */
jal system_early_init
@@ -98,6 +112,28 @@ _irqstack_loop:
j main
nop
+ /* 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
+
/* Exception entry points */
.section .vectors.1, "ax", %progbits
j tlb_refill_handler
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c
index 98b8f95fb5..236442a880 100644
--- a/firmware/target/mips/ingenic_x1000/debug-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c
@@ -118,12 +118,18 @@ static bool dbg_gpios(void)
}
extern volatile unsigned aic_tx_underruns;
+#ifdef HAVE_RECORDING
+extern volatile unsigned aic_rx_overruns;
+#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
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
index 073bddb8b4..0d43a3f010 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
@@ -99,6 +99,20 @@ static const uint32_t erosqnative_lcd_cmd_enable[] = {
/* Display On */
LCD_INSTR_CMD, 0x29,
LCD_INSTR_UDELAY, 20000,
+ LCD_INSTR_END,
+};
+
+static const uint32_t erosqnative_lcd_of_compat_cmd[] = {
+ /* Pixel Format Set */
+ LCD_INSTR_CMD, 0x3a,
+ LCD_INSTR_DAT, 0x66, /* 18 bpp */
+ /* Exit Sleep */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 120000,
+ /* Display On */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_UDELAY, 20000,
+ LCD_INSTR_END,
};
/* sleep and wake copied directly from m3k */
@@ -179,6 +193,15 @@ void lcd_tgt_enable(bool enable)
}
}
+void lcd_tgt_enable_of(bool enable)
+{
+ /* silence the unused parameter warning */
+ if (enable)
+ {}
+
+ lcd_exec_commands(&erosqnative_lcd_of_compat_cmd[0]);
+}
+
void lcd_tgt_sleep(bool sleep)
{
if(sleep)
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
index 5573919aa2..ab6393a9fe 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
@@ -63,10 +63,15 @@ void power_init(void)
/* Set lowest sample rate */
axp_adc_set_rate(AXP_ADC_RATE_25HZ);
- /* Ensure battery voltage ADC is enabled */
- int bits = axp_adc_get_enabled();
- bits |= (1 << ADC_BATTERY_VOLTAGE);
- axp_adc_set_enabled(bits);
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
/* Turn on all power outputs */
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
@@ -78,11 +83,13 @@ void power_init(void)
* OF's setting, although it's not strictly within the USB spec. */
axp_set_charge_current(780);
+#ifdef BOOTLOADER
/* Delay to give power outputs time to stabilize.
* With the power thread delay, this can apparently go as low as 50,
* Keeping a higher value here just to ensure the bootloader works
* correctly. */
mdelay(200);
+#endif
}
#ifdef HAVE_USB_CHARGING_ENABLE
@@ -111,3 +118,13 @@ int _battery_voltage(void)
{
return axp_adc_read(ADC_BATTERY_VOLTAGE);
}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
index 8528b803f7..9374d23a81 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -20,6 +20,7 @@
****************************************************************************/
#include "audiohw.h"
+#include "audio.h"
#include "system.h"
#include "pcm_sampr.h"
#include "aic-x1000.h"
@@ -27,8 +28,13 @@
#include "gpio-x1000.h"
#include "logf.h"
+static int cur_audio_source = AUDIO_SRC_PLAYBACK;
+static int cur_vol_r = AK4376_MIN_VOLUME;
+static int cur_vol_l = AK4376_MIN_VOLUME;
+static int cur_recvol = 0;
+static int cur_filter_roll_off = 0;
static int cur_fsel = HW_FREQ_48;
-static int cur_power_mode = 0;
+static int cur_power_mode = SOUND_HIGH_POWER;
static void set_ak_freqmode(void)
{
@@ -60,29 +66,138 @@ void audiohw_postinit(void)
void audiohw_close(void)
{
- ak4376_close();
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_close();
+ else if(cur_audio_source == AUDIO_SRC_MIC)
+ x1000_icodec_close();
+}
+
+void audio_set_output_source(int source)
+{
+ /* this is a no-op */
+ (void)source;
+}
+
+void audio_input_mux(int source, unsigned flags)
+{
+ (void)flags;
+
+ if(source == cur_audio_source)
+ return;
+
+ /* close whatever codec is currently in use */
+ audiohw_close();
+ aic_enable_i2s_bit_clock(false);
+
+ /* switch to new source */
+ cur_audio_source = source;
+
+ if(source == AUDIO_SRC_PLAYBACK) {
+ /* power on DAC */
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(AIC_I2S_MASTER_MODE);
+ ak4376_open();
+
+ /* apply the old settings */
+ audiohw_set_volume(cur_vol_l, cur_vol_r);
+ audiohw_set_filter_roll_off(cur_filter_roll_off);
+ set_ak_freqmode();
+ } else if(source == AUDIO_SRC_MIC) {
+ aic_set_external_codec(false);
+ aic_set_i2s_mode(AIC_I2S_SLAVE_MODE);
+ /* Note: Sampling frequency is irrelevant here */
+ aic_set_i2s_clock(X1000_CLK_EXCLK, 48000, 0);
+ aic_enable_i2s_bit_clock(true);
+
+ x1000_icodec_open();
+
+ /* configure the mic */
+ x1000_icodec_mic1_enable(true);
+ x1000_icodec_mic1_bias_enable(true);
+ x1000_icodec_mic1_configure(JZCODEC_MIC1_DIFFERENTIAL |
+ JZCODEC_MIC1_BIAS_2_08V);
+
+ /* configure the ADC */
+ x1000_icodec_adc_mic_sel(JZCODEC_MIC_SEL_ANALOG);
+ x1000_icodec_adc_highpass_filter(true);
+ x1000_icodec_adc_frequency(cur_fsel);
+ x1000_icodec_adc_enable(true);
+
+ /* configure the mixer to put mic input in both channels */
+ x1000_icodec_mixer_enable(true);
+ x1000_icodec_write(JZCODEC_MIX2, 0x10);
+
+ /* set gain and unmute */
+ audiohw_set_recvol(cur_recvol, 0, AUDIO_GAIN_MIC);
+ x1000_icodec_adc_mute(false);
+ } else {
+ logf("bad audio input source: %d (flags: %x)", source, flags);
+ }
}
void audiohw_set_volume(int vol_l, int vol_r)
{
- ak4376_set_volume(vol_l, vol_r);
+ cur_vol_l = vol_l;
+ cur_vol_r = vol_r;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_set_volume(vol_l, vol_r);
}
void audiohw_set_filter_roll_off(int val)
{
- ak4376_set_filter_roll_off(val);
+ cur_filter_roll_off = val;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_set_filter_roll_off(val);
}
void audiohw_set_frequency(int fsel)
{
cur_fsel = fsel;
- set_ak_freqmode();
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ set_ak_freqmode();
+ else
+ x1000_icodec_adc_frequency(fsel);
}
void audiohw_set_power_mode(int mode)
{
cur_power_mode = mode;
- set_ak_freqmode();
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ set_ak_freqmode();
+}
+
+static int round_step_up(int x, int step)
+{
+ int rem = x % step;
+ if(rem > 0)
+ rem -= step;
+ return x - rem;
+}
+
+void audiohw_set_recvol(int left, int right, int type)
+{
+ (void)right;
+
+ if(type == AUDIO_GAIN_MIC) {
+ cur_recvol = left;
+
+ if(cur_audio_source == AUDIO_SRC_MIC) {
+ int mic_gain = round_step_up(left, X1000_ICODEC_MIC_GAIN_STEP);
+ mic_gain = MIN(mic_gain, X1000_ICODEC_MIC_GAIN_MAX);
+ mic_gain = MAX(mic_gain, X1000_ICODEC_MIC_GAIN_MIN);
+
+ int adc_gain = left - mic_gain;
+ adc_gain = MIN(adc_gain, X1000_ICODEC_ADC_GAIN_MAX);
+ adc_gain = MAX(adc_gain, X1000_ICODEC_ADC_GAIN_MIN);
+
+ x1000_icodec_adc_gain(adc_gain);
+ x1000_icodec_mic1_gain(mic_gain);
+ }
+ }
}
void ak4376_set_pdn_pin(int level)
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
index 5c92fa81e2..840be36a75 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
@@ -61,10 +61,15 @@ void power_init(void)
/* Set lowest sample rate */
axp_adc_set_rate(AXP_ADC_RATE_25HZ);
- /* Ensure battery voltage ADC is enabled */
- int bits = axp_adc_get_enabled();
- bits |= (1 << ADC_BATTERY_VOLTAGE);
- axp_adc_set_enabled(bits);
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
/* Turn on all power outputs */
i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
@@ -106,3 +111,13 @@ int _battery_voltage(void)
{
return axp_adc_read(ADC_BATTERY_VOLTAGE);
}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
deleted file mode 100644
index cbbe8b1d5d..0000000000
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include "system.h"
-#include "clk-x1000.h"
-#include "spl-x1000.h"
-#include "gpio-x1000.h"
-
-#define CMDLINE_COMMON \
- "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
-#define CMDLINE_NORMAL \
- " init=/linuxrc ubi.mtd=3 root=ubi0:rootfs ubi.mtd=4 rootfstype=ubifs rw loglevel=8"
-
-static int dualboot_setup(void)
-{
- spl_dualboot_init_clocktree();
- spl_dualboot_init_uart2();
-
- /* load PDMA MCU firmware */
- jz_writef(CPM_CLKGR, PDMA(0));
- return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
-}
-
-const struct spl_boot_option spl_boot_options[] = {
- [BOOT_OPTION_ROCKBOX] = {
- .storage_addr = 0x6800,
- .storage_size = 102 * 1024,
- .load_addr = X1000_DRAM_BASE,
- .exec_addr = X1000_DRAM_BASE,
- .flags = BOOTFLAG_UCLPACK,
- },
- [BOOT_OPTION_OFW_PLAYER] = {
- .storage_addr = 0x20000,
- .storage_size = 4 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
- [BOOT_OPTION_OFW_RECOVERY] = {
- .storage_addr = 0x420000,
- .storage_size = 5 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
-};
-
-int spl_get_boot_option(void)
-{
- /* Button debounce time in OST clock cycles */
- const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
-
- /* Buttons to poll */
- const unsigned port = GPIO_A;
- const uint32_t recov_pin = (1 << 19); /* Volume Up */
- const uint32_t orig_fw_pin = (1 << 17); /* Play */
-
- uint32_t pin = -1, lastpin = 0;
- uint32_t deadline = 0;
- int iter_count = 30; /* to avoid an infinite loop */
-
- /* set GPIOs to input state */
- gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
-
- /* Poll until we get a stable reading */
- do {
- lastpin = pin;
- pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
- if(pin != lastpin) {
- deadline = __ost_read32() + btn_stable_time;
- iter_count -= 1;
- }
- } while(iter_count > 0 && __ost_read32() < deadline);
-
- if(iter_count >= 0 && (pin & orig_fw_pin)) {
- if(pin & recov_pin)
- return BOOT_OPTION_OFW_RECOVERY;
- else
- return BOOT_OPTION_OFW_PLAYER;
- }
-
- return BOOT_OPTION_ROCKBOX;
-}
-
-void spl_error(void)
-{
- /* Flash the buttonlight */
- int level = 0;
- while(1) {
- gpio_set_function(GPIO_PC(24), GPIOF_OUTPUT(level));
- mdelay(100);
- level = 1 - level;
- }
-}
diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.c b/firmware/target/mips/ingenic_x1000/installer-x1000.c
index 0a09ad0e91..66aa42d4a1 100644
--- a/firmware/target/mips/ingenic_x1000/installer-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/installer-x1000.c
@@ -148,7 +148,7 @@ static int updater_init(struct updater* u)
/* buf_len is a bit oversized here, but it's not really important */
u->buf_len = u->img_len + sizeof(mtar_t) + 2*CACHEALIGN_SIZE;
- u->buf_hnd = core_alloc("boot_image", u->buf_len);
+ u->buf_hnd = core_alloc_ex("boot_image", u->buf_len, &buflib_ops_locked);
if(u->buf_hnd < 0) {
rc = IERR_OUT_OF_MEMORY;
goto error;
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.h b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
index 749fac8240..e88a8733ed 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
@@ -105,6 +105,14 @@ extern void lcd_exec_commands(const uint32_t* cmdseq);
*/
extern void lcd_tgt_enable(bool on);
+/* Enable/disable the LCD controller, but intended for booting the OF.
+ *
+ * This is only used for the Eros Q Native port, as the OF seems to be
+ * unable to initialize the LCD in the kernel boot rather than having
+ * the bootloader do it.
+ */
+extern void lcd_tgt_enable_of(bool on);
+
/* Enter or exit sleep mode to save power, normally by sending the necessary
* commands with lcd_exec_commands().
*/
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index a818ba10aa..5838b21b39 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -69,6 +69,7 @@ const nand_chip supported_nand_chips[] = {
.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),
@@ -190,8 +191,10 @@ static void setup_chip_registers(nand_drv* drv)
int nand_open(nand_drv* drv)
{
- if(drv->refcount > 0)
+ if(drv->refcount > 0) {
+ drv->refcount++;
return NAND_SUCCESS;
+ }
/* Initialize the controller */
sfc_open();
@@ -222,7 +225,8 @@ int nand_open(nand_drv* drv)
void nand_close(nand_drv* drv)
{
- if(drv->refcount == 0)
+ --drv->refcount;
+ if(drv->refcount > 0)
return;
/* Let's reset the chip... the idea is to restore the registers
@@ -231,7 +235,6 @@ void nand_close(nand_drv* drv)
mdelay(10);
sfc_close();
- drv->refcount--;
}
static uint8_t nand_wait_busy(nand_drv* drv)
@@ -290,14 +293,14 @@ int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void*
if(rc < 0)
return rc;
- memcpy(buffer, &drv->page_buf[offset], MIN(pg_size, byte_len));
+ memcpy(buffer, &drv->page_buf[offset], MIN(pg_size - offset, byte_len));
- if(byte_len <= pg_size)
+ if(byte_len <= pg_size - offset)
break;
+ byte_len -= pg_size - offset;
+ buffer += pg_size - offset;
offset = 0;
- byte_len -= pg_size;
- buffer += pg_size;
page++;
}
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h
index 711bf190b5..668b3e3f82 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h
@@ -78,6 +78,9 @@ typedef struct nand_chip {
/* Total number of blocks in the chip */
unsigned nr_blocks;
+ /* Bad block marker offset within the 1st page of a bad block */
+ unsigned bbm_pos;
+
/* Clock frequency to use */
uint32_t clock_freq;
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
index ef54d45e62..7d1c83a35a 100644
--- a/firmware/target/mips/ingenic_x1000/pcm-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,28 +31,31 @@
#include "x1000/aic.h"
#include "x1000/cpm.h"
-#define AIC_STATE_STOPPED 0
-#define AIC_STATE_PLAYING 1
+#define AIC_STATE_STOPPED 0x00
+#define AIC_STATE_PLAYING 0x01
+#define AIC_STATE_RECORDING 0x02
volatile unsigned aic_tx_underruns = 0;
static int aic_state = AIC_STATE_STOPPED;
-static int aic_lock = 0;
-static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
-
-static dma_desc aic_dma_desc;
-
+static int play_lock = 0;
+static volatile int play_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc play_dma_desc;
static void pcm_play_dma_int_cb(int event);
+
#ifdef HAVE_RECORDING
+volatile unsigned aic_rx_overruns = 0;
+static int rec_lock = 0;
+static volatile int rec_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc rec_dma_desc;
+
static void pcm_rec_dma_int_cb(int event);
#endif
void pcm_play_dma_init(void)
{
- /* Ungate clock, assign pins. NB this overlaps with pins labeled "sa0-sa4"
- * on Ingenic's datasheets but I'm not sure what they are. Probably safe to
- * assume they are not useful to Rockbox... */
+ /* Ungate clock */
jz_writef(CPM_CLKGR, AIC(0));
/* Configure AIC with some sane defaults */
@@ -79,7 +82,7 @@ void pcm_play_dma_init(void)
#endif
/* Set DMA settings */
- jz_writef(AIC_CFG, TFTH(16), RFTH(16));
+ jz_writef(AIC_CFG, TFTH(16), RFTH(15));
dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb);
#ifdef HAVE_RECORDING
dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb);
@@ -106,23 +109,23 @@ void pcm_dma_apply_settings(void)
audiohw_set_frequency(pcm_fsel);
}
-static void pcm_dma_start(const void* addr, size_t size)
+static void play_dma_start(const void* addr, size_t size)
{
- aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
- SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
- STDE(0), TIE(1), LINK(0));
- aic_dma_desc.sa = PHYSADDR(addr);
- aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
- aic_dma_desc.tc = size;
- aic_dma_desc.sd = 0;
- aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
- aic_dma_desc.pad0 = 0;
- aic_dma_desc.pad1 = 0;
-
- commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
+ play_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(0), TIE(1), LINK(0));
+ play_dma_desc.sa = PHYSADDR(addr);
+ play_dma_desc.ta = PHYSADDR(JA_AIC_DR);
+ play_dma_desc.tc = size;
+ play_dma_desc.sd = 0;
+ play_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
+ play_dma_desc.pad0 = 0;
+ play_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&play_dma_desc, sizeof(dma_desc));
commit_dcache_range(addr, size);
- REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc);
+ REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&play_dma_desc);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1));
@@ -130,13 +133,13 @@ static void pcm_dma_start(const void* addr, size_t size)
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
-static void pcm_dma_handle_event(int event)
+static void play_dma_handle_event(int event)
{
if(event == DMA_EVENT_COMPLETE) {
const void* addr;
size_t size;
if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
} else if(event == DMA_EVENT_NONE) {
/* ignored, so callers don't need to check for this */
} else {
@@ -146,20 +149,20 @@ 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));
}
@@ -168,21 +171,23 @@ void pcm_play_dma_stop(void)
jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0));
jz_writef(AIC_CCR, TFLUSH(1));
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_STOPPED;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_PLAYING;
}
void pcm_play_lock(void)
{
- ++aic_lock;
+ int irq = disable_irq_save();
+ ++play_lock;
+ restore_irq(irq);
}
void pcm_play_unlock(void)
{
int irq = disable_irq_save();
- if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
- pcm_dma_handle_event(aic_dma_pending_event);
- aic_dma_pending_event = DMA_EVENT_NONE;
+ if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) {
+ play_dma_handle_event(play_dma_pending_event);
+ play_dma_pending_event = DMA_EVENT_NONE;
}
restore_irq(irq);
@@ -193,11 +198,56 @@ void pcm_play_unlock(void)
* Recording
*/
-/* FIXME need to implement this!! */
+static void rec_dma_start(void* addr, size_t size)
+{
+ /* NOTE: Rockbox always records in stereo and the AIC pushes in the
+ * sample for each channel separately. One frame therefore requires
+ * two 16-bit transfers from the AIC. */
+ rec_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(0), DAI(1), RDIL(6),
+ SP_V(16BIT), DP_V(16BIT), TSZ_V(16BIT),
+ STDE(0), TIE(1), LINK(0));
+ rec_dma_desc.sa = PHYSADDR(JA_AIC_DR);
+ rec_dma_desc.ta = PHYSADDR(addr);
+ rec_dma_desc.tc = size / 2;
+ rec_dma_desc.sd = 0;
+ rec_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_RX));
+ rec_dma_desc.pad0 = 0;
+ rec_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&rec_dma_desc, sizeof(dma_desc));
+ if((unsigned long)addr < 0xa0000000ul)
+ discard_dcache_range(addr, size);
+
+ REG_DMA_CHN_DA(DMA_CHANNEL_RECORD) = PHYSADDR(&rec_dma_desc);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), DES8(1), NDES(0));
+ jz_set(DMA_DB, 1 << DMA_CHANNEL_RECORD);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), CTE(1));
+
+ pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
+}
+
+static void rec_dma_handle_event(int event)
+{
+ if(event == DMA_EVENT_COMPLETE) {
+ void* addr;
+ size_t size;
+ if(pcm_rec_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
+ rec_dma_start(addr, size);
+ } else if(event == DMA_EVENT_NONE) {
+ /* ignored, so callers don't need to check for this */
+ } else {
+ pcm_rec_dma_status_callback(PCM_DMAST_ERR_DMA);
+ }
+}
static void pcm_rec_dma_int_cb(int event)
{
- (void)event;
+ if(rec_lock) {
+ rec_dma_pending_event = event;
+ return;
+ } else {
+ rec_dma_handle_event(event);
+ }
}
void pcm_rec_dma_init(void)
@@ -210,40 +260,52 @@ void pcm_rec_dma_close(void)
void pcm_rec_dma_start(void* addr, size_t size)
{
- (void)addr;
- (void)size;
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_RECORDING;
+
+ rec_dma_start(addr, size);
+ jz_writef(AIC_CCR, RDMS(1), EROR(1), EREC(1));
}
void pcm_rec_dma_stop(void)
{
+ jz_writef(AIC_CCR, RDMS(0), EROR(0), EREC(0));
+ jz_writef(AIC_CCR, RFLUSH(1));
+
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_RECORDING;
}
void pcm_rec_lock(void)
{
-
+ int irq = disable_irq_save();
+ ++rec_lock;
+ restore_irq(irq);
}
void pcm_rec_unlock(void)
{
+ int irq = disable_irq_save();
+ if(--rec_lock == 0 && (aic_state & AIC_STATE_RECORDING)) {
+ rec_dma_handle_event(rec_dma_pending_event);
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ }
+ restore_irq(irq);
}
const void* pcm_rec_dma_get_peak_buffer(void)
{
- return NULL;
-}
-
-void audio_set_output_source(int source)
-{
- (void)source;
+ return (const void*)UNCACHEDADDR(REG_DMA_CHN_TA(DMA_CHANNEL_RECORD));
}
+#endif /* HAVE_RECORDING */
-void audio_input_mux(int source, unsigned flags)
+#ifdef HAVE_PCM_DMA_ADDRESS
+void* pcm_dma_addr(void* addr)
{
- (void)source;
- (void)flags;
+ return (void*)UNCACHEDADDR(addr);
}
-#endif /* HAVE_RECORDING */
+#endif
void AIC(void)
{
@@ -251,4 +313,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.h b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
index d28bcb6740..afb4aa3ce6 100644
--- a/firmware/target/mips/ingenic_x1000/sfc-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
@@ -87,13 +87,13 @@ extern void sfc_irq_end(void);
extern void sfc_set_clock(uint32_t freq);
/* Set the device configuration register */
-inline void sfc_set_dev_conf(uint32_t conf)
+static inline void sfc_set_dev_conf(uint32_t conf)
{
REG_SFC_DEV_CONF = conf;
}
/* Control the state of the write protect pin */
-inline void sfc_set_wp_enable(bool en)
+static inline void sfc_set_wp_enable(bool en)
{
jz_writef(SFC_GLB, WP_EN(en ? 1 : 0));
}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
index 7314f20412..787c35c494 100644
--- a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
@@ -59,7 +59,7 @@ static void codec_stop(void)
{
es9218_mute(true);
es9218_close();
- mdelay(1);
+ mdelay(4);
}
void audiohw_init(void)
@@ -154,7 +154,7 @@ void audiohw_set_filter_roll_off(int value)
void audiohw_set_power_mode(int mode)
{
enum es9218_amp_mode new_amp_mode;
- if(mode == 0)
+ if(mode == SOUND_HIGH_POWER)
new_amp_mode = ES9218_AMP_MODE_2VRMS;
else
new_amp_mode = ES9218_AMP_MODE_1VRMS;
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
index 17fbe1cede..59a2262f25 100644
--- a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
@@ -78,6 +78,19 @@ void power_init(void)
cw2015_init();
#endif
+ /* Set lowest sample rate */
+ axp_adc_set_rate(AXP_ADC_RATE_25HZ);
+
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
+
/* Change supply voltage from the default of 1250 mV to 1200 mV,
* this matches the original firmware's settings. Didn't observe
* any obviously bad behavior at 1250 mV, but better to be safe. */
@@ -121,6 +134,16 @@ int _battery_voltage(void)
return axp_adc_read(ADC_BATTERY_VOLTAGE);
}
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
+
#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) != 0
int _battery_level(void)
{
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
deleted file mode 100644
index 33303c5e6b..0000000000
--- a/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include "system.h"
-#include "clk-x1000.h"
-#include "spl-x1000.h"
-#include "gpio-x1000.h"
-
-#define CMDLINE_COMMON \
- "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
-#define CMDLINE_NORMAL \
- " init=/linuxrc ubi.mtd=5 root=ubi0:rootfs ubi.mtd=6 rootfstype=ubifs rw"
-
-static int dualboot_setup(void)
-{
- spl_dualboot_init_clocktree();
- spl_dualboot_init_uart2();
-
- /* load PDMA MCU firmware */
- jz_writef(CPM_CLKGR, PDMA(0));
- return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
-}
-
-const struct spl_boot_option spl_boot_options[] = {
- [BOOT_OPTION_ROCKBOX] = {
- .storage_addr = 0x6800,
- .storage_size = 102 * 1024,
- .load_addr = X1000_DRAM_BASE,
- .exec_addr = X1000_DRAM_BASE,
- .flags = BOOTFLAG_UCLPACK,
- },
- [BOOT_OPTION_OFW_PLAYER] = {
- .storage_addr = 0x140000,
- .storage_size = 8 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
- [BOOT_OPTION_OFW_RECOVERY] = {
- .storage_addr = 0x940000,
- .storage_size = 10 * 1024 * 1024,
- .load_addr = 0x80efffc0,
- .exec_addr = 0x80f00000,
- .cmdline = CMDLINE_COMMON,
- .cmdline_addr = 0x80004000,
- .setup = dualboot_setup,
- },
-};
-
-int spl_get_boot_option(void)
-{
- /* Button debounce time in OST clock cycles */
- const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
-
- /* Buttons to poll */
- const unsigned port = GPIO_B;
- const uint32_t recov_pin = (1 << 22); /* Next */
- const uint32_t orig_fw_pin = (1 << 21); /* Prev */
-
- uint32_t pin = -1, lastpin = 0;
- uint32_t deadline = 0;
- int iter_count = 30; /* to avoid an infinite loop */
-
- /* set GPIOs to input state */
- gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
-
- /* Poll until we get a stable reading */
- do {
- lastpin = pin;
- pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
- if(pin != lastpin) {
- deadline = __ost_read32() + btn_stable_time;
- iter_count -= 1;
- }
- } while(iter_count > 0 && __ost_read32() < deadline);
-
- if(iter_count >= 0 && (pin & orig_fw_pin)) {
- if(pin & recov_pin)
- return BOOT_OPTION_OFW_RECOVERY;
- else
- return BOOT_OPTION_OFW_PLAYER;
- }
-
- return BOOT_OPTION_ROCKBOX;
-}
-
-void spl_error(void)
-{
- /* Flash the backlight */
- int level = 0;
- while(1) {
- gpio_set_function(GPIO_PC(25), GPIOF_OUTPUT(level));
- mdelay(100);
- level = 1 - level;
- }
-}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c
index 9d7a1d118a..82a05abf75 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c
@@ -19,45 +19,32 @@
*
****************************************************************************/
-#include "system.h"
-#include "clk-x1000.h"
#include "spl-x1000.h"
#include "gpio-x1000.h"
+#include "nand-x1000.h"
-/* TODO: get dual-boot working */
+static nand_drv* ndrv = NULL;
-const struct spl_boot_option spl_boot_options[] = {
- [BOOT_OPTION_ROCKBOX] = {
- .storage_addr = 0x6800,
- .storage_size = 102 * 1024,
- .load_addr = X1000_DRAM_BASE,
- .exec_addr = X1000_DRAM_BASE,
- .flags = BOOTFLAG_UCLPACK,
- },
-};
-
-int spl_get_boot_option(void)
+int spl_storage_open(void)
{
- return BOOT_OPTION_ROCKBOX;
-}
+ /* We need to assign the GPIOs manually */
+ gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
-void spl_error(void)
-{
- const uint32_t pin = (1 << 25);
+ /* Allocate NAND driver manually in DRAM */
+ ndrv = spl_alloc(sizeof(nand_drv));
+ ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
+ ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
+ ndrv->refcount = 0;
- /* Turn on backlight */
- jz_clr(GPIO_INT(GPIO_C), pin);
- jz_set(GPIO_MSK(GPIO_C), pin);
- jz_clr(GPIO_PAT1(GPIO_C), pin);
- jz_set(GPIO_PAT0(GPIO_C), pin);
+ return nand_open(ndrv);
+}
- while(1) {
- /* Turn it off */
- mdelay(100);
- jz_set(GPIO_PAT0(GPIO_C), pin);
+void spl_storage_close(void)
+{
+ nand_close(ndrv);
+}
- /* Turn it on */
- mdelay(100);
- jz_clr(GPIO_PAT0(GPIO_C), pin);
- }
+int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
+{
+ return nand_read_bytes(ndrv, addr, length, buffer);
}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index afaf5a7dd6..b9ee6cc1c1 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -34,20 +34,49 @@
#include "ucl_decompress.h"
#include <string.h>
-#if defined(FIIO_M3K) || defined(SHANLING_Q1)
-# define SPL_DDR_MEMORYSIZE 64
-# define SPL_DDR_AUTOSR_EN 1
-# define SPL_DDR_NEED_BYPASS 1
+#if defined(FIIO_M3K)
+/* Size of memory, either 64 or 32 is legal. */
+# define SPL_DDR_MEMORYSIZE 64
+/* Pin to flash on spl_error(). Should be a backlight. */
+# define SPL_ERROR_PIN GPIO_PC(24)
+/* Address and size of the bootloader on the storage medium used by the SPL */
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
+#elif defined(SHANLING_Q1)
+# define SPL_DDR_MEMORYSIZE 64
+# define SPL_ERROR_PIN GPIO_PC(25)
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
#elif defined(EROS_QN)
-# define SPL_DDR_MEMORYSIZE 32
-# define SPL_DDR_AUTOSR_EN 1
-# define SPL_DDR_NEED_BYPASS 1
+# define SPL_DDR_MEMORYSIZE 32
+# define SPL_ERROR_PIN GPIO_PC(25)
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
#else
-# error "please define DRAM settings"
+# error "please define SPL config"
+#endif
+
+/* Hardcode this since the SPL is considered part of the bootloader,
+ * and should never get built or updated separately. */
+#define BOOT_LOAD_ADDR X1000_DRAM_BASE
+#define BOOT_EXEC_ADDR BOOT_LOAD_ADDR
+
+/* Whether the bootloader is UCL-compressed */
+#ifndef SPL_USE_UCLPACK
+# define SPL_USE_UCLPACK 1
+#endif
+
+/* Whether auto-self-refresh should be enabled (seems it always should be?) */
+#ifndef SPL_DDR_AUTOSR_EN
+# define SPL_DDR_AUTOSR_EN 1
+#endif
+
+/* Whether DLL bypass is necessary (probably always?) */
+#ifndef SPL_DDR_NEED_BYPASS
+# define SPL_DDR_NEED_BYPASS 1
#endif
static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE);
-static nand_drv* ndrv = NULL;
void* spl_alloc(size_t count)
{
@@ -56,114 +85,14 @@ void* spl_alloc(size_t count)
return heap;
}
-int spl_storage_open(void)
-{
- /* We need to assign the GPIOs manually */
- gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
-
- /* Allocate NAND driver manually in DRAM */
- ndrv = spl_alloc(sizeof(nand_drv));
- ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
- ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
- ndrv->refcount = 0;
-
- return nand_open(ndrv);
-}
-
-void spl_storage_close(void)
-{
- nand_close(ndrv);
-}
-
-int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
-{
- return nand_read_bytes(ndrv, addr, length, buffer);
-}
-
-/* Used by:
- * - FiiO M3K
- * - Shanling Q1
- *
- * Amend it and add #ifdefs for other targets if needed.
- */
-void spl_dualboot_init_clocktree(void)
+void spl_error(void)
{
- /* Make sure these are gated to match the OF behavior. */
- jz_writef(CPM_CLKGR, PCM(1), MAC(1), LCD(1), MSC0(1), MSC1(1), OTG(1), CIM(1));
-
- /* Set clock sources, and make sure every clock starts out stopped */
- jz_writef(CPM_I2SCDR, CS_V(EXCLK));
- jz_writef(CPM_PCMCDR, CS_V(EXCLK));
-
- jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_MACCDR, BUSY));
-
- jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_LPCDR, BUSY));
-
- jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_MSC0CDR, BUSY));
-
- jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_MSC1CDR, BUSY));
-
- jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
- while(jz_readf(CPM_CIMCDR, BUSY));
-
- jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
- while(jz_readf(CPM_USBCDR, BUSY));
-}
-
-void spl_dualboot_init_uart2(void)
-{
- /* Ungate the clock and select UART2 device function */
- jz_writef(CPM_CLKGR, UART2(0));
- gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
-
- /* Disable all interrupts */
- jz_write(UART_UIER(2), 0);
-
- /* FIFO configuration */
- jz_overwritef(UART_UFCR(2),
- RDTR(3), /* FIFO trigger level = 60? */
- UME(0), /* UART module disable */
- DME(1), /* DMA mode enable? */
- TFRT(1), /* transmit FIFO reset */
- RFRT(1), /* receive FIFO reset */
- FME(1)); /* FIFO mode enable */
-
- /* IR mode configuration */
- jz_overwritef(UART_ISR(2),
- RDPL(1), /* Zero is negative pulse for receive */
- TDPL(1), /* ... and for transmit */
- XMODE(1), /* Pulse width 1.6us */
- RCVEIR(0), /* Disable IR for recieve */
- XMITIR(0)); /* ... and for transmit */
-
- /* Line configuration */
- jz_overwritef(UART_ULCR(2), DLAB(0),
- WLS_V(8BITS), /* 8 bit words */
- SBLS_V(1_STOP_BIT), /* 1 stop bit */
- PARE(0), /* no parity */
- SBK(0)); /* don't set break */
-
- /* Set the baud rate... not too sure how this works. (Docs unclear!) */
- const unsigned divisor = 0x0004;
- jz_writef(UART_ULCR(2), DLAB(1));
- jz_write(UART_UDLHR(2), (divisor >> 8) & 0xff);
- jz_write(UART_UDLLR(2), divisor & 0xff);
- jz_write(UART_UMR(2), 16);
- jz_write(UART_UACR(2), 0);
- jz_writef(UART_ULCR(2), DLAB(0));
-
- /* Enable UART */
- jz_overwritef(UART_UFCR(2),
- RDTR(0), /* FIFO trigger level = 1 */
- DME(0), /* DMA mode disable */
- UME(1), /* UART module enable */
- TFRT(1), /* transmit FIFO reset */
- RFRT(1), /* receive FIFO reset */
- FME(1)); /* FIFO mode enable */
+ int level = 0;
+ while(1) {
+ gpio_set_function(SPL_ERROR_PIN, GPIOF_OUTPUT(level));
+ mdelay(100);
+ level = 1 - level;
+ }
}
static void init_ost(void)
@@ -346,14 +275,14 @@ static int init_dram(void)
return 0;
}
-static void* get_load_buffer(const struct spl_boot_option* opt)
+static void* get_load_buffer(void)
{
/* read to a temporary location if we need to decompress,
* otherwise simply read directly to the load address. */
- if(opt->flags & BOOTFLAG_UCLPACK)
- return spl_alloc(opt->storage_size);
+ if(SPL_USE_UCLPACK)
+ return spl_alloc(BOOT_STORAGE_SIZE);
else
- return (void*)opt->load_addr;
+ return (void*)BOOT_LOAD_ADDR;
}
/* Mapping of boot_sel[1:0] pins.
@@ -377,15 +306,10 @@ static uint32_t get_boot_sel(void)
return (*(uint32_t*)0xf40001ec) & 3;
}
-typedef void(*entry_fn)(int, char**, int, int) __attribute__((noreturn));
-
void spl_main(void)
{
- int rc, boot_option;
- const struct spl_boot_option* opt;
+ int rc;
void* load_buffer;
- char** kargv = NULL;
- int kargc = 0;
/* magic */
REG_CPM_PSWC0ST = 0x00;
@@ -395,7 +319,6 @@ void spl_main(void)
/* set up boot flags */
init_boot_flags();
- set_boot_option(BOOT_OPTION_ROCKBOX);
/* early clock and DRAM init */
clk_init_early();
@@ -409,14 +332,6 @@ void spl_main(void)
return;
}
- /* find out what we should boot */
- boot_option = spl_get_boot_option();
- opt = &spl_boot_options[boot_option];
- load_buffer = get_load_buffer(opt);
-
- /* save the selection for later */
- set_boot_option(boot_option);
-
/* finish up clock init */
clk_init();
@@ -425,44 +340,29 @@ void spl_main(void)
if(rc != 0)
spl_error();
- rc = spl_storage_read(opt->storage_addr, opt->storage_size, load_buffer);
+ load_buffer = get_load_buffer();
+ rc = spl_storage_read(BOOT_STORAGE_ADDR, BOOT_STORAGE_SIZE, load_buffer);
if(rc != 0)
spl_error();
- /* handle compression */
- switch(opt->flags & BOOTFLAG_COMPRESSED) {
- case BOOTFLAG_UCLPACK: {
- uint32_t out_size = X1000_DRAM_END - opt->load_addr;
- rc = ucl_unpack((uint8_t*)load_buffer, opt->storage_size,
- (uint8_t*)opt->load_addr, &out_size);
- } break;
-
- default:
- break;
+ /* decompress */
+ if(SPL_USE_UCLPACK) {
+ uint32_t out_size = X1000_SDRAM_END - BOOT_LOAD_ADDR;
+ rc = ucl_unpack((uint8_t*)load_buffer, BOOT_STORAGE_SIZE,
+ (uint8_t*)BOOT_LOAD_ADDR, &out_size);
+ } else {
+ rc = 0;
}
if(rc != 0)
spl_error();
- /* call the setup hook */
- if(opt->setup) {
- rc = opt->setup();
- if(rc != 0)
- spl_error();
- }
-
/* close off storage access */
spl_storage_close();
- /* handle kernel command line, if specified */
- if(opt->cmdline) {
- kargv = (char**)opt->cmdline_addr;
- kargv[kargc++] = 0;
- kargv[kargc++] = (char*)opt->cmdline;
- }
-
/* jump to the entry point */
- entry_fn fn = (entry_fn)opt->exec_addr;
+ typedef void(*entry_fn)(void);
+ entry_fn fn = (entry_fn)BOOT_EXEC_ADDR;
commit_discard_idcache();
- fn(kargc, kargv, 0, 0);
+ fn();
}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h
index 6d60dbf880..9ee1aa768e 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h
@@ -26,23 +26,6 @@
#include <stddef.h>
#include <stdint.h>
-#define BOOTFLAG_COMPRESSED 0x0f /* mask for compression flags */
-#define BOOTFLAG_UCLPACK 0x01 /* image is compressed with 'uclpack' */
-
-struct spl_boot_option {
- uint32_t storage_addr; /* image's location in storage */
- uint32_t storage_size; /* number of bytes to load */
- uint32_t load_addr; /* address to load image to */
- uint32_t exec_addr; /* address of the entry point */
- uint32_t flags; /* any special flags */
- const char* cmdline; /* command line; use NULL if not needed */
- uint32_t cmdline_addr; /* address to contain command line 'argv[]' */
- int(*setup)(void); /* setup hook, called before jumping to image */
-};
-
-/* array of boot option descriptions */
-extern const struct spl_boot_option spl_boot_options[];
-
/* Memory allocator. Allocation starts from the top of DRAM and counts down.
* Allocation sizes are rounded up to a multiple of the cacheline size, so
* the returned address is always suitably aligned for DMA. */
@@ -58,13 +41,6 @@ extern int spl_storage_open(void);
extern void spl_storage_close(void);
extern int spl_storage_read(uint32_t addr, uint32_t length, void* buffer);
-/* Helpers for dual-booting with the Ingenic Linux OF */
-extern void spl_dualboot_init_clocktree(void);
-extern void spl_dualboot_init_uart2(void);
-
-/* Get the boot option selected by the user, eg. by a key press */
-extern int spl_get_boot_option(void);
-
/* Called on a fatal error -- it should do something visible to the user
* like flash the backlight repeatedly. */
extern void spl_error(void) __attribute__((noreturn));
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds
index b0169ab1aa..09ab75d520 100644
--- a/firmware/target/mips/ingenic_x1000/spl.lds
+++ b/firmware/target/mips/ingenic_x1000/spl.lds
@@ -7,10 +7,8 @@ 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
diff --git a/firmware/target/mips/ingenic_x1000/system-x1000.c b/firmware/target/mips/ingenic_x1000/system-x1000.c
index d43c8e67e4..7542b97a3d 100644
--- a/firmware/target/mips/ingenic_x1000/system-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/system-x1000.c
@@ -72,7 +72,6 @@ void system_early_init(void)
* This hack should keep everything working as usual. */
if(jz_readf(CPM_MPCR, ON) == 0) {
init_boot_flags();
- set_boot_option(BOOT_OPTION_ROCKBOX);
set_boot_flag(BOOT_FLAG_CLK_INIT);
}
#endif
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index a32cf185e7..eb82f72eae 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -449,12 +449,11 @@ 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("usb storage",
+ ALLOCATE_BUFFER_SIZE + MAX_CBW_SIZE + 31,
+ &buflib_ops_locked);
if (usb_handle < 0)
panicf("%s(): OOM", __func__);