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