/ src / hal / components / streamer.c
streamer.c
  1  /********************************************************************
  2  * Description:  streamer.c
  3  *               A HAL component that can be used to stream data
  4  *               from a file onto HAL pins at a specific realtime
  5  *               sample rate.
  6  *
  7  * Author: John Kasunich <jmkasunich at sourceforge dot net>
  8  * License: GPL Version 2
  9  *    
 10  * Copyright (c) 2006 All rights reserved.
 11  *
 12  ********************************************************************/
 13  /** This file, 'streamer.c', is the realtime part of a HAL component
 14      that allows numbers stored in a file to be "streamed" onto HAL
 15      pins at a uniform realtime sample rate.  When the realtime module
 16      is loaded, it creates a fifo in shared memory.  Then, the user
 17      space program 'hal_streamer' is invoked.  'hal_streamer' takes 
 18      input from stdin and writes it to the fifo, and this component 
 19      transfers the data from the fifo to HAL pins.
 20  
 21      Loading:
 22  
 23      loadrt streamer depth=100 cfg=uffb
 24  
 25  
 26  */
 27  
 28  /** Copyright (C) 2006 John Kasunich
 29   */
 30  
 31  /** This program is free software; you can redistribute it and/or
 32      modify it under the terms of version 2 of the GNU General
 33      Public License as published by the Free Software Foundation.
 34      This library is distributed in the hope that it will be useful,
 35      but WITHOUT ANY WARRANTY; without even the implied warranty of
 36      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 37      GNU General Public License for more details.
 38  
 39      You should have received a copy of the GNU General Public
 40      License along with this library; if not, write to the Free Software
 41      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 42  
 43      THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
 44      ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 45      TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 46      harming persons must have provisions for completely removing power
 47      from all motors, etc, before persons enter any danger area.  All
 48      machinery must be designed to comply with local and national safety
 49      codes, and the authors of this software can not, and do not, take
 50      any responsibility for such compliance.
 51  
 52      This code was written as part of the EMC HAL project.  For more
 53      information, go to www.linuxcnc.org.
 54  */
 55  
 56  
 57  /* Notes:
 58   * streamer.N.cur-depth, streamer.N.empty and streamer.N.underruns are
 59   * updated even if streamer.N.enabled is set to false.
 60   *
 61   * clock and clock_mode pins are provided to enable clocking.
 62   * The clock input pin actions are controlled by the clock_mode pin value:
 63   *   0: freerun at every loop (default)
 64   *   1: clock by falling edge
 65   *   2: clock by rising edge
 66   *   3: clock by any edge
 67   */
 68  #include "rtapi.h"              /* RTAPI realtime OS API */
 69  #include "rtapi_app.h"          /* RTAPI realtime module decls */
 70  #include "hal.h"                /* HAL public API decls */
 71  #include "streamer.h"		/* decls and such for fifos */
 72  #include "rtapi_errno.h"
 73  #include "rtapi_string.h"
 74  
 75  /* module information */
 76  MODULE_AUTHOR("John Kasunich");
 77  MODULE_DESCRIPTION("Realtime File Streamer HAL");
 78  MODULE_LICENSE("GPL");
 79  static char *cfg[MAX_STREAMERS];	/* config string, no default */
 80  RTAPI_MP_ARRAY_STRING(cfg,MAX_STREAMERS,"config string");
 81  static int depth[MAX_STREAMERS];	/* depth of fifo, default 0 */
 82  RTAPI_MP_ARRAY_INT(depth,MAX_STREAMERS,"fifo depth");
 83  
 84  /***********************************************************************
 85  *                STRUCTURES AND GLOBAL VARIABLES                       *
 86  ************************************************************************/
 87  
 88  /* this structure contains the HAL shared memory data for one streamer */
 89  
 90  typedef struct {
 91      hal_stream_t fifo;		/* pointer to user/RT fifo */
 92      hal_s32_t *curr_depth;	/* pin: current fifo depth */
 93      hal_bit_t *empty;		/* pin: underrun flag */
 94      hal_bit_t *enable;		/* pin: enable streaming */
 95      hal_s32_t *underruns;	/* pin: number of underruns */
 96      hal_bit_t *clock;		/* pin: clock input */
 97      hal_s32_t *clock_mode;	/* pin: clock mode */
 98      int myclockedge;	        /* clock edge detector */
 99      pin_data_t pins[HAL_STREAM_MAX_PINS];
100  } streamer_t;
101  
102  /* other globals */
103  static int comp_id;		/* component ID */
104  static int nstreamers;
105  static streamer_t *streams;
106  
107  /***********************************************************************
108  *                  LOCAL FUNCTION DECLARATIONS                         *
109  ************************************************************************/
110  
111  static int init_streamer(int num, streamer_t *stream);
112  static void update(void *arg, long period);
113  
114  /***********************************************************************
115  *                       INIT AND EXIT CODE                             *
116  ************************************************************************/
117  
118  int rtapi_app_main(void)
119  {
120      int n, retval;
121  
122      comp_id = hal_init("streamer");
123      if (comp_id < 0) {
124  	rtapi_print_msg(RTAPI_MSG_ERR, "STREAMER: ERROR: hal_init() failed\n");
125  	return -EINVAL;
126      }
127  
128      streams = hal_malloc(MAX_STREAMERS * sizeof(streamer_t));
129  
130      /* validate config info */
131      for ( n = 0 ; n < MAX_STREAMERS ; n++ ) {
132  	if (( cfg[n] == NULL ) || ( *cfg == '\0' ) || ( depth[n] <= 0 )) {
133  	    break;
134  	}
135  	retval = hal_stream_create(&streams[n].fifo, comp_id, STREAMER_SHMEM_KEY+n, depth[n], cfg[n]);
136  	if(retval < 0) {
137  	    goto fail;
138  	}
139  	nstreamers++;
140  	retval = init_streamer(n, &streams[n]);
141      }
142      if ( n == 0 ) {
143  	rtapi_print_msg(RTAPI_MSG_ERR,
144  	    "STREAMER: ERROR: no channels specified\n");
145  	retval = -EINVAL;
146  	goto fail;
147      }
148  
149      hal_ready(comp_id);
150      return 0;
151  fail:
152      for(n=0; n<nstreamers; n++) hal_stream_destroy(&streams[n].fifo);
153      hal_exit(comp_id);
154      return retval;
155  }
156  
157  void rtapi_app_exit(void)
158  {
159      int i;
160      for(i=0; i<nstreamers; i++) hal_stream_destroy(&streams[i].fifo);
161      hal_exit(comp_id);
162  }
163  
164  /***********************************************************************
165  *            REALTIME COUNTER COUNTING AND UPDATE FUNCTIONS            *
166  ************************************************************************/
167  
168  static void update(void *arg, long period)
169  {
170      streamer_t *str;
171      pin_data_t *pptr;
172      int n, doclk;
173  
174      /* point at streamer struct in HAL shmem */
175      str = arg;
176      /* keep last two clock states to get all possible clock edges */
177      int myclockedge =
178          str->myclockedge=((str->myclockedge<<1) | (*(str->clock) & 1)) & 3;
179      /* are we enabled? - generate doclock if enabled and right mode  */
180      doclk=0;
181      if ( *(str->enable) ) {
182         doclk=1;
183         switch (*str->clock_mode) {
184               /* clock-mode 0 means do clock if enabled */
185               case 0:
186                     break;
187               /* clock-mode 1 means enabled & falling edge */
188               case 1:
189                     if ( myclockedge!=2) {
190                           doclk=0;
191                     }
192                     break;
193               /* clock-mode 2 means enabled & rising edge */
194               case 2:
195                     if ( myclockedge!=1) {
196                           doclk=0;
197                     }
198                     break;
199               /* clock-mode 3 means enabled & both edges */
200               case 3:
201                     if ((myclockedge==0) | ( myclockedge==3)) {
202                           doclk=0;
203                     }
204                     break;
205               default:
206                     break;
207         }
208      }
209      /* pint at HAL pins */
210      pptr = str->pins;
211      /* point at user/RT fifo in other shmem */
212      int depth = hal_stream_depth(&str->fifo);
213      *(str->curr_depth) = depth;
214      *(str->empty) = depth == 0;
215      if(!doclk)
216  	/* done - output pins retain current values */
217  	return;
218      if(depth == 0) {
219  	/* increase underrun only for valid clock*/
220  	(*str->underruns)++;
221  	return;
222      }
223      union hal_stream_data data[HAL_STREAM_MAX_PINS];
224      if(hal_stream_read(&str->fifo, data, NULL) < 0)
225      {
226          /* should not happen (single reader invariant) */
227  	(*str->underruns)++;
228  	return;
229      }
230      union hal_stream_data *dptr = data;
231      int num_pins = hal_stream_element_count(&str->fifo);
232      /* copy data from fifo to HAL pins */
233      for ( n = 0 ; n < num_pins ; n++ ) {
234  	switch ( hal_stream_element_type(&str->fifo, n) ) {
235  	case HAL_FLOAT:
236  	    *(pptr->hfloat) = dptr->f;
237  	    break;
238  	case HAL_BIT:
239  	    if ( dptr->b ) {
240  		*(pptr->hbit) = 1;
241  	    } else {
242  		*(pptr->hbit) = 0;
243  	    }
244  	    break;
245  	case HAL_U32:
246  	    *(pptr->hu32) = dptr->u;
247  	    break;
248  	case HAL_S32:
249  	    *(pptr->hs32) = dptr->s;
250  	    break;
251  	default:
252  	    break;
253  	}
254  	dptr++;
255  	pptr++;
256      }
257  }
258  
259  static int init_streamer(int num, streamer_t *str)
260  {
261      int retval, n, usefp;
262      pin_data_t *pptr;
263      char buf[HAL_NAME_LEN + 1];
264  
265      /* export "standard" pins and params */
266      retval = hal_pin_bit_newf(HAL_OUT, &(str->empty), comp_id,
267  	"streamer.%d.empty", num);
268      if (retval != 0 ) {
269  	rtapi_print_msg(RTAPI_MSG_ERR,
270  	    "STREAMER: ERROR: 'empty' pin export failed\n");
271  	return -EIO;
272      }
273      retval = hal_pin_bit_newf(HAL_IN, &(str->enable), comp_id,
274  	"streamer.%d.enable", num);
275      if (retval != 0 ) {
276  	rtapi_print_msg(RTAPI_MSG_ERR,
277  	    "STREAMER: ERROR: 'enable' pin export failed\n");
278  	return -EIO;
279      }
280      retval = hal_pin_s32_newf(HAL_OUT, &(str->curr_depth), comp_id,
281  	"streamer.%d.curr-depth", num);
282      if (retval != 0 ) {
283  	rtapi_print_msg(RTAPI_MSG_ERR,
284  	    "STREAMER: ERROR: 'curr_depth' pin export failed\n");
285  	return -EIO;
286      }
287      retval = hal_pin_s32_newf(HAL_IO, &(str->underruns), comp_id,
288  	"streamer.%d.underruns", num);
289      if (retval != 0 ) {
290  	rtapi_print_msg(RTAPI_MSG_ERR,
291  	    "STREAMER: ERROR: 'underruns' pin export failed\n");
292  	return -EIO;
293      }
294  
295      retval = hal_pin_bit_newf(HAL_IN, &(str->clock), comp_id,
296  	"streamer.%d.clock", num);
297      if (retval != 0 ) {
298  	rtapi_print_msg(RTAPI_MSG_ERR,
299  	    "STREAMER: ERROR: 'clock' pin export failed\n");
300  	return -EIO;
301      }
302  
303      retval = hal_pin_s32_newf(HAL_IN, &(str->clock_mode), comp_id,
304  	"streamer.%d.clock-mode", num);
305      if (retval != 0 ) {
306  	rtapi_print_msg(RTAPI_MSG_ERR,
307  	    "STREAMER: ERROR: 'clock_mode' pin export failed\n");
308  	return -EIO;
309      }
310  
311      /* init the standard pins and params */
312      *(str->empty) = 1;
313      *(str->enable) = 1;
314      *(str->curr_depth) = 0;
315      *(str->underruns) = 0;
316      *(str->clock_mode) = 0;
317      pptr = str->pins;
318      usefp = 0;
319      /* export user specified pins (the ones that stream data) */
320      for ( n = 0 ; n < hal_stream_element_count(&str->fifo); n++ ) {
321  	rtapi_snprintf(buf, sizeof(buf), "streamer.%d.pin.%d", num, n);
322  	retval = hal_pin_new(buf, hal_stream_element_type(&str->fifo, n), HAL_OUT, (void **)pptr, comp_id );
323  	if (retval != 0 ) {
324  	    rtapi_print_msg(RTAPI_MSG_ERR,
325  		"STREAMER: ERROR: pin '%s' export failed\n", buf);
326  	    return -EIO;
327  	}
328  	/* init the pin value */
329  	switch ( hal_stream_element_type(&str->fifo, n) ) {
330  	case HAL_FLOAT:
331  	    *(pptr->hfloat) = 0.0;
332  	    usefp = 1;
333  	    break;
334  	case HAL_BIT:
335  	    *(pptr->hbit) = 0;
336  	    break;
337  	case HAL_U32:
338  	    *(pptr->hu32) = 0;
339  	    break;
340  	case HAL_S32:
341  	    *(pptr->hs32) = 0;
342  	    break;
343  	default:
344  	    break;
345  	}
346  	pptr++;
347      }
348      /* export update function */
349      rtapi_snprintf(buf, sizeof(buf), "streamer.%d", num);
350      retval = hal_export_funct(buf, update, str, usefp, 0, comp_id);
351      if (retval != 0) {
352  	rtapi_print_msg(RTAPI_MSG_ERR,
353  	    "STREAMER: ERROR: function export failed\n");
354  	return retval;
355      }
356  
357      return 0;
358  }
359