Don't die on opendir() failure. Index .mp2 files too.
[kugel-rb.git] / apps / plugin.c
blobc1796ac1d23e2552c6997f949eeded1a94b8c16c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdbool.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <atoi.h>
23 #include <timefuncs.h>
24 #include <ctype.h>
25 #include "debug.h"
26 #include "button.h"
27 #include "lcd.h"
28 #include "dir.h"
29 #include "file.h"
30 #include "kernel.h"
31 #include "sprintf.h"
32 #include "screens.h"
33 #include "misc.h"
34 #include "mas.h"
35 #include "plugin.h"
36 #include "lang.h"
37 #include "keyboard.h"
38 #include "mpeg.h"
39 #include "buffer.h"
40 #include "mp3_playback.h"
41 #include "backlight.h"
42 #include "ata.h"
43 #include "talk.h"
44 #include "mp3data.h"
45 #include "powermgmt.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "peakmeter.h"
49 #include "widgets.h"
50 #endif
52 #ifdef SIMULATOR
53 #include <debug.h>
54 #ifdef WIN32
55 #include "plugin-win32.h"
56 #else
57 #include <dlfcn.h>
58 #endif
59 #define PREFIX(_x_) sim_ ## _x_
60 #else
61 #define PREFIX(_x_) _x_
62 #endif
64 #define PLUGIN_BUFFER_SIZE 0x8000
66 #ifdef SIMULATOR
67 static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE];
68 #else
69 extern unsigned char pluginbuf[];
70 #include "bitswap.h"
71 #endif
73 static bool plugin_loaded = false;
74 static int plugin_size = 0;
75 #ifndef SIMULATOR
76 static void (*pfn_timer)(void) = NULL; /* user timer handler */
77 #endif
78 static void (*pfn_tsr_exit)(void) = NULL; /* TSR exit callback */
80 static int plugin_test(int api_version, int model, int memsize);
82 static const struct plugin_api rockbox_api = {
83 PLUGIN_API_VERSION,
85 plugin_test,
87 /* lcd */
88 lcd_clear_display,
89 lcd_puts,
90 lcd_puts_scroll,
91 lcd_stop_scroll,
92 lcd_set_contrast,
93 #ifdef HAVE_LCD_CHARCELLS
94 lcd_define_pattern,
95 lcd_get_locked_pattern,
96 lcd_unlock_pattern,
97 lcd_putc,
98 lcd_put_cursor,
99 lcd_remove_cursor,
100 lcd_icon,
101 #else
102 lcd_putsxy,
103 lcd_puts_style,
104 lcd_puts_scroll_style,
105 lcd_bitmap,
106 lcd_drawline,
107 lcd_clearline,
108 lcd_drawpixel,
109 lcd_clearpixel,
110 lcd_setfont,
111 font_get,
112 lcd_clearrect,
113 lcd_fillrect,
114 lcd_drawrect,
115 lcd_invertrect,
116 lcd_getstringsize,
117 lcd_update,
118 lcd_update_rect,
119 scrollbar,
120 checkbox,
121 &lcd_framebuffer[0][0],
122 lcd_blit,
123 #ifndef SIMULATOR
124 lcd_roll,
125 #endif
126 #endif
127 backlight_on,
128 backlight_off,
129 backlight_set_timeout,
130 splash,
132 /* button */
133 button_get,
134 button_get_w_tmo,
135 button_status,
136 button_clear_queue,
138 /* file */
139 (open_func)PREFIX(open),
140 PREFIX(close),
141 (read_func)read,
142 lseek,
143 (creat_func)PREFIX(creat),
144 (write_func)write,
145 PREFIX(remove),
146 PREFIX(rename),
147 ftruncate,
148 PREFIX(filesize),
149 fprintf,
150 read_line,
151 settings_parseline,
152 #ifndef SIMULATOR
153 ata_sleep,
154 #endif
156 /* dir */
157 PREFIX(opendir),
158 PREFIX(closedir),
159 PREFIX(readdir),
161 /* kernel/ system */
162 PREFIX(sleep),
163 yield,
164 &current_tick,
165 default_event_handler,
166 default_event_handler_ex,
167 create_thread,
168 remove_thread,
169 reset_poweroff_timer,
170 #ifndef SIMULATOR
171 system_memory_guard,
172 #endif
174 /* strings and memory */
175 snprintf,
176 strcpy,
177 strncpy,
178 strlen,
179 strrchr,
180 strcmp,
181 strcasecmp,
182 strncasecmp,
183 memset,
184 memcpy,
185 #ifndef SIMULATOR
186 _ctype_,
187 #endif
188 atoi,
190 /* sound */
191 mpeg_sound_set,
192 #ifndef SIMULATOR
193 mp3_play_data,
194 mp3_play_pause,
195 mp3_play_stop,
196 mp3_is_playing,
197 bitswap,
198 #endif
200 /* playback control */
201 mpeg_play,
202 mpeg_stop,
203 mpeg_pause,
204 mpeg_resume,
205 mpeg_next,
206 mpeg_prev,
207 mpeg_ff_rewind,
208 mpeg_next_track,
209 playlist_amount,
210 mpeg_status,
211 mpeg_has_changed_track,
212 mpeg_current_track,
213 mpeg_flush_and_reload_tracks,
214 mpeg_get_file_pos,
215 mpeg_get_last_header,
216 #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
217 mpeg_set_pitch,
218 #endif
220 /* MAS communication */
221 #ifndef SIMULATOR
222 mas_readmem,
223 mas_writemem,
224 mas_readreg,
225 mas_writereg,
226 #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
227 mas_codec_writereg,
228 mas_codec_readreg,
229 #endif
230 #endif
232 /* misc */
233 srand,
234 rand,
235 (qsort_func)qsort,
236 kbd_input,
237 get_time,
238 set_time,
239 plugin_get_buffer,
240 plugin_get_mp3_buffer,
241 #ifndef SIMULATOR
242 plugin_register_timer,
243 plugin_unregister_timer,
244 #endif
245 plugin_tsr,
246 #if defined(DEBUG) || defined(SIMULATOR)
247 debugf,
248 #endif
249 &global_settings,
250 mp3info,
251 count_mp3_frames,
252 create_xing_header,
253 find_next_frame,
254 battery_level,
255 battery_level_safe,
256 #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
257 peak_meter_scale_value,
258 peak_meter_set_use_dbfs,
259 peak_meter_get_use_dbfs,
260 #endif
262 /* new stuff at the end, sort into place next time
263 the API gets incompatible */
267 int plugin_load(const char* plugin, void* parameter)
269 enum plugin_status (*plugin_start)(struct plugin_api* api, void* param);
270 int rc;
271 char buf[64];
272 #ifdef SIMULATOR
273 void* pd;
274 char path[256];
275 #else
276 int fd;
277 #endif
278 #ifdef HAVE_LCD_BITMAP
279 int xm,ym;
280 #endif
282 if (pfn_tsr_exit != NULL) /* if we have a resident old plugin: */
284 pfn_tsr_exit(); /* force it to exit now */
285 pfn_tsr_exit = NULL;
288 #ifdef HAVE_LCD_BITMAP
289 lcd_clear_display();
290 xm = lcd_getxmargin();
291 ym = lcd_getymargin();
292 lcd_setmargins(0,0);
293 lcd_update();
294 #else
295 lcd_clear_display();
296 #endif
297 #ifdef SIMULATOR
298 snprintf(path, sizeof path, "archos%s", plugin);
300 pd = dlopen(path, RTLD_NOW);
301 if (!pd) {
302 snprintf(buf, sizeof buf, str(LANG_PLUGIN_CANT_OPEN), plugin);
303 splash(HZ*2, true, buf);
304 DEBUGF("dlopen(%s): %s\n",path,dlerror());
305 dlclose(pd);
306 return -1;
309 plugin_start = dlsym(pd, "plugin_start");
310 if (!plugin_start) {
311 plugin_start = dlsym(pd, "_plugin_start");
312 if (!plugin_start) {
313 splash(HZ*2, true, "Can't find entry point");
314 dlclose(pd);
315 return -1;
318 #else
319 fd = open(plugin, O_RDONLY);
320 if (fd < 0) {
321 snprintf(buf, sizeof buf, str(LANG_PLUGIN_CANT_OPEN), plugin);
322 splash(HZ*2, true, buf);
323 return fd;
326 /* zero out plugin buffer to ensure a properly zeroed bss area */
327 memset(pluginbuf, 0, PLUGIN_BUFFER_SIZE);
329 plugin_start = (void*)&pluginbuf;
330 plugin_size = read(fd, plugin_start, PLUGIN_BUFFER_SIZE);
331 close(fd);
332 if (plugin_size < 0) {
333 /* read error */
334 snprintf(buf, sizeof buf, str(LANG_READ_FAILED), plugin);
335 splash(HZ*2, true, buf);
336 return -1;
338 if (plugin_size == 0) {
339 /* loaded a 0-byte plugin, implying it's not for this model */
340 splash(HZ*2, true, str(LANG_PLUGIN_WRONG_MODEL));
341 return -1;
343 #endif
345 plugin_loaded = true;
346 rc = plugin_start((struct plugin_api*) &rockbox_api, parameter);
347 /* explicitly casting the pointer here to avoid touching every plugin. */
349 button_clear_queue();
351 plugin_loaded = false;
353 switch (rc) {
354 case PLUGIN_OK:
355 break;
357 case PLUGIN_USB_CONNECTED:
358 return PLUGIN_USB_CONNECTED;
360 case PLUGIN_WRONG_API_VERSION:
361 splash(HZ*2, true, str(LANG_PLUGIN_WRONG_VERSION));
362 break;
364 case PLUGIN_WRONG_MODEL:
365 splash(HZ*2, true, str(LANG_PLUGIN_WRONG_MODEL));
366 break;
368 default:
369 splash(HZ*2, true, str(LANG_PLUGIN_ERROR));
370 break;
373 #ifdef SIMULATOR
374 dlclose(pd);
375 #endif
377 #ifdef HAVE_LCD_BITMAP
378 /* restore margins */
379 lcd_setmargins(xm,ym);
380 #endif
382 return PLUGIN_OK;
385 /* Returns a pointer to the portion of the plugin buffer that is not already
386 being used. If no plugin is loaded, returns the entire plugin buffer */
387 void* plugin_get_buffer(int* buffer_size)
389 int buffer_pos;
391 if (plugin_loaded)
393 if (plugin_size >= PLUGIN_BUFFER_SIZE)
394 return NULL;
396 *buffer_size = PLUGIN_BUFFER_SIZE-plugin_size;
397 buffer_pos = plugin_size;
399 else
401 *buffer_size = PLUGIN_BUFFER_SIZE;
402 buffer_pos = 0;
405 return &pluginbuf[buffer_pos];
408 /* Returns a pointer to the mp3 buffer.
409 Playback gets stopped, to avoid conflicts. */
410 void* plugin_get_mp3_buffer(int* buffer_size)
412 #ifdef SIMULATOR
413 static unsigned char buf[1700*1024];
414 *buffer_size = sizeof(buf);
415 return buf;
416 #else
417 mpeg_stop();
418 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
419 *buffer_size = mp3end - mp3buf;
420 return mp3buf;
421 #endif
424 #ifndef SIMULATOR
425 /* Register a periodic time callback, called every "cycles" CPU clocks.
426 Note that this function will be called in interrupt context! */
427 int plugin_register_timer(int cycles, int prio, void (*timer_callback)(void))
429 int phi = 0; /* bits for the prescaler */
430 int prescale = 1;
432 while (cycles > 0x10000)
433 { /* work out the smallest prescaler that makes it fit */
434 phi++;
435 prescale *= 2;
436 cycles /= 2;
439 if (prescale > 8 || cycles == 0 || prio < 1 || prio > 15)
440 return 0; /* error, we can't do such period, bad argument */
442 and_b(~0x10, &TSTR); /* Stop the timer 4 */
443 and_b(~0x10, &TSNC); /* No synchronization */
444 and_b(~0x10, &TMDR); /* Operate normally */
446 pfn_timer = timer_callback; /* install 2nd level ISR */
448 and_b(~0x01, &TSR4);
449 TIER4 = 0xF9; /* Enable GRA match interrupt */
451 GRA4 = (unsigned short)(cycles - 1);
452 TCR4 = 0x20 | phi; /* clear at GRA match, set prescaler */
453 IPRD = (IPRD & 0xFF0F) | prio << 4; /* interrupt priority */
454 or_b(0x10, &TSTR); /* start timer 4 */
456 return cycles * prescale; /* return the actual period, in CPU clocks */
459 /* disable the user timer */
460 void plugin_unregister_timer(void)
462 and_b(~0x10, &TSTR); /* stop the timer 4 */
463 IPRD = (IPRD & 0xFF0F); /* disable interrupt */
464 pfn_timer = NULL;
467 /* interrupt handler for user timer */
468 #pragma interrupt
469 void IMIA4(void)
471 if (pfn_timer != NULL)
472 pfn_timer(); /* call the user timer function */
473 and_b(~0x01, &TSR4); /* clear the interrupt */
475 #endif /* #ifndef SIMULATOR */
477 /* The plugin wants to stay resident after leaving its main function, e.g.
478 runs from timer or own thread. The callback is registered to later
479 instruct it to free its resources before a new plugin gets loaded. */
480 void plugin_tsr(void (*exit_callback)(void))
482 pfn_tsr_exit = exit_callback; /* remember the callback for later */
486 static int plugin_test(int api_version, int model, int memsize)
488 if (api_version < PLUGIN_MIN_API_VERSION ||
489 api_version > PLUGIN_API_VERSION)
490 return PLUGIN_WRONG_API_VERSION;
492 if (model != MODEL)
493 return PLUGIN_WRONG_MODEL;
495 if (memsize != MEM)
496 return PLUGIN_WRONG_MODEL;
498 return PLUGIN_OK;