/ src / rtapi / rtai_ulapi.c
rtai_ulapi.c
  1  /** RTAPI is a library providing a uniform API for several real time
  2      operating systems.  As of ver 2.0, RTLinux and RTAI are supported.
  3  */
  4  /********************************************************************
  5  * Description:  rtai_ulapi.c
  6  *               This file, 'rtai_ulapi.c', implements the nonrealtime 
  7  *               portion of the API for the RTAI platform.
  8  *
  9  * Author: John Kasunich, Paul Corner
 10  * License: LGPL Version 2
 11  *    
 12  * Copyright (c) 2004 All rights reserved.
 13  *
 14  * Last change: 
 15  ********************************************************************/
 16  
 17  /** This file, 'rtai_ulapi.c', implements the non-realtime portion of
 18      the API for the RTAI platform.  The API is defined in rtapi.h,
 19      which includes documentation for all the API functions.  The
 20      realtime portion of the API is implemented in rtai_rtapi.c
 21      (for the RTAI platform).
 22  */
 23  
 24  /** Copyright (C) 2003 John Kasunich
 25                         <jmkasunich AT users DOT sourceforge DOT net>
 26      Copyright (C) 2003 Paul Corner
 27                         <paul_c AT users DOT sourceforge DOT net>
 28      This library is based on version 1.0, which was released into
 29      the public domain by its author, Fred Proctor.  Thanks Fred!
 30  */
 31  
 32  /* This library is free software; you can redistribute it and/or
 33     modify it under the terms of version 2.1 of the GNU Lesser General
 34     Public License as published by the Free Software Foundation.
 35     This library is distributed in the hope that it will be useful,
 36     but WITHOUT ANY WARRANTY; without even the implied warranty of
 37     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 38     GNU Lesser General Public License for more details.
 39  
 40     You should have received a copy of the GNU General Lesser Public
 41     License along with this library; if not, write to the Free Software
 42     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 43  */
 44  
 45  /** THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
 46      ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 47      TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 48      harming persons must have provisions for completely removing power
 49      from all motors, etc, before persons enter any danger area.  All
 50      machinery must be designed to comply with local and national safety
 51      codes, and the authors of this software can not, and do not, take
 52      any responsibility for such compliance.
 53  
 54      This code was written as part of the EMC HAL project.  For more
 55      information, go to www.linuxcnc.org.
 56  */
 57  
 58  #define _GNU_SOURCE
 59  
 60  #include <stdio.h>		/* sprintf() */
 61  #include <string.h>		/* strcpy, etc. */
 62  #include <stddef.h>		/* NULL, needed for rtai_shm.h */
 63  #include <stdarg.h>		/* va_arg, etc. */
 64  #include <unistd.h>		/* open(), close() */
 65  #include <sys/mman.h>		/* PROT_READ, needed for rtai_shm.h */
 66  #include <sys/types.h>		/* off_t, needed for rtai_shm.h */
 67  #include <sys/fcntl.h>		/* O_RDWR, needed for rtai_shm.h */
 68  #include "rtapi_rtai_shm_wrap.h" /*rtai_malloc,free() */
 69  #include <malloc.h>		/* malloc(), free() */
 70  #include <sys/io.h>		/* inb(), outb() */
 71  #include <errno.h>		/* errno */
 72  
 73  #include "rtapi.h"		/* public RTAPI decls */
 74  #include <rtapi_mutex.h>
 75  #include "rtapi_common.h"	/* shared realtime/nonrealtime stuff */
 76  
 77  /* the following are internal functions that do the real work associated
 78     with deleting resources.  They do not check the mutex that protects
 79     the internal data structures.  When someone calls a rtapi_xxx_delete()
 80     function, the ulapi funct gets the mutex before calling one of these
 81     internal functions.  When internal code that already has the mutex
 82     needs to delete something, it calls these functions directly.
 83  */
 84  
 85  static int shmem_delete(int shmem_id, int module_id);
 86  static int fifo_delete(int fifo_id, int module_id);
 87  
 88  /* resource data unique to this process */
 89  static void *shmem_addr_array[RTAPI_MAX_SHMEMS + 1];
 90  static int fifo_fd_array[RTAPI_MAX_FIFOS + 1];
 91  
 92  static int msg_level = RTAPI_MSG_ERR;	/* message printing level */
 93  
 94  static void check_memlock_limit(const char *where);
 95  
 96  static int nummods;
 97  /***********************************************************************
 98  *                      GENERAL PURPOSE FUNCTIONS                       *
 99  ************************************************************************/
100  
101  int rtapi_init(const char *modname)
102  {
103      int n, module_id;
104      module_data *module;
105  
106      /* say hello */
107      rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: initing module %s\n", modname);
108      /* get shared memory block from OS and save its address */
109      errno = 0;
110      if(!rtapi_data)
111          rtapi_data = rtai_malloc(RTAPI_KEY, sizeof(rtapi_data_t));
112      // the check for -1 here is because rtai_malloc (in at least
113      // rtai 3.6.1, and probably others) has a bug where it
114      // sometimes returns -1 on error
115      if (rtapi_data == NULL || rtapi_data == (rtapi_data_t*)-1) {
116  	rtapi_print_msg(RTAPI_MSG_ERR,
117  	    "RTAPI: ERROR: could not open shared memory (%s)\n", strerror(errno));
118  	check_memlock_limit("could not open shared memory");
119  	rtapi_data = 0;
120  	return -ENOMEM;
121      }
122      nummods++;
123      /* perform a global init if needed */
124      init_rtapi_data(rtapi_data);
125      /* check revision code */
126      if (rtapi_data->rev_code != rev_code) {
127  	/* mismatch - release master shared memory block */
128  	rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: version mismatch %d vs %d\n", rtapi_data->rev_code, rev_code);
129  	return -EINVAL;
130      }
131      /* set up local pointers to global data */
132      module_array = rtapi_data->module_array;
133      task_array = rtapi_data->task_array;
134      shmem_array = rtapi_data->shmem_array;
135      sem_array = rtapi_data->sem_array;
136      fifo_array = rtapi_data->fifo_array;
137      irq_array = rtapi_data->irq_array;
138      /* perform local init */
139      for (n = 0; n <= RTAPI_MAX_SHMEMS; n++) {
140  	shmem_addr_array[n] = NULL;
141      }
142  
143      /* get the mutex */
144      rtapi_mutex_get(&(rtapi_data->mutex));
145      /* find empty spot in module array */
146      n = 1;
147      while ((n <= RTAPI_MAX_MODULES) && (module_array[n].state != NO_MODULE)) {
148  	n++;
149      }
150      if (n > RTAPI_MAX_MODULES) {
151  	/* no room */
152  	rtapi_mutex_give(&(rtapi_data->mutex));
153  	rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: reached module limit %d\n",
154  	    n);
155  	return -EMFILE;
156      }
157      /* we have space for the module */
158      module_id = n;
159      module = &(module_array[n]);
160      /* update module data */
161      module->state = USERSPACE;
162      if (modname != NULL) {
163  	/* use name supplied by caller, truncating if needed */
164  	snprintf(module->name, RTAPI_NAME_LEN, "%s", modname);
165      } else {
166  	/* make up a name */
167  	snprintf(module->name, RTAPI_NAME_LEN, "ULMOD%03d", module_id);
168      }
169      rtapi_data->ul_module_count++;
170      rtapi_mutex_give(&(rtapi_data->mutex));
171      rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: module '%s' inited, ID = %02d\n",
172  	module->name, module_id);
173      return module_id;
174  }
175  
176  int rtapi_exit(int module_id)
177  {
178      module_data *module;
179      int n;
180  
181      if (rtapi_data == NULL) {
182  	rtapi_print_msg(RTAPI_MSG_ERR,
183  	    "RTAPI: ERROR: exit called before init\n");
184  	return -EINVAL;
185      }
186      rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: module %02d exiting\n", module_id);
187      /* validate module ID */
188      if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
189  	rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: bad module id\n");
190  	return -EINVAL;
191      }
192      /* get mutex */
193      rtapi_mutex_get(&(rtapi_data->mutex));
194      /* point to the module's data */
195      module = &(module_array[module_id]);
196      /* check module status */
197      if (module->state != USERSPACE) {
198  	rtapi_print_msg(RTAPI_MSG_ERR,
199  	    "RTAPI: ERROR: not a userspace module\n");
200  	rtapi_mutex_give(&(rtapi_data->mutex));
201  	return -EINVAL;
202      }
203      /* clean up any mess left behind by the module */
204      for (n = 1; n <= RTAPI_MAX_SHMEMS; n++) {
205  	if (test_bit(module_id, shmem_array[n].bitmap)) {
206  	    fprintf(stderr,
207  		"ULAPI: WARNING: module '%s' failed to delete shmem %02d\n",
208  		module->name, n);
209  	    shmem_delete(n, module_id);
210  	}
211      }
212      for (n = 1; n <= RTAPI_MAX_FIFOS; n++) {
213  	if ((fifo_array[n].reader == module_id) ||
214  	    (fifo_array[n].writer == module_id)) {
215  	    fprintf(stderr,
216  		"ULAPI: WARNING: module '%s' failed to delete fifo %02d\n",
217  		module->name, n);
218  	    fifo_delete(n, module_id);
219  	}
220      }
221      /* update module data */
222      rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI: module %02d exited, name = '%s'\n",
223  	module_id, module->name);
224      module->state = NO_MODULE;
225      module->name[0] = '\0';
226      rtapi_data->ul_module_count--;
227      rtapi_mutex_give(&(rtapi_data->mutex));
228      nummods--;
229      if(nummods == 0)
230      {
231  	rtai_free(RTAPI_KEY, rtapi_data);
232  	rtapi_data = 0;
233      }
234      return 0;
235  }
236  
237  int rtapi_vsnprintf(char *buf, unsigned long int size, const char *fmt, va_list ap) {
238      return vsnprintf(buf, size, fmt, ap);
239  }
240  
241  int rtapi_snprintf(char *buf, unsigned long int size, const char *fmt, ...)
242  {
243      va_list args;
244      int i;
245  
246      va_start(args, fmt);
247      /* call the normal library vnsprintf() */
248      i = vsnprintf(buf, size, fmt, args);
249      va_end(args);
250      return i;
251  }
252  
253  #define BUFFERLEN 1024
254  
255  void rtapi_print(const char *fmt, ...)
256  {
257      char buffer[BUFFERLEN + 1];
258      va_list args;
259  
260      va_start(args, fmt);
261      /* call the normal library vnsprintf() */
262      vsnprintf(buffer, BUFFERLEN, fmt, args);
263      fputs(buffer, stdout);
264      va_end(args);
265  }
266  
267  void rtapi_print_msg(msg_level_t level, const char *fmt, ...)
268  {
269      va_list args;
270  
271      if ((level <= msg_level) && (msg_level != RTAPI_MSG_NONE)) {
272  	va_start(args, fmt);
273  	vfprintf(stderr, fmt, args);
274  	va_end(args);
275      }
276  }
277  
278  int rtapi_set_msg_level(int level)
279  {
280      if ((level < RTAPI_MSG_NONE) || (level > RTAPI_MSG_ALL)) {
281  	return -EINVAL;
282      }
283      msg_level = level;
284      return 0;
285  }
286  
287  int rtapi_get_msg_level(void)
288  {
289      return msg_level;
290  }
291  
292  void rtapi_printall(void)
293  {
294      module_data *modules;
295      task_data *tasks;
296      shmem_data *shmems;
297      sem_data *sems;
298      fifo_data *fifos;
299      irq_data *irqs;
300      int n, m;
301  
302      if (rtapi_data == NULL) {
303  	printf("rtapi_data = NULL, not initialized\n");
304  	return;
305      }
306      printf("rtapi_data = %p\n", rtapi_data);
307      printf("  magic = %d\n", rtapi_data->magic);
308      printf("  rev_code = %08x\n", rtapi_data->rev_code);
309      printf("  mutex = %lu\n", rtapi_data->mutex);
310      printf("  rt_module_count = %d\n", rtapi_data->rt_module_count);
311      printf("  ul_module_count = %d\n", rtapi_data->ul_module_count);
312      printf("  task_count  = %d\n", rtapi_data->task_count);
313      printf("  shmem_count = %d\n", rtapi_data->shmem_count);
314      printf("  sem_count   = %d\n", rtapi_data->sem_count);
315      printf("  fifo_count  = %d\n", rtapi_data->fifo_count);
316      printf("  irq_countc  = %d\n", rtapi_data->irq_count);
317      printf("  timer_running = %d\n", rtapi_data->timer_running);
318      printf("  timer_period  = %ld\n", rtapi_data->timer_period);
319      modules = &(rtapi_data->module_array[0]);
320      tasks = &(rtapi_data->task_array[0]);
321      shmems = &(rtapi_data->shmem_array[0]);
322      sems = &(rtapi_data->sem_array[0]);
323      fifos = &(rtapi_data->fifo_array[0]);
324      irqs = &(rtapi_data->irq_array[0]);
325      printf("  module array = %p\n", modules);
326      printf("  task array   = %p\n", tasks);
327      printf("  shmem array  = %p\n", shmems);
328      printf("  sem array    = %p\n", sems);
329      printf("  fifo array   = %p\n", fifos);
330      printf("  irq array    = %p\n", irqs);
331      for (n = 0; n <= RTAPI_MAX_MODULES; n++) {
332  	if (modules[n].state != NO_MODULE) {
333  	    printf("  module %02d\n", n);
334  	    printf("    state = %d\n", modules[n].state);
335  	    printf("    name = %p\n", modules[n].name);
336  	    printf("    name = '%s'\n", modules[n].name);
337  	}
338      }
339      for (n = 0; n <= RTAPI_MAX_TASKS; n++) {
340  	if (tasks[n].state != EMPTY) {
341  	    printf("  task %02d\n", n);
342  	    printf("    state = %d\n", tasks[n].state);
343  	    printf("    prio  = %d\n", tasks[n].prio);
344  	    printf("    owner = %d\n", tasks[n].owner);
345  	    printf("    code  = %p\n", tasks[n].taskcode);
346  	}
347      }
348      for (n = 0; n <= RTAPI_MAX_SHMEMS; n++) {
349  	if (shmems[n].key != 0) {
350  	    printf("  shmem %02d\n", n);
351  	    printf("    key     = %d\n", shmems[n].key);
352  	    printf("    rtusers = %d\n", shmems[n].rtusers);
353  	    printf("    ulusers = %d\n", shmems[n].ulusers);
354  	    printf("    size    = %ld\n", shmems[n].size);
355  	    printf("    bitmap  = ");
356  	    for (m = 0; m <= RTAPI_MAX_MODULES; m++) {
357  		if (test_bit(m, shmems[n].bitmap)) {
358  		    putchar('1');
359  		} else {
360  		    putchar('0');
361  		}
362  	    }
363  	    putchar('\n');
364  	}
365      }
366      for (n = 0; n <= RTAPI_MAX_SEMS; n++) {
367  	if (sems[n].key != 0) {
368  	    printf("  sem %02d\n", n);
369  	    printf("    key     = %d\n", sems[n].key);
370  	    printf("    users   = %d\n", sems[n].users);
371  	    printf("    bitmap  = ");
372  	    for (m = 0; m <= RTAPI_MAX_MODULES; m++) {
373  		if (test_bit(m, sems[n].bitmap)) {
374  		    putchar('1');
375  		} else {
376  		    putchar('0');
377  		}
378  	    }
379  	    putchar('\n');
380  	}
381      }
382      for (n = 0; n <= RTAPI_MAX_FIFOS; n++) {
383  	if (fifos[n].state != UNUSED) {
384  	    printf("  fifo %02d\n", n);
385  	    printf("    state  = %d\n", fifos[n].state);
386  	    printf("    key    = %d\n", fifos[n].key);
387  	    printf("    reader = %d\n", fifos[n].reader);
388  	    printf("    writer = %d\n", fifos[n].writer);
389  	    printf("    size   = %ld\n", fifos[n].size);
390  	}
391      }
392      for (n = 0; n <= RTAPI_MAX_IRQS; n++) {
393  	if (irqs[n].irq_num != 0) {
394  	    printf("  irq %02d\n", n);
395  	    printf("    irq_num = %d\n", irqs[n].irq_num);
396  	    printf("    owner   = %d\n", irqs[n].owner);
397  	    printf("    handler = %p\n", irqs[n].handler);
398  	}
399      }
400  }
401  
402  /***********************************************************************
403  *                  SHARED MEMORY RELATED FUNCTIONS                     *
404  ************************************************************************/
405  
406  int rtapi_shmem_new(int key, int module_id, unsigned long int size)
407  {
408      int n;
409      int shmem_id;
410      shmem_data *shmem;
411  
412      /* key must be non-zero, and also cannot match the key that RTAPI uses */
413      if ((key == 0) || (key == RTAPI_KEY)) {
414  	rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: bad shmem key: %d\n",
415  	    key);
416  	return -EINVAL;
417      }
418      /* get the mutex */
419      rtapi_mutex_get(&(rtapi_data->mutex));
420      /* validate module_id */
421      if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
422  	rtapi_mutex_give(&(rtapi_data->mutex));
423  	rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: bad module ID: %d\n",
424  	    module_id);
425  	return -EINVAL;
426      }
427      if (module_array[module_id].state != USERSPACE) {
428  	rtapi_print_msg(RTAPI_MSG_ERR,
429  	    "RTAPI: ERROR: not a user space module ID: %d\n", module_id);
430  	rtapi_mutex_give(&(rtapi_data->mutex));
431  	return -EINVAL;
432      }
433      /* check if a block is already open for this key */
434      for (n = 1; n <= RTAPI_MAX_SHMEMS; n++) {
435  	if (shmem_array[n].key == key) {
436  	    /* found a match */
437  	    shmem_id = n;
438  	    shmem = &(shmem_array[n]);
439  	    /* is it big enough? */
440  	    if (shmem->size < size) {
441  		rtapi_mutex_give(&(rtapi_data->mutex));
442  		rtapi_print_msg(RTAPI_MSG_ERR,
443  		    "RTAPI: ERROR: shmem size mismatch\n");
444  		return -EINVAL;
445  	    }
446  	    /* is this module already using it? */
447  	    if (test_bit(module_id, shmem->bitmap)) {
448  		rtapi_mutex_give(&(rtapi_data->mutex));
449  		rtapi_print_msg(RTAPI_MSG_ERR,
450  		    "RTAPI: Warning: shmem already mapped\n");
451  		return -EINVAL;
452  	    }
453  	    /* no, map it */
454  	    shmem_addr_array[shmem_id] = rtai_malloc(key, shmem->size);
455              // the check for -1 here is because rtai_malloc (in at least
456              // rtai 3.6.1, and probably others) has a bug where it
457              // sometimes returns -1 on error
458              if (shmem_addr_array[shmem_id] == NULL || shmem_addr_array[shmem_id] == (void*)-1) {
459  		/* map failed */
460  		rtapi_print_msg(RTAPI_MSG_ERR,
461  		    "RTAPI: ERROR: failed to map shmem\n");
462  		rtapi_mutex_give(&(rtapi_data->mutex));
463  		check_memlock_limit("failed to map shmem");
464  		return -ENOMEM;
465  	    }
466  	    /* update usage data */
467  	    set_bit(module_id, shmem->bitmap);
468  	    shmem->ulusers++;
469  	    /* done */
470  	    rtapi_mutex_give(&(rtapi_data->mutex));
471  	    return shmem_id;
472  	}
473      }
474      /* find empty spot in shmem array */
475      n = 1;
476      while ((n <= RTAPI_MAX_SHMEMS) && (shmem_array[n].key != 0)) {
477  	n++;
478      }
479      if (n > RTAPI_MAX_SHMEMS) {
480  	/* no room */
481  	rtapi_mutex_give(&(rtapi_data->mutex));
482  	rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI: ERROR: reached shmem limit %d\n",
483  	    n);
484  	return -EMFILE;
485      }
486      /* we have space for the block data */
487      shmem_id = n;
488      shmem = &(shmem_array[n]);
489      /* now get shared memory block from OS and save its address */
490      shmem_addr_array[shmem_id] = rtai_malloc(key, size);
491      // the check for -1 here is because rtai_malloc (in at least
492      // rtai 3.6.1, and probably others) has a bug where it
493      // sometimes returns -1 on error
494      if (shmem_addr_array[shmem_id] == NULL || shmem_addr_array[shmem_id] == (void*)-1) {
495  	rtapi_mutex_give(&(rtapi_data->mutex));
496  	rtapi_print_msg(RTAPI_MSG_ERR,
497  	    "RTAPI: ERROR: could not create shmem %d\n", n);
498  	return -ENOMEM;
499      }
500      /* the block has been created, update data */
501      set_bit(module_id, shmem->bitmap);
502      shmem->key = key;
503      shmem->rtusers = 0;
504      shmem->ulusers = 1;
505      shmem->size = size;
506      rtapi_data->shmem_count++;
507      /* zero the first word of the shmem area */
508      *((long int *) (shmem_addr_array[shmem_id])) = 0;
509      /* done */
510      rtapi_mutex_give(&(rtapi_data->mutex));
511      return shmem_id;
512  }
513  
514  #include <sys/time.h>
515  #include <sys/resource.h>
516  #define RECOMMENDED (20480*1024lu)
517  static void check_memlock_limit(const char *where) {
518      static int checked=0;
519      struct rlimit lim;
520      int result;
521      if(checked) return;
522      checked=1;
523  
524      result = getrlimit(RLIMIT_MEMLOCK, &lim);
525      if(result < 0) { perror("getrlimit"); return; }
526      if(lim.rlim_cur == (rlim_t)-1) return; // unlimited
527      if(lim.rlim_cur >= RECOMMENDED) return; // limit is at least recommended
528      rtapi_print_msg(RTAPI_MSG_ERR,
529          "RTAPI: Locked memory limit is %luKiB, recommended at least %luKiB.\n"
530          "This can cause the error '%s'.\n"
531          "For more information, see\n"
532          "\thttp://wiki.linuxcnc.org/cgi-bin/emcinfo.pl?LockedMemory\n",
533          (unsigned long)lim.rlim_cur/1024, RECOMMENDED/1024, where);
534      return;
535  }
536  
537  int rtapi_shmem_delete(int shmem_id, int module_id)
538  {
539      int retval;
540  
541      rtapi_mutex_get(&(rtapi_data->mutex));
542      retval = shmem_delete(shmem_id, module_id);
543      rtapi_mutex_give(&(rtapi_data->mutex));
544      return retval;
545  }
546  
547  int shmem_delete(int shmem_id, int module_id)
548  {
549      shmem_data *shmem;
550  
551      /* validate shmem ID */
552      if ((shmem_id < 1) || (shmem_id > RTAPI_MAX_SHMEMS)) {
553  	return -EINVAL;
554      }
555      /* point to the shmem's data */
556      shmem = &(shmem_array[shmem_id]);
557      /* is the block valid? */
558      if (shmem->key == 0) {
559  	return -EINVAL;
560      }
561      /* validate module_id */
562      if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
563  	return -EINVAL;
564      }
565      if (module_array[module_id].state != USERSPACE) {
566  	return -EINVAL;
567      }
568      /* is this module using the block? */
569      if (test_bit(module_id, shmem->bitmap) == 0) {
570  	return -EINVAL;
571      }
572      /* OK, we're no longer using it */
573      clear_bit(module_id, shmem->bitmap);
574      shmem->ulusers--;
575      /* unmap the block */
576      rtai_free(shmem->key, shmem_addr_array[shmem_id]);
577      shmem_addr_array[shmem_id] = NULL;
578      /* is somebody else still using the block? */
579      if ((shmem->ulusers > 0) || (shmem->rtusers > 0)) {
580  	/* yes, we're done for now */
581  	return 0;
582      }
583      /* update the data array and usage count */
584      shmem->key = 0;
585      shmem->size = 0;
586      rtapi_data->shmem_count--;
587      return 0;
588  }
589  
590  int rtapi_shmem_getptr(int shmem_id, void **ptr)
591  {
592      /* validate shmem ID */
593      if ((shmem_id < 1) || (shmem_id > RTAPI_MAX_SHMEMS)) {
594  	return -EINVAL;
595      }
596      /* is the block mapped? */
597      if (shmem_addr_array[shmem_id] == NULL) {
598  	return -EINVAL;
599      }
600      /* pass memory address back to caller */
601      *ptr = shmem_addr_array[shmem_id];
602      return 0;
603  }
604  
605  /***********************************************************************
606  *                       FIFO RELATED FUNCTIONS                         *
607  ************************************************************************/
608  
609  int rtapi_fifo_new(int key, int module_id, unsigned long int size, char mode)
610  {
611      enum { DEVSTR_LEN = 256 };
612      char devstr[DEVSTR_LEN];
613      int n, flags;
614      int fifo_id;
615      fifo_data *fifo;
616  
617      /* key must be non-zero */
618      if (key == 0) {
619  	return -EINVAL;
620      }
621      /* mode must be "R" or "W" */
622      if ((mode != 'R') && (mode != 'W')) {
623  	return -EINVAL;
624      }
625      /* determine mode for fifo */
626      if (mode == 'R') {
627  	flags = O_RDONLY;
628      } else {			/* mode == 'W' */
629  
630  	flags = O_WRONLY;
631      }
632      /* get the mutex */
633      rtapi_mutex_get(&(rtapi_data->mutex));
634      /* validate module_id */
635      if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
636  	rtapi_mutex_give(&(rtapi_data->mutex));
637  	return -EINVAL;
638      }
639      if (module_array[module_id].state != USERSPACE) {
640  	rtapi_mutex_give(&(rtapi_data->mutex));
641  	return -EINVAL;
642      }
643      /* check if a fifo already exists for this key */
644      for (n = 1; n <= RTAPI_MAX_FIFOS; n++) {
645  	if ((fifo_array[n].state != UNUSED) && (fifo_array[n].key == key)) {
646  	    /* found a match */
647  	    fifo_id = n;
648  	    fifo = &(fifo_array[n]);
649  	    /* is the desired mode available */
650  	    if (mode == 'R') {
651  		if (fifo->state & HAS_READER) {
652  		    rtapi_mutex_give(&(rtapi_data->mutex));
653  		    return -EBUSY;
654  		}
655  		/* determine system name for fifo */
656  		sprintf(devstr, "/dev/rtf%d", fifo_id);
657  		/* open the fifo */
658  		fifo_fd_array[fifo_id] = open(devstr, flags);
659  		if (fifo_fd_array[fifo_id] < 0) {
660  		    /* open failed */
661  		    rtapi_mutex_give(&(rtapi_data->mutex));
662  		    return -ENOENT;
663  		}
664  		/* fifo opened, update status */
665  		fifo->state |= HAS_READER;
666  		fifo->reader = module_id;
667  		rtapi_mutex_give(&(rtapi_data->mutex));
668  		return fifo_id;
669  	    } else {		/* mode == 'W' */
670  
671  		if (fifo->state & HAS_WRITER) {
672  		    rtapi_mutex_give(&(rtapi_data->mutex));
673  		    return -EBUSY;
674  		}
675  		/* determine system name for fifo */
676  		sprintf(devstr, "/dev/rtf%d", fifo_id);
677  		/* open the fifo */
678  		fifo_fd_array[fifo_id] = open(devstr, flags);
679  		if (fifo_fd_array[fifo_id] < 0) {
680  		    /* open failed */
681  		    rtapi_mutex_give(&(rtapi_data->mutex));
682  		    return -ENOENT;
683  		}
684  		/* fifo opened, update status */
685  		fifo->state |= HAS_WRITER;
686  		fifo->writer = module_id;
687  		rtapi_mutex_give(&(rtapi_data->mutex));
688  		return fifo_id;
689  	    }
690  	}
691      }
692      /* find empty spot in fifo array */
693      n = 1;
694      while ((n <= RTAPI_MAX_FIFOS) && (fifo_array[n].state != UNUSED)) {
695  	n++;
696      }
697      if (n > RTAPI_MAX_FIFOS) {
698  	/* no room */
699  	rtapi_mutex_give(&(rtapi_data->mutex));
700  	return -EMFILE;
701      }
702      /* we have a free ID for the fifo */
703      fifo_id = n;
704      fifo = &(fifo_array[n]);
705      /* determine system name for fifo */
706      sprintf(devstr, "/dev/rtf%d", fifo_id);
707      /* open the fifo */
708      fifo_fd_array[fifo_id] = open(devstr, flags);
709      if (fifo_fd_array[fifo_id] < 0) {
710  	/* open failed */
711  	rtapi_mutex_give(&(rtapi_data->mutex));
712  	return -ENOENT;
713      }
714      /* the fifo has been created, update data */
715      if (mode == 'R') {
716  	fifo->state = HAS_READER;
717  	fifo->reader = module_id;
718      } else {			/* mode == 'W' */
719  
720  	fifo->state = HAS_WRITER;
721  	fifo->writer = module_id;
722      }
723      fifo->key = key;
724      fifo->size = size;
725      rtapi_data->fifo_count++;
726      /* done */
727      rtapi_mutex_give(&(rtapi_data->mutex));
728      return fifo_id;
729  }
730  
731  int rtapi_fifo_delete(int fifo_id, int module_id)
732  {
733      int retval;
734  
735      rtapi_mutex_get(&(rtapi_data->mutex));
736      retval = fifo_delete(fifo_id, module_id);
737      rtapi_mutex_give(&(rtapi_data->mutex));
738      return retval;
739  }
740  
741  static int fifo_delete(int fifo_id, int module_id)
742  {
743      fifo_data *fifo;
744  
745      /* validate fifo ID */
746      if ((fifo_id < 1) || (fifo_id > RTAPI_MAX_FIFOS)) {
747  	return -EINVAL;
748      }
749      /* point to the fifo's data */
750      fifo = &(fifo_array[fifo_id]);
751      /* is the fifo valid? */
752      if (fifo->state == UNUSED) {
753  	return -EINVAL;
754      }
755      /* validate module_id */
756      if ((module_id < 1) || (module_id > RTAPI_MAX_MODULES)) {
757  	return -EINVAL;
758      }
759      if (module_array[module_id].state != USERSPACE) {
760  	return -EINVAL;
761      }
762      /* is this module using the fifo? */
763      if ((fifo->reader != module_id) && (fifo->writer != module_id)) {
764  	return -EINVAL;
765      }
766      /* update fifo state */
767      if (fifo->reader == module_id) {
768  	fifo->state &= ~HAS_READER;
769  	fifo->reader = 0;
770      }
771      if (fifo->writer == module_id) {
772  	fifo->state &= ~HAS_WRITER;
773  	fifo->writer = 0;
774      }
775      /* close the fifo */
776      if (close(fifo_id) < 0) {
777  	return -ENOENT;
778      }
779      /* is somebody else still using the fifo */
780      if (fifo->state != UNUSED) {
781  	/* yes, done for now */
782  	return 0;
783      }
784      /* no other users, update the data array and usage count */
785      fifo->state = UNUSED;
786      fifo->key = 0;
787      fifo->size = 0;
788      rtapi_data->fifo_count--;
789      return 0;
790  }
791  
792  int rtapi_fifo_read(int fifo_id, char *buf, unsigned long int size)
793  {
794      int retval;
795  
796      fifo_data *fifo;
797  
798      /* validate fifo ID */
799      if ((fifo_id < 1) || (fifo_id > RTAPI_MAX_FIFOS)) {
800  	return -EINVAL;
801      }
802      /* point to the fifo's data */
803      fifo = &(fifo_array[fifo_id]);
804      /* is the fifo valid? */
805      if ((fifo->state & HAS_READER) == 0) {
806  	return -EINVAL;
807      }
808      /* get whatever data is available */
809      retval = read(fifo_fd_array[fifo_id], buf, size);
810      if (retval <= 0) {
811  	return -EINVAL;
812      }
813      return retval;
814  
815  }
816  
817  int rtapi_fifo_write(int fifo_id, char *buf, unsigned long int size)
818  {
819      int retval;
820      fifo_data *fifo;
821  
822      /* validate fifo ID */
823      if ((fifo_id < 1) || (fifo_id > RTAPI_MAX_FIFOS)) {
824  	return -EINVAL;
825      }
826      /* point to the fifo's data */
827      fifo = &(fifo_array[fifo_id]);
828      /* is the fifo valid? */
829      if ((fifo->state & HAS_WRITER) == 0) {
830  	return -EINVAL;
831      }
832      /* put whatever data will fit */
833      retval = write(fifo_fd_array[fifo_id], buf, size);
834      if (retval < 0) {
835  	return -EINVAL;
836      }
837      return retval;
838  }
839  
840  /***********************************************************************
841  *                        I/O RELATED FUNCTIONS                         *
842  ************************************************************************/
843  
844  void rtapi_outb(unsigned char byte, unsigned int port)
845  {
846      outb(byte, port);
847  }
848  
849  unsigned char rtapi_inb(unsigned int port)
850  {
851      return inb(port);
852  }
853  
854  int rtapi_is_realtime() { return 1; }
855  int rtapi_is_kernelspace() { return 1; }
856  
857  void rtapi_delay(long ns) {
858      if(ns > rtapi_delay_max()) ns = rtapi_delay_max();
859      struct timespec ts = {0, ns};
860      clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, 0);
861  }
862  
863  long int rtapi_delay_max() { return 999999999; }