#include <unistd.h>
#include <signal.h>
#include <asm/ucontext.h>
-#include <pthread.h>
#include <errno.h>
#include <time.h>
+#include <sched.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
+#include <sys/syscall.h>
#include <linux/soundcard.h>
#include <linux/fb.h>
+#include <linux/futex.h>
#include "header.h"
#include "../common/host_fb.h"
#include "../common/cmn.h"
-#include "sys_cacheflush.h"
+#include "syscalls.h"
#include "realfuncs.h"
+#include "llibc.h"
#if (DBG & 2) && !(DBG & 4)
#define LOG_IO_UNK
#endif
#ifdef LOG_SEGV
-#define segvlog printf
+#define segvlog g_printf
#else
#define segvlog(...)
#endif
typedef unsigned short u16;
typedef unsigned char u8;
-static pthread_mutex_t fb_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t fb_cond = PTHREAD_COND_INITIALIZER;
+#define THREAD_STACK_SIZE 0x200000
+
+static int fb_sync_thread_paused;
+static int fb_sync_thread_futex;
static struct {
u32 dstctrl;
if ((a & ~0xffff) == 0x7f000000)
reg = regnames[a & 0xffff];
- printf(fmt, pfx, a, d, reg);
+ g_printf(fmt, pfx, a, d, reg);
}
#endif
u32 *r = &blitter.dstctrl; \
int i; \
for (i = 0; i < 4*4; i++, r++) { \
- printf("%08x ", *r); \
+ g_printf("%08x ", *r); \
if ((i & 3) == 3) \
- printf("\n"); \
+ g_printf("\n"); \
} \
}
}
}
- if (to_screen)
- pthread_cond_signal(&fb_cond);
+ if (to_screen) {
+ fb_sync_thread_futex = 1;
+ g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
+ }
return;
bad_blit:
}
}
-#define ts_add_nsec(ts, ns) { \
- ts.tv_nsec += ns; \
- if (ts.tv_nsec >= 1000000000) { \
- ts.tv_sec++; \
- ts.tv_nsec -= 1000000000; \
- } \
-}
-
-static int fb_sync_thread_paused;
-
static void *fb_sync_thread(void *arg)
{
+ unsigned long sigmask[2] = { ~0ul, ~0ul };
+ struct timespec ts = { 0, 0 };
int invalid_fb_addr = 1;
int manual_refresh = 0;
int frame_counter = 0;
- struct timespec ts;
- int ret, wait_ret;
+ int wait_ret;
+
+ // this thread can't run any signal handlers since the
+ // app's stack/tls stuff will never be set up here
+ sigmask[0] &= ~(1ul << (SIGSEGV - 1));
+ g_rt_sigprocmask_raw(SIG_SETMASK, sigmask, NULL, sizeof(sigmask));
- //ret = pthread_setschedprio(pthread_self(), -1);
- //log("pthread_setschedprio %d\n", ret);
//ret = setpriority(PRIO_PROCESS, 0, -1);
//log("setpriority %d\n", ret);
- ret = clock_gettime(CLOCK_REALTIME, &ts);
- if (ret != 0) {
- perror(PFX "clock_gettime");
- exit(1);
- }
+ // tell the main thread we're done init
+ fb_sync_thread_futex = 0;
+ g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
while (1) {
u8 *gp2x_fb, *gp2x_fb_end;
- ret = pthread_mutex_lock(&fb_mutex);
- wait_ret = pthread_cond_timedwait(&fb_cond, &fb_mutex, &ts);
- ret |= pthread_mutex_unlock(&fb_mutex);
+ wait_ret = g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 0, &ts);
- if (ret != 0) {
- err("fb_thread: mutex error: %d\n", ret);
- sleep(1);
- goto check_keys;
- }
- if (wait_ret != 0 && wait_ret != ETIMEDOUT) {
- err("fb_thread: cond error: %d\n", wait_ret);
+ // this is supposed to be done atomically, but to make life
+ // easier ignore it for now, race impact is low anyway
+ fb_sync_thread_futex = 0;
+
+ if (wait_ret != 0 && wait_ret != -EWOULDBLOCK
+ && wait_ret != -ETIMEDOUT)
+ {
+ err("fb_thread: futex error: %d\n", wait_ret);
sleep(1);
goto check_keys;
}
if (fb_sync_thread_paused) {
- ts_add_nsec(ts, 100000000);
+ ts.tv_nsec = 100000000;
goto check_keys;
}
- if (wait_ret != ETIMEDOUT) {
- clock_gettime(CLOCK_REALTIME, &ts);
- ts_add_nsec(ts, 50000000);
+ if (wait_ret == 0) {
+ ts.tv_nsec = 50000000;
manual_refresh++;
if (manual_refresh == 2)
dbg("fb_thread: switch to manual refresh\n");
} else {
- ts_add_nsec(ts, 16666667);
+ ts.tv_nsec = 16666667;
if (manual_refresh > 1)
dbg("fb_thread: switch to auto refresh\n");
manual_refresh = 0;
switch (a_) {
case 0x0a00: // TCOUNT, 1/7372800s
- clock_gettime(CLOCK_REALTIME, &ts);
+ g_clock_gettime_raw(CLOCK_REALTIME, &ts);
t64 = (u64)ts.tv_sec * 1000000000 + ts.tv_nsec;
// t * 7372800.0 / 1000000000 * 0x100000000 ~= t * 31665935
t64 *= 31665935;
return;
case 0x2914:
mmsp2.mlc_stl_adrh = d;
- if (mmsp2.mlc_stl_adr != mmsp2.old_mlc_stl_adr)
+ if (mmsp2.mlc_stl_adr != mmsp2.old_mlc_stl_adr) {
// ask for refresh
- pthread_cond_signal(&fb_cond);
+ fb_sync_thread_futex = 1;
+ g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
+ }
mmsp2.old_mlc_stl_adr = mmsp2.mlc_stl_adr;
return;
case 0x2958:
case 0x4038: // MLCADDRESS0
case 0x406c: // MLCADDRESS1
pollux.mlc_stl_adr = d;
- if (d != mmsp2.old_mlc_stl_adr)
+ if (d != mmsp2.old_mlc_stl_adr) {
// ask for refresh
- pthread_cond_signal(&fb_cond);
+ fb_sync_thread_futex = 1;
+ g_futex_raw(&fb_sync_thread_futex, FUTEX_WAKE, 1, NULL);
+ }
mmsp2.old_mlc_stl_adr = d;
return;
case 0x403c: // MLCPALETTE0
.sa_sigaction = segv_sigaction,
.sa_flags = SA_SIGINFO,
};
- pthread_t tid;
void *pret;
int ret;
#ifdef PND
if (geteuid() == 0) {
- fprintf(stderr, "don't try to run as root, device registers or memory "
- "might get trashed crashing the OS or even damaging the device.\n");
+ err("don't try to run as root, device registers or memory "
+ "might get trashed crashing the OS or even damaging the device.\n");
exit(1);
}
#endif
exit(1);
}
- ret = pthread_create(&tid, NULL, fb_sync_thread, NULL);
- if (ret != 0) {
- err("failed to create fb_sync_thread: %d\n", ret);
+ pret = mmap(NULL, THREAD_STACK_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
+ if (mmsp2.umem == MAP_FAILED) {
+ perror(PFX "mmap thread stack");
exit(1);
}
- pthread_detach(tid);
+ fb_sync_thread_futex = 1;
+ ret = g_clone(CLONE_VM | CLONE_FS | CLONE_FILES
+ | CLONE_SIGHAND | CLONE_THREAD,
+ (char *)pret + THREAD_STACK_SIZE, 0, 0, 0,
+ fb_sync_thread);
+ if (ret == 0 || ret == -1) {
+ perror(PFX "start fb thread");
+ exit(1);
+ }
+ g_futex_raw(&fb_sync_thread_futex, FUTEX_WAIT, 1, NULL);
// defaults
mmsp2.mlc_stl_adr = 0x03101000; // fb2 is at 0x03381000
sigaction(SIGSEGV, &segv_action, NULL);
}
-int emu_read_gpiodev(void *buf, int count)
+long emu_read_gpiodev(void *buf, int count)
{
if (count <= 0) {
err("gpiodev read %d?\n", count);
- return -1;
+ return -EINVAL;
}
if (count > 4)
count = 4;
return count;
}
-static void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset)
+static long emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset)
{
u8 *umem, *umem_end;
// SoC regs
if ((offset & ~0x1ffff) == 0xc0000000) {
- return mmap((void *)0x7f000000, length, PROT_NONE,
+ return g_mmap2_raw((void *)0x7f000000, length, PROT_NONE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
}
// MMSP2 blitter
if ((offset & ~0xffff) == 0xe0020000) {
- return mmap((void *)0x7f100000, length, PROT_NONE,
+ return g_mmap2_raw((void *)0x7f100000, length, PROT_NONE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
}
// upper mem
if ((offset & 0xfe000000) != 0x02000000) {
err("unexpected devmem mmap @ %08x\n", offset);
- errno = EINVAL;
- return MAP_FAILED;
+ return -EINVAL;
}
umem = uppermem_lookup(offset, &umem_end);
offset, umem + length - umem_end);
dbg("upper mem @ %08x %x = %p\n", offset, length, umem);
- return umem;
+ return (long)umem;
}
-void *emu_do_mmap(unsigned int length, int prot, int flags, int fd, unsigned int offset)
+long emu_do_mmap(unsigned int length, int prot, int flags, int fd,
+ unsigned int offset)
{
if (fd == FAKEDEV_MEM)
return emu_mmap_dev(length, prot, flags, offset);
return emu_mmap_dev(length, prot, flags, offset + 0x03381000);
err("bad/ni mmap(?, %d, %x, %x, %d, %08x)\n", length, prot, flags, fd, offset);
- errno = EINVAL;
- return MAP_FAILED;
+ return -EINVAL;
}
-int emu_do_munmap(void *addr, unsigned int length)
+long emu_do_munmap(void *addr, unsigned int length)
{
u8 *p = addr;
// set default buffer size to 16 * 1K
frag = (16<<16) | 10; // 16K
- ret = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
- if (ret != 0) {
- err("snd ioctl SETFRAGMENT %08x: ", frag);
- perror(NULL);
- }
+ ret = g_ioctl_raw(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
+ if (ret != 0)
+ err("snd ioctl SETFRAGMENT %08x: %d\n", frag, ret);
#endif
}
-static int emu_sound_ioctl(int fd, int request, void *argp)
+static long emu_sound_ioctl(int fd, int request, void *argp)
{
int *arg = argp;
* Catch this and set to something that works. */
switch(request) {
case SNDCTL_DSP_SETFRAGMENT: {
- int ret, bsize, frag, frag_cnt;
+ int bsize, frag, frag_cnt;
+ long ret;
+
if (arg == NULL)
break;
}
frag |= frag_cnt << 16;
- ret = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
- if (ret != 0) {
- err("snd ioctl SETFRAGMENT %08x: ", frag);
- perror(NULL);
- }
+ ret = g_ioctl_raw(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
+ if (ret != 0)
+ err("snd ioctl SETFRAGMENT %08x: %ld\n", frag, ret);
// indicate success even if we fail (because of ALSA mostly),
// things like MikMod will bail out otherwise.
return 0;
break;
}
- return ioctl(fd, request, argp);
+ return g_ioctl_raw(fd, request, argp);
}
-int emu_do_ioctl(int fd, int request, void *argp)
+long emu_do_ioctl(int fd, int request, void *argp)
{
if (fd == emu_interesting_fds[IFD_SOUND].fd)
return emu_sound_ioctl(fd, request, argp);
fail:
err("bad/ni ioctl(%d, %08x, %p)\n", fd, request, argp);
- errno = EINVAL;
- return -1;
+ return -EINVAL;
}
struct dev_fd_t emu_interesting_fds[] = {