summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-08-30 14:01:33 +0000
committerThomas Martitz <kugel@rockbox.org>2011-08-30 14:01:33 +0000
commitd0b72e25903574acb1cf9184a6052cdd646dbc37 (patch)
tree5be8db5ee00b2a727e4821cf51a5f7bcf3991073
parentc940811ade7d99a0e0d414df7c6509672413684a (diff)
downloadrockbox-d0b72e25903574acb1cf9184a6052cdd646dbc37.tar.gz
rockbox-d0b72e25903574acb1cf9184a6052cdd646dbc37.zip
GSoC/Buflib: Add buflib memory alocator to the core.
The buflib memory allocator is handle based and can free and compact, move or resize memory on demand. This allows to effeciently allocate memory dynamically without an MMU, by avoiding fragmentation through memory compaction. This patch adds the buflib library to the core, along with convinience wrappers to omit the context parameter. Compaction is not yet enabled, but will be in a later patch. Therefore, this acts as a replacement for buffer_alloc/buffer_get_buffer() with the benifit of a debug menu. See buflib.h for some API documentation. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30380 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c18
-rw-r--r--apps/dsp.c10
-rw-r--r--apps/filetypes.c29
-rw-r--r--apps/gui/skin_engine/skin_engine.c3
-rw-r--r--apps/main.c12
-rw-r--r--apps/menus/main_menu.c4
-rw-r--r--apps/mpeg.c68
-rw-r--r--apps/playback.c54
-rw-r--r--apps/playlist.c23
-rw-r--r--apps/playlist.h6
-rw-r--r--apps/scrobbler.c20
-rw-r--r--apps/tagcache.c21
-rw-r--r--apps/tagtree.c12
-rw-r--r--apps/talk.c3
-rw-r--r--apps/tdspeed.c27
-rw-r--r--apps/tree.c9
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/buflib.c777
-rw-r--r--firmware/common/dircache.c23
-rw-r--r--firmware/core_alloc.c57
-rw-r--r--firmware/export/audio.h1
-rw-r--r--firmware/include/buflib.h319
-rw-r--r--firmware/include/core_alloc.h36
-rw-r--r--firmware/rolo.c8
-rw-r--r--firmware/target/arm/ata-nand-telechips.c15
-rw-r--r--firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c21
26 files changed, 1453 insertions, 125 deletions
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 @@
79#include "peakmeter.h" 79#include "peakmeter.h"
80#endif 80#endif
81#include "logfdisp.h" 81#include "logfdisp.h"
82#include "core_alloc.h"
82#if CONFIG_CODEC == SWCODEC 83#if CONFIG_CODEC == SWCODEC
83#include "pcmbuf.h" 84#include "pcmbuf.h"
84#include "buffering.h" 85#include "buffering.h"
@@ -416,6 +417,22 @@ static bool dbg_buffering_thread(void)
416#endif /* CONFIG_CODEC */ 417#endif /* CONFIG_CODEC */
417#endif /* HAVE_LCD_BITMAP */ 418#endif /* HAVE_LCD_BITMAP */
418 419
420static const char* bf_getname(int selected_item, void *data,
421 char *buffer, size_t buffer_len)
422{
423 (void)data;
424 core_print_block_at(selected_item, buffer, buffer_len);
425 return buffer;
426}
427
428static bool dbg_buflib_allocs(void)
429{
430 struct simplelist_info info;
431 simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL);
432 info.get_name = bf_getname;
433 return simplelist_show_list(&info);
434}
435
419#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 436#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
420static const char* dbg_partitions_getname(int selected_item, void *data, 437static const char* dbg_partitions_getname(int selected_item, void *data,
421 char *buffer, size_t buffer_len) 438 char *buffer, size_t buffer_len)
@@ -2040,6 +2057,7 @@ static const struct the_menu_item menuitems[] = {
2040 { "pm histogram", peak_meter_histogram}, 2057 { "pm histogram", peak_meter_histogram},
2041#endif /* PM_DEBUG */ 2058#endif /* PM_DEBUG */
2042#endif /* HAVE_LCD_BITMAP */ 2059#endif /* HAVE_LCD_BITMAP */
2060 { "View buflib allocs", dbg_buflib_allocs },
2043#ifndef SIMULATOR 2061#ifndef SIMULATOR
2044#if CONFIG_TUNER 2062#if CONFIG_TUNER
2045 { "FM Radio", dbg_fm_radio }, 2063 { "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 @@
30#include "settings.h" 30#include "settings.h"
31#include "replaygain.h" 31#include "replaygain.h"
32#include "tdspeed.h" 32#include "tdspeed.h"
33#include "buffer.h" 33#include "core_alloc.h"
34#include "fixedpoint.h" 34#include "fixedpoint.h"
35#include "fracmul.h" 35#include "fracmul.h"
36 36
@@ -325,10 +325,16 @@ void dsp_timestretch_enable(bool enabled)
325 { 325 {
326 if (enabled) 326 if (enabled)
327 { 327 {
328 int handle;
328 /* Set up timestretch buffers */ 329 /* Set up timestretch buffers */
329 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; 330 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO;
330 big_sample_buf = small_resample_buf; 331 big_sample_buf = small_resample_buf;
331 big_resample_buf = (int32_t *) buffer_alloc(big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); 332 handle = core_alloc("resample buf",
333 big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t));
334 if (handle > 0)
335 big_resample_buf = core_get_data(handle);
336 else
337 big_sample_buf_count = 0;
332 } 338 }
333 else 339 else
334 { 340 {
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 @@
36#include "dir.h" 36#include "dir.h"
37#include "file.h" 37#include "file.h"
38#include "splash.h" 38#include "splash.h"
39#include "buffer.h" 39#include "core_alloc.h"
40#include "icons.h" 40#include "icons.h"
41#include "logf.h" 41#include "logf.h"
42 42
@@ -183,12 +183,14 @@ static int filetype_count = 0;
183static unsigned char highest_attr = 0; 183static unsigned char highest_attr = 0;
184static int viewer_count = 0; 184static int viewer_count = 0;
185 185
186static int strdup_handle, strdup_bufsize, strdup_cur_idx;
186static char *filetypes_strdup(char* string) 187static char *filetypes_strdup(char* string)
187{ 188{
188 char *buffer = (char*)buffer_alloc(strlen(string)+1); 189 char *buffer = core_get_data(strdup_handle) + strdup_cur_idx;
189 strcpy(buffer, string); 190 strdup_cur_idx += strlcpy(buffer, string, strdup_bufsize-strdup_cur_idx)+1;
190 return buffer; 191 return buffer;
191} 192}
193
192static char *filetypes_store_plugin(char *plugin, int n) 194static char *filetypes_store_plugin(char *plugin, int n)
193{ 195{
194 int i; 196 int i;
@@ -219,7 +221,7 @@ static int find_extension(const char* extension)
219} 221}
220 222
221static void read_builtin_types(void); 223static void read_builtin_types(void);
222static void read_config(const char* config_file); 224static void read_config(int fd);
223#ifdef HAVE_LCD_COLOR 225#ifdef HAVE_LCD_COLOR
224/* Colors file format is similar to icons: 226/* Colors file format is similar to icons:
225 * ext:hex_color 227 * ext:hex_color
@@ -312,16 +314,28 @@ void filetype_init(void)
312 filetypes[0].attr = 0; 314 filetypes[0].attr = 0;
313 filetypes[0].icon = Icon_Folder; 315 filetypes[0].icon = Icon_Folder;
314 316
317 /* estimate bufsize with the filesize, will not be larger */
315 viewer_count = 0; 318 viewer_count = 0;
316 filetype_count = 1; 319 filetype_count = 1;
320
321 int fd = open(VIEWERS_CONFIG, O_RDONLY);
322 if (fd < 0)
323 return;
324
325 strdup_bufsize = filesize(fd);
326 strdup_handle = core_alloc("filetypes", strdup_bufsize);
327 if (strdup_handle <= 0)
328 return;
317 read_builtin_types(); 329 read_builtin_types();
318 read_config(VIEWERS_CONFIG); 330 read_config(fd);
319#ifdef HAVE_LCD_BITMAP 331#ifdef HAVE_LCD_BITMAP
320 read_viewer_theme_file(); 332 read_viewer_theme_file();
321#endif 333#endif
322#ifdef HAVE_LCD_COLOR 334#ifdef HAVE_LCD_COLOR
323 read_color_theme_file(); 335 read_color_theme_file();
324#endif 336#endif
337 close(fd);
338 core_shrink(strdup_handle, core_get_data(strdup_handle), strdup_cur_idx);
325} 339}
326 340
327/* remove all white spaces from string */ 341/* remove all white spaces from string */
@@ -355,13 +369,10 @@ static void read_builtin_types(void)
355 } 369 }
356} 370}
357 371
358static void read_config(const char* config_file) 372static void read_config(int fd)
359{ 373{
360 char line[64], *s, *e; 374 char line[64], *s, *e;
361 char *extension, *plugin; 375 char *extension, *plugin;
362 int fd = open(config_file, O_RDONLY);
363 if (fd < 0)
364 return;
365 /* config file is in the format 376 /* config file is in the format
366 <extension>,<plugin>,<icon code> 377 <extension>,<plugin>,<icon code>
367 ignore line if either of the first two are missing */ 378 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)
48 skins_initialising = false; 48 skins_initialising = false;
49} 49}
50#else 50#else
51static char *skin_buffer = NULL; 51static char skin_buffer[SKIN_BUFFER_SIZE];
52void theme_init_buffer(void) 52void theme_init_buffer(void)
53{ 53{
54 skin_buffer = buffer_alloc(SKIN_BUFFER_SIZE);
55 skins_initialising = false; 54 skins_initialising = false;
56} 55}
57#endif 56#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 @@
53#include "language.h" 53#include "language.h"
54#include "wps.h" 54#include "wps.h"
55#include "playlist.h" 55#include "playlist.h"
56#include "buffer.h" 56#include "core_alloc.h"
57#include "rolo.h" 57#include "rolo.h"
58#include "screens.h" 58#include "screens.h"
59#include "usb_screen.h" 59#include "usb_screen.h"
@@ -337,7 +337,7 @@ static void init_tagcache(void)
337static void init(void) 337static void init(void)
338{ 338{
339 system_init(); 339 system_init();
340 buffer_init(); 340 core_allocator_init();
341 kernel_init(); 341 kernel_init();
342#ifdef APPLICATION 342#ifdef APPLICATION
343 paths_init(); 343 paths_init();
@@ -400,9 +400,6 @@ static void init(void)
400 global_settings.mdb_shape, 400 global_settings.mdb_shape,
401 global_settings.mdb_enable, 401 global_settings.mdb_enable,
402 global_settings.superbass); 402 global_settings.superbass);
403
404 /* audio_init must to know the size of voice buffer so init voice first */
405 talk_init();
406#endif /* CONFIG_CODEC != SWCODEC */ 403#endif /* CONFIG_CODEC != SWCODEC */
407 404
408 scrobbler_init(); 405 scrobbler_init();
@@ -428,7 +425,7 @@ static void init(void)
428#endif 425#endif
429 426
430 system_init(); 427 system_init();
431 buffer_init(); 428 core_allocator_init();
432 kernel_init(); 429 kernel_init();
433 430
434#ifdef HAVE_ADJUSTABLE_CPU_FREQ 431#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -684,9 +681,6 @@ static void init(void)
684 global_settings.mdb_shape, 681 global_settings.mdb_shape,
685 global_settings.mdb_enable, 682 global_settings.mdb_enable,
686 global_settings.superbass); 683 global_settings.superbass);
687
688 /* audio_init must to know the size of voice buffer so init voice first */
689 talk_init();
690#endif /* CONFIG_CODEC != SWCODEC */ 684#endif /* CONFIG_CODEC != SWCODEC */
691 685
692 CHART(">audio_init"); 686 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,
182 182
183 case INFO_BUFFER: /* buffer */ 183 case INFO_BUFFER: /* buffer */
184 { 184 {
185 long kib = buffer_available() / 1024; /* to KiB */ 185 long kib = audio_buffer_available() / 1024; /* to KiB */
186 output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true); 186 output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true);
187 snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1); 187 snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1);
188 } 188 }
@@ -272,7 +272,7 @@ static int info_speak_item(int selected_item, void * data)
272 case INFO_BUFFER: /* buffer */ 272 case INFO_BUFFER: /* buffer */
273 { 273 {
274 talk_id(LANG_BUFFER_STAT, false); 274 talk_id(LANG_BUFFER_STAT, false);
275 long kib = buffer_available() / 1024; /* to KiB */ 275 long kib = audio_buffer_available() / 1024; /* to KiB */
276 output_dyn_value(NULL, 0, kib, kbyte_units, true); 276 output_dyn_value(NULL, 0, kib, kbyte_units, true);
277 break; 277 break;
278 } 278 }
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 @@
35#include "thread.h" 35#include "thread.h"
36#include "errno.h" 36#include "errno.h"
37#include "mp3data.h" 37#include "mp3data.h"
38#include "buffer.h" 38#include "core_alloc.h"
39#include "mp3_playback.h" 39#include "mp3_playback.h"
40#include "talk.h" 40#include "talk.h"
41#include "sound.h" 41#include "sound.h"
@@ -145,7 +145,8 @@ static unsigned int mpeg_errno;
145static bool playing = false; /* We are playing an MP3 stream */ 145static bool playing = false; /* We are playing an MP3 stream */
146static bool is_playing = false; /* We are (attempting to) playing MP3 files */ 146static bool is_playing = false; /* We are (attempting to) playing MP3 files */
147static bool paused; /* playback is paused */ 147static bool paused; /* playback is paused */
148static char* mpeg_audiobuf; /* the audio buffer */ 148static int audiobuf_handle; /* handle to the audio buffer */
149static char* mpeg_audiobuf; /* poiunter to the audio buffer */
149static long audiobuflen; /* length of the audio buffer */ 150static long audiobuflen; /* length of the audio buffer */
150 151
151#ifdef SIMULATOR 152#ifdef SIMULATOR
@@ -491,15 +492,27 @@ unsigned long mpeg_get_last_header(void)
491#endif /* !SIMULATOR */ 492#endif /* !SIMULATOR */
492} 493}
493 494
494 495/* Buffer must not move. And not shrink for now */
496static struct buflib_callbacks ops = { NULL, NULL };
495unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) 497unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
496{ 498{
497 (void)talk_buf; /* always grab the voice buffer for now */ 499 (void)talk_buf; /* always grab the voice buffer for now */
498 500
499 audio_hard_stop();
500 if (buffer_size) /* special case for talk_init() */ 501 if (buffer_size) /* special case for talk_init() */
501 return buffer_get_buffer(buffer_size); 502 {
502 return NULL; 503 size_t bufsize;
504 audio_hard_stop();
505 /* audio_hard_stop() frees audiobuf, so re-aquire */
506 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
507 audiobuflen = bufsize;
508 *buffer_size = audiobuflen;
509 }
510 mpeg_audiobuf = core_get_data(audiobuf_handle);
511
512 if (!buffer_size) /* special case for talk_init() */
513 talkbuf_init(mpeg_audiobuf);
514
515 return mpeg_audiobuf;
503} 516}
504 517
505 518
@@ -2659,17 +2672,26 @@ void audio_set_recording_options(struct audio_recording_options *options)
2659#endif /* SIMULATOR */ 2672#endif /* SIMULATOR */
2660#endif /* CONFIG_CODEC == MAS3587F */ 2673#endif /* CONFIG_CODEC == MAS3587F */
2661 2674
2675size_t audio_buffer_available(void)
2676{
2677 if (audiobuf_handle > 0)
2678 return audiobuflen;
2679 return core_available();
2680}
2681
2662static void audio_reset_buffer(void) 2682static void audio_reset_buffer(void)
2663{ 2683{
2664 size_t bufsize; /* dont break strict-aliasing */
2665 talk_buffer_steal(); /* will use the mp3 buffer */ 2684 talk_buffer_steal(); /* will use the mp3 buffer */
2666 2685
2667 /* release buffer on behalf of any audio_get_buffer() caller, 2686 /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */
2668 * non-fatal if there was none */ 2687 if (!audiobuf_handle)
2669 buffer_release_buffer(0); 2688 {
2670 /* re-aquire */ 2689 size_t bufsize; /* dont break strict-aliasing */
2671 mpeg_audiobuf = buffer_get_buffer(&bufsize); 2690 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
2672 audiobuflen = bufsize; 2691 mpeg_audiobuf = core_get_data(audiobuf_handle);
2692 audiobuflen = bufsize;
2693 }
2694 talkbuf_init(mpeg_audiobuf);
2673} 2695}
2674 2696
2675void audio_play(long offset) 2697void audio_play(long offset)
@@ -2742,7 +2764,11 @@ void audio_hard_stop(void)
2742 audio_stop(); 2764 audio_stop();
2743 /* tell voice we obtain the buffer before freeing */ 2765 /* tell voice we obtain the buffer before freeing */
2744 talk_buffer_steal(); 2766 talk_buffer_steal();
2745 buffer_release_buffer(0); 2767 if (audiobuf_handle > 0)
2768 {
2769 audiobuf_handle = core_free(audiobuf_handle);
2770 mpeg_audiobuf = NULL;
2771 }
2746} 2772}
2747 2773
2748void audio_pause(void) 2774void audio_pause(void)
@@ -2899,13 +2925,15 @@ void audio_init(void)
2899 mpeg_errno = 0; 2925 mpeg_errno = 0;
2900 /* cuesheet support */ 2926 /* cuesheet support */
2901 if (global_settings.cuesheet) 2927 if (global_settings.cuesheet)
2902 curr_cuesheet = (struct cuesheet*)buffer_alloc(sizeof(struct cuesheet)); 2928 {
2929 int handle = core_alloc("cuesheet", sizeof(struct cuesheet));
2930 if (handle > 0)
2931 curr_cuesheet = core_get_data(handle);
2932 }
2933
2934 talk_init();
2935 audio_reset_buffer();
2903 2936
2904 size_t bufsize; /* don't break strict-aliasing */
2905 mpeg_audiobuf = buffer_get_buffer(&bufsize);
2906 audiobuflen = bufsize;
2907 /* give voice buffer until we start to play */
2908 talkbuf_init(mpeg_audiobuf);
2909#ifndef SIMULATOR 2937#ifndef SIMULATOR
2910 queue_init(&mpeg_queue, true); 2938 queue_init(&mpeg_queue, true);
2911#endif /* !SIMULATOR */ 2939#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 @@
24#include "system.h" 24#include "system.h"
25#include "kernel.h" 25#include "kernel.h"
26#include "panic.h" 26#include "panic.h"
27#include "buffer.h" 27#include "core_alloc.h"
28#include "sound.h" 28#include "sound.h"
29#include "ata.h" 29#include "ata.h"
30#include "usb.h" 30#include "usb.h"
@@ -732,6 +732,18 @@ static void scratch_mem_init(void *mem)
732 } 732 }
733} 733}
734 734
735/* Buffer must not move. And not shrink for now */
736static struct buflib_callbacks ops = { NULL, NULL };
737static int audiobuf_handle;
738static size_t filebuflen;
739
740size_t audio_buffer_available(void)
741{
742 if (audiobuf_handle > 0) /* if allocated return what we got */
743 return filebuflen;
744 return core_available();
745}
746
735/* Set up the audio buffer for playback */ 747/* Set up the audio buffer for playback */
736static void audio_reset_buffer(void) 748static void audio_reset_buffer(void)
737{ 749{
@@ -743,16 +755,17 @@ static void audio_reset_buffer(void)
743 /* see audio_get_recording_buffer if this is modified */ 755 /* see audio_get_recording_buffer if this is modified */
744 logf("%s()", __func__); 756 logf("%s()", __func__);
745 757
746 /* release the buffer on behalf of any caller of audio_get_buffer() */
747 buffer_release_buffer(0);
748
749 /* If the setup of anything allocated before the file buffer is 758 /* If the setup of anything allocated before the file buffer is
750 changed, do check the adjustments after the buffer_alloc call 759 changed, do check the adjustments after the buffer_alloc call
751 as it will likely be affected and need sliding over */ 760 as it will likely be affected and need sliding over */
752 761
753 /* Initially set up file buffer as all space available */ 762 /* Initially set up file buffer as all space available */
754 size_t filebuflen, allocsize; 763 size_t allocsize;
755 unsigned char *filebuf = buffer_get_buffer(&filebuflen); 764 if (audiobuf_handle > 0)
765 audiobuf_handle = core_free(audiobuf_handle);
766
767 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
768 unsigned char *filebuf = core_get_data(audiobuf_handle);
756 769
757 /* Subtract whatever voice needs */ 770 /* Subtract whatever voice needs */
758 allocsize = talkbuf_init(filebuf); 771 allocsize = talkbuf_init(filebuf);
@@ -3324,7 +3337,8 @@ void audio_hard_stop(void)
3324#ifdef PLAYBACK_VOICE 3337#ifdef PLAYBACK_VOICE
3325 voice_stop(); 3338 voice_stop();
3326#endif 3339#endif
3327 buffer_release_buffer(0); 3340 if (audiobuf_handle > 0)
3341 audiobuf_handle = core_free(audiobuf_handle);
3328} 3342}
3329 3343
3330/* Resume playback if paused */ 3344/* Resume playback if paused */
@@ -3447,6 +3461,14 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
3447 return NULL; 3461 return NULL;
3448 } 3462 }
3449 3463
3464 /* make sure buffer is freed and re-allocated to simplify code below
3465 * (audio_hard_stop() likely has done that already) */
3466 if (audiobuf_handle > 0)
3467 audiobuf_handle = core_free(audiobuf_handle);
3468
3469 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
3470 buf = core_get_data(audiobuf_handle);
3471
3450 if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED 3472 if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED
3451 || !talk_voice_required()) 3473 || !talk_voice_required())
3452 { 3474 {
@@ -3464,27 +3486,24 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
3464 talk_buffer_steal(); 3486 talk_buffer_steal();
3465 buffer_state = AUDIOBUF_STATE_TRASHED; 3487 buffer_state = AUDIOBUF_STATE_TRASHED;
3466 } 3488 }
3467 buf = buffer_get_buffer(buffer_size);
3468 } 3489 }
3469 else 3490 else
3470 { 3491 {
3492 logf("get buffer: audio");
3471 /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or 3493 /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
3472 still AUDIOBUF_STATE_INITIALIZED */ 3494 still AUDIOBUF_STATE_INITIALIZED */
3473 /* Skip talk buffer and move pcm buffer to end to maximize available 3495 /* Skip talk buffer and move pcm buffer to end to maximize available
3474 contiguous memory - no audio running means voice will not need the 3496 contiguous memory - no audio running means voice will not need the
3475 swap space */ 3497 swap space */
3476 size_t siz, talkbuf_size; 3498 size_t talkbuf_size;
3477 logf("get buffer: audio");
3478 /* call buffer_get_buffer() to make use of the locking mechanism */
3479 buf = buffer_get_buffer(&siz);
3480 buf += talkbuf_size = talkbuf_init(buf); 3499 buf += talkbuf_size = talkbuf_init(buf);
3481 siz -= talkbuf_size; 3500 filebuflen -= talkbuf_size;
3482 siz -= voicebuf_init(buf + siz); 3501 filebuflen -= voicebuf_init(buf + filebuflen);
3483 *buffer_size = siz;
3484 3502
3485 buffer_state = AUDIOBUF_STATE_VOICED_ONLY; 3503 buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
3486 } 3504 }
3487 3505
3506 *buffer_size = filebuflen;
3488 return buf; 3507 return buf;
3489} 3508}
3490 3509
@@ -3492,11 +3511,8 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
3492/* Stop audio, voice and obtain all available buffer space */ 3511/* Stop audio, voice and obtain all available buffer space */
3493unsigned char * audio_get_recording_buffer(size_t *buffer_size) 3512unsigned char * audio_get_recording_buffer(size_t *buffer_size)
3494{ 3513{
3495 talk_buffer_steal();
3496 audio_hard_stop(); 3514 audio_hard_stop();
3497 3515 return audio_get_buffer(true, buffer_size);
3498 buffer_state = AUDIOBUF_STATE_TRASHED;
3499 return buffer_get_buffer(buffer_size);
3500} 3516}
3501#endif /* HAVE_RECORDING */ 3517#endif /* HAVE_RECORDING */
3502 3518
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 @@
84#include "status.h" 84#include "status.h"
85#include "applimits.h" 85#include "applimits.h"
86#include "screens.h" 86#include "screens.h"
87#include "buffer.h" 87#include "core_alloc.h"
88#include "misc.h" 88#include "misc.h"
89#include "filefuncs.h" 89#include "filefuncs.h"
90#include "button.h" 90#include "button.h"
@@ -1929,6 +1929,7 @@ static int rotate_index(const struct playlist_info* playlist, int index)
1929 */ 1929 */
1930void playlist_init(void) 1930void playlist_init(void)
1931{ 1931{
1932 int handle;
1932 struct playlist_info* playlist = &current_playlist; 1933 struct playlist_info* playlist = &current_playlist;
1933 1934
1934 mutex_init(&current_playlist_mutex); 1935 mutex_init(&current_playlist_mutex);
@@ -1940,18 +1941,19 @@ void playlist_init(void)
1940 playlist->fd = -1; 1941 playlist->fd = -1;
1941 playlist->control_fd = -1; 1942 playlist->control_fd = -1;
1942 playlist->max_playlist_size = global_settings.max_files_in_playlist; 1943 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1943 playlist->indices = buffer_alloc( 1944 handle = core_alloc("playlist idx", playlist->max_playlist_size * sizeof(int));
1944 playlist->max_playlist_size * sizeof(int)); 1945 playlist->indices = core_get_data(handle);
1945 playlist->buffer_size = 1946 playlist->buffer_size =
1946 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; 1947 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1947 playlist->buffer = buffer_alloc(playlist->buffer_size); 1948 handle = core_alloc("playlist buf", playlist->buffer_size);
1949 playlist->buffer = core_get_data(handle);
1948 playlist->control_mutex = &current_playlist_mutex; 1950 playlist->control_mutex = &current_playlist_mutex;
1949 1951
1950 empty_playlist(playlist, true); 1952 empty_playlist(playlist, true);
1951 1953
1952#ifdef HAVE_DIRCACHE 1954#ifdef HAVE_DIRCACHE
1953 playlist->filenames = buffer_alloc( 1955 handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int));
1954 playlist->max_playlist_size * sizeof(int)); 1956 playlist->filenames = core_get_data(handle);
1955 memset(playlist->filenames, 0xff, 1957 memset(playlist->filenames, 0xff,
1956 playlist->max_playlist_size * sizeof(int)); 1958 playlist->max_playlist_size * sizeof(int));
1957 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), 1959 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
@@ -3356,7 +3358,6 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3356 char tmp_buf[MAX_PATH+1]; 3358 char tmp_buf[MAX_PATH+1];
3357 int result = 0; 3359 int result = 0;
3358 bool overwrite_current = false; 3360 bool overwrite_current = false;
3359 int* index_buf = NULL;
3360 char* old_buffer = NULL; 3361 char* old_buffer = NULL;
3361 size_t old_buffer_size = 0; 3362 size_t old_buffer_size = 0;
3362 3363
@@ -3391,10 +3392,6 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3391 } 3392 }
3392 } 3393 }
3393 3394
3394 /* in_ram buffer is unused for m3u files so we'll use for storing
3395 updated indices */
3396 index_buf = (int*)playlist->buffer;
3397
3398 /* use temporary pathname */ 3395 /* use temporary pathname */
3399 snprintf(path, sizeof(path), "%s_temp", playlist->filename); 3396 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3400 overwrite_current = true; 3397 overwrite_current = true;
@@ -3453,7 +3450,7 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3453 } 3450 }
3454 3451
3455 if (overwrite_current) 3452 if (overwrite_current)
3456 index_buf[count] = lseek(fd, 0, SEEK_CUR); 3453 playlist->seek_buf[count] = lseek(fd, 0, SEEK_CUR);
3457 3454
3458 if (fdprintf(fd, "%s\n", tmp_buf) < 0) 3455 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3459 { 3456 {
@@ -3498,7 +3495,7 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3498 { 3495 {
3499 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK)) 3496 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3500 { 3497 {
3501 playlist->indices[index] = index_buf[count]; 3498 playlist->indices[index] = playlist->seek_buf[count];
3502 count++; 3499 count++;
3503 } 3500 }
3504 index = (index+1)%playlist->amount; 3501 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
85 int max_playlist_size; /* Max number of files in playlist. Mirror of 85 int max_playlist_size; /* Max number of files in playlist. Mirror of
86 global_settings.max_files_in_playlist */ 86 global_settings.max_files_in_playlist */
87 bool in_ram; /* playlist stored in ram (dirplay) */ 87 bool in_ram; /* playlist stored in ram (dirplay) */
88 char *buffer; /* buffer for in-ram playlists */ 88
89 union {
90 char *buffer; /* buffer for in-ram playlists */
91 int *seek_buf; /* buffer for seeks in real playlists */
92 };
89 int buffer_size; /* size of buffer */ 93 int buffer_size; /* size of buffer */
90 int buffer_end_pos; /* last position where buffer was written */ 94 int buffer_end_pos; /* last position where buffer was written */
91 int index; /* index of current playing track */ 95 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
30#include "metadata.h" 30#include "metadata.h"
31#include "kernel.h" 31#include "kernel.h"
32#include "audio.h" 32#include "audio.h"
33#include "buffer.h" 33#include "core_alloc.h"
34#include "settings.h" 34#include "settings.h"
35#include "ata_idle_notify.h" 35#include "ata_idle_notify.h"
36#include "filefuncs.h" 36#include "filefuncs.h"
@@ -52,7 +52,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
52/* longest entry I've had is 323, add a safety margin */ 52/* longest entry I've had is 323, add a safety margin */
53#define SCROBBLER_CACHE_LEN 512 53#define SCROBBLER_CACHE_LEN 512
54 54
55static char* scrobbler_cache; 55static int scrobbler_cache;
56 56
57static int cache_pos; 57static int cache_pos;
58static struct mp3entry scrobbler_entry; 58static struct mp3entry scrobbler_entry;
@@ -139,11 +139,16 @@ static void write_cache(void)
139 if(fd >= 0) 139 if(fd >= 0)
140 { 140 {
141 logf("SCROBBLER: writing %d entries", cache_pos); 141 logf("SCROBBLER: writing %d entries", cache_pos);
142 142 /* copy data to temporary storage in case data moves during I/O */
143 char temp_buf[SCROBBLER_CACHE_LEN];
143 for ( i=0; i < cache_pos; i++ ) 144 for ( i=0; i < cache_pos; i++ )
144 { 145 {
145 logf("SCROBBLER: write %d", i); 146 logf("SCROBBLER: write %d", i);
146 fdprintf(fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i)); 147 char* scrobbler_buf = core_get_data(scrobbler_cache);
148 ssize_t len = strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i),
149 sizeof(temp_buf));
150 if (write(fd, temp_buf, len) != len)
151 break;
147 } 152 }
148 close(fd); 153 close(fd);
149 } 154 }
@@ -170,6 +175,7 @@ static void add_to_cache(unsigned long play_length)
170 175
171 int ret; 176 int ret;
172 char rating = 'S'; /* Skipped */ 177 char rating = 'S'; /* Skipped */
178 char* scrobbler_buf = core_get_data(scrobbler_cache);
173 179
174 logf("SCROBBLER: add_to_cache[%d]", cache_pos); 180 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
175 181
@@ -178,7 +184,7 @@ static void add_to_cache(unsigned long play_length)
178 184
179 if (scrobbler_entry.tracknum > 0) 185 if (scrobbler_entry.tracknum > 0)
180 { 186 {
181 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), 187 ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos),
182 SCROBBLER_CACHE_LEN, 188 SCROBBLER_CACHE_LEN,
183 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n", 189 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n",
184 scrobbler_entry.artist, 190 scrobbler_entry.artist,
@@ -190,7 +196,7 @@ static void add_to_cache(unsigned long play_length)
190 (long)timestamp, 196 (long)timestamp,
191 scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); 197 scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:"");
192 } else { 198 } else {
193 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), 199 ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos),
194 SCROBBLER_CACHE_LEN, 200 SCROBBLER_CACHE_LEN,
195 "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n", 201 "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n",
196 scrobbler_entry.artist, 202 scrobbler_entry.artist,
@@ -248,7 +254,7 @@ int scrobbler_init(void)
248 if(!global_settings.audioscrobbler) 254 if(!global_settings.audioscrobbler)
249 return -1; 255 return -1;
250 256
251 scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); 257 scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
252 258
253 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); 259 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
254 cache_pos = 0; 260 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 @@
74#include "usb.h" 74#include "usb.h"
75#include "metadata.h" 75#include "metadata.h"
76#include "tagcache.h" 76#include "tagcache.h"
77#include "buffer.h" 77#include "core_alloc.h"
78#include "crc32.h" 78#include "crc32.h"
79#include "misc.h" 79#include "misc.h"
80#include "settings.h" 80#include "settings.h"
@@ -3082,6 +3082,7 @@ static bool commit(void)
3082 return true; 3082 return true;
3083} 3083}
3084 3084
3085static int tempbuf_handle;
3085static void allocate_tempbuf(void) 3086static void allocate_tempbuf(void)
3086{ 3087{
3087 /* Yeah, malloc would be really nice now :) */ 3088 /* Yeah, malloc would be really nice now :) */
@@ -3089,7 +3090,8 @@ static void allocate_tempbuf(void)
3089 tempbuf_size = 32*1024*1024; 3090 tempbuf_size = 32*1024*1024;
3090 tempbuf = malloc(tempbuf_size); 3091 tempbuf = malloc(tempbuf_size);
3091#else 3092#else
3092 tempbuf = buffer_get_buffer(&tempbuf_size); 3093 tempbuf_handle = core_alloc_maximum("tc tempbuf", &tempbuf_size, NULL);
3094 tempbuf = core_get_data(tempbuf_handle);
3093#endif 3095#endif
3094} 3096}
3095 3097
@@ -3101,7 +3103,7 @@ static void free_tempbuf(void)
3101#ifdef __PCTOOL__ 3103#ifdef __PCTOOL__
3102 free(tempbuf); 3104 free(tempbuf);
3103#else 3105#else
3104 buffer_release_buffer(0); 3106 tempbuf_handle = core_free(tempbuf_handle);
3105#endif 3107#endif
3106 tempbuf = NULL; 3108 tempbuf = NULL;
3107 tempbuf_size = 0; 3109 tempbuf_size = 0;
@@ -3829,9 +3831,10 @@ static bool allocate_tagcache(void)
3829 * Now calculate the required cache size plus 3831 * Now calculate the required cache size plus
3830 * some extra space for alignment fixes. 3832 * some extra space for alignment fixes.
3831 */ 3833 */
3832 tc_stat.ramcache_allocated = tcmh.tch.datasize + 128 + TAGCACHE_RESERVE + 3834 tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE +
3833 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); 3835 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
3834 ramcache_hdr = buffer_alloc(tc_stat.ramcache_allocated + 128); 3836 int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated);
3837 ramcache_hdr = core_get_data(handle);
3835 memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); 3838 memset(ramcache_hdr, 0, sizeof(struct ramcache_header));
3836 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh); 3839 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh);
3837 logf("tagcache: %d bytes allocated.", tc_stat.ramcache_allocated); 3840 logf("tagcache: %d bytes allocated.", tc_stat.ramcache_allocated);
@@ -3845,7 +3848,7 @@ static bool tagcache_dumpload(void)
3845 struct statefile_header shdr; 3848 struct statefile_header shdr;
3846 int fd, rc; 3849 int fd, rc;
3847 long offpos; 3850 long offpos;
3848 int i; 3851 int i, handle;
3849 3852
3850 fd = open(TAGCACHE_STATEFILE, O_RDONLY); 3853 fd = open(TAGCACHE_STATEFILE, O_RDONLY);
3851 if (fd < 0) 3854 if (fd < 0)
@@ -3855,7 +3858,6 @@ static bool tagcache_dumpload(void)
3855 } 3858 }
3856 3859
3857 /* Check the statefile memory placement */ 3860 /* Check the statefile memory placement */
3858 ramcache_hdr = buffer_alloc(0);
3859 rc = read(fd, &shdr, sizeof(struct statefile_header)); 3861 rc = read(fd, &shdr, sizeof(struct statefile_header));
3860 if (rc != sizeof(struct statefile_header) 3862 if (rc != sizeof(struct statefile_header)
3861 || shdr.magic != TAGCACHE_STATEFILE_MAGIC 3863 || shdr.magic != TAGCACHE_STATEFILE_MAGIC
@@ -3867,13 +3869,14 @@ static bool tagcache_dumpload(void)
3867 return false; 3869 return false;
3868 } 3870 }
3869 3871
3870 offpos = (long)ramcache_hdr - (long)shdr.hdr;
3871 3872
3872 /* Lets allocate real memory and load it */ 3873 /* Lets allocate real memory and load it */
3873 ramcache_hdr = buffer_alloc(shdr.tc_stat.ramcache_allocated); 3874 handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated);
3875 ramcache_hdr = core_get_data(handle);
3874 rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); 3876 rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated);
3875 close(fd); 3877 close(fd);
3876 3878
3879 offpos = (long)ramcache_hdr - (long)shdr.hdr;
3877 if (rc != shdr.tc_stat.ramcache_allocated) 3880 if (rc != shdr.tc_stat.ramcache_allocated)
3878 { 3881 {
3879 logf("read failure!"); 3882 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 @@
44#include "playlist.h" 44#include "playlist.h"
45#include "keyboard.h" 45#include "keyboard.h"
46#include "gui/list.h" 46#include "gui/list.h"
47#include "buffer.h" 47#include "core_alloc.h"
48#include "yesno.h" 48#include "yesno.h"
49#include "misc.h" 49#include "misc.h"
50#include "filetypes.h" 50#include "filetypes.h"
@@ -176,9 +176,14 @@ static int current_entry_count;
176static struct tree_context *tc; 176static struct tree_context *tc;
177 177
178/* a few memory alloc helper */ 178/* a few memory alloc helper */
179static int tagtree_handle;
180static size_t tagtree_bufsize, tagtree_buf_used;
179static void* tagtree_alloc(size_t size) 181static void* tagtree_alloc(size_t size)
180{ 182{
181 return buffer_alloc(size); 183 char* buf = core_get_data(tagtree_handle) + tagtree_buf_used;
184 size = ALIGN_UP(size, sizeof(void*));
185 tagtree_buf_used += size;
186 return buf;
182} 187}
183 188
184static void* tagtree_alloc0(size_t size) 189static void* tagtree_alloc0(size_t size)
@@ -1035,6 +1040,7 @@ void tagtree_init(void)
1035 menu_count = 0; 1040 menu_count = 0;
1036 menu = NULL; 1041 menu = NULL;
1037 rootmenu = -1; 1042 rootmenu = -1;
1043 tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL);
1038 parse_menu(FILE_SEARCH_INSTRUCTIONS); 1044 parse_menu(FILE_SEARCH_INSTRUCTIONS);
1039 1045
1040 /* If no root menu is set, assume it's the first single menu 1046 /* If no root menu is set, assume it's the first single menu
@@ -1046,6 +1052,8 @@ void tagtree_init(void)
1046 1052
1047 add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); 1053 add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event);
1048 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); 1054 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event);
1055
1056 core_shrink(tagtree_handle, core_get_data(tagtree_handle), tagtree_buf_used);
1049} 1057}
1050 1058
1051static bool show_search_progress(bool init, int count) 1059static 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 @@
27#include <stddef.h> 27#include <stddef.h>
28#include "string-extra.h" 28#include "string-extra.h"
29#include "file.h" 29#include "file.h"
30#include "buffer.h"
31#include "system.h" 30#include "system.h"
32#include "kernel.h" 31#include "kernel.h"
33#include "settings.h" 32#include "settings.h"
@@ -711,7 +710,7 @@ void talk_init(void)
711 710
712 711
713 /* test if we can open and if it fits in the audiobuffer */ 712 /* test if we can open and if it fits in the audiobuffer */
714 size_t audiobufsz = buffer_available(); 713 size_t audiobufsz = audio_buffer_available();
715 if (voicefile_size <= audiobufsz) { 714 if (voicefile_size <= audiobufsz) {
716 has_voicefile = true; 715 has_voicefile = true;
717 } else { 716 } 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 @@
25#include <stdio.h> 25#include <stdio.h>
26#include <string.h> 26#include <string.h>
27#include "sound.h" 27#include "sound.h"
28#include "buffer.h" 28#include "core_alloc.h"
29#include "system.h" 29#include "system.h"
30#include "tdspeed.h" 30#include "tdspeed.h"
31#include "settings.h" 31#include "settings.h"
@@ -56,21 +56,32 @@ static int32_t *outbuf[2] = { NULL, NULL };
56 56
57void tdspeed_init(void) 57void tdspeed_init(void)
58{ 58{
59 int handle;
60
59 if (!global_settings.timestretch_enabled) 61 if (!global_settings.timestretch_enabled)
60 return; 62 return;
61 63
62 /* Allocate buffers */ 64 /* Allocate buffers */
63 if (overlap_buffer[0] == NULL) 65 if (overlap_buffer[0] == NULL)
64 overlap_buffer[0] = (int32_t *)buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t)); 66 {
65 67 handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t));
68 overlap_buffer[0] = core_get_data(handle);
69 }
66 if (overlap_buffer[1] == NULL) 70 if (overlap_buffer[1] == NULL)
67 overlap_buffer[1] = (int32_t *)buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t)); 71 {
68 72 handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t));
73 overlap_buffer[1] = core_get_data(handle);
74 }
69 if (outbuf[0] == NULL) 75 if (outbuf[0] == NULL)
70 outbuf[0] = (int32_t *)buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t)); 76 {
71 77 handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t));
78 outbuf[0] = core_get_data(handle);
79 }
72 if (outbuf[1] == NULL) 80 if (outbuf[1] == NULL)
73 outbuf[1] = (int32_t *)buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t)); 81 {
82 handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t));
83 outbuf[1] = core_get_data(handle);
84 }
74} 85}
75 86
76 87
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 @@
46#include "keyboard.h" 46#include "keyboard.h"
47#include "bookmark.h" 47#include "bookmark.h"
48#include "onplay.h" 48#include "onplay.h"
49#include "buffer.h" 49#include "core_alloc.h"
50#include "power.h" 50#include "power.h"
51#include "action.h" 51#include "action.h"
52#include "talk.h" 52#include "talk.h"
@@ -1002,6 +1002,7 @@ int rockbox_browse(struct browse_context *browse)
1002void tree_mem_init(void) 1002void tree_mem_init(void)
1003{ 1003{
1004 /* initialize tree context struct */ 1004 /* initialize tree context struct */
1005 int handle;
1005 struct tree_cache* cache = &tc.cache; 1006 struct tree_cache* cache = &tc.cache;
1006 memset(&tc, 0, sizeof(tc)); 1007 memset(&tc, 0, sizeof(tc));
1007 tc.dirfilter = &global_settings.dirfilter; 1008 tc.dirfilter = &global_settings.dirfilter;
@@ -1009,10 +1010,12 @@ void tree_mem_init(void)
1009 1010
1010 cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * 1011 cache->name_buffer_size = AVERAGE_FILENAME_LENGTH *
1011 global_settings.max_files_in_dir; 1012 global_settings.max_files_in_dir;
1012 cache->name_buffer = buffer_alloc(cache->name_buffer_size); 1013 handle = core_alloc("tree names", cache->name_buffer_size);
1014 cache->name_buffer = core_get_data(handle);
1013 1015
1014 cache->max_entries = global_settings.max_files_in_dir; 1016 cache->max_entries = global_settings.max_files_in_dir;
1015 cache->entries = buffer_alloc(cache->max_entries*(sizeof(struct entry))); 1017 handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry)));
1018 cache->entries = core_get_data(handle);
1016 tree_get_filetypes(&filetypes, &filetypes_count); 1019 tree_get_filetypes(&filetypes, &filetypes_count);
1017} 1020}
1018 1021
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
2events.c 2events.c
3backlight.c 3backlight.c
4buffer.c 4buffer.c
5buflib.c
6core_alloc.c
5general.c 7general.c
6load_code.c 8load_code.c
7powermgmt.c 9powermgmt.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 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* This is a memory allocator designed to provide reasonable management of free
11* space and fast access to allocated data. More than one allocator can be used
12* at a time by initializing multiple contexts.
13*
14* Copyright (C) 2009 Andrew Mahone
15* Copyright (C) 2011 Thomas Martitz
16*
17*
18* This program is free software; you can redistribute it and/or
19* modify it under the terms of the GNU General Public License
20* as published by the Free Software Foundation; either version 2
21* of the License, or (at your option) any later version.
22*
23* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24* KIND, either express or implied.
25*
26****************************************************************************/
27
28#include <stdlib.h> /* for abs() */
29#include <stdio.h> /* for snprintf() */
30#include "buflib.h"
31#include "string-extra.h" /* strlcpy() */
32#include "debug.h"
33#include "buffer.h"
34#include "system.h" /* for ALIGN_*() */
35
36/* The main goal of this design is fast fetching of the pointer for a handle.
37 * For that reason, the handles are stored in a table at the end of the buffer
38 * with a fixed address, so that returning the pointer for a handle is a simple
39 * table lookup. To reduce the frequency with which allocated blocks will need
40 * to be moved to free space, allocations grow up in address from the start of
41 * the buffer. The buffer is treated as an array of union buflib_data. Blocks
42 * start with a length marker, which is included in their length. Free blocks
43 * are marked by negative length. Allocated blocks have a positiv length marker,
44 * and additional metadata forllowing that: It follows a pointer
45 * (union buflib_data*) to the corresponding handle table entry. so that it can
46 * be quickly found and updated during compaction. After that follows
47 * the pointer to the struct buflib_callbacks associated with this allocation
48 * (may be NULL). That pointer follows a variable length character array
49 * containing the nul-terminated string identifier of the allocation. After this
50 * array there's a length marker for the length of the character array including
51 * this length marker (counted in n*sizeof(union buflib_data)), which allows
52 * to find the start of the character array (and therefore the start of the
53 * entire block) when only the handle or payload start is known.
54 *
55 * Example:
56 * |<- alloc block #1 ->|<- unalloc block ->|<- alloc block #2 ->|<-handle table->|
57 * |L|H|C|cccc|L2|XXXXXX|-L|YYYYYYYYYYYYYYYY|L|H|C|cc|L2|XXXXXXXXXXXXX|AAA|
58 *
59 * L - length marker (negative if block unallocated)
60 * H - handle table enry pointer
61 * C - pointer to struct buflib_callbacks
62 * c - variable sized string identifier
63 * L2 - second length marker for string identifier
64 * X - actual payload
65 * Y - unallocated space
66 *
67 * A - pointer to start of payload (first X) in the handle table (may be null)
68 *
69 * The blocks can be walked by jumping the abs() of the L length marker, i.e.
70 * union buflib_data* L;
71 * for(L = start; L < end; L += abs(L->val)) { .... }
72 *
73 *
74 * The allocator functions are passed a context struct so that two allocators
75 * can be run, for example, one per core may be used, with convenience wrappers
76 * for the single-allocator case that use a predefined context.
77 */
78
79#define B_ALIGN_DOWN(x) \
80 ALIGN_DOWN(x, sizeof(union buflib_data))
81
82#define B_ALIGN_UP(x) \
83 ALIGN_UP(x, sizeof(union buflib_data))
84
85#ifdef DEBUG
86 #include <stdio.h>
87 #define BDEBUGF DEBUGF
88#else
89 #define BDEBUGF(...) do { } while(0)
90#endif
91
92/* Initialize buffer manager */
93void
94buflib_init(struct buflib_context *ctx, void *buf, size_t size)
95{
96 union buflib_data *bd_buf = buf;
97
98 /* Align on sizeof(buflib_data), to prevent unaligned access */
99 ALIGN_BUFFER(bd_buf, size, sizeof(union buflib_data));
100 size /= sizeof(union buflib_data);
101 /* The handle table is initialized with no entries */
102 ctx->handle_table = bd_buf + size;
103 ctx->last_handle = bd_buf + size;
104 ctx->first_free_handle = bd_buf + size - 1;
105 ctx->first_free_block = bd_buf;
106 ctx->buf_start = bd_buf;
107 /* A marker is needed for the end of allocated data, to make sure that it
108 * does not collide with the handle table, and to detect end-of-buffer.
109 */
110 ctx->alloc_end = bd_buf;
111 ctx->compact = true;
112
113 BDEBUGF("buflib initialized with %d.%2d kiB", size / 1024, (size%1000)/10);
114}
115
116/* Allocate a new handle, returning 0 on failure */
117static inline
118union buflib_data* handle_alloc(struct buflib_context *ctx)
119{
120 union buflib_data *handle;
121 /* first_free_handle is a lower bound on free handles, work through the
122 * table from there until a handle containing NULL is found, or the end
123 * of the table is reached.
124 */
125 for (handle = ctx->first_free_handle; handle >= ctx->last_handle; handle--)
126 if (!handle->alloc)
127 break;
128 /* If the search went past the end of the table, it means we need to extend
129 * the table to get a new handle.
130 */
131 if (handle < ctx->last_handle)
132 {
133 if (handle >= ctx->alloc_end)
134 ctx->last_handle--;
135 else
136 return NULL;
137 }
138 handle->val = -1;
139 return handle;
140}
141
142/* Free one handle, shrinking the handle table if it's the last one */
143static inline
144void handle_free(struct buflib_context *ctx, union buflib_data *handle)
145{
146 handle->alloc = 0;
147 /* Update free handle lower bound if this handle has a lower index than the
148 * old one.
149 */
150 if (handle > ctx->first_free_handle)
151 ctx->first_free_handle = handle;
152 if (handle == ctx->last_handle)
153 ctx->last_handle++;
154 else
155 ctx->compact = false;
156}
157
158/* Get the start block of an allocation */
159static union buflib_data* handle_to_block(struct buflib_context* ctx, int handle)
160{
161 union buflib_data* name_field =
162 (union buflib_data*)buflib_get_name(ctx, handle);
163
164 return name_field - 3;
165}
166
167/* Shrink the handle table, returning true if its size was reduced, false if
168 * not
169 */
170static inline
171bool
172handle_table_shrink(struct buflib_context *ctx)
173{
174 bool rv;
175 union buflib_data *handle;
176 for (handle = ctx->last_handle; !(handle->alloc); handle++);
177 if (handle > ctx->first_free_handle)
178 ctx->first_free_handle = handle - 1;
179 rv = handle == ctx->last_handle;
180 ctx->last_handle = handle;
181 return rv;
182}
183
184
185/* If shift is non-zero, it represents the number of places to move
186 * blocks in memory. Calculate the new address for this block,
187 * update its entry in the handle table, and then move its contents.
188 *
189 * Returns false if moving was unsucessful
190 * (NULL callback or BUFLIB_CB_CANNOT_MOVE was returned)
191 */
192static bool
193move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
194{
195#if 1 /* moving temporarily disabled */
196 (void)ctx;(void)block;(void)shift;
197 return false;
198#else
199 char* new_start;
200 union buflib_data *new_block, *tmp = block[1].handle;
201 struct buflib_callbacks *ops = block[2].ops;
202 if (ops && !ops->move_callback)
203 return false;
204
205 int handle = ctx->handle_table - tmp;
206 BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name,
207 handle, shift, shift*sizeof(union buflib_data));
208 new_block = block + shift;
209 new_start = tmp->alloc + shift*sizeof(union buflib_data);
210 /* call the callback before moving */
211 if (ops)
212 {
213 if (ops->move_callback(handle, tmp->alloc, new_start)
214 == BUFLIB_CB_CANNOT_MOVE)
215 return false;
216 }
217 tmp->alloc = new_start; /* update handle table */
218 memmove(new_block, block, block->val * sizeof(union buflib_data));
219
220 return true;
221#endif
222}
223
224/* Compact allocations and handle table, adjusting handle pointers as needed.
225 * Return true if any space was freed or consolidated, false otherwise.
226 */
227static bool
228buflib_compact(struct buflib_context *ctx)
229{
230 BDEBUGF("%s(): Compacting!\n", __func__);
231 union buflib_data *block;
232 int shift = 0, len;
233 /* Store the results of attempting to shrink the handle table */
234 bool ret = handle_table_shrink(ctx);
235 for(block = ctx->first_free_block; block != ctx->alloc_end; block += len)
236 {
237 len = block->val;
238 /* This block is free, add its length to the shift value */
239 if (len < 0)
240 {
241 shift += len;
242 len = -len;
243 continue;
244 }
245 /* attempt to fill any hole */
246 if (-ctx->first_free_block->val > block->val)
247 {
248 intptr_t size = ctx->first_free_block->val;
249 if (move_block(ctx, block, ctx->first_free_block - block))
250 {
251 /* moving was successful. Mark the next block as the new
252 * first_free_block and merge it with the free space
253 * that the move created */
254 ctx->first_free_block += block->val;
255 ctx->first_free_block->val = size + block->val;
256 continue;
257 }
258 }
259 /* attempt move the allocation by shift */
260 if (shift)
261 {
262 /* failing to move creates a hole, therefore mark this
263 * block as not allocated anymore and move first_free_block up */
264 if (!move_block(ctx, block, shift))
265 {
266 union buflib_data* hole = block + shift;
267 hole->val = shift;
268 if (ctx->first_free_block > hole)
269 ctx->first_free_block = hole;
270 shift = 0;
271 }
272 /* if move was successful, the just moved block is now
273 * possibly in place of the first free one, so move this thing up */
274 else if (ctx->first_free_block == block+shift)
275 {
276 ctx->first_free_block += ctx->first_free_block->val;
277 ctx->first_free_block->val = shift;
278 }
279 }
280 }
281 /* Move the end-of-allocation mark, and return true if any new space has
282 * been freed.
283 */
284 ctx->alloc_end += shift;
285 /* only move first_free_block up if it wasn't already by a hole */
286 if (ctx->first_free_block > ctx->alloc_end)
287 ctx->first_free_block = ctx->alloc_end;
288 ctx->compact = true;
289 return ret || shift;
290}
291
292/* Compact the buffer by trying both shrinking and moving.
293 *
294 * Try to move first. If unsuccesfull, try to shrink. If that was successful
295 * try to move once more as there might be more room now.
296 */
297static bool
298buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
299{
300 bool result = false;
301 /* if something compacted before already there will be no further gain */
302 if (!ctx->compact)
303 result = buflib_compact(ctx);
304 if (!result)
305 {
306 union buflib_data* this;
307 for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val))
308 {
309 if (this->val > 0 && this[2].ops
310 && this[2].ops->shrink_callback)
311 {
312 int ret;
313 int handle = ctx->handle_table - this[1].handle;
314 char* data = this[1].handle->alloc;
315 ret = this[2].ops->shrink_callback(handle, shrink_hints,
316 data, (char*)(this+this->val)-data);
317 result |= (ret == BUFLIB_CB_OK);
318 /* this might have changed in the callback (if
319 * it shrinked from the top), get it again */
320 this = handle_to_block(ctx, handle);
321 }
322 }
323 /* shrinking was successful at least once, try compaction again */
324 if (result)
325 result |= buflib_compact(ctx);
326 }
327
328 return result;
329}
330
331/* Shift buffered items by size units, and update handle pointers. The shift
332 * value must be determined to be safe *before* calling.
333 */
334static void
335buflib_buffer_shift(struct buflib_context *ctx, int shift)
336{
337 memmove(ctx->buf_start + shift, ctx->buf_start,
338 (ctx->alloc_end - ctx->buf_start) * sizeof(union buflib_data));
339 union buflib_data *handle;
340 for (handle = ctx->last_handle; handle < ctx->handle_table; handle++)
341 if (handle->alloc)
342 handle->alloc += shift;
343 ctx->first_free_block += shift;
344 ctx->buf_start += shift;
345 ctx->alloc_end += shift;
346}
347
348/* Shift buffered items up by size bytes, or as many as possible if size == 0.
349 * Set size to the number of bytes freed.
350 */
351void*
352buflib_buffer_out(struct buflib_context *ctx, size_t *size)
353{
354 if (!ctx->compact)
355 buflib_compact(ctx);
356 size_t avail = ctx->last_handle - ctx->alloc_end;
357 size_t avail_b = avail * sizeof(union buflib_data);
358 if (*size && *size < avail_b)
359 {
360 avail = (*size + sizeof(union buflib_data) - 1)
361 / sizeof(union buflib_data);
362 avail_b = avail * sizeof(union buflib_data);
363 }
364 *size = avail_b;
365 void *ret = ctx->buf_start;
366 buflib_buffer_shift(ctx, avail);
367 return ret;
368}
369
370/* Shift buffered items down by size bytes */
371void
372buflib_buffer_in(struct buflib_context *ctx, int size)
373{
374 size /= sizeof(union buflib_data);
375 buflib_buffer_shift(ctx, -size);
376}
377
378/* Allocate a buffer of size bytes, returning a handle for it */
379int
380buflib_alloc(struct buflib_context *ctx, size_t size)
381{
382 return buflib_alloc_ex(ctx, size, "<anonymous>", NULL);
383}
384
385/* Allocate a buffer of size bytes, returning a handle for it.
386 *
387 * The additional name parameter gives the allocation a human-readable name,
388 * the ops parameter points to caller-implemented callbacks for moving and
389 * shrinking. NULL for default callbacks (which do nothing but don't
390 * prevent moving or shrinking)
391 */
392
393int
394buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
395 struct buflib_callbacks *ops)
396{
397 union buflib_data *handle, *block;
398 size_t name_len = name ? B_ALIGN_UP(strlen(name)+1) : 0;
399 bool last;
400 /* This really is assigned a value before use */
401 int block_len;
402 size += name_len;
403 size = (size + sizeof(union buflib_data) - 1) /
404 sizeof(union buflib_data)
405 /* add 4 objects for alloc len, pointer to handle table entry and
406 * name length, and the ops pointer */
407 + 4;
408handle_alloc:
409 handle = handle_alloc(ctx);
410 if (!handle)
411 {
412 /* If allocation has failed, and compaction has succeded, it may be
413 * possible to get a handle by trying again.
414 */
415 if (!ctx->compact && buflib_compact(ctx))
416 goto handle_alloc;
417 else
418 { /* first try to shrink the alloc before the handle table
419 * to make room for new handles */
420 int handle = ctx->handle_table - ctx->last_handle;
421 union buflib_data* last_block = handle_to_block(ctx, handle);
422 struct buflib_callbacks* ops = last_block[2].ops;
423 if (ops && ops->shrink_callback)
424 {
425 char *data = buflib_get_data(ctx, handle);
426 unsigned hint = BUFLIB_SHRINK_POS_BACK | 10*sizeof(union buflib_data);
427 if (ops->shrink_callback(handle, hint, data,
428 (char*)(last_block+last_block->val)-data) == BUFLIB_CB_OK)
429 { /* retry one more time */
430 goto handle_alloc;
431 }
432 }
433 return 0;
434 }
435 }
436
437buffer_alloc:
438 /* need to re-evaluate last before the loop because the last allocation
439 * possibly made room in its front to fit this, so last would be wrong */
440 last = false;
441 for (block = ctx->first_free_block;;block += block_len)
442 {
443 /* If the last used block extends all the way to the handle table, the
444 * block "after" it doesn't have a header. Because of this, it's easier
445 * to always find the end of allocation by saving a pointer, and always
446 * calculate the free space at the end by comparing it to the
447 * last_handle pointer.
448 */
449 if(block == ctx->alloc_end)
450 {
451 last = true;
452 block_len = ctx->last_handle - block;
453 if ((size_t)block_len < size)
454 block = NULL;
455 break;
456 }
457 block_len = block->val;
458 /* blocks with positive length are already allocated. */
459 if(block_len > 0)
460 continue;
461 block_len = -block_len;
462 /* The search is first-fit, any fragmentation this causes will be
463 * handled at compaction.
464 */
465 if ((size_t)block_len >= size)
466 break;
467 }
468 if (!block)
469 {
470 /* Try compacting if allocation failed */
471 unsigned hint = BUFLIB_SHRINK_POS_FRONT |
472 ((size*sizeof(union buflib_data))&BUFLIB_SHRINK_SIZE_MASK);
473 if (buflib_compact_and_shrink(ctx, hint))
474 {
475 goto buffer_alloc;
476 } else {
477 handle->val=1;
478 handle_free(ctx, handle);
479 return 0;
480 }
481 }
482
483 /* Set up the allocated block, by marking the size allocated, and storing
484 * a pointer to the handle.
485 */
486 union buflib_data *name_len_slot;
487 block->val = size;
488 block[1].handle = handle;
489 block[2].ops = ops;
490 strcpy(block[3].name, name);
491 name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len);
492 name_len_slot->val = 1 + name_len/sizeof(union buflib_data);
493 handle->alloc = (char*)(name_len_slot + 1);
494 /* If we have just taken the first free block, the next allocation search
495 * can save some time by starting after this block.
496 */
497 if (block == ctx->first_free_block)
498 ctx->first_free_block += size;
499 block += size;
500 /* alloc_end must be kept current if we're taking the last block. */
501 if (last)
502 ctx->alloc_end = block;
503 /* Only free blocks *before* alloc_end have tagged length. */
504 else if ((size_t)block_len > size)
505 block->val = size - block_len;
506 /* Return the handle index as a positive integer. */
507 return ctx->handle_table - handle;
508}
509
510/* Finds the free block before block, and returns NULL if it's not free */
511static union buflib_data*
512find_free_block_before(struct buflib_context *ctx, union buflib_data* block)
513{
514 union buflib_data *ret = ctx->first_free_block,
515 *next_block = ret;
516
517 /* find the block that's before the current one */
518 while (next_block < block)
519 {
520 ret = next_block;
521 next_block += abs(ret->val);
522 }
523
524 /* If next_block == block, the above loop didn't go anywhere. If it did,
525 * and the block before this one is empty, that is the wanted one
526 */
527 if (next_block == block && ret < block && ret->val < 0)
528 return ret;
529 /* otherwise, e.g. if ret > block, or if the buffer is compact,
530 * there's no free block before */
531 return NULL;
532}
533
534/* Free the buffer associated with handle_num. */
535int
536buflib_free(struct buflib_context *ctx, int handle_num)
537{
538 union buflib_data *handle = ctx->handle_table - handle_num,
539 *freed_block = handle_to_block(ctx, handle_num),
540 *block, *next_block;
541 /* We need to find the block before the current one, to see if it is free
542 * and can be merged with this one.
543 */
544 block = find_free_block_before(ctx, freed_block);
545 if (block)
546 {
547 block->val -= freed_block->val;
548 }
549 else
550 {
551 /* Otherwise, set block to the newly-freed block, and mark it free, before
552 * continuing on, since the code below exects block to point to a free
553 * block which may have free space after it.
554 */
555 block = freed_block;
556 block->val = -block->val;
557 }
558 next_block = block - block->val;
559 /* Check if we are merging with the free space at alloc_end. */
560 if (next_block == ctx->alloc_end)
561 ctx->alloc_end = block;
562 /* Otherwise, the next block might still be a "normal" free block, and the
563 * mid-allocation free means that the buffer is no longer compact.
564 */
565 else {
566 ctx->compact = false;
567 if (next_block->val < 0)
568 block->val += next_block->val;
569 }
570 handle_free(ctx, handle);
571 handle->alloc = NULL;
572 /* If this block is before first_free_block, it becomes the new starting
573 * point for free-block search.
574 */
575 if (block < ctx->first_free_block)
576 ctx->first_free_block = block;
577
578 return 0; /* unconditionally */
579}
580
581/* Return the maximum allocatable memory in bytes */
582size_t
583buflib_available(struct buflib_context* ctx)
584{
585 /* subtract 5 elements for
586 * val, handle, name_len, ops and the handle table entry*/
587 ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5);
588 diff -= 16; /* space for future handles */
589 diff *= sizeof(union buflib_data); /* make it bytes */
590 diff -= 16; /* reserve 16 for the name */
591
592 if (diff > 0)
593 return diff;
594 else
595 return 0;
596}
597
598/*
599 * Allocate all available (as returned by buflib_available()) memory and return
600 * a handle to it
601 *
602 * This grabs a lock which can only be unlocked by buflib_free() or
603 * buflib_shrink(), to protect from further allocations (which couldn't be
604 * serviced anyway).
605 */
606int
607buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size, struct buflib_callbacks *ops)
608{
609 /* limit name to 16 since that's what buflib_available() accounts for it */
610 char buf[16];
611 *size = buflib_available(ctx);
612 strlcpy(buf, name, sizeof(buf));
613
614 return buflib_alloc_ex(ctx, *size, buf, ops);
615}
616
617/* Shrink the allocation indicated by the handle according to new_start and
618 * new_size. Grow is not possible, therefore new_start and new_start + new_size
619 * must be within the original allocation
620 */
621bool
622buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size)
623{
624 char* oldstart = buflib_get_data(ctx, handle);
625 char* newstart = new_start;
626 char* newend = newstart + new_size;
627
628 /* newstart must be higher and new_size not "negative" */
629 if (newstart < oldstart || newend < newstart)
630 return false;
631 union buflib_data *block = handle_to_block(ctx, handle),
632 *old_next_block = block + block->val,
633 /* newstart isn't necessarily properly aligned but it
634 * needn't be since it's only dereferenced by the user code */
635 *aligned_newstart = (union buflib_data*)B_ALIGN_DOWN(newstart),
636 *aligned_oldstart = (union buflib_data*)B_ALIGN_DOWN(oldstart),
637 *new_next_block = (union buflib_data*)B_ALIGN_UP(newend),
638 *new_block, metadata_size;
639
640 /* growing is not supported */
641 if (new_next_block > old_next_block)
642 return false;
643
644 metadata_size.val = aligned_oldstart - block;
645 /* update val and the handle table entry */
646 new_block = aligned_newstart - metadata_size.val;
647 block[0].val = new_next_block - new_block;
648
649 block[1].handle->alloc = newstart;
650 if (block != new_block)
651 {
652 /* move metadata over, i.e. pointer to handle table entry and name
653 * This is actually the point of no return. Data in the allocation is
654 * being modified, and therefore we must successfully finish the shrink
655 * operation */
656 memmove(new_block, block, metadata_size.val*sizeof(metadata_size));
657 /* mark the old block unallocated */
658 block->val = block - new_block;
659 /* find the block before in order to merge with the new free space */
660 union buflib_data *free_before = find_free_block_before(ctx, block);
661 if (free_before)
662 free_before->val += block->val;
663 else if (ctx->first_free_block > block)
664 ctx->first_free_block = block;
665
666 /* We didn't handle size changes yet, assign block to the new one
667 * the code below the wants block whether it changed or not */
668 block = new_block;
669 }
670
671 /* Now deal with size changes that create free blocks after the allocation */
672 if (old_next_block != new_next_block)
673 {
674 if (ctx->alloc_end == old_next_block)
675 ctx->alloc_end = new_next_block;
676 else if (old_next_block->val < 0)
677 { /* enlarge next block by moving it up */
678 new_next_block->val = old_next_block->val - (old_next_block - new_next_block);
679 }
680 else if (old_next_block != new_next_block)
681 { /* creating a hole */
682 /* must be negative to indicate being unallocated */
683 new_next_block->val = new_next_block - old_next_block;
684 }
685 /* update first_free_block for the newly created free space */
686 if (ctx->first_free_block > new_next_block)
687 ctx->first_free_block = new_next_block;
688 }
689
690 return true;
691}
692
693const char* buflib_get_name(struct buflib_context *ctx, int handle)
694{
695 union buflib_data *data = (union buflib_data*)ALIGN_DOWN((intptr_t)buflib_get_data(ctx, handle), sizeof (*data));
696 size_t len = data[-1].val;
697 if (len <= 1)
698 return NULL;
699 return data[-len].name;
700}
701
702#ifdef BUFLIB_DEBUG_BLOCKS
703void buflib_print_allocs(struct buflib_context *ctx,
704 void (*print)(int, const char*))
705{
706 union buflib_data *this, *end = ctx->handle_table;
707 char buf[128];
708 for(this = end - 1; this >= ctx->last_handle; this--)
709 {
710 if (!this->alloc) continue;
711
712 int handle_num;
713 const char *name;
714 union buflib_data *block_start, *alloc_start;
715 intptr_t alloc_len;
716
717 handle_num = end - this;
718 alloc_start = buflib_get_data(ctx, handle_num);
719 name = buflib_get_name(ctx, handle_num);
720 block_start = (union buflib_data*)name - 3;
721 alloc_len = block_start->val * sizeof(union buflib_data);
722
723 snprintf(buf, sizeof(buf),
724 "%s(%d):\t%p\n"
725 " \t%p\n"
726 " \t%ld\n",
727 name?:"(null)", handle_num, block_start, alloc_start, alloc_len);
728 /* handle_num is 1-based */
729 print(handle_num - 1, buf);
730 }
731}
732
733void buflib_print_blocks(struct buflib_context *ctx,
734 void (*print)(int, const char*))
735{
736 char buf[128];
737 int i = 0;
738 for(union buflib_data* this = ctx->buf_start;
739 this < ctx->alloc_end;
740 this += abs(this->val))
741 {
742 snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)",
743 this, this->val,
744 this->val > 0? this[3].name:"<unallocated>");
745 print(i++, buf);
746 }
747}
748#endif
749
750#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
751int buflib_get_num_blocks(struct buflib_context *ctx)
752{
753 int i = 0;
754 for(union buflib_data* this = ctx->buf_start;
755 this < ctx->alloc_end;
756 this += abs(this->val))
757 {
758 i++;
759 }
760 return i;
761}
762
763void buflib_print_block_at(struct buflib_context *ctx, int block_num,
764 char* buf, size_t bufsize)
765{
766 union buflib_data* this = ctx->buf_start;
767 while(block_num > 0 && this < ctx->alloc_end)
768 {
769 this += abs(this->val);
770 block_num -= 1;
771 }
772 snprintf(buf, bufsize, "%8p: val: %4ld (%s)",
773 this, this->val,
774 this->val > 0? this[3].name:"<unallocated>");
775}
776
777#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 @@
38#include "kernel.h" 38#include "kernel.h"
39#include "usb.h" 39#include "usb.h"
40#include "file.h" 40#include "file.h"
41#include "buffer.h" 41#include "core_alloc.h"
42#include "dir.h" 42#include "dir.h"
43#include "storage.h" 43#include "storage.h"
44#if CONFIG_RTC 44#if CONFIG_RTC
@@ -57,6 +57,8 @@
57#else 57#else
58#define MAX_OPEN_DIRS 8 58#define MAX_OPEN_DIRS 8
59#endif 59#endif
60static DIR_CACHED opendirs[MAX_OPEN_DIRS];
61static char opendir_dnames[MAX_OPEN_DIRS][MAX_PATH];
60 62
61#define MAX_PENDING_BINDINGS 2 63#define MAX_PENDING_BINDINGS 2
62struct fdbind_queue { 64struct fdbind_queue {
@@ -571,7 +573,8 @@ int dircache_load(void)
571 } 573 }
572 574
573 allocated_size = maindata.size + DIRCACHE_RESERVE; 575 allocated_size = maindata.size + DIRCACHE_RESERVE;
574 dircache_root = buffer_alloc(allocated_size); 576 int handle = core_alloc("dircache", allocated_size);
577 dircache_root = core_get_data(handle);
575 /* needs to be struct-size aligned so that the pointer arithmetic below works */ 578 /* needs to be struct-size aligned so that the pointer arithmetic below works */
576 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 579 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry));
577 entry_count = maindata.entry_count; 580 entry_count = maindata.entry_count;
@@ -814,6 +817,7 @@ static void generate_dot_d_names(void)
814 strcpy(dot, "."); 817 strcpy(dot, ".");
815 strcpy(dotdot, ".."); 818 strcpy(dotdot, "..");
816} 819}
820
817/** 821/**
818 * Start scanning the disk to build the dircache. 822 * Start scanning the disk to build the dircache.
819 * Either transparent or non-transparent build method is used. 823 * Either transparent or non-transparent build method is used.
@@ -841,11 +845,13 @@ int dircache_build(int last_size)
841 queue_post(&dircache_queue, DIRCACHE_BUILD, 0); 845 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
842 return 2; 846 return 2;
843 } 847 }
844 848
845 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) 849 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
846 { 850 {
851 int handle;
847 allocated_size = last_size + DIRCACHE_RESERVE; 852 allocated_size = last_size + DIRCACHE_RESERVE;
848 dircache_root = buffer_alloc(allocated_size); 853 handle = core_alloc("dircache", allocated_size);
854 dircache_root = core_get_data(handle);
849 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 855 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry));
850 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; 856 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1;
851 dircache_size = 0; 857 dircache_size = 0;
@@ -863,7 +869,8 @@ int dircache_build(int last_size)
863 * after generation the buffer will be compacted with DIRCACHE_RESERVE 869 * after generation the buffer will be compacted with DIRCACHE_RESERVE
864 * free bytes inbetween */ 870 * free bytes inbetween */
865 size_t got_size; 871 size_t got_size;
866 char* buf = buffer_get_buffer(&got_size); 872 int handle = core_alloc_maximum("dircache", &got_size, NULL);
873 char* buf = core_get_data(handle);
867 dircache_root = (struct dircache_entry*)ALIGN_UP(buf, 874 dircache_root = (struct dircache_entry*)ALIGN_UP(buf,
868 sizeof(struct dircache_entry)); 875 sizeof(struct dircache_entry));
869 d_names_start = d_names_end = buf + got_size - 1; 876 d_names_start = d_names_end = buf + got_size - 1;
@@ -902,11 +909,11 @@ int dircache_build(int last_size)
902 allocated_size = (d_names_end - buf); 909 allocated_size = (d_names_end - buf);
903 reserve_used = 0; 910 reserve_used = 0;
904 911
905 buffer_release_buffer(allocated_size); 912 core_shrink(handle, dircache_root, allocated_size);
906 return res; 913 return res;
907fail: 914fail:
908 dircache_disable(); 915 dircache_disable();
909 buffer_release_buffer(0); 916 core_free(handle);
910 return res; 917 return res;
911} 918}
912 919
@@ -942,7 +949,7 @@ void dircache_init(void)
942 memset(opendirs, 0, sizeof(opendirs)); 949 memset(opendirs, 0, sizeof(opendirs));
943 for (i = 0; i < MAX_OPEN_DIRS; i++) 950 for (i = 0; i < MAX_OPEN_DIRS; i++)
944 { 951 {
945 opendirs[i].theent.d_name = buffer_alloc(MAX_PATH); 952 opendirs[i].theent.d_name = opendir_dnames[i];
946 } 953 }
947 954
948 queue_init(&dircache_queue, true); 955 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 @@
1
2#include <string.h>
3#include "core_alloc.h"
4#include "buflib.h"
5#include "buffer.h"
6
7/* not static so it can be discovered by core_get_data() */
8struct buflib_context core_ctx;
9
10void core_allocator_init(void)
11{
12 buffer_init();
13 size_t size;
14 void *start = buffer_get_buffer(&size);
15 buflib_init(&core_ctx, start, size);
16 buffer_release_buffer(size);
17}
18
19int core_alloc(const char* name, size_t size)
20{
21 return buflib_alloc_ex(&core_ctx, size, name, NULL);
22}
23
24int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops)
25{
26 return buflib_alloc_ex(&core_ctx, size, name, ops);
27}
28
29size_t core_available(void)
30{
31 return buflib_available(&core_ctx);
32}
33
34int core_free(int handle)
35{
36 return buflib_free(&core_ctx, handle);
37}
38
39int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops)
40{
41 return buflib_alloc_maximum(&core_ctx, name, size, ops);
42}
43
44bool core_shrink(int handle, void* new_start, size_t new_size)
45{
46 return buflib_shrink(&core_ctx, handle, new_start, new_size);
47}
48
49int core_get_num_blocks(void)
50{
51 return buflib_get_num_blocks(&core_ctx);
52}
53
54void core_print_block_at(int block_num, char* buf, size_t bufsize)
55{
56 buflib_print_block_at(&core_ctx, block_num, buf, bufsize);
57}
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);
58void audio_next(void); 58void audio_next(void);
59void audio_prev(void); 59void audio_prev(void);
60int audio_status(void); 60int audio_status(void);
61size_t audio_buffer_available(void);
61void audio_ff_rewind(long newpos); 62void audio_ff_rewind(long newpos);
62void audio_flush_and_reload_tracks(void); 63void audio_flush_and_reload_tracks(void);
63struct mp3entry* audio_current_track(void); 64struct 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 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* This is a memory allocator designed to provide reasonable management of free
11* space and fast access to allocated data. More than one allocator can be used
12* at a time by initializing multiple contexts.
13*
14* Copyright (C) 2009 Andrew Mahone
15* Copyright (C) 2011 Thomas Martitz
16*
17* This program is free software; you can redistribute it and/or
18* modify it under the terms of the GNU General Public License
19* as published by the Free Software Foundation; either version 2
20* of the License, or (at your option) any later version.
21*
22* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23* KIND, either express or implied.
24*
25****************************************************************************/
26
27#ifndef _BUFLIB_H_
28#define _BUFLIB_H_
29#include <stdint.h>
30#include <stdbool.h>
31#include <string.h>
32
33/* enable single block debugging */
34#define BUFLIB_DEBUG_BLOCK_SINGLE
35
36union buflib_data
37{
38 intptr_t val;
39 char name[1]; /* actually a variable sized string */
40 struct buflib_callbacks* ops;
41 char* alloc;
42 union buflib_data *handle;
43};
44
45struct buflib_context
46{
47 union buflib_data *handle_table;
48 union buflib_data *first_free_handle;
49 union buflib_data *last_handle;
50 union buflib_data *first_free_block;
51 union buflib_data *buf_start;
52 union buflib_data *alloc_end;
53 bool compact;
54};
55
56/**
57 * Callbacks used by the buflib to inform allocation that compaction
58 * is happening (before data is moved)
59 *
60 * Note that buflib tries to move to satisfy new allocations before shrinking.
61 * So if you have something to resize try to do it outside of the callback.
62 *
63 * Regardless of the above, if the allocation is SHRINKABLE, but not
64 * MUST_NOT_MOVE buflib will move the allocation before even attempting to
65 * shrink.
66 */
67struct buflib_callbacks {
68 /**
69 * This is called before data is moved. Use this to fix up any cached
70 * pointers pointing to inside the allocation. The size is unchanged.
71 *
72 * This is not needed if you don't cache the data pointer (but always
73 * call buflib_get_data()) and don't pass pointer to the data to yielding
74 * functions.
75 *
76 * handle: The corresponding handle
77 * current: The current start of the allocation
78 * new: The new start of the allocation, after data movement
79 *
80 * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_MOVE if movement
81 * is impossible at this moment.
82 *
83 * If NULL: this allocation must not be moved around by the buflib when
84 * compation occurs
85 */
86 int (*move_callback)(int handle, void* current, void* new);
87 /**
88 * This is called when the buflib desires to shrink a buffer
89 * in order to satisfy new allocation. This happens when buflib runs
90 * out of memory, e.g. because buflib_alloc_maximum() was called.
91 * Move data around as you need to make space and call core_shrink() as
92 * appropriate from within the callback to complete the shrink operation.
93 * buflib will not move data as part of shrinking.
94 *
95 * hint: bit mask containing hints on how shrinking is desired (see below)
96 * handle: The corresponding handle
97 * start: The old start of the allocation
98 *
99 * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_SHRINK if shirinking
100 * is impossible at this moment.
101 *
102 * if NULL: this allocation cannot be resized.
103 * It is recommended that allocation that must not move are
104 * at least shrinkable
105 */
106 int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size);
107};
108
109#define BUFLIB_SHRINK_POS_MASK ((1<<0|1<<1)<<30)
110#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK)
111#define BUFLIB_SHRINK_POS_FRONT (1u<<31)
112#define BUFLIB_SHRINK_POS_BACK (1u<<30)
113
114/**
115 * Possible return values for the callbacks, some of them can cause
116 * compaction to fail and therefore new allocations to fail
117 */
118/* Everything alright */
119#define BUFLIB_CB_OK 0
120/* Tell buflib that moving failed. Buflib may retry to move at any point */
121#define BUFLIB_CB_CANNOT_MOVE 1
122/* Tell buflib that resizing failed, possibly future making allocations fail */
123#define BUFLIB_CB_CANNOT_SHRINK 1
124
125/**
126 * Initializes buflib with a caller allocated context instance and memory pool.
127 *
128 * The buflib_context instance needs to be passed to every other buflib
129 * function. It's should be considered opaque, even though it is not yet
130 * (that's to make inlining core_get_data() possible). The documentation
131 * of the other functions will not describe the context
132 * instance paramter further as it's obligatory.
133 *
134 * context: The new buflib instance to be initialized, allocated by the caller
135 * size: The size of the memory pool
136 */
137void buflib_init(struct buflib_context *context, void *buf, size_t size);
138
139
140/**
141 * Returns how many bytes left the buflib has to satisfy allocations.
142 *
143 * This function does not yet consider possible compaction so there might
144 * be more space left. This may change in the future.
145 *
146 * Returns: The number of bytes left in the memory pool.
147 */
148size_t buflib_available(struct buflib_context *ctx);
149
150
151/**
152 * Allocates memory from buflib's memory pool
153 *
154 * size: How many bytes to allocate
155 *
156 * Returns: An integer handle identifying this allocation
157 */
158int buflib_alloc(struct buflib_context *context, size_t size);
159
160
161/**
162 * Allocates memory from the buflib's memory pool with additional callbacks
163 * and flags
164 *
165 * name: A string identifier giving this allocation a name
166 * size: How many bytes to allocate
167 * ops: a struct with pointers to callback functions (see above)
168 *
169 * Returns: An integer handle identifying this allocation
170 */
171int buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
172 struct buflib_callbacks *ops);
173
174
175/**
176 * Gets all available memory from buflib, for temporary use.
177 *
178 * Since this effectively makes all future allocations fail (unless
179 * another allocation is freed in the meantime), you should definitely provide
180 * a shrink callback if you plan to hold the buffer for a longer period. This
181 * will allow buflib to permit allocations by shrinking the buffer returned by
182 * this function.
183 *
184 * Note that this currently gives whatever buflib_available() returns. However,
185 * do not depend on this behavior, it may change.
186 *
187 * name: A string identifier giving this allocation a name
188 * size: The actual size will be returned into size
189 * ops: a struct with pointers to callback functions
190 *
191 * Returns: An integer handle identifying this allocation
192 */
193int buflib_alloc_maximum(struct buflib_context* ctx, const char* name,
194 size_t *size, struct buflib_callbacks *ops);
195
196/**
197 * Queries the data pointer for the given handle. It's actually a cheap
198 * operation, don't hesitate using it extensivly.
199 *
200 * Notice that you need to re-query after every direct or indirect yield(),
201 * because compaction can happen by other threads which may get your data
202 * moved around (or you can get notified about changes by callbacks,
203 * see further above).
204 *
205 * handle: The handle corresponding to the allocation
206 *
207 * Returns: The start pointer of the allocation
208 */
209static inline void* buflib_get_data(struct buflib_context *context, int handle)
210{
211 return (void*)(context->handle_table[-handle].alloc);
212}
213
214/**
215 * Shrink the memory allocation associated with the given handle
216 * Mainly intended to be used with the shrink callback, but it can also
217 * be called outside as well, e.g. to give back buffer space allocated
218 * with buflib_alloc_maximum().
219 *
220 * Note that you must move/copy data around yourself before calling this,
221 * buflib will not do this as part of shrinking.
222 *
223 * handle: The handle identifying this allocation
224 * new_start: the new start of the allocation
225 * new_size: the new size of the allocation
226 *
227 * Returns: true if shrinking was successful. Otherwise it returns false,
228 * without having modified memory.
229 *
230 */
231bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size);
232
233/**
234 * Frees memory associated with the given handle
235 *
236 * Returns: 0 (to invalidate handles in one line)
237 */
238int buflib_free(struct buflib_context *context, int handle);
239
240/**
241 * Moves the underlying buflib buffer up by size bytes (as much as
242 * possible for size == 0) without moving the end. This effectively
243 * reduces the available space by taking away managable space from the
244 * front. This space is not available for new allocations anymore.
245 *
246 * To make space available in the front, everything is moved up.
247 * It does _NOT_ call the move callbacks
248 *
249 *
250 * size: size in bytes to move the buffer up (take away). The actual
251 * bytes moved is returned in this
252 * Returns: The new start of the underlying buflib buffer
253 */
254void* buflib_buffer_out(struct buflib_context *ctx, size_t *size);
255
256/**
257 * Moves the underlying buflib buffer down by size bytes without
258 * moving the end. This grows the buflib buffer by adding space to the front.
259 * The new bytes are available for new allocations.
260 *
261 * Everything is moved down, and the new free space will be in the middle.
262 * It does _NOT_ call the move callbacks.
263 *
264 * size: size in bytes to move the buffer down (new free space)
265 */
266void buflib_buffer_in(struct buflib_context *ctx, int size);
267
268/* debugging */
269
270/**
271 * Returns the name, as given to core_alloc() and core_allloc_ex(), of the
272 * allocation associated with the given handle
273 *
274 * handle: The handle indicating the allocation
275 *
276 * Returns: A pointer to the string identifier of the allocation
277 */
278const char* buflib_get_name(struct buflib_context *ctx, int handle);
279
280/**
281 * Prints an overview of all current allocations with the help
282 * of the passed printer helper
283 *
284 * This walks only the handle table and prints only valid allocations
285 *
286 * Only available if BUFLIB_DEBUG_BLOCKS is defined
287 */
288void buflib_print_allocs(struct buflib_context *ctx, void (*print)(int, const char*));
289
290/**
291 * Prints an overview of all blocks in the buflib buffer, allocated
292 * or unallocated, with the help pf the passted printer helper
293 *
294 * This walks the entire buffer and prints unallocated space also.
295 * The output is also different from buflib_print_allocs().
296 *
297 * Only available if BUFLIB_DEBUG_BLOCKS is defined
298 */
299void buflib_print_blocks(struct buflib_context *ctx, void (*print)(int, const char*));
300
301/**
302 * Gets the number of blocks in the entire buffer, allocated or unallocated
303 *
304 * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
305 */
306int buflib_get_num_blocks(struct buflib_context *ctx);
307
308/**
309 * Print information about a single block as indicated by block_num
310 * into buf
311 *
312 * buflib_get_num_blocks() beforehand to get the total number of blocks,
313 * as passing an block_num higher than that is undefined
314 *
315 * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
316 */
317void buflib_print_block_at(struct buflib_context *ctx, int block_num,
318 char* buf, size_t bufsize);
319#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 @@
1
2#ifndef __CORE_ALLOC_H__
3#define __CORE_ALLOC_H__
4#include <string.h>
5#include <stdbool.h>
6#include "buflib.h"
7
8/* All functions below are wrappers for functions in buflib.h, except
9 * they have a predefined context
10 */
11void core_allocator_init(void);
12int core_alloc(const char* name, size_t size);
13int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops);
14int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops);
15bool core_shrink(int handle, void* new_start, size_t new_size);
16int core_free(int handle);
17size_t core_available(void);
18
19/* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call
20 * the move callbacks and are therefore unsafe in the core */
21
22#ifdef BUFLIB_DEBUG_BLOCKS
23void core_print_allocs(void (*print)(const char*));
24void core_print_blocks(void (*print)(const char*));
25#endif
26#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
27int core_get_num_blocks(void);
28void core_print_block_at(int block_num, char* buf, size_t bufsize);
29#endif
30
31static inline void* core_get_data(int handle)
32{
33 extern struct buflib_context core_ctx;
34 return buflib_get_data(&core_ctx, handle);
35}
36#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 @@
31#include "i2c.h" 31#include "i2c.h"
32#include "adc.h" 32#include "adc.h"
33#include "string.h" 33#include "string.h"
34#include "buffer.h" 34#include "core_alloc.h"
35#include "storage.h" 35#include "storage.h"
36#include "rolo.h" 36#include "rolo.h"
37 37
@@ -48,6 +48,7 @@
48 48
49#define IRQ0_EDGE_TRIGGER 0x80 49#define IRQ0_EDGE_TRIGGER 0x80
50 50
51static int rolo_handle;
51#ifdef CPU_PP 52#ifdef CPU_PP
52/* Handle the COP properly - it needs to jump to a function outside SDRAM while 53/* Handle the COP properly - it needs to jump to a function outside SDRAM while
53 * the new firmware is being loaded, and then jump to the start of SDRAM 54 * the new firmware is being loaded, and then jump to the start of SDRAM
@@ -99,7 +100,7 @@ void rolo_restart_cop(void)
99 100
100static void rolo_error(const char *text) 101static void rolo_error(const char *text)
101{ 102{
102 buffer_release_buffer(0); 103 rolo_handle = core_free(rolo_handle);
103 lcd_clear_display(); 104 lcd_clear_display();
104 lcd_puts(0, 0, "ROLO error:"); 105 lcd_puts(0, 0, "ROLO error:");
105 lcd_puts_scroll(0, 1, text); 106 lcd_puts_scroll(0, 1, text);
@@ -240,7 +241,8 @@ int rolo_load(const char* filename)
240 241
241 /* get the system buffer. release only in case of error, otherwise 242 /* get the system buffer. release only in case of error, otherwise
242 * we don't return anyway */ 243 * we don't return anyway */
243 filebuf = buffer_get_buffer(&filebuf_size); 244 rolo_handle = core_alloc_maximum("rolo", &filebuf_size, NULL);
245 filebuf = core_get_data(rolo_handle);
244 246
245#if CONFIG_CPU != SH7034 247#if CONFIG_CPU != SH7034
246 /* Read and save checksum */ 248 /* 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 @@
26#include "panic.h" 26#include "panic.h"
27#include "nand_id.h" 27#include "nand_id.h"
28#include "storage.h" 28#include "storage.h"
29#include "buffer.h"
30 29
31#define SECTOR_SIZE 512 30#define SECTOR_SIZE 512
32 31
@@ -122,8 +121,9 @@ struct lpt_entry
122#ifdef BOOTLOADER 121#ifdef BOOTLOADER
123static struct lpt_entry lpt_lookup[MAX_SEGMENTS]; 122static struct lpt_entry lpt_lookup[MAX_SEGMENTS];
124#else 123#else
125/* buffer_alloc'd in nand_init() when the correct size has been determined */ 124/* core_alloc()'d in nand_init() when the correct size has been determined */
126static struct lpt_entry* lpt_lookup = NULL; 125#include "core_alloc.h"
126static int lpt_handle;
127#endif 127#endif
128 128
129/* Write Caches */ 129/* Write Caches */
@@ -607,6 +607,9 @@ static bool nand_read_sector_of_logical_segment(int log_segment, int sector,
607 int page_in_segment = sector / sectors_per_page; 607 int page_in_segment = sector / sectors_per_page;
608 int sector_in_page = sector % sectors_per_page; 608 int sector_in_page = sector % sectors_per_page;
609 609
610#ifndef BOOTLOADER
611 struct lpt_entry* lpt_lookup = core_get_data(lpt_handle);
612#endif
610 int bank = lpt_lookup[log_segment].bank; 613 int bank = lpt_lookup[log_segment].bank;
611 int phys_segment = lpt_lookup[log_segment].phys_segment; 614 int phys_segment = lpt_lookup[log_segment].phys_segment;
612 615
@@ -918,7 +921,8 @@ int nand_init(void)
918#ifndef BOOTLOADER 921#ifndef BOOTLOADER
919 /* Use chip info to allocate the correct size LPT buffer */ 922 /* Use chip info to allocate the correct size LPT buffer */
920 lptbuf_size = sizeof(struct lpt_entry) * segments_per_bank * total_banks; 923 lptbuf_size = sizeof(struct lpt_entry) * segments_per_bank * total_banks;
921 lpt_lookup = buffer_alloc(lptbuf_size); 924 lpt_handle = core_alloc("lpt lookup", lptbuf_size);
925 struct lpt_entry* lpt_lookup = core_get_data(lpt_handle);
922#else 926#else
923 /* Use a static array in the bootloader */ 927 /* Use a static array in the bootloader */
924 lptbuf_size = sizeof(lpt_lookup); 928 lptbuf_size = sizeof(lpt_lookup);
@@ -968,6 +972,9 @@ int nand_init(void)
968 972
969 if (log_segment < segments_per_bank * total_banks) 973 if (log_segment < segments_per_bank * total_banks)
970 { 974 {
975#ifndef BOOTLOADER
976 lpt_lookup = core_get_data(lpt_handle);
977#endif
971 if (lpt_lookup[log_segment].bank == -1 || 978 if (lpt_lookup[log_segment].bank == -1 ||
972 lpt_lookup[log_segment].phys_segment == -1) 979 lpt_lookup[log_segment].phys_segment == -1)
973 { 980 {
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 @@
30#include "dm320.h" 30#include "dm320.h"
31#include "ata.h" 31#include "ata.h"
32#include "string.h" 32#include "string.h"
33#include "buffer.h" 33#include "core_alloc.h"
34#include "logf.h" 34#include "logf.h"
35#include "ata-defines.h" 35#include "ata-defines.h"
36 36
@@ -202,7 +202,11 @@ struct cfs_direntry_item
202 202
203static bool cfs_inited = false; 203static bool cfs_inited = false;
204static unsigned long cfs_start; 204static unsigned long cfs_start;
205#ifdef BOOTLOADER
205static unsigned long *sectors; 206static unsigned long *sectors;
207#else
208static int sectors_handle;
209#endif
206 210
207#define CFS_START ( ((hdr->partitions[1].start*hdr->sector_size) & ~0xFFFF) + 0x10000 ) 211#define CFS_START ( ((hdr->partitions[1].start*hdr->sector_size) & ~0xFFFF) + 0x10000 )
208#define CFS_CLUSTER2CLUSTER(x) ( (CFS_START/512)+((x)-1)*64 ) 212#define CFS_CLUSTER2CLUSTER(x) ( (CFS_START/512)+((x)-1)*64 )
@@ -299,7 +303,8 @@ static void cfs_init(void)
299 _ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, &sector); 303 _ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, &sector);
300 inode = (struct cfs_inode*)&sector; 304 inode = (struct cfs_inode*)&sector;
301#ifndef BOOTLOADER 305#ifndef BOOTLOADER
302 sectors = (unsigned long*)buffer_alloc(VFAT_SECTOR_SIZE(inode->filesize)); 306 sectors_handle = core_alloc("ata sectors", VFAT_SECTOR_SIZE(inode->filesize));
307 unsigned long *sectors = core_get_data(sectors_handle);
303#else 308#else
304 static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */ 309 static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */
305 sectors = _sector; 310 sectors = _sector;
@@ -322,6 +327,9 @@ static void cfs_init(void)
322 _ata_read_sectors(CFS_CLUSTER2CLUSTER(inode->second_class_chain_second_cluster), 64, &vfat_data[1]); 327 _ata_read_sectors(CFS_CLUSTER2CLUSTER(inode->second_class_chain_second_cluster), 64, &vfat_data[1]);
323 328
324 /* First class chain */ 329 /* First class chain */
330#ifndef BOOTLOADER
331 sectors = core_get_data(sectors_handle);
332#endif
325 for(j=0; j<12; j++) 333 for(j=0; j<12; j++)
326 { 334 {
327 if( (inode->first_class_chain[j] & 0xFFFF) != 0xFFFF && 335 if( (inode->first_class_chain[j] & 0xFFFF) != 0xFFFF &&
@@ -331,6 +339,9 @@ static void cfs_init(void)
331 } 339 }
332 340
333 /* Second class chain */ 341 /* Second class chain */
342#ifndef BOOTLOADER
343 sectors = core_get_data(sectors_handle);
344#endif
334 for(j=0; j<0x8000/4; j++) 345 for(j=0; j<0x8000/4; j++)
335 { 346 {
336 if( (vfat_data[0][j] & 0xFFFF) != 0xFFFF && 347 if( (vfat_data[0][j] & 0xFFFF) != 0xFFFF &&
@@ -351,6 +362,9 @@ static void cfs_init(void)
351 /* Read third class subchain(s) */ 362 /* Read third class subchain(s) */
352 _ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_data[1][j]), 64, &vfat_data[0]); 363 _ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_data[1][j]), 64, &vfat_data[0]);
353 364
365#ifndef BOOTLOADER
366 sectors = core_get_data(sectors_handle);
367#endif
354 for(k=0; k<0x8000/4; k++) 368 for(k=0; k<0x8000/4; k++)
355 { 369 {
356 if( (vfat_data[0][k] & 0xFFFF) != 0xFFFF && 370 if( (vfat_data[0][k] & 0xFFFF) != 0xFFFF &&
@@ -376,6 +390,9 @@ static inline unsigned long map_sector(unsigned long sector)
376 * Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part 390 * Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part
377 * FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes. 391 * FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes.
378 */ 392 */
393#ifndef BOOTLOADER
394 unsigned long *sectors = core_get_data(sectors_handle);
395#endif
379 return cfs_start+sectors[sector/64]*64+sector%64; 396 return cfs_start+sectors[sector/64]*64+sector%64;
380} 397}
381 398