summaryrefslogtreecommitdiff
path: root/apps/plugins/plugin_crt0.c
blob: 917ccb11a9413048f61dab62a5a557e420da1d04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/***************************************************************************
*             __________               __   ___.
*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
*                     \/            \/     \/    \/            \/
* $Id$
*
* Copyright (C) 2010 by Thomas Martitz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/


#include "plugin.h"
#include <setjmp.h>

PLUGIN_HEADER

/*
 * EXIT_MAGIC magic, because 0 cannot be used due to setjmp()
 * must be > 0
 */                      
#define EXIT_MAGIC 0x0CDEBABE

extern enum plugin_status plugin_start(const void*);
extern unsigned char plugin_bss_start[];
extern unsigned char plugin_end_addr[];

static jmp_buf __exit_env;
/* only 1 atexit handler for now, chain in the exit handler if you need more */
static void (*atexit_handler)(void);

int rb_atexit(void (*fn)(void))
{
    if (atexit_handler)
        return -1;
    atexit_handler = fn;
    return 0;
}

void exit(int status)
{   /* jump back in time to before starting the plugin */
    longjmp(__exit_env, status != 0 ? status : EXIT_MAGIC);
}

void _exit(int status)
{   /* don't call exit handler */
    atexit_handler = NULL;
    exit(status);
}

enum plugin_status plugin__start(const void *param)
{
    int exit_ret;
    enum plugin_status ret;

#if (CONFIG_PLATFORM & PLATFORM_NATIVE)

/* IRAM must be copied before clearing the BSS ! */
#ifdef PLUGIN_USE_IRAM
    extern char iramcopy[], iramstart[], iramend[], iedata[], iend[];
    size_t iram_size = iramend - iramstart;
    size_t ibss_size = iend - iedata;
    if (iram_size > 0 || ibss_size > 0)
    {
        /* We need to stop audio playback in order to use codec IRAM */
        rb->audio_stop();
        rb->memcpy(iramstart, iramcopy, iram_size);
        rb->memset(iedata, 0, ibss_size);
#ifdef HAVE_CPUCACHE_INVALIDATE
        /* make the icache (if it exists) up to date with the new code */
        rb->cpucache_invalidate();
#endif /* HAVE_CPUCACHE_INVALIDATE */

        /* barrier to prevent reordering iram copy and BSS clearing,
         * because the BSS segment alias the IRAM copy.
         */
        asm volatile ("" ::: "memory");
    }
#endif /* PLUGIN_USE_IRAM */

    /* zero out the bss section */
    rb->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start);

#ifdef HAVE_CPUCACHE_INVALIDATE
    /* Some parts of bss may be used via a no-cache alias (at least
     * portalplayer has this). If we don't clear the cache, those aliases
     * may read garbage */
    rb->cpucache_invalidate();
#endif /* HAVE_CPUCACHE_INVALIDATE */
#endif

    /* we come back here if exit() was called or the plugin returned normally */
    exit_ret = setjmp(__exit_env);
    if (exit_ret == 0)
    {   /* start the plugin */
        ret = plugin_start(param);
    }
    else
    {   /* plugin exit via exit() */
        if (exit_ret == EXIT_MAGIC)
        {   /* exit(EXIT_SUCCESS) */
            ret = PLUGIN_OK;
        }
        else if (exit_ret < INTERNAL_PLUGIN_RETVAL_START)
        {   /* exit(EXIT_FAILURE) */
            ret = PLUGIN_ERROR;
        }
        else
        {   /* exit(PLUGIN_XXX) */
            ret = (enum plugin_status)exit_ret;
        }
    }

    /* before finishing, call the exit handler if there was one */
    if (atexit_handler != NULL)
        atexit_handler();

    return ret;
}

static void cleanup_wrapper(void *param)
{
    (void)param;
    if (atexit_handler)
        atexit_handler();
}

void exit_on_usb(int button)
{   /* the default handler will call the exit handler before
     * showing the usb screen; after that we don't want the exit handler
     * to be called a second time, hence _exit()
     *
     * if not usb, then the handler will only be called if powering off
     * if poweroff, the plugin doesn't want to run any further so exit as well*/
    long result = rb->default_event_handler_ex(button, cleanup_wrapper, NULL);
    if (result == SYS_USB_CONNECTED)
        _exit(PLUGIN_USB_CONNECTED);
    else if (result == SYS_POWEROFF)
        _exit(PLUGIN_POWEROFF);
}