1 /*
2 * libpri: An implementation of Primary Rate ISDN
3 *
4 * Written by Mark Spencer <markster@digium.com>
5 *
6 * Copyright (C) 2001-2005, Digium, Inc.
7 * All Rights Reserved.
8 */
9
10 /*
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
16 *
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2 as published by the
19 * Free Software Foundation. See the LICENSE file included with
20 * this program for more details.
21 *
22 * In addition, when this program is distributed with Asterisk in
23 * any form that would qualify as a 'combined work' or as a
24 * 'derivative work' (but not mere aggregation), you can redistribute
25 * and/or modify the combination under the terms of the license
26 * provided with that copy of Asterisk, instead of the license
27 * terms granted here.
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "libpri.h"
35 #include "pri_internal.h"
36
37
38 /*! Initial number of scheduled timer slots. */
39 #define SCHED_EVENTS_INITIAL 128
40 /*!
41 * \brief Maximum number of scheduled timer slots.
42 * \note Should be a power of 2 and at least SCHED_EVENTS_INITIAL.
43 */
44 #define SCHED_EVENTS_MAX 8192
45
46 /*! \brief The maximum number of timers that were active at once. */
47 static unsigned maxsched = 0;
48 /*! Last pool id */
49 static unsigned pool_id = 0;
50
51 /* Scheduler routines */
52
53 /*!
54 * \internal
55 * \brief Increase the number of scheduler timer slots available.
56 *
57 * \param ctrl D channel controller.
58 *
59 * \retval 0 on success.
60 * \retval -1 on error.
61 */
pri_schedule_grow(struct pri * ctrl)62 static int pri_schedule_grow(struct pri *ctrl)
63 {
64 unsigned num_slots;
65 struct pri_sched *timers;
66
67 /* Determine how many slots in the new timer table. */
68 if (ctrl->sched.num_slots) {
69 if (SCHED_EVENTS_MAX <= ctrl->sched.num_slots) {
70 /* Cannot grow the timer table any more. */
71 return -1;
72 }
73 num_slots = ctrl->sched.num_slots * 2;
74 if (SCHED_EVENTS_MAX < num_slots) {
75 num_slots = SCHED_EVENTS_MAX;
76 }
77 } else {
78 num_slots = SCHED_EVENTS_INITIAL;
79 }
80
81 /* Get and initialize the new timer table. */
82 timers = calloc(num_slots, sizeof(struct pri_sched));
83 if (!timers) {
84 /* Could not get a new timer table. */
85 return -1;
86 }
87 if (ctrl->sched.timer) {
88 /* Copy over the old timer table. */
89 memcpy(timers, ctrl->sched.timer,
90 ctrl->sched.num_slots * sizeof(struct pri_sched));
91 free(ctrl->sched.timer);
92 } else {
93 /* Creating the timer pool. */
94 pool_id += SCHED_EVENTS_MAX;
95 if (pool_id < SCHED_EVENTS_MAX
96 || pool_id + (SCHED_EVENTS_MAX - 1) < SCHED_EVENTS_MAX) {
97 /*
98 * Not likely to happen.
99 *
100 * Timer id's may be aliased if this D channel is used in an
101 * NFAS group with redundant D channels. Another D channel in
102 * the group may have the same pool_id.
103 */
104 pri_error(ctrl,
105 "Pool_id wrapped. Please ignore if you are not using NFAS with backup D channels.\n");
106 pool_id = SCHED_EVENTS_MAX;
107 }
108 ctrl->sched.first_id = pool_id;
109 }
110
111 /* Put the new timer table in place. */
112 ctrl->sched.timer = timers;
113 ctrl->sched.num_slots = num_slots;
114 return 0;
115 }
116
117 /*!
118 * \brief Start a timer to schedule an event.
119 *
120 * \param ctrl D channel controller.
121 * \param ms Number of milliseconds to scheduled event.
122 * \param function Callback function to call when timeout.
123 * \param data Value to give callback function when timeout.
124 *
125 * \retval 0 if scheduler table is full and could not schedule the event.
126 * \retval id Scheduled event id.
127 */
pri_schedule_event(struct pri * ctrl,int ms,void (* function)(void * data),void * data)128 unsigned pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), void *data)
129 {
130 unsigned max_used;
131 unsigned x;
132 struct timeval tv;
133
134 max_used = ctrl->sched.max_used;
135 for (x = 0; x < max_used; ++x) {
136 if (!ctrl->sched.timer[x].callback) {
137 break;
138 }
139 }
140 if (x == ctrl->sched.num_slots && pri_schedule_grow(ctrl)) {
141 pri_error(ctrl, "No more room in scheduler\n");
142 return 0;
143 }
144 if (ctrl->sched.max_used <= x) {
145 ctrl->sched.max_used = x + 1;
146 }
147 if (x >= maxsched) {
148 maxsched = x + 1;
149 }
150 gettimeofday(&tv, NULL);
151 tv.tv_sec += ms / 1000;
152 tv.tv_usec += (ms % 1000) * 1000;
153 if (tv.tv_usec > 1000000) {
154 tv.tv_usec -= 1000000;
155 tv.tv_sec += 1;
156 }
157 ctrl->sched.timer[x].when = tv;
158 ctrl->sched.timer[x].callback = function;
159 ctrl->sched.timer[x].data = data;
160 return ctrl->sched.first_id + x;
161 }
162
163 /*!
164 * \brief Determine the time of the next scheduled event to expire.
165 *
166 * \param ctrl D channel controller.
167 *
168 * \return Time of the next scheduled event to expire or NULL if no timers active.
169 */
pri_schedule_next(struct pri * ctrl)170 struct timeval *pri_schedule_next(struct pri *ctrl)
171 {
172 struct timeval *closest = NULL;
173 unsigned x;
174
175 /* Scan the scheduled timer slots backwards so we can update the max_used value. */
176 for (x = ctrl->sched.max_used; x--;) {
177 if (ctrl->sched.timer[x].callback) {
178 if (!closest) {
179 /* This is the highest sheduled timer slot in use. */
180 closest = &ctrl->sched.timer[x].when;
181 ctrl->sched.max_used = x + 1;
182 } else if ((closest->tv_sec > ctrl->sched.timer[x].when.tv_sec)
183 || ((closest->tv_sec == ctrl->sched.timer[x].when.tv_sec)
184 && (closest->tv_usec > ctrl->sched.timer[x].when.tv_usec))) {
185 closest = &ctrl->sched.timer[x].when;
186 }
187 }
188 }
189 if (!closest) {
190 /* No scheduled timer slots are active. */
191 ctrl->sched.max_used = 0;
192 }
193 return closest;
194 }
195
196 /*!
197 * \internal
198 * \brief Run all expired timers or return an event generated by an expired timer.
199 *
200 * \param ctrl D channel controller.
201 * \param tv Current time.
202 *
203 * \return Event for upper layer to process or NULL if all expired timers run.
204 */
__pri_schedule_run(struct pri * ctrl,struct timeval * tv)205 static pri_event *__pri_schedule_run(struct pri *ctrl, struct timeval *tv)
206 {
207 unsigned x;
208 unsigned max_used;
209 void (*callback)(void *);
210 void *data;
211
212 max_used = ctrl->sched.max_used;
213 for (x = 0; x < max_used; ++x) {
214 if (ctrl->sched.timer[x].callback
215 && ((ctrl->sched.timer[x].when.tv_sec < tv->tv_sec)
216 || ((ctrl->sched.timer[x].when.tv_sec == tv->tv_sec)
217 && (ctrl->sched.timer[x].when.tv_usec <= tv->tv_usec)))) {
218 /* This timer has expired. */
219 ctrl->schedev = 0;
220 callback = ctrl->sched.timer[x].callback;
221 data = ctrl->sched.timer[x].data;
222 ctrl->sched.timer[x].callback = NULL;
223 callback(data);
224 if (ctrl->schedev) {
225 return &ctrl->ev;
226 }
227 }
228 }
229 return NULL;
230 }
231
232 /*!
233 * \brief Run all expired timers or return an event generated by an expired timer.
234 *
235 * \param ctrl D channel controller.
236 *
237 * \return Event for upper layer to process or NULL if all expired timers run.
238 */
pri_schedule_run(struct pri * ctrl)239 pri_event *pri_schedule_run(struct pri *ctrl)
240 {
241 struct timeval tv;
242
243 gettimeofday(&tv, NULL);
244 return __pri_schedule_run(ctrl, &tv);
245 }
246
247 /*!
248 * \brief Delete a scheduled event.
249 *
250 * \param ctrl D channel controller.
251 * \param id Scheduled event id to delete.
252 * 0 is a disabled/unscheduled event id that is ignored.
253 *
254 * \return Nothing
255 */
pri_schedule_del(struct pri * ctrl,unsigned id)256 void pri_schedule_del(struct pri *ctrl, unsigned id)
257 {
258 struct pri *nfas;
259
260 if (!id) {
261 /* Disabled/unscheduled event id. */
262 return;
263 }
264 if (ctrl->sched.first_id <= id
265 && id <= ctrl->sched.first_id + (SCHED_EVENTS_MAX - 1)) {
266 ctrl->sched.timer[id - ctrl->sched.first_id].callback = NULL;
267 return;
268 }
269 if (ctrl->nfas) {
270 /* Try to find the timer on another D channel. */
271 for (nfas = PRI_NFAS_MASTER(ctrl); nfas; nfas = nfas->slave) {
272 if (nfas->sched.first_id <= id
273 && id <= nfas->sched.first_id + (SCHED_EVENTS_MAX - 1)) {
274 nfas->sched.timer[id - nfas->sched.first_id].callback = NULL;
275 return;
276 }
277 }
278 }
279 pri_error(ctrl,
280 "Asked to delete sched id 0x%08x??? first_id=0x%08x, num_slots=0x%08x\n", id,
281 ctrl->sched.first_id, ctrl->sched.num_slots);
282 }
283
284 /*!
285 * \brief Is the scheduled event this callback.
286 *
287 * \param ctrl D channel controller.
288 * \param id Scheduled event id to check.
289 * 0 is a disabled/unscheduled event id.
290 * \param function Callback function to call when timeout.
291 * \param data Value to give callback function when timeout.
292 *
293 * \return TRUE if scheduled event has the callback.
294 */
pri_schedule_check(struct pri * ctrl,unsigned id,void (* function)(void * data),void * data)295 int pri_schedule_check(struct pri *ctrl, unsigned id, void (*function)(void *data), void *data)
296 {
297 struct pri *nfas;
298
299 if (!id) {
300 /* Disabled/unscheduled event id. */
301 return 0;
302 }
303 if (ctrl->sched.first_id <= id
304 && id <= ctrl->sched.first_id + (SCHED_EVENTS_MAX - 1)) {
305 return ctrl->sched.timer[id - ctrl->sched.first_id].callback == function
306 && ctrl->sched.timer[id - ctrl->sched.first_id].data == data;
307 }
308 if (ctrl->nfas) {
309 /* Try to find the timer on another D channel. */
310 for (nfas = PRI_NFAS_MASTER(ctrl); nfas; nfas = nfas->slave) {
311 if (nfas->sched.first_id <= id
312 && id <= nfas->sched.first_id + (SCHED_EVENTS_MAX - 1)) {
313 return nfas->sched.timer[id - nfas->sched.first_id].callback == function
314 && nfas->sched.timer[id - nfas->sched.first_id].data == data;
315 }
316 }
317 }
318 pri_error(ctrl,
319 "Asked to check sched id 0x%08x??? first_id=0x%08x, num_slots=0x%08x\n", id,
320 ctrl->sched.first_id, ctrl->sched.num_slots);
321 return 0;
322 }
323