diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 9e4621b749..fb8575ec62 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -79,6 +79,7 @@ #include "peakmeter.h" #endif #include "logfdisp.h" +#include "core_alloc.h" #if CONFIG_CODEC == SWCODEC #include "pcmbuf.h" #include "buffering.h" @@ -416,6 +417,22 @@ static bool dbg_buffering_thread(void) #endif /* CONFIG_CODEC */ #endif /* HAVE_LCD_BITMAP */ +static const char* bf_getname(int selected_item, void *data, + char *buffer, size_t buffer_len) +{ + (void)data; + core_print_block_at(selected_item, buffer, buffer_len); + return buffer; +} + +static bool dbg_buflib_allocs(void) +{ + struct simplelist_info info; + simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); + info.get_name = bf_getname; + return simplelist_show_list(&info); +} + #if (CONFIG_PLATFORM & PLATFORM_NATIVE) static const char* dbg_partitions_getname(int selected_item, void *data, char *buffer, size_t buffer_len) @@ -2040,6 +2057,7 @@ static const struct the_menu_item menuitems[] = { { "pm histogram", peak_meter_histogram}, #endif /* PM_DEBUG */ #endif /* HAVE_LCD_BITMAP */ + { "View buflib allocs", dbg_buflib_allocs }, #ifndef SIMULATOR #if CONFIG_TUNER { "FM Radio", dbg_fm_radio }, diff --git a/apps/dsp.c b/apps/dsp.c index 3cff1918d7..a728dd75ea 100644 --- a/apps/dsp.c +++ b/apps/dsp.c @@ -30,7 +30,7 @@ #include "settings.h" #include "replaygain.h" #include "tdspeed.h" -#include "buffer.h" +#include "core_alloc.h" #include "fixedpoint.h" #include "fracmul.h" @@ -325,10 +325,16 @@ void dsp_timestretch_enable(bool enabled) { if (enabled) { + int handle; /* Set up timestretch buffers */ big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; big_sample_buf = small_resample_buf; - big_resample_buf = (int32_t *) buffer_alloc(big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); + handle = core_alloc("resample buf", + big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); + if (handle > 0) + big_resample_buf = core_get_data(handle); + else + big_sample_buf_count = 0; } else { diff --git a/apps/filetypes.c b/apps/filetypes.c index 17a16db4ec..c52c734a1d 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -36,7 +36,7 @@ #include "dir.h" #include "file.h" #include "splash.h" -#include "buffer.h" +#include "core_alloc.h" #include "icons.h" #include "logf.h" @@ -183,12 +183,14 @@ static int filetype_count = 0; static unsigned char highest_attr = 0; static int viewer_count = 0; +static int strdup_handle, strdup_bufsize, strdup_cur_idx; static char *filetypes_strdup(char* string) { - char *buffer = (char*)buffer_alloc(strlen(string)+1); - strcpy(buffer, string); + char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; + strdup_cur_idx += strlcpy(buffer, string, strdup_bufsize-strdup_cur_idx)+1; return buffer; } + static char *filetypes_store_plugin(char *plugin, int n) { int i; @@ -219,7 +221,7 @@ static int find_extension(const char* extension) } static void read_builtin_types(void); -static void read_config(const char* config_file); +static void read_config(int fd); #ifdef HAVE_LCD_COLOR /* Colors file format is similar to icons: * ext:hex_color @@ -312,16 +314,28 @@ void filetype_init(void) filetypes[0].attr = 0; filetypes[0].icon = Icon_Folder; + /* estimate bufsize with the filesize, will not be larger */ viewer_count = 0; filetype_count = 1; + + int fd = open(VIEWERS_CONFIG, O_RDONLY); + if (fd < 0) + return; + + strdup_bufsize = filesize(fd); + strdup_handle = core_alloc("filetypes", strdup_bufsize); + if (strdup_handle <= 0) + return; read_builtin_types(); - read_config(VIEWERS_CONFIG); + read_config(fd); #ifdef HAVE_LCD_BITMAP read_viewer_theme_file(); #endif #ifdef HAVE_LCD_COLOR read_color_theme_file(); #endif + close(fd); + core_shrink(strdup_handle, core_get_data(strdup_handle), strdup_cur_idx); } /* remove all white spaces from string */ @@ -355,13 +369,10 @@ static void read_builtin_types(void) } } -static void read_config(const char* config_file) +static void read_config(int fd) { char line[64], *s, *e; char *extension, *plugin; - int fd = open(config_file, O_RDONLY); - if (fd < 0) - return; /* config file is in the format ,, ignore line if either of the first two are missing */ diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c index fbedbb96fd..d136f90aa5 100644 --- a/apps/gui/skin_engine/skin_engine.c +++ b/apps/gui/skin_engine/skin_engine.c @@ -48,10 +48,9 @@ void theme_init_buffer(void) skins_initialising = false; } #else -static char *skin_buffer = NULL; +static char skin_buffer[SKIN_BUFFER_SIZE]; void theme_init_buffer(void) { - skin_buffer = buffer_alloc(SKIN_BUFFER_SIZE); skins_initialising = false; } #endif diff --git a/apps/main.c b/apps/main.c index 9cb724562c..cc9c9e8d8e 100644 --- a/apps/main.c +++ b/apps/main.c @@ -53,7 +53,7 @@ #include "language.h" #include "wps.h" #include "playlist.h" -#include "buffer.h" +#include "core_alloc.h" #include "rolo.h" #include "screens.h" #include "usb_screen.h" @@ -337,7 +337,7 @@ static void init_tagcache(void) static void init(void) { system_init(); - buffer_init(); + core_allocator_init(); kernel_init(); #ifdef APPLICATION paths_init(); @@ -400,9 +400,6 @@ static void init(void) global_settings.mdb_shape, global_settings.mdb_enable, global_settings.superbass); - - /* audio_init must to know the size of voice buffer so init voice first */ - talk_init(); #endif /* CONFIG_CODEC != SWCODEC */ scrobbler_init(); @@ -428,7 +425,7 @@ static void init(void) #endif system_init(); - buffer_init(); + core_allocator_init(); kernel_init(); #ifdef HAVE_ADJUSTABLE_CPU_FREQ @@ -684,9 +681,6 @@ static void init(void) global_settings.mdb_shape, global_settings.mdb_enable, global_settings.superbass); - - /* audio_init must to know the size of voice buffer so init voice first */ - talk_init(); #endif /* CONFIG_CODEC != SWCODEC */ CHART(">audio_init"); diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c index c5758d1274..e88317aeab 100644 --- a/apps/menus/main_menu.c +++ b/apps/menus/main_menu.c @@ -182,7 +182,7 @@ static const char* info_getname(int selected_item, void *data, case INFO_BUFFER: /* buffer */ { - long kib = buffer_available() / 1024; /* to KiB */ + long kib = audio_buffer_available() / 1024; /* to KiB */ output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true); snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1); } @@ -272,7 +272,7 @@ static int info_speak_item(int selected_item, void * data) case INFO_BUFFER: /* buffer */ { talk_id(LANG_BUFFER_STAT, false); - long kib = buffer_available() / 1024; /* to KiB */ + long kib = audio_buffer_available() / 1024; /* to KiB */ output_dyn_value(NULL, 0, kib, kbyte_units, true); break; } diff --git a/apps/mpeg.c b/apps/mpeg.c index 6dd55b741c..595f943545 100644 --- a/apps/mpeg.c +++ b/apps/mpeg.c @@ -35,7 +35,7 @@ #include "thread.h" #include "errno.h" #include "mp3data.h" -#include "buffer.h" +#include "core_alloc.h" #include "mp3_playback.h" #include "talk.h" #include "sound.h" @@ -145,7 +145,8 @@ static unsigned int mpeg_errno; static bool playing = false; /* We are playing an MP3 stream */ static bool is_playing = false; /* We are (attempting to) playing MP3 files */ static bool paused; /* playback is paused */ -static char* mpeg_audiobuf; /* the audio buffer */ +static int audiobuf_handle; /* handle to the audio buffer */ +static char* mpeg_audiobuf; /* poiunter to the audio buffer */ static long audiobuflen; /* length of the audio buffer */ #ifdef SIMULATOR @@ -491,15 +492,27 @@ unsigned long mpeg_get_last_header(void) #endif /* !SIMULATOR */ } - +/* Buffer must not move. And not shrink for now */ +static struct buflib_callbacks ops = { NULL, NULL }; unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) { (void)talk_buf; /* always grab the voice buffer for now */ - audio_hard_stop(); if (buffer_size) /* special case for talk_init() */ - return buffer_get_buffer(buffer_size); - return NULL; + { + size_t bufsize; + audio_hard_stop(); + /* audio_hard_stop() frees audiobuf, so re-aquire */ + audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); + audiobuflen = bufsize; + *buffer_size = audiobuflen; + } + mpeg_audiobuf = core_get_data(audiobuf_handle); + + if (!buffer_size) /* special case for talk_init() */ + talkbuf_init(mpeg_audiobuf); + + return mpeg_audiobuf; } @@ -2659,17 +2672,26 @@ void audio_set_recording_options(struct audio_recording_options *options) #endif /* SIMULATOR */ #endif /* CONFIG_CODEC == MAS3587F */ +size_t audio_buffer_available(void) +{ + if (audiobuf_handle > 0) + return audiobuflen; + return core_available(); +} + static void audio_reset_buffer(void) { - size_t bufsize; /* dont break strict-aliasing */ talk_buffer_steal(); /* will use the mp3 buffer */ - /* release buffer on behalf of any audio_get_buffer() caller, - * non-fatal if there was none */ - buffer_release_buffer(0); - /* re-aquire */ - mpeg_audiobuf = buffer_get_buffer(&bufsize); - audiobuflen = bufsize; + /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ + if (!audiobuf_handle) + { + size_t bufsize; /* dont break strict-aliasing */ + audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); + mpeg_audiobuf = core_get_data(audiobuf_handle); + audiobuflen = bufsize; + } + talkbuf_init(mpeg_audiobuf); } void audio_play(long offset) @@ -2742,7 +2764,11 @@ void audio_hard_stop(void) audio_stop(); /* tell voice we obtain the buffer before freeing */ talk_buffer_steal(); - buffer_release_buffer(0); + if (audiobuf_handle > 0) + { + audiobuf_handle = core_free(audiobuf_handle); + mpeg_audiobuf = NULL; + } } void audio_pause(void) @@ -2899,13 +2925,15 @@ void audio_init(void) mpeg_errno = 0; /* cuesheet support */ if (global_settings.cuesheet) - curr_cuesheet = (struct cuesheet*)buffer_alloc(sizeof(struct cuesheet)); + { + int handle = core_alloc("cuesheet", sizeof(struct cuesheet)); + if (handle > 0) + curr_cuesheet = core_get_data(handle); + } + + talk_init(); + audio_reset_buffer(); - size_t bufsize; /* don't break strict-aliasing */ - mpeg_audiobuf = buffer_get_buffer(&bufsize); - audiobuflen = bufsize; - /* give voice buffer until we start to play */ - talkbuf_init(mpeg_audiobuf); #ifndef SIMULATOR queue_init(&mpeg_queue, true); #endif /* !SIMULATOR */ diff --git a/apps/playback.c b/apps/playback.c index 7dad08644a..e35d652ffb 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -24,7 +24,7 @@ #include "system.h" #include "kernel.h" #include "panic.h" -#include "buffer.h" +#include "core_alloc.h" #include "sound.h" #include "ata.h" #include "usb.h" @@ -732,6 +732,18 @@ static void scratch_mem_init(void *mem) } } +/* Buffer must not move. And not shrink for now */ +static struct buflib_callbacks ops = { NULL, NULL }; +static int audiobuf_handle; +static size_t filebuflen; + +size_t audio_buffer_available(void) +{ + if (audiobuf_handle > 0) /* if allocated return what we got */ + return filebuflen; + return core_available(); +} + /* Set up the audio buffer for playback */ static void audio_reset_buffer(void) { @@ -743,16 +755,17 @@ static void audio_reset_buffer(void) /* see audio_get_recording_buffer if this is modified */ logf("%s()", __func__); - /* release the buffer on behalf of any caller of audio_get_buffer() */ - buffer_release_buffer(0); - /* If the setup of anything allocated before the file buffer is changed, do check the adjustments after the buffer_alloc call as it will likely be affected and need sliding over */ /* Initially set up file buffer as all space available */ - size_t filebuflen, allocsize; - unsigned char *filebuf = buffer_get_buffer(&filebuflen); + size_t allocsize; + if (audiobuf_handle > 0) + audiobuf_handle = core_free(audiobuf_handle); + + audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); + unsigned char *filebuf = core_get_data(audiobuf_handle); /* Subtract whatever voice needs */ allocsize = talkbuf_init(filebuf); @@ -3324,7 +3337,8 @@ void audio_hard_stop(void) #ifdef PLAYBACK_VOICE voice_stop(); #endif - buffer_release_buffer(0); + if (audiobuf_handle > 0) + audiobuf_handle = core_free(audiobuf_handle); } /* Resume playback if paused */ @@ -3447,6 +3461,14 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) return NULL; } + /* make sure buffer is freed and re-allocated to simplify code below + * (audio_hard_stop() likely has done that already) */ + if (audiobuf_handle > 0) + audiobuf_handle = core_free(audiobuf_handle); + + audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); + buf = core_get_data(audiobuf_handle); + if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED || !talk_voice_required()) { @@ -3464,27 +3486,24 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) talk_buffer_steal(); buffer_state = AUDIOBUF_STATE_TRASHED; } - buf = buffer_get_buffer(buffer_size); } else { + logf("get buffer: audio"); /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or still AUDIOBUF_STATE_INITIALIZED */ /* Skip talk buffer and move pcm buffer to end to maximize available contiguous memory - no audio running means voice will not need the swap space */ - size_t siz, talkbuf_size; - logf("get buffer: audio"); - /* call buffer_get_buffer() to make use of the locking mechanism */ - buf = buffer_get_buffer(&siz); + size_t talkbuf_size; buf += talkbuf_size = talkbuf_init(buf); - siz -= talkbuf_size; - siz -= voicebuf_init(buf + siz); - *buffer_size = siz; + filebuflen -= talkbuf_size; + filebuflen -= voicebuf_init(buf + filebuflen); buffer_state = AUDIOBUF_STATE_VOICED_ONLY; } + *buffer_size = filebuflen; return buf; } @@ -3492,11 +3511,8 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) /* Stop audio, voice and obtain all available buffer space */ unsigned char * audio_get_recording_buffer(size_t *buffer_size) { - talk_buffer_steal(); audio_hard_stop(); - - buffer_state = AUDIOBUF_STATE_TRASHED; - return buffer_get_buffer(buffer_size); + return audio_get_buffer(true, buffer_size); } #endif /* HAVE_RECORDING */ diff --git a/apps/playlist.c b/apps/playlist.c index 3d3b5f44f8..77d8141af8 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -84,7 +84,7 @@ #include "status.h" #include "applimits.h" #include "screens.h" -#include "buffer.h" +#include "core_alloc.h" #include "misc.h" #include "filefuncs.h" #include "button.h" @@ -1929,6 +1929,7 @@ static int rotate_index(const struct playlist_info* playlist, int index) */ void playlist_init(void) { + int handle; struct playlist_info* playlist = ¤t_playlist; mutex_init(¤t_playlist_mutex); @@ -1940,18 +1941,19 @@ void playlist_init(void) playlist->fd = -1; playlist->control_fd = -1; playlist->max_playlist_size = global_settings.max_files_in_playlist; - playlist->indices = buffer_alloc( - playlist->max_playlist_size * sizeof(int)); + handle = core_alloc("playlist idx", playlist->max_playlist_size * sizeof(int)); + playlist->indices = core_get_data(handle); playlist->buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - playlist->buffer = buffer_alloc(playlist->buffer_size); + handle = core_alloc("playlist buf", playlist->buffer_size); + playlist->buffer = core_get_data(handle); playlist->control_mutex = ¤t_playlist_mutex; empty_playlist(playlist, true); #ifdef HAVE_DIRCACHE - playlist->filenames = buffer_alloc( - playlist->max_playlist_size * sizeof(int)); + handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int)); + playlist->filenames = core_get_data(handle); memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), @@ -3356,7 +3358,6 @@ int playlist_save(struct playlist_info* playlist, char *filename) char tmp_buf[MAX_PATH+1]; int result = 0; bool overwrite_current = false; - int* index_buf = NULL; char* old_buffer = NULL; size_t old_buffer_size = 0; @@ -3391,10 +3392,6 @@ int playlist_save(struct playlist_info* playlist, char *filename) } } - /* in_ram buffer is unused for m3u files so we'll use for storing - updated indices */ - index_buf = (int*)playlist->buffer; - /* use temporary pathname */ snprintf(path, sizeof(path), "%s_temp", playlist->filename); overwrite_current = true; @@ -3453,7 +3450,7 @@ int playlist_save(struct playlist_info* playlist, char *filename) } if (overwrite_current) - index_buf[count] = lseek(fd, 0, SEEK_CUR); + playlist->seek_buf[count] = lseek(fd, 0, SEEK_CUR); if (fdprintf(fd, "%s\n", tmp_buf) < 0) { @@ -3498,7 +3495,7 @@ int playlist_save(struct playlist_info* playlist, char *filename) { if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK)) { - playlist->indices[index] = index_buf[count]; + playlist->indices[index] = playlist->seek_buf[count]; count++; } index = (index+1)%playlist->amount; diff --git a/apps/playlist.h b/apps/playlist.h index d994f6e800..f14b5c6460 100644 --- a/apps/playlist.h +++ b/apps/playlist.h @@ -85,7 +85,11 @@ struct playlist_info int max_playlist_size; /* Max number of files in playlist. Mirror of global_settings.max_files_in_playlist */ bool in_ram; /* playlist stored in ram (dirplay) */ - char *buffer; /* buffer for in-ram playlists */ + + union { + char *buffer; /* buffer for in-ram playlists */ + int *seek_buf; /* buffer for seeks in real playlists */ + }; int buffer_size; /* size of buffer */ int buffer_end_pos; /* last position where buffer was written */ int index; /* index of current playing track */ diff --git a/apps/scrobbler.c b/apps/scrobbler.c index 6ed9bbb037..a6307d5dd7 100644 --- a/apps/scrobbler.c +++ b/apps/scrobbler.c @@ -30,7 +30,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging #include "metadata.h" #include "kernel.h" #include "audio.h" -#include "buffer.h" +#include "core_alloc.h" #include "settings.h" #include "ata_idle_notify.h" #include "filefuncs.h" @@ -52,7 +52,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging /* longest entry I've had is 323, add a safety margin */ #define SCROBBLER_CACHE_LEN 512 -static char* scrobbler_cache; +static int scrobbler_cache; static int cache_pos; static struct mp3entry scrobbler_entry; @@ -139,11 +139,16 @@ static void write_cache(void) if(fd >= 0) { logf("SCROBBLER: writing %d entries", cache_pos); - + /* copy data to temporary storage in case data moves during I/O */ + char temp_buf[SCROBBLER_CACHE_LEN]; for ( i=0; i < cache_pos; i++ ) { logf("SCROBBLER: write %d", i); - fdprintf(fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i)); + char* scrobbler_buf = core_get_data(scrobbler_cache); + ssize_t len = strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i), + sizeof(temp_buf)); + if (write(fd, temp_buf, len) != len) + break; } close(fd); } @@ -170,6 +175,7 @@ static void add_to_cache(unsigned long play_length) int ret; char rating = 'S'; /* Skipped */ + char* scrobbler_buf = core_get_data(scrobbler_cache); logf("SCROBBLER: add_to_cache[%d]", cache_pos); @@ -178,7 +184,7 @@ static void add_to_cache(unsigned long play_length) if (scrobbler_entry.tracknum > 0) { - ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), + ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), SCROBBLER_CACHE_LEN, "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n", scrobbler_entry.artist, @@ -190,7 +196,7 @@ static void add_to_cache(unsigned long play_length) (long)timestamp, scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); } else { - ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), + ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), SCROBBLER_CACHE_LEN, "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n", scrobbler_entry.artist, @@ -248,7 +254,7 @@ int scrobbler_init(void) if(!global_settings.audioscrobbler) return -1; - scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); + scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); cache_pos = 0; diff --git a/apps/tagcache.c b/apps/tagcache.c index 52e059a04a..753675f906 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -74,7 +74,7 @@ #include "usb.h" #include "metadata.h" #include "tagcache.h" -#include "buffer.h" +#include "core_alloc.h" #include "crc32.h" #include "misc.h" #include "settings.h" @@ -3082,6 +3082,7 @@ static bool commit(void) return true; } +static int tempbuf_handle; static void allocate_tempbuf(void) { /* Yeah, malloc would be really nice now :) */ @@ -3089,7 +3090,8 @@ static void allocate_tempbuf(void) tempbuf_size = 32*1024*1024; tempbuf = malloc(tempbuf_size); #else - tempbuf = buffer_get_buffer(&tempbuf_size); + tempbuf_handle = core_alloc_maximum("tc tempbuf", &tempbuf_size, NULL); + tempbuf = core_get_data(tempbuf_handle); #endif } @@ -3101,7 +3103,7 @@ static void free_tempbuf(void) #ifdef __PCTOOL__ free(tempbuf); #else - buffer_release_buffer(0); + tempbuf_handle = core_free(tempbuf_handle); #endif tempbuf = NULL; tempbuf_size = 0; @@ -3829,9 +3831,10 @@ static bool allocate_tagcache(void) * Now calculate the required cache size plus * some extra space for alignment fixes. */ - tc_stat.ramcache_allocated = tcmh.tch.datasize + 128 + TAGCACHE_RESERVE + + tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); - ramcache_hdr = buffer_alloc(tc_stat.ramcache_allocated + 128); + int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated); + ramcache_hdr = core_get_data(handle); memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); logf("tagcache: %d bytes allocated.", tc_stat.ramcache_allocated); @@ -3845,7 +3848,7 @@ static bool tagcache_dumpload(void) struct statefile_header shdr; int fd, rc; long offpos; - int i; + int i, handle; fd = open(TAGCACHE_STATEFILE, O_RDONLY); if (fd < 0) @@ -3855,7 +3858,6 @@ static bool tagcache_dumpload(void) } /* Check the statefile memory placement */ - ramcache_hdr = buffer_alloc(0); rc = read(fd, &shdr, sizeof(struct statefile_header)); if (rc != sizeof(struct statefile_header) || shdr.magic != TAGCACHE_STATEFILE_MAGIC @@ -3867,13 +3869,14 @@ static bool tagcache_dumpload(void) return false; } - offpos = (long)ramcache_hdr - (long)shdr.hdr; /* Lets allocate real memory and load it */ - ramcache_hdr = buffer_alloc(shdr.tc_stat.ramcache_allocated); + handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated); + ramcache_hdr = core_get_data(handle); rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); close(fd); + offpos = (long)ramcache_hdr - (long)shdr.hdr; if (rc != shdr.tc_stat.ramcache_allocated) { logf("read failure!"); diff --git a/apps/tagtree.c b/apps/tagtree.c index 5012c084d0..0d4330bac8 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -44,7 +44,7 @@ #include "playlist.h" #include "keyboard.h" #include "gui/list.h" -#include "buffer.h" +#include "core_alloc.h" #include "yesno.h" #include "misc.h" #include "filetypes.h" @@ -176,9 +176,14 @@ static int current_entry_count; static struct tree_context *tc; /* a few memory alloc helper */ +static int tagtree_handle; +static size_t tagtree_bufsize, tagtree_buf_used; static void* tagtree_alloc(size_t size) { - return buffer_alloc(size); + char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; + size = ALIGN_UP(size, sizeof(void*)); + tagtree_buf_used += size; + return buf; } static void* tagtree_alloc0(size_t size) @@ -1035,6 +1040,7 @@ void tagtree_init(void) menu_count = 0; menu = NULL; rootmenu = -1; + tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL); parse_menu(FILE_SEARCH_INSTRUCTIONS); /* If no root menu is set, assume it's the first single menu @@ -1046,6 +1052,8 @@ void tagtree_init(void) add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); + + core_shrink(tagtree_handle, core_get_data(tagtree_handle), tagtree_buf_used); } static bool show_search_progress(bool init, int count) diff --git a/apps/talk.c b/apps/talk.c index 0c3b769d82..29657385a7 100644 --- a/apps/talk.c +++ b/apps/talk.c @@ -27,7 +27,6 @@ #include #include "string-extra.h" #include "file.h" -#include "buffer.h" #include "system.h" #include "kernel.h" #include "settings.h" @@ -711,7 +710,7 @@ void talk_init(void) /* test if we can open and if it fits in the audiobuffer */ - size_t audiobufsz = buffer_available(); + size_t audiobufsz = audio_buffer_available(); if (voicefile_size <= audiobufsz) { has_voicefile = true; } else { diff --git a/apps/tdspeed.c b/apps/tdspeed.c index b940c928fc..476995a271 100644 --- a/apps/tdspeed.c +++ b/apps/tdspeed.c @@ -25,7 +25,7 @@ #include #include #include "sound.h" -#include "buffer.h" +#include "core_alloc.h" #include "system.h" #include "tdspeed.h" #include "settings.h" @@ -56,21 +56,32 @@ static int32_t *outbuf[2] = { NULL, NULL }; void tdspeed_init(void) { + int handle; + if (!global_settings.timestretch_enabled) return; /* Allocate buffers */ if (overlap_buffer[0] == NULL) - overlap_buffer[0] = (int32_t *)buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t)); - + { + handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t)); + overlap_buffer[0] = core_get_data(handle); + } if (overlap_buffer[1] == NULL) - overlap_buffer[1] = (int32_t *)buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t)); - + { + handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t)); + overlap_buffer[1] = core_get_data(handle); + } if (outbuf[0] == NULL) - outbuf[0] = (int32_t *)buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t)); - + { + handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + outbuf[0] = core_get_data(handle); + } if (outbuf[1] == NULL) - outbuf[1] = (int32_t *)buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + { + handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + outbuf[1] = core_get_data(handle); + } } diff --git a/apps/tree.c b/apps/tree.c index e981aeecfc..211ddb2f9b 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -46,7 +46,7 @@ #include "keyboard.h" #include "bookmark.h" #include "onplay.h" -#include "buffer.h" +#include "core_alloc.h" #include "power.h" #include "action.h" #include "talk.h" @@ -1002,6 +1002,7 @@ int rockbox_browse(struct browse_context *browse) void tree_mem_init(void) { /* initialize tree context struct */ + int handle; struct tree_cache* cache = &tc.cache; memset(&tc, 0, sizeof(tc)); tc.dirfilter = &global_settings.dirfilter; @@ -1009,10 +1010,12 @@ void tree_mem_init(void) cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - cache->name_buffer = buffer_alloc(cache->name_buffer_size); + handle = core_alloc("tree names", cache->name_buffer_size); + cache->name_buffer = core_get_data(handle); cache->max_entries = global_settings.max_files_in_dir; - cache->entries = buffer_alloc(cache->max_entries*(sizeof(struct entry))); + handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry))); + cache->entries = core_get_data(handle); tree_get_filetypes(&filetypes, &filetypes_count); } diff --git a/firmware/SOURCES b/firmware/SOURCES index f685ed7dc7..4517c37e7f 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -2,6 +2,8 @@ ata_idle_notify.c events.c backlight.c buffer.c +buflib.c +core_alloc.c general.c load_code.c powermgmt.c diff --git a/firmware/buflib.c b/firmware/buflib.c new file mode 100644 index 0000000000..51cf86bf5b --- /dev/null +++ b/firmware/buflib.c @@ -0,0 +1,777 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* This is a memory allocator designed to provide reasonable management of free +* space and fast access to allocated data. More than one allocator can be used +* at a time by initializing multiple contexts. +* +* Copyright (C) 2009 Andrew Mahone +* Copyright (C) 2011 Thomas Martitz +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +****************************************************************************/ + +#include /* for abs() */ +#include /* for snprintf() */ +#include "buflib.h" +#include "string-extra.h" /* strlcpy() */ +#include "debug.h" +#include "buffer.h" +#include "system.h" /* for ALIGN_*() */ + +/* The main goal of this design is fast fetching of the pointer for a handle. + * For that reason, the handles are stored in a table at the end of the buffer + * with a fixed address, so that returning the pointer for a handle is a simple + * table lookup. To reduce the frequency with which allocated blocks will need + * to be moved to free space, allocations grow up in address from the start of + * the buffer. The buffer is treated as an array of union buflib_data. Blocks + * start with a length marker, which is included in their length. Free blocks + * are marked by negative length. Allocated blocks have a positiv length marker, + * and additional metadata forllowing that: It follows a pointer + * (union buflib_data*) to the corresponding handle table entry. so that it can + * be quickly found and updated during compaction. After that follows + * the pointer to the struct buflib_callbacks associated with this allocation + * (may be NULL). That pointer follows a variable length character array + * containing the nul-terminated string identifier of the allocation. After this + * array there's a length marker for the length of the character array including + * this length marker (counted in n*sizeof(union buflib_data)), which allows + * to find the start of the character array (and therefore the start of the + * entire block) when only the handle or payload start is known. + * + * Example: + * |<- alloc block #1 ->|<- unalloc block ->|<- alloc block #2 ->|<-handle table->| + * |L|H|C|cccc|L2|XXXXXX|-L|YYYYYYYYYYYYYYYY|L|H|C|cc|L2|XXXXXXXXXXXXX|AAA| + * + * L - length marker (negative if block unallocated) + * H - handle table enry pointer + * C - pointer to struct buflib_callbacks + * c - variable sized string identifier + * L2 - second length marker for string identifier + * X - actual payload + * Y - unallocated space + * + * A - pointer to start of payload (first X) in the handle table (may be null) + * + * The blocks can be walked by jumping the abs() of the L length marker, i.e. + * union buflib_data* L; + * for(L = start; L < end; L += abs(L->val)) { .... } + * + * + * The allocator functions are passed a context struct so that two allocators + * can be run, for example, one per core may be used, with convenience wrappers + * for the single-allocator case that use a predefined context. + */ + +#define B_ALIGN_DOWN(x) \ + ALIGN_DOWN(x, sizeof(union buflib_data)) + +#define B_ALIGN_UP(x) \ + ALIGN_UP(x, sizeof(union buflib_data)) + +#ifdef DEBUG + #include + #define BDEBUGF DEBUGF +#else + #define BDEBUGF(...) do { } while(0) +#endif + +/* Initialize buffer manager */ +void +buflib_init(struct buflib_context *ctx, void *buf, size_t size) +{ + union buflib_data *bd_buf = buf; + + /* Align on sizeof(buflib_data), to prevent unaligned access */ + ALIGN_BUFFER(bd_buf, size, sizeof(union buflib_data)); + size /= sizeof(union buflib_data); + /* The handle table is initialized with no entries */ + ctx->handle_table = bd_buf + size; + ctx->last_handle = bd_buf + size; + ctx->first_free_handle = bd_buf + size - 1; + ctx->first_free_block = bd_buf; + ctx->buf_start = bd_buf; + /* A marker is needed for the end of allocated data, to make sure that it + * does not collide with the handle table, and to detect end-of-buffer. + */ + ctx->alloc_end = bd_buf; + ctx->compact = true; + + BDEBUGF("buflib initialized with %d.%2d kiB", size / 1024, (size%1000)/10); +} + +/* Allocate a new handle, returning 0 on failure */ +static inline +union buflib_data* handle_alloc(struct buflib_context *ctx) +{ + union buflib_data *handle; + /* first_free_handle is a lower bound on free handles, work through the + * table from there until a handle containing NULL is found, or the end + * of the table is reached. + */ + for (handle = ctx->first_free_handle; handle >= ctx->last_handle; handle--) + if (!handle->alloc) + break; + /* If the search went past the end of the table, it means we need to extend + * the table to get a new handle. + */ + if (handle < ctx->last_handle) + { + if (handle >= ctx->alloc_end) + ctx->last_handle--; + else + return NULL; + } + handle->val = -1; + return handle; +} + +/* Free one handle, shrinking the handle table if it's the last one */ +static inline +void handle_free(struct buflib_context *ctx, union buflib_data *handle) +{ + handle->alloc = 0; + /* Update free handle lower bound if this handle has a lower index than the + * old one. + */ + if (handle > ctx->first_free_handle) + ctx->first_free_handle = handle; + if (handle == ctx->last_handle) + ctx->last_handle++; + else + ctx->compact = false; +} + +/* Get the start block of an allocation */ +static union buflib_data* handle_to_block(struct buflib_context* ctx, int handle) +{ + union buflib_data* name_field = + (union buflib_data*)buflib_get_name(ctx, handle); + + return name_field - 3; +} + +/* Shrink the handle table, returning true if its size was reduced, false if + * not + */ +static inline +bool +handle_table_shrink(struct buflib_context *ctx) +{ + bool rv; + union buflib_data *handle; + for (handle = ctx->last_handle; !(handle->alloc); handle++); + if (handle > ctx->first_free_handle) + ctx->first_free_handle = handle - 1; + rv = handle == ctx->last_handle; + ctx->last_handle = handle; + return rv; +} + + +/* If shift is non-zero, it represents the number of places to move + * blocks in memory. Calculate the new address for this block, + * update its entry in the handle table, and then move its contents. + * + * Returns false if moving was unsucessful + * (NULL callback or BUFLIB_CB_CANNOT_MOVE was returned) + */ +static bool +move_block(struct buflib_context* ctx, union buflib_data* block, int shift) +{ +#if 1 /* moving temporarily disabled */ + (void)ctx;(void)block;(void)shift; + return false; +#else + char* new_start; + union buflib_data *new_block, *tmp = block[1].handle; + struct buflib_callbacks *ops = block[2].ops; + if (ops && !ops->move_callback) + return false; + + int handle = ctx->handle_table - tmp; + BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name, + handle, shift, shift*sizeof(union buflib_data)); + new_block = block + shift; + new_start = tmp->alloc + shift*sizeof(union buflib_data); + /* call the callback before moving */ + if (ops) + { + if (ops->move_callback(handle, tmp->alloc, new_start) + == BUFLIB_CB_CANNOT_MOVE) + return false; + } + tmp->alloc = new_start; /* update handle table */ + memmove(new_block, block, block->val * sizeof(union buflib_data)); + + return true; +#endif +} + +/* Compact allocations and handle table, adjusting handle pointers as needed. + * Return true if any space was freed or consolidated, false otherwise. + */ +static bool +buflib_compact(struct buflib_context *ctx) +{ + BDEBUGF("%s(): Compacting!\n", __func__); + union buflib_data *block; + int shift = 0, len; + /* Store the results of attempting to shrink the handle table */ + bool ret = handle_table_shrink(ctx); + for(block = ctx->first_free_block; block != ctx->alloc_end; block += len) + { + len = block->val; + /* This block is free, add its length to the shift value */ + if (len < 0) + { + shift += len; + len = -len; + continue; + } + /* attempt to fill any hole */ + if (-ctx->first_free_block->val > block->val) + { + intptr_t size = ctx->first_free_block->val; + if (move_block(ctx, block, ctx->first_free_block - block)) + { + /* moving was successful. Mark the next block as the new + * first_free_block and merge it with the free space + * that the move created */ + ctx->first_free_block += block->val; + ctx->first_free_block->val = size + block->val; + continue; + } + } + /* attempt move the allocation by shift */ + if (shift) + { + /* failing to move creates a hole, therefore mark this + * block as not allocated anymore and move first_free_block up */ + if (!move_block(ctx, block, shift)) + { + union buflib_data* hole = block + shift; + hole->val = shift; + if (ctx->first_free_block > hole) + ctx->first_free_block = hole; + shift = 0; + } + /* if move was successful, the just moved block is now + * possibly in place of the first free one, so move this thing up */ + else if (ctx->first_free_block == block+shift) + { + ctx->first_free_block += ctx->first_free_block->val; + ctx->first_free_block->val = shift; + } + } + } + /* Move the end-of-allocation mark, and return true if any new space has + * been freed. + */ + ctx->alloc_end += shift; + /* only move first_free_block up if it wasn't already by a hole */ + if (ctx->first_free_block > ctx->alloc_end) + ctx->first_free_block = ctx->alloc_end; + ctx->compact = true; + return ret || shift; +} + +/* Compact the buffer by trying both shrinking and moving. + * + * Try to move first. If unsuccesfull, try to shrink. If that was successful + * try to move once more as there might be more room now. + */ +static bool +buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) +{ + bool result = false; + /* if something compacted before already there will be no further gain */ + if (!ctx->compact) + result = buflib_compact(ctx); + if (!result) + { + union buflib_data* this; + for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val)) + { + if (this->val > 0 && this[2].ops + && this[2].ops->shrink_callback) + { + int ret; + int handle = ctx->handle_table - this[1].handle; + char* data = this[1].handle->alloc; + ret = this[2].ops->shrink_callback(handle, shrink_hints, + data, (char*)(this+this->val)-data); + result |= (ret == BUFLIB_CB_OK); + /* this might have changed in the callback (if + * it shrinked from the top), get it again */ + this = handle_to_block(ctx, handle); + } + } + /* shrinking was successful at least once, try compaction again */ + if (result) + result |= buflib_compact(ctx); + } + + return result; +} + +/* Shift buffered items by size units, and update handle pointers. The shift + * value must be determined to be safe *before* calling. + */ +static void +buflib_buffer_shift(struct buflib_context *ctx, int shift) +{ + memmove(ctx->buf_start + shift, ctx->buf_start, + (ctx->alloc_end - ctx->buf_start) * sizeof(union buflib_data)); + union buflib_data *handle; + for (handle = ctx->last_handle; handle < ctx->handle_table; handle++) + if (handle->alloc) + handle->alloc += shift; + ctx->first_free_block += shift; + ctx->buf_start += shift; + ctx->alloc_end += shift; +} + +/* Shift buffered items up by size bytes, or as many as possible if size == 0. + * Set size to the number of bytes freed. + */ +void* +buflib_buffer_out(struct buflib_context *ctx, size_t *size) +{ + if (!ctx->compact) + buflib_compact(ctx); + size_t avail = ctx->last_handle - ctx->alloc_end; + size_t avail_b = avail * sizeof(union buflib_data); + if (*size && *size < avail_b) + { + avail = (*size + sizeof(union buflib_data) - 1) + / sizeof(union buflib_data); + avail_b = avail * sizeof(union buflib_data); + } + *size = avail_b; + void *ret = ctx->buf_start; + buflib_buffer_shift(ctx, avail); + return ret; +} + +/* Shift buffered items down by size bytes */ +void +buflib_buffer_in(struct buflib_context *ctx, int size) +{ + size /= sizeof(union buflib_data); + buflib_buffer_shift(ctx, -size); +} + +/* Allocate a buffer of size bytes, returning a handle for it */ +int +buflib_alloc(struct buflib_context *ctx, size_t size) +{ + return buflib_alloc_ex(ctx, size, "", NULL); +} + +/* Allocate a buffer of size bytes, returning a handle for it. + * + * The additional name parameter gives the allocation a human-readable name, + * the ops parameter points to caller-implemented callbacks for moving and + * shrinking. NULL for default callbacks (which do nothing but don't + * prevent moving or shrinking) + */ + +int +buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name, + struct buflib_callbacks *ops) +{ + union buflib_data *handle, *block; + size_t name_len = name ? B_ALIGN_UP(strlen(name)+1) : 0; + bool last; + /* This really is assigned a value before use */ + int block_len; + size += name_len; + size = (size + sizeof(union buflib_data) - 1) / + sizeof(union buflib_data) + /* add 4 objects for alloc len, pointer to handle table entry and + * name length, and the ops pointer */ + + 4; +handle_alloc: + handle = handle_alloc(ctx); + if (!handle) + { + /* If allocation has failed, and compaction has succeded, it may be + * possible to get a handle by trying again. + */ + if (!ctx->compact && buflib_compact(ctx)) + goto handle_alloc; + else + { /* first try to shrink the alloc before the handle table + * to make room for new handles */ + int handle = ctx->handle_table - ctx->last_handle; + union buflib_data* last_block = handle_to_block(ctx, handle); + struct buflib_callbacks* ops = last_block[2].ops; + if (ops && ops->shrink_callback) + { + char *data = buflib_get_data(ctx, handle); + unsigned hint = BUFLIB_SHRINK_POS_BACK | 10*sizeof(union buflib_data); + if (ops->shrink_callback(handle, hint, data, + (char*)(last_block+last_block->val)-data) == BUFLIB_CB_OK) + { /* retry one more time */ + goto handle_alloc; + } + } + return 0; + } + } + +buffer_alloc: + /* need to re-evaluate last before the loop because the last allocation + * possibly made room in its front to fit this, so last would be wrong */ + last = false; + for (block = ctx->first_free_block;;block += block_len) + { + /* If the last used block extends all the way to the handle table, the + * block "after" it doesn't have a header. Because of this, it's easier + * to always find the end of allocation by saving a pointer, and always + * calculate the free space at the end by comparing it to the + * last_handle pointer. + */ + if(block == ctx->alloc_end) + { + last = true; + block_len = ctx->last_handle - block; + if ((size_t)block_len < size) + block = NULL; + break; + } + block_len = block->val; + /* blocks with positive length are already allocated. */ + if(block_len > 0) + continue; + block_len = -block_len; + /* The search is first-fit, any fragmentation this causes will be + * handled at compaction. + */ + if ((size_t)block_len >= size) + break; + } + if (!block) + { + /* Try compacting if allocation failed */ + unsigned hint = BUFLIB_SHRINK_POS_FRONT | + ((size*sizeof(union buflib_data))&BUFLIB_SHRINK_SIZE_MASK); + if (buflib_compact_and_shrink(ctx, hint)) + { + goto buffer_alloc; + } else { + handle->val=1; + handle_free(ctx, handle); + return 0; + } + } + + /* Set up the allocated block, by marking the size allocated, and storing + * a pointer to the handle. + */ + union buflib_data *name_len_slot; + block->val = size; + block[1].handle = handle; + block[2].ops = ops; + strcpy(block[3].name, name); + name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len); + name_len_slot->val = 1 + name_len/sizeof(union buflib_data); + handle->alloc = (char*)(name_len_slot + 1); + /* If we have just taken the first free block, the next allocation search + * can save some time by starting after this block. + */ + if (block == ctx->first_free_block) + ctx->first_free_block += size; + block += size; + /* alloc_end must be kept current if we're taking the last block. */ + if (last) + ctx->alloc_end = block; + /* Only free blocks *before* alloc_end have tagged length. */ + else if ((size_t)block_len > size) + block->val = size - block_len; + /* Return the handle index as a positive integer. */ + return ctx->handle_table - handle; +} + +/* Finds the free block before block, and returns NULL if it's not free */ +static union buflib_data* +find_free_block_before(struct buflib_context *ctx, union buflib_data* block) +{ + union buflib_data *ret = ctx->first_free_block, + *next_block = ret; + + /* find the block that's before the current one */ + while (next_block < block) + { + ret = next_block; + next_block += abs(ret->val); + } + + /* If next_block == block, the above loop didn't go anywhere. If it did, + * and the block before this one is empty, that is the wanted one + */ + if (next_block == block && ret < block && ret->val < 0) + return ret; + /* otherwise, e.g. if ret > block, or if the buffer is compact, + * there's no free block before */ + return NULL; +} + +/* Free the buffer associated with handle_num. */ +int +buflib_free(struct buflib_context *ctx, int handle_num) +{ + union buflib_data *handle = ctx->handle_table - handle_num, + *freed_block = handle_to_block(ctx, handle_num), + *block, *next_block; + /* We need to find the block before the current one, to see if it is free + * and can be merged with this one. + */ + block = find_free_block_before(ctx, freed_block); + if (block) + { + block->val -= freed_block->val; + } + else + { + /* Otherwise, set block to the newly-freed block, and mark it free, before + * continuing on, since the code below exects block to point to a free + * block which may have free space after it. + */ + block = freed_block; + block->val = -block->val; + } + next_block = block - block->val; + /* Check if we are merging with the free space at alloc_end. */ + if (next_block == ctx->alloc_end) + ctx->alloc_end = block; + /* Otherwise, the next block might still be a "normal" free block, and the + * mid-allocation free means that the buffer is no longer compact. + */ + else { + ctx->compact = false; + if (next_block->val < 0) + block->val += next_block->val; + } + handle_free(ctx, handle); + handle->alloc = NULL; + /* If this block is before first_free_block, it becomes the new starting + * point for free-block search. + */ + if (block < ctx->first_free_block) + ctx->first_free_block = block; + + return 0; /* unconditionally */ +} + +/* Return the maximum allocatable memory in bytes */ +size_t +buflib_available(struct buflib_context* ctx) +{ + /* subtract 5 elements for + * val, handle, name_len, ops and the handle table entry*/ + ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5); + diff -= 16; /* space for future handles */ + diff *= sizeof(union buflib_data); /* make it bytes */ + diff -= 16; /* reserve 16 for the name */ + + if (diff > 0) + return diff; + else + return 0; +} + +/* + * Allocate all available (as returned by buflib_available()) memory and return + * a handle to it + * + * This grabs a lock which can only be unlocked by buflib_free() or + * buflib_shrink(), to protect from further allocations (which couldn't be + * serviced anyway). + */ +int +buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size, struct buflib_callbacks *ops) +{ + /* limit name to 16 since that's what buflib_available() accounts for it */ + char buf[16]; + *size = buflib_available(ctx); + strlcpy(buf, name, sizeof(buf)); + + return buflib_alloc_ex(ctx, *size, buf, ops); +} + +/* Shrink the allocation indicated by the handle according to new_start and + * new_size. Grow is not possible, therefore new_start and new_start + new_size + * must be within the original allocation + */ +bool +buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) +{ + char* oldstart = buflib_get_data(ctx, handle); + char* newstart = new_start; + char* newend = newstart + new_size; + + /* newstart must be higher and new_size not "negative" */ + if (newstart < oldstart || newend < newstart) + return false; + union buflib_data *block = handle_to_block(ctx, handle), + *old_next_block = block + block->val, + /* newstart isn't necessarily properly aligned but it + * needn't be since it's only dereferenced by the user code */ + *aligned_newstart = (union buflib_data*)B_ALIGN_DOWN(newstart), + *aligned_oldstart = (union buflib_data*)B_ALIGN_DOWN(oldstart), + *new_next_block = (union buflib_data*)B_ALIGN_UP(newend), + *new_block, metadata_size; + + /* growing is not supported */ + if (new_next_block > old_next_block) + return false; + + metadata_size.val = aligned_oldstart - block; + /* update val and the handle table entry */ + new_block = aligned_newstart - metadata_size.val; + block[0].val = new_next_block - new_block; + + block[1].handle->alloc = newstart; + if (block != new_block) + { + /* move metadata over, i.e. pointer to handle table entry and name + * This is actually the point of no return. Data in the allocation is + * being modified, and therefore we must successfully finish the shrink + * operation */ + memmove(new_block, block, metadata_size.val*sizeof(metadata_size)); + /* mark the old block unallocated */ + block->val = block - new_block; + /* find the block before in order to merge with the new free space */ + union buflib_data *free_before = find_free_block_before(ctx, block); + if (free_before) + free_before->val += block->val; + else if (ctx->first_free_block > block) + ctx->first_free_block = block; + + /* We didn't handle size changes yet, assign block to the new one + * the code below the wants block whether it changed or not */ + block = new_block; + } + + /* Now deal with size changes that create free blocks after the allocation */ + if (old_next_block != new_next_block) + { + if (ctx->alloc_end == old_next_block) + ctx->alloc_end = new_next_block; + else if (old_next_block->val < 0) + { /* enlarge next block by moving it up */ + new_next_block->val = old_next_block->val - (old_next_block - new_next_block); + } + else if (old_next_block != new_next_block) + { /* creating a hole */ + /* must be negative to indicate being unallocated */ + new_next_block->val = new_next_block - old_next_block; + } + /* update first_free_block for the newly created free space */ + if (ctx->first_free_block > new_next_block) + ctx->first_free_block = new_next_block; + } + + return true; +} + +const char* buflib_get_name(struct buflib_context *ctx, int handle) +{ + union buflib_data *data = (union buflib_data*)ALIGN_DOWN((intptr_t)buflib_get_data(ctx, handle), sizeof (*data)); + size_t len = data[-1].val; + if (len <= 1) + return NULL; + return data[-len].name; +} + +#ifdef BUFLIB_DEBUG_BLOCKS +void buflib_print_allocs(struct buflib_context *ctx, + void (*print)(int, const char*)) +{ + union buflib_data *this, *end = ctx->handle_table; + char buf[128]; + for(this = end - 1; this >= ctx->last_handle; this--) + { + if (!this->alloc) continue; + + int handle_num; + const char *name; + union buflib_data *block_start, *alloc_start; + intptr_t alloc_len; + + handle_num = end - this; + alloc_start = buflib_get_data(ctx, handle_num); + name = buflib_get_name(ctx, handle_num); + block_start = (union buflib_data*)name - 3; + alloc_len = block_start->val * sizeof(union buflib_data); + + snprintf(buf, sizeof(buf), + "%s(%d):\t%p\n" + " \t%p\n" + " \t%ld\n", + name?:"(null)", handle_num, block_start, alloc_start, alloc_len); + /* handle_num is 1-based */ + print(handle_num - 1, buf); + } +} + +void buflib_print_blocks(struct buflib_context *ctx, + void (*print)(int, const char*)) +{ + char buf[128]; + int i = 0; + for(union buflib_data* this = ctx->buf_start; + this < ctx->alloc_end; + this += abs(this->val)) + { + snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)", + this, this->val, + this->val > 0? this[3].name:""); + print(i++, buf); + } +} +#endif + +#ifdef BUFLIB_DEBUG_BLOCK_SINGLE +int buflib_get_num_blocks(struct buflib_context *ctx) +{ + int i = 0; + for(union buflib_data* this = ctx->buf_start; + this < ctx->alloc_end; + this += abs(this->val)) + { + i++; + } + return i; +} + +void buflib_print_block_at(struct buflib_context *ctx, int block_num, + char* buf, size_t bufsize) +{ + union buflib_data* this = ctx->buf_start; + while(block_num > 0 && this < ctx->alloc_end) + { + this += abs(this->val); + block_num -= 1; + } + snprintf(buf, bufsize, "%8p: val: %4ld (%s)", + this, this->val, + this->val > 0? this[3].name:""); +} + +#endif diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index f47e65e428..334801ce57 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c @@ -38,7 +38,7 @@ #include "kernel.h" #include "usb.h" #include "file.h" -#include "buffer.h" +#include "core_alloc.h" #include "dir.h" #include "storage.h" #if CONFIG_RTC @@ -57,6 +57,8 @@ #else #define MAX_OPEN_DIRS 8 #endif +static DIR_CACHED opendirs[MAX_OPEN_DIRS]; +static char opendir_dnames[MAX_OPEN_DIRS][MAX_PATH]; #define MAX_PENDING_BINDINGS 2 struct fdbind_queue { @@ -571,7 +573,8 @@ int dircache_load(void) } allocated_size = maindata.size + DIRCACHE_RESERVE; - dircache_root = buffer_alloc(allocated_size); + int handle = core_alloc("dircache", allocated_size); + dircache_root = core_get_data(handle); /* needs to be struct-size aligned so that the pointer arithmetic below works */ ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); entry_count = maindata.entry_count; @@ -814,6 +817,7 @@ static void generate_dot_d_names(void) strcpy(dot, "."); strcpy(dotdot, ".."); } + /** * Start scanning the disk to build the dircache. * Either transparent or non-transparent build method is used. @@ -841,11 +845,13 @@ int dircache_build(int last_size) queue_post(&dircache_queue, DIRCACHE_BUILD, 0); return 2; } - + if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) { + int handle; allocated_size = last_size + DIRCACHE_RESERVE; - dircache_root = buffer_alloc(allocated_size); + handle = core_alloc("dircache", allocated_size); + dircache_root = core_get_data(handle); ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; dircache_size = 0; @@ -863,7 +869,8 @@ int dircache_build(int last_size) * after generation the buffer will be compacted with DIRCACHE_RESERVE * free bytes inbetween */ size_t got_size; - char* buf = buffer_get_buffer(&got_size); + int handle = core_alloc_maximum("dircache", &got_size, NULL); + char* buf = core_get_data(handle); dircache_root = (struct dircache_entry*)ALIGN_UP(buf, sizeof(struct dircache_entry)); d_names_start = d_names_end = buf + got_size - 1; @@ -902,11 +909,11 @@ int dircache_build(int last_size) allocated_size = (d_names_end - buf); reserve_used = 0; - buffer_release_buffer(allocated_size); + core_shrink(handle, dircache_root, allocated_size); return res; fail: dircache_disable(); - buffer_release_buffer(0); + core_free(handle); return res; } @@ -942,7 +949,7 @@ void dircache_init(void) memset(opendirs, 0, sizeof(opendirs)); for (i = 0; i < MAX_OPEN_DIRS; i++) { - opendirs[i].theent.d_name = buffer_alloc(MAX_PATH); + opendirs[i].theent.d_name = opendir_dnames[i]; } queue_init(&dircache_queue, true); diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c new file mode 100644 index 0000000000..75dfc75b86 --- /dev/null +++ b/firmware/core_alloc.c @@ -0,0 +1,57 @@ + +#include +#include "core_alloc.h" +#include "buflib.h" +#include "buffer.h" + +/* not static so it can be discovered by core_get_data() */ +struct buflib_context core_ctx; + +void core_allocator_init(void) +{ + buffer_init(); + size_t size; + void *start = buffer_get_buffer(&size); + buflib_init(&core_ctx, start, size); + buffer_release_buffer(size); +} + +int core_alloc(const char* name, size_t size) +{ + return buflib_alloc_ex(&core_ctx, size, name, NULL); +} + +int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops) +{ + return buflib_alloc_ex(&core_ctx, size, name, ops); +} + +size_t core_available(void) +{ + return buflib_available(&core_ctx); +} + +int core_free(int handle) +{ + return buflib_free(&core_ctx, handle); +} + +int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops) +{ + return buflib_alloc_maximum(&core_ctx, name, size, ops); +} + +bool core_shrink(int handle, void* new_start, size_t new_size) +{ + return buflib_shrink(&core_ctx, handle, new_start, new_size); +} + +int core_get_num_blocks(void) +{ + return buflib_get_num_blocks(&core_ctx); +} + +void core_print_block_at(int block_num, char* buf, size_t bufsize) +{ + buflib_print_block_at(&core_ctx, block_num, buf, bufsize); +} diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 2835d8f4c4..57f3c24aae 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -58,6 +58,7 @@ void audio_resume(void); void audio_next(void); void audio_prev(void); int audio_status(void); +size_t audio_buffer_available(void); void audio_ff_rewind(long newpos); void audio_flush_and_reload_tracks(void); struct mp3entry* audio_current_track(void); diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h new file mode 100644 index 0000000000..db7b5ec50a --- /dev/null +++ b/firmware/include/buflib.h @@ -0,0 +1,319 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* This is a memory allocator designed to provide reasonable management of free +* space and fast access to allocated data. More than one allocator can be used +* at a time by initializing multiple contexts. +* +* Copyright (C) 2009 Andrew Mahone +* Copyright (C) 2011 Thomas Martitz +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +****************************************************************************/ + +#ifndef _BUFLIB_H_ +#define _BUFLIB_H_ +#include +#include +#include + +/* enable single block debugging */ +#define BUFLIB_DEBUG_BLOCK_SINGLE + +union buflib_data +{ + intptr_t val; + char name[1]; /* actually a variable sized string */ + struct buflib_callbacks* ops; + char* alloc; + union buflib_data *handle; +}; + +struct buflib_context +{ + union buflib_data *handle_table; + union buflib_data *first_free_handle; + union buflib_data *last_handle; + union buflib_data *first_free_block; + union buflib_data *buf_start; + union buflib_data *alloc_end; + bool compact; +}; + +/** + * Callbacks used by the buflib to inform allocation that compaction + * is happening (before data is moved) + * + * Note that buflib tries to move to satisfy new allocations before shrinking. + * So if you have something to resize try to do it outside of the callback. + * + * Regardless of the above, if the allocation is SHRINKABLE, but not + * MUST_NOT_MOVE buflib will move the allocation before even attempting to + * shrink. + */ +struct buflib_callbacks { + /** + * This is called before data is moved. Use this to fix up any cached + * pointers pointing to inside the allocation. The size is unchanged. + * + * This is not needed if you don't cache the data pointer (but always + * call buflib_get_data()) and don't pass pointer to the data to yielding + * functions. + * + * handle: The corresponding handle + * current: The current start of the allocation + * new: The new start of the allocation, after data movement + * + * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_MOVE if movement + * is impossible at this moment. + * + * If NULL: this allocation must not be moved around by the buflib when + * compation occurs + */ + int (*move_callback)(int handle, void* current, void* new); + /** + * This is called when the buflib desires to shrink a buffer + * in order to satisfy new allocation. This happens when buflib runs + * out of memory, e.g. because buflib_alloc_maximum() was called. + * Move data around as you need to make space and call core_shrink() as + * appropriate from within the callback to complete the shrink operation. + * buflib will not move data as part of shrinking. + * + * hint: bit mask containing hints on how shrinking is desired (see below) + * handle: The corresponding handle + * start: The old start of the allocation + * + * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_SHRINK if shirinking + * is impossible at this moment. + * + * if NULL: this allocation cannot be resized. + * It is recommended that allocation that must not move are + * at least shrinkable + */ + int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size); +}; + +#define BUFLIB_SHRINK_POS_MASK ((1<<0|1<<1)<<30) +#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK) +#define BUFLIB_SHRINK_POS_FRONT (1u<<31) +#define BUFLIB_SHRINK_POS_BACK (1u<<30) + +/** + * Possible return values for the callbacks, some of them can cause + * compaction to fail and therefore new allocations to fail + */ +/* Everything alright */ +#define BUFLIB_CB_OK 0 +/* Tell buflib that moving failed. Buflib may retry to move at any point */ +#define BUFLIB_CB_CANNOT_MOVE 1 +/* Tell buflib that resizing failed, possibly future making allocations fail */ +#define BUFLIB_CB_CANNOT_SHRINK 1 + +/** + * Initializes buflib with a caller allocated context instance and memory pool. + * + * The buflib_context instance needs to be passed to every other buflib + * function. It's should be considered opaque, even though it is not yet + * (that's to make inlining core_get_data() possible). The documentation + * of the other functions will not describe the context + * instance paramter further as it's obligatory. + * + * context: The new buflib instance to be initialized, allocated by the caller + * size: The size of the memory pool + */ +void buflib_init(struct buflib_context *context, void *buf, size_t size); + + +/** + * Returns how many bytes left the buflib has to satisfy allocations. + * + * This function does not yet consider possible compaction so there might + * be more space left. This may change in the future. + * + * Returns: The number of bytes left in the memory pool. + */ +size_t buflib_available(struct buflib_context *ctx); + + +/** + * Allocates memory from buflib's memory pool + * + * size: How many bytes to allocate + * + * Returns: An integer handle identifying this allocation + */ +int buflib_alloc(struct buflib_context *context, size_t size); + + +/** + * Allocates memory from the buflib's memory pool with additional callbacks + * and flags + * + * name: A string identifier giving this allocation a name + * size: How many bytes to allocate + * ops: a struct with pointers to callback functions (see above) + * + * Returns: An integer handle identifying this allocation + */ +int buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name, + struct buflib_callbacks *ops); + + +/** + * Gets all available memory from buflib, for temporary use. + * + * Since this effectively makes all future allocations fail (unless + * another allocation is freed in the meantime), you should definitely provide + * a shrink callback if you plan to hold the buffer for a longer period. This + * will allow buflib to permit allocations by shrinking the buffer returned by + * this function. + * + * Note that this currently gives whatever buflib_available() returns. However, + * do not depend on this behavior, it may change. + * + * name: A string identifier giving this allocation a name + * size: The actual size will be returned into size + * ops: a struct with pointers to callback functions + * + * Returns: An integer handle identifying this allocation + */ +int buflib_alloc_maximum(struct buflib_context* ctx, const char* name, + size_t *size, struct buflib_callbacks *ops); + +/** + * Queries the data pointer for the given handle. It's actually a cheap + * operation, don't hesitate using it extensivly. + * + * Notice that you need to re-query after every direct or indirect yield(), + * because compaction can happen by other threads which may get your data + * moved around (or you can get notified about changes by callbacks, + * see further above). + * + * handle: The handle corresponding to the allocation + * + * Returns: The start pointer of the allocation + */ +static inline void* buflib_get_data(struct buflib_context *context, int handle) +{ + return (void*)(context->handle_table[-handle].alloc); +} + +/** + * Shrink the memory allocation associated with the given handle + * Mainly intended to be used with the shrink callback, but it can also + * be called outside as well, e.g. to give back buffer space allocated + * with buflib_alloc_maximum(). + * + * Note that you must move/copy data around yourself before calling this, + * buflib will not do this as part of shrinking. + * + * handle: The handle identifying this allocation + * new_start: the new start of the allocation + * new_size: the new size of the allocation + * + * Returns: true if shrinking was successful. Otherwise it returns false, + * without having modified memory. + * + */ +bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size); + +/** + * Frees memory associated with the given handle + * + * Returns: 0 (to invalidate handles in one line) + */ +int buflib_free(struct buflib_context *context, int handle); + +/** + * Moves the underlying buflib buffer up by size bytes (as much as + * possible for size == 0) without moving the end. This effectively + * reduces the available space by taking away managable space from the + * front. This space is not available for new allocations anymore. + * + * To make space available in the front, everything is moved up. + * It does _NOT_ call the move callbacks + * + * + * size: size in bytes to move the buffer up (take away). The actual + * bytes moved is returned in this + * Returns: The new start of the underlying buflib buffer + */ +void* buflib_buffer_out(struct buflib_context *ctx, size_t *size); + +/** + * Moves the underlying buflib buffer down by size bytes without + * moving the end. This grows the buflib buffer by adding space to the front. + * The new bytes are available for new allocations. + * + * Everything is moved down, and the new free space will be in the middle. + * It does _NOT_ call the move callbacks. + * + * size: size in bytes to move the buffer down (new free space) + */ +void buflib_buffer_in(struct buflib_context *ctx, int size); + +/* debugging */ + +/** + * Returns the name, as given to core_alloc() and core_allloc_ex(), of the + * allocation associated with the given handle + * + * handle: The handle indicating the allocation + * + * Returns: A pointer to the string identifier of the allocation + */ +const char* buflib_get_name(struct buflib_context *ctx, int handle); + +/** + * Prints an overview of all current allocations with the help + * of the passed printer helper + * + * This walks only the handle table and prints only valid allocations + * + * Only available if BUFLIB_DEBUG_BLOCKS is defined + */ +void buflib_print_allocs(struct buflib_context *ctx, void (*print)(int, const char*)); + +/** + * Prints an overview of all blocks in the buflib buffer, allocated + * or unallocated, with the help pf the passted printer helper + * + * This walks the entire buffer and prints unallocated space also. + * The output is also different from buflib_print_allocs(). + * + * Only available if BUFLIB_DEBUG_BLOCKS is defined + */ +void buflib_print_blocks(struct buflib_context *ctx, void (*print)(int, const char*)); + +/** + * Gets the number of blocks in the entire buffer, allocated or unallocated + * + * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined + */ +int buflib_get_num_blocks(struct buflib_context *ctx); + +/** + * Print information about a single block as indicated by block_num + * into buf + * + * buflib_get_num_blocks() beforehand to get the total number of blocks, + * as passing an block_num higher than that is undefined + * + * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined + */ +void buflib_print_block_at(struct buflib_context *ctx, int block_num, + char* buf, size_t bufsize); +#endif diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h new file mode 100644 index 0000000000..f5206c9db9 --- /dev/null +++ b/firmware/include/core_alloc.h @@ -0,0 +1,36 @@ + +#ifndef __CORE_ALLOC_H__ +#define __CORE_ALLOC_H__ +#include +#include +#include "buflib.h" + +/* All functions below are wrappers for functions in buflib.h, except + * they have a predefined context + */ +void core_allocator_init(void); +int core_alloc(const char* name, size_t size); +int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops); +int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops); +bool core_shrink(int handle, void* new_start, size_t new_size); +int core_free(int handle); +size_t core_available(void); + +/* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call + * the move callbacks and are therefore unsafe in the core */ + +#ifdef BUFLIB_DEBUG_BLOCKS +void core_print_allocs(void (*print)(const char*)); +void core_print_blocks(void (*print)(const char*)); +#endif +#ifdef BUFLIB_DEBUG_BLOCK_SINGLE +int core_get_num_blocks(void); +void core_print_block_at(int block_num, char* buf, size_t bufsize); +#endif + +static inline void* core_get_data(int handle) +{ + extern struct buflib_context core_ctx; + return buflib_get_data(&core_ctx, handle); +} +#endif /* __CORE_ALLOC_H__ */ diff --git a/firmware/rolo.c b/firmware/rolo.c index 9b6f4fec4a..283779d7ee 100644 --- a/firmware/rolo.c +++ b/firmware/rolo.c @@ -31,7 +31,7 @@ #include "i2c.h" #include "adc.h" #include "string.h" -#include "buffer.h" +#include "core_alloc.h" #include "storage.h" #include "rolo.h" @@ -48,6 +48,7 @@ #define IRQ0_EDGE_TRIGGER 0x80 +static int rolo_handle; #ifdef CPU_PP /* Handle the COP properly - it needs to jump to a function outside SDRAM while * the new firmware is being loaded, and then jump to the start of SDRAM @@ -99,7 +100,7 @@ void rolo_restart_cop(void) static void rolo_error(const char *text) { - buffer_release_buffer(0); + rolo_handle = core_free(rolo_handle); lcd_clear_display(); lcd_puts(0, 0, "ROLO error:"); lcd_puts_scroll(0, 1, text); @@ -240,7 +241,8 @@ int rolo_load(const char* filename) /* get the system buffer. release only in case of error, otherwise * we don't return anyway */ - filebuf = buffer_get_buffer(&filebuf_size); + rolo_handle = core_alloc_maximum("rolo", &filebuf_size, NULL); + filebuf = core_get_data(rolo_handle); #if CONFIG_CPU != SH7034 /* Read and save checksum */ diff --git a/firmware/target/arm/ata-nand-telechips.c b/firmware/target/arm/ata-nand-telechips.c index 81dde33938..2ae425f4c6 100644 --- a/firmware/target/arm/ata-nand-telechips.c +++ b/firmware/target/arm/ata-nand-telechips.c @@ -26,7 +26,6 @@ #include "panic.h" #include "nand_id.h" #include "storage.h" -#include "buffer.h" #define SECTOR_SIZE 512 @@ -122,8 +121,9 @@ struct lpt_entry #ifdef BOOTLOADER static struct lpt_entry lpt_lookup[MAX_SEGMENTS]; #else -/* buffer_alloc'd in nand_init() when the correct size has been determined */ -static struct lpt_entry* lpt_lookup = NULL; +/* core_alloc()'d in nand_init() when the correct size has been determined */ +#include "core_alloc.h" +static int lpt_handle; #endif /* Write Caches */ @@ -607,6 +607,9 @@ static bool nand_read_sector_of_logical_segment(int log_segment, int sector, int page_in_segment = sector / sectors_per_page; int sector_in_page = sector % sectors_per_page; +#ifndef BOOTLOADER + struct lpt_entry* lpt_lookup = core_get_data(lpt_handle); +#endif int bank = lpt_lookup[log_segment].bank; int phys_segment = lpt_lookup[log_segment].phys_segment; @@ -918,7 +921,8 @@ int nand_init(void) #ifndef BOOTLOADER /* Use chip info to allocate the correct size LPT buffer */ lptbuf_size = sizeof(struct lpt_entry) * segments_per_bank * total_banks; - lpt_lookup = buffer_alloc(lptbuf_size); + lpt_handle = core_alloc("lpt lookup", lptbuf_size); + struct lpt_entry* lpt_lookup = core_get_data(lpt_handle); #else /* Use a static array in the bootloader */ lptbuf_size = sizeof(lpt_lookup); @@ -968,6 +972,9 @@ int nand_init(void) if (log_segment < segments_per_bank * total_banks) { +#ifndef BOOTLOADER + lpt_lookup = core_get_data(lpt_handle); +#endif if (lpt_lookup[log_segment].bank == -1 || lpt_lookup[log_segment].phys_segment == -1) { diff --git a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c index afb8d5cf62..ad10502f2d 100644 --- a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c +++ b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c @@ -30,7 +30,7 @@ #include "dm320.h" #include "ata.h" #include "string.h" -#include "buffer.h" +#include "core_alloc.h" #include "logf.h" #include "ata-defines.h" @@ -202,7 +202,11 @@ struct cfs_direntry_item static bool cfs_inited = false; static unsigned long cfs_start; +#ifdef BOOTLOADER static unsigned long *sectors; +#else +static int sectors_handle; +#endif #define CFS_START ( ((hdr->partitions[1].start*hdr->sector_size) & ~0xFFFF) + 0x10000 ) #define CFS_CLUSTER2CLUSTER(x) ( (CFS_START/512)+((x)-1)*64 ) @@ -299,7 +303,8 @@ static void cfs_init(void) _ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, §or); inode = (struct cfs_inode*)§or; #ifndef BOOTLOADER - sectors = (unsigned long*)buffer_alloc(VFAT_SECTOR_SIZE(inode->filesize)); + sectors_handle = core_alloc("ata sectors", VFAT_SECTOR_SIZE(inode->filesize)); + unsigned long *sectors = core_get_data(sectors_handle); #else static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */ sectors = _sector; @@ -322,6 +327,9 @@ static void cfs_init(void) _ata_read_sectors(CFS_CLUSTER2CLUSTER(inode->second_class_chain_second_cluster), 64, &vfat_data[1]); /* First class chain */ +#ifndef BOOTLOADER + sectors = core_get_data(sectors_handle); +#endif for(j=0; j<12; j++) { if( (inode->first_class_chain[j] & 0xFFFF) != 0xFFFF && @@ -331,6 +339,9 @@ static void cfs_init(void) } /* Second class chain */ +#ifndef BOOTLOADER + sectors = core_get_data(sectors_handle); +#endif for(j=0; j<0x8000/4; j++) { if( (vfat_data[0][j] & 0xFFFF) != 0xFFFF && @@ -351,6 +362,9 @@ static void cfs_init(void) /* Read third class subchain(s) */ _ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_data[1][j]), 64, &vfat_data[0]); +#ifndef BOOTLOADER + sectors = core_get_data(sectors_handle); +#endif for(k=0; k<0x8000/4; k++) { if( (vfat_data[0][k] & 0xFFFF) != 0xFFFF && @@ -376,6 +390,9 @@ static inline unsigned long map_sector(unsigned long sector) * Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part * FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes. */ +#ifndef BOOTLOADER + unsigned long *sectors = core_get_data(sectors_handle); +#endif return cfs_start+sectors[sector/64]*64+sector%64; }