#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include "rtapi_rtai_shm_wrap.h"
#include <malloc.h>
#include <sys/io.h>
#include <errno.h>
#include "rtapi.h"
#include <rtapi_mutex.h>
#include "rtapi_common.h"
static int shmem_delete(int shmem_id, int module_id);
static int fifo_delete(int fifo_id, int module_id);
static void *shmem_addr_array[RTAPI_MAX_SHMEMS + 1];
static int fifo_fd_array[RTAPI_MAX_FIFOS + 1];
static int msg_level = RTAPI_MSG_ERR;
static void check_memlock_limit(const char *where);
static int nummods;
int rtapi_init(const char *modname)
{
int n, module_id;
module_data *module;
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: initing module %s\n", modname);
errno = 0;
if(!rtapi_data)
rtapi_data = rtai_malloc(RTAPI_KEY, sizeof(rtapi_data_t));
if (rtapi_data == NULL || rtapi_data == (rtapi_data_t*)-1) {
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: could not open shared memory (%s)\n", strerror(errno));
check_memlock_limit("could not open shared memory");
rtapi_data = 0;
return -ENOMEM;
}
nummods++;
init_rtapi_data(rtapi_data);
if (rtapi_data->rev_code != rev_code) {
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: version mismatch %d vs %d\n", rtapi_data->rev_code, rev_code);
return -EINVAL;
}
module_array = rtapi_data->module_array;
task_array = rtapi_data->task_array;
shmem_array = rtapi_data->shmem_array;
sem_array = rtapi_data->sem_array;
fifo_array = rtapi_data->fifo_array;
irq_array = rtapi_data->irq_array;
for (n = 0; n <= RTAPI_MAX_SHMEMS; n++) {
shmem_addr_array[n] = NULL;
}
rtapi_mutex_get(&(rtapi_data->mutex));
n = 1;
while ((n <= RTAPI_MAX_MODULES) && (module_array[n].state != NO_MODULE)) {
n++;
}
if (n > RTAPI_MAX_MODULES) {
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: reached module limit %d\n",
n);
return -EMFILE;
}
module_id = n;
module = &(module_array[n]);
module->state = USERSPACE;
if (modname != NULL) {
snprintf(module->name, RTAPI_NAME_LEN, "%s", modname);
} else {
snprintf(module->name, RTAPI_NAME_LEN, "ULMOD%03d", module_id);
}
rtapi_data->ul_module_count++;
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: module '%s' inited, ID = %02d\n",
module->name, module_id);
return module_id;
}
int rtapi_exit(int module_id)
{
module_data *module;
int n;
if (rtapi_data == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: exit called before init\n");
return -EINVAL;
}
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: module %02d exiting\n", module_id);
if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: bad module id\n");
return -EINVAL;
}
rtapi_mutex_get(&(rtapi_data->mutex));
module = &(module_array[module_id]);
if (module->state != USERSPACE) {
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: not a userspace module\n");
rtapi_mutex_give(&(rtapi_data->mutex));
return -EINVAL;
}
for (n = 1; n <= RTAPI_MAX_SHMEMS; n++) {
if (test_bit(module_id, shmem_array[n].bitmap)) {
fprintf(stderr,
"ULAPI: WARNING: module '%s' failed to delete shmem %02d\n",
module->name, n);
shmem_delete(n, module_id);
}
}
for (n = 1; n <= RTAPI_MAX_FIFOS; n++) {
if ((fifo_array[n].reader == module_id) ||
(fifo_array[n].writer == module_id)) {
fprintf(stderr,
"ULAPI: WARNING: module '%s' failed to delete fifo %02d\n",
module->name, n);
fifo_delete(n, module_id);
}
}
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: module %02d exited, name = '%s'\n",
module_id, module->name);
module->state = NO_MODULE;
module->name[0] = '\0';
rtapi_data->ul_module_count--;
rtapi_mutex_give(&(rtapi_data->mutex));
nummods--;
if(nummods == 0)
{
rtai_free(RTAPI_KEY, rtapi_data);
rtapi_data = 0;
}
return 0;
}
int rtapi_vsnprintf(char *buf, unsigned long int size, const char *fmt, va_list ap) {
return vsnprintf(buf, size, fmt, ap);
}
int rtapi_snprintf(char *buf, unsigned long int size, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i = vsnprintf(buf, size, fmt, args);
va_end(args);
return i;
}
#define BUFFERLEN 1024
void rtapi_print(const char *fmt, ...)
{
char buffer[BUFFERLEN + 1];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, BUFFERLEN, fmt, args);
fputs(buffer, stdout);
va_end(args);
}
void rtapi_print_msg(msg_level_t level, const char *fmt, ...)
{
va_list args;
if ((level <= msg_level) && (msg_level != RTAPI_MSG_NONE)) {
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
}
int rtapi_set_msg_level(int level)
{
if ((level < RTAPI_MSG_NONE) || (level > RTAPI_MSG_ALL)) {
return -EINVAL;
}
msg_level = level;
return 0;
}
int rtapi_get_msg_level(void)
{
return msg_level;
}
void rtapi_printall(void)
{
module_data *modules;
task_data *tasks;
shmem_data *shmems;
sem_data *sems;
fifo_data *fifos;
irq_data *irqs;
int n, m;
if (rtapi_data == NULL) {
printf("rtapi_data = NULL, not initialized\n");
return;
}
printf("rtapi_data = %p\n", rtapi_data);
printf(" magic = %d\n", rtapi_data->magic);
printf(" rev_code = %08x\n", rtapi_data->rev_code);
printf(" mutex = %lu\n", rtapi_data->mutex);
printf(" rt_module_count = %d\n", rtapi_data->rt_module_count);
printf(" ul_module_count = %d\n", rtapi_data->ul_module_count);
printf(" task_count = %d\n", rtapi_data->task_count);
printf(" shmem_count = %d\n", rtapi_data->shmem_count);
printf(" sem_count = %d\n", rtapi_data->sem_count);
printf(" fifo_count = %d\n", rtapi_data->fifo_count);
printf(" irq_countc = %d\n", rtapi_data->irq_count);
printf(" timer_running = %d\n", rtapi_data->timer_running);
printf(" timer_period = %ld\n", rtapi_data->timer_period);
modules = &(rtapi_data->module_array[0]);
tasks = &(rtapi_data->task_array[0]);
shmems = &(rtapi_data->shmem_array[0]);
sems = &(rtapi_data->sem_array[0]);
fifos = &(rtapi_data->fifo_array[0]);
irqs = &(rtapi_data->irq_array[0]);
printf(" module array = %p\n", modules);
printf(" task array = %p\n", tasks);
printf(" shmem array = %p\n", shmems);
printf(" sem array = %p\n", sems);
printf(" fifo array = %p\n", fifos);
printf(" irq array = %p\n", irqs);
for (n = 0; n <= RTAPI_MAX_MODULES; n++) {
if (modules[n].state != NO_MODULE) {
printf(" module %02d\n", n);
printf(" state = %d\n", modules[n].state);
printf(" name = %p\n", modules[n].name);
printf(" name = '%s'\n", modules[n].name);
}
}
for (n = 0; n <= RTAPI_MAX_TASKS; n++) {
if (tasks[n].state != EMPTY) {
printf(" task %02d\n", n);
printf(" state = %d\n", tasks[n].state);
printf(" prio = %d\n", tasks[n].prio);
printf(" owner = %d\n", tasks[n].owner);
printf(" code = %p\n", tasks[n].taskcode);
}
}
for (n = 0; n <= RTAPI_MAX_SHMEMS; n++) {
if (shmems[n].key != 0) {
printf(" shmem %02d\n", n);
printf(" key = %d\n", shmems[n].key);
printf(" rtusers = %d\n", shmems[n].rtusers);
printf(" ulusers = %d\n", shmems[n].ulusers);
printf(" size = %ld\n", shmems[n].size);
printf(" bitmap = ");
for (m = 0; m <= RTAPI_MAX_MODULES; m++) {
if (test_bit(m, shmems[n].bitmap)) {
putchar('1');
} else {
putchar('0');
}
}
putchar('\n');
}
}
for (n = 0; n <= RTAPI_MAX_SEMS; n++) {
if (sems[n].key != 0) {
printf(" sem %02d\n", n);
printf(" key = %d\n", sems[n].key);
printf(" users = %d\n", sems[n].users);
printf(" bitmap = ");
for (m = 0; m <= RTAPI_MAX_MODULES; m++) {
if (test_bit(m, sems[n].bitmap)) {
putchar('1');
} else {
putchar('0');
}
}
putchar('\n');
}
}
for (n = 0; n <= RTAPI_MAX_FIFOS; n++) {
if (fifos[n].state != UNUSED) {
printf(" fifo %02d\n", n);
printf(" state = %d\n", fifos[n].state);
printf(" key = %d\n", fifos[n].key);
printf(" reader = %d\n", fifos[n].reader);
printf(" writer = %d\n", fifos[n].writer);
printf(" size = %ld\n", fifos[n].size);
}
}
for (n = 0; n <= RTAPI_MAX_IRQS; n++) {
if (irqs[n].irq_num != 0) {
printf(" irq %02d\n", n);
printf(" irq_num = %d\n", irqs[n].irq_num);
printf(" owner = %d\n", irqs[n].owner);
printf(" handler = %p\n", irqs[n].handler);
}
}
}
int rtapi_shmem_new(int key, int module_id, unsigned long int size)
{
int n;
int shmem_id;
shmem_data *shmem;
if ((key == 0) || (key == RTAPI_KEY)) {
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: bad shmem key: %d\n",
key);
return -EINVAL;
}
rtapi_mutex_get(&(rtapi_data->mutex));
if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: bad module ID: %d\n",
module_id);
return -EINVAL;
}
if (module_array[module_id].state != USERSPACE) {
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: not a user space module ID: %d\n", module_id);
rtapi_mutex_give(&(rtapi_data->mutex));
return -EINVAL;
}
for (n = 1; n <= RTAPI_MAX_SHMEMS; n++) {
if (shmem_array[n].key == key) {
shmem_id = n;
shmem = &(shmem_array[n]);
if (shmem->size < size) {
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: shmem size mismatch\n");
return -EINVAL;
}
if (test_bit(module_id, shmem->bitmap)) {
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: Warning: shmem already mapped\n");
return -EINVAL;
}
shmem_addr_array[shmem_id] = rtai_malloc(key, shmem->size);
if (shmem_addr_array[shmem_id] == NULL || shmem_addr_array[shmem_id] == (void*)-1) {
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: failed to map shmem\n");
rtapi_mutex_give(&(rtapi_data->mutex));
check_memlock_limit("failed to map shmem");
return -ENOMEM;
}
set_bit(module_id, shmem->bitmap);
shmem->ulusers++;
rtapi_mutex_give(&(rtapi_data->mutex));
return shmem_id;
}
}
n = 1;
while ((n <= RTAPI_MAX_SHMEMS) && (shmem_array[n].key != 0)) {
n++;
}
if (n > RTAPI_MAX_SHMEMS) {
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: reached shmem limit %d\n",
n);
return -EMFILE;
}
shmem_id = n;
shmem = &(shmem_array[n]);
shmem_addr_array[shmem_id] = rtai_malloc(key, size);
if (shmem_addr_array[shmem_id] == NULL || shmem_addr_array[shmem_id] == (void*)-1) {
rtapi_mutex_give(&(rtapi_data->mutex));
rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: ERROR: could not create shmem %d\n", n);
return -ENOMEM;
}
set_bit(module_id, shmem->bitmap);
shmem->key = key;
shmem->rtusers = 0;
shmem->ulusers = 1;
shmem->size = size;
rtapi_data->shmem_count++;
*((long int *) (shmem_addr_array[shmem_id])) = 0;
rtapi_mutex_give(&(rtapi_data->mutex));
return shmem_id;
}
#include <sys/time.h>
#include <sys/resource.h>
#define RECOMMENDED (20480*1024lu)
static void check_memlock_limit(const char *where) {
static int checked=0;
struct rlimit lim;
int result;
if(checked) return;
checked=1;
result = getrlimit(RLIMIT_MEMLOCK, &lim);
if(result < 0) { perror("getrlimit"); return; }
if(lim.rlim_cur == (rlim_t)-1) return; if(lim.rlim_cur >= RECOMMENDED) return; rtapi_print_msg(RTAPI_MSG_ERR,
"RTAPI: Locked memory limit is %luKiB, recommended at least %luKiB.\n"
"This can cause the error '%s'.\n"
"For more information, see\n"
"\thttp://wiki.linuxcnc.org/cgi-bin/emcinfo.pl?LockedMemory\n",
(unsigned long)lim.rlim_cur/1024, RECOMMENDED/1024, where);
return;
}
int rtapi_shmem_delete(int shmem_id, int module_id)
{
int retval;
rtapi_mutex_get(&(rtapi_data->mutex));
retval = shmem_delete(shmem_id, module_id);
rtapi_mutex_give(&(rtapi_data->mutex));
return retval;
}
int shmem_delete(int shmem_id, int module_id)
{
shmem_data *shmem;
if ((shmem_id < 1) || (shmem_id > RTAPI_MAX_SHMEMS)) {
return -EINVAL;
}
shmem = &(shmem_array[shmem_id]);
if (shmem->key == 0) {
return -EINVAL;
}
if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
return -EINVAL;
}
if (module_array[module_id].state != USERSPACE) {
return -EINVAL;
}
if (test_bit(module_id, shmem->bitmap) == 0) {
return -EINVAL;
}
clear_bit(module_id, shmem->bitmap);
shmem->ulusers--;
rtai_free(shmem->key, shmem_addr_array[shmem_id]);
shmem_addr_array[shmem_id] = NULL;
if ((shmem->ulusers > 0) || (shmem->rtusers > 0)) {
return 0;
}
shmem->key = 0;
shmem->size = 0;
rtapi_data->shmem_count--;
return 0;
}
int rtapi_shmem_getptr(int shmem_id, void **ptr)
{
if ((shmem_id < 1) || (shmem_id > RTAPI_MAX_SHMEMS)) {
return -EINVAL;
}
if (shmem_addr_array[shmem_id] == NULL) {
return -EINVAL;
}
*ptr = shmem_addr_array[shmem_id];
return 0;
}
int rtapi_fifo_new(int key, int module_id, unsigned long int size, char mode)
{
enum { DEVSTR_LEN = 256 };
char devstr[DEVSTR_LEN];
int n, flags;
int fifo_id;
fifo_data *fifo;
if (key == 0) {
return -EINVAL;
}
if ((mode != 'R') && (mode != 'W')) {
return -EINVAL;
}
if (mode == 'R') {
flags = O_RDONLY;
} else {
flags = O_WRONLY;
}
rtapi_mutex_get(&(rtapi_data->mutex));
if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -EINVAL;
}
if (module_array[module_id].state != USERSPACE) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -EINVAL;
}
for (n = 1; n <= RTAPI_MAX_FIFOS; n++) {
if ((fifo_array[n].state != UNUSED) && (fifo_array[n].key == key)) {
fifo_id = n;
fifo = &(fifo_array[n]);
if (mode == 'R') {
if (fifo->state & HAS_READER) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -EBUSY;
}
sprintf(devstr, "/dev/rtf%d", fifo_id);
fifo_fd_array[fifo_id] = open(devstr, flags);
if (fifo_fd_array[fifo_id] < 0) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -ENOENT;
}
fifo->state |= HAS_READER;
fifo->reader = module_id;
rtapi_mutex_give(&(rtapi_data->mutex));
return fifo_id;
} else {
if (fifo->state & HAS_WRITER) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -EBUSY;
}
sprintf(devstr, "/dev/rtf%d", fifo_id);
fifo_fd_array[fifo_id] = open(devstr, flags);
if (fifo_fd_array[fifo_id] < 0) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -ENOENT;
}
fifo->state |= HAS_WRITER;
fifo->writer = module_id;
rtapi_mutex_give(&(rtapi_data->mutex));
return fifo_id;
}
}
}
n = 1;
while ((n <= RTAPI_MAX_FIFOS) && (fifo_array[n].state != UNUSED)) {
n++;
}
if (n > RTAPI_MAX_FIFOS) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -EMFILE;
}
fifo_id = n;
fifo = &(fifo_array[n]);
sprintf(devstr, "/dev/rtf%d", fifo_id);
fifo_fd_array[fifo_id] = open(devstr, flags);
if (fifo_fd_array[fifo_id] < 0) {
rtapi_mutex_give(&(rtapi_data->mutex));
return -ENOENT;
}
if (mode == 'R') {
fifo->state = HAS_READER;
fifo->reader = module_id;
} else {
fifo->state = HAS_WRITER;
fifo->writer = module_id;
}
fifo->key = key;
fifo->size = size;
rtapi_data->fifo_count++;
rtapi_mutex_give(&(rtapi_data->mutex));
return fifo_id;
}
int rtapi_fifo_delete(int fifo_id, int module_id)
{
int retval;
rtapi_mutex_get(&(rtapi_data->mutex));
retval = fifo_delete(fifo_id, module_id);
rtapi_mutex_give(&(rtapi_data->mutex));
return retval;
}
static int fifo_delete(int fifo_id, int module_id)
{
fifo_data *fifo;
if ((fifo_id < 1) || (fifo_id > RTAPI_MAX_FIFOS)) {
return -EINVAL;
}
fifo = &(fifo_array[fifo_id]);
if (fifo->state == UNUSED) {
return -EINVAL;
}
if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
return -EINVAL;
}
if (module_array[module_id].state != USERSPACE) {
return -EINVAL;
}
if ((fifo->reader != module_id) && (fifo->writer != module_id)) {
return -EINVAL;
}
if (fifo->reader == module_id) {
fifo->state &= ~HAS_READER;
fifo->reader = 0;
}
if (fifo->writer == module_id) {
fifo->state &= ~HAS_WRITER;
fifo->writer = 0;
}
if (close(fifo_id) < 0) {
return -ENOENT;
}
if (fifo->state != UNUSED) {
return 0;
}
fifo->state = UNUSED;
fifo->key = 0;
fifo->size = 0;
rtapi_data->fifo_count--;
return 0;
}
int rtapi_fifo_read(int fifo_id, char *buf, unsigned long int size)
{
int retval;
fifo_data *fifo;
if ((fifo_id < 1) || (fifo_id > RTAPI_MAX_FIFOS)) {
return -EINVAL;
}
fifo = &(fifo_array[fifo_id]);
if ((fifo->state & HAS_READER) == 0) {
return -EINVAL;
}
retval = read(fifo_fd_array[fifo_id], buf, size);
if (retval <= 0) {
return -EINVAL;
}
return retval;
}
int rtapi_fifo_write(int fifo_id, char *buf, unsigned long int size)
{
int retval;
fifo_data *fifo;
if ((fifo_id < 1) || (fifo_id > RTAPI_MAX_FIFOS)) {
return -EINVAL;
}
fifo = &(fifo_array[fifo_id]);
if ((fifo->state & HAS_WRITER) == 0) {
return -EINVAL;
}
retval = write(fifo_fd_array[fifo_id], buf, size);
if (retval < 0) {
return -EINVAL;
}
return retval;
}
void rtapi_outb(unsigned char byte, unsigned int port)
{
outb(byte, port);
}
unsigned char rtapi_inb(unsigned int port)
{
return inb(port);
}
int rtapi_is_realtime() { return 1; }
int rtapi_is_kernelspace() { return 1; }
void rtapi_delay(long ns) {
if(ns > rtapi_delay_max()) ns = rtapi_delay_max();
struct timespec ts = {0, ns};
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, 0);
}
long int rtapi_delay_max() { return 999999999; }