/ src / hal / components / watchdog.c
watchdog.c
  1  /********************************************************************
  2  * Description: watchdog.c
  3  *   Watchdog for HAL
  4  *
  5  *   See the "Users Manual" at emc2/docs/Hal_Introduction.pdf
  6  *
  7  *
  8  * This module provides a single module that can monitor heartbeats from
  9  * several other modules (like a charge pump signal), to make sure that
 10  * no monitored process or HAL thread dies or takes too long between
 11  * execution cycles
 12  *
 13  *********************************************************************
 14  *
 15  * Author: Stephen Wille Padnos (swpadnos AT sourceforge DOT net)
 16  * License: GPL Version 2
 17  * Created on: Jue 19, 2010
 18  * System: Linux
 19  *
 20  * Copyright (c) 2010 All rights reserved.
 21  *
 22  ********************************************************************/
 23  
 24  #include "rtapi.h"		/* RTAPI realtime OS API */
 25  #include "rtapi_app.h"		/* RTAPI realtime module decls */
 26  #include "hal.h"		/* HAL public API decls */
 27  
 28  /* module information */
 29  MODULE_AUTHOR("Stephen Wille Padnos");
 30  MODULE_DESCRIPTION("Multiple input watchdog for EMC HAL");
 31  MODULE_LICENSE("GPL");
 32  int num_inputs=-1;			// must specify a count on the loadrt line
 33  RTAPI_MP_INT(num_inputs, "Number of inputs");
 34  
 35  /***********************************************************************
 36  *                STRUCTURES AND GLOBAL VARIABLES                       *
 37  ************************************************************************/
 38  
 39  /* Data needed for each input */
 40  typedef struct {
 41      hal_bit_t *input;		/* pin: the input bit HAL pin */
 42      hal_float_t timeout;	/* param: maximum alloewd timeb without a transition on bit */
 43      hal_float_t oldtimeout;	/* internal:  used to determine whether the timeout has changed */
 44      hal_s32_t c_secs, c_nsecs;	/* internal:  elapsed seconds and nanoseconds */
 45      hal_s32_t t_secs, t_nsecs;	/* internal:  seconds and nanoseconds for timeout */
 46      hal_bit_t last;		/* internal:  last value of the input pin */
 47  } watchdog_input_t;
 48  
 49  #define MAX_INPUTS	32
 50  
 51  /* Base data for a weighted summer. */
 52  typedef struct {
 53    hal_bit_t *output;		/* output pin: high if all inputs are toggling, low otherwise */
 54    hal_bit_t *enable;		/* pin: only runs while this is high (kind of like an enable) */
 55  } watchdog_data_t;
 56  
 57  /* other globals */
 58  static int comp_id;		/* component ID */
 59  watchdog_input_t *inputs;	/* internal: pointer to the input bits and weights */
 60  watchdog_data_t *data;		/* common data */
 61  hal_bit_t old_enable;
 62  /***********************************************************************
 63  *                  LOCAL FUNCTION DECLARATIONS                         *
 64  ************************************************************************/
 65  
 66  static void process(void *arg, long period);
 67  static void set_timeouts(void *arg, long period);
 68  
 69  /***********************************************************************
 70  *                       INIT AND EXIT CODE                             *
 71  ************************************************************************/
 72  
 73  int rtapi_app_main(void)
 74  {
 75      int n, retval;
 76  
 77  
 78      /* check that there's at least one valid input requested */
 79      if (num_inputs<1) {
 80        rtapi_print_msg(RTAPI_MSG_ERR, "WATCHDOG: ERROR: must specify at least one input\n");
 81        return -1;
 82      }
 83  
 84      /* but not too many */
 85      if (num_inputs> MAX_INPUTS) {
 86        rtapi_print_msg(RTAPI_MSG_ERR, "WATCHDOG: ERROR: too many inputs requested (%d > %d)\n", num_inputs, MAX_INPUTS);
 87        return -1;
 88      }
 89  
 90      /* have good config info, connect to the HAL */
 91      comp_id = hal_init("watchdog");
 92      if (comp_id < 0) {
 93  	rtapi_print_msg(RTAPI_MSG_ERR,
 94  	    "WATCHDOG: ERROR: hal_init() failed (Return code %d)\n", comp_id);
 95  	return -1;
 96      }
 97  
 98      /* allocate shared memory for watchdog global and pin info */
 99      data = hal_malloc(sizeof(watchdog_data_t));
100      if (data == 0) {
101  	rtapi_print_msg(RTAPI_MSG_ERR,
102  	    "WATCHDOG: ERROR: hal_malloc() for common data failed\n");
103  	hal_exit(comp_id);
104  	goto err;
105      }
106  
107      inputs = hal_malloc(num_inputs * sizeof(watchdog_input_t));
108      if (inputs == 0) {
109  	rtapi_print_msg(RTAPI_MSG_ERR,
110  	    "WATCHDOG: ERROR: hal_malloc() for input pins failed\n");
111  	hal_exit(comp_id);
112  	goto err;
113      }
114  
115      /* export pins/params for all inputs */
116      for (n = 0; n < num_inputs; n++) {
117        retval=hal_pin_bit_newf(HAL_IN, &(inputs[n].input), comp_id, "watchdog.input-%d", n);
118        if (retval != 0) {
119  	  rtapi_print_msg(RTAPI_MSG_ERR,
120  	      "WATCHDOG: ERROR: couldn't create input pin watchdog.input-%d\n", n);
121  	  goto err;
122        }
123        retval=hal_param_float_newf(HAL_RW, &(inputs[n].timeout), comp_id, "watchdog.timeout-%d", n);
124        if (retval != 0) {
125  	  rtapi_print_msg(RTAPI_MSG_ERR,
126  	      "WATCHDOG: ERROR: couldn't create input parameter watchdog.timeout-%d\n", n);
127  	  goto err;
128        }
129        
130        inputs[n].timeout=0;
131        inputs[n].oldtimeout=-1;
132        inputs[n].c_secs = inputs[n].t_secs = 0;
133        inputs[n].c_nsecs = inputs[n].t_nsecs = 0;
134        inputs[n].last = *(inputs[n].input);
135      }
136  
137      /* export "global" pins */
138      retval=hal_pin_bit_newf(HAL_OUT, &(data->output), comp_id, "watchdog.ok-out");
139      if (retval != 0) {
140  	rtapi_print_msg(RTAPI_MSG_ERR,
141  	    "WATCHDOG: ERROR: couldn't create output pin watchdog.ok-out\n");
142  	goto err;
143      }
144      retval=hal_pin_bit_newf(HAL_IN, &(data->enable), comp_id, "watchdog.enable-in");
145      if (retval != 0) {
146  	rtapi_print_msg(RTAPI_MSG_ERR,
147  	    "WATCHDOG: ERROR: couldn't create input pin watchdog.enable-in\n");
148  	goto err;
149      }
150      
151      /* export functions */
152      retval = hal_export_funct("watchdog.process", process, inputs, 0, 0, comp_id);
153      if (retval != 0) {
154  	rtapi_print_msg(RTAPI_MSG_ERR,
155  	    "WATCHDOG: ERROR: process funct export failed\n");
156  	goto err;
157      }
158  
159      retval = hal_export_funct("watchdog.set-timeouts", set_timeouts, inputs, 1, 0, comp_id);
160      if (retval != 0) {
161  	rtapi_print_msg(RTAPI_MSG_ERR,
162  	    "WATCHDOG: ERROR: set_timeouts funct export failed\n");
163  	goto err;
164      }
165  
166      rtapi_print_msg(RTAPI_MSG_INFO,
167  	"WATCHDOG: installed watchdog with %d inputs\n", num_inputs);
168      hal_ready(comp_id);
169      return 0;
170      
171  err:
172      hal_exit(comp_id);
173      return -1;
174  }
175  
176  void rtapi_app_exit(void)
177  {
178      hal_exit(comp_id);
179  }
180  
181  /***********************************************************************
182  *                     REALTIME FUNCTIONS                               *
183  ************************************************************************/
184  
185  /*  This procedude checks all the input bits for changes.  If a change
186      is detected, the corresponding timer is reset.  If no change was
187      detected, "period" is subtracted from the timer.  If any timer gets
188      to zero, the output is cleared and enable must go low and high again
189      to re-start the watchdog.
190  */
191  static void process(void *arg, long period)
192  {
193      int i, fault=0;
194      // set_timeouts has to turn on the output when it detects a valid
195      // transition on enable
196      if (!(*data->enable) || (!(*data->output))) return;
197      for (i=0;i<num_inputs;i++) {
198        if (*(inputs[i].input) != inputs[i].last) {
199  	inputs[i].c_secs = inputs[i].t_secs;
200  	inputs[i].c_nsecs = inputs[i].t_nsecs;
201        } else {
202  	inputs[i].c_nsecs -= period;
203  	if (inputs[i].c_nsecs<0) {
204  	  inputs[i].c_nsecs += 1000000000;
205  	  if (inputs[i].c_secs>0) {
206  	    inputs[i].c_secs--;
207  	  } else {
208  	    fault=1;
209  	    inputs[i].c_secs = inputs[i].c_nsecs = 0;
210  	  }
211  	}
212        }
213        inputs[i].last=*(inputs[i].input);
214      }
215      if (fault) *(data->output)=0;
216  }
217  
218  static void set_timeouts(void *arg, long period)
219  {
220      int i;
221      hal_float_t temp;
222      
223      for (i=0;i<num_inputs;i++) {
224        temp=inputs[i].timeout;
225        if (temp<0) temp=0;	// no negative timeout periods
226        if (temp != inputs[i].oldtimeout) {
227  	// new timeout, convert to secs/ns
228  	inputs[i].oldtimeout=temp;
229  	inputs[i].t_secs=temp;
230  	temp -= inputs[i].t_secs;
231  	inputs[i].t_nsecs=(1e9*temp);
232        }
233      }
234      if (!*(data->output)) {
235  	if (*(data->enable) && !old_enable) {
236  	  // rising edge on enable, so we can restart
237  	  for (i=0;i<num_inputs;i++) {
238  	    inputs[i].c_secs = inputs[i].t_secs;
239  	    inputs[i].c_nsecs = inputs[i].t_nsecs;
240  	  }
241  	  *(data->output) = 1;
242  	}
243      }
244      old_enable=*(data->enable);
245  }
246  /***********************************************************************
247  *                   LOCAL FUNCTION DEFINITIONS                         *
248  ************************************************************************/