#ifndef __XENO_SIM__
#ifndef __KERNEL__
#include <stdio.h>
#define xnarch_printf printf
#endif
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
#include <pthread.h>
#include <mqueue.h>
#else
#include <posix/posix.h>
#endif
#define CONSUMER_TASK_PRI 1
#define CONSUMER_STACK_SIZE 8192
#define PRODUCER_TASK_PRI 2
#define PRODUCER_STACK_SIZE 8192
#define CONSUMER_WAIT 150
#define PRODUCER_TRIG 40
#define MQ_NAME "/satchmq"
#define SHM_NAME "/satchshm"
void normalize(struct timespec *ts)
{
if (ts->tv_nsec > 1000000000)
{
ts->tv_sec += ts->tv_nsec / 1000000000;
ts->tv_nsec %= 1000000000;
}
if (ts->tv_nsec < 0)
{
ts->tv_sec -= (-ts->tv_nsec) / 1000000000 + 1;
ts->tv_nsec = 1000000000 - (-ts->tv_nsec % 1000000000);
}
}
void abort_perror(const char *pref)
{
xnprintf("%s: %d\n", pref, errno);
pthread_exit(NULL);
}
#ifdef PRODUCER
static const char *private_satch_s_tunes[] = {
"Surfing With The Alien",
"Lords of Karma",
"Banana Mango",
"Psycho Monkey",
"Luminous Flesh Giants",
"Moroccan Sunset",
"Satch Boogie",
"Flying In A Blue Dream",
"Ride",
"Summer Song",
"Speed Of Light",
"Crystal Planet",
"Raspberry Jam Delta-V",
"Champagne?",
"Clouds Race Across The Sky",
"Engines Of Creation"
};
static unsigned satch_s_tunes[sizeof(private_satch_s_tunes)/sizeof(char *)];
static timer_t producer_tm = (timer_t) -1;
static mqd_t producer_mq = (mqd_t) -1;
static void *producer_shm = MAP_FAILED;
static pthread_t producer_task;
void *producer (void *cookie)
{
struct itimerspec its;
sigset_t blocked;
unsigned pos;
int next_msg;
pos = 0;
for (next_msg = 0;
next_msg < sizeof(private_satch_s_tunes)/sizeof(char *);
next_msg++)
{
const char *msg = private_satch_s_tunes[next_msg];
size_t len = strlen(msg) + 1;
memcpy(producer_shm + pos, msg, len);
satch_s_tunes[next_msg] = pos;
pos += len;
}
next_msg = 0;
sigemptyset(&blocked);
sigaddset(&blocked, SIGRTMIN+1);
pthread_sigmask(SIG_BLOCK, &blocked, NULL);
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 10000000 * PRODUCER_TRIG;
normalize(&its.it_value);
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
normalize(&its.it_interval);
for (;;)
{
unsigned msg_off;
siginfo_t si;
int nchar;
if (timer_settime(producer_tm, 0, &its, NULL))
abort_perror("timer_settime");
while (sigwaitinfo(&blocked, &si) == -1 && errno == EINTR)
;
msg_off = satch_s_tunes[next_msg++];
next_msg %= (sizeof(satch_s_tunes) / sizeof(satch_s_tunes[0]));
do
{
nchar = mq_send(producer_mq, (char *)&msg_off, sizeof(msg_off), 0);
}
while (nchar == -1 && errno == EINTR);
if (nchar == -1)
abort_perror("mq_send");
}
return NULL;
}
#endif
#ifdef CONSUMER
static timer_t consumer_tm = (timer_t) -1;
static mqd_t consumer_mq = (mqd_t) -1;
static void *consumer_shm = MAP_FAILED;
static pthread_t consumer_task;
void *consumer (void *cookie)
{
struct itimerspec its;
sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGALRM);
pthread_sigmask(SIG_BLOCK, &blocked, NULL);
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = CONSUMER_WAIT * 10000000;
normalize(&its.it_value);
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = CONSUMER_WAIT * 10000000;
normalize(&its.it_interval);
if(timer_settime(consumer_tm, 0, &its, NULL))
abort_perror("timer_settime");
for (;;)
{
siginfo_t si;
while (sigwaitinfo(&blocked, &si) == -1 && errno == EINTR)
;
for (;;)
{
unsigned prio;
unsigned msg;
int nchar;
do
{
nchar = mq_receive(consumer_mq,(char *)&msg, sizeof(msg), &prio);
}
while (nchar == -1 && errno == EINTR);
if (nchar == -1 && errno == EAGAIN)
break;
if (nchar == -1)
abort_perror("mq_receive");
printf("Now playing %s...\n",(char *) consumer_shm + msg);
}
}
return NULL;
}
#endif
void __xeno_user_exit (void)
{
#ifdef PRODUCER
if (producer_task)
{
pthread_cancel(producer_task);
pthread_join(producer_task, NULL);
}
if (producer_tm != (timer_t) -1)
timer_delete(producer_tm);
if (producer_mq != (mqd_t) -1)
mq_close(producer_mq);
mq_unlink(MQ_NAME);
if (producer_shm != MAP_FAILED)
munmap(producer_shm, 65536);
shm_unlink(SHM_NAME);
#endif
#ifdef CONSUMER
if (consumer_task)
{
pthread_cancel(consumer_task);
pthread_join(consumer_task, NULL);
}
if (consumer_tm != (timer_t) -1)
timer_delete(consumer_tm);
if (consumer_mq != (mqd_t) -1)
mq_close(consumer_mq);
if (consumer_shm != MAP_FAILED)
munmap(consumer_shm, 65536);
#endif
}
int __xeno_user_init (void)
{
struct sched_param parm;
pthread_attr_t attr;
int rc = 0, fd = -1;
struct mq_attr mattr;
struct sigevent evt;
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0777);
if (fd == -1)
{
xnprintf("shm_open: %d\n", errno);
return -errno;
}
if (ftruncate(fd, 65536))
{
xnprintf("ftruncate: %d\n", errno);
goto out;
}
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, 1);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
#ifdef PRODUCER
mattr.mq_maxmsg = 30;
mattr.mq_msgsize = sizeof(unsigned);
producer_mq = mq_open(MQ_NAME, O_CREAT| O_EXCL| O_WRONLY, 0, &mattr);
if (producer_mq == (mqd_t) -1)
{
if (errno == EEXIST)
{
xnprintf("Satch: producer module is already running, please "
"only launch one producer instance.\n");
goto out;
}
xnprintf("mq_open(producer_mq): %d\n", errno);
goto out;
}
producer_shm = mmap(NULL, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (producer_shm == MAP_FAILED)
{
xnprintf("mmap(producer_shm): %d\n", errno);
return -errno;
}
evt.sigev_notify = SIGEV_SIGNAL;
evt.sigev_signo = SIGRTMIN+1;
evt.sigev_value.sival_ptr = &producer_tm;
if (timer_create(CLOCK_REALTIME, &evt, &producer_tm))
{
xnprintf("timer_create(producer_tm): %d\n", errno);
goto out;
}
pthread_attr_setstacksize(&attr, PRODUCER_STACK_SIZE);
parm.sched_priority = PRODUCER_TASK_PRI;
pthread_attr_setschedparam(&attr, &parm);
rc = pthread_create(&producer_task, &attr, &producer, NULL);
if (rc)
{
xnprintf("pthread_create(producer_task): %d\n", rc);
goto out;
}
#endif
#ifdef CONSUMER
mattr.mq_maxmsg = 30;
mattr.mq_msgsize = sizeof(unsigned);
consumer_mq = mq_open(MQ_NAME, O_NONBLOCK| O_RDONLY, 0, &mattr);
if (consumer_mq == (mqd_t) -1)
{
if (errno == ENOENT)
{
xnprintf("Satch: producer module not running, please launch producer"
" module before\nlaunching consumer application.\n");
goto out;
}
xnprintf("mq_open(consumer_mq): %d\n", errno);
goto out;
}
consumer_shm = mmap(NULL, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (consumer_shm == MAP_FAILED)
{
xnprintf("mmap(consumer_shm): %d\n", errno);
goto out;
}
evt.sigev_notify = SIGEV_SIGNAL;
evt.sigev_signo = SIGALRM;
evt.sigev_value.sival_ptr = &consumer_tm;
if(timer_create(CLOCK_REALTIME, &evt, &consumer_tm))
{
xnprintf("timer_create(consumer_tm): %d\n", errno);
goto out;
}
pthread_attr_setstacksize(&attr, CONSUMER_STACK_SIZE);
parm.sched_priority = CONSUMER_TASK_PRI;
pthread_attr_setschedparam(&attr, &parm);
rc = pthread_create(&consumer_task, &attr, &consumer, NULL);
if (rc)
{
xnprintf("pthread_create(consumer_task): %d\n", rc);
goto out;
}
#endif
if (close(fd))
{
xnprintf("close: %d\n", errno);
rc = -errno;
goto err;
}
return 0;
out:
rc = -rc ?: -errno;
if (close(fd))
xnprintf("close: %d\n", errno);
err:
__xeno_user_exit();
return rc;
}
#ifdef __KERNEL__
MODULE_AUTHOR("[email protected]");
MODULE_LICENSE("GPL");
module_init(__xeno_user_init);
module_exit(__xeno_user_exit);
#elif !defined(__XENO_SIM__)
int main (int ac, char *av[])
{
sigset_t mask;
int rc, sig;
sigemptyset(&mask);
sigaddset(&mask,SIGINT);
sigaddset(&mask,SIGTERM);
sigaddset(&mask,SIGHUP);
sigaddset(&mask,SIGALRM);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
mlockall(MCL_CURRENT|MCL_FUTURE);
rc = __xeno_user_init();
if (rc)
{
xnprintf("__xeno_user_init: %d\n", -rc);
return -rc;
}
sigwait(&mask, &sig);
__xeno_user_exit();
return 0;
}
#endif