Merge changes from timing branch

- Convert chan_iax2 to use the timing API
 - Convert usage of timing in the core to use the timing API instead of
   using DAHDI directly
 - Make a change to the timing API to add the set_rate() function
 - change the timing core to use a rwlock
 - merge a timing implementation, res_timing_dahdi

Basic testing was successful using res_timing_dahdi


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@122523 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.1
Russell Bryant 17 years ago
parent a68a160b75
commit b6457ecf4c

@ -31,7 +31,6 @@
*/
/*** MODULEINFO
<use>dahdi</use>
<use>crypto</use>
***/
@ -55,7 +54,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/stat.h>
#include <regex.h>
#include "asterisk/dahdi.h"
#include "asterisk/paths.h" /* need ast_config_AST_DATA_DIR for firmware */
#include "asterisk/lock.h"
@ -89,6 +87,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/linkedlists.h"
#include "asterisk/event.h"
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "iax2.h"
#include "iax2-parser.h"
@ -7253,32 +7252,17 @@ static inline int iax2_trunk_expired(struct iax2_trunk_peer *tpeer, struct timev
static int timing_read(int *id, int fd, short events, void *cbdata)
{
char buf[1024];
int res, processed = 0, totalcalls = 0;
struct iax2_trunk_peer *tpeer = NULL, *drop = NULL;
#ifdef DAHDI_TIMERACK
int x = 1;
#endif
struct timeval now = ast_tvnow();
if (iaxtrunkdebug)
ast_verbose("Beginning trunk processing. Trunk queue ceiling is %d bytes per host\n", trunkmaxsize);
if (events & AST_IO_PRI) {
#ifdef DAHDI_TIMERACK
/* Great, this is a timing interface, just call the ioctl */
if (ioctl(fd, DAHDI_TIMERACK, &x)) {
ast_log(LOG_WARNING, "Unable to acknowledge DAHDI timer. IAX trunking will fail!\n");
usleep(1);
return -1;
}
#endif
} else {
/* Read and ignore from the pseudo channel for timing */
res = read(fd, buf, sizeof(buf));
if (res < 1) {
ast_log(LOG_WARNING, "Unable to read from timing fd\n");
return 1;
}
if (timingfd > -1) {
ast_timer_ack(timingfd, 1);
}
/* For each peer that supports trunking... */
AST_LIST_LOCK(&tpeers);
AST_LIST_TRAVERSE_SAFE_BEGIN(&tpeers, tpeer, list) {
@ -10844,21 +10828,6 @@ static void prune_peers(void)
}
}
static void set_timing(void)
{
#ifdef HAVE_DAHDI
int bs = trunkfreq * 8;
if (timingfd > -1) {
if (
#ifdef DAHDI_TIMERACK
ioctl(timingfd, DAHDI_TIMERCONFIG, &bs) &&
#endif
ioctl(timingfd, DAHDI_SET_BLOCKSIZE, &bs))
ast_log(LOG_WARNING, "Unable to set blocksize on timing source\n");
}
#endif
}
static void set_config_destroy(void)
{
strcpy(accountcode, "");
@ -11265,7 +11234,6 @@ static int set_config(char *config_file, int reload)
cat = ast_category_browse(cfg, cat);
}
ast_config_destroy(cfg);
set_timing();
return 1;
}
@ -12050,6 +12018,10 @@ static int __unload_module(void)
ao2_ref(users, -1);
ao2_ref(iax_peercallno_pvts, -1);
if (timingfd > -1) {
ast_timer_close(timingfd);
}
con = ast_context_find(regcontext);
if (con)
ast_context_destroy(con, "IAX2");
@ -12121,16 +12093,6 @@ static int load_module(void)
iax_set_error(iax_error_output);
jb_setoutput(jb_error_output, jb_warning_output, NULL);
#ifdef HAVE_DAHDI
#ifdef DAHDI_TIMERACK
timingfd = open("/dev/dahdi/timer", O_RDWR);
if (timingfd < 0)
#endif
timingfd = open("/dev/dahdi/pseudo", O_RDWR);
if (timingfd < 0)
ast_log(LOG_WARNING, "Unable to open IAX timing interface: %s\n", strerror(errno));
#endif
memset(iaxs, 0, sizeof(iaxs));
for (x = 0; x < ARRAY_LEN(iaxsl); x++) {
@ -12178,6 +12140,11 @@ static int load_module(void)
if(set_config(config, 0) == -1)
return AST_MODULE_LOAD_DECLINE;
timingfd = ast_timer_open();
if (timingfd > -1) {
ast_timer_set_rate(timingfd, trunkfreq);
}
if (ast_channel_register(&iax2_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class %s\n", "IAX2");
__unload_module();

@ -1423,9 +1423,17 @@ int ast_autoservice_start(struct ast_channel *chan);
*/
int ast_autoservice_stop(struct ast_channel *chan);
/* If built with dahdi optimizations, force a scheduled expiration on the
timer fd, at which point we call the callback function / data */
int ast_settimeout(struct ast_channel *c, int samples, int (*func)(const void *data), void *data);
/*!
* \brief Enable or disable timer ticks for a channel
*
* \arg rate number of timer ticks per second
*
* If timers are supported, force a scheduled expiration on the
* timer fd, at which point we call the callback function / data
*
* Call this function with a rate of 0 to turn off the timer ticks
*/
int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data);
/*! \brief Transfer a channel (if supported). Returns -1 on error, 0 if not supported
and 1 if supported and requested

@ -65,8 +65,9 @@ enum ast_timing_event {
* public API calls.
*/
struct ast_timing_functions {
int (*timer_open)(unsigned int rate);
int (*timer_open)(void);
void (*timer_close)(int handle);
int (*timer_set_rate)(int handle, unsigned int rate);
void (*timer_ack)(int handle, unsigned int quantity);
int (*timer_enable_continuous)(int handle);
int (*timer_disable_continuous)(int handle);
@ -97,12 +98,10 @@ void ast_uninstall_timing_functions(void *handle);
/*!
* \brief Open a timing fd
*
* \arg rate number of timer ticks per second
*
* \retval -1 error, with errno set
* \retval >=0 success
*/
int ast_timer_open(unsigned int rate);
int ast_timer_open(void);
/*!
* \brief Close an opened timing handle
@ -113,6 +112,21 @@ int ast_timer_open(unsigned int rate);
*/
void ast_timer_close(int handle);
/*!
* \brief Set the timing tick rate
*
* \arg handle timing fd returned from timer_open()
* \arg rate ticks per second, 0 turns the ticks off if needed
*
* Use this function if you want the timing fd to show input at a certain
* rate. The other alternative use of a timing fd, is using the continuous
* mode.
*
* \retval -1 error, with errno set
* \retval 0 success
*/
int ast_timer_set_rate(int handle, unsigned int rate);
/*!
* \brief Acknowledge a timer event
*

@ -3299,35 +3299,7 @@ int main(int argc, char *argv[])
printf("%s", term_quit());
exit(1);
}
#ifdef HAVE_DAHDI
{
int fd;
int x = 160;
fd = open("/dev/dahdi/timer", O_RDWR);
if (fd >= 0) {
if (ioctl(fd, DAHDI_TIMERCONFIG, &x)) {
ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer test failed to set DAHDI_TIMERCONFIG to %d.\n", x);
exit(1);
}
if ((x = ast_wait_for_input(fd, 300)) < 0) {
ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer could not be polled during the DAHDI timer test.\n");
exit(1);
}
if (!x) {
const char dahdi_timer_error[] = {
"Asterisk has detected a problem with your DAHDI configuration and will shutdown for your protection. You have options:"
"\n\t1. You only have to compile DAHDI support into Asterisk if you need it. One option is to recompile without DAHDI support."
"\n\t2. You only have to load DAHDI drivers if you want to take advantage of DAHDI services. One option is to unload DAHDI modules if you don't need them."
"\n\t3. If you need DAHDI services, you must correctly configure DAHDI."
};
ast_log(LOG_ERROR, "%s\n", dahdi_timer_error);
usleep(100);
exit(1);
}
close(fd);
}
}
#endif
threadstorage_init();
astobj2_init();

@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/threadstorage.h"
#include "asterisk/slinfactory.h"
#include "asterisk/audiohook.h"
#include "asterisk/timing.h"
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
@ -808,27 +809,19 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_
#endif
}
#ifdef HAVE_DAHDI
tmp->timingfd = open("/dev/dahdi/timer", O_RDWR);
tmp->timingfd = ast_timer_open();
if (tmp->timingfd > -1) {
/* Check if timing interface supports new
ping/pong scheme */
flags = 1;
if (!ioctl(tmp->timingfd, DAHDI_TIMERPONG, &flags))
needqueue = 0;
needqueue = 0;
}
#else
tmp->timingfd = -1;
#endif
if (needqueue) {
if (pipe(tmp->alertpipe)) {
ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe!\n");
alertpipe_failed:
#ifdef HAVE_DAHDI
if (tmp->timingfd > -1)
close(tmp->timingfd);
#endif
if (tmp->timingfd > -1) {
ast_timer_close(tmp->timingfd);
}
sched_context_destroy(tmp->sched);
ast_string_field_free_memory(tmp);
ast_free(tmp);
@ -1007,10 +1000,8 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
chan->name, f->frametype, f->subclass, qlen, strerror(errno));
#ifdef HAVE_DAHDI
} else if (chan->timingfd > -1) {
ioctl(chan->timingfd, DAHDI_TIMERPING, &blah);
#endif
ast_timer_enable_continuous(chan->timingfd);
} else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
pthread_kill(chan->blocker, SIGURG);
}
@ -1343,7 +1334,7 @@ void ast_channel_free(struct ast_channel *chan)
if ((fd = chan->alertpipe[1]) > -1)
close(fd);
if ((fd = chan->timingfd) > -1)
close(fd);
ast_timer_close(fd);
#ifdef HAVE_EPOLL
for (i = 0; i < AST_MAX_FDS; i++) {
if (chan->epfd_data[i])
@ -1795,7 +1786,7 @@ int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen,
}
if (!res) {
ast_settimeout(chan, 160, generator_force, chan);
ast_settimeout(chan, 50, generator_force, chan);
chan->generator = gen;
}
@ -2181,21 +2172,26 @@ int ast_waitfordigit(struct ast_channel *c, int ms)
return ast_waitfordigit_full(c, ms, -1, -1);
}
int ast_settimeout(struct ast_channel *c, int samples, int (*func)(const void *data), void *data)
int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const void *data), void *data)
{
int res = -1;
#ifdef HAVE_DAHDI
if (c->timingfd > -1) {
if (!func) {
samples = 0;
data = 0;
}
ast_debug(1, "Scheduling timer at %d sample intervals\n", samples);
res = ioctl(c->timingfd, DAHDI_TIMERCONFIG, &samples);
c->timingfunc = func;
c->timingdata = data;
int res;
if (c->timingfd == -1) {
return -1;
}
#endif
if (!func) {
rate = 0;
data = NULL;
}
ast_debug(1, "Scheduling timer at %u timer ticks per second\n", rate);
res = ast_timer_set_rate(c->timingfd, rate);
c->timingfunc = func;
c->timingdata = data;
return res;
}
@ -2334,7 +2330,7 @@ static void ast_read_generator_actions(struct ast_channel *chan, struct ast_fram
} else if (f->frametype == AST_FRAME_CNG) {
if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
ast_debug(1, "Generator got CNG, switching to timed mode\n");
ast_settimeout(chan, 160, generator_force, chan);
ast_settimeout(chan, 50, generator_force, chan);
}
}
}

@ -29,6 +29,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <dirent.h>
#include <sys/stat.h>
#include <math.h>
#include "asterisk/_private.h" /* declare ast_file_init() */
#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
@ -659,21 +660,17 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
}
}
if (whennext != s->lasttimeout) {
#ifdef HAVE_DAHDI
if (s->owner->timingfd > -1) {
int zap_timer_samples = whennext;
int rate;
/* whennext is in samples, but DAHDI timers operate in 8 kHz samples. */
if ((rate = ast_format_rate(s->fmt->format)) != 8000) {
float factor;
factor = ((float) rate) / ((float) 8000.0);
zap_timer_samples = (int) ( ((float) zap_timer_samples) / factor );
}
ast_settimeout(s->owner, zap_timer_samples, ast_fsread_audio, s);
} else
#endif
float samp_rate = (float) ast_format_rate(s->fmt->format);
unsigned int rate;
rate = (unsigned int) roundf(samp_rate / ((float) whennext));
ast_settimeout(s->owner, rate, ast_fsread_audio, s);
} else {
s->owner->streamid = ast_sched_add(s->owner->sched,
whennext / (ast_format_rate(s->fmt->format) / 1000), ast_fsread_audio, s);
}
s->lasttimeout = whennext;
return FSREAD_SUCCESS_NOSCHED;
}
@ -681,9 +678,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
return_failure:
s->owner->streamid = -1;
#ifdef HAVE_DAHDI
ast_settimeout(s->owner, 0, NULL, NULL);
#endif
return FSREAD_FAILURE;
}
@ -792,9 +787,7 @@ int ast_closestream(struct ast_filestream *f)
if (f->fmt->format & AST_FORMAT_AUDIO_MASK) {
f->owner->stream = NULL;
AST_SCHED_DEL(f->owner->sched, f->owner->streamid);
#ifdef HAVE_DAHDI
ast_settimeout(f->owner, 0, NULL, NULL);
#endif
} else {
f->owner->vstream = NULL;
AST_SCHED_DEL(f->owner->sched, f->owner->vstreamid);

@ -30,7 +30,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/timing.h"
#include "asterisk/lock.h"
AST_MUTEX_DEFINE_STATIC(lock);
AST_RWLOCK_DEFINE_STATIC(lock);
static struct ast_timing_functions timer_funcs;
@ -38,6 +38,7 @@ void *ast_install_timing_functions(struct ast_timing_functions *funcs)
{
if (!funcs->timer_open ||
!funcs->timer_close ||
!funcs->timer_set_rate ||
!funcs->timer_ack ||
!funcs->timer_get_event ||
!funcs->timer_enable_continuous ||
@ -45,94 +46,113 @@ void *ast_install_timing_functions(struct ast_timing_functions *funcs)
return NULL;
}
ast_mutex_lock(&lock);
ast_rwlock_wrlock(&lock);
if (timer_funcs.timer_open) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
ast_log(LOG_NOTICE, "Multiple timing modules are loaded. You should only load one.\n");
return NULL;
}
timer_funcs = *funcs;
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return &timer_funcs;
}
void ast_uninstall_timing_functions(void *handle)
{
ast_mutex_lock(&lock);
ast_rwlock_wrlock(&lock);
if (handle != &timer_funcs) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return;
}
memset(&timer_funcs, 0, sizeof(timer_funcs));
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
}
int ast_timer_open(unsigned int rate)
int ast_timer_open(void)
{
int timer;
ast_mutex_lock(&lock);
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_open) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return -1;
}
timer = timer_funcs.timer_open(rate);
timer = timer_funcs.timer_open();
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return timer;
}
void ast_timer_close(int timer)
{
ast_mutex_lock(&lock);
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_close) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return;
}
timer_funcs.timer_close(timer);
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
}
int ast_timer_set_rate(int handle, unsigned int rate)
{
int res;
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_set_rate) {
ast_rwlock_unlock(&lock);
return -1;
}
res = timer_funcs.timer_set_rate(handle, rate);
ast_rwlock_unlock(&lock);
return res;
}
void ast_timer_ack(int handle, unsigned int quantity)
{
ast_mutex_lock(&lock);
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_ack) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return;
}
timer_funcs.timer_ack(handle, quantity);
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
}
int ast_timer_enable_continuous(int handle)
{
int result;
ast_mutex_lock(&lock);
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_enable_continuous) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return -1;
}
result = timer_funcs.timer_enable_continuous(handle);
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return result;
}
@ -141,16 +161,16 @@ int ast_timer_disable_continous(int handle)
{
int result;
ast_mutex_lock(&lock);
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_disable_continuous) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return -1;
}
result = timer_funcs.timer_disable_continuous(handle);
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return result;
}
@ -159,16 +179,16 @@ enum ast_timing_event ast_timer_get_event(int handle)
{
enum ast_timing_event result;
ast_mutex_lock(&lock);
ast_rwlock_rdlock(&lock);
if (!timer_funcs.timer_get_event) {
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return -1;
}
result = timer_funcs.timer_get_event(handle);
ast_mutex_unlock(&lock);
ast_rwlock_unlock(&lock);
return result;
}

@ -0,0 +1,188 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008, Digium, Inc.
*
* Russell Bryant <russell@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \author Russell Bryant <russell@digium.com>
*
* \brief DAHDI timing interface
*/
/*** MODULEINFO
<depend>dahdi</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include "asterisk/module.h"
#include "asterisk/timing.h"
#include "asterisk/dahdi.h"
#include "asterisk/utils.h"
static void *timing_funcs_handle;
static int dahdi_timer_open(void);
static void dahdi_timer_close(int handle);
static int dahdi_timer_set_rate(int handle, unsigned int rate);
static void dahdi_timer_ack(int handle, unsigned int quantity);
static int dahdi_timer_enable_continuous(int handle);
static int dahdi_timer_disable_continuous(int handle);
static enum ast_timing_event dahdi_timer_get_event(int handle);
static struct ast_timing_functions dahdi_timing_functions = {
.timer_open = dahdi_timer_open,
.timer_close = dahdi_timer_close,
.timer_set_rate = dahdi_timer_set_rate,
.timer_ack = dahdi_timer_ack,
.timer_enable_continuous = dahdi_timer_enable_continuous,
.timer_disable_continuous = dahdi_timer_disable_continuous,
.timer_get_event = dahdi_timer_get_event,
};
static int dahdi_timer_open(void)
{
return open("/dev/dahdi/timer", O_RDWR);
}
static void dahdi_timer_close(int handle)
{
close(handle);
}
static int dahdi_timer_set_rate(int handle, unsigned int rate)
{
int samples;
/* DAHDI timers are configured using a number of samples,
* based on an 8 kHz sample rate. */
samples = (unsigned int) roundf((8000.0 / ((float) rate)));
if (ioctl(handle, DAHDI_TIMERCONFIG, &samples)) {
ast_log(LOG_ERROR, "Failed to configure DAHDI timing fd for %u sample timer ticks\n",
samples);
return -1;
}
return 0;
}
static void dahdi_timer_ack(int handle, unsigned int quantity)
{
ioctl(handle, DAHDI_TIMERACK, &quantity);
}
static int dahdi_timer_enable_continuous(int handle)
{
int flags = 1;
return ioctl(handle, DAHDI_TIMERPING, &flags) ? -1 : 0;
}
static int dahdi_timer_disable_continuous(int handle)
{
int flags = -1;
return ioctl(handle, DAHDI_TIMERPONG, &flags) ? -1 : 0;
}
static enum ast_timing_event dahdi_timer_get_event(int handle)
{
int res;
int event;
res = ioctl(handle, DAHDI_GETEVENT, &event);
if (res) {
event = DAHDI_EVENT_TIMER_EXPIRED;
}
switch (event) {
case DAHDI_EVENT_TIMER_PING:
return AST_TIMING_EVENT_CONTINUOUS;
case DAHDI_EVENT_TIMER_EXPIRED:
default:
return AST_TIMING_EVENT_EXPIRED;
}
}
static int dahdi_test_timer(void)
{
int fd;
int x = 160;
fd = open("/dev/dahdi/timer", O_RDWR);
if (fd < 0) {
return -1;
}
if (ioctl(fd, DAHDI_TIMERCONFIG, &x)) {
ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer test failed to set DAHDI_TIMERCONFIG to %d.\n", x);
close(fd);
return -1;
}
if ((x = ast_wait_for_input(fd, 300)) < 0) {
ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer could not be polled during the DAHDI timer test.\n");
close(fd);
return -1;
}
if (!x) {
const char dahdi_timer_error[] = {
"Asterisk has detected a problem with your DAHDI configuration and will shutdown for your protection. You have options:"
"\n\t1. You only have to compile DAHDI support into Asterisk if you need it. One option is to recompile without DAHDI support."
"\n\t2. You only have to load DAHDI drivers if you want to take advantage of DAHDI services. One option is to unload DAHDI modules if you don't need them."
"\n\t3. If you need DAHDI services, you must correctly configure DAHDI."
};
ast_log(LOG_ERROR, "%s\n", dahdi_timer_error);
usleep(100);
close(fd);
return -1;
}
close(fd);
return 0;
}
static int load_module(void)
{
if (dahdi_test_timer()) {
return AST_MODULE_LOAD_DECLINE;
}
return (timing_funcs_handle = ast_install_timing_functions(&dahdi_timing_functions)) ?
AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE;
}
static int unload_module(void)
{
ast_uninstall_timing_functions(timing_funcs_handle);
return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DAHDI Timing Interface");
Loading…
Cancel
Save