diff options
Diffstat (limited to 'firmware')
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, ®s[0]); - if(rc != I2C_STATUS_OK) - return; - - /* Parse registers to set ADC enable bits */ - const struct axp_adc_info* info = axp_adc_info; - for(int i = 0; i < NUM_ADC_CHANNELS; ++i) { - if(info[i].en_reg == 0xff) - continue; - - if(regs[info[i].en_reg - AXP_REG_ADCENABLE1] & info[i].en_bit) - axp.adc_enable |= 1 << i; - } - - /* Handle battery power ADC */ - if((axp.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) && - (axp.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) { - axp.adc_enable |= (1 << ADC_BATTERY_POWER); - } -} - void axp_init(void) { - axp_init_enabled_adcs(); - - /* We need discharge current ADC to reliably poll for a full battery */ - int bits = axp.adc_enable; - bits |= (1 << ADC_DISCHARGE_CURRENT); - axp_adc_set_enabled(bits); - - /* Read the maximum charging current */ - int value = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGECONTROL1); - axp.chargecurrent_setting = (value < 0) ? -1 : (value & 0xf); } void axp_supply_set_voltage(int supply, int voltage) @@ -305,22 +257,15 @@ int axp_adc_read(int adc) int axp_adc_read_raw(int adc) { - /* Don't give a reading if the ADC is not enabled */ - if((axp.adc_enable & (1 << adc)) == 0) - return INT_MIN; - /* Read the ADC */ - uint8_t buf[3]; - int count = (adc == ADC_BATTERY_POWER) ? 3 : 2; + uint8_t buf[2]; uint8_t reg = axp_adc_info[adc].reg; - int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, count, &buf[0]); + int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, 2, &buf[0]); if(rc != I2C_STATUS_OK) return INT_MIN; /* Parse the value */ - if(adc == ADC_BATTERY_POWER) - return (buf[0] << 16) | (buf[1] << 8) | buf[2]; - else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT) + if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT) return (buf[0] << 5) | (buf[1] & 0x1f); else return (buf[0] << 4) | (buf[1] & 0xf); @@ -328,79 +273,33 @@ int axp_adc_read_raw(int adc) int axp_adc_conv_raw(int adc, int value) { - switch(adc) { - case ADC_ACIN_VOLTAGE: - case ADC_VBUS_VOLTAGE: - /* 0 mV ... 6.9615 mV, step 1.7 mV */ - return value * 17 / 10; - case ADC_ACIN_CURRENT: - /* 0 mA ... 2.5594 A, step 0.625 mA */ - return value * 5 / 8; - case ADC_VBUS_CURRENT: - /* 0 mA ... 1.5356 A, step 0.375 mA */ - return value * 3 / 8; - case ADC_INTERNAL_TEMP: - /* -144.7 C ... 264.8 C, step 0.1 C */ + if(adc == ADC_INTERNAL_TEMP) return value - 1447; - case ADC_TS_INPUT: - /* 0 mV ... 3.276 V, step 0.8 mV */ - return value * 4 / 5; - case ADC_BATTERY_VOLTAGE: - /* 0 mV ... 4.5045 V, step 1.1 mV */ - return value * 11 / 10; - case ADC_CHARGE_CURRENT: - case ADC_DISCHARGE_CURRENT: - /* 0 mA to 4.095 A, step 0.5 mA */ - return value / 2; - case ADC_APS_VOLTAGE: - /* 0 mV to 5.733 V, step 1.4 mV */ - return value * 7 / 5; - case ADC_BATTERY_POWER: - /* 0 uW to 23.6404 W, step 0.55 uW */ - return value * 11 / 20; - default: - /* Shouldn't happen */ - return INT_MIN; - } -} - -int axp_adc_get_enabled(void) -{ - return axp.adc_enable; + else + return axp_adc_info[adc].num * value / axp_adc_info[adc].den; } void axp_adc_set_enabled(int adc_bits) { - /* Ignore no-op */ - if(adc_bits == axp.adc_enable) - return; + uint8_t xfer[3]; + xfer[0] = 0; + xfer[1] = AXP_REG_ADCENABLE2; + xfer[2] = 0; /* Compute the new register values */ const struct axp_adc_info* info = axp_adc_info; - uint8_t regs[2] = {0, 0}; for(int i = 0; i < NUM_ADC_CHANNELS; ++i) { - if(info[i].en_reg == 0xff) + if(!(adc_bits & (1 << i))) continue; - if(adc_bits & (1 << i)) - regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit; - } - - /* These ADCs share an enable bit */ - if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) { - adc_bits |= (1 << ADC_CHARGE_CURRENT); - adc_bits |= (1 << ADC_DISCHARGE_CURRENT); - } - - /* Enable required bits for battery power ADC */ - if(adc_bits & (1 << ADC_BATTERY_POWER)) { - regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit; - regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit; + if(info[i].en_reg == AXP_REG_ADCENABLE1) + xfer[0] |= info[i].en_bit; + else + xfer[2] |= info[i].en_bit; } /* Update the configuration */ - i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 2, ®s[0]); - axp.adc_enable = adc_bits; + i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 3, &xfer[0]); } int axp_adc_get_rate(void) @@ -468,38 +367,26 @@ static const int chargecurrent_tbl[] = { 1080, 1160, 1240, 1320, }; -static const int chargecurrent_tblsz = sizeof(chargecurrent_tbl)/sizeof(int); - -void axp_set_charge_current(int maxcurrent) +void axp_set_charge_current(int current_mA) { - /* Find the charge current just higher than maxcurrent */ - int value = 0; - while(value < chargecurrent_tblsz && - chargecurrent_tbl[value] <= maxcurrent) - ++value; - - /* Select the next lower current, the greatest current <= maxcurrent */ - if(value >= chargecurrent_tblsz) - value = chargecurrent_tblsz - 1; - else if(value > 0) - --value; - - /* Don't issue i2c write if desired setting is already in use */ - if(value == axp.chargecurrent_setting) - return; + /* find greatest charging current not exceeding requested current */ + unsigned int index = 0; + while(index < ARRAYLEN(chargecurrent_tbl)-1 && + chargecurrent_tbl[index+1] <= current_mA) + ++index; - /* Update register */ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, - AXP_REG_CHARGECONTROL1, 0x0f, value, NULL); - axp.chargecurrent_setting = value; + AXP_REG_CHARGECONTROL1, 0x0f, index, NULL); } int axp_get_charge_current(void) { - if(axp.chargecurrent_setting < 0) - return chargecurrent_tbl[0]; - else - return chargecurrent_tbl[axp.chargecurrent_setting]; + int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_CHARGECONTROL1); + if(ret < 0) + ret = 0; + + return chargecurrent_tbl[ret & 0x0f]; } void axp_power_off(void) @@ -511,7 +398,6 @@ void axp_power_off(void) #ifndef BOOTLOADER enum { - AXP_DEBUG_CHIP_ID, AXP_DEBUG_BATTERY_STATUS, AXP_DEBUG_INPUT_STATUS, AXP_DEBUG_CHARGE_CURRENT, @@ -585,12 +471,6 @@ static const char* axp_debug_menu_get_name(int item, void* data, } switch(item) { - case AXP_DEBUG_CHIP_ID: { - snprintf(buf, buflen, "Chip ID: %d (%02x) [Driver: AXP%d]", - axp.chip_id, axp.chip_id, HAVE_AXP_PMU); - return buf; - } break; - case AXP_DEBUG_BATTERY_STATUS: { switch(axp_battery_status()) { case AXP_BATT_FULL: diff --git a/firmware/drivers/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__); |