pxt-ev3/libs/core/linux.cpp

311 lines
6.8 KiB
C++
Raw Normal View History

2017-06-29 19:41:38 +02:00
#include "pxt.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
2017-07-01 12:43:10 +02:00
#include <cstdarg>
2017-07-03 13:57:34 +02:00
#include <pthread.h>
#include <assert.h>
2017-07-05 18:53:22 +02:00
#include <unistd.h>
2017-07-01 12:43:10 +02:00
2017-07-04 16:11:54 +02:00
void *operator new(size_t size) {
return malloc(size);
}
void *operator new[](size_t size) {
return malloc(size);
}
void operator delete(void *p) {
free(p);
}
void operator delete[](void *p) {
free(p);
}
2017-07-03 13:57:34 +02:00
namespace pxt {
2017-07-02 13:32:17 +02:00
static int startTime;
2017-07-03 13:57:34 +02:00
static pthread_mutex_t execMutex;
2017-07-05 02:17:26 +02:00
static pthread_mutex_t eventMutex;
2017-07-03 13:57:34 +02:00
static pthread_cond_t newEventBroadcast;
2017-07-05 18:53:22 +02:00
static FILE *dmesgFile;
static FILE *serialFile;
2017-07-02 13:32:17 +02:00
struct Thread {
struct Thread *next;
Action act;
TValue arg0;
2017-07-03 13:57:34 +02:00
pthread_t pid;
pthread_cond_t waitCond;
2017-07-02 13:32:17 +02:00
int waitSource;
int waitValue;
};
static struct Thread *allThreads;
2017-07-03 13:57:34 +02:00
static struct Event *eventHead, *eventTail;
2017-07-02 13:32:17 +02:00
struct Event {
2017-07-03 13:57:34 +02:00
struct Event *next;
2017-07-02 13:32:17 +02:00
int source;
int value;
};
2017-07-03 13:57:34 +02:00
Event lastEvent;
2017-07-02 13:32:17 +02:00
Event *mkEvent(int source, int value) {
auto res = new Event();
memset(res, 0, sizeof(Event));
res->source = source;
res->value = value;
return res;
}
2017-06-29 19:41:38 +02:00
void sendSerial(const char *data, int len) {
2017-07-05 18:53:22 +02:00
if (!serialFile) {
serialFile = fopen("/tmp/serial.txt", "w");
if (!serialFile)
serialFile = stderr;
}
fwrite(data, 1, len, serialFile);
fflush(serialFile);
fdatasync(fileno(serialFile));
2017-06-29 19:41:38 +02:00
}
extern "C" void target_panic(int error_code) {
char buf[50];
snprintf(buf, sizeof(buf), "\nPANIC %d\n", error_code);
sendSerial(buf, strlen(buf));
abort();
}
extern "C" void target_reset() {
exit(0);
}
2017-07-03 13:57:34 +02:00
void startUser() {
pthread_mutex_lock(&execMutex);
}
void stopUser() {
pthread_mutex_unlock(&execMutex);
}
2017-07-04 11:53:49 +02:00
void sleep_core_us(uint64_t us) {
2017-07-03 13:57:34 +02:00
struct timespec ts;
2017-07-04 11:53:49 +02:00
ts.tv_sec = us / 1000000;
ts.tv_nsec = (us % 1000000) * 1000;
2017-07-03 13:57:34 +02:00
while (nanosleep(&ts, &ts))
;
2017-07-03 14:02:03 +02:00
}
2017-07-03 13:57:34 +02:00
2017-07-03 14:02:03 +02:00
void sleep_ms(uint32_t ms) {
2017-07-04 11:53:49 +02:00
stopUser();
sleep_core_us(ms * 1000);
startUser();
2017-07-02 13:32:17 +02:00
}
2017-06-29 19:41:38 +02:00
void sleep_us(uint64_t us) {
2017-07-04 11:53:49 +02:00
if (us > 50000) {
2017-07-02 13:32:17 +02:00
sleep_ms(us / 1000);
}
2017-07-04 11:53:49 +02:00
sleep_core_us(us);
2017-06-29 19:41:38 +02:00
}
uint64_t currTime() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
int current_time_ms() {
return currTime() - startTime;
}
int getSerialNumber() {
return 42; // TODO
}
2017-07-02 13:32:17 +02:00
void disposeThread(Thread *t) {
if (allThreads == t) {
allThreads = t->next;
} else {
for (auto tt = allThreads; tt; tt = tt->next) {
if (tt->next == t) {
tt->next = t->next;
break;
}
}
}
decr(t->act);
2017-07-03 13:57:34 +02:00
pthread_cond_destroy(&t->waitCond);
2017-07-02 13:32:17 +02:00
delete t;
}
static void runAct(Thread *thr) {
2017-07-03 13:57:34 +02:00
startUser();
2017-07-02 13:32:17 +02:00
pxt::runAction1(thr->act, thr->arg0);
2017-07-03 13:57:34 +02:00
stopUser();
2017-07-02 13:32:17 +02:00
disposeThread(thr);
}
2017-07-03 13:57:34 +02:00
void setupThread(Action a, TValue arg = 0, void (*runner)(Thread *) = NULL) {
if (runner == NULL)
runner = runAct;
2017-07-02 13:32:17 +02:00
auto thr = new Thread();
memset(thr, 0, sizeof(Thread));
thr->next = allThreads;
allThreads = thr;
thr->act = a;
thr->arg0 = a;
2017-07-03 13:57:34 +02:00
pthread_cond_init(&thr->waitCond, NULL);
2017-07-02 13:32:17 +02:00
incr(a);
2017-07-03 13:57:34 +02:00
pthread_create(&thr->pid, NULL, (void *(*)(void *))runner, thr);
pthread_detach(thr->pid);
2017-06-29 19:41:38 +02:00
}
2017-07-02 13:32:17 +02:00
2017-06-29 19:41:38 +02:00
void runInBackground(Action a) {
2017-07-02 13:32:17 +02:00
setupThread(a);
2017-06-29 19:41:38 +02:00
}
2017-07-02 13:32:17 +02:00
static void runFor(Thread *t) {
2017-07-03 13:57:34 +02:00
startUser();
2017-07-02 13:32:17 +02:00
while (true) {
pxt::runAction0(t->act);
sleep_ms(20);
}
}
2017-06-29 19:41:38 +02:00
void runForever(Action a) {
2017-07-02 13:32:17 +02:00
setupThread(a, 0, runFor);
2017-06-29 19:41:38 +02:00
}
2017-07-02 13:32:17 +02:00
2017-07-03 13:57:34 +02:00
void waitForEvent(int source, int value) {
auto self = pthread_self();
for (auto t = allThreads; t; t = t->next) {
if (t->pid == self) {
2017-07-05 02:17:26 +02:00
pthread_mutex_lock(&eventMutex);
2017-07-03 13:57:34 +02:00
t->waitSource = source;
t->waitValue = value;
// spourious wake ups may occur they say
while (t->waitSource) {
2017-07-05 02:17:26 +02:00
pthread_cond_wait(&t->waitCond, &eventMutex);
2017-07-03 13:57:34 +02:00
}
2017-07-05 02:17:26 +02:00
pthread_mutex_unlock(&eventMutex);
2017-07-03 13:57:34 +02:00
return;
}
}
assert(0);
2017-06-29 19:41:38 +02:00
}
2017-07-02 13:32:17 +02:00
static void dispatchEvent(Event &e) {
2017-07-03 13:57:34 +02:00
lastEvent = e;
2017-07-02 13:32:17 +02:00
2017-07-04 16:11:54 +02:00
auto curr = findBinding(e.source, e.value);
2017-07-02 13:32:17 +02:00
if (curr)
2017-07-04 16:11:54 +02:00
setupThread(curr->action, fromInt(e.value));
2017-07-02 13:32:17 +02:00
2017-07-04 16:11:54 +02:00
curr = findBinding(e.source, DEVICE_EVT_ANY);
2017-07-02 13:32:17 +02:00
if (curr)
2017-07-04 16:11:54 +02:00
setupThread(curr->action, fromInt(e.value));
2017-07-02 13:32:17 +02:00
}
static void *evtDispatcher(void *dummy) {
2017-07-05 02:17:26 +02:00
pthread_mutex_lock(&eventMutex);
2017-07-02 13:32:17 +02:00
while (true) {
2017-07-05 02:17:26 +02:00
pthread_cond_wait(&newEventBroadcast, &eventMutex);
2017-07-03 13:57:34 +02:00
while (eventHead != NULL) {
Event *ev = eventHead;
eventHead = ev->next;
if (eventHead == NULL)
eventTail = NULL;
2017-07-02 13:32:17 +02:00
for (auto thr = allThreads; thr; thr = thr->next) {
2017-07-07 12:49:13 +02:00
if (thr->waitSource == 0)
continue;
if (thr->waitValue != ev->value && thr->waitValue != DEVICE_EVT_ANY)
continue;
if (thr->waitSource == ev->source) {
2017-07-02 13:32:17 +02:00
thr->waitSource = 0; // once!
2017-07-03 13:57:34 +02:00
pthread_cond_broadcast(&thr->waitCond);
2017-07-07 12:49:13 +02:00
} else if (thr->waitSource == DEVICE_ID_NOTIFY &&
ev->source == DEVICE_ID_NOTIFY_ONE) {
thr->waitSource = 0; // once!
pthread_cond_broadcast(&thr->waitCond);
break; // do not wake up any other threads
2017-07-02 13:32:17 +02:00
}
}
2017-07-03 13:57:34 +02:00
2017-07-02 13:32:17 +02:00
dispatchEvent(*ev);
delete ev;
}
}
}
2017-07-07 12:44:34 +02:00
int allocateNotifyEvent() {
static volatile int notifyId;
pthread_mutex_lock(&eventMutex);
int res = ++notifyId;
pthread_mutex_unlock(&eventMutex);
return res;
}
2017-07-02 13:32:17 +02:00
void raiseEvent(int id, int event) {
auto e = mkEvent(id, event);
2017-07-05 02:17:26 +02:00
pthread_mutex_lock(&eventMutex);
2017-07-03 13:57:34 +02:00
if (eventTail == NULL) {
assert(eventHead == NULL);
eventHead = eventTail = e;
} else {
eventTail->next = e;
eventTail = e;
}
pthread_cond_broadcast(&newEventBroadcast);
2017-07-05 02:17:26 +02:00
pthread_mutex_unlock(&eventMutex);
2017-07-02 13:32:17 +02:00
}
void registerWithDal(int id, int event, Action a) {
2017-07-04 16:11:54 +02:00
setBinding(id, event, a);
2017-07-02 13:32:17 +02:00
}
2017-06-29 19:41:38 +02:00
uint32_t afterProgramPage() {
return 0;
}
void dumpDmesg() {
// TODO
}
2017-07-02 13:32:17 +02:00
void screen_init();
2017-07-02 13:32:17 +02:00
void initRuntime() {
daemon(1, 1);
2017-07-02 13:32:17 +02:00
startTime = currTime();
2017-07-05 18:53:22 +02:00
DMESG("runtime starting...");
2017-07-03 13:57:34 +02:00
pthread_t disp;
pthread_create(&disp, NULL, evtDispatcher, NULL);
pthread_detach(disp);
2017-07-05 19:35:05 +02:00
target_init();
screen_init();
2017-07-04 11:53:49 +02:00
startUser();
2017-07-02 13:32:17 +02:00
}
2017-07-05 18:53:22 +02:00
void dmesg(const char *format, ...) {
char buf[500];
if (!dmesgFile) {
dmesgFile = fopen("/tmp/dmesg.txt", "w");
if (!dmesgFile)
dmesgFile = stderr;
}
va_list arg;
va_start(arg, format);
vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
fprintf(dmesgFile, "[%8d] %s\n", current_time_ms(), buf);
fflush(dmesgFile);
fdatasync(fileno(dmesgFile));
}
2017-06-29 19:41:38 +02:00
}