c876d3bbef
rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
771 lines
20 KiB
C
771 lines
20 KiB
C
/***************************************************************************
|
|
* __________ __ ___
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
*
|
|
* Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
|
|
* Copyright (C) 2014 by Mario Basister: iBasso DX90 port
|
|
* Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
|
|
* Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
|
|
*
|
|
* 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 <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <linux/fb.h>
|
|
#include <linux/input.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include "qdbmp.h"
|
|
|
|
|
|
/*- Android logcat ------------------------------------------------------------------------------*/
|
|
|
|
|
|
#ifdef DEBUG
|
|
#include <android/log.h>
|
|
|
|
|
|
static const char log_tag[] = "Rockbox Boot";
|
|
|
|
|
|
void debugf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
__android_log_vprint(ANDROID_LOG_DEBUG, log_tag, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
void ldebugf(const char* file, int line, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
/* 13: 5 literal chars and 8 chars for the line number. */
|
|
char buf[strlen(file) + strlen(fmt) + 13];
|
|
snprintf(buf, sizeof(buf), "%s (%d): %s", file, line, fmt);
|
|
va_start(ap, fmt);
|
|
__android_log_vprint(ANDROID_LOG_DEBUG, log_tag, buf, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
void debug_trace(const char* function)
|
|
{
|
|
static const char trace_tag[] = "TRACE: ";
|
|
char msg[strlen(trace_tag) + strlen(function) + 1];
|
|
snprintf(msg, sizeof(msg), "%s%s", trace_tag, function);
|
|
__android_log_write(ANDROID_LOG_DEBUG, log_tag, msg);
|
|
}
|
|
|
|
|
|
#define DEBUGF debugf
|
|
#define TRACE debug_trace(__func__)
|
|
#else
|
|
#define DEBUGF(...)
|
|
#define TRACE
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/*- Vold monitor --------------------------------------------------------------------------------*/
|
|
|
|
|
|
/*
|
|
Without this socket iBasso Vold will not start.
|
|
iBasso Vold uses this to send status messages about storage devices.
|
|
*/
|
|
static const char VOLD_MONITOR_SOCKET_NAME[] = "UNIX_domain";
|
|
static int _vold_monitor_socket_fd = -1;
|
|
|
|
|
|
static void vold_monitor_open_socket(void)
|
|
{
|
|
TRACE;
|
|
|
|
unlink(VOLD_MONITOR_SOCKET_NAME);
|
|
|
|
_vold_monitor_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if(_vold_monitor_socket_fd < 0)
|
|
{
|
|
_vold_monitor_socket_fd = -1;
|
|
return;
|
|
}
|
|
|
|
struct sockaddr_un addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, VOLD_MONITOR_SOCKET_NAME, sizeof(addr.sun_path) - 1);
|
|
|
|
if(bind(_vold_monitor_socket_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
|
|
{
|
|
close(_vold_monitor_socket_fd);
|
|
unlink(VOLD_MONITOR_SOCKET_NAME);
|
|
_vold_monitor_socket_fd = -1;
|
|
return;
|
|
}
|
|
|
|
if(listen(_vold_monitor_socket_fd, 1) < 0)
|
|
{
|
|
close(_vold_monitor_socket_fd);
|
|
unlink(VOLD_MONITOR_SOCKET_NAME);
|
|
_vold_monitor_socket_fd = -1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
bionic does not have pthread_cancel.
|
|
0: Vold monitor thread stopped/ending.
|
|
1: Vold monitor thread started/running.
|
|
*/
|
|
static volatile sig_atomic_t _vold_monitor_active = 0;
|
|
|
|
|
|
/* true: sdcard not mounted. */
|
|
static bool _sdcard_not_mounted = true;
|
|
|
|
|
|
/* Mutex for sdcard mounted flag. */
|
|
static pthread_mutex_t _sdcard_mount_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
/* Signal condition for sdcard mounted flag. */
|
|
static pthread_cond_t _sdcard_mount_cond = PTHREAD_COND_INITIALIZER;
|
|
|
|
|
|
static void* vold_monitor_run(void* nothing)
|
|
{
|
|
_vold_monitor_active = 1;
|
|
|
|
(void) nothing;
|
|
|
|
DEBUGF("DEBUG %s: Thread start.", __func__);
|
|
|
|
vold_monitor_open_socket();
|
|
if(_vold_monitor_socket_fd < 0)
|
|
{
|
|
DEBUGF("ERROR %s: Thread end: No socket.", __func__);
|
|
|
|
_vold_monitor_active = 0;
|
|
return 0;
|
|
}
|
|
|
|
struct pollfd fds[1];
|
|
fds[0].fd = _vold_monitor_socket_fd;
|
|
fds[0].events = POLLIN;
|
|
|
|
while(_vold_monitor_active == 1)
|
|
{
|
|
poll(fds, 1, 10);
|
|
if(! (fds[0].revents & POLLIN))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int socket_fd = accept(_vold_monitor_socket_fd, NULL, NULL);
|
|
|
|
if(socket_fd < 0)
|
|
{
|
|
DEBUGF("ERROR %s: accept failed.", __func__);
|
|
|
|
continue;
|
|
}
|
|
|
|
while(true)
|
|
{
|
|
char msg[1024];
|
|
memset(msg, 0, sizeof(msg));
|
|
int length = read(socket_fd, msg, sizeof(msg));
|
|
|
|
if(length <= 0)
|
|
{
|
|
close(socket_fd);
|
|
break;
|
|
}
|
|
|
|
DEBUGF("DEBUG %s: msg: %s", __func__, msg);
|
|
|
|
if(strcmp(msg, "Volume flash /mnt/sdcard state changed from 3 (Checking) to 4 (Mounted)") == 0)
|
|
{
|
|
pthread_mutex_lock(&_sdcard_mount_mtx);
|
|
_sdcard_not_mounted = false;
|
|
pthread_cond_signal(&_sdcard_mount_cond);
|
|
pthread_mutex_unlock(&_sdcard_mount_mtx);
|
|
}
|
|
}
|
|
}
|
|
|
|
close(_vold_monitor_socket_fd);
|
|
unlink(VOLD_MONITOR_SOCKET_NAME);
|
|
_vold_monitor_socket_fd = -1;
|
|
|
|
DEBUGF("DEBUG %s: Thread end.", __func__);
|
|
|
|
_vold_monitor_active = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Vold monitor thread. */
|
|
static pthread_t _vold_monitor_thread;
|
|
|
|
|
|
static void vold_monitor_start(void)
|
|
{
|
|
TRACE;
|
|
|
|
if(_vold_monitor_active == 0)
|
|
{
|
|
pthread_create(&_vold_monitor_thread, NULL, vold_monitor_run, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
static void vold_monitor_stop(void)
|
|
{
|
|
TRACE;
|
|
|
|
if(_vold_monitor_active == 1)
|
|
{
|
|
_vold_monitor_active = 0;
|
|
int ret = pthread_join(_vold_monitor_thread, NULL);
|
|
DEBUGF("DEBUG %s: Thread joined: ret: %d.", __func__, ret);
|
|
}
|
|
}
|
|
|
|
|
|
/*- Input handler -------------------------------------------------------------------------------*/
|
|
|
|
|
|
/* Input devices monitored with poll API. */
|
|
static struct pollfd* _fds = NULL;
|
|
|
|
|
|
/* Number of input devices monitored with poll API. */
|
|
static nfds_t _nfds = 0;
|
|
|
|
|
|
/* The names of the devices in _fds. */
|
|
static char** _device_names = NULL;
|
|
|
|
|
|
/* Open device device_name and add it to the list of polled devices. */
|
|
static void open_device(const char* device_name)
|
|
{
|
|
int fd = open(device_name, O_RDONLY);
|
|
if(fd == -1)
|
|
{
|
|
DEBUGF("ERROR %s: open failed on %s.", __func__, device_name);
|
|
exit(-1);
|
|
}
|
|
|
|
struct pollfd* new_fds = realloc(_fds, sizeof(struct pollfd) * (_nfds + 1));
|
|
if(new_fds == NULL)
|
|
{
|
|
DEBUGF("ERROR %s: realloc for _fds failed.", __func__);
|
|
exit(-1);
|
|
}
|
|
|
|
_fds = new_fds;
|
|
_fds[_nfds].fd = fd;
|
|
_fds[_nfds].events = POLLIN;
|
|
|
|
char** new_device_names = realloc(_device_names, sizeof(char*) * (_nfds + 1));
|
|
if(new_device_names == NULL)
|
|
{
|
|
DEBUGF("ERROR %s: realloc for _device_names failed.", __func__);
|
|
exit(-1);
|
|
}
|
|
|
|
_device_names = new_device_names;
|
|
_device_names[_nfds] = strdup(device_name);
|
|
if(_device_names[_nfds] == NULL)
|
|
{
|
|
DEBUGF("ERROR %s: strdup failed.", __func__);
|
|
exit(-1);
|
|
}
|
|
|
|
++_nfds;
|
|
|
|
DEBUGF("DEBUG %s: Opened device %s.", __func__, device_name);
|
|
}
|
|
|
|
|
|
static void button_init_device(void)
|
|
{
|
|
TRACE;
|
|
|
|
if((_fds != NULL) || (_nfds != 0) || (_device_names != NULL))
|
|
{
|
|
DEBUGF("ERROR %s: Allready initialized.", __func__);
|
|
return;
|
|
}
|
|
|
|
/* The input device directory. */
|
|
static const char device_path[] = "/dev/input";
|
|
|
|
/* Path delimeter. */
|
|
static const char delimeter[] = "/";
|
|
|
|
/* Open all devices in device_path. */
|
|
DIR* dir = opendir(device_path);
|
|
if(dir == NULL)
|
|
{
|
|
DEBUGF("ERROR %s: opendir failed: errno: %d.", __func__, errno);
|
|
exit(errno);
|
|
}
|
|
|
|
char device_name[PATH_MAX];
|
|
strcpy(device_name, device_path);
|
|
strcat(device_name, delimeter);
|
|
char* device_name_idx = device_name + strlen(device_name);
|
|
|
|
struct dirent* dir_entry;
|
|
while((dir_entry = readdir(dir)))
|
|
{
|
|
if( ((dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '\0'))
|
|
|| ((dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '.') && (dir_entry->d_name[2] == '\0')))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
strcpy(device_name_idx, dir_entry->d_name);
|
|
|
|
/* Open and add device to _fds. */
|
|
open_device(device_name);
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
/* Sanity check. */
|
|
if(_nfds < 2)
|
|
{
|
|
DEBUGF("ERROR %s: No input devices.", __func__);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
|
|
#define EVENT_TYPE_BUTTON 1
|
|
|
|
|
|
#define EVENT_CODE_BUTTON_PWR_LONG 117
|
|
#define EVENT_CODE_BUTTON_REV 160
|
|
#define EVENT_CODE_BUTTON_NEXT 162
|
|
|
|
|
|
#define EVENT_TYPE_TOUCHSCREEN 3
|
|
|
|
|
|
#define EVENT_CODE_TOUCHSCREEN_X 53
|
|
|
|
|
|
enum user_choice
|
|
{
|
|
CHOICE_NONE = -1,
|
|
CHOICE_MANGO,
|
|
CHOICE_ROCKBOX,
|
|
CHOICE_POWEROFF
|
|
};
|
|
|
|
|
|
static int get_user_choice(void)
|
|
{
|
|
TRACE;
|
|
|
|
button_init_device();
|
|
|
|
enum user_choice choice = CHOICE_NONE;
|
|
|
|
while(choice == CHOICE_NONE)
|
|
{
|
|
/* Poll all input devices. */
|
|
poll(_fds, _nfds, 0);
|
|
|
|
nfds_t fds_idx = 0;
|
|
for( ; fds_idx < _nfds; ++fds_idx)
|
|
{
|
|
if(! (_fds[fds_idx].revents & POLLIN))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
struct input_event event;
|
|
if(read(_fds[fds_idx].fd, &event, sizeof(event)) < (int) sizeof(event))
|
|
{
|
|
DEBUGF("ERROR %s: Read of input devices failed.", __func__);
|
|
continue;
|
|
}
|
|
|
|
DEBUGF("DEBUG %s: device: %s, event.type: %d, event.code: %d, event.value: %d", __func__, _device_names[fds_idx], event.type, event.code, event.value);
|
|
|
|
if(event.type == EVENT_TYPE_BUTTON)
|
|
{
|
|
switch(event.code)
|
|
{
|
|
case EVENT_CODE_BUTTON_REV:
|
|
{
|
|
choice = CHOICE_MANGO;
|
|
break;
|
|
}
|
|
|
|
case EVENT_CODE_BUTTON_NEXT:
|
|
{
|
|
choice = CHOICE_ROCKBOX;
|
|
break;
|
|
}
|
|
|
|
case EVENT_CODE_BUTTON_PWR_LONG:
|
|
{
|
|
choice = CHOICE_POWEROFF;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if((event.type == EVENT_TYPE_TOUCHSCREEN) && (event.code == EVENT_CODE_TOUCHSCREEN_X))
|
|
{
|
|
if(event.value < 160)
|
|
{
|
|
choice = CHOICE_MANGO;
|
|
}
|
|
else
|
|
{
|
|
choice = CHOICE_ROCKBOX;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(_fds)
|
|
{
|
|
nfds_t fds_idx = 0;
|
|
for( ; fds_idx < _nfds; ++fds_idx)
|
|
{
|
|
close(_fds[fds_idx].fd);
|
|
}
|
|
free(_fds);
|
|
_fds = NULL;
|
|
}
|
|
|
|
if(_device_names)
|
|
{
|
|
nfds_t fds_idx = 0;
|
|
for( ; fds_idx < _nfds; ++fds_idx)
|
|
{
|
|
free(_device_names[fds_idx]);
|
|
}
|
|
free(_device_names);
|
|
_device_names = NULL;
|
|
}
|
|
|
|
_nfds = 0;
|
|
|
|
return choice;
|
|
}
|
|
|
|
|
|
/*
|
|
Changing bit, when hold switch is toggled.
|
|
Bit is off when hold switch is engaged.
|
|
*/
|
|
#define HOLD_SWITCH_BIT 16
|
|
|
|
|
|
static bool check_for_hold(void)
|
|
{
|
|
TRACE;
|
|
|
|
char hold_state;
|
|
|
|
FILE* f = fopen("/sys/class/axppower/holdkey", "r");
|
|
fscanf(f, "%c", &hold_state);
|
|
fclose(f);
|
|
|
|
return(! (hold_state & HOLD_SWITCH_BIT));
|
|
}
|
|
|
|
|
|
/*- Display -------------------------------------------------------------------------------------*/
|
|
|
|
|
|
static void draw(const char* file)
|
|
{
|
|
DEBUGF("DEBUG %s: file: %s.", __func__, file);
|
|
|
|
int dev_fd = open("/dev/graphics/fb0", O_RDWR);
|
|
if(dev_fd == -1)
|
|
{
|
|
DEBUGF("ERROR %s: open failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
|
|
exit(errno);
|
|
}
|
|
|
|
/* Get fixed screen information. */
|
|
struct fb_fix_screeninfo finfo;
|
|
if(ioctl(dev_fd, FBIOGET_FSCREENINFO, &finfo) < 0)
|
|
{
|
|
DEBUGF("ERROR %s: ioctl FBIOGET_FSCREENINFO failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
|
|
exit(errno);
|
|
}
|
|
|
|
/* Get the changeable information. */
|
|
struct fb_var_screeninfo vinfo;
|
|
if(ioctl(dev_fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
|
|
{
|
|
DEBUGF("ERROR %s: ioctl FBIOGET_VSCREENINFO failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
|
|
exit(errno);
|
|
}
|
|
|
|
DEBUGF("DEBUG %s: bits_per_pixel: %u, width: %u, height: %u.", __func__, vinfo.bits_per_pixel, vinfo.width, vinfo.height);
|
|
|
|
size_t screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
|
|
|
|
/* ToDo: Is this needed? */
|
|
vinfo.xres = 320;
|
|
vinfo.xres_virtual = 320;
|
|
vinfo.width = 320;
|
|
vinfo.yres = 240;
|
|
vinfo.yres_virtual = 240;
|
|
vinfo.height = 240;
|
|
vinfo.xoffset = 0;
|
|
vinfo.yoffset = 0;
|
|
vinfo.sync = 0;
|
|
vinfo.vmode = 0;
|
|
vinfo.pixclock = 104377;
|
|
vinfo.left_margin = 20;
|
|
vinfo.right_margin = 50;
|
|
vinfo.upper_margin = 2;
|
|
vinfo.lower_margin = 4;
|
|
vinfo.hsync_len = 10;
|
|
vinfo.vsync_len = 2;
|
|
vinfo.red.offset = 11;
|
|
vinfo.red.length = 5;
|
|
vinfo.red.msb_right = 0;
|
|
vinfo.green.offset = 5;
|
|
vinfo.green.length = 6;
|
|
vinfo.green.msb_right = 0;
|
|
vinfo.blue.offset = 0;
|
|
vinfo.blue.length = 5;
|
|
vinfo.blue.msb_right = 0;
|
|
vinfo.transp.offset = 0;
|
|
vinfo.transp.length = 0;
|
|
vinfo.transp.msb_right = 0;
|
|
vinfo.nonstd = 4;
|
|
if(ioctl(dev_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0)
|
|
{
|
|
DEBUGF("ERROR %s: ioctl FBIOPUT_VSCREENINFO failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
|
|
exit(errno);
|
|
}
|
|
|
|
/* Map the device to memory. */
|
|
char* dev_fb = (char*) mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, 0);
|
|
if(dev_fb == MAP_FAILED)
|
|
{
|
|
DEBUGF("ERROR %s: mmap failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
|
|
exit(errno);
|
|
}
|
|
|
|
BMP* bmp = BMP_ReadFile(file);
|
|
if(BMP_GetError() != BMP_OK )
|
|
{
|
|
DEBUGF("ERROR %s: BMP_ReadFile failed on %s: %d.", __func__, file, BMP_GetError());
|
|
exit(BMP_GetError());
|
|
}
|
|
|
|
int y = 0;
|
|
for( ; y < 240; ++y)
|
|
{
|
|
int x = 0;
|
|
for( ; x < 320; ++x)
|
|
{
|
|
long int position = (x + vinfo.xoffset) * (vinfo.bits_per_pixel / 8 )
|
|
+ (y + vinfo.yoffset) * finfo.line_length;
|
|
UCHAR r, g, b;
|
|
BMP_GetPixelRGB(bmp, x, y, &r, &g, &b);
|
|
unsigned short int pixel = (r >> 3) << 11 | (g >> 2) << 5 | (b >> 3);
|
|
*((unsigned short int*)(dev_fb + position)) = pixel;
|
|
}
|
|
}
|
|
|
|
BMP_Free(bmp);
|
|
munmap(dev_fb, screensize);
|
|
close(dev_fd);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
static const char ROCKBOX_BIN[] = "/mnt/sdcard/.rockbox/rockbox";
|
|
static const char OF_PLAYER_BIN[] = "/system/bin/MangoPlayer_original";
|
|
static const char PLAYER_FILE[] = "/data/chosen_player";
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
TRACE;
|
|
|
|
/*
|
|
Create the iBasso Vold socket and monitor it.
|
|
Do this early to not block Vold.
|
|
*/
|
|
vold_monitor_start();
|
|
|
|
int last_chosen_player = CHOICE_NONE;
|
|
|
|
FILE* f = fopen(PLAYER_FILE, "r");
|
|
if(f != NULL)
|
|
{
|
|
fscanf(f, "%d", &last_chosen_player);
|
|
fclose(f);
|
|
}
|
|
|
|
DEBUGF("DEBUG %s: Current player choice: %d.", __func__, last_chosen_player);
|
|
|
|
if(check_for_hold() || (last_chosen_player == CHOICE_NONE))
|
|
{
|
|
draw("/system/chooser.bmp");
|
|
|
|
enum user_choice choice = get_user_choice();
|
|
|
|
if(choice == CHOICE_POWEROFF)
|
|
{
|
|
reboot(RB_POWER_OFF);
|
|
while(true)
|
|
{
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
if(choice != last_chosen_player)
|
|
{
|
|
last_chosen_player = choice;
|
|
|
|
f = fopen(PLAYER_FILE, "w");
|
|
fprintf(f, "%d", last_chosen_player);
|
|
fclose(f);
|
|
}
|
|
|
|
DEBUGF("DEBUG %s: New player choice: %d.", __func__, last_chosen_player);
|
|
}
|
|
|
|
/* true, Rockbox was started at least once. */
|
|
bool rockboxStarted = false;
|
|
|
|
while(true)
|
|
{
|
|
/* Excecute OF MangoPlayer or Rockbox and restart it if it crashes. */
|
|
|
|
if(last_chosen_player == CHOICE_ROCKBOX)
|
|
{
|
|
if(rockboxStarted)
|
|
{
|
|
/*
|
|
At this point it is assumed, that Rockbox has exited due to a USB connection
|
|
triggering a remount of the internal storage for mass storage access.
|
|
Rockbox will eventually restart, when /mnt/sdcard becomes available again.
|
|
*/
|
|
draw("/system/usb.bmp");
|
|
}
|
|
|
|
pthread_mutex_lock(&_sdcard_mount_mtx);
|
|
while(_sdcard_not_mounted)
|
|
{
|
|
DEBUGF("DEBUG %s: Waiting on /mnt/sdcard/.", __func__);
|
|
|
|
pthread_cond_wait(&_sdcard_mount_cond, &_sdcard_mount_mtx);
|
|
|
|
DEBUGF("DEBUG %s: /mnt/sdcard/ available.", __func__);
|
|
}
|
|
pthread_mutex_unlock(&_sdcard_mount_mtx);
|
|
|
|
/* To be able to execute rockbox. */
|
|
system("mount -o remount,exec /mnt/sdcard");
|
|
|
|
/* This symlink is needed mainly to keep themes functional. */
|
|
system("ln -s /mnt/sdcard/.rockbox /.rockbox");
|
|
|
|
if(access(ROCKBOX_BIN, X_OK) != -1)
|
|
{
|
|
/* Start Rockbox. */
|
|
|
|
/* Rockbox has its own vold monitor. */
|
|
vold_monitor_stop();
|
|
|
|
DEBUGF("DEBUG %s: Excecuting %s.", __func__, ROCKBOX_BIN);
|
|
|
|
int ret_code = system(ROCKBOX_BIN);
|
|
rockboxStarted = true;
|
|
|
|
DEBUGF("DEBUG %s: ret_code: %d.", __func__, ret_code);
|
|
|
|
if(WIFEXITED(ret_code) && (WEXITSTATUS(ret_code) == 42))
|
|
{
|
|
/*
|
|
Rockbox terminated to prevent a froced shutdown due to a USB connection
|
|
triggering a remount of the internal storage for mass storage access.
|
|
*/
|
|
_sdcard_not_mounted = true;
|
|
}
|
|
/* else Rockbox crashed ... */
|
|
|
|
vold_monitor_start();
|
|
}
|
|
else
|
|
{
|
|
/* Rockbox executable missing. Show info screen for 30 seconds. */
|
|
draw("/system/rbmissing.bmp");
|
|
sleep(30);
|
|
|
|
/* Do not block Vold, so stop after sleep. */
|
|
vold_monitor_stop();
|
|
|
|
#ifdef DEBUG
|
|
system("setprop persist.sys.usb.config adb");
|
|
system("setprop persist.usb.debug 1");
|
|
#endif
|
|
|
|
DEBUGF("DEBUG %s: Rockbox missing, excecuting %s.", __func__, OF_PLAYER_BIN);
|
|
|
|
/* Start OF MangoPlayer. */
|
|
int ret_code = system(OF_PLAYER_BIN);
|
|
|
|
DEBUGF("DEBUG %s: ret_code: %d.", __func__, ret_code);
|
|
}
|
|
}
|
|
else /* if(last_chosen_player == CHOICE_MANGO) */
|
|
{
|
|
vold_monitor_stop();
|
|
|
|
DEBUGF("DEBUG %s: Excecuting %s.", __func__, OF_PLAYER_BIN);
|
|
|
|
int ret_code = system(OF_PLAYER_BIN);
|
|
|
|
DEBUGF("DEBUG %s: ret_code: %d.", __func__, ret_code);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|