提交 d91f67d0 authored 作者: Anthony Minessale's avatar Anthony Minessale

Add T31 modem support to mod_spandsp - similar to iaxmodem only wired into FS. …

Add T31 modem support to mod_spandsp - similar to iaxmodem only wired into FS.  Also merge configs into single spandsp.conf.xml --see in tree example--
上级 0ed06ad7
<configuration name="spandsp.conf" description="Tone detector descriptors">
<descriptors>
<configuration name="spandsp.conf" description="SpanDSP config">
<modem-settings>
<!--
total-modems set to N will create that many soft-modems.
If you use them with Hylafax you need the following for each one numbered 0..N:
1) A line like this in /etc/inittab:
f0:2345:respawn:/usr/lib/fax/faxgetty /dev/FS0
2) copy conf/config.FS0 to /var/spool/hylafax/etc (or wherver the appropriate dir is on your system)
Subsequent modem configs would incrment the 0 to 1 and so on.
-->
<param name="total-modems" value="1"/>
<!-- Default context and dialplan to use on inbound calls from the modems -->
<param name="context" value="default"/>
<param name="dialplan" value="XML"/>
<!-- Extra tracing for debugging -->
<param name="verbose" value="false"/>
</modem-settings>
<fax-settings>
<param name="use-ecm" value="true"/>
<param name="verbose" value="false"/>
<param name="disable-v17" value="false"/>
<param name="ident" value="SpanDSP Fax Ident"/>
<param name="header" value="SpanDSP Fax Header"/>
<param name="spool-dir" value="/tmp"/>
<param name="file-prefix" value="faxrx"/>
</fax-settings>
<descriptors>
<!-- These tones are defined in Annex to ITU Operational Bulletin No. 781 - 1.II.2003 -->
<!-- Various Tones Used in National Networks (According to ITU-T Recommendation E.180)(03/1998) -->
......
CountryCode: 1
AreaCode: 800
FAXNumber: +1.800.555.1212
LongDistancePrefix: 1
InternationalPrefix: 011
DialStringRules: etc/dialrules
ServerTracing: 0xFFF
SessionTracing: 0xFFF
RecvFileMode: 0600
LogFileMode: 0600
DeviceMode: 0600
RingsBeforeAnswer: 1
SpeakerVolume: off
GettyArgs: "-h %l dx_%s"
LocalIdentifier: "FS"
TagLineFont: etc/lutRS18.pcf
TagLineFormat: "From %%l|%c|Page %%P of %%T"
MaxRecvPages: 200
#
#
# Modem-related stuff: should reflect modem command interface
# and hardware connection/cabling (e.g. flow control).
#
ModemType: Class1 # use this to supply a hint
#
# Enabling this will use the hfaxd-protocol to set Caller*ID
#
#ModemSetOriginCmd: AT+VSID="%s","%d"
#
# If "glare" during initialization becomes a problem then take
# the modem off-hook during initialization, and then place it
# back on-hook when done.
#
#ModemResetCmds: "ATH1\nAT+VCID=1" # enables CallID display
#ModemReadyCmds: ATH0
Class1AdaptRecvCmd: AT+FAR=1
Class1TMConnectDelay: 400 # counteract quick CONNECT response
#
# If you have trouble with V.17 receiving or sending,
# you may want to enable one of these, respectively.
#
#Class1RMQueryCmd: "!24,48,72,96" # enable this to disable V.17 receiving
#Class1TMQueryCmd: "!24,48,72,96" # enable this to disable V.17 sending
#
# You'll likely want Caller*ID display (also displays DID) enabled.
#
ModemResetCmds: AT+VCID=1 # enables CallID display
#
# The pty does not support changing parity.
#
PagerTTYParity: none
#
# If you are "missing" Caller*ID data on some calls (but not all)
# and if you do not have adequate glare protection you may want to
# not answer based on RINGs, but rather enable the CallIDAnswerLength
# for NDID, disable AT+VCID=1 and do this:
#
#RingsBeforeAnswer: 0
#ModemRingResponse: AT+VRID=1
# Uncomment DATE and TIME if you really want them, but you probably don't.
#CallIDPattern: "DATE="
#CallIDPattern: "TIME="
CallIDPattern: "NMBR="
CallIDPattern: "NAME="
CallIDPattern: "ANID="
#CallIDPattern: "USER=" # username provided by call
#CallIDPattern: "PASS=" # password provided by call
#CallIDPattern: "CDID=" # DID context in call
CallIDPattern: "NDID="
#CallIDAnswerLength: 4
......@@ -483,7 +483,7 @@ AC_PROG_GCC_TRADITIONAL
AC_FUNC_MALLOC
AC_TYPE_SIGNAL
AC_FUNC_STRFTIME
AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep getifaddrs timerfd_create getdtablesize])
AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep getifaddrs timerfd_create getdtablesize posix_openpt])
AC_CHECK_FUNCS([sched_setscheduler setpriority setrlimit setgroups initgroups])
AC_CHECK_FUNCS([wcsncmp setgroups asprintf setenv pselect gettimeofday localtime_r gmtime_r strcasecmp stricmp _stricmp])
......@@ -494,6 +494,9 @@ AC_CHECK_LIB(rt, clock_getres, [AC_DEFINE(HAVE_CLOCK_GETRES, 1, [Define if you h
AC_CHECK_LIB(rt, clock_nanosleep, [AC_DEFINE(HAVE_CLOCK_NANOSLEEP, 1, [Define if you have clock_nanosleep()])])
AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket))
AC_CHECK_FILE(/dev/ptmx, [AC_DEFINE(HAVE_DEV_PTMX, 1, [Define if you have /dev/ptmx])])
AC_CHECK_LIB(util, openpty, [AC_DEFINE(HAVE_OPENPTY, 1, [Define if you have openpty()])])
AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[
#include <sys/types.h>
#include <time.h>])
......
......@@ -4,15 +4,15 @@ MODNAME=mod_spandsp
TIFF_DIR=$(switch_srcdir)/libs/tiff-3.8.2
TIFF_BUILDDIR=$(switch_builddir)/libs/tiff-3.8.2
TIFF_LA=$(TIFF_BUILDDIR)/libtiff/libtiff.la
BUILD_CFLAGS=
SPANDSP_DIR=$(switch_srcdir)/libs/spandsp
SPANDSP_BUILDDIR=$(switch_builddir)/libs/spandsp
SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la
mod_LTLIBRARIES = mod_spandsp.la
mod_spandsp_la_SOURCES = mod_spandsp.c udptl.c mod_spandsp_fax.c mod_spandsp_dsp.c mod_spandsp_codecs.c
mod_spandsp_la_CFLAGS = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff -I.
mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA) -ljpeg -lz
mod_spandsp_la_SOURCES = mod_spandsp.c udptl.c mod_spandsp_fax.c mod_spandsp_dsp.c mod_spandsp_codecs.c mod_spandsp_modem.c
mod_spandsp_la_CFLAGS = $(BUILD_CFLAGS) $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff -I.
mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA) -ljpeg -lz -lutil
mod_spandsp_la_LDFLAGS = -avoid-version -module -no-undefined -shared
$(SPANDSP_LA): $(TIFF_LA) $(SPANDSP_DIR) $(SPANDSP_DIR)/.update
......
......@@ -36,11 +36,14 @@
#include "mod_spandsp.h"
#include <spandsp/version.h>
#include "mod_spandsp_modem.h"
/* **************************************************************************
FREESWITCH MODULE DEFINITIONS
************************************************************************* */
struct spandsp_globals spandsp_globals = { 0 };
#define SPANFAX_RX_USAGE "<filename>"
#define SPANFAX_TX_USAGE "<filename>"
......@@ -116,10 +119,9 @@ SWITCH_STANDARD_APP(spandsp_stop_fax_detect_session_function)
static void event_handler(switch_event_t *event)
{
mod_spandsp_fax_event_handler(event);
load_configuration(1);
}
SWITCH_STANDARD_APP(t38_gateway_function)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
......@@ -261,12 +263,239 @@ SWITCH_STANDARD_API(stop_tone_detect_api)
return status;
}
/* **************************************************************************
CONFIGURATION
************************************************************************* */
switch_status_t load_configuration(switch_bool_t reload)
{
switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, cfg = NULL, callprogress = NULL, xdescriptor = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_mutex_lock(spandsp_globals.mutex);
if (spandsp_globals.tones) {
switch_core_hash_destroy(&spandsp_globals.tones);
}
if (spandsp_globals.config_pool) {
switch_core_destroy_memory_pool(&spandsp_globals.config_pool);
}
switch_core_new_memory_pool(&spandsp_globals.config_pool);
switch_core_hash_init(&spandsp_globals.tones, spandsp_globals.config_pool);
spandsp_globals.modem_dialplan = "XML";
spandsp_globals.modem_context = "default";
spandsp_globals.modem_count = 1;
spandsp_globals.enable_t38 = 1;
spandsp_globals.total_sessions = 0;
spandsp_globals.verbose = 0;
spandsp_globals.use_ecm = 1;
spandsp_globals.disable_v17 = 0;
spandsp_globals.prepend_string = switch_core_strdup(spandsp_globals.config_pool, "fax");
spandsp_globals.spool = switch_core_strdup(spandsp_globals.config_pool, "/tmp");
spandsp_globals.ident = "SpanDSP Fax Ident";
spandsp_globals.header = "SpanDSP Fax Header";
/* TODO make configuration param */
spandsp_globals.tonedebug = 1;
if ((xml = switch_xml_open_cfg("spandsp.conf", &cfg, NULL)) || (xml = switch_xml_open_cfg("fax.conf", &cfg, NULL))) {
status = SWITCH_STATUS_SUCCESS;
if ((x_lists = switch_xml_child(cfg, "modem-settings"))) {
for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) {
const char *name = switch_xml_attr(x_list, "name");
const char *value = switch_xml_attr(x_list, "value");
if (zstr(name)) {
continue;
}
if (zstr(value)) {
continue;
}
if (!reload && !strcmp(name, "total-modems")) {
int tmp = atoi(value);
if (tmp > 0 && tmp < MAX_MODEMS) {
spandsp_globals.modem_count = tmp;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid value [%d] for total-modems\n", tmp);
}
} else if (!strcmp(name, "dialplan")) {
spandsp_globals.modem_dialplan = switch_core_strdup(spandsp_globals.config_pool, value);
} else if (!strcmp(name, "context")) {
spandsp_globals.modem_context = switch_core_strdup(spandsp_globals.config_pool, value);
} else if (!strcmp(name, "verbose")) {
if (switch_true(value)) {
spandsp_globals.modem_verbose = 1;
} else {
spandsp_globals.modem_verbose = 0;
}
}
}
}
if ((x_lists = switch_xml_child(cfg, "fax-settings")) || (x_lists = switch_xml_child(cfg, "settings"))) {
for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) {
const char *name = switch_xml_attr(x_list, "name");
const char *value = switch_xml_attr(x_list, "value");
if (zstr(name)) {
continue;
}
if (zstr(value)) {
continue;
}
if (!strcmp(name, "use-ecm")) {
if (switch_true(value))
spandsp_globals.use_ecm = 1;
else
spandsp_globals.use_ecm = 0;
} else if (!strcmp(name, "verbose")) {
if (switch_true(value))
spandsp_globals.verbose = 1;
else
spandsp_globals.verbose = 0;
} else if (!strcmp(name, "disable-v17")) {
if (switch_true(value))
spandsp_globals.disable_v17 = 1;
else
spandsp_globals.disable_v17 = 0;
} else if (!strcmp(name, "enable-t38")) {
if (switch_true(value)) {
spandsp_globals.enable_t38= 1;
} else {
spandsp_globals.enable_t38 = 0;
}
} else if (!strcmp(name, "enable-t38-request")) {
if (switch_true(value)) {
spandsp_globals.enable_t38_request = 1;
} else {
spandsp_globals.enable_t38_request = 0;
}
} else if (!strcmp(name, "ident")) {
spandsp_globals.ident = switch_core_strdup(spandsp_globals.config_pool, value);
} else if (!strcmp(name, "header")) {
spandsp_globals.header = switch_core_strdup(spandsp_globals.config_pool, value);
} else if (!strcmp(name, "spool-dir")) {
spandsp_globals.spool = switch_core_strdup(spandsp_globals.config_pool, value);
} else if (!strcmp(name, "file-prefix")) {
spandsp_globals.prepend_string = switch_core_strdup(spandsp_globals.config_pool, value);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown parameter %s\n", name);
}
}
}
/* Configure call progress detector */
if ((callprogress = switch_xml_child(cfg, "descriptors"))) {
for (xdescriptor = switch_xml_child(callprogress, "descriptor"); xdescriptor; xdescriptor = switch_xml_next(xdescriptor)) {
const char *name = switch_xml_attr(xdescriptor, "name");
const char *tone_name = NULL;
switch_xml_t tone = NULL, element = NULL;
tone_descriptor_t *descriptor = NULL;
/* create descriptor */
if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing <descriptor> name\n");
switch_goto_status(SWITCH_STATUS_FALSE, done);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding tone_descriptor: %s\n", name);
if (tone_descriptor_create(&descriptor, name, spandsp_globals.config_pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to allocate tone_descriptor: %s\n", name);
switch_goto_status(SWITCH_STATUS_FALSE, done);
}
switch_core_hash_insert(spandsp_globals.tones, name, descriptor);
/* add tones to descriptor */
for (tone = switch_xml_child(xdescriptor, "tone"); tone; tone = switch_xml_next(tone)) {
int id = 0;
tone_name = switch_xml_attr(tone, "name");
if (zstr(tone_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing <tone> name for <descriptor> %s\n", name);
switch_goto_status(SWITCH_STATUS_FALSE, done);
}
id = tone_descriptor_add_tone(descriptor, tone_name);
if (id == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Unable to add tone_descriptor: %s, tone: %s. (too many tones)\n", name, tone_name);
switch_goto_status(SWITCH_STATUS_FALSE, done);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
"Adding tone_descriptor: %s, tone: %s(%d)\n", name, tone_name, id);
/* add elements to tone */
for (element = switch_xml_child(tone, "element"); element; element = switch_xml_next(element)) {
const char *freq1_attr = switch_xml_attr(element, "freq1");
const char *freq2_attr = switch_xml_attr(element, "freq2");
const char *min_attr = switch_xml_attr(element, "min");
const char *max_attr = switch_xml_attr(element, "max");
int freq1, freq2, min, max;
if (zstr(freq1_attr)) {
freq1 = 0;
} else {
freq1 = atoi(freq1_attr);
}
if (zstr(freq2_attr)) {
freq2 = 0;
} else {
freq2 = atoi(freq2_attr);
}
if (zstr(min_attr)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Missing min in <element> of <descriptor> %s <tone> %s(%d)\n", name, tone_name, id);
switch_goto_status(SWITCH_STATUS_FALSE, done);
}
min = atoi(min_attr);
if (zstr(max_attr)) {
max = 0;
} else {
max = atoi(max_attr);
}
/* check params */
if ((freq1 < 0 || freq2 < 0 || min < 0 || max < 0) || (freq1 == 0 && min == 0 && max == 0)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid element param.\n");
switch_goto_status(SWITCH_STATUS_FALSE, done);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
"Adding tone_descriptor: %s, tone: %s(%d), element (%d, %d, %d, %d)\n", name, tone_name, id, freq1, freq2, min, max);
tone_descriptor_add_tone_element(descriptor, id, freq1, freq2, min, max);
}
}
}
}
done:
switch_xml_free(xml);
}
switch_mutex_unlock(spandsp_globals.mutex);
return status;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init)
{
switch_application_interface_t *app_interface;
switch_api_interface_t *api_interface;
memset(&spandsp_globals, 0, sizeof(spandsp_globals));
spandsp_globals.pool = pool;
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
switch_mutex_init(&spandsp_globals.mutex, SWITCH_MUTEX_NESTED, pool);
SWITCH_ADD_APP(app_interface, "t38_gateway", "Convert to T38 Gateway if tones are heard", "Convert to T38 Gateway if tones are heard",
t38_gateway_function, "", SAF_MEDIA_TAP);
......@@ -284,10 +513,12 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init)
SWITCH_ADD_APP(app_interface, "spandsp_stop_fax_detect", "stop fax detect", "stop fax detect", spandsp_stop_fax_detect_session_function, "", SAF_NONE);
load_configuration(0);
mod_spandsp_fax_load(pool);
mod_spandsp_codecs_load(module_interface, pool);
if (mod_spandsp_dsp_load(module_interface, pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't load or process spandsp.conf, not adding tone_detect applications\n");
} else {
......@@ -302,6 +533,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init)
/* Not such severe to prevent loading */
}
modem_global_init(module_interface, pool);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "mod_spandsp loaded, using spandsp library version [%s]\n", SPANDSP_RELEASE_DATETIME_STRING);
return SWITCH_STATUS_SUCCESS;
......@@ -313,6 +545,15 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_spandsp_shutdown)
mod_spandsp_fax_shutdown();
mod_spandsp_dsp_shutdown();
modem_global_shutdown();
if (spandsp_globals.tones) {
switch_core_hash_destroy(&spandsp_globals.tones);
}
if (spandsp_globals.config_pool) {
switch_core_destroy_memory_pool(&spandsp_globals.config_pool);
}
return SWITCH_STATUS_UNLOAD;
}
......
......@@ -42,9 +42,40 @@ typedef HANDLE zap_socket_t;
typedef int zap_socket_t;
#endif
#define MAX_MODEMS 1024
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
#include <spandsp.h>
/* The global stuff */
struct spandsp_globals {
switch_memory_pool_t *pool;
switch_memory_pool_t *config_pool;
switch_mutex_t *mutex;
uint32_t total_sessions;
short int use_ecm;
short int verbose;
short int disable_v17;
short int enable_t38;
short int enable_t38_request;
short int enable_t38_insist;
char *ident;
char *header;
char *prepend_string;
char *spool;
switch_thread_cond_t *cond;
switch_mutex_t *cond_mutex;
int modem_count;
int modem_verbose;
char *modem_context;
char *modem_dialplan;
switch_hash_t *tones;
int tonedebug;
};
extern struct spandsp_globals spandsp_globals;
typedef enum {
FUNCTION_TX,
......@@ -52,6 +83,37 @@ typedef enum {
FUNCTION_GW
} mod_spandsp_fax_application_mode_t;
/******************************************************************************
* TONE DETECTION WITH CADENCE
*/
#define MAX_TONES 32
#define STRLEN 128
/**
* Tone descriptor
*
* Defines a set of tones to look for
*/
struct tone_descriptor {
/** The name of this descriptor set */
const char *name;
/** Describes the tones to watch */
super_tone_rx_descriptor_t *spandsp_tone_descriptor;
/** The mapping of tone id to key */
char tone_keys[MAX_TONES][STRLEN];
int idx;
};
typedef struct tone_descriptor tone_descriptor_t;
switch_status_t tone_descriptor_create(tone_descriptor_t **descriptor, const char *name, switch_memory_pool_t *memory_pool);
int tone_descriptor_add_tone(tone_descriptor_t *descriptor, const char *name);
switch_status_t tone_descriptor_add_tone_element(tone_descriptor_t *descriptor, int tone_id, int freq1, int freq2, int min, int max);
void mod_spandsp_fax_load(switch_memory_pool_t *pool);
switch_status_t mod_spandsp_codecs_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool);
switch_status_t mod_spandsp_dsp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool);
......@@ -74,3 +136,5 @@ switch_status_t spandsp_fax_detect_session(switch_core_session_t *session,
int hits, const char *app, const char *data, switch_tone_detect_callback_t callback);
switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session);
void spanfax_log_message(int level, const char *msg);
switch_status_t load_configuration(switch_bool_t reload);
......@@ -155,48 +155,7 @@ switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session)
/* private channel data */
#define TONE_PRIVATE "mod_tone_detect_bug"
/**
* Module global variables
*/
struct globals {
/** Memory pool */
switch_memory_pool_t *pool;
/** Call progress tones mapped by descriptor name */
switch_hash_t *tones;
/** Default debug level */
int debug;
};
typedef struct globals globals_t;
static globals_t globals;
/******************************************************************************
* TONE DETECTION WITH CADENCE
*/
#define MAX_TONES 32
#define STRLEN 128
/**
* Tone descriptor
*
* Defines a set of tones to look for
*/
struct tone_descriptor {
/** The name of this descriptor set */
const char *name;
/** Describes the tones to watch */
super_tone_rx_descriptor_t *spandsp_tone_descriptor;
/** The mapping of tone id to key */
char tone_keys[MAX_TONES][STRLEN];
int idx;
};
typedef struct tone_descriptor tone_descriptor_t;
static switch_status_t tone_descriptor_create(tone_descriptor_t **descriptor, const char *name, switch_memory_pool_t *memory_pool);
static int tone_descriptor_add_tone(tone_descriptor_t *descriptor, const char *name);
static switch_status_t tone_descriptor_add_tone_element(tone_descriptor_t *descriptor, int tone_id, int freq1, int freq2, int min, int max);
/**
* Tone detector
......@@ -232,7 +191,7 @@ static switch_bool_t callprogress_detector_process_buffer(switch_media_bug_t *bu
* @param memory_pool the pool to use
* @return SWITCH_STATUS_SUCCESS if successful
*/
static switch_status_t tone_descriptor_create(tone_descriptor_t **descriptor, const char *name, switch_memory_pool_t *memory_pool)
switch_status_t tone_descriptor_create(tone_descriptor_t **descriptor, const char *name, switch_memory_pool_t *memory_pool)
{
tone_descriptor_t *ldescriptor = NULL;
ldescriptor = switch_core_alloc(memory_pool, sizeof(tone_descriptor_t));
......@@ -253,7 +212,7 @@ static switch_status_t tone_descriptor_create(tone_descriptor_t **descriptor, co
* @param key the tone key - this will be returned by the detector upon match
* @return the tone ID
*/
static int tone_descriptor_add_tone(tone_descriptor_t *descriptor, const char *key)
int tone_descriptor_add_tone(tone_descriptor_t *descriptor, const char *key)
{
int id = super_tone_rx_add_tone(descriptor->spandsp_tone_descriptor);
if (id >= MAX_TONES) {
......@@ -279,7 +238,7 @@ static int tone_descriptor_add_tone(tone_descriptor_t *descriptor, const char *k
* @param max the maximum tone duration in ms
* @return SWITCH_STATUS_SUCCESS if successful
*/
static switch_status_t tone_descriptor_add_tone_element(tone_descriptor_t *descriptor, int tone_id, int freq1, int freq2, int min, int max)
switch_status_t tone_descriptor_add_tone_element(tone_descriptor_t *descriptor, int tone_id, int freq1, int freq2, int min, int max)
{
if (super_tone_rx_add_element(descriptor->spandsp_tone_descriptor, tone_id, freq1, freq2, min, max) == 0) {
return SWITCH_STATUS_SUCCESS;
......@@ -337,7 +296,7 @@ static switch_status_t tone_detector_create(tone_detector_t **detector, tone_des
}
memset(ldetector, 0, sizeof(tone_detector_t));
ldetector->descriptor = descriptor;
ldetector->debug = globals.debug;
ldetector->debug = spandsp_globals.tonedebug;
*detector = ldetector;
return SWITCH_STATUS_SUCCESS;
}
......@@ -409,7 +368,7 @@ switch_status_t callprogress_detector_start(switch_core_session_t *session, cons
}
/* find the tone descriptor with the matching name and create the detector */
descriptor = switch_core_hash_find(globals.tones, name);
descriptor = switch_core_hash_find(spandsp_globals.tones, name);
if (!descriptor) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "(%s) no tone descriptor defined with name '%s'. Update configuration. \n", switch_channel_get_name(channel), name);
return SWITCH_STATUS_FALSE;
......@@ -509,116 +468,11 @@ switch_status_t callprogress_detector_stop(switch_core_session_t *session)
return SWITCH_STATUS_SUCCESS;
}
/**
* Process configuration file
*/
static switch_status_t do_config(void)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_xml_t cfg = NULL, xml = NULL, callprogress = NULL, xdescriptor = NULL;
if (!(xml = switch_xml_open_cfg("spandsp.conf", &cfg, NULL))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not open spandsp.conf\n");
status = SWITCH_STATUS_FALSE;
goto done;
}
/* TODO make configuration param */
globals.debug = 1;
/* Configure call progress detector */
if ((callprogress = switch_xml_child(cfg, "descriptors"))) {
for (xdescriptor = switch_xml_child(callprogress, "descriptor"); xdescriptor; xdescriptor = switch_xml_next(xdescriptor)) {
const char *name = switch_xml_attr(xdescriptor, "name");
const char *tone_name = NULL;
switch_xml_t tone = NULL, element = NULL;
tone_descriptor_t *descriptor = NULL;
/* create descriptor */
if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing <descriptor> name\n");
return SWITCH_STATUS_FALSE;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding tone_descriptor: %s\n", name);
if (tone_descriptor_create(&descriptor, name, globals.pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to allocate tone_descriptor: %s\n", name);
return SWITCH_STATUS_FALSE;
}
switch_core_hash_insert(globals.tones, name, descriptor);
/* add tones to descriptor */
for (tone = switch_xml_child(xdescriptor, "tone"); tone; tone = switch_xml_next(tone)) {
int id = 0;
tone_name = switch_xml_attr(tone, "name");
if (zstr(tone_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing <tone> name for <descriptor> %s\n", name);
return SWITCH_STATUS_FALSE;
}
id = tone_descriptor_add_tone(descriptor, tone_name);
if (id == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to add tone_descriptor: %s, tone: %s. (too many tones)\n", name, tone_name);
return SWITCH_STATUS_FALSE;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding tone_descriptor: %s, tone: %s(%d)\n", name, tone_name, id);
/* add elements to tone */
for (element = switch_xml_child(tone, "element"); element; element = switch_xml_next(element)) {
const char *freq1_attr = switch_xml_attr(element, "freq1");
const char *freq2_attr = switch_xml_attr(element, "freq2");
const char *min_attr = switch_xml_attr(element, "min");
const char *max_attr = switch_xml_attr(element, "max");
int freq1, freq2, min, max;
if (zstr(freq1_attr)) {
freq1 = 0;
} else {
freq1 = atoi(freq1_attr);
}
if (zstr(freq2_attr)) {
freq2 = 0;
} else {
freq2 = atoi(freq2_attr);
}
if (zstr(min_attr)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing min in <element> of <descriptor> %s <tone> %s(%d)\n", name, tone_name, id);
return SWITCH_STATUS_FALSE;
}
min = atoi(min_attr);
if (zstr(max_attr)) {
max = 0;
} else {
max = atoi(max_attr);
}
/* check params */
if ((freq1 < 0 || freq2 < 0 || min < 0 || max < 0) || (freq1 == 0 && min == 0 && max == 0)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid element param.\n");
return SWITCH_STATUS_FALSE;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding tone_descriptor: %s, tone: %s(%d), element (%d, %d, %d, %d)\n", name, tone_name, id, freq1, freq2, min, max);
tone_descriptor_add_tone_element(descriptor, id, freq1, freq2, min, max);
}
}
}
}
done:
if (xml) {
switch_xml_free(xml);
}
return status;
}
/**
* Called when FreeSWITCH loads the module
*/
switch_status_t mod_spandsp_dsp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
{
memset(&globals, 0, sizeof(globals_t));
globals.pool = pool;
switch_core_hash_init(&globals.tones, globals.pool);
if (do_config() != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
......@@ -628,7 +482,7 @@ switch_status_t mod_spandsp_dsp_load(switch_loadable_module_interface_t **module
*/
void mod_spandsp_dsp_shutdown(void)
{
switch_core_hash_destroy(&globals.tones);
return;
}
......
......@@ -60,26 +60,6 @@ typedef enum {
T38_MODE_REFUSED = -1,
} t38_mode_t;
/* The global stuff */
static struct {
switch_memory_pool_t *pool;
switch_mutex_t *mutex;
uint32_t total_sessions;
short int use_ecm;
short int verbose;
short int disable_v17;
short int enable_t38;
short int enable_t38_request;
short int enable_t38_insist;
char ident[20];
char header[50];
char *prepend_string;
char *spool;
switch_thread_cond_t *cond;
switch_mutex_t *cond_mutex;
} globals;
struct pvt_s {
switch_core_session_t *session;
......@@ -128,13 +108,13 @@ static struct {
static void wake_thread(int force)
{
if (force) {
switch_thread_cond_signal(globals.cond);
switch_thread_cond_signal(spandsp_globals.cond);
return;
}
if (switch_mutex_trylock(globals.cond_mutex) == SWITCH_STATUS_SUCCESS) {
switch_thread_cond_signal(globals.cond);
switch_mutex_unlock(globals.cond_mutex);
if (switch_mutex_trylock(spandsp_globals.cond_mutex) == SWITCH_STATUS_SUCCESS) {
switch_thread_cond_signal(spandsp_globals.cond);
switch_mutex_unlock(spandsp_globals.cond_mutex);
}
}
......@@ -206,7 +186,7 @@ static void *SWITCH_THREAD_FUNC timer_thread_run(switch_thread_t *thread, void *
goto end;
}
switch_mutex_lock(globals.cond_mutex);
switch_mutex_lock(spandsp_globals.cond_mutex);
while(t38_state_list.thread_running) {
......@@ -214,7 +194,7 @@ static void *SWITCH_THREAD_FUNC timer_thread_run(switch_thread_t *thread, void *
if (!t38_state_list.head) {
switch_mutex_unlock(t38_state_list.mutex);
switch_thread_cond_wait(globals.cond, globals.cond_mutex);
switch_thread_cond_wait(spandsp_globals.cond, spandsp_globals.cond_mutex);
switch_core_timer_sync(&timer);
continue;
}
......@@ -230,7 +210,7 @@ static void *SWITCH_THREAD_FUNC timer_thread_run(switch_thread_t *thread, void *
switch_core_timer_next(&timer);
}
switch_mutex_unlock(globals.cond_mutex);
switch_mutex_unlock(spandsp_globals.cond_mutex);
end:
......@@ -252,9 +232,9 @@ static void launch_timer_thread(void)
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, globals.pool);
switch_threadattr_create(&thd_attr, spandsp_globals.pool);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&t38_state_list.thread, thd_attr, timer_thread_run, NULL, globals.pool);
switch_thread_create(&t38_state_list.thread, thd_attr, timer_thread_run, NULL, spandsp_globals.pool);
}
......@@ -264,12 +244,12 @@ static void launch_timer_thread(void)
static void counter_increment(void)
{
switch_mutex_lock(globals.mutex);
globals.total_sessions++;
switch_mutex_unlock(globals.mutex);
switch_mutex_lock(spandsp_globals.mutex);
spandsp_globals.total_sessions++;
switch_mutex_unlock(spandsp_globals.mutex);
}
static void spanfax_log_message(int level, const char *msg)
void spanfax_log_message(int level, const char *msg)
{
int fs_log_level;
......@@ -821,7 +801,7 @@ static t38_mode_t negotiate_t38(pvt_t *pvt)
} else if ((v = switch_channel_get_variable(channel, "fax_enable_t38"))) {
enabled = switch_true(v);
} else {
enabled = globals.enable_t38;
enabled = spandsp_globals.enable_t38;
}
if (!(enabled && t38_options)) {
......@@ -877,7 +857,7 @@ static t38_mode_t negotiate_t38(pvt_t *pvt)
if ((v = switch_channel_get_variable(channel, "fax_enable_t38_insist"))) {
insist = switch_true(v);
} else {
insist = globals.enable_t38_insist;
insist = spandsp_globals.enable_t38_insist;
}
/* This will send the options back in a response */
......@@ -907,14 +887,14 @@ static t38_mode_t request_t38(pvt_t *pvt)
} else if ((v = switch_channel_get_variable(channel, "fax_enable_t38"))) {
enabled = switch_true(v);
} else {
enabled = globals.enable_t38;
enabled = spandsp_globals.enable_t38;
}
if (enabled) {
if ((v = switch_channel_get_variable(channel, "fax_enable_t38_request"))) {
enabled = switch_true(v);
} else {
enabled = globals.enable_t38_request;
enabled = spandsp_globals.enable_t38_request;
}
}
......@@ -922,7 +902,7 @@ static t38_mode_t request_t38(pvt_t *pvt)
if ((v = switch_channel_get_variable(channel, "fax_enable_t38_insist"))) {
insist = switch_true(v);
} else {
insist = globals.enable_t38_insist;
insist = spandsp_globals.enable_t38_insist;
}
if ((t38_options = switch_channel_get_private(channel, "t38_options"))) {
......@@ -1004,19 +984,19 @@ static pvt_t *pvt_init(switch_core_session_t *session, mod_spandsp_fax_applicati
if ((tmp = switch_channel_get_variable(channel, "fax_use_ecm"))) {
pvt->use_ecm = switch_true(tmp);
} else {
pvt->use_ecm = globals.use_ecm;
pvt->use_ecm = spandsp_globals.use_ecm;
}
if ((tmp = switch_channel_get_variable(channel, "fax_disable_v17"))) {
pvt->disable_v17 = switch_true(tmp);
} else {
pvt->disable_v17 = globals.disable_v17;
pvt->disable_v17 = spandsp_globals.disable_v17;
}
if ((tmp = switch_channel_get_variable(channel, "fax_verbose"))) {
pvt->verbose = switch_true(tmp);
} else {
pvt->verbose = globals.verbose;
pvt->verbose = spandsp_globals.verbose;
}
if ((tmp = switch_channel_get_variable(channel, "fax_force_caller"))) {
......@@ -1036,7 +1016,7 @@ static pvt_t *pvt_init(switch_core_session_t *session, mod_spandsp_fax_applicati
switch_safe_free(data);
} else {
pvt->ident = switch_core_session_strdup(session, globals.ident);
pvt->ident = switch_core_session_strdup(session, spandsp_globals.ident);
}
if ((tmp = switch_channel_get_variable(channel, "fax_header"))) {
......@@ -1048,7 +1028,7 @@ static pvt_t *pvt_init(switch_core_session_t *session, mod_spandsp_fax_applicati
switch_safe_free(data);
} else {
pvt->header = switch_core_session_strdup(session, globals.header);
pvt->header = switch_core_session_strdup(session, spandsp_globals.header);
}
if (pvt->app_mode == FUNCTION_TX) {
......@@ -1118,10 +1098,10 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat
time = switch_time_now();
if (!(prefix = switch_channel_get_variable(channel, "fax_prefix"))) {
prefix = globals.prepend_string;
prefix = spandsp_globals.prepend_string;
}
if (!(pvt->filename = switch_core_session_sprintf(session, "%s/%s-%ld-%ld.tif", globals.spool, prefix, globals.total_sessions, time))) {
if (!(pvt->filename = switch_core_session_sprintf(session, "%s/%s-%ld-%ld.tif", spandsp_globals.spool, prefix, spandsp_globals.total_sessions, time))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot automatically set fax RX destination file\n");
goto done;
}
......@@ -1338,108 +1318,18 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat
}
}
/* **************************************************************************
CONFIGURATION
************************************************************************* */
void load_configuration(switch_bool_t reload)
{
switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, cfg = NULL;
if ((xml = switch_xml_open_cfg("fax.conf", &cfg, NULL))) {
if ((x_lists = switch_xml_child(cfg, "settings"))) {
for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) {
const char *name = switch_xml_attr(x_list, "name");
const char *value = switch_xml_attr(x_list, "value");
if (zstr(name)) {
continue;
}
if (zstr(value)) {
continue;
}
if (!strcmp(name, "use-ecm")) {
if (switch_true(value))
globals.use_ecm = 1;
else
globals.use_ecm = 0;
} else if (!strcmp(name, "verbose")) {
if (switch_true(value))
globals.verbose = 1;
else
globals.verbose = 0;
} else if (!strcmp(name, "disable-v17")) {
if (switch_true(value))
globals.disable_v17 = 1;
else
globals.disable_v17 = 0;
} else if (!strcmp(name, "enable-t38")) {
if (switch_true(value)) {
globals.enable_t38= 1;
} else {
globals.enable_t38 = 0;
}
} else if (!strcmp(name, "enable-t38-request")) {
if (switch_true(value)) {
globals.enable_t38_request = 1;
} else {
globals.enable_t38_request = 0;
}
} else if (!strcmp(name, "ident")) {
strncpy(globals.ident, value, sizeof(globals.ident) - 1);
} else if (!strcmp(name, "header")) {
strncpy(globals.header, value, sizeof(globals.header) - 1);
} else if (!strcmp(name, "spool-dir")) {
globals.spool = switch_core_strdup(globals.pool, value);
} else if (!strcmp(name, "file-prefix")) {
globals.prepend_string = switch_core_strdup(globals.pool, value);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown parameter %s\n", name);
}
}
}
switch_xml_free(xml);
}
}
void mod_spandsp_fax_event_handler(switch_event_t *event)
{
load_configuration(1);
}
void mod_spandsp_fax_load(switch_memory_pool_t *pool)
{
uint32_t sanity = 200;
memset(&globals, 0, sizeof(globals));
memset(&t38_state_list, 0, sizeof(t38_state_list));
globals.pool = pool;
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
switch_mutex_init(&t38_state_list.mutex, SWITCH_MUTEX_NESTED, globals.pool);
switch_mutex_init(&spandsp_globals.mutex, SWITCH_MUTEX_NESTED, spandsp_globals.pool);
switch_mutex_init(&t38_state_list.mutex, SWITCH_MUTEX_NESTED, spandsp_globals.pool);
switch_mutex_init(&globals.cond_mutex, SWITCH_MUTEX_NESTED, globals.pool);
switch_thread_cond_create(&globals.cond, globals.pool);
switch_mutex_init(&spandsp_globals.cond_mutex, SWITCH_MUTEX_NESTED, spandsp_globals.pool);
switch_thread_cond_create(&spandsp_globals.cond, spandsp_globals.pool);
globals.enable_t38 = 1;
globals.total_sessions = 0;
globals.verbose = 1;
globals.use_ecm = 1;
globals.disable_v17 = 0;
globals.prepend_string = switch_core_strdup(globals.pool, "fax");
globals.spool = switch_core_strdup(globals.pool, "/tmp");
strncpy(globals.ident, "SpanDSP Fax Ident", sizeof(globals.ident) - 1);
strncpy(globals.header, "SpanDSP Fax Header", sizeof(globals.header) - 1);
load_configuration(0);
launch_timer_thread();
while(--sanity && !t38_state_list.thread_running) {
......@@ -1454,7 +1344,7 @@ void mod_spandsp_fax_shutdown(void)
t38_state_list.thread_running = 0;
wake_thread(1);
switch_thread_join(&tstatus, t38_state_list.thread);
memset(&globals, 0, sizeof(globals));
memset(&spandsp_globals, 0, sizeof(spandsp_globals));
}
static const switch_state_handler_table_t t38_gateway_state_handlers;
......
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH mod_fax.
*
* The Initial Developer of the Original Code is
* Massimo Cetra <devel@navynet.it>
*
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Brian West <brian@freeswitch.org>
* Anthony Minessale II <anthm@freeswitch.org>
* Steve Underwood <steveu@coppice.org>
* mod_spandsp_modem.c -- t31 Soft Modem
*
*/
#include "mod_spandsp.h"
#include "mod_spandsp_modem.h"
#if defined(MODEM_SUPPORT)
#include <poll.h>
static struct {
int NEXT_ID;
int REF_COUNT;
int THREADCOUNT;
switch_memory_pool_t *pool;
switch_mutex_t *mutex;
modem_t MODEM_POOL[MAX_MODEMS];
int SOFT_MAX_MODEMS;
} globals;
struct modem_state {
int state;
char *name;
};
static struct modem_state MODEM_STATE[] = {
{MODEM_STATE_INIT, "INIT"},
{MODEM_STATE_ONHOOK, "ONHOOK"},
{MODEM_STATE_OFFHOOK, "OFFHOOK"},
{MODEM_STATE_ACQUIRED, "ACQUIRED"},
{MODEM_STATE_RINGING, "RINGING"},
{MODEM_STATE_ANSWERED, "ANSWERED"},
{MODEM_STATE_DIALING, "DIALING"},
{MODEM_STATE_CONNECTED, "CONNECTED"},
{MODEM_STATE_HANGUP, "HANGUP"},
{MODEM_STATE_LAST, "UNKNOWN"}
};
static modem_t *acquire_modem(int index);
static int t31_at_tx_handler(at_state_t *s, void *user_data, const uint8_t *buf, size_t len)
{
modem_t *modem = user_data;
ssize_t wrote;
wrote = write(modem->master, buf, len);
if (wrote != len) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to pass the full buffer onto the device file. %zd bytes of %ld written: %s\n",
wrote, len, strerror(errno));
if (wrote == -1) wrote = 0;
if (tcflush(modem->master, TCOFLUSH)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to flush pty master buffer: %s\n", strerror(errno));
} else if (tcflush(modem->slave, TCOFLUSH)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to flush pty slave buffer: %s\n", strerror(errno));
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Successfully flushed pty buffer\n");
}
}
return wrote;
}
static int t31_call_control_handler(t31_state_t *s, void *user_data, int op, const char *num)
{
modem_t *modem = user_data;
int ret = 0;
if (modem->control_handler) {
ret = modem->control_handler(modem, num, op);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DOH! NO CONTROL HANDLER INSTALLED\n");
}
return ret;
}
static modem_state_t modem_get_state(modem_t *modem)
{
modem_state_t state;
switch_mutex_lock(modem->mutex);
state = modem->state;
switch_mutex_unlock(modem->mutex);
return state;
}
static void _modem_set_state(modem_t *modem, modem_state_t state, const char *file, const char *func, int line)
{
switch_mutex_lock(modem->mutex);
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG,"Modem %s [%s] - Changing state to %s\n", modem->devlink,
modem_state2name(modem->state), modem_state2name(state));
modem->state = state;
switch_mutex_unlock(modem->mutex);
}
#define modem_set_state(_modem, _state) _modem_set_state(_modem, _state, __FILE__, __SWITCH_FUNC__, __LINE__)
char *modem_state2name(int state)
{
if (state > MODEM_STATE_LAST || state < 0) {
state = MODEM_STATE_LAST;
}
return MODEM_STATE[state].name;
}
int modem_close(modem_t *modem)
{
int r = 0;
switch_clear_flag(modem, MODEM_FLAG_RUNNING);
if (modem->master > -1) {
shutdown(modem->master, 2);
close(modem->master);
modem->master = -1;
r++;
}
if (modem->slave > -1) {
shutdown(modem->slave, 2);
close(modem->slave);
modem->slave = -1;
r++;
}
if (modem->t31_state) {
t31_free(modem->t31_state);
modem->t31_state = NULL;
}
unlink(modem->devlink);
switch_mutex_lock(globals.mutex);
globals.REF_COUNT--;
switch_mutex_unlock(globals.mutex);
return r;
}
int modem_init(modem_t *modem, modem_control_handler_t control_handler)
{
memset(modem, 0, sizeof(*modem));
modem->master = -1;
modem->slave = -1;
/* windows will have to try something like:
http://com0com.cvs.sourceforge.net/viewvc/com0com/com0com/ReadMe.txt?revision=RELEASED
*/
#if USE_OPENPTY
if (openpty(&modem->master, &modem->slave, NULL, NULL, NULL)) {
if (modem->master < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to initialize pty\n");
return -1;
}
modem->stty = ttyname(modem->slave);
#else
#if !defined(HAVE_POSIX_OPENPT)
modem->master = open("/dev/ptmx", O_RDWR);
#else
modem->master = posix_openpt(O_RDWR | O_NOCTTY);
#endif
if (modem->master < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to initialize UNIX98 master pty\n");
}
if (grantpt(modem->master) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to grant access to slave pty\n");
}
if (unlockpt(modem->master) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to unlock slave pty\n");
}
modem->stty = ptsname(modem->master);
if (modem->stty == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to obtain slave pty filename\n");
}
modem->slave = open(modem->stty, O_RDWR);
if (modem->slave < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to open slave pty %s\n", modem->stty);
}
#ifdef SOLARIS
ioctl(modem->slave, I_PUSH, "ptem"); /* push ptem */
ioctl(modem->slave, I_PUSH, "ldterm"); /* push ldterm*/
#endif
#endif
modem->slot = globals.NEXT_ID++;
snprintf(modem->devlink, sizeof(modem->devlink), "/dev/FS%d", modem->slot);
unlink(modem->devlink);
if (symlink(modem->stty, modem->devlink)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to create %s symbolic link\n", modem->devlink);
modem_close(modem);
return -1;
}
if (fcntl(modem->master, F_SETFL, fcntl(modem->master, F_GETFL, 0) | O_NONBLOCK)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot set up non-blocking read on %s\n", ttyname(modem->master));
modem_close(modem);
return -1;
}
if (!(modem->t31_state = t31_init(NULL, t31_at_tx_handler, modem, t31_call_control_handler, modem, NULL, NULL))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot initialize the T.31 modem\n");
modem_close(modem);
return -1;
}
if (spandsp_globals.modem_verbose) {
span_log_set_message_handler(&modem->t31_state->logging, spanfax_log_message);
span_log_set_message_handler(&modem->t31_state->audio.modems.v17_rx.logging, spanfax_log_message);
span_log_set_message_handler(&modem->t31_state->audio.modems.v29_rx.logging, spanfax_log_message);
span_log_set_message_handler(&modem->t31_state->audio.modems.v27ter_rx.logging, spanfax_log_message);
modem->t31_state->logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
modem->t31_state->audio.modems.v17_rx.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
modem->t31_state->audio.modems.v29_rx.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
modem->t31_state->audio.modems.v27ter_rx.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
}
modem->control_handler = control_handler;
modem->flags = 0;
switch_set_flag(modem, MODEM_FLAG_RUNNING);
switch_mutex_init(&modem->mutex, SWITCH_MUTEX_NESTED, globals.pool);
modem_set_state(modem, MODEM_STATE_INIT);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Modem [%s]->[%s] Ready\n", modem->devlink, modem->stty);
switch_mutex_lock(globals.mutex);
globals.REF_COUNT++;
switch_mutex_unlock(globals.mutex);
return 0;
}
static switch_endpoint_interface_t *modem_endpoint_interface = NULL;
struct private_object {
switch_mutex_t *mutex;
switch_core_session_t *session;
switch_channel_t *channel;
switch_codec_t read_codec;
switch_codec_t write_codec;
switch_frame_t read_frame;
unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
switch_timer_t timer;
modem_t *modem;
switch_caller_profile_t *caller_profile;
int dead;
};
typedef struct private_object private_t;
static switch_status_t channel_on_init(switch_core_session_t *session);
static switch_status_t channel_on_hangup(switch_core_session_t *session);
static switch_status_t channel_on_destroy(switch_core_session_t *session);
static switch_status_t channel_on_routing(switch_core_session_t *session);
static switch_status_t channel_on_exchange_media(switch_core_session_t *session);
static switch_status_t channel_on_soft_execute(switch_core_session_t *session);
static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
switch_caller_profile_t *outbound_profile,
switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
switch_call_cause_t *cancel_cause);
static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig);
/*
State methods they get called when the state changes to the specific state
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
*/
static switch_status_t channel_on_init(switch_core_session_t *session)
{
switch_channel_t *channel;
private_t *tech_pvt = NULL;
int to_ticks = 60, ring_ticks = 10, rt = ring_ticks;
int rest = 500000;
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
int tioflags;
char call_time[16];
char call_date[16];
switch_size_t retsize;
switch_time_exp_t tm;
switch_time_exp_lt(&tm, switch_micro_time_now());
switch_strftime(call_date, &retsize, sizeof(call_date), "%m%d", &tm);
switch_strftime(call_time, &retsize, sizeof(call_time), "%H%M", &tm);
ioctl(tech_pvt->modem->slave, TIOCMGET, &tioflags);
tioflags |= TIOCM_RI;
ioctl(tech_pvt->modem->slave, TIOCMSET, &tioflags);
at_reset_call_info(&tech_pvt->modem->t31_state->at_state);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "DATE", call_date);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "TIME", call_time);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "NAME", tech_pvt->caller_profile->caller_id_name);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "NMBR", tech_pvt->caller_profile->caller_id_number);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "ANID", tech_pvt->caller_profile->ani);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "USER", tech_pvt->caller_profile->username);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "CDID", tech_pvt->caller_profile->context);
at_set_call_info(&tech_pvt->modem->t31_state->at_state, "NDID", tech_pvt->caller_profile->destination_number);
modem_set_state(tech_pvt->modem, MODEM_STATE_RINGING);
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_ALERTING);
while(to_ticks > 0 && switch_channel_up(channel) && modem_get_state(tech_pvt->modem) == MODEM_STATE_RINGING) {
if (--rt <= 0) {
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_ALERTING);
rt = ring_ticks;
}
switch_yield(rest);
to_ticks--;
}
if (to_ticks < 1 || modem_get_state(tech_pvt->modem) != MODEM_STATE_ANSWERED) {
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_NO_ANSWER);
switch_channel_hangup(channel, SWITCH_CAUSE_NO_ANSWER);
} else {
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_ANSWERED);
modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED);
switch_channel_mark_answered(channel);
}
}
switch_channel_set_state(channel, CS_ROUTING);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_routing(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_execute(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_destroy(switch_core_session_t *session)
{
//switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
//channel = switch_core_session_get_channel(session);
//switch_assert(channel != NULL);
if ((tech_pvt = switch_core_session_get_private(session))) {
switch_core_timer_destroy(&tech_pvt->timer);
if (tech_pvt->modem) {
*tech_pvt->modem->uuid_str = '\0';
*tech_pvt->modem->digits = '\0';
modem_set_state(tech_pvt->modem, MODEM_STATE_ONHOOK);
tech_pvt->modem = NULL;
}
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_hangup(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_HANGUP);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
switch (sig) {
case SWITCH_SIG_BREAK:
break;
case SWITCH_SIG_KILL:
tech_pvt->dead = 1;
break;
default:
break;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL KILL\n", switch_channel_get_name(channel));
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_soft_execute(switch_core_session_t *session)
{
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL TRANSMIT\n");
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_exchange_media(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL MODEM\n");
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_reset(switch_core_session_t *session)
{
private_t *tech_pvt = (private_t *) switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s RESET\n",
switch_channel_get_name(switch_core_session_get_channel(session)));
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_hibernate(switch_core_session_t *session)
{
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s HIBERNATE\n",
switch_channel_get_name(switch_core_session_get_channel(session)));
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_on_consume_media(switch_core_session_t *session)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL CONSUME_MEDIA\n");
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf)
{
private_t *tech_pvt = NULL;
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
int r, samples_wanted, samples_read = 0;
int16_t *data;
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
if (tech_pvt->dead) return SWITCH_STATUS_FALSE;
data = tech_pvt->read_frame.data;
samples_wanted = tech_pvt->read_codec.implementation->samples_per_packet;
tech_pvt->read_frame.flags = SFF_NONE;
switch_core_timer_next(&tech_pvt->timer);
do {
r = t31_tx(tech_pvt->modem->t31_state, data + samples_read, samples_wanted - samples_read);
if (r < 0) break;
samples_read += r;
} while(samples_read < samples_wanted && r > 0);
if (r < 0) {
return SWITCH_STATUS_FALSE;
} else if (samples_read < samples_wanted) {
memset(data + samples_read, 0, sizeof(int16_t)*(samples_wanted - samples_read));
samples_read = samples_wanted;
}
tech_pvt->read_frame.samples = samples_read;
tech_pvt->read_frame.datalen = samples_read * 2;
*frame = &tech_pvt->read_frame;
return status;
}
static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
{
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
if (tech_pvt->dead) return SWITCH_STATUS_FALSE;
if (t31_rx(tech_pvt->modem->t31_state, frame->data, frame->datalen / 2)) {
status = SWITCH_STATUS_FALSE;
}
return status;
}
static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
{
switch_channel_t *channel;
private_t *tech_pvt;
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
switch (msg->message_id) {
case SWITCH_MESSAGE_INDICATE_ANSWER:
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_CONNECTED);
modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED);
break;
case SWITCH_MESSAGE_INDICATE_PROGRESS:
t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_CONNECTED);
modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED);
break;
case SWITCH_MESSAGE_INDICATE_RINGING:
break;
case SWITCH_MESSAGE_INDICATE_BRIDGE:
break;
case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
break;
default:
break;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t tech_init(private_t *tech_pvt, switch_core_session_t *session)
{
const char *iananame = "L16";
int rate = 8000;
int interval = 20;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_channel_t *channel = switch_core_session_get_channel(session);
const switch_codec_implementation_t *read_impl;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s setup codec %s/%d/%d\n", switch_channel_get_name(channel), iananame, rate,
interval);
status = switch_core_codec_init(&tech_pvt->read_codec,
iananame,
NULL,
rate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session));
if (status != SWITCH_STATUS_SUCCESS || !tech_pvt->read_codec.implementation || !switch_core_codec_ready(&tech_pvt->read_codec)) {
goto end;
}
status = switch_core_codec_init(&tech_pvt->write_codec,
iananame,
NULL,
rate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session));
if (status != SWITCH_STATUS_SUCCESS) {
switch_core_codec_destroy(&tech_pvt->read_codec);
goto end;
}
tech_pvt->read_frame.data = tech_pvt->databuf;
tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
tech_pvt->read_frame.codec = &tech_pvt->read_codec;
tech_pvt->read_frame.flags = SFF_NONE;
switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
switch_core_session_set_write_codec(session, &tech_pvt->write_codec);
read_impl = tech_pvt->read_codec.implementation;
switch_core_timer_init(&tech_pvt->timer, "soft",
read_impl->microseconds_per_packet / 1000, read_impl->samples_per_packet, switch_core_session_get_pool(session));
switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
switch_core_session_set_private(session, tech_pvt);
tech_pvt->session = session;
tech_pvt->channel = switch_core_session_get_channel(session);
end:
return status;
}
static void tech_attach(private_t *tech_pvt, modem_t *modem)
{
tech_pvt->modem = modem;
switch_set_string(modem->uuid_str, switch_core_session_get_uuid(tech_pvt->session));
switch_channel_set_variable_printf(tech_pvt->channel, "modem_slot", "%d", modem->slot);
switch_channel_set_variable(tech_pvt->channel, "modem_devlink", modem->devlink);
switch_channel_set_variable(tech_pvt->channel, "modem_digits", modem->digits);
}
static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
switch_caller_profile_t *outbound_profile,
switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
switch_call_cause_t *cancel_cause)
{
char name[128];
switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
if ((*new_session = switch_core_session_request(modem_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool)) != 0) {
private_t *tech_pvt;
switch_channel_t *channel;
switch_caller_profile_t *caller_profile;
char *dest = switch_core_session_strdup(*new_session, outbound_profile->destination_number);
char *modem_id_string = NULL;
char *number = NULL;
int modem_id = 0;
modem_t *modem = NULL;
if ((modem_id_string = dest)) {
if ((number = strchr(modem_id_string, '/'))) {
*number++ = '\0';
}
}
if (zstr(modem_id_string) || zstr(number)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_ERROR, "Invalid dial string.\n");
cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; goto fail;
}
if (!strcasecmp(modem_id_string, "a")) {
modem_id = -1;
} else {
modem_id = atoi(modem_id_string);
}
if (!(modem = acquire_modem(modem_id))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_ERROR, "Cannot find a modem.\n");
cause = SWITCH_CAUSE_USER_BUSY; goto fail;
}
switch_core_session_add_stream(*new_session, NULL);
if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) {
channel = switch_core_session_get_channel(*new_session);
switch_snprintf(name, sizeof(name), "modem/%d/%s", modem->slot, number);
switch_channel_set_name(channel, name);
if (tech_init(tech_pvt, *new_session) != SWITCH_STATUS_SUCCESS) {
cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail;
}
switch_set_string(modem->digits, number);
tech_attach(tech_pvt, modem);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n");
switch_core_session_destroy(new_session);
cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail;
}
if (outbound_profile) {
caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
caller_profile->source = switch_core_strdup(caller_profile->pool, "mod_spandsp");
switch_channel_set_caller_profile(channel, caller_profile);
tech_pvt->caller_profile = caller_profile;
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_ERROR, "Doh! no caller profile\n");
cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail;
}
switch_channel_set_state(channel, CS_INIT);
return SWITCH_CAUSE_SUCCESS;
fail:
if (new_session) {
switch_core_session_destroy(new_session);
}
if (modem) {
modem_set_state(modem, MODEM_STATE_ONHOOK);
}
}
return cause;
}
static switch_state_handler_table_t channel_event_handlers = {
/*.on_init */ channel_on_init,
/*.on_routing */ channel_on_routing,
/*.on_execute */ channel_on_execute,
/*.on_hangup */ channel_on_hangup,
/*.on_exchange_media */ channel_on_exchange_media,
/*.on_soft_execute */ channel_on_soft_execute,
/*.on_consume_media */ channel_on_consume_media,
/*.on_hibernate */ channel_on_hibernate,
/*.on_reset */ channel_on_reset,
/*.on_park */ NULL,
/*.on_reporting */ NULL,
/*.on_destroy */ channel_on_destroy
};
static switch_io_routines_t channel_io_routines = {
/*.outgoing_channel */ channel_outgoing_channel,
/*.read_frame */ channel_read_frame,
/*.write_frame */ channel_write_frame,
/*.kill_channel */ channel_kill_channel,
/*.send_dtmf */ channel_send_dtmf,
/*.receive_message */ channel_receive_message
};
static switch_status_t create_session(switch_core_session_t **new_session, modem_t *modem)
{
switch_status_t status = SWITCH_STATUS_FALSE;
switch_core_session_t *session;
switch_channel_t *channel;
private_t *tech_pvt = NULL;
char name[1024];
switch_caller_profile_t *caller_profile;
char *ani = NULL, *p, *digits = NULL;
if (!(session = switch_core_session_request(modem_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failure.\n");
goto end;
}
switch_core_session_add_stream(session, NULL);
channel = switch_core_session_get_channel(session);
tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(*tech_pvt));
p = switch_core_session_strdup(session, modem->digits);
if (*p == '*') {
ani = p + 1;
if ((digits = strchr(ani, '*'))) {
*digits++ = '\0';
} else {
ani = NULL;
}
}
if (zstr(digits)) {
digits = p;
}
if (zstr(ani)) {
ani = modem->devlink + 5;
}
switch_snprintf(name, sizeof(name), "modem/%d/%s", modem->slot, digits);
switch_channel_set_name(channel, name);
if (tech_init(tech_pvt, session) != SWITCH_STATUS_SUCCESS) {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_destroy(&session);
goto end;
}
caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
modem->devlink,
spandsp_globals.modem_dialplan,
"FSModem",
ani,
NULL,
ani,
NULL,
NULL,
"mod_spandsp",
spandsp_globals.modem_context,
digits);
caller_profile->source = switch_core_strdup(caller_profile->pool, "mod_spandsp");
switch_channel_set_caller_profile(channel, caller_profile);
tech_pvt->caller_profile = caller_profile;
switch_channel_set_state(channel, CS_INIT);
if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error spawning thread\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
goto end;
}
status = SWITCH_STATUS_SUCCESS;
tech_attach(tech_pvt, modem);
*new_session = session;
end:
return status;
}
static int control_handler(modem_t *modem, const char *num, int op)
{
switch_core_session_t *session = NULL;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Control Handler op:%d state:[%s] %s\n",
op, modem_state2name(modem_get_state(modem)), modem->devlink);
switch (op) {
case AT_MODEM_CONTROL_ANSWER:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - Answering\n", modem->devlink, modem_state2name(modem_get_state(modem)));
modem_set_state(modem, MODEM_STATE_ANSWERED);
break;
case AT_MODEM_CONTROL_CALL:
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - Dialing '%s'\n", modem->devlink, modem_state2name(modem_get_state(modem)), num);
modem_set_state(modem, MODEM_STATE_DIALING);
switch_clear_flag(modem, MODEM_FLAG_XOFF);
switch_set_string(modem->digits, num);
if (create_session(&session, modem) != SWITCH_STATUS_SUCCESS) {
t31_call_event(modem->t31_state, AT_CALL_EVENT_HANGUP);
} else {
switch_core_session_thread_launch(session);
}
}
break;
case AT_MODEM_CONTROL_OFFHOOK:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - Going off hook\n", modem->devlink, modem_state2name(modem_get_state(modem)));
modem_set_state(modem, MODEM_STATE_OFFHOOK);
break;
case AT_MODEM_CONTROL_ONHOOK:
case AT_MODEM_CONTROL_HANGUP:
{
int set_state = 1;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - Hanging up\n", modem->devlink, modem_state2name(modem_get_state(modem)));
switch_clear_flag(modem, MODEM_FLAG_XOFF);
modem_set_state(modem, MODEM_STATE_HANGUP);
if (!zstr(modem->uuid_str)) {
switch_core_session_t *session;
if ((session = switch_core_session_force_locate(modem->uuid_str))) {
switch_channel_t *channel = switch_core_session_get_channel(session);
if (switch_channel_up(channel)) {
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
set_state = 0;
}
switch_core_session_rwunlock(session);
}
}
if (set_state) {
modem_set_state(modem, MODEM_STATE_ONHOOK);
}
}
break;
case AT_MODEM_CONTROL_DTR:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - DTR %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num);
break;
case AT_MODEM_CONTROL_RTS:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - RTS %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num);
break;
case AT_MODEM_CONTROL_CTS:
{
u_char x[1];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1,
"Modem %s [%s] - CTS %s\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num ? "XON" : "XOFF");
if (num) {
x[0] = 0x11;
t31_at_tx_handler(&modem->t31_state->at_state, modem, x, 1);
switch_clear_flag(modem, MODEM_FLAG_XOFF);
} else {
x[0] = 0x13;
t31_at_tx_handler(&modem->t31_state->at_state, modem, x, 1);
switch_set_flag(modem, MODEM_FLAG_XOFF);
}
}
break;
case AT_MODEM_CONTROL_CAR:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - CAR %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num);
break;
case AT_MODEM_CONTROL_RNG:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - RNG %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num);
break;
case AT_MODEM_CONTROL_DSR:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - DSR %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num);
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Modem %s [%s] - operation %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), op);
break;
}
/*endswitch*/
return 0;
}
typedef enum {
MODEM_POLL_READ = (1 << 0),
MODEM_POLL_WRITE = (1 << 1),
MODEM_POLL_ERROR = (1 << 2)
} modem_poll_t;
static int modem_wait_sock(int sock, uint32_t ms, modem_poll_t flags)
{
struct pollfd pfds[2] = { { 0 } };
int s = 0, r = 0;
pfds[0].fd = sock;
if ((flags & MODEM_POLL_READ)) {
pfds[0].events |= POLLIN;
}
if ((flags & MODEM_POLL_WRITE)) {
pfds[0].events |= POLLOUT;
}
if ((flags & MODEM_POLL_ERROR)) {
pfds[0].events |= POLLERR;
}
s = poll(pfds, 1, ms);
if (s < 0) {
r = s;
} else if (s > 0) {
if ((pfds[0].revents & POLLIN)) {
r |= MODEM_POLL_READ;
}
if ((pfds[0].revents & POLLOUT)) {
r |= MODEM_POLL_WRITE;
}
if ((pfds[0].revents & POLLERR)) {
r |= MODEM_POLL_ERROR;
}
}
return r;
}
static void *SWITCH_THREAD_FUNC modem_thread(switch_thread_t *thread, void *obj)
{
modem_t *modem = obj;
int r, avail;
char buf[T31_TX_BUF_LEN], tmp[80];
switch_mutex_lock(globals.mutex);
modem_init(modem, control_handler);
globals.THREADCOUNT++;
switch_mutex_unlock(globals.mutex);
while (switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
r = modem_wait_sock(modem->master, -1, MODEM_POLL_READ | MODEM_POLL_ERROR);
if (!switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
break;
}
if (r < 0 || !(r & MODEM_POLL_READ) || (r & MODEM_POLL_ERROR)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Read on master [%s] [%d]\n", modem->devlink, r);
break;
}
modem->last_event = switch_time_now();
avail = sizeof(buf) - modem->t31_state->tx.in_bytes + modem->t31_state->tx.out_bytes - 1;
if (avail == 0 || switch_test_flag(modem, MODEM_FLAG_XOFF)) {
switch_yield(100000);
continue;
}
r = read(modem->master, buf, avail);
t31_at_rx(modem->t31_state, buf, r);
memset(tmp, 0, sizeof(tmp));
if (!strncasecmp(buf, "AT", 2)) {
int x;
strncpy(tmp, buf, r);
for(x = 0; x < r; x++) {
if(tmp[x] == '\r' || tmp[x] == '\n') {
tmp[x] = '\0';
}
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Command on %s [%s]\n", modem->devlink, tmp);
}
}
if (switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
modem_close(modem);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Thread ended for %s\n", modem->devlink);
switch_mutex_lock(globals.mutex);
globals.THREADCOUNT--;
switch_mutex_unlock(globals.mutex);
return NULL;
}
static void launch_modem_thread(modem_t *modem)
{
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, globals.pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, thd_attr, modem_thread, modem, globals.pool);
}
static void activate_modems(void)
{
int max = globals.SOFT_MAX_MODEMS;
int x;
switch_mutex_lock(globals.mutex);
memset(globals.MODEM_POOL, 0, MAX_MODEMS);
for(x = 0; x < max; x++) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Starting Modem SLOT %d\n", x);
launch_modem_thread(&globals.MODEM_POOL[x]);
}
switch_mutex_unlock(globals.mutex);
}
static void deactivate_modems(void)
{
int max = globals.SOFT_MAX_MODEMS;
int x;
switch_mutex_lock(globals.mutex);
for(x = 0; x < max; x++) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Stopping Modem SLOT %d\n", x);
modem_close(&globals.MODEM_POOL[x]);
}
switch_mutex_unlock(globals.mutex);
/* Wait for Threads to die */
while (globals.THREADCOUNT) {
switch_yield(100000);
}
}
static modem_t *acquire_modem(int index)
{
modem_t *modem = NULL;
switch_time_t now = switch_time_now();
int64_t idle_debounce = 2000000;
switch_mutex_lock(globals.mutex);
if (index > -1 && index < globals.SOFT_MAX_MODEMS) {
modem = &globals.MODEM_POOL[index];
} else {
int x;
for(x = 0; x < globals.SOFT_MAX_MODEMS; x++) {
if (globals.MODEM_POOL[x].state == MODEM_STATE_ONHOOK && (now - globals.MODEM_POOL[x].last_event) > idle_debounce) {
modem = &globals.MODEM_POOL[x];
break;
}
}
}
if (modem && (modem->state != MODEM_STATE_ONHOOK || (now - modem->last_event) < idle_debounce)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Modem %s In Use!\n", modem->devlink);
modem = NULL;
}
if (modem) {
modem_set_state(modem, MODEM_STATE_ACQUIRED);
modem->last_event = switch_time_now();
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Modems Available!\n");
}
switch_mutex_unlock(globals.mutex);
return modem;
}
switch_status_t modem_global_init(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
{
memset(&globals, 0, sizeof(globals));
globals.pool = pool;
globals.SOFT_MAX_MODEMS = 1;
if (spandsp_globals.modem_count > 0) {
globals.SOFT_MAX_MODEMS = spandsp_globals.modem_count;
}
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool);
modem_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
modem_endpoint_interface->interface_name = "modem";
modem_endpoint_interface->io_routines = &channel_io_routines;
modem_endpoint_interface->state_handler = &channel_event_handlers;
activate_modems();
return SWITCH_STATUS_SUCCESS;
}
void modem_global_shutdown(void)
{
deactivate_modems();
}
#else
void modem_global_init(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Modem support disabled\n");
}
void modem_global_shutdown(void)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Modem support disabled\n");
}
#endif
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH mod_fax.
*
* The Initial Developer of the Original Code is
* Massimo Cetra <devel@navynet.it>
*
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Brian West <brian@freeswitch.org>
* Anthony Minessale II <anthm@freeswitch.org>
* Steve Underwood <steveu@coppice.org>
* mod_spandsp_modem.h -- Fax modem applications provided by SpanDSP
*
*/
#include "switch_private.h"
#if defined(HAVE_OPENPTY) || defined(HAVE_DEV_PTMX) || defined(HAVE_POSIX_OPENPT)
#define MODEM_SUPPORT 1
#if !defined(HAVE_POSIX_OPENPT) && !defined(HAVE_DEV_PTMX)
#define USE_OPENPTY 1
#endif
#ifndef _MOD_SPANDSP_MODEM_H
#define _MOD_SPANDSP_MODEM_H
#include <stdio.h>
#include <string.h>
#include <pty.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <byteswap.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <tiffio.h>
#include <spandsp.h>
typedef enum {
MODEM_STATE_INIT,
MODEM_STATE_ONHOOK,
MODEM_STATE_OFFHOOK,
MODEM_STATE_ACQUIRED,
MODEM_STATE_RINGING,
MODEM_STATE_ANSWERED,
MODEM_STATE_DIALING,
MODEM_STATE_CONNECTED,
MODEM_STATE_HANGUP,
MODEM_STATE_LAST
} modem_state_t;
struct modem;
typedef int (*modem_control_handler_t)(struct modem *, const char *, int);
typedef enum {
MODEM_FLAG_RUNNING = ( 1 << 0),
MODEM_FLAG_XOFF = ( 1 << 1)
} modem_flags;
struct modem {
t31_state_t *t31_state;
char digits[512];
modem_flags flags;
int master;
int slave;
char *stty;
char devlink[128];
int id;
modem_state_t state;
modem_control_handler_t control_handler;
void *user_data;
switch_mutex_t *mutex;
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
switch_time_t last_event;
int slot;
};
typedef struct modem modem_t;
char *modem_state2name(int state);
int modem_close(struct modem *fm);
int modem_init(struct modem *fm, modem_control_handler_t control_handler);
#endif //MODEM_SUPPORT
switch_status_t modem_global_init(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool);
void modem_global_shutdown(void);
#endif //_MOD_SPANDSP_MODEM_H
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论