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