Strip trailing directory slash
[kugel-rb.git] / apps / settings.c
blob00ea560b406949d37b848d7e20c6a0a5a969420e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by wavey@wavey.org
11 * RTC config saving code (C) 2002 by hessu@hes.iki.fi
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include <stdio.h>
21 #include <stddef.h>
22 #include "config.h"
23 #include "kernel.h"
24 #include "thread.h"
25 #include "settings.h"
26 #include "disk.h"
27 #include "panic.h"
28 #include "debug.h"
29 #include "button.h"
30 #include "usb.h"
31 #include "backlight.h"
32 #include "lcd.h"
33 #include "mpeg.h"
34 #include "mp3_playback.h"
35 #include "talk.h"
36 #include "string.h"
37 #include "ata.h"
38 #include "fat.h"
39 #include "power.h"
40 #include "backlight.h"
41 #include "powermgmt.h"
42 #include "status.h"
43 #include "atoi.h"
44 #include "screens.h"
45 #include "ctype.h"
46 #include "file.h"
47 #include "errno.h"
48 #include "system.h"
49 #include "misc.h"
50 #include "timefuncs.h"
51 #ifdef HAVE_LCD_BITMAP
52 #include "icons.h"
53 #include "font.h"
54 #include "peakmeter.h"
55 #include "hwcompat.h"
56 #endif
57 #include "lang.h"
58 #include "language.h"
59 #include "wps-display.h"
60 #include "powermgmt.h"
61 #include "bookmark.h"
62 #include "sprintf.h"
63 #include "keyboard.h"
64 #include "version.h"
65 #include "rtc.h"
66 #if CONFIG_HWCODEC == MAS3507D
67 void dac_line_in(bool enable);
68 #endif
69 struct user_settings global_settings;
70 const char rec_base_directory[] = REC_BASE_DIR;
74 #define CONFIG_BLOCK_VERSION 19
75 #define CONFIG_BLOCK_SIZE 512
76 #define RTC_BLOCK_SIZE 44
78 #ifdef HAVE_LCD_BITMAP
79 #define MAX_LINES 10
80 #else
81 #define MAX_LINES 2
82 #endif
84 long lasttime = 0;
85 static int config_sector = 0; /* mark uninitialized */
86 static unsigned char config_block[CONFIG_BLOCK_SIZE];
89 /* descriptor for a configuration value */
90 /* (watch the struct packing and member sizes to keep this small) */
91 struct bit_entry
93 /* how many bits within the bitfield (1-32), MSB set if value is signed */
94 unsigned char bit_size; /* min 6+1 bit */
95 /* how many bytes in the global_settings struct (1,2,4) */
96 unsigned char byte_size; /* min 3 bits */
97 /* store position in global_settings struct */
98 short settings_offset; /* min 9 bit, better 10 */
99 /* default value */
100 int default_val; /* min 15 bit */
101 /* variable name in a .cfg file, NULL if not to be saved */
102 const char* cfg_name;
103 /* set of values, or NULL for a numerical value */
104 const char* cfg_val;
107 /********************************************
109 Config block as saved on the battery-packed RTC user RAM memory block
110 of 44 bytes, starting at offset 0x14 of the RTC memory space.
112 offset abs
113 0x00 0x14 "Roc" header signature: 0x52 0x6f 0x63
114 0x03 0x17 <version byte: 0x0>
115 0x04 0x18 start of bit-table
117 0x28,0x29 unused, not reachable by set_bits() without disturbing the next 2
118 0x2A,0x2B <checksum 2 bytes: xor of 0x00-0x29>
120 Config memory is reset to 0xff and initialized with 'factory defaults' if
121 a valid header & checksum is not found. Config version number is only
122 increased when information is _relocated_ or space is _reused_ so that old
123 versions can read and modify configuration changed by new versions.
124 Memory locations not used by a given version should not be
125 modified unless the header & checksum test fails.
127 Rest of config block, only saved to disk:
128 0x2C start of 2nd bit-table
130 0xB8 (char[20]) WPS file
131 0xCC (char[20]) Lang file
132 0xE0 (char[20]) Font file
133 0xF4-0xFF <unused>
135 *************************************/
137 /* The persistence of the global_settings members is now controlled by
138 the two tables below, rtc_bits and hd_bits.
139 New values can just be added to the end, it will be backwards
140 compatible. If you however change order, bitsize, etc. of existing
141 entries, you need to bump CONFIG_BLOCK_VERSION to break compatibility.
145 /* convenience macro for both size and offset of global_settings member */
146 #define S_O(val) sizeof(global_settings.val), offsetof(struct user_settings, val)
147 #define SIGNED 0x80 /* for bitsize value with signed attribute */
149 /* some sets of values which are used more than once, to save memory */
150 static const char off_on[] = "off,on";
151 static const char off_on_ask[] = "off,on,ask";
152 static const char graphic_numeric[] = "graphic,numeric";
153 static const char off_number_spell_hover[] = "off,number,spell,hover";
155 /* the part of the settings which ends up in the RTC RAM, where available
156 (those we either need early, save frequently, or without spinup) */
157 static const struct bit_entry rtc_bits[] =
159 /* placeholder, containing the size information */
160 {9, 0, 0, 0, NULL, NULL }, /* 9 bit to tell how far this is populated */
162 /* # of bits, offset+size, default, .cfg name, .cfg values */
163 /* sound */
164 {7, S_O(volume), 70, "volume", NULL }, /* 0...100 */
165 {8 | SIGNED, S_O(balance), 0, "balance", NULL }, /* -100...100 */
166 {5 | SIGNED, S_O(bass), 0, "bass", NULL }, /* -15..+15 / -12..+12 */
167 {5 | SIGNED, S_O(treble), 0, "treble", NULL }, /* -15..+15 / -12..+12 */
168 #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
169 {5, S_O(loudness), 0, "loudness", NULL }, /* 0...17 */
170 {3, S_O(avc), 0, "auto volume", "off,20ms,2,4,8" },
171 {1, S_O(superbass), false, "superbass", off_on },
172 #endif
173 {3, S_O(channel_config), 0, "channels",
174 "stereo,mono,custom,mono left,mono right,karaoke" },
175 {8, S_O(stereo_width), 100, "stereo width", NULL},
176 /* playback */
177 {2, S_O(resume), RESUME_ASK, "resume", "off,ask,ask once,on" },
178 {1, S_O(playlist_shuffle), false, "shuffle", off_on },
179 {16 | SIGNED, S_O(resume_index), -1, NULL, NULL },
180 {16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL },
181 {32 | SIGNED, S_O(resume_offset), -1, NULL, NULL },
182 {32 | SIGNED, S_O(resume_seed), -1, NULL, NULL },
183 {2, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one" },
184 /* LCD */
185 {6, S_O(contrast), 40, "contrast", NULL },
186 #ifdef CONFIG_BACKLIGHT
187 #ifdef HAVE_CHARGING
188 {1, S_O(backlight_on_when_charging), false,
189 "backlight when plugged", off_on },
190 #endif
191 {5, S_O(backlight_timeout), 5, "backlight timeout",
192 "off,on,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60,90" },
193 #endif /* CONFIG_BACKLIGHT */
194 #ifdef HAVE_LCD_BITMAP
195 {1, S_O(invert), false, "invert", off_on },
196 {1, S_O(flip_display), false, "flip display", off_on },
197 /* display */
198 {1, S_O(invert_cursor), false, "invert cursor", off_on },
199 {1, S_O(statusbar), true, "statusbar", off_on },
200 {1, S_O(scrollbar), true, "scrollbar", off_on },
201 #if CONFIG_KEYPAD == RECORDER_PAD
202 {1, S_O(buttonbar), true, "buttonbar", off_on },
203 #endif
204 {1, S_O(volume_type), 0, "volume display", graphic_numeric },
205 {1, S_O(battery_type), 0, "battery display", graphic_numeric },
206 {1, S_O(timeformat), 0, "time format", "24hour,12hour" },
207 #endif
208 {1, S_O(show_icons), true, "show icons", off_on },
209 /* system */
210 {4, S_O(poweroff), 10,
211 "idle poweroff", "off,1,2,3,4,5,6,7,8,9,10,15,30,45,60" },
212 {18, S_O(runtime), 0, NULL, NULL },
213 {18, S_O(topruntime), 0, NULL, NULL },
214 {15, S_O(max_files_in_playlist), 10000,
215 "max files in playlist", NULL }, /* 1000...20000 */
216 {14, S_O(max_files_in_dir), 400,
217 "max files in dir", NULL }, /* 50...10000 */
218 /* battery */
219 #ifdef HAVE_CHARGE_CTRL
220 {1, S_O(discharge), 0, "deep discharge", off_on },
221 {1, S_O(trickle_charge), true, "trickle charge", off_on },
222 #endif
223 {12, S_O(battery_capacity), BATTERY_CAPACITY_MIN, "battery capacity",
224 NULL }, /* 1500...3200 for NiMH, 2200...3200 for LiIon,
225 500...1500 for Alkaline */
226 #ifdef HAVE_CHARGING
227 {1, S_O(car_adapter_mode), false, "car adapter mode", off_on },
228 #endif
229 /* tuner */
230 #ifdef CONFIG_TUNER
231 {1, S_O(fm_force_mono), false, "force fm mono", off_on },
232 {8, S_O(last_frequency), 0, NULL, NULL }, /* Default: MIN_FREQ */
233 #endif
235 /* new stuff to be added here */
236 /* If values are just added to the end, no need to bump the version. */
238 /* Current sum of bits: 259 (worst case) */
239 /* Sum of all bit sizes must not grow beyond 288! */
243 /* the part of the settings which ends up in HD sector only */
244 static const struct bit_entry hd_bits[] =
246 /* This table starts after the 44 RTC bytes = 352 bits. */
247 /* Here we need 11 bits to tell how far this is populated. */
249 /* placeholder, containing the size information */
250 {11, 0, 0, 0, NULL, NULL }, /* 11 bit to tell how far this is populated */
252 /* # of bits, offset+size, default, .cfg name, .cfg values */
253 /* more display */
254 #ifdef CONFIG_BACKLIGHT
255 {1, S_O(caption_backlight), false, "caption backlight", off_on },
256 #endif
257 {4, S_O(scroll_speed), 9, "scroll speed", NULL }, /* 0...15 */
258 {7, S_O(scroll_step), 6, "scroll step", NULL }, /* 1...112 */
259 {8, S_O(scroll_delay), 100, "scroll delay", NULL }, /* 0...250 */
260 {8, S_O(bidir_limit), 50, "bidir limit", NULL }, /* 0...200 */
261 #ifdef HAVE_LCD_CHARCELLS
262 {3, S_O(jump_scroll), 0, "jump scroll", NULL }, /* 0...5 */
263 {8, S_O(jump_scroll_delay), 50, "jump scroll delay", NULL }, /* 0...250 */
264 #endif
265 /* more playback */
266 {1, S_O(play_selected), true, "play selected", off_on },
267 {1, S_O(fade_on_stop), true, "volume fade", off_on },
268 {4, S_O(ff_rewind_min_step), FF_REWIND_1000,
269 "scan min step", "1,2,3,4,5,6,8,10,15,20,25,30,45,60" },
270 {4, S_O(ff_rewind_accel), 3, "scan accel", NULL },
271 {3, S_O(buffer_margin), 0, "antiskip", NULL },
272 /* disk */
273 #ifndef HAVE_MMC
274 #ifdef HAVE_ATA_POWER_OFF
275 {1, S_O(disk_poweroff), false, "disk poweroff", off_on },
276 #endif
277 {8, S_O(disk_spindown), 5, "disk spindown", NULL },
278 #endif
279 /* browser */
280 {3, S_O(dirfilter), SHOW_MUSIC,
281 "show files", "all,supported,music,playlists,id3 database" },
282 {1, S_O(sort_case), false, "sort case", off_on },
283 {1, S_O(browse_current), false, "follow playlist", off_on },
284 /* playlist */
285 {1, S_O(playlist_viewer_icons), true, "playlist viewer icons", off_on },
286 {1, S_O(playlist_viewer_indices), true,
287 "playlist viewer indices", off_on },
288 {1, S_O(playlist_viewer_track_display), 0,
289 "playlist viewer track display", "track name,full path" },
290 {2, S_O(recursive_dir_insert), RECURSE_OFF,
291 "recursive directory insert", off_on_ask },
292 /* bookmarks */
293 {3, S_O(autocreatebookmark), BOOKMARK_NO, "autocreate bookmarks",
294 "off,on,ask,recent only - on,recent only - ask" },
295 {2, S_O(autoloadbookmark), BOOKMARK_NO,
296 "autoload bookmarks", off_on_ask },
297 {2, S_O(usemrb), BOOKMARK_NO,
298 "use most-recent-bookmarks", "off,on,unique only" },
299 #ifdef HAVE_LCD_BITMAP
300 /* peak meter */
301 {5, S_O(peak_meter_clip_hold), 16, "peak meter clip hold", /* 0...25 */
302 "on,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60,90,2min,3min,5min,10min,20min,45min,90min" },
303 {1, S_O(peak_meter_performance), false, "peak meter busy", off_on },
304 {5, S_O(peak_meter_hold), 3, "peak meter hold",
305 "off,200ms,300ms,500ms,1,2,3,4,5,6,7,8,9,10,15,20,30,1min" },
306 {7, S_O(peak_meter_release), 8, "peak meter release", NULL }, /* 0...126 */
307 {1, S_O(peak_meter_dbfs), true, "peak meter dbfs", off_on },
308 {7, S_O(peak_meter_min), 60, "peak meter min", NULL }, /* 0...100 */
309 {7, S_O(peak_meter_max), 0, "peak meter max", NULL }, /* 0...100 */
310 #endif
311 #if CONFIG_HWCODEC == MAS3587F
312 /* recording */
313 {1, S_O(rec_editable), false, "editable recordings", off_on },
314 {4, S_O(rec_timesplit), 0, "rec timesplit", /* 0...15 */
315 "off,00:05,00:10,00:15,00:30,01:00,01:14,01:20,02:00,04:00,06:00,08:00,10:00,12:00,18:00,24:00" },
316 {1, S_O(rec_channels), 0, "rec channels", "stereo,mono" },
317 {4, S_O(rec_mic_gain), 8, "rec mic gain", NULL },
318 {3, S_O(rec_quality), 5, "rec quality", NULL },
319 {2, S_O(rec_source), 0, /* 0=mic */
320 "rec source", "mic,line,spdif" },
321 {3, S_O(rec_frequency), 0, /* 0=44.1kHz */
322 "rec frequency", "44,48,32,22,24,16" },
323 {4, S_O(rec_left_gain), 2, /* 0dB */
324 "rec left gain", NULL }, /* 0...15 */
325 {4, S_O(rec_right_gain), 2, /* 0dB */
326 "rec right gain", NULL }, /* 0...15 */
327 {5, S_O(rec_prerecord_time), 0, "prerecording time", NULL }, /* 0...30 */
328 {1, S_O(rec_directory), 0, /* rec_base_directory */
329 "rec directory", REC_BASE_DIR ",current" },
330 #endif
331 #if CONFIG_HWCODEC == MAS3507D
332 {1, S_O(line_in), false, "line in", off_on },
333 #endif
334 /* voice */
335 {2, S_O(talk_dir), 0, "talk dir", off_number_spell_hover },
336 {2, S_O(talk_file), 0, "talk file", off_number_spell_hover },
337 {1, S_O(talk_menu), true, "talk menu", off_on },
339 /* If values are just added to the end, no need to bump the version. */
340 {2, S_O(sort_file), 0, "sort files", "alpha,oldest,newest,type" },
341 {2, S_O(sort_dir), 0, "sort dirs", "alpha,oldest,newest" },
342 {7, S_O(mdb_strength), 0, "mdb strength", NULL},
343 {7, S_O(mdb_harmonics), 0, "mdb harmonics", NULL},
344 {9, S_O(mdb_center), 0, "mdb center", NULL},
345 {9, S_O(mdb_shape), 0, "mdb shape", NULL},
346 {1, S_O(mdb_enable), 0, "mdb enable", off_on},
347 {1, S_O(id3_v1_first), 0, "id3 tag priority", "v2-v1,v1-v2"},
349 /* new stuff to be added at the end */
351 /* Sum of all bit sizes must not grow beyond 0xB8*8 = 1472 */
355 /* helper function to extract n (<=32) bits from an arbitrary position */
356 static unsigned long get_bits(
357 const unsigned long* p, /* the start of the bitfield array */
358 unsigned int from, /* bit no. to start reading from */
359 unsigned int size) /* how many bits to read */
361 unsigned int bit_index;
362 unsigned int bits_to_use;
364 unsigned long mask;
365 unsigned long result;
367 if (size==1)
368 { /* short cut */
369 return (p[from/32] & 1<<from%32) != 0;
372 result = 0;
373 while (size)
375 bit_index = from % 32;
376 bits_to_use = MIN(32 - bit_index, size);
377 mask = 0xFFFFFFFF >> (32 - bits_to_use);
378 mask <<= bit_index;
380 result <<= bits_to_use; /* from last round */
381 result |= (p[from/32] & mask) >> bit_index;
383 from += bits_to_use;
384 size -= bits_to_use;
387 return result;
390 /* helper function to set n (<=32) bits to an arbitrary position */
391 static void set_bits(
392 unsigned long* p, /* the start of the bitfield array */
393 unsigned int from, /* bit no. to start writing into */
394 unsigned int size, /* how many bits to change */
395 unsigned long value) /* content (LSBs will be taken) */
397 unsigned int end;
398 unsigned int word_index, bit_index;
399 unsigned int bits_to_use;
401 unsigned long mask;
403 if (size==1)
404 { /* short cut */
405 if (value & 1)
406 p[from/32] |= 1<<from%32;
407 else
408 p[from/32] &= ~(1<<from%32);
409 return;
412 end = from + size - 1;
414 /* write back to front, least to most significant */
415 while (size)
417 word_index = end / 32;
418 bit_index = (end % 32) + 1;
419 bits_to_use = MIN(bit_index, size);
420 bit_index -= bits_to_use;
421 mask = 0xFFFFFFFF >> (32 - bits_to_use);
422 mask <<= bit_index;
424 p[word_index] = (p[word_index] & ~mask) | (value<<bit_index & mask);
426 value >>= bits_to_use;
427 size -= bits_to_use;
428 end -= bits_to_use;
433 * Calculates the checksum for the config block and returns it
436 static unsigned short calculate_config_checksum(const unsigned char* buf)
438 unsigned int i;
439 unsigned char cksum[2];
440 cksum[0] = cksum[1] = 0;
442 for (i=0; i < RTC_BLOCK_SIZE - 2; i+=2 ) {
443 cksum[0] ^= buf[i];
444 cksum[1] ^= buf[i+1];
447 return (cksum[0] << 8) | cksum[1];
451 * initialize the config block buffer
453 static void init_config_buffer( void )
455 DEBUGF( "init_config_buffer()\n" );
457 /* reset to 0 - all unused */
458 memset(config_block, 0, CONFIG_BLOCK_SIZE);
459 /* insert header */
460 config_block[0] = 'R';
461 config_block[1] = 'o';
462 config_block[2] = 'c';
463 config_block[3] = CONFIG_BLOCK_VERSION;
467 * save the config block buffer to disk or RTC RAM
469 static int save_config_buffer( void )
471 unsigned short chksum;
472 #ifdef HAVE_RTC
473 unsigned int i;
474 #endif
476 /* update the checksum in the end of the block before saving */
477 chksum = calculate_config_checksum(config_block);
478 config_block[ RTC_BLOCK_SIZE - 2 ] = chksum >> 8;
479 config_block[ RTC_BLOCK_SIZE - 1 ] = chksum & 0xff;
481 #ifdef HAVE_RTC
482 /* FIXME: okay, it _would_ be cleaner and faster to implement rtc_write so
483 that it would write a number of bytes at a time since the RTC chip
484 supports that, but this will have to do for now 8-) */
485 for (i=0; i < RTC_BLOCK_SIZE; i++ ) {
486 int r = rtc_write(0x14+i, config_block[i]);
487 if (r) {
488 DEBUGF( "save_config_buffer: rtc_write failed at addr 0x%02x: %d\n",
489 14+i, r );
490 return r;
494 #endif
496 if (config_sector != 0)
497 ata_delayed_write( config_sector, config_block);
498 else
499 return -1;
501 return 0;
505 * load the config block buffer from disk or RTC RAM
507 static int load_config_buffer(int which)
509 unsigned short chksum;
510 bool correct = false;
513 DEBUGF( "load_config_buffer()\n" );
515 if (which & SETTINGS_HD)
517 if (config_sector != 0) {
518 ata_read_sectors(IF_MV2(0,) config_sector, 1, config_block);
519 /* calculate the checksum, check it and the header */
520 chksum = calculate_config_checksum(config_block);
522 if (config_block[0] == 'R' &&
523 config_block[1] == 'o' &&
524 config_block[2] == 'c' &&
525 config_block[3] == CONFIG_BLOCK_VERSION &&
526 (chksum >> 8) == config_block[RTC_BLOCK_SIZE - 2] &&
527 (chksum & 0xff) == config_block[RTC_BLOCK_SIZE - 1])
529 DEBUGF( "load_config_buffer: header & checksum test ok\n" );
530 correct = true;
535 #ifdef HAVE_RTC
536 if(!correct)
538 /* If the disk sector was incorrect, reinit the buffer */
539 memset(config_block, 0, CONFIG_BLOCK_SIZE);
542 if (which & SETTINGS_RTC)
544 unsigned int i;
545 unsigned char rtc_block[RTC_BLOCK_SIZE];
547 /* read rtc block */
548 for (i=0; i < RTC_BLOCK_SIZE; i++ )
549 rtc_block[i] = rtc_read(0x14+i);
551 chksum = calculate_config_checksum(rtc_block);
553 /* if rtc block is ok, use that */
554 if (rtc_block[0] == 'R' &&
555 rtc_block[1] == 'o' &&
556 rtc_block[2] == 'c' &&
557 rtc_block[3] == CONFIG_BLOCK_VERSION &&
558 (chksum >> 8) == rtc_block[RTC_BLOCK_SIZE - 2] &&
559 (chksum & 0xff) == rtc_block[RTC_BLOCK_SIZE - 1])
561 memcpy(config_block, rtc_block, RTC_BLOCK_SIZE);
562 correct = true;
565 #endif
567 if ( !correct ) {
568 /* if checksum is not valid, clear the config buffer */
569 DEBUGF( "load_config_buffer: header & checksum test failed\n" );
570 init_config_buffer();
571 return -1;
574 return 0;
578 /* helper to save content of global_settings into a bitfield,
579 as described per table */
580 static void save_bit_table(const struct bit_entry* p_table, int count, int bitstart)
582 unsigned long* p_bitfield = (unsigned long*)config_block; /* 32 bit addr. */
583 unsigned long value; /* 32 bit content */
584 int i;
585 const struct bit_entry* p_run = p_table; /* start after the size info */
586 int curr_bit = bitstart + p_table->bit_size;
587 count--; /* first is excluded from loop */
589 for (i=0; i<count; i++)
591 p_run++;
592 /* could do a memcpy, but that would be endian-dependent */
593 switch(p_run->byte_size)
595 case 1:
596 value = ((unsigned char*)&global_settings)[p_run->settings_offset];
597 break;
598 case 2:
599 value = ((unsigned short*)&global_settings)[p_run->settings_offset/2];
600 break;
601 case 4:
602 value = ((unsigned int*)&global_settings)[p_run->settings_offset/4];
603 break;
604 default:
605 DEBUGF( "illegal size!" );
606 continue;
608 set_bits(p_bitfield, curr_bit, p_run->bit_size & 0x3F, value);
609 curr_bit += p_run->bit_size & 0x3F;
611 set_bits(p_bitfield, bitstart, p_table->bit_size, /* write size */
612 curr_bit); /* = position after last element */
616 * figure out the config sector from the partition table and the
617 * mounted file system
619 void settings_calc_config_sector(void)
621 #ifdef SIMULATOR
622 config_sector = 61;
623 #else
624 int i, partition_start;
625 int sector = 0;
627 if (fat_startsector(IF_MV(0)) != 0) /* There is a partition table */
629 sector = 61;
630 for (i = 0; i < 4; i++)
632 partition_start = disk_partinfo(i)->start;
633 if (partition_start != 0 && (partition_start - 2) < sector)
634 sector = partition_start - 2;
636 if (sector < 0)
637 sector = 0;
640 config_sector = sector;
641 #endif
645 * persist all runtime user settings to disk or RTC RAM
647 int settings_save( void )
650 int elapsed_secs;
652 elapsed_secs = (current_tick - lasttime) / HZ;
653 global_settings.runtime += elapsed_secs;
654 lasttime += (elapsed_secs * HZ);
656 if ( global_settings.runtime > global_settings.topruntime )
657 global_settings.topruntime = global_settings.runtime;
660 /* serialize scalar values into RTC and HD sector, specified via table */
661 save_bit_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]), 4*8);
662 save_bit_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]), RTC_BLOCK_SIZE*8);
664 strncpy(&config_block[0xb8], global_settings.wps_file, MAX_FILENAME);
665 strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME);
666 strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME);
668 if(save_config_buffer())
670 lcd_clear_display();
671 #ifdef HAVE_LCD_CHARCELLS
672 lcd_puts(0, 0, str(LANG_SETTINGS_SAVE_PLAYER));
673 lcd_puts(0, 1, str(LANG_SETTINGS_BATTERY_PLAYER));
674 #else
675 lcd_puts(4, 2, str(LANG_SETTINGS_SAVE_RECORDER));
676 lcd_puts(2, 4, str(LANG_SETTINGS_BATTERY_RECORDER));
677 lcd_update();
678 #endif
679 sleep(HZ*2);
680 return -1;
682 return 0;
685 #ifdef HAVE_LCD_BITMAP
687 * Applies the range infos stored in global_settings to
688 * the peak meter.
690 void settings_apply_pm_range(void)
692 int pm_min, pm_max;
694 /* depending on the scale mode (dBfs or percent) the values
695 of global_settings.peak_meter_dbfs have different meanings */
696 if (global_settings.peak_meter_dbfs)
698 /* convert to dBfs * 100 */
699 pm_min = -(((int)global_settings.peak_meter_min) * 100);
700 pm_max = -(((int)global_settings.peak_meter_max) * 100);
702 else
704 /* percent is stored directly -> no conversion */
705 pm_min = global_settings.peak_meter_min;
706 pm_max = global_settings.peak_meter_max;
709 /* apply the range */
710 peak_meter_init_range(global_settings.peak_meter_dbfs, pm_min, pm_max);
712 #endif /* HAVE_LCD_BITMAP */
714 void sound_settings_apply(void)
716 mpeg_sound_set(SOUND_BASS, global_settings.bass);
717 mpeg_sound_set(SOUND_TREBLE, global_settings.treble);
718 mpeg_sound_set(SOUND_BALANCE, global_settings.balance);
719 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
720 mpeg_sound_set(SOUND_CHANNELS, global_settings.channel_config);
721 mpeg_sound_set(SOUND_STEREO_WIDTH, global_settings.stereo_width);
722 #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
723 mpeg_sound_set(SOUND_LOUDNESS, global_settings.loudness);
724 mpeg_sound_set(SOUND_AVC, global_settings.avc);
725 mpeg_sound_set(SOUND_MDB_STRENGTH, global_settings.mdb_strength);
726 mpeg_sound_set(SOUND_MDB_HARMONICS, global_settings.mdb_harmonics);
727 mpeg_sound_set(SOUND_MDB_CENTER, global_settings.mdb_center);
728 mpeg_sound_set(SOUND_MDB_SHAPE, global_settings.mdb_shape);
729 mpeg_sound_set(SOUND_MDB_ENABLE, global_settings.mdb_enable);
730 mpeg_sound_set(SOUND_SUPERBASS, global_settings.superbass);
731 #endif
734 void settings_apply(void)
736 char buf[64];
738 sound_settings_apply();
740 mpeg_set_buffer_margin(global_settings.buffer_margin);
742 lcd_set_contrast(global_settings.contrast);
743 lcd_scroll_speed(global_settings.scroll_speed);
744 backlight_set_timeout(global_settings.backlight_timeout);
745 backlight_set_on_when_charging(global_settings.backlight_on_when_charging);
746 ata_spindown(global_settings.disk_spindown);
747 #if CONFIG_HWCODEC == MAS3507D
748 dac_line_in(global_settings.line_in);
749 #endif
750 #ifdef HAVE_ATA_POWER_OFF
751 ata_poweroff(global_settings.disk_poweroff);
752 #endif
754 set_poweroff_timeout(global_settings.poweroff);
755 #ifdef HAVE_CHARGE_CTRL
756 charge_restart_level = global_settings.discharge ?
757 CHARGE_RESTART_LO : CHARGE_RESTART_HI;
758 enable_trickle_charge(global_settings.trickle_charge);
759 #endif
761 set_battery_capacity(global_settings.battery_capacity);
763 #ifdef HAVE_LCD_BITMAP
764 lcd_set_invert_display(global_settings.invert);
765 lcd_set_flip(global_settings.flip_display);
766 button_set_flip(global_settings.flip_display);
767 lcd_update(); /* refresh after flipping the screen */
768 settings_apply_pm_range();
769 peak_meter_init_times(
770 global_settings.peak_meter_release, global_settings.peak_meter_hold,
771 global_settings.peak_meter_clip_hold);
772 #endif
774 if ( global_settings.wps_file[0] &&
775 global_settings.wps_file[0] != 0xff ) {
776 snprintf(buf, sizeof buf, ROCKBOX_DIR "/%s.wps",
777 global_settings.wps_file);
778 wps_load(buf, false);
780 else
781 wps_reset();
783 #ifdef HAVE_LCD_BITMAP
784 if ( global_settings.font_file[0] &&
785 global_settings.font_file[0] != 0xff ) {
786 snprintf(buf, sizeof buf, ROCKBOX_DIR FONT_DIR "/%s.fnt",
787 global_settings.font_file);
788 font_load(buf);
790 else
791 font_reset();
793 lcd_scroll_step(global_settings.scroll_step);
794 #else
795 lcd_jump_scroll(global_settings.jump_scroll);
796 lcd_jump_scroll_delay(global_settings.jump_scroll_delay * (HZ/10));
797 #endif
798 lcd_bidir_scroll(global_settings.bidir_limit);
799 lcd_scroll_delay(global_settings.scroll_delay * (HZ/10));
801 if ( global_settings.lang_file[0] &&
802 global_settings.lang_file[0] != 0xff ) {
803 snprintf(buf, sizeof buf, ROCKBOX_DIR LANG_DIR "/%s.lng",
804 global_settings.lang_file);
805 lang_load(buf);
806 talk_init(); /* use voice of same language */
809 set_car_adapter_mode(global_settings.car_adapter_mode);
813 /* helper to load global_settings from a bitfield, as described per table */
814 static void load_bit_table(const struct bit_entry* p_table, int count, int bitstart)
816 unsigned long* p_bitfield = (unsigned long*)config_block; /* 32 bit addr. */
817 unsigned long value; /* 32 bit content */
818 int i;
819 int maxbit; /* how many bits are valid in the saved part */
820 const struct bit_entry* p_run = p_table; /* start after the size info */
821 count--; /* first is excluded from loop */
822 maxbit = get_bits(p_bitfield, bitstart, p_table->bit_size);
823 bitstart += p_table->bit_size;
825 for (i=0; i<count; i++)
827 int size;
828 p_run++;
830 size = p_run->bit_size & 0x3F; /* mask off abused bits */
831 if (bitstart + size > maxbit)
832 break; /* exit if this is not valid any more in bitfield */
834 value = get_bits(p_bitfield, bitstart, size);
835 bitstart += size;
836 if (p_run->bit_size & SIGNED)
837 { // sign extend the read value
838 unsigned long mask = 0xFFFFFFFF << (size - 1);
839 if (value & mask) /* true if MSB of value is set */
840 value |= mask;
843 /* could do a memcpy, but that would be endian-dependent */
844 switch(p_run->byte_size)
846 case 1:
847 ((unsigned char*)&global_settings)[p_run->settings_offset] =
848 (unsigned char)value;
849 break;
850 case 2:
851 ((unsigned short*)&global_settings)[p_run->settings_offset/2] =
852 (unsigned short)value;
853 break;
854 case 4:
855 ((unsigned int*)&global_settings)[p_run->settings_offset/4] =
856 (unsigned int)value;
857 break;
858 default:
859 DEBUGF( "illegal size!" );
860 continue;
867 * load settings from disk or RTC RAM
869 void settings_load(int which)
871 DEBUGF( "reload_all_settings()\n" );
873 /* load the buffer from the RTC (resets it to all-unused if the block
874 is invalid) and decode the settings which are set in the block */
875 if (!load_config_buffer(which))
877 /* load scalar values from RTC and HD sector, specified via table */
878 if (which & SETTINGS_RTC)
880 load_bit_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]), 4*8);
882 if (which & SETTINGS_HD)
884 load_bit_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]),
885 RTC_BLOCK_SIZE*8);
888 if ( global_settings.contrast < MIN_CONTRAST_SETTING )
889 global_settings.contrast = lcd_default_contrast();
891 strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME);
892 strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME);
893 strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME);
897 void set_file(char* filename, char* setting, int maxlen)
899 char* fptr = strrchr(filename,'/');
900 int len;
901 int extlen = 0;
902 char* ptr;
904 if (!fptr)
905 return;
907 *fptr = 0;
908 fptr++;
910 len = strlen(fptr);
911 ptr = fptr + len;
912 while (*ptr != '.') {
913 extlen++;
914 ptr--;
917 if (strncasecmp(ROCKBOX_DIR, filename ,strlen(ROCKBOX_DIR)) ||
918 (len-extlen > maxlen))
919 return;
921 strncpy(setting, fptr, len-extlen);
922 setting[len-extlen]=0;
924 settings_save();
927 /* helper to sort a .cfg file entry into a global_settings member,
928 as described per table. Returns the position if found, else 0. */
929 static int load_cfg_table(
930 const struct bit_entry* p_table, /* the table which describes the entries */
931 int count, /* number of entries in the table, including the first */
932 const char* name, /* the item to be searched */
933 const char* value, /* the value which got loaded for that item */
934 int hint) /* position to start looking */
936 int i = hint;
940 if (p_table[i].cfg_name != NULL && !strcasecmp(name, p_table[i].cfg_name))
941 { /* found */
942 int val = 0;
943 if (p_table[i].cfg_val == NULL)
944 { /* numerical value, just convert the string */
945 val = atoi(value);
947 else
948 { /* set of string values, find the index */
949 const char* item;
950 const char* run;
951 int len = strlen(value);
953 item = run = p_table[i].cfg_val;
955 while(1)
957 /* count the length of the field */
958 while (*run != ',' && *run != '\0')
959 run++;
961 if (!strncasecmp(value, item, MAX(run-item, len)))
962 break; /* match, exit the search */
964 if (*run == '\0') /* reached the end of the choices */
965 return i; /* return the position, but don't update */
967 val++; /* count the item up */
968 run++; /* behind the ',' */
969 item = run;
973 /* could do a memcpy, but that would be endian-dependent */
974 switch(p_table[i].byte_size)
976 case 1:
977 ((unsigned char*)&global_settings)[p_table[i].settings_offset] =
978 (unsigned char)val;
979 break;
980 case 2:
981 ((unsigned short*)&global_settings)[p_table[i].settings_offset/2] =
982 (unsigned short)val;
983 break;
984 case 4:
985 ((unsigned int*)&global_settings)[p_table[i].settings_offset/4] =
986 (unsigned int)val;
987 break;
988 default:
989 DEBUGF( "illegal size!" );
990 continue;
993 return i; /* return the position */
996 i++;
997 if (i==count)
998 i=1; /* wraparound */
999 } while (i != hint); /* back where we started, all searched */
1001 return 0; /* indicate not found */
1005 bool settings_load_config(const char* file)
1007 int fd;
1008 char line[128];
1010 fd = open(file, O_RDONLY);
1011 if (fd < 0)
1012 return false;
1014 while (read_line(fd, line, sizeof line) > 0)
1016 char* name;
1017 char* value;
1018 const struct bit_entry* table[2] = { rtc_bits, hd_bits };
1019 const int ta_size[2] = {
1020 sizeof(rtc_bits)/sizeof(rtc_bits[0]),
1021 sizeof(hd_bits)/sizeof(hd_bits[0])
1023 int last_table = 0; /* which table was used last round */
1024 int last_pos = 1; /* at which position did we succeed */
1025 int pos; /* currently returned position */
1027 if (!settings_parseline(line, &name, &value))
1028 continue;
1030 /* check for the string values */
1031 if (!strcasecmp(name, "wps")) {
1032 if (wps_load(value,false))
1033 set_file(value, global_settings.wps_file, MAX_FILENAME);
1035 else if (!strcasecmp(name, "lang")) {
1036 if (!lang_load(value))
1038 set_file(value, global_settings.lang_file, MAX_FILENAME);
1039 talk_init(); /* use voice of same language */
1042 #ifdef HAVE_LCD_BITMAP
1043 else if (!strcasecmp(name, "font")) {
1044 if (font_load(value))
1045 set_file(value, global_settings.font_file, MAX_FILENAME);
1047 #endif
1049 /* check for scalar values, using the two tables */
1050 pos = load_cfg_table(table[last_table], ta_size[last_table],
1051 name, value, last_pos);
1052 if (pos) /* success */
1054 last_pos = pos; /* remember as a position hint for next round */
1055 continue;
1058 last_table = 1-last_table; /* try other table */
1059 last_pos = 1; /* search from start */
1060 pos = load_cfg_table(table[last_table], ta_size[last_table],
1061 name, value, last_pos);
1062 if (pos) /* success */
1064 last_pos = pos; /* remember as a position hint for next round */
1065 continue;
1069 close(fd);
1070 settings_apply();
1071 settings_save();
1072 return true;
1076 /* helper to save content of global_settings into a file,
1077 as described per table */
1078 static void save_cfg_table(const struct bit_entry* p_table, int count, int fd)
1080 long value; /* 32 bit content */
1081 int i;
1082 const struct bit_entry* p_run = p_table; /* start after the size info */
1083 count--; /* first is excluded from loop */
1085 for (i=0; i<count; i++)
1087 p_run++;
1089 if (p_run->cfg_name == NULL)
1090 continue; /* this value is not to be saved */
1092 /* could do a memcpy, but that would be endian-dependent */
1093 switch(p_run->byte_size)
1095 case 1:
1096 if (p_run->bit_size & SIGNED) /* signed? */
1097 value = ((char*)&global_settings)[p_run->settings_offset];
1098 else
1099 value = ((unsigned char*)&global_settings)[p_run->settings_offset];
1100 break;
1101 case 2:
1102 if (p_run->bit_size & SIGNED) /* signed? */
1103 value = ((short*)&global_settings)[p_run->settings_offset/2];
1104 else
1105 value = ((unsigned short*)&global_settings)[p_run->settings_offset/2];
1106 break;
1107 case 4:
1108 value = ((unsigned int*)&global_settings)[p_run->settings_offset/4];
1109 break;
1110 default:
1111 DEBUGF( "illegal size!" );
1112 continue;
1115 if (p_run->cfg_val == NULL) /* write as number */
1117 fprintf(fd, "%s: %d\r\n", p_run->cfg_name, value);
1119 else /* write as item */
1121 const char* p = p_run->cfg_val;
1123 fprintf(fd, "%s: ", p_run->cfg_name);
1125 while(value >= 0)
1127 char c = *p++; /* currently processed char */
1128 if (c == ',') /* separator */
1129 value--;
1130 else if (c == '\0') /* end of string */
1131 break; /* not found */
1132 else if (value == 0) /* the right place */
1133 write(fd, &c, 1); /* char by char, this is lame, OK */
1136 fprintf(fd, "\r\n");
1137 if (p_run->cfg_val != off_on) /* explaination for non-bool */
1138 fprintf(fd, "# (possible values: %s)\r\n", p_run->cfg_val);
1144 bool settings_save_config(void)
1146 bool done = false;
1147 int fd, i;
1148 char filename[MAX_PATH];
1150 /* find unused filename */
1151 for (i=0; ; i++) {
1152 snprintf(filename, sizeof filename, ROCKBOX_DIR "/config%02d.cfg", i);
1153 fd = open(filename, O_RDONLY);
1154 if (fd < 0)
1155 break;
1156 close(fd);
1159 /* allow user to modify filename */
1160 while (!done) {
1161 if (!kbd_input(filename, sizeof filename)) {
1162 fd = creat(filename,0);
1163 if (fd < 0) {
1164 lcd_clear_display();
1165 lcd_puts(0,0,str(LANG_FAILED));
1166 lcd_update();
1167 sleep(HZ);
1169 else
1170 done = true;
1172 else
1173 break;
1176 /* abort if file couldn't be created */
1177 if (!done) {
1178 lcd_clear_display();
1179 lcd_puts(0,0,str(LANG_RESET_DONE_CANCEL));
1180 lcd_update();
1181 sleep(HZ);
1182 return false;
1185 fprintf(fd, "# >>> .cfg file created by rockbox %s <<<\r\n", appsversion);
1186 fprintf(fd, "# >>> http://rockbox.haxx.se <<<\r\n#\r\n");
1187 fprintf(fd, "#\r\n# wps / language / font \r\n#\r\n");
1189 if (global_settings.wps_file[0] != 0)
1190 fprintf(fd, "wps: %s/%s.wps\r\n", ROCKBOX_DIR,
1191 global_settings.wps_file);
1193 if (global_settings.lang_file[0] != 0)
1194 fprintf(fd, "lang: %s/%s.lng\r\n", ROCKBOX_DIR LANG_DIR,
1195 global_settings.lang_file);
1197 #ifdef HAVE_LCD_BITMAP
1198 if (global_settings.font_file[0] != 0)
1199 fprintf(fd, "font: %s/%s.fnt\r\n", ROCKBOX_DIR FONT_DIR,
1200 global_settings.font_file);
1201 #endif
1203 /* here's the action: write values to file, specified via table */
1204 save_cfg_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]), fd);
1205 save_cfg_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]), fd);
1207 close(fd);
1209 lcd_clear_display();
1210 lcd_puts(0,0,str(LANG_SETTINGS_SAVED1));
1211 lcd_puts(0,1,str(LANG_SETTINGS_SAVED2));
1212 lcd_update();
1213 sleep(HZ);
1214 return true;
1218 /* helper to load defaults from table into global_settings members */
1219 static void default_table(const struct bit_entry* p_table, int count)
1221 int i;
1223 for (i=1; i<count; i++) /* exclude the first, the size placeholder */
1225 /* could do a memcpy, but that would be endian-dependent */
1226 switch(p_table[i].byte_size)
1228 case 1:
1229 ((unsigned char*)&global_settings)[p_table[i].settings_offset] =
1230 (unsigned char)p_table[i].default_val;
1231 break;
1232 case 2:
1233 ((unsigned short*)&global_settings)[p_table[i].settings_offset/2] =
1234 (unsigned short)p_table[i].default_val;
1235 break;
1236 case 4:
1237 ((unsigned int*)&global_settings)[p_table[i].settings_offset/4] =
1238 (unsigned int)p_table[i].default_val;
1239 break;
1240 default:
1241 DEBUGF( "illegal size!" );
1242 continue;
1249 * reset all settings to their default value
1251 void settings_reset(void) {
1253 DEBUGF( "settings_reset()\n" );
1255 /* read defaults from table(s) into global_settings */
1256 default_table(rtc_bits, sizeof(rtc_bits)/sizeof(rtc_bits[0]));
1257 default_table(hd_bits, sizeof(hd_bits)/sizeof(hd_bits[0]));
1259 /* do some special cases not covered by table */
1260 global_settings.volume = mpeg_sound_default(SOUND_VOLUME);
1261 global_settings.balance = mpeg_sound_default(SOUND_BALANCE);
1262 global_settings.bass = mpeg_sound_default(SOUND_BASS);
1263 global_settings.treble = mpeg_sound_default(SOUND_TREBLE);
1264 global_settings.loudness = mpeg_sound_default(SOUND_LOUDNESS);
1265 global_settings.avc = mpeg_sound_default(SOUND_AVC);
1266 global_settings.channel_config = mpeg_sound_default(SOUND_CHANNELS);
1267 global_settings.stereo_width = mpeg_sound_default(SOUND_STEREO_WIDTH);
1268 global_settings.mdb_strength = mpeg_sound_default(SOUND_MDB_STRENGTH);
1269 global_settings.mdb_harmonics = mpeg_sound_default(SOUND_MDB_HARMONICS);
1270 global_settings.mdb_center = mpeg_sound_default(SOUND_MDB_CENTER);
1271 global_settings.mdb_shape = mpeg_sound_default(SOUND_MDB_SHAPE);
1272 global_settings.mdb_enable = mpeg_sound_default(SOUND_MDB_ENABLE);
1273 global_settings.superbass = mpeg_sound_default(SOUND_SUPERBASS);
1274 global_settings.contrast = lcd_default_contrast();
1275 global_settings.wps_file[0] = '\0';
1276 global_settings.font_file[0] = '\0';
1277 global_settings.lang_file[0] = '\0';
1281 bool set_bool(const char* string, bool* variable )
1283 return set_bool_options(string, variable,
1284 STR(LANG_SET_BOOL_YES),
1285 STR(LANG_SET_BOOL_NO),
1286 NULL);
1289 /* wrapper to convert from int param to bool param in set_option */
1290 static void (*boolfunction)(bool);
1291 void bool_funcwrapper(int value)
1293 if (value)
1294 boolfunction(true);
1295 else
1296 boolfunction(false);
1299 bool set_bool_options(const char* string, bool* variable,
1300 const char* yes_str, int yes_voice,
1301 const char* no_str, int no_voice,
1302 void (*function)(bool))
1304 struct opt_items names[] = { {no_str, no_voice}, {yes_str, yes_voice} };
1305 bool result;
1307 boolfunction = function;
1308 result = set_option(string, variable, BOOL, names, 2,
1309 function ? bool_funcwrapper : NULL);
1310 return result;
1313 bool set_int(const char* string,
1314 const char* unit,
1315 int voice_unit,
1316 int* variable,
1317 void (*function)(int),
1318 int step,
1319 int min,
1320 int max )
1322 bool done = false;
1323 int button;
1324 int org_value=*variable;
1325 int last_value = 0x7FFFFFFF; /* out of range init */
1327 #ifdef HAVE_LCD_BITMAP
1328 if(global_settings.statusbar)
1329 lcd_setmargins(0, STATUSBAR_HEIGHT);
1330 else
1331 lcd_setmargins(0, 0);
1332 #endif
1334 lcd_clear_display();
1335 lcd_puts_scroll(0, 0, string);
1337 while (!done) {
1338 char str[32];
1339 snprintf(str,sizeof str,"%d %s ", *variable, unit);
1340 lcd_puts(0, 1, str);
1341 #ifdef HAVE_LCD_BITMAP
1342 status_draw(true);
1343 #endif
1344 lcd_update();
1346 if (global_settings.talk_menu && *variable != last_value)
1348 if (voice_unit < UNIT_LAST)
1349 { /* use the available unit definition */
1350 talk_value(*variable, voice_unit, false);
1352 else
1353 { /* say the number, followed by an arbitrary voice ID */
1354 talk_number(*variable, false);
1355 talk_id(voice_unit, true);
1357 last_value = *variable;
1360 button = button_get_w_tmo(HZ/2);
1361 switch(button) {
1362 case SETTINGS_INC:
1363 case SETTINGS_INC | BUTTON_REPEAT:
1364 *variable += step;
1365 break;
1367 case SETTINGS_DEC:
1368 case SETTINGS_DEC | BUTTON_REPEAT:
1369 *variable -= step;
1370 break;
1372 case SETTINGS_OK:
1373 #ifdef SETTINGS_OK2
1374 case SETTINGS_OK2:
1375 #endif
1376 done = true;
1377 break;
1379 case SETTINGS_CANCEL:
1380 #ifdef SETTINGS_CANCEL2
1381 case SETTINGS_CANCEL2:
1382 #endif
1383 if (*variable != org_value) {
1384 *variable=org_value;
1385 lcd_stop_scroll();
1386 lcd_puts(0, 0, str(LANG_MENU_SETTING_CANCEL));
1387 lcd_update();
1388 sleep(HZ/2);
1390 done = true;
1391 break;
1393 default:
1394 if(default_event_handler(button) == SYS_USB_CONNECTED)
1395 return true;
1396 break;
1398 if(*variable > max )
1399 *variable = max;
1401 if(*variable < min )
1402 *variable = min;
1404 if ( function && button != BUTTON_NONE)
1405 function(*variable);
1407 lcd_stop_scroll();
1409 return false;
1412 /* NOTE: the 'type' parameter specifies the actual type of the variable
1413 that 'variable' points to. not the value within. Only variables with
1414 type 'bool' should use parameter BOOL.
1416 The type separation is necessary since int and bool are fundamentally
1417 different and bit-incompatible types and can not share the same access
1418 code. */
1420 bool set_option(const char* string, void* variable, enum optiontype type,
1421 const struct opt_items* options, int numoptions, void (*function)(int))
1423 bool done = false;
1424 int button;
1425 int* intvar = (int*)variable;
1426 bool* boolvar = (bool*)variable;
1427 int oldval = 0;
1428 int index, oldindex = -1; /* remember what we said */
1430 if (type==INT)
1431 oldval=*intvar;
1432 else
1433 oldval=*boolvar;
1435 #ifdef HAVE_LCD_BITMAP
1436 if(global_settings.statusbar)
1437 lcd_setmargins(0, STATUSBAR_HEIGHT);
1438 else
1439 lcd_setmargins(0, 0);
1440 #endif
1442 lcd_clear_display();
1443 lcd_puts_scroll(0, 0, string);
1445 while ( !done ) {
1446 index = type==INT ? *intvar : (int)*boolvar;
1447 lcd_puts(0, 1, P2STR(options[index].string));
1448 if (global_settings.talk_menu && index != oldindex)
1450 talk_id(options[index].voice_id, false);
1451 oldindex = index;
1453 #ifdef HAVE_LCD_BITMAP
1454 status_draw(true);
1455 #endif
1456 lcd_update();
1458 button = button_get_w_tmo(HZ/2);
1459 switch (button) {
1460 case SETTINGS_INC:
1461 case SETTINGS_INC | BUTTON_REPEAT:
1462 if (type == INT) {
1463 if ( *intvar < (numoptions-1) )
1464 (*intvar)++;
1465 else
1466 (*intvar) -= (numoptions-1);
1468 else
1469 *boolvar = !*boolvar;
1470 break;
1472 case SETTINGS_DEC:
1473 case SETTINGS_DEC | BUTTON_REPEAT:
1474 if (type == INT) {
1475 if ( *intvar > 0 )
1476 (*intvar)--;
1477 else
1478 (*intvar) += (numoptions-1);
1480 else
1481 *boolvar = !*boolvar;
1482 break;
1484 case SETTINGS_OK:
1485 #ifdef SETTINGS_OK2
1486 case SETTINGS_OK2:
1487 #endif
1488 done = true;
1489 break;
1491 case SETTINGS_CANCEL:
1492 #ifdef SETTINGS_CANCEL2
1493 case SETTINGS_CANCEL2:
1494 #endif
1495 if (((type==INT) && (*intvar != oldval)) ||
1496 ((type==BOOL) && (*boolvar != (bool)oldval))) {
1497 if (type==INT)
1498 *intvar=oldval;
1499 else
1500 *boolvar=oldval;
1501 lcd_stop_scroll();
1502 lcd_puts(0, 0, str(LANG_MENU_SETTING_CANCEL));
1503 lcd_update();
1504 sleep(HZ/2);
1506 done = true;
1507 break;
1509 default:
1510 if(default_event_handler(button) == SYS_USB_CONNECTED)
1511 return true;
1512 break;
1515 if ( function && button != BUTTON_NONE) {
1516 if (type == INT)
1517 function(*intvar);
1518 else
1519 function(*boolvar);
1522 lcd_stop_scroll();
1523 return false;
1526 #if CONFIG_HWCODEC == MAS3587F
1527 /* This array holds the record timer interval lengths, in seconds */
1528 static const unsigned long rec_timer_seconds[] =
1530 24*60*60, /* OFF really means 24 hours, to avoid >2Gbyte files */
1531 5*60, /* 00:05 */
1532 10*60, /* 00:10 */
1533 15*60, /* 00:15 */
1534 30*60, /* 00:30 */
1535 60*60, /* 01:00 */
1536 74*60, /* 74:00 */
1537 80*60, /* 80:00 */
1538 2*60*60, /* 02:00 */
1539 4*60*60, /* 04:00 */
1540 6*60*60, /* 06:00 */
1541 8*60*60, /* 08:00 */
1542 10*60*60, /* 10:00 */
1543 12*60*60, /* 12:00 */
1544 18*60*60, /* 18:00 */
1545 24*60*60 /* 24:00 */
1548 unsigned int rec_timesplit_seconds(void)
1550 return rec_timer_seconds[global_settings.rec_timesplit];
1552 #endif