2011-12-06 17:51:27 -06:00
|
|
|
/*
|
|
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
2012-04-18 11:51:48 -05:00
|
|
|
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
|
2011-12-06 17:51:27 -06:00
|
|
|
*
|
|
|
|
* 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)
|
2011-12-07 23:06:00 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
#include <poll.h>
|
2011-12-07 23:06:00 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifndef WIN32
|
2011-12-09 23:46:39 -06:00
|
|
|
switch_size_t wrote;
|
2011-12-06 17:51:27 -06:00
|
|
|
wrote = write(modem->master, buf, len);
|
2011-12-09 15:10:55 -06:00
|
|
|
#else
|
2011-12-09 23:46:39 -06:00
|
|
|
DWORD wrote;
|
2011-12-09 15:10:55 -06:00
|
|
|
OVERLAPPED o;
|
|
|
|
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
|
|
|
|
/* Initialize the rest of the OVERLAPPED structure to zero. */
|
|
|
|
o.Internal = 0;
|
|
|
|
o.InternalHigh = 0;
|
|
|
|
o.Offset = 0;
|
|
|
|
o.OffsetHigh = 0;
|
|
|
|
assert(o.hEvent);
|
2011-12-09 23:46:39 -06:00
|
|
|
if (!WriteFile(modem->master, buf, (DWORD)len, &wrote, &o)) {
|
|
|
|
GetOverlappedResult(modem->master, &o, &wrote, TRUE);
|
2011-12-09 15:10:55 -06:00
|
|
|
}
|
|
|
|
CloseHandle (o.hEvent);
|
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
if (wrote != len) {
|
2011-12-07 09:28:36 -06:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to pass the full buffer onto the device file. "
|
|
|
|
"%"SWITCH_SSIZE_T_FMT " bytes of " "%"SWITCH_SIZE_T_FMT " written: %s\n", wrote, len, strerror(errno));
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
if (wrote == -1) wrote = 0;
|
|
|
|
|
2011-12-07 23:06:00 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
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");
|
|
|
|
}
|
2011-12-07 23:06:00 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
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;
|
2011-12-17 16:39:04 -05:00
|
|
|
switch_status_t was_running = switch_test_flag(modem, MODEM_FLAG_RUNNING);
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
switch_clear_flag(modem, MODEM_FLAG_RUNNING);
|
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifndef WIN32
|
2011-12-10 01:33:51 -06:00
|
|
|
if (modem->master > -1) {
|
2011-12-06 17:51:27 -06:00
|
|
|
shutdown(modem->master, 2);
|
|
|
|
close(modem->master);
|
2011-12-10 01:33:51 -06:00
|
|
|
modem->master = -1;
|
2011-12-09 15:10:55 -06:00
|
|
|
#else
|
2011-12-10 01:33:51 -06:00
|
|
|
if (modem->master) {
|
|
|
|
SetEvent(modem->threadAbort);
|
|
|
|
CloseHandle(modem->threadAbort);
|
2011-12-09 23:46:39 -06:00
|
|
|
CloseHandle(modem->master);
|
2011-12-10 01:33:51 -06:00
|
|
|
modem->master = 0;
|
2011-12-09 15:10:55 -06:00
|
|
|
#endif
|
2011-12-10 01:33:51 -06:00
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
if (was_running) {
|
|
|
|
switch_mutex_lock(globals.mutex);
|
|
|
|
globals.REF_COUNT--;
|
|
|
|
switch_mutex_unlock(globals.mutex);
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
switch_status_t modem_init(modem_t *modem, modem_control_handler_t control_handler)
|
2011-12-06 17:51:27 -06:00
|
|
|
{
|
2011-12-17 16:39:04 -05:00
|
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
2011-12-07 23:06:00 -06:00
|
|
|
#ifdef WIN32
|
2011-12-09 15:10:55 -06:00
|
|
|
COMMTIMEOUTS timeouts={0};
|
2011-12-07 23:06:00 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
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)) {
|
|
|
|
|
2012-05-08 19:25:14 -05:00
|
|
|
if (modem->master < 0) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: failed to initialize pty\n");
|
|
|
|
status = SWITCH_STATUS_FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2012-05-08 19:25:14 -05:00
|
|
|
modem->stty = ttyname(modem->slave);
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
#else
|
2011-12-09 15:10:55 -06:00
|
|
|
#if WIN32
|
|
|
|
modem->slot = 4+globals.NEXT_ID++; /* need work here we start at COM4 for now*/
|
|
|
|
snprintf(modem->devlink, sizeof(modem->devlink), "COM%d", modem->slot);
|
|
|
|
|
2011-12-09 23:46:39 -06:00
|
|
|
modem->master = CreateFile(modem->devlink,
|
2011-12-09 15:10:55 -06:00
|
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_FLAG_OVERLAPPED,
|
|
|
|
0);
|
2011-12-09 23:46:39 -06:00
|
|
|
if(modem->master==INVALID_HANDLE_VALUE) {
|
2011-12-17 16:39:04 -05:00
|
|
|
status = SWITCH_STATUS_FALSE;
|
2011-12-09 15:10:55 -06:00
|
|
|
if(GetLastError()==ERROR_FILE_NOT_FOUND) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: Serial port does not exist\n");
|
2011-12-17 16:39:04 -05:00
|
|
|
goto end;
|
2011-12-09 15:10:55 -06:00
|
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Fatal error: Serial port open error\n");
|
2011-12-17 16:39:04 -05:00
|
|
|
goto end;
|
2011-12-09 15:10:55 -06:00
|
|
|
}
|
|
|
|
#elif !defined(HAVE_POSIX_OPENPT)
|
2011-12-06 17:51:27 -06:00
|
|
|
modem->master = open("/dev/ptmx", O_RDWR);
|
|
|
|
#else
|
|
|
|
modem->master = posix_openpt(O_RDWR | O_NOCTTY);
|
|
|
|
#endif
|
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
|
|
|
}
|
2011-12-09 15:10:55 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
#ifdef SOLARIS
|
|
|
|
ioctl(modem->slave, I_PUSH, "ptem"); /* push ptem */
|
|
|
|
ioctl(modem->slave, I_PUSH, "ldterm"); /* push ldterm*/
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
2011-12-17 16:39:04 -05:00
|
|
|
status = SWITCH_STATUS_FALSE;
|
|
|
|
goto end;
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fcntl(modem->master, F_SETFL, fcntl(modem->master, F_GETFL, 0) | O_NONBLOCK)) {
|
2011-12-17 16:39:04 -05:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot set up non-blocking read on %s\n", ttyname(modem->master));
|
|
|
|
modem_close(modem);
|
|
|
|
status = SWITCH_STATUS_FALSE;
|
|
|
|
goto end;
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
2011-12-07 23:06:00 -06:00
|
|
|
#else
|
2011-12-09 15:10:55 -06:00
|
|
|
timeouts.ReadIntervalTimeout=50;
|
|
|
|
timeouts.ReadTotalTimeoutConstant=50;
|
|
|
|
timeouts.ReadTotalTimeoutMultiplier=10;
|
|
|
|
|
|
|
|
timeouts.WriteTotalTimeoutConstant=50;
|
|
|
|
timeouts.WriteTotalTimeoutMultiplier=10;
|
|
|
|
|
2011-12-10 01:33:51 -06:00
|
|
|
SetCommMask(modem->master, EV_RXCHAR);
|
|
|
|
|
2011-12-09 23:46:39 -06:00
|
|
|
if(!SetCommTimeouts(modem->master, &timeouts)){
|
2011-12-09 15:10:55 -06:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot set up non-blocking read on %s\n", modem->devlink);
|
2011-12-07 23:06:00 -06:00
|
|
|
modem_close(modem);
|
2011-12-17 16:39:04 -05:00
|
|
|
status = SWITCH_STATUS_FALSE;
|
|
|
|
goto end;
|
2011-12-07 23:06:00 -06:00
|
|
|
}
|
2011-12-10 01:33:51 -06:00
|
|
|
modem->threadAbort = CreateEvent(NULL, TRUE, FALSE, NULL);
|
2011-12-07 23:06:00 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
if (!(modem->t31_state = t31_init(NULL, t31_at_tx_handler, modem, t31_call_control_handler, modem, NULL, NULL))) {
|
2011-12-06 17:51:27 -06:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot initialize the T.31 modem\n");
|
|
|
|
modem_close(modem);
|
2011-12-17 16:39:04 -05:00
|
|
|
status = SWITCH_STATUS_FALSE;
|
|
|
|
goto end;
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
if (spandsp_globals.modem_verbose) {
|
2012-03-28 23:36:30 +08:00
|
|
|
span_log_set_message_handler(&modem->t31_state->logging, spanfax_log_message, NULL);
|
|
|
|
span_log_set_message_handler(&modem->t31_state->audio.modems.fast_modems.v17_rx.logging, spanfax_log_message, NULL);
|
|
|
|
span_log_set_message_handler(&modem->t31_state->audio.modems.fast_modems.v29_rx.logging, spanfax_log_message, NULL);
|
|
|
|
span_log_set_message_handler(&modem->t31_state->audio.modems.fast_modems.v27ter_rx.logging, spanfax_log_message, NULL);
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
modem->t31_state->logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
|
2012-03-28 23:36:30 +08:00
|
|
|
modem->t31_state->audio.modems.fast_modems.v17_rx.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
|
|
|
|
modem->t31_state->audio.modems.fast_modems.v29_rx.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
|
|
|
|
modem->t31_state->audio.modems.fast_modems.v27ter_rx.logging.level = SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW;
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2011-12-08 15:36:21 -06:00
|
|
|
switch_mutex_init(&modem->cond_mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
|
|
|
switch_thread_cond_create(&modem->cond, globals.pool);
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
end:
|
|
|
|
return status;
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
int tioflags;
|
2011-12-09 15:10:55 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
|
|
|
|
2011-12-07 23:06:00 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
ioctl(tech_pvt->modem->slave, TIOCMGET, &tioflags);
|
|
|
|
tioflags |= TIOCM_RI;
|
|
|
|
ioctl(tech_pvt->modem->slave, TIOCMSET, &tioflags);
|
2011-12-07 23:06:00 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
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;
|
2011-12-08 12:26:31 -06:00
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
2011-12-08 12:26:31 -06:00
|
|
|
mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE);
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
2011-12-08 12:26:31 -06:00
|
|
|
mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE);
|
2011-12-06 17:51:27 -06:00
|
|
|
break;
|
|
|
|
case SWITCH_MESSAGE_INDICATE_RINGING:
|
|
|
|
break;
|
|
|
|
case SWITCH_MESSAGE_INDICATE_BRIDGE:
|
2011-12-08 12:26:31 -06:00
|
|
|
mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE);
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
break;
|
|
|
|
case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
|
2011-12-08 12:26:31 -06:00
|
|
|
|
|
|
|
mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE);
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
2011-12-08 12:26:31 -06:00
|
|
|
switch_channel_export_variable(tech_pvt->channel, "rtp_autoflush_during_bridge", "false", SWITCH_EXPORT_VARS_VARIABLE);
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-12-08 15:36:21 -06:00
|
|
|
static void wake_modem_thread(modem_t *modem)
|
|
|
|
{
|
|
|
|
if (switch_mutex_trylock(modem->cond_mutex) == SWITCH_STATUS_SUCCESS) {
|
|
|
|
switch_thread_cond_signal(modem->cond);
|
|
|
|
switch_mutex_unlock(modem->cond_mutex);
|
|
|
|
}
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
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);
|
2011-12-08 15:36:21 -06:00
|
|
|
wake_modem_thread(modem);
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
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:
|
|
|
|
{
|
2011-12-13 14:01:00 -06:00
|
|
|
if (modem_get_state(modem) != MODEM_STATE_RINGING) {
|
|
|
|
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);
|
|
|
|
wake_modem_thread(modem);
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-13 14:01:00 -06:00
|
|
|
modem_set_state(modem, MODEM_STATE_HANGUP);
|
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-13 14:01:00 -06:00
|
|
|
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);
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
}
|
2011-12-13 14:01:00 -06:00
|
|
|
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-13 14:01:00 -06:00
|
|
|
if (set_state) {
|
|
|
|
modem_set_state(modem, MODEM_STATE_ONHOOK);
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
2011-12-08 15:36:21 -06:00
|
|
|
wake_modem_thread(modem);
|
2011-12-06 17:51:27 -06:00
|
|
|
} 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;
|
|
|
|
|
2011-12-07 23:06:00 -06:00
|
|
|
#ifndef WIN32
|
2011-12-06 17:51:27 -06:00
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
2011-12-07 23:06:00 -06:00
|
|
|
#else
|
2011-12-10 01:33:51 -06:00
|
|
|
static int modem_wait_sock(modem_t *modem, int ms, modem_poll_t flags)
|
2011-12-07 23:06:00 -06:00
|
|
|
{
|
2011-12-09 15:10:55 -06:00
|
|
|
/* this method ignores ms and waits infinitely */
|
2011-12-10 01:33:51 -06:00
|
|
|
DWORD dwEvtMask, dwWait;
|
2011-12-09 15:10:55 -06:00
|
|
|
OVERLAPPED o;
|
|
|
|
BOOL result;
|
2011-12-10 01:33:51 -06:00
|
|
|
int ret = MODEM_POLL_ERROR;
|
|
|
|
HANDLE arHandles[2];
|
2011-12-07 23:06:00 -06:00
|
|
|
|
2011-12-10 01:33:51 -06:00
|
|
|
arHandles[0] = modem->threadAbort;
|
2011-12-07 23:06:00 -06:00
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
2011-12-10 01:33:51 -06:00
|
|
|
arHandles[1] = o.hEvent;
|
2011-12-09 15:10:55 -06:00
|
|
|
|
|
|
|
/* Initialize the rest of the OVERLAPPED structure to zero. */
|
|
|
|
o.Internal = 0;
|
|
|
|
o.InternalHigh = 0;
|
|
|
|
o.Offset = 0;
|
|
|
|
o.OffsetHigh = 0;
|
|
|
|
assert(o.hEvent);
|
|
|
|
|
2011-12-10 01:33:51 -06:00
|
|
|
result = WaitCommEvent(modem->master, &dwEvtMask, &o);
|
2011-12-09 15:10:55 -06:00
|
|
|
|
|
|
|
if (result == 0)
|
|
|
|
{
|
|
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
|
|
/* something went horribly wrong with WaitCommEvent(), so
|
|
|
|
clear all errors and try again */
|
|
|
|
DWORD comerrors;
|
2011-12-10 01:33:51 -06:00
|
|
|
ClearCommError(modem->master,&comerrors,0);
|
2011-12-09 15:10:55 -06:00
|
|
|
} else {
|
|
|
|
/* IO is pending, wait for it to finish */
|
2011-12-10 01:33:51 -06:00
|
|
|
dwWait = WaitForMultipleObjects(2, arHandles, FALSE, INFINITE);
|
|
|
|
if (dwWait == WAIT_OBJECT_0 + 1) {
|
|
|
|
ret = MODEM_POLL_READ;
|
|
|
|
}
|
2011-12-07 23:06:00 -06:00
|
|
|
}
|
|
|
|
}
|
2011-12-11 21:07:17 -06:00
|
|
|
else {
|
|
|
|
ret = MODEM_POLL_READ;
|
|
|
|
}
|
2011-12-07 23:06:00 -06:00
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
CloseHandle (o.hEvent);
|
2011-12-10 01:33:51 -06:00
|
|
|
return ret;
|
2011-12-07 23:06:00 -06:00
|
|
|
}
|
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
static void *SWITCH_THREAD_FUNC modem_thread(switch_thread_t *thread, void *obj)
|
|
|
|
{
|
|
|
|
modem_t *modem = obj;
|
|
|
|
int r, avail;
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifdef WIN32
|
|
|
|
DWORD readBytes;
|
|
|
|
OVERLAPPED o;
|
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
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);
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
if (switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
|
|
|
|
switch_mutex_lock(modem->cond_mutex);
|
|
|
|
|
|
|
|
while (switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
|
2011-12-08 15:36:21 -06:00
|
|
|
|
2011-12-10 01:33:51 -06:00
|
|
|
#ifndef WIN32
|
2011-12-17 16:39:04 -05:00
|
|
|
r = modem_wait_sock(modem->master, -1, MODEM_POLL_READ | MODEM_POLL_ERROR);
|
2011-12-10 01:33:51 -06:00
|
|
|
#else
|
2011-12-17 16:39:04 -05:00
|
|
|
r = modem_wait_sock(modem, -1, MODEM_POLL_READ | MODEM_POLL_ERROR);
|
2011-12-10 01:33:51 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
if (!switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
|
|
|
|
break;
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
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;
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-08 15:36:21 -06:00
|
|
|
modem->last_event = switch_time_now();
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
if (switch_test_flag(modem, MODEM_FLAG_XOFF)) {
|
|
|
|
switch_thread_cond_wait(modem->cond, modem->cond_mutex);
|
|
|
|
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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Buffer Full, retrying....\n");
|
|
|
|
switch_yield(10000);
|
|
|
|
continue;
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-09 15:10:55 -06:00
|
|
|
#ifndef WIN32
|
2011-12-17 16:39:04 -05:00
|
|
|
r = read(modem->master, buf, avail);
|
2011-12-09 15:10:55 -06:00
|
|
|
#else
|
2011-12-17 16:39:04 -05:00
|
|
|
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
|
|
|
|
/* Initialize the rest of the OVERLAPPED structure to zero. */
|
|
|
|
o.Internal = 0;
|
|
|
|
o.InternalHigh = 0;
|
|
|
|
o.Offset = 0;
|
|
|
|
o.OffsetHigh = 0;
|
|
|
|
assert(o.hEvent);
|
|
|
|
if (!ReadFile(modem->master, buf, avail, &readBytes, &o)) {
|
|
|
|
GetOverlappedResult(modem->master,&o,&readBytes,TRUE);
|
|
|
|
}
|
|
|
|
CloseHandle (o.hEvent);
|
|
|
|
r = readBytes;
|
2011-12-09 15:10:55 -06:00
|
|
|
#endif
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
t31_at_rx(modem->t31_state, buf, r);
|
2011-12-06 17:51:27 -06:00
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
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';
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Command on %s [%s]\n", modem->devlink, tmp);
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
switch_mutex_unlock(modem->cond_mutex);
|
2011-12-08 15:36:21 -06:00
|
|
|
|
2011-12-17 16:39:04 -05:00
|
|
|
if (switch_test_flag(modem, MODEM_FLAG_RUNNING)) {
|
|
|
|
modem_close(modem);
|
|
|
|
}
|
2011-12-06 17:51:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2011-12-07 05:13:08 -06:00
|
|
|
globals.SOFT_MAX_MODEMS = spandsp_globals.modem_count;
|
2011-12-06 17:51:27 -06:00
|
|
|
|
|
|
|
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
|