hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/*
 * Copyright (C) 2011 Philippe Gerum <rpm@xenomai.org>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include <errno.h>
#include <string.h>
#include <copperplate/threadobj.h>
#include <copperplate/heapobj.h>
#include "reference.h"
#include "internal.h"
#include "alarm.h"
#include "timer.h"
 
/**
 * @ingroup alchemy
 * @defgroup alchemy_alarm Alarm services
 *
 * General-purpose watchdog timers
 *
 * Alarms are general-purpose watchdog timers. Alchemy tasks may
 * create any number of alarms and use them to run a user-defined
 * handler, after a specified initial delay has elapsed. Alarms can be
 * either one shot or periodic; in the latter case, the real-time
 * system automatically reprograms the alarm for the next shot
 * according to a user-defined interval value.
 *
 * @{
 */
 
struct pvcluster alchemy_alarm_table;
 
static DEFINE_NAME_GENERATOR(alarm_namegen, "alarm",
                struct alchemy_alarm, name);
 
#ifdef CONFIG_XENO_REGISTRY
 
static int alarm_registry_open(struct fsobj *fsobj, void *priv)
{
   struct fsobstack *o = priv;
   struct alchemy_alarm *acb;
   struct itimerspec itmspec;
   unsigned long expiries;
   struct timespec delta;
   int ret;
 
   acb = container_of(fsobj, struct alchemy_alarm, fsobj);
   ret = timerobj_lock(&acb->tmobj);
   if (ret)
       return ret;
   itmspec = acb->itmspec;
   expiries = acb->expiries;
   timerobj_unlock(&acb->tmobj);
 
   fsobstack_init(o);
 
   fsobstack_grow_format(o, "%-12s%-12s%-12s\n",
                 "[EXPIRIES]", "[DISTANCE]", "[INTERVAL]");
   clockobj_get_distance(&alchemy_clock, &itmspec, &delta);
   fsobstack_grow_format(o, "%8lu%10ld\"%ld%10ld\"%ld\n",
                 expiries,
                 delta.tv_sec,
                 delta.tv_nsec / 100000000,
                 itmspec.it_interval.tv_sec,
                 itmspec.it_interval.tv_nsec / 100000000);
 
   fsobstack_finish(o);
 
   return 0;
}
 
static struct registry_operations registry_ops = {
   .open        = alarm_registry_open,
   .release    = fsobj_obstack_release,
   .read        = fsobj_obstack_read
};
 
#else /* !CONFIG_XENO_REGISTRY */
 
static struct registry_operations registry_ops;
 
#endif /* CONFIG_XENO_REGISTRY */
 
static struct alchemy_alarm *get_alchemy_alarm(RT_ALARM *alarm, int *err_r)
{
   struct alchemy_alarm *acb;
 
   if (bad_pointer(alarm))
       goto bad_handle;
 
   acb = (struct alchemy_alarm *)alarm->handle;
   if (bad_pointer(acb) || timerobj_lock(&acb->tmobj))
       goto bad_handle;
 
   if (acb->magic == alarm_magic)
       return acb;
bad_handle:
   *err_r = -EINVAL;
 
   return NULL;
}
 
static inline void put_alchemy_alarm(struct alchemy_alarm *acb)
{
   timerobj_unlock(&acb->tmobj);
}
 
static void alarm_handler(struct timerobj *tmobj)
{
   struct alchemy_alarm *acb;
 
   acb = container_of(tmobj, struct alchemy_alarm, tmobj);
   acb->expiries++;
   acb->handler(acb->arg);
}
 
/**
 * @fn int rt_alarm_create(RT_ALARM *alarm,const char *name,void (*handler)(void *arg),void *arg)
 * @brief Create an alarm object.
 *
 * This routine creates an object triggering an alarm routine at a
 * specified time in the future. Alarms can be periodic or oneshot,
 * depending on the reload interval value passed to rt_alarm_start().
 *
 * @param alarm The address of an alarm descriptor which can be later
 * used to identify uniquely the created object, upon success of this
 * call.
 *
 * @param name An ASCII string standing for the symbolic name of the
 * alarm. When non-NULL and non-empty, a copy of this string is used
 * for indexing the created alarm into the object registry.
 *
 * @param handler The address of the routine to call when the alarm
 * expires. This routine is passed the @a arg value.
 *
 * @param arg A user-defined opaque argument passed to the @a handler.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -ENOMEM is returned if the system fails to get memory from the
 * local pool in order to create the alarm.
 *
 * - -EEXIST is returned if the @a name is conflicting with an already
 * registered alarm.
 *
 * - -EPERM is returned if this service was called from an
 * asynchronous context.
 *
 * @apitags{mode-unrestricted, switch-secondary}
 *
 * @note Alarms are process-private objects and thus cannot be shared
 * by multiple processes, even if they belong to the same Xenomai
 * session.
 */
#ifndef DOXYGEN_CPP
CURRENT_IMPL(int, rt_alarm_create, (RT_ALARM *alarm, const char *name,
                   void (*handler)(void *arg),
                   void *arg))
#else
int rt_alarm_create(RT_ALARM *alarm, const char *name,
           void (*handler)(void *arg),
           void *arg)
#endif
{
   struct alchemy_alarm *acb;
   struct service svc;
   int ret;
 
   CANCEL_DEFER(svc);
 
   acb = pvmalloc(sizeof(*acb));
   if (acb == NULL) {
       ret = -ENOMEM;
       goto out;
   }
 
   ret = timerobj_init(&acb->tmobj);
   if (ret)
       goto fail;
 
   generate_name(acb->name, name, &alarm_namegen);
   acb->handler = handler;
   acb->arg = arg;
   acb->expiries = 0;
   memset(&acb->itmspec, 0, sizeof(acb->itmspec));
   acb->magic = alarm_magic;
 
   registry_init_file_obstack(&acb->fsobj, &registry_ops);
   ret = __bt(registry_add_file(&acb->fsobj, O_RDONLY,
                    "/alchemy/alarms/%s", acb->name));
   if (ret)
       warning("failed to export alarm %s to registry, %s",
           acb->name, symerror(ret));
 
   if (pvcluster_addobj(&alchemy_alarm_table, acb->name, &acb->cobj)) {
       registry_destroy_file(&acb->fsobj);
       timerobj_destroy(&acb->tmobj);
       ret = -EEXIST;
       goto fail;
   }
 
   alarm->handle = (uintptr_t)acb;
 
   CANCEL_RESTORE(svc);
 
   return 0;
fail:
   pvfree(acb);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_alarm_delete(RT_ALARM *alarm)
 * @brief Delete an alarm.
 *
 * This routine deletes an alarm object previously created by a call
 * to rt_alarm_create().
 *
 * @param alarm The alarm descriptor.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a alarm is not a valid alarm descriptor.
 *
 * - -EPERM is returned if this service was called from an
 * asynchronous context.
 *
 * @apitags{mode-unrestricted, switch-secondary}
 */
#ifndef DOXYGEN_CPP
CURRENT_IMPL(int, rt_alarm_delete, (RT_ALARM *alarm))
#else
int rt_alarm_delete(RT_ALARM *alarm)
#endif
{
   struct alchemy_alarm *acb;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   acb = get_alchemy_alarm(alarm, &ret);
   if (acb == NULL)
       goto out;
 
   timerobj_destroy(&acb->tmobj);
   pvcluster_delobj(&alchemy_alarm_table, &acb->cobj);
   acb->magic = ~alarm_magic;
   registry_destroy_file(&acb->fsobj);
   pvfree(acb);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * Start an alarm.
 *
 * This routine programs the trigger date of an alarm object. An alarm
 * can be either periodic or oneshot, depending on the @a interval
 * value.
 *
 * Alarm handlers are always called on behalf of Xenomai's internal
 * timer event routine. Therefore, Xenomai routines which can be
 * called from such handlers are restricted to the set of services
 * available on behalf of an asynchronous context.
 *
 * This service overrides any previous setup of the expiry date and
 * reload interval for the alarm.
 *
 * @param alarm The alarm descriptor.
 *
 * @param value The relative date of the first expiry, expressed in
 * clock ticks (see note).
 *
 * @param interval The reload value of the alarm. It is a periodic
 * interval value to be used for reprogramming the next alarm shot,
 * expressed in clock ticks (see note). If @a interval is equal to
 * TM_INFINITE, the alarm will not be reloaded after it has expired.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a alarm is not a valid alarm descriptor.
 *
 * - -EPERM is returned if this service was called from an invalid
 * context.
 *
 * @apitags{xthread-only, switch-primary}
 *
 * @note Each of the initial @a value and @a interval is interpreted
 * as a multiple of the Alchemy clock resolution (see
 * --alchemy-clock-resolution option, defaults to 1 nanosecond).
 */
int rt_alarm_start(RT_ALARM *alarm, RTIME value, RTIME interval)
{
   struct alchemy_alarm *acb;
   struct itimerspec it;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   acb = get_alchemy_alarm(alarm, &ret);
   if (acb == NULL)
       goto out;
 
   clockobj_ticks_to_timeout(&alchemy_clock, value, &it.it_value);
   clockobj_ticks_to_timespec(&alchemy_clock, interval, &it.it_interval);
   acb->itmspec = it;
   ret = timerobj_start(&acb->tmobj, alarm_handler, &it);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_alarm_stop(RT_ALARM *alarm)
 * @brief Stop an alarm.
 *
 * This routine disables an alarm object, preventing any further
 * expiry until it is re-enabled via rt_alarm_start().
 *
 * @param alarm The alarm descriptor.
 *
 * @return Zero is returned upon success. Otherwise:
 *
 * - -EINVAL is returned if @a alarm is not a valid alarm descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
int rt_alarm_stop(RT_ALARM *alarm)
{
   struct alchemy_alarm *acb;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   acb = get_alchemy_alarm(alarm, &ret);
   if (acb == NULL)
       goto out;
 
   memset(&acb->itmspec, 0, sizeof(acb->itmspec));
   ret = timerobj_stop(&acb->tmobj);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/**
 * @fn int rt_alarm_inquire(RT_ALARM *alarm, RT_ALARM_INFO *info)
 * @brief Query alarm status.
 *
 * This routine returns the status information about the specified @a
 * alarm.
 *
 * @param alarm The alarm descriptor.
 *
 * @param info A pointer to the @ref RT_ALARM_INFO "return
 * buffer" to copy the information to.
 *
 * @return Zero is returned and status information is written to the
 * structure pointed at by @a info upon success. Otherwise:
 *
 * - -EINVAL is returned if @a alarm is not a valid alarm descriptor.
 *
 * @apitags{unrestricted, switch-primary}
 */
int rt_alarm_inquire(RT_ALARM *alarm, RT_ALARM_INFO *info)
{
   struct alchemy_alarm *acb;
   struct service svc;
   int ret = 0;
 
   CANCEL_DEFER(svc);
 
   acb = get_alchemy_alarm(alarm, &ret);
   if (acb == NULL)
       goto out;
 
   strcpy(info->name, acb->name);
   info->expiries = acb->expiries;
   info->active = !(alchemy_poll_mode(&acb->itmspec.it_value) &&
            alchemy_poll_mode(&acb->itmspec.it_interval));
 
   put_alchemy_alarm(acb);
out:
   CANCEL_RESTORE(svc);
 
   return ret;
}
 
/** @} */