mirror of https://github.com/asterisk/asterisk
Add smdi support for asterisk (see doc/smdi.txt for config info) (#5945)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@9423 65c4cc65-6c06-0410-ace0-fbb531ad65f31.4
parent
cadfcdfe8e
commit
af07dc8883
@ -0,0 +1,43 @@
|
||||
; Asterisk SMDI configuration
|
||||
|
||||
[interfaces]
|
||||
; Specify serial ports to listen for SMDI messages on below. These will be
|
||||
; referenced later in zapata.conf. If you do not specify any interfaces then
|
||||
; SMDI will be disabled. Interfaces can have several different attributes
|
||||
; associated with them.
|
||||
|
||||
; Set the number of stop bits to use per character here. The default is no,
|
||||
; in which case one stop bit will be used.
|
||||
|
||||
;twostopbits = no
|
||||
|
||||
; Character size or bit length is the size of each character sent accross the
|
||||
; link. Character size can be 7 or 8. The default is 7.
|
||||
|
||||
;charsize = 7
|
||||
|
||||
; If you need parity checking enabled you can turn it on here. Acceptable
|
||||
; values are even, odd, and none. The default is even.
|
||||
|
||||
;paritybit = even
|
||||
|
||||
; The baudrate to use for this port. Acceptable values are 1200, 2400, 4800,
|
||||
; and 9600. The default is 9600.
|
||||
|
||||
;baudrate = 1200
|
||||
|
||||
; Often the numbering scheme for a set of mailboxes or extensions will not be 7
|
||||
; or 10 digits (as SMDI requires). Use the msdstrip option to strip unused
|
||||
; digits from the start of numbers.
|
||||
|
||||
;msdstrip = 0
|
||||
|
||||
; Occasionally Asterisk and the SMDI switch may become out of sync. If this
|
||||
; happens, Asterisk will appear one or several calls behind as it processes
|
||||
; voicemail requests. To prevent this from hapening adjust the msgexpirytime.
|
||||
; This will make Asterisk discard old SMDI messages that have not yet been
|
||||
; processed. The default expiry time is 30000 milliseconds.
|
||||
|
||||
;msgexpirytime = 30000
|
||||
|
||||
;smdiport => /dev/ttyS0
|
@ -0,0 +1,34 @@
|
||||
Asterisk SMDI (Simple Message Desk Interface) integration
|
||||
---------------------------------------------------------
|
||||
|
||||
Support for using Asterisk as an SMDI message desk was developed by Matthew A.
|
||||
Nicholson <mnicholson@digium.com>. To enable SMDI support in asterisk edit the
|
||||
Makefile file and uncomment the line regarding SMDI so it reads like
|
||||
this:
|
||||
|
||||
#
|
||||
# Asterisk SMDI integration
|
||||
#
|
||||
WITH_SMDI = 1
|
||||
|
||||
SMDI integration is configured in smdi.conf, zaptel.conf, and voicemail.conf.
|
||||
Various characteristics of the SMDI interfaces to be used (serial ports) are
|
||||
defined in smdi.conf. SMDI integration for callerid and MWI are defined in
|
||||
zaptel.conf and voicemail.conf respectively.
|
||||
|
||||
When SMDI is enabled and a call comes into Asterisk, the forwarding station
|
||||
number is used as the destination for the call and any callerid information
|
||||
present is used. This way you can configure your extensions.conf as follows to
|
||||
behave as a message desk.
|
||||
|
||||
[default]
|
||||
|
||||
exten => _XXXXXXX,1,VoiceMail(${EXTEN}|${SMDI_VM_TYPE})
|
||||
exten => _XXXXXXX,n,Hangup
|
||||
|
||||
exten => s,1,VoiceMailMain(${CALLERIDNUM})
|
||||
exten => s,n,Hangup
|
||||
|
||||
The ${SMDI_VM_TYPE} variable will be set to u, b, or nothing depending on the
|
||||
contents of the type of SMDI message received.
|
||||
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* SMDI support for Asterisk.
|
||||
*
|
||||
* Copyright (C) 2005, Digium, Inc.
|
||||
*
|
||||
* Matthew A. Nicholson <mnicholson@digium.com>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief SMDI support for Asterisk.
|
||||
* \author Matthew A. Nicholson <mnicholson@digium.com>
|
||||
*/
|
||||
|
||||
|
||||
/* C is simply a ego booster for those who want to do objects the hard way. */
|
||||
|
||||
|
||||
#ifndef AST_SMDI_H
|
||||
#define AST_SMDI_H
|
||||
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/astobj.h"
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
|
||||
#define SMDI_MESG_DESK_NUM_LEN 3
|
||||
#define SMDI_MESG_DESK_TERM_LEN 4
|
||||
#define SMDI_MWI_FAIL_CAUSE_LEN 3
|
||||
#define SMDI_MAX_STATION_NUM_LEN 10
|
||||
#define SMDI_MAX_FILENAME_LEN 256
|
||||
|
||||
/*!
|
||||
* \brief An SMDI message waiting indicator message.
|
||||
*
|
||||
* The ast_smdi_mwi_message structure contains the parsed out parts of an smdi
|
||||
* message. Each ast_smdi_interface structure has a message queue consisting
|
||||
* ast_smdi_mwi_message structures.
|
||||
*/
|
||||
struct ast_smdi_mwi_message {
|
||||
ASTOBJ_COMPONENTS(struct ast_smdi_mwi_message);
|
||||
char fwd_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* forwarding station number */
|
||||
char cause[SMDI_MWI_FAIL_CAUSE_LEN + 1]; /* the type of failure */
|
||||
struct timeval timestamp; /* a timestamp for the message */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief An SMDI message desk message.
|
||||
*
|
||||
* The ast_smdi_md_message structure contains the parsed out parts of an smdi
|
||||
* message. Each ast_smdi_interface structure has a message queue consisting
|
||||
* ast_smdi_md_message structures.
|
||||
*/
|
||||
struct ast_smdi_md_message {
|
||||
ASTOBJ_COMPONENTS(struct ast_smdi_md_message);
|
||||
char mesg_desk_num[SMDI_MESG_DESK_NUM_LEN + 1]; /* message desk number */
|
||||
char mesg_desk_term[SMDI_MESG_DESK_TERM_LEN + 1]; /* message desk terminal */
|
||||
char fwd_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* forwarding station number */
|
||||
char calling_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* calling station number */
|
||||
char type; /* the type of the call */
|
||||
struct timeval timestamp; /* a timestamp for the message */
|
||||
};
|
||||
|
||||
/*! \brief SMDI message desk message queue. */
|
||||
struct ast_smdi_md_queue {
|
||||
ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
|
||||
};
|
||||
|
||||
/*! \brief SMDI message waiting indicator message queue. */
|
||||
struct ast_smdi_mwi_queue {
|
||||
ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief SMDI interface structure.
|
||||
*
|
||||
* The ast_smdi_interface structure holds information on a serial port that
|
||||
* should be monitored for SMDI activity. The structure contains a message
|
||||
* queue of messages that have been recieved on the interface.
|
||||
*/
|
||||
struct ast_smdi_interface {
|
||||
ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
|
||||
struct ast_smdi_md_queue md_q;
|
||||
struct ast_smdi_mwi_queue mwi_q;
|
||||
FILE *file;
|
||||
int fd;
|
||||
pthread_t thread;
|
||||
struct termios mode;
|
||||
int msdstrip;
|
||||
long msg_expiry;
|
||||
};
|
||||
|
||||
|
||||
/* MD message queue functions */
|
||||
extern struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface);
|
||||
extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout);
|
||||
extern void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
|
||||
|
||||
/* MWI message queue functions */
|
||||
extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface);
|
||||
extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout);
|
||||
extern void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
|
||||
|
||||
extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name);
|
||||
|
||||
/* MWI functions */
|
||||
extern int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox);
|
||||
extern int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox);
|
||||
|
||||
extern void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg);
|
||||
extern void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg);
|
||||
|
||||
extern void ast_smdi_interface_destroy(struct ast_smdi_interface *iface);
|
||||
#endif
|
@ -0,0 +1,794 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* SMDI support for Asterisk.
|
||||
*
|
||||
* Copyright (C) 2005, Digium, Inc.
|
||||
*
|
||||
* Matthew A. Nicholson <mnicholson@digium.com>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/smdi.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/astobj.h"
|
||||
#include "asterisk/io.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief SMDI support for Asterisk.
|
||||
* \author Matthew A. Nicholson <mnicholson@digium.com>
|
||||
*/
|
||||
|
||||
/* Message expiry time in milliseconds */
|
||||
#define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */
|
||||
|
||||
static const char tdesc[] = "Asterisk Simplified Message Desk Interface (SMDI) Module";
|
||||
static const char config_file[] = "smdi.conf";
|
||||
|
||||
static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
|
||||
static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
|
||||
|
||||
static void *smdi_read(void *iface_p);
|
||||
static int smdi_load(int reload);
|
||||
|
||||
/* Use count stuff */
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(localuser_lock);
|
||||
static int localusecnt = 0;
|
||||
|
||||
/*! \brief SMDI interface container. */
|
||||
struct ast_smdi_interface_container {
|
||||
ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
|
||||
} smdi_ifaces;
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Push an SMDI message to the back of an interface's message queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
* \param md_msg a pointer to the message to use.
|
||||
*/
|
||||
static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
|
||||
{
|
||||
ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Push an SMDI message to the back of an interface's message queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
* \param mwi_msg a pointer to the message to use.
|
||||
*/
|
||||
static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
|
||||
{
|
||||
ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Set the MWI indicator for a mailbox.
|
||||
* \param iface the interface to use.
|
||||
* \param mailbox the mailbox to use.
|
||||
*/
|
||||
extern int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
|
||||
{
|
||||
FILE *file;
|
||||
int i;
|
||||
|
||||
file = fopen(iface->name, "w");
|
||||
if(!file) {
|
||||
ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
ASTOBJ_WRLOCK(iface);
|
||||
|
||||
fprintf(file, "OP:MWI ");
|
||||
|
||||
for(i = 0; i < iface->msdstrip; i++)
|
||||
fprintf(file, "0");
|
||||
|
||||
fprintf(file, "%s!\x04", mailbox);
|
||||
fclose(file);
|
||||
|
||||
ASTOBJ_UNLOCK(iface);
|
||||
ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Unset the MWI indicator for a mailbox.
|
||||
* \param iface the interface to use.
|
||||
* \param mailbox the mailbox to use.
|
||||
*/
|
||||
extern int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
|
||||
{
|
||||
FILE *file;
|
||||
int i;
|
||||
|
||||
file = fopen(iface->name, "w");
|
||||
if(!file) {
|
||||
ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
ASTOBJ_WRLOCK(iface);
|
||||
|
||||
fprintf(file, "RMV:MWI ");
|
||||
|
||||
for(i = 0; i < iface->msdstrip; i++)
|
||||
fprintf(file, "0");
|
||||
|
||||
fprintf(file, "%s!\x04", mailbox);
|
||||
fclose(file);
|
||||
|
||||
ASTOBJ_UNLOCK(iface);
|
||||
ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s", mailbox, iface->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Put an SMDI message back in the front of the queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
* \param msg a pointer to the message to use.
|
||||
*
|
||||
* This function puts a message back in the front of the specified queue. It
|
||||
* should be used if a message was popped but is not going to be processed for
|
||||
* some reason, and the message needs to be returned to the queue.
|
||||
*/
|
||||
extern void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
|
||||
{
|
||||
ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Put an SMDI message back in the front of the queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
* \param msg a pointer to the message to use.
|
||||
*
|
||||
* This function puts a message back in the front of the specified queue. It
|
||||
* should be used if a message was popped but is not going to be processed for
|
||||
* some reason, and the message needs to be returned to the queue.
|
||||
*/
|
||||
extern void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
|
||||
{
|
||||
ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the next SMDI message from the queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
*
|
||||
* This function pulls the first unexpired message from the SMDI message queue
|
||||
* on the specified interface. It will purge all expired SMDI messages before
|
||||
* returning.
|
||||
*
|
||||
* \return the next SMDI message, or NULL if there were no pending messages.
|
||||
*/
|
||||
extern struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
|
||||
{
|
||||
struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
|
||||
struct timeval now;
|
||||
long elapsed = 0;
|
||||
|
||||
/* purge old messages */
|
||||
gettimeofday(&now, NULL);
|
||||
while(md_msg)
|
||||
{
|
||||
/* calculate the elapsed time since this message was recieved ( in milliseconds) */
|
||||
elapsed = (now.tv_sec - md_msg->timestamp.tv_sec) * 1000;
|
||||
elapsed += (now.tv_usec - md_msg->timestamp.tv_usec) / 1000;
|
||||
|
||||
if(elapsed > iface->msg_expiry)
|
||||
{ /* found an expired message */
|
||||
ASTOBJ_UNREF(md_msg,ast_smdi_md_message_destroy);
|
||||
ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue. Message was %ld milliseconds too old.", iface->name, elapsed - iface->msg_expiry);
|
||||
md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
|
||||
}
|
||||
else /* good message, return it */
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return md_msg;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the next SMDI message from the queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
* \param timeout the time to wait before returning in milliseconds.
|
||||
*
|
||||
* This function pulls a message from the SMDI message queue on the specified
|
||||
* interface. If no message is available this function will wait the specified
|
||||
* amount of time before returning.
|
||||
*
|
||||
* \return the next SMDI message, or NULL if there were no pending messages and
|
||||
* the timeout has expired.
|
||||
*/
|
||||
extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
|
||||
{
|
||||
struct timeval start, end;
|
||||
long diff = 0;
|
||||
struct ast_smdi_md_message *msg;
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
while(diff < timeout) {
|
||||
|
||||
if((msg = ast_smdi_md_message_pop(iface)))
|
||||
return msg;
|
||||
|
||||
/* check timeout */
|
||||
gettimeofday(&end, NULL);
|
||||
diff = (end.tv_sec - start.tv_sec) * 1000;
|
||||
diff += (end.tv_usec - start.tv_usec) / 1000;
|
||||
}
|
||||
return (ast_smdi_md_message_pop(iface));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the next SMDI message from the queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
*
|
||||
* This function pulls the first unexpired message from the SMDI message queue
|
||||
* on the specified interface. It will purge all expired SMDI messages before
|
||||
* returning.
|
||||
*
|
||||
* \return the next SMDI message, or NULL if there were no pending messages.
|
||||
*/
|
||||
extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
|
||||
{
|
||||
struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
|
||||
struct timeval now;
|
||||
long elapsed = 0;
|
||||
|
||||
/* purge old messages */
|
||||
gettimeofday(&now, NULL);
|
||||
while(mwi_msg)
|
||||
{
|
||||
/* calculate the elapsed time since this message was recieved ( in milliseconds) */
|
||||
elapsed = (now.tv_sec - mwi_msg->timestamp.tv_sec) * 1000;
|
||||
elapsed += (now.tv_usec - mwi_msg->timestamp.tv_usec) / 1000;
|
||||
|
||||
if(elapsed > iface->msg_expiry)
|
||||
{ /* found an expired message */
|
||||
ASTOBJ_UNREF(mwi_msg,ast_smdi_mwi_message_destroy);
|
||||
ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue. Message was %ld milliseconds too old.", iface->name, elapsed - iface->msg_expiry);
|
||||
mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
|
||||
}
|
||||
else /* good message, return it */
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mwi_msg;
|
||||
return (ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the next SMDI message from the queue.
|
||||
* \param iface a pointer to the interface to use.
|
||||
* \param timeout the time to wait before returning in milliseconds.
|
||||
*
|
||||
* This function pulls a message from the SMDI message queue on the specified
|
||||
* interface. If no message is available this function will wait the specified
|
||||
* amount of time before returning.
|
||||
*
|
||||
* \return the next SMDI message, or NULL if there were no pending messages and
|
||||
* the timeout has expired.
|
||||
*/
|
||||
extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
|
||||
{
|
||||
struct timeval start, end;
|
||||
long diff = 0;
|
||||
struct ast_smdi_mwi_message *msg;
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
while(diff < timeout) {
|
||||
|
||||
if((msg = ast_smdi_mwi_message_pop(iface)))
|
||||
return msg;
|
||||
|
||||
/* check timeout */
|
||||
gettimeofday(&end, NULL);
|
||||
diff = (end.tv_sec - start.tv_sec) * 1000;
|
||||
diff += (end.tv_usec - start.tv_usec) / 1000;
|
||||
}
|
||||
return (ast_smdi_mwi_message_pop(iface));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Find an SMDI interface with the specified name.
|
||||
* \param iface_name the name/port of the interface to search for.
|
||||
*
|
||||
* \return a pointer to the interface located or NULL if none was found. This
|
||||
* actually returns an ASTOBJ reference and should be released using
|
||||
* #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
|
||||
*/
|
||||
extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
|
||||
{
|
||||
return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces,iface_name));
|
||||
}
|
||||
|
||||
/*! \brief Read an SMDI message.
|
||||
*
|
||||
* \param iface the SMDI interface to read from.
|
||||
*
|
||||
* This function loops and reads from and SMDI interface. It must be stopped
|
||||
* using pthread_cancel().
|
||||
*/
|
||||
static void *smdi_read(void *iface_p)
|
||||
{
|
||||
struct ast_smdi_interface *iface = iface_p;
|
||||
struct ast_smdi_md_message *md_msg;
|
||||
struct ast_smdi_mwi_message *mwi_msg;
|
||||
char c = '\0';
|
||||
char *cp = NULL;
|
||||
int i;
|
||||
int start = 0;
|
||||
|
||||
/* read an smdi message */
|
||||
while((c = fgetc(iface->file))) {
|
||||
|
||||
/* check if this is the start of a message */
|
||||
if(!start)
|
||||
{
|
||||
if(c == 'M')
|
||||
start = 1;
|
||||
}
|
||||
else /* Determine if this is a MD or MWI message */
|
||||
{
|
||||
if(c == 'D') { /* MD message */
|
||||
start = 0;
|
||||
|
||||
md_msg = malloc(sizeof(struct ast_smdi_md_message));
|
||||
if(!md_msg) {
|
||||
ast_log(LOG_ERROR, "Error allocating memory for ast_smdi_md_message. Stopping listner thread.\n");
|
||||
ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
|
||||
return NULL;
|
||||
}
|
||||
memset(md_msg, 0, sizeof(struct ast_smdi_md_message));
|
||||
|
||||
ASTOBJ_INIT(md_msg);
|
||||
|
||||
/* read the message desk number */
|
||||
for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++) {
|
||||
md_msg->mesg_desk_num[i] = fgetc(iface->file);
|
||||
}
|
||||
md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
|
||||
|
||||
/* read the message desk terminal number */
|
||||
for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++) {
|
||||
md_msg->mesg_desk_term[i] = fgetc(iface->file);
|
||||
}
|
||||
md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
|
||||
|
||||
/* read the message type */
|
||||
md_msg->type = fgetc(iface->file);
|
||||
|
||||
/* read the forwarding station number (may be blank) */
|
||||
cp = &md_msg->fwd_st[0];
|
||||
for(i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
|
||||
if((c = fgetc(iface->file)) == ' ') {
|
||||
*cp = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
/* store c in md_msg->fwd_st */
|
||||
if( i >= iface->msdstrip) {
|
||||
*cp = c;
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
/* make sure the value is null terminated, even if this truncates it */
|
||||
md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
|
||||
cp = NULL;
|
||||
|
||||
/* read the calling station number (may be blank) */
|
||||
cp = &md_msg->calling_st[0];
|
||||
for(i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
|
||||
if(!isdigit( (c = fgetc(iface->file)) )) {
|
||||
*cp = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
/* store c in md_msg->calling_st */
|
||||
if( i >= iface->msdstrip) {
|
||||
*cp = c;
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
/* make sure the value is null terminated, even if this truncates it */
|
||||
md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
|
||||
cp = NULL;
|
||||
|
||||
/* add the message to the message queue */
|
||||
gettimeofday(&md_msg->timestamp, NULL);
|
||||
ast_smdi_md_message_push(iface, md_msg);
|
||||
ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
|
||||
|
||||
ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
|
||||
|
||||
} else if(c == 'W') { /* MWI message */
|
||||
start = 0;
|
||||
|
||||
mwi_msg = malloc(sizeof(struct ast_smdi_mwi_message));
|
||||
if(!mwi_msg) {
|
||||
ast_log(LOG_ERROR, "Error allocating memory for ast_smdi_mwi_message. Stopping listner thread.\n");
|
||||
ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
|
||||
return NULL;
|
||||
}
|
||||
memset(mwi_msg, 0, sizeof(struct ast_smdi_mwi_message));
|
||||
|
||||
ASTOBJ_INIT(mwi_msg);
|
||||
|
||||
/* discard the 'I' (from 'MWI') */
|
||||
fgetc(iface->file);
|
||||
|
||||
/* read the forwarding station number (may be blank) */
|
||||
cp = &mwi_msg->fwd_st[0];
|
||||
for(i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
|
||||
if((c = fgetc(iface->file)) == ' ') {
|
||||
*cp = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
/* store c in md_msg->fwd_st */
|
||||
if( i >= iface->msdstrip) {
|
||||
*cp = c;
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
/* make sure the station number is null terminated, even if this will truncate it */
|
||||
mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
|
||||
cp = NULL;
|
||||
|
||||
/* read the mwi failure cause */
|
||||
for(i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++) {
|
||||
mwi_msg->cause[i] = fgetc(iface->file);
|
||||
}
|
||||
mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
|
||||
|
||||
/* add the message to the message queue */
|
||||
gettimeofday(&mwi_msg->timestamp, NULL);
|
||||
ast_smdi_mwi_message_push(iface, mwi_msg);
|
||||
ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
|
||||
|
||||
ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
|
||||
ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! \brief ast_smdi_md_message destructor. */
|
||||
void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
|
||||
{
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/*! \brief ast_smdi_mwi_message destructor. */
|
||||
void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
|
||||
{
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/*! \brief ast_smdi_interface destructor. */
|
||||
void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
|
||||
{
|
||||
if(iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
|
||||
pthread_cancel(iface->thread);
|
||||
pthread_join(iface->thread, NULL);
|
||||
}
|
||||
|
||||
iface->thread = AST_PTHREADT_STOP;
|
||||
|
||||
if(iface->file)
|
||||
fclose(iface->file);
|
||||
|
||||
ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
|
||||
ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
|
||||
ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
|
||||
ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
|
||||
free(iface);
|
||||
|
||||
ast_mutex_lock(&localuser_lock);
|
||||
localusecnt--;
|
||||
ast_mutex_unlock(&localuser_lock);
|
||||
ast_update_use_count();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Load and reload SMDI configuration.
|
||||
* \param reload this should be 1 if we are reloading and 0 if not.
|
||||
*
|
||||
* This function loads/reloads the SMDI configuration and starts and stops
|
||||
* interfaces accordingly.
|
||||
*
|
||||
* \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
|
||||
*/
|
||||
static int smdi_load(int reload)
|
||||
{
|
||||
struct ast_config *conf;
|
||||
struct ast_variable *v;
|
||||
struct ast_smdi_interface *iface = NULL;
|
||||
int res = 0;
|
||||
|
||||
/* Config options */
|
||||
speed_t baud_rate = B9600; /* 9600 baud rate */
|
||||
tcflag_t paritybit = PARENB; /* even parity checking */
|
||||
tcflag_t charsize = CS7; /* seven bit characters */
|
||||
int stopbits = 0; /* One stop bit */
|
||||
|
||||
int msdstrip = 0; /* strip zero digits */
|
||||
long msg_expiry = SMDI_MSG_EXPIRY_TIME;
|
||||
|
||||
conf = ast_config_load(config_file);
|
||||
|
||||
if(!conf) {
|
||||
ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Mark all interfaces that we are listening on. We will unmark them
|
||||
* as we find them in the config file, this way we know any interfaces
|
||||
* still marked after we have finished parsing the config file should
|
||||
* be stopped.
|
||||
*/
|
||||
if(reload)
|
||||
ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
|
||||
|
||||
v = ast_variable_browse(conf, "interfaces");
|
||||
while(v) {
|
||||
if(!strcasecmp(v->name, "baudrate")) {
|
||||
if(!strcasecmp(v->value, "9600")) {
|
||||
baud_rate = B9600;
|
||||
} else if(!strcasecmp(v->value, "4800")) {
|
||||
baud_rate = B4800;
|
||||
} else if(!strcasecmp(v->value, "2400")) {
|
||||
baud_rate = B2400;
|
||||
} else if(!strcasecmp(v->value, "1200")) {
|
||||
baud_rate = B1200;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
|
||||
baud_rate = B9600;
|
||||
}
|
||||
} else if(!strcasecmp(v->name, "msdstrip")) {
|
||||
if(!sscanf(v->value, "%d", &msdstrip)) {
|
||||
ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
|
||||
msdstrip = 0;
|
||||
} else if(0 > msdstrip || msdstrip > 9) {
|
||||
ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
|
||||
msdstrip = 0;
|
||||
}
|
||||
} else if(!strcasecmp(v->name, "msgexpirytime")) {
|
||||
if(!sscanf(v->value, "%ld", &msg_expiry)) {
|
||||
ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
|
||||
msg_expiry = SMDI_MSG_EXPIRY_TIME;
|
||||
}
|
||||
} else if(!strcasecmp(v->name, "paritybit")) {
|
||||
if(!strcasecmp(v->value, "even")) {
|
||||
paritybit = PARENB;
|
||||
} else if(!strcasecmp(v->value, "odd")) {
|
||||
paritybit = PARENB | PARODD;
|
||||
} else if(!strcasecmp(v->value, "none")) {
|
||||
paritybit = ~PARENB;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
|
||||
paritybit = PARENB;
|
||||
}
|
||||
} else if(!strcasecmp(v->name, "charsize")) {
|
||||
if(!strcasecmp(v->value, "7")) {
|
||||
charsize = CS7;
|
||||
} else if(!strcasecmp(v->value, "8")) {
|
||||
charsize = CS8;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
|
||||
charsize = CS7;
|
||||
}
|
||||
} else if(!strcasecmp(v->name, "twostopbits")) {
|
||||
stopbits = ast_true(v->name);
|
||||
} else if(!strcasecmp(v->name, "smdiport")) {
|
||||
if(reload) {
|
||||
/* we are reloading, check if we are already
|
||||
* monitoring this interface, if we are we do
|
||||
* not want to start it again. This also has
|
||||
* the side effect of not updating different
|
||||
* setting for the serial port, but it should
|
||||
* be trivial to rewrite this section so that
|
||||
* options on the port are changed without
|
||||
* restarting the interface. Or the interface
|
||||
* could be restarted with out emptying the
|
||||
* queue. */
|
||||
iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value);
|
||||
if(iface) {
|
||||
ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
|
||||
ASTOBJ_UNMARK(iface);
|
||||
ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
iface = malloc(sizeof(struct ast_smdi_interface));
|
||||
|
||||
ASTOBJ_INIT(iface);
|
||||
ASTOBJ_CONTAINER_INIT(&iface->md_q);
|
||||
ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
|
||||
iface->md_q.head = NULL;
|
||||
iface->mwi_q.head = NULL;
|
||||
iface->thread = AST_PTHREADT_NULL;
|
||||
memset(&iface->mode, 0, sizeof(iface->mode));
|
||||
|
||||
ast_copy_string(iface->name, v->value, sizeof(iface->name));
|
||||
|
||||
iface->file = fopen(iface->name, "r");
|
||||
if(!(iface->file)) {
|
||||
ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
|
||||
ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
iface->fd = fileno(iface->file);
|
||||
|
||||
/* Set the proper attributes for our serial port. */
|
||||
|
||||
/* get the current attributes from the port */
|
||||
if(tcgetattr(iface->fd, &iface->mode)) {
|
||||
ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
|
||||
ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set the desired speed */
|
||||
if(cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
|
||||
ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
|
||||
ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set the stop bits */
|
||||
if(stopbits)
|
||||
iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */
|
||||
else
|
||||
iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */
|
||||
|
||||
/* set the parity */
|
||||
iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
|
||||
|
||||
/* set the character size */
|
||||
iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
|
||||
|
||||
/* commit the desired attributes */
|
||||
if(tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
|
||||
ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
|
||||
ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set the msdstrip */
|
||||
iface->msdstrip = msdstrip;
|
||||
|
||||
/* set the message expiry time */
|
||||
iface->msg_expiry = msg_expiry;
|
||||
|
||||
/* start the listner thread */
|
||||
if(option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name);
|
||||
if(ast_pthread_create(&iface->thread, NULL, smdi_read, iface)) {
|
||||
ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
|
||||
ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
|
||||
v = v->next;
|
||||
continue;
|
||||
}
|
||||
ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
|
||||
ast_mutex_lock(&localuser_lock);
|
||||
localusecnt++;
|
||||
ast_mutex_unlock(&localuser_lock);
|
||||
ast_update_use_count();
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
|
||||
}
|
||||
v = v->next;
|
||||
}
|
||||
ast_config_destroy(conf);
|
||||
|
||||
/* Prune any interfaces we should no longer monitor. */
|
||||
if(reload)
|
||||
ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
|
||||
|
||||
ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
|
||||
if(!smdi_ifaces.head)
|
||||
res = 1;
|
||||
ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return (char *)tdesc;
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* initilize our containers */
|
||||
ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
|
||||
smdi_ifaces.head = NULL;
|
||||
|
||||
/* load the config and start the listener threads*/
|
||||
res = smdi_load(0);
|
||||
if(res < 0) {
|
||||
return res;
|
||||
} else if(res == 1) {
|
||||
ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SDMI listener.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
/* this destructor stops any running smdi_read threads */
|
||||
ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces,ast_smdi_interface_destroy);
|
||||
ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reload(void)
|
||||
{
|
||||
int res;
|
||||
res = smdi_load(1);
|
||||
|
||||
if(res < 0) {
|
||||
return res;
|
||||
} else if(res == 1) {
|
||||
ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* notify the listner thread? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
Loading…
Reference in new issue