| File: | src/mod/applications/mod_spandsp/mod_spandsp_modem.c | 
| Location: | line 284, column 17 | 
| Description: | Null pointer passed as an argument to a 'nonnull' parameter | 
| 1 | /* | |||
| 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application | |||
| 3 | * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> | |||
| 4 | * | |||
| 5 | * Version: MPL 1.1 | |||
| 6 | * | |||
| 7 | * The contents of this file are subject to the Mozilla Public License Version | |||
| 8 | * 1.1 (the "License"); you may not use this file except in compliance with | |||
| 9 | * the License. You may obtain a copy of the License at | |||
| 10 | * http://www.mozilla.org/MPL/ | |||
| 11 | * | |||
| 12 | * Software distributed under the License is distributed on an "AS IS" basis, | |||
| 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
| 14 | * for the specific language governing rights and limitations under the | |||
| 15 | * License. | |||
| 16 | * | |||
| 17 | * The Original Code is FreeSWITCH mod_fax. | |||
| 18 | * | |||
| 19 | * The Initial Developer of the Original Code is | |||
| 20 | * Massimo Cetra <devel@navynet.it> | |||
| 21 | * | |||
| 22 | * Portions created by the Initial Developer are Copyright (C) | |||
| 23 | * the Initial Developer. All Rights Reserved. | |||
| 24 | * | |||
| 25 | * Contributor(s): | |||
| 26 | * | |||
| 27 | * Brian West <brian@freeswitch.org> | |||
| 28 | * Anthony Minessale II <anthm@freeswitch.org> | |||
| 29 | * Steve Underwood <steveu@coppice.org> | |||
| 30 | * mod_spandsp_modem.c -- t31 Soft Modem | |||
| 31 | * | |||
| 32 | */ | |||
| 33 | ||||
| 34 | #include "mod_spandsp.h" | |||
| 35 | #include "udptl.h" | |||
| 36 | #include "mod_spandsp_modem.h" | |||
| 37 | ||||
| 38 | #if defined(MODEM_SUPPORT1) | |||
| 39 | #ifndef WIN32 | |||
| 40 | #include <poll.h> | |||
| 41 | #endif | |||
| 42 | ||||
| 43 | #define LOCAL_FAX_MAX_DATAGRAM400 400 | |||
| 44 | #define MAX_FEC_ENTRIES4 4 | |||
| 45 | #define MAX_FEC_SPAN4 4 | |||
| 46 | #define DEFAULT_FEC_ENTRIES3 3 | |||
| 47 | #define DEFAULT_FEC_SPAN3 3 | |||
| 48 | ||||
| 49 | static struct { | |||
| 50 | int REF_COUNT; | |||
| 51 | int THREADCOUNT; | |||
| 52 | switch_memory_pool_t *pool; | |||
| 53 | switch_mutex_t *mutex; | |||
| 54 | modem_t MODEM_POOL[MAX_MODEMS1024]; | |||
| 55 | int SOFT_MAX_MODEMS; | |||
| 56 | } globals; | |||
| 57 | ||||
| 58 | struct modem_state { | |||
| 59 | int state; | |||
| 60 | char *name; | |||
| 61 | }; | |||
| 62 | ||||
| 63 | static struct modem_state MODEM_STATE[] = { | |||
| 64 | {MODEM_STATE_INIT, "INIT"}, | |||
| 65 | {MODEM_STATE_ONHOOK, "ONHOOK"}, | |||
| 66 | {MODEM_STATE_OFFHOOK, "OFFHOOK"}, | |||
| 67 | {MODEM_STATE_ACQUIRED, "ACQUIRED"}, | |||
| 68 | {MODEM_STATE_RINGING, "RINGING"}, | |||
| 69 | {MODEM_STATE_ANSWERED, "ANSWERED"}, | |||
| 70 | {MODEM_STATE_DIALING, "DIALING"}, | |||
| 71 | {MODEM_STATE_CONNECTED, "CONNECTED"}, | |||
| 72 | {MODEM_STATE_HANGUP, "HANGUP"}, | |||
| 73 | {MODEM_STATE_LAST, "UNKNOWN"} | |||
| 74 | }; | |||
| 75 | ||||
| 76 | ||||
| 77 | static modem_t *acquire_modem(int index); | |||
| 78 | ||||
| 79 | static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count) | |||
| 80 | { | |||
| 81 | return 0; | |||
| 82 | } | |||
| 83 | ||||
| 84 | static int t31_at_tx_handler(void *user_data, const uint8_t *buf, size_t len) | |||
| 85 | { | |||
| 86 | modem_t *modem = user_data; | |||
| 87 | ||||
| 88 | #ifndef WIN32 | |||
| 89 | switch_size_t wrote; | |||
| 90 | wrote = write(modem->master, buf, len); | |||
| 91 | #else | |||
| 92 | DWORD wrote; | |||
| 93 | OVERLAPPED o; | |||
| 94 | o.hEvent = CreateEvent(NULL((void*)0), TRUE(!0), FALSE0, NULL((void*)0)); | |||
| 95 | ||||
| 96 | /* Initialize the rest of the OVERLAPPED structure to zero. */ | |||
| 97 | o.Internal = 0; | |||
| 98 | o.InternalHigh = 0; | |||
| 99 | o.Offset = 0; | |||
| 100 | o.OffsetHigh = 0; | |||
| 101 | assert(o.hEvent)((o.hEvent) ? (void) (0) : __assert_fail ("o.hEvent", "mod_spandsp_modem.c" , 101, __PRETTY_FUNCTION__)); | |||
| 102 | if (!WriteFile(modem->master, buf, (DWORD)len, &wrote, &o)) { | |||
| 103 | GetOverlappedResult(modem->master, &o, &wrote, TRUE(!0)); | |||
| 104 | } | |||
| 105 | CloseHandle (o.hEvent); | |||
| 106 | #endif | |||
| 107 | ||||
| 108 | if (wrote != len) { | |||
| 109 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 109, ((void*)0), SWITCH_LOG_ERROR, "Unable to pass the full buffer onto the device file. " | |||
| 110 | "%"SWITCH_SSIZE_T_FMT"ld" " bytes of " "%"SWITCH_SIZE_T_FMT"ld" " written: %s\n", wrote, len, strerror(errno(*__errno_location ()))); | |||
| 111 | ||||
| 112 | if (wrote == -1) wrote = 0; | |||
| 113 | ||||
| 114 | #ifndef WIN32 | |||
| 115 | if (tcflush(modem->master, TCOFLUSH1)) { | |||
| 116 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 116, ((void*)0), SWITCH_LOG_ERROR, "Unable to flush pty master buffer: %s\n", strerror(errno(*__errno_location ()))); | |||
| 117 | } else if (tcflush(modem->slave, TCOFLUSH1)) { | |||
| 118 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 118, ((void*)0), SWITCH_LOG_ERROR, "Unable to flush pty slave buffer: %s\n", strerror(errno(*__errno_location ()))); | |||
| 119 | } else { | |||
| 120 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 120, ((void*)0), SWITCH_LOG_DEBUG, "Successfully flushed pty buffer\n"); | |||
| 121 | } | |||
| 122 | #endif | |||
| 123 | } | |||
| 124 | return wrote; | |||
| 125 | } | |||
| 126 | ||||
| 127 | static int t31_call_control_handler(t31_state_t *s, void *user_data, int op, const char *num) | |||
| 128 | { | |||
| 129 | modem_t *modem = user_data; | |||
| 130 | int ret = 0; | |||
| 131 | ||||
| 132 | if (modem->control_handler) { | |||
| 133 | ret = modem->control_handler(modem, num, op); | |||
| 134 | } else { | |||
| 135 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 135, ((void*)0), SWITCH_LOG_ERROR, "DOH! NO CONTROL HANDLER INSTALLED\n"); | |||
| 136 | } | |||
| 137 | ||||
| 138 | return ret; | |||
| 139 | } | |||
| 140 | ||||
| 141 | static modem_state_t modem_get_state(modem_t *modem) | |||
| 142 | { | |||
| 143 | modem_state_t state; | |||
| 144 | ||||
| 145 | switch_mutex_lock(modem->mutex); | |||
| 146 | state = modem->state; | |||
| 147 | switch_mutex_unlock(modem->mutex); | |||
| 148 | ||||
| 149 | return state; | |||
| 150 | } | |||
| 151 | ||||
| 152 | static void _modem_set_state(modem_t *modem, modem_state_t state, const char *file, const char *func, int line) | |||
| 153 | { | |||
| 154 | ||||
| 155 | switch_mutex_lock(modem->mutex); | |||
| 156 | switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL((void*)0), SWITCH_LOG_DEBUG,"Modem %s [%s] - Changing state to %s\n", modem->devlink, | |||
| 157 | modem_state2name(modem->state), modem_state2name(state)); | |||
| 158 | modem->state = state; | |||
| 159 | switch_mutex_unlock(modem->mutex); | |||
| 160 | } | |||
| 161 | #define modem_set_state(_modem, _state)_modem_set_state(_modem, _state, "mod_spandsp_modem.c", (const char *)__func__, 161) _modem_set_state(_modem, _state, __FILE__"mod_spandsp_modem.c", __SWITCH_FUNC__(const char *)__func__, __LINE__161) | |||
| 162 | ||||
| 163 | char *modem_state2name(int state) | |||
| 164 | { | |||
| 165 | if (state > MODEM_STATE_LAST || state < 0) { | |||
| 166 | state = MODEM_STATE_LAST; | |||
| 167 | } | |||
| 168 | ||||
| 169 | return MODEM_STATE[state].name; | |||
| 170 | } | |||
| 171 | ||||
| 172 | int modem_close(modem_t *modem) | |||
| 173 | { | |||
| 174 | int r = 0; | |||
| 175 | switch_status_t was_running = switch_test_flag(modem, MODEM_FLAG_RUNNING)((modem)->flags & MODEM_FLAG_RUNNING); | |||
| 176 | ||||
| 177 | switch_clear_flag(modem, MODEM_FLAG_RUNNING)(modem)->flags &= ~(MODEM_FLAG_RUNNING); | |||
| 178 | ||||
| 179 | #ifndef WIN32 | |||
| 180 | if (modem->master > -1) { | |||
| 181 | shutdown(modem->master, 2); | |||
| 182 | close(modem->master); | |||
| 183 | modem->master = -1; | |||
| 184 | #else | |||
| 185 | if (modem->master) { | |||
| 186 | SetEvent(modem->threadAbort); | |||
| 187 | CloseHandle(modem->threadAbort); | |||
| 188 | CloseHandle(modem->master); | |||
| 189 | modem->master = 0; | |||
| 190 | #endif | |||
| 191 | r++; | |||
| 192 | } | |||
| 193 | ||||
| 194 | if (modem->slave > -1) { | |||
| 195 | shutdown(modem->slave, 2); | |||
| 196 | close(modem->slave); | |||
| 197 | modem->slave = -1; | |||
| 198 | r++; | |||
| 199 | } | |||
| 200 | ||||
| 201 | if (modem->t31_state) { | |||
| 202 | t31_free(modem->t31_state); | |||
| 203 | modem->t31_state = NULL((void*)0); | |||
| 204 | } | |||
| 205 | ||||
| 206 | unlink(modem->devlink); | |||
| 207 | ||||
| 208 | if (was_running) { | |||
| 209 | switch_mutex_lock(globals.mutex); | |||
| 210 | globals.REF_COUNT--; | |||
| 211 | switch_mutex_unlock(globals.mutex); | |||
| 212 | } | |||
| 213 | ||||
| 214 | return r; | |||
| 215 | } | |||
| 216 | ||||
| 217 | switch_status_t modem_init(modem_t *modem, modem_control_handler_t control_handler) | |||
| 218 | { | |||
| 219 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
| 220 | #ifdef WIN32 | |||
| 221 | COMMTIMEOUTS timeouts = {0}; | |||
| 222 | #endif | |||
| 223 | logging_state_t *logging; | |||
| 224 | ||||
| 225 | modem->master = -1; | |||
| 226 | modem->slave = -1; | |||
| 227 | ||||
| 228 | /* windows will have to try something like: | |||
| 229 | http://com0com.cvs.sourceforge.net/viewvc/com0com/com0com/ReadMe.txt?revision=RELEASED | |||
| 230 | */ | |||
| 231 | ||||
| 232 | #if USE_OPENPTY | |||
| 233 | if (openpty(&modem->master, &modem->slave, NULL((void*)0), NULL((void*)0), NULL((void*)0))) { | |||
| 234 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 234, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to initialize pty\n"); | |||
| 235 | status = SWITCH_STATUS_FALSE; | |||
| 236 | goto end; | |||
| 237 | } | |||
| 238 | ||||
| 239 | modem->stty = ttyname(modem->slave); | |||
| 240 | #else | |||
| 241 | #ifdef WIN32 | |||
| 242 | snprintf(modem->devlink, sizeof(modem->devlink), "COM%d", modem->slot); | |||
| 243 | ||||
| 244 | modem->master = CreateFile(modem->devlink, | |||
| 245 | GENERIC_READ | GENERIC_WRITE, | |||
| 246 | 0, | |||
| 247 | 0, | |||
| 248 | OPEN_EXISTING, | |||
| 249 | FILE_FLAG_OVERLAPPED, | |||
| 250 | 0); | |||
| 251 | if (modem->master == INVALID_HANDLE_VALUE) { | |||
| 252 | status = SWITCH_STATUS_FALSE; | |||
| 253 | if (GetLastError() == ERROR_FILE_NOT_FOUND) { | |||
| 254 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 254, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: Serial port does not exist\n"); | |||
| 255 | goto end; | |||
| 256 | } | |||
| 257 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 257, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: Serial port open error\n"); | |||
| 258 | goto end; | |||
| 259 | } | |||
| 260 | #elif !defined(HAVE_POSIX_OPENPT1) | |||
| 261 | modem->master = open("/dev/ptmx", O_RDWR02); | |||
| 262 | #else | |||
| 263 | modem->master = posix_openpt(O_RDWR02 | O_NOCTTY0400); | |||
| 264 | #endif | |||
| 265 | ||||
| 266 | #ifndef WIN32 | |||
| 267 | if (modem->master < 0) { | |||
| 268 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 268, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to initialize UNIX98 master pty\n"); | |||
| 269 | } | |||
| 270 | ||||
| 271 | if (grantpt(modem->master) < 0) { | |||
| 272 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 272, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to grant access to slave pty\n"); | |||
| 273 | } | |||
| 274 | ||||
| 275 | if (unlockpt(modem->master) < 0) { | |||
| 276 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 276, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to unlock slave pty\n"); | |||
| 277 | } | |||
| 278 | ||||
| 279 | modem->stty = ptsname(modem->master); | |||
| 280 | if (modem->stty == NULL((void*)0)) { | |||
| 281 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 281, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to obtain slave pty filename\n"); | |||
| 282 | } | |||
| 283 | ||||
| 284 | modem->slave = open(modem->stty, O_RDWR02); | |||
| 
 | ||||
| 285 | if (modem->slave < 0) { | |||
| 286 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 286, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to open slave pty %s\n", modem->stty); | |||
| 287 | } | |||
| 288 | #endif | |||
| 289 | ||||
| 290 | #ifdef SOLARIS | |||
| 291 | ioctl(modem->slave, I_PUSH, "ptem"); /* push ptem */ | |||
| 292 | ioctl(modem->slave, I_PUSH, "ldterm"); /* push ldterm*/ | |||
| 293 | #endif | |||
| 294 | #endif | |||
| 295 | ||||
| 296 | #ifndef WIN32 | |||
| 297 | snprintf(modem->devlink, sizeof(modem->devlink), "%s/FS%d", spandsp_globals.modem_directory, modem->slot); | |||
| 298 | ||||
| 299 | unlink(modem->devlink); | |||
| 300 | ||||
| 301 | if (symlink(modem->stty, modem->devlink)) { | |||
| 302 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 302, ((void*)0), SWITCH_LOG_ERROR, "Fatal error: failed to create %s symbolic link\n", modem->devlink); | |||
| 303 | modem_close(modem); | |||
| 304 | status = SWITCH_STATUS_FALSE; | |||
| 305 | goto end; | |||
| 306 | } | |||
| 307 | ||||
| 308 | if (fcntl(modem->master, F_SETFL4, fcntl(modem->master, F_GETFL3, 0) | O_NONBLOCK04000)) { | |||
| 309 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 309, ((void*)0), SWITCH_LOG_ERROR, "Cannot set up non-blocking read on %s\n", ttyname(modem->master)); | |||
| 310 | modem_close(modem); | |||
| 311 | status = SWITCH_STATUS_FALSE; | |||
| 312 | goto end; | |||
| 313 | } | |||
| 314 | #else | |||
| 315 | timeouts.ReadIntervalTimeout = 50; | |||
| 316 | timeouts.ReadTotalTimeoutConstant = 50; | |||
| 317 | timeouts.ReadTotalTimeoutMultiplier = 10; | |||
| 318 | ||||
| 319 | timeouts.WriteTotalTimeoutConstant = 50; | |||
| 320 | timeouts.WriteTotalTimeoutMultiplier = 10; | |||
| 321 | ||||
| 322 | SetCommMask(modem->master, EV_RXCHAR); | |||
| 323 | ||||
| 324 | if (!SetCommTimeouts(modem->master, &timeouts)) { | |||
| 325 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 325, ((void*)0), SWITCH_LOG_ERROR, "Cannot set up non-blocking read on %s\n", modem->devlink); | |||
| 326 | modem_close(modem); | |||
| 327 | status = SWITCH_STATUS_FALSE; | |||
| 328 | goto end; | |||
| 329 | } | |||
| 330 | modem->threadAbort = CreateEvent(NULL((void*)0), TRUE(!0), FALSE0, NULL((void*)0)); | |||
| 331 | #endif | |||
| 332 | ||||
| 333 | if (!(modem->t31_state = t31_init(NULL((void*)0), t31_at_tx_handler, modem, t31_call_control_handler, modem, t38_tx_packet_handler, modem))) { | |||
| 334 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 334, ((void*)0), SWITCH_LOG_ERROR, "Cannot initialize the T.31 modem\n"); | |||
| 335 | modem_close(modem); | |||
| 336 | status = SWITCH_STATUS_FALSE; | |||
| 337 | goto end; | |||
| 338 | } | |||
| 339 | modem->t38_core = t31_get_t38_core_state(modem->t31_state); | |||
| 340 | ||||
| 341 | if (spandsp_globals.modem_verbose) { | |||
| 342 | logging = t31_get_logging_state(modem->t31_state); | |||
| 343 | span_log_set_message_handler(logging, mod_spandsp_log_message, NULL((void*)0)); | |||
| 344 | span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); | |||
| 345 | ||||
| 346 | logging = v17_rx_get_logging_state(&modem->t31_state->audio.modems.fast_modems.v17_rx); | |||
| 347 | span_log_set_message_handler(logging, mod_spandsp_log_message, NULL((void*)0)); | |||
| 348 | span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); | |||
| 349 | ||||
| 350 | logging = v29_rx_get_logging_state(&modem->t31_state->audio.modems.fast_modems.v29_rx); | |||
| 351 | span_log_set_message_handler(logging, mod_spandsp_log_message, NULL((void*)0)); | |||
| 352 | span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); | |||
| 353 | ||||
| 354 | logging = v27ter_rx_get_logging_state(&modem->t31_state->audio.modems.fast_modems.v27ter_rx); | |||
| 355 | span_log_set_message_handler(logging, mod_spandsp_log_message, NULL((void*)0)); | |||
| 356 | span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); | |||
| 357 | ||||
| 358 | logging = t38_core_get_logging_state(modem->t38_core); | |||
| 359 | span_log_set_message_handler(logging, mod_spandsp_log_message, NULL((void*)0)); | |||
| 360 | span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); | |||
| 361 | } | |||
| 362 | ||||
| 363 | modem->control_handler = control_handler; | |||
| 364 | modem->flags = 0; | |||
| 365 | switch_set_flag(modem, MODEM_FLAG_RUNNING)(modem)->flags |= (MODEM_FLAG_RUNNING); | |||
| 366 | ||||
| 367 | switch_mutex_init(&modem->mutex, SWITCH_MUTEX_NESTED0x1, globals.pool); | |||
| 368 | switch_mutex_init(&modem->cond_mutex, SWITCH_MUTEX_NESTED0x1, globals.pool); | |||
| 369 | switch_thread_cond_create(&modem->cond, globals.pool); | |||
| 370 | ||||
| 371 | modem_set_state(modem, MODEM_STATE_INIT)_modem_set_state(modem, MODEM_STATE_INIT, "mod_spandsp_modem.c" , (const char *)__func__, 371); | |||
| 372 | ||||
| 373 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 373, ((void*)0), SWITCH_LOG_INFO, "Modem [%s]->[%s] Ready\n", modem->devlink, modem->stty); | |||
| 374 | ||||
| 375 | switch_mutex_lock(globals.mutex); | |||
| 376 | globals.REF_COUNT++; | |||
| 377 | switch_mutex_unlock(globals.mutex); | |||
| 378 | ||||
| 379 | end: | |||
| 380 | return status; | |||
| 381 | } | |||
| 382 | ||||
| 383 | static switch_endpoint_interface_t *modem_endpoint_interface = NULL((void*)0); | |||
| 384 | ||||
| 385 | struct private_object { | |||
| 386 | switch_mutex_t *mutex; | |||
| 387 | switch_core_session_t *session; | |||
| 388 | switch_channel_t *channel; | |||
| 389 | switch_codec_t read_codec; | |||
| 390 | switch_codec_t write_codec; | |||
| 391 | switch_frame_t read_frame; | |||
| 392 | udptl_state_t udptl_state; | |||
| 393 | unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE8192]; | |||
| 394 | switch_timer_t timer; | |||
| 395 | modem_t *modem; | |||
| 396 | switch_caller_profile_t *caller_profile; | |||
| 397 | int dead; | |||
| 398 | }; | |||
| 399 | ||||
| 400 | typedef struct private_object private_t; | |||
| 401 | ||||
| 402 | static switch_status_t channel_on_init(switch_core_session_t *session); | |||
| 403 | static switch_status_t channel_on_hangup(switch_core_session_t *session); | |||
| 404 | static switch_status_t channel_on_destroy(switch_core_session_t *session); | |||
| 405 | static switch_status_t channel_on_routing(switch_core_session_t *session); | |||
| 406 | static switch_status_t channel_on_exchange_media(switch_core_session_t *session); | |||
| 407 | static switch_status_t channel_on_soft_execute(switch_core_session_t *session); | |||
| 408 | static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, | |||
| 409 | switch_caller_profile_t *outbound_profile, | |||
| 410 | switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, | |||
| 411 | switch_call_cause_t *cancel_cause); | |||
| 412 | static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id); | |||
| 413 | static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); | |||
| 414 | static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig); | |||
| 415 | ||||
| 416 | ||||
| 417 | /* | |||
| 418 | State methods they get called when the state changes to the specific state | |||
| 419 | returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next | |||
| 420 | so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it. | |||
| 421 | */ | |||
| 422 | static switch_status_t channel_on_init(switch_core_session_t *session) | |||
| 423 | { | |||
| 424 | switch_channel_t *channel; | |||
| 425 | private_t *tech_pvt = NULL((void*)0); | |||
| 426 | int to_ticks = 60, ring_ticks = 10, rt = ring_ticks; | |||
| 427 | int rest = 500000; | |||
| 428 | at_state_t *at_state; | |||
| 429 | ||||
| 430 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 431 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 431, __PRETTY_FUNCTION__)); | |||
| 432 | ||||
| 433 | channel = switch_core_session_get_channel(session); | |||
| 434 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 434, __PRETTY_FUNCTION__)); | |||
| 435 | ||||
| 436 | if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { | |||
| 437 | #ifndef WIN32 | |||
| 438 | int tioflags; | |||
| 439 | #endif | |||
| 440 | char call_time[16]; | |||
| 441 | char call_date[16]; | |||
| 442 | switch_size_t retsize; | |||
| 443 | switch_time_exp_t tm; | |||
| 444 | ||||
| 445 | switch_time_exp_lt(&tm, switch_micro_time_now()); | |||
| 446 | switch_strftime(call_date, &retsize, sizeof(call_date), "%m%d", &tm); | |||
| 447 | switch_strftime(call_time, &retsize, sizeof(call_time), "%H%M", &tm); | |||
| 448 | ||||
| 449 | #ifndef WIN32 | |||
| 450 | ioctl(tech_pvt->modem->slave, TIOCMGET0x5415, &tioflags); | |||
| 451 | tioflags |= TIOCM_RI0x080; | |||
| 452 | ioctl(tech_pvt->modem->slave, TIOCMSET0x5418, &tioflags); | |||
| 453 | #endif | |||
| 454 | ||||
| 455 | at_state = t31_get_at_state(tech_pvt->modem->t31_state); | |||
| 456 | at_reset_call_info(at_state); | |||
| 457 | at_set_call_info(at_state, "DATE", call_date); | |||
| 458 | at_set_call_info(at_state, "TIME", call_time); | |||
| 459 | at_set_call_info(at_state, "NAME", tech_pvt->caller_profile->caller_id_name); | |||
| 460 | at_set_call_info(at_state, "NMBR", tech_pvt->caller_profile->caller_id_number); | |||
| 461 | at_set_call_info(at_state, "ANID", tech_pvt->caller_profile->ani); | |||
| 462 | at_set_call_info(at_state, "USER", tech_pvt->caller_profile->username); | |||
| 463 | at_set_call_info(at_state, "CDID", tech_pvt->caller_profile->context); | |||
| 464 | at_set_call_info(at_state, "NDID", tech_pvt->caller_profile->destination_number); | |||
| 465 | ||||
| 466 | modem_set_state(tech_pvt->modem, MODEM_STATE_RINGING)_modem_set_state(tech_pvt->modem, MODEM_STATE_RINGING, "mod_spandsp_modem.c" , (const char *)__func__, 466); | |||
| 467 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_ALERTING); | |||
| 468 | ||||
| 469 | while (to_ticks > 0 && switch_channel_up(channel)(switch_channel_check_signal(channel, SWITCH_TRUE) || switch_channel_get_state (channel) < CS_HANGUP) && modem_get_state(tech_pvt->modem) == MODEM_STATE_RINGING) { | |||
| 470 | if (--rt <= 0) { | |||
| 471 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_ALERTING); | |||
| 472 | rt = ring_ticks; | |||
| 473 | } | |||
| 474 | ||||
| 475 | switch_yield(rest)switch_sleep(rest);; | |||
| 476 | to_ticks--; | |||
| 477 | } | |||
| 478 | ||||
| 479 | if (to_ticks < 1 || modem_get_state(tech_pvt->modem) != MODEM_STATE_ANSWERED) { | |||
| 480 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_NO_ANSWER); | |||
| 481 | switch_channel_hangup(channel, SWITCH_CAUSE_NO_ANSWER)switch_channel_perform_hangup(channel, "mod_spandsp_modem.c", (const char *)__func__, 481, SWITCH_CAUSE_NO_ANSWER); | |||
| 482 | } else { | |||
| 483 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_ANSWERED); | |||
| 484 | modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED)_modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED, "mod_spandsp_modem.c" , (const char *)__func__, 484); | |||
| 485 | switch_channel_mark_answered(channel)switch_channel_perform_mark_answered(channel, "mod_spandsp_modem.c" , (const char *)__func__, 485); | |||
| 486 | } | |||
| 487 | } | |||
| 488 | ||||
| 489 | switch_channel_set_state(channel, CS_ROUTING)switch_channel_perform_set_state(channel, "mod_spandsp_modem.c" , (const char *)__func__, 489, CS_ROUTING); | |||
| 490 | ||||
| 491 | return SWITCH_STATUS_SUCCESS; | |||
| 492 | } | |||
| 493 | ||||
| 494 | static switch_status_t channel_on_routing(switch_core_session_t *session) | |||
| 495 | { | |||
| 496 | switch_channel_t *channel = NULL((void*)0); | |||
| 497 | private_t *tech_pvt = NULL((void*)0); | |||
| 498 | ||||
| 499 | channel = switch_core_session_get_channel(session); | |||
| 500 | assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 500, __PRETTY_FUNCTION__)); | |||
| 501 | ||||
| 502 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 503 | assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 503, __PRETTY_FUNCTION__)); | |||
| 504 | ||||
| 505 | return SWITCH_STATUS_SUCCESS; | |||
| 506 | } | |||
| 507 | ||||
| 508 | static switch_status_t channel_on_execute(switch_core_session_t *session) | |||
| 509 | { | |||
| 510 | switch_channel_t *channel = NULL((void*)0); | |||
| 511 | private_t *tech_pvt = NULL((void*)0); | |||
| 512 | ||||
| 513 | channel = switch_core_session_get_channel(session); | |||
| 514 | assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 514, __PRETTY_FUNCTION__)); | |||
| 515 | ||||
| 516 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 517 | assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 517, __PRETTY_FUNCTION__)); | |||
| 518 | ||||
| 519 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 519, (const char*)(session), SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel)); | |||
| 520 | ||||
| 521 | return SWITCH_STATUS_SUCCESS; | |||
| 522 | } | |||
| 523 | ||||
| 524 | static switch_status_t channel_on_destroy(switch_core_session_t *session) | |||
| 525 | { | |||
| 526 | //switch_channel_t *channel = NULL; | |||
| 527 | private_t *tech_pvt = NULL((void*)0); | |||
| 528 | ||||
| 529 | //channel = switch_core_session_get_channel(session); | |||
| 530 | //switch_assert(channel != NULL); | |||
| 531 | ||||
| 532 | if ((tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ))) { | |||
| 533 | ||||
| 534 | switch_core_timer_destroy(&tech_pvt->timer); | |||
| 535 | ||||
| 536 | if (tech_pvt->modem) { | |||
| 537 | *tech_pvt->modem->uuid_str = '\0'; | |||
| 538 | *tech_pvt->modem->digits = '\0'; | |||
| 539 | modem_set_state(tech_pvt->modem, MODEM_STATE_ONHOOK)_modem_set_state(tech_pvt->modem, MODEM_STATE_ONHOOK, "mod_spandsp_modem.c" , (const char *)__func__, 539); | |||
| 540 | tech_pvt->modem = NULL((void*)0); | |||
| 541 | } | |||
| 542 | } | |||
| 543 | ||||
| 544 | return SWITCH_STATUS_SUCCESS; | |||
| 545 | } | |||
| 546 | ||||
| 547 | static switch_status_t channel_on_hangup(switch_core_session_t *session) | |||
| 548 | { | |||
| 549 | switch_channel_t *channel = NULL((void*)0); | |||
| 550 | private_t *tech_pvt = NULL((void*)0); | |||
| 551 | ||||
| 552 | channel = switch_core_session_get_channel(session); | |||
| 553 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 553, __PRETTY_FUNCTION__)); | |||
| 554 | ||||
| 555 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 556 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 556, __PRETTY_FUNCTION__)); | |||
| 557 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 557, (const char*)(session), SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); | |||
| 558 | ||||
| 559 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_HANGUP); | |||
| 560 | ||||
| 561 | return SWITCH_STATUS_SUCCESS; | |||
| 562 | } | |||
| 563 | ||||
| 564 | static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig) | |||
| 565 | { | |||
| 566 | switch_channel_t *channel = NULL((void*)0); | |||
| 567 | private_t *tech_pvt = NULL((void*)0); | |||
| 568 | ||||
| 569 | channel = switch_core_session_get_channel(session); | |||
| 570 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 570, __PRETTY_FUNCTION__)); | |||
| 571 | ||||
| 572 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 573 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 573, __PRETTY_FUNCTION__)); | |||
| 574 | ||||
| 575 | switch (sig) { | |||
| 576 | case SWITCH_SIG_BREAK: | |||
| 577 | break; | |||
| 578 | case SWITCH_SIG_KILL: | |||
| 579 | tech_pvt->dead = 1; | |||
| 580 | break; | |||
| 581 | default: | |||
| 582 | break; | |||
| 583 | } | |||
| 584 | ||||
| 585 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 585, (const char*)(session), SWITCH_LOG_DEBUG, "%s CHANNEL KILL\n", switch_channel_get_name(channel)); | |||
| 586 | ||||
| 587 | return SWITCH_STATUS_SUCCESS; | |||
| 588 | } | |||
| 589 | ||||
| 590 | static switch_status_t channel_on_soft_execute(switch_core_session_t *session) | |||
| 591 | { | |||
| 592 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 592, (const char*)(session), SWITCH_LOG_DEBUG, "CHANNEL TRANSMIT\n"); | |||
| 593 | return SWITCH_STATUS_SUCCESS; | |||
| 594 | } | |||
| 595 | ||||
| 596 | static switch_status_t channel_on_exchange_media(switch_core_session_t *session) | |||
| 597 | { | |||
| 598 | switch_channel_t *channel = NULL((void*)0); | |||
| 599 | private_t *tech_pvt = NULL((void*)0); | |||
| 600 | ||||
| 601 | channel = switch_core_session_get_channel(session); | |||
| 602 | assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 602, __PRETTY_FUNCTION__)); | |||
| 603 | ||||
| 604 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 605 | assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 605, __PRETTY_FUNCTION__)); | |||
| 606 | ||||
| 607 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 607, (const char*)(session), SWITCH_LOG_DEBUG, "CHANNEL MODEM\n"); | |||
| 608 | ||||
| 609 | return SWITCH_STATUS_SUCCESS; | |||
| 610 | } | |||
| 611 | ||||
| 612 | static switch_status_t channel_on_reset(switch_core_session_t *session) | |||
| 613 | { | |||
| 614 | private_t *tech_pvt = (private_t *) switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 615 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 615, __PRETTY_FUNCTION__)); | |||
| 616 | ||||
| 617 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 617, (const char*)(session), SWITCH_LOG_DEBUG, "%s RESET\n", | |||
| 618 | switch_channel_get_name(switch_core_session_get_channel(session))); | |||
| 619 | ||||
| 620 | return SWITCH_STATUS_SUCCESS; | |||
| 621 | } | |||
| 622 | ||||
| 623 | static switch_status_t channel_on_hibernate(switch_core_session_t *session) | |||
| 624 | { | |||
| 625 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 625, (const char*)(session), SWITCH_LOG_DEBUG, "%s HIBERNATE\n", | |||
| 626 | switch_channel_get_name(switch_core_session_get_channel(session))); | |||
| 627 | ||||
| 628 | return SWITCH_STATUS_SUCCESS; | |||
| 629 | } | |||
| 630 | ||||
| 631 | static switch_status_t channel_on_consume_media(switch_core_session_t *session) | |||
| 632 | { | |||
| 633 | switch_channel_t *channel = NULL((void*)0); | |||
| 634 | private_t *tech_pvt = NULL((void*)0); | |||
| 635 | ||||
| 636 | channel = switch_core_session_get_channel(session); | |||
| 637 | assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 637, __PRETTY_FUNCTION__)); | |||
| 638 | ||||
| 639 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 640 | assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 640, __PRETTY_FUNCTION__)); | |||
| 641 | ||||
| 642 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 642, (const char*)(session), SWITCH_LOG_DEBUG, "CHANNEL CONSUME_MEDIA\n"); | |||
| 643 | ||||
| 644 | return SWITCH_STATUS_SUCCESS; | |||
| 645 | } | |||
| 646 | ||||
| 647 | static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) | |||
| 648 | { | |||
| 649 | private_t *tech_pvt = NULL((void*)0); | |||
| 650 | ||||
| 651 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 652 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 652, __PRETTY_FUNCTION__)); | |||
| 653 | ||||
| 654 | return SWITCH_STATUS_SUCCESS; | |||
| 655 | } | |||
| 656 | ||||
| 657 | static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) | |||
| 658 | { | |||
| 659 | switch_channel_t *channel = NULL((void*)0); | |||
| 660 | private_t *tech_pvt = NULL((void*)0); | |||
| 661 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
| 662 | int r, samples_wanted, samples_read = 0; | |||
| 663 | int16_t *data; | |||
| 664 | ||||
| 665 | channel = switch_core_session_get_channel(session); | |||
| 666 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 666, __PRETTY_FUNCTION__)); | |||
| 667 | ||||
| 668 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 669 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 669, __PRETTY_FUNCTION__)); | |||
| 670 | ||||
| 671 | if (tech_pvt->dead) return SWITCH_STATUS_FALSE; | |||
| 672 | ||||
| 673 | data = tech_pvt->read_frame.data; | |||
| 674 | samples_wanted = tech_pvt->read_codec.implementation->samples_per_packet; | |||
| 675 | ||||
| 676 | tech_pvt->read_frame.flags = SFF_NONE; | |||
| 677 | switch_core_timer_next(&tech_pvt->timer); | |||
| 678 | ||||
| 679 | do { | |||
| 680 | r = t31_tx(tech_pvt->modem->t31_state, data + samples_read, samples_wanted - samples_read); | |||
| 681 | if (r < 0) break; | |||
| 682 | samples_read += r; | |||
| 683 | } while (samples_read < samples_wanted && r > 0); | |||
| 684 | ||||
| 685 | if (r < 0) { | |||
| 686 | return SWITCH_STATUS_FALSE; | |||
| 687 | } | |||
| 688 | if (samples_read < samples_wanted) { | |||
| 689 | memset(data + samples_read, 0, sizeof(int16_t)*(samples_wanted - samples_read)); | |||
| 690 | samples_read = samples_wanted; | |||
| 691 | } | |||
| 692 | ||||
| 693 | tech_pvt->read_frame.samples = samples_read; | |||
| 694 | tech_pvt->read_frame.datalen = samples_read * 2; | |||
| 695 | ||||
| 696 | *frame = &tech_pvt->read_frame; | |||
| 697 | ||||
| 698 | return status; | |||
| 699 | } | |||
| 700 | ||||
| 701 | static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) | |||
| 702 | { | |||
| 703 | switch_channel_t *channel = NULL((void*)0); | |||
| 704 | private_t *tech_pvt = NULL((void*)0); | |||
| 705 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
| 706 | ||||
| 707 | channel = switch_core_session_get_channel(session); | |||
| 708 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 708, __PRETTY_FUNCTION__)); | |||
| 709 | ||||
| 710 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 711 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 711, __PRETTY_FUNCTION__)); | |||
| 712 | ||||
| 713 | if (tech_pvt->dead) return SWITCH_STATUS_FALSE; | |||
| 714 | ||||
| 715 | if (t31_rx(tech_pvt->modem->t31_state, frame->data, frame->datalen / 2)) { | |||
| 716 | status = SWITCH_STATUS_FALSE; | |||
| 717 | } | |||
| 718 | ||||
| 719 | return status; | |||
| 720 | } | |||
| 721 | ||||
| 722 | static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) | |||
| 723 | { | |||
| 724 | switch_channel_t *channel; | |||
| 725 | private_t *tech_pvt; | |||
| 726 | ||||
| 727 | channel = switch_core_session_get_channel(session); | |||
| 728 | switch_assert(channel != NULL)((channel != ((void*)0)) ? (void) (0) : __assert_fail ("channel != ((void*)0)" , "mod_spandsp_modem.c", 728, __PRETTY_FUNCTION__)); | |||
| 729 | ||||
| 730 | tech_pvt = switch_core_session_get_private(session)switch_core_session_get_private_class(session, SWITCH_PVT_PRIMARY ); | |||
| 731 | switch_assert(tech_pvt != NULL)((tech_pvt != ((void*)0)) ? (void) (0) : __assert_fail ("tech_pvt != ((void*)0)" , "mod_spandsp_modem.c", 731, __PRETTY_FUNCTION__)); | |||
| 732 | ||||
| 733 | switch (msg->message_id) { | |||
| 734 | case SWITCH_MESSAGE_INDICATE_ANSWER: | |||
| 735 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_CONNECTED); | |||
| 736 | modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED)_modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED, "mod_spandsp_modem.c" , (const char *)__func__, 736); | |||
| 737 | mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE); | |||
| 738 | break; | |||
| 739 | case SWITCH_MESSAGE_INDICATE_PROGRESS: | |||
| 740 | t31_call_event(tech_pvt->modem->t31_state, AT_CALL_EVENT_CONNECTED); | |||
| 741 | modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED)_modem_set_state(tech_pvt->modem, MODEM_STATE_CONNECTED, "mod_spandsp_modem.c" , (const char *)__func__, 741); | |||
| 742 | mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE); | |||
| 743 | break; | |||
| 744 | case SWITCH_MESSAGE_INDICATE_RINGING: | |||
| 745 | break; | |||
| 746 | case SWITCH_MESSAGE_INDICATE_BRIDGE: | |||
| 747 | mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE); | |||
| 748 | break; | |||
| 749 | case SWITCH_MESSAGE_INDICATE_UNBRIDGE: | |||
| 750 | mod_spandsp_indicate_data(session, SWITCH_FALSE, SWITCH_TRUE); | |||
| 751 | break; | |||
| 752 | default: | |||
| 753 | break; | |||
| 754 | } | |||
| 755 | ||||
| 756 | return SWITCH_STATUS_SUCCESS; | |||
| 757 | } | |||
| 758 | ||||
| 759 | static switch_status_t tech_init(private_t *tech_pvt, switch_core_session_t *session) | |||
| 760 | { | |||
| 761 | const char *iananame = "L16"; | |||
| 762 | int rate = 8000; | |||
| 763 | int interval = 20; | |||
| 764 | switch_status_t status = SWITCH_STATUS_SUCCESS; | |||
| 765 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
| 766 | const switch_codec_implementation_t *read_impl; | |||
| 767 | ||||
| 768 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 768, (const char*)(session), SWITCH_LOG_DEBUG, "%s setup codec %s/%d/%d\n", switch_channel_get_name(channel), iananame, rate, | |||
| 769 | interval); | |||
| 770 | ||||
| 771 | status = switch_core_codec_init(&tech_pvt->read_codec,switch_core_codec_init_with_bitrate(&tech_pvt->read_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
| 772 | iananame,switch_core_codec_init_with_bitrate(&tech_pvt->read_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
| 773 | NULL,switch_core_codec_init_with_bitrate(&tech_pvt->read_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
| 774 | rate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session))switch_core_codec_init_with_bitrate(&tech_pvt->read_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)); | |||
| 775 | ||||
| 776 | if (status != SWITCH_STATUS_SUCCESS || !tech_pvt->read_codec.implementation || !switch_core_codec_ready(&tech_pvt->read_codec)) { | |||
| 777 | goto end; | |||
| 778 | } | |||
| 779 | ||||
| 780 | status = switch_core_codec_init(&tech_pvt->write_codec,switch_core_codec_init_with_bitrate(&tech_pvt->write_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
| 781 | iananame,switch_core_codec_init_with_bitrate(&tech_pvt->write_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
| 782 | NULL,switch_core_codec_init_with_bitrate(&tech_pvt->write_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)) | |||
| 783 | rate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session))switch_core_codec_init_with_bitrate(&tech_pvt->write_codec , iananame, ((void*)0), rate, interval, 1, 0, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, ((void*)0), switch_core_session_get_pool (session)); | |||
| 784 | ||||
| 785 | if (status != SWITCH_STATUS_SUCCESS) { | |||
| 786 | switch_core_codec_destroy(&tech_pvt->read_codec); | |||
| 787 | goto end; | |||
| 788 | } | |||
| 789 | ||||
| 790 | tech_pvt->read_frame.data = tech_pvt->databuf; | |||
| 791 | tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); | |||
| 792 | tech_pvt->read_frame.codec = &tech_pvt->read_codec; | |||
| 793 | tech_pvt->read_frame.flags = SFF_NONE; | |||
| 794 | ||||
| 795 | switch_core_session_set_read_codec(session, &tech_pvt->read_codec); | |||
| 796 | switch_core_session_set_write_codec(session, &tech_pvt->write_codec); | |||
| 797 | ||||
| 798 | read_impl = tech_pvt->read_codec.implementation; | |||
| 799 | ||||
| 800 | switch_core_timer_init(&tech_pvt->timer, "soft", | |||
| 801 | read_impl->microseconds_per_packet / 1000, read_impl->samples_per_packet, switch_core_session_get_pool(session)); | |||
| 802 | ||||
| 803 | switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED0x1, switch_core_session_get_pool(session)); | |||
| 804 | switch_core_session_set_private(session, tech_pvt)switch_core_session_set_private_class(session, tech_pvt, SWITCH_PVT_PRIMARY ); | |||
| 805 | tech_pvt->session = session; | |||
| 806 | tech_pvt->channel = switch_core_session_get_channel(session); | |||
| 807 | ||||
| 808 | end: | |||
| 809 | ||||
| 810 | return status; | |||
| 811 | } | |||
| 812 | ||||
| 813 | static void tech_attach(private_t *tech_pvt, modem_t *modem) | |||
| 814 | { | |||
| 815 | tech_pvt->modem = modem; | |||
| 816 | switch_set_string(modem->uuid_str, switch_core_session_get_uuid(tech_pvt->session))switch_copy_string(modem->uuid_str, switch_core_session_get_uuid (tech_pvt->session), sizeof(modem->uuid_str)); | |||
| 817 | switch_channel_set_variable_printf(tech_pvt->channel, "modem_slot", "%d", modem->slot); | |||
| 818 | switch_channel_set_variable(tech_pvt->channel, "modem_devlink", modem->devlink)switch_channel_set_variable_var_check(tech_pvt->channel, "modem_devlink" , modem->devlink, SWITCH_TRUE); | |||
| 819 | switch_channel_set_variable(tech_pvt->channel, "modem_digits", modem->digits)switch_channel_set_variable_var_check(tech_pvt->channel, "modem_digits" , modem->digits, SWITCH_TRUE); | |||
| 820 | switch_channel_export_variable(tech_pvt->channel, "rtp_autoflush_during_bridge", "false", SWITCH_EXPORT_VARS_VARIABLE)switch_channel_export_variable_var_check(tech_pvt->channel , "rtp_autoflush_during_bridge", "false", "export_vars", SWITCH_TRUE ); | |||
| 821 | } | |||
| 822 | ||||
| 823 | ||||
| 824 | static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, | |||
| 825 | switch_caller_profile_t *outbound_profile, | |||
| 826 | switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, | |||
| 827 | switch_call_cause_t *cancel_cause) | |||
| 828 | { | |||
| 829 | char name[128]; | |||
| 830 | switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; | |||
| 831 | ||||
| 832 | if ((*new_session = switch_core_session_request(modem_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool)switch_core_session_request_uuid(modem_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND , flags, pool, ((void*)0))) != 0) { | |||
| 833 | private_t *tech_pvt; | |||
| 834 | switch_channel_t *channel; | |||
| 835 | switch_caller_profile_t *caller_profile; | |||
| 836 | char *dest = switch_core_session_strdup(*new_session, outbound_profile->destination_number)switch_core_perform_session_strdup(*new_session, outbound_profile ->destination_number, "mod_spandsp_modem.c", (const char * )__func__, 836); | |||
| 837 | char *modem_id_string = NULL((void*)0); | |||
| 838 | char *number = NULL((void*)0); | |||
| 839 | int modem_id = 0; | |||
| 840 | modem_t *modem = NULL((void*)0); | |||
| 841 | ||||
| 842 | if ((modem_id_string = dest)) { | |||
| 843 | if ((number = strchr(modem_id_string, '/')(__extension__ (__builtin_constant_p ('/') && !__builtin_constant_p (modem_id_string) && ('/') == '\0' ? (char *) __rawmemchr (modem_id_string, '/') : __builtin_strchr (modem_id_string, '/' ))))) { | |||
| 844 | *number++ = '\0'; | |||
| 845 | } | |||
| 846 | } | |||
| 847 | ||||
| 848 | if (zstr(modem_id_string)_zstr(modem_id_string) || zstr(number)_zstr(number)) { | |||
| 849 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 849, (const char*)(*new_session), SWITCH_LOG_ERROR, "Invalid dial string.\n"); | |||
| 850 | cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; goto fail; | |||
| 851 | } | |||
| 852 | ||||
| 853 | if (!strcasecmp(modem_id_string, "a")) { | |||
| 854 | modem_id = -1; | |||
| 855 | } else { | |||
| 856 | modem_id = atoi(modem_id_string); | |||
| 857 | } | |||
| 858 | ||||
| 859 | if (!(modem = acquire_modem(modem_id))) { | |||
| 860 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 860, (const char*)(*new_session), SWITCH_LOG_ERROR, "Cannot find a modem.\n"); | |||
| 861 | cause = SWITCH_CAUSE_USER_BUSY; goto fail; | |||
| 862 | } | |||
| 863 | ||||
| 864 | switch_core_session_add_stream(*new_session, NULL((void*)0)); | |||
| 865 | ||||
| 866 | if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))switch_core_perform_session_alloc(*new_session, sizeof(private_t ), "mod_spandsp_modem.c", (const char *)__func__, 866)) != 0) { | |||
| 867 | channel = switch_core_session_get_channel(*new_session); | |||
| 868 | switch_snprintf(name, sizeof(name), "modem/%d/%s", modem->slot, number); | |||
| 869 | switch_channel_set_name(channel, name); | |||
| 870 | ||||
| 871 | if (tech_init(tech_pvt, *new_session) != SWITCH_STATUS_SUCCESS) { | |||
| 872 | cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; | |||
| 873 | } | |||
| 874 | ||||
| 875 | switch_set_string(modem->digits, number)switch_copy_string(modem->digits, number, sizeof(modem-> digits)); | |||
| 876 | tech_attach(tech_pvt, modem); | |||
| 877 | } else { | |||
| 878 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 878, (const char*)(*new_session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); | |||
| 879 | switch_core_session_destroy(new_session)switch_core_session_perform_destroy(new_session, "mod_spandsp_modem.c" , (const char *)__func__, 879); | |||
| 880 | cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; | |||
| 881 | } | |||
| 882 | ||||
| 883 | if (outbound_profile) { | |||
| 884 | caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); | |||
| 885 | caller_profile->source = switch_core_strdup(caller_profile->pool, "mod_spandsp")switch_core_perform_strdup(caller_profile->pool, "mod_spandsp" , "mod_spandsp_modem.c", (const char *)__func__, 885); | |||
| 886 | caller_profile->destination_number = switch_core_strdup(caller_profile->pool, number)switch_core_perform_strdup(caller_profile->pool, number, "mod_spandsp_modem.c" , (const char *)__func__, 886); | |||
| 887 | switch_channel_set_caller_profile(channel, caller_profile); | |||
| 888 | tech_pvt->caller_profile = caller_profile; | |||
| 889 | } else { | |||
| 890 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 890, (const char*)(*new_session), SWITCH_LOG_ERROR, "Doh! no caller profile\n"); | |||
| 891 | cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto fail; | |||
| 892 | } | |||
| 893 | ||||
| 894 | switch_channel_set_state(channel, CS_INIT)switch_channel_perform_set_state(channel, "mod_spandsp_modem.c" , (const char *)__func__, 894, CS_INIT); | |||
| 895 | ||||
| 896 | return SWITCH_CAUSE_SUCCESS; | |||
| 897 | ||||
| 898 | fail: | |||
| 899 | ||||
| 900 | if (new_session) { | |||
| 901 | switch_core_session_destroy(new_session)switch_core_session_perform_destroy(new_session, "mod_spandsp_modem.c" , (const char *)__func__, 901); | |||
| 902 | } | |||
| 903 | ||||
| 904 | if (modem) { | |||
| 905 | modem_set_state(modem, MODEM_STATE_ONHOOK)_modem_set_state(modem, MODEM_STATE_ONHOOK, "mod_spandsp_modem.c" , (const char *)__func__, 905); | |||
| 906 | } | |||
| 907 | } | |||
| 908 | ||||
| 909 | return cause; | |||
| 910 | } | |||
| 911 | ||||
| 912 | static switch_state_handler_table_t channel_event_handlers = { | |||
| 913 | /*.on_init */ channel_on_init, | |||
| 914 | /*.on_routing */ channel_on_routing, | |||
| 915 | /*.on_execute */ channel_on_execute, | |||
| 916 | /*.on_hangup */ channel_on_hangup, | |||
| 917 | /*.on_exchange_media */ channel_on_exchange_media, | |||
| 918 | /*.on_soft_execute */ channel_on_soft_execute, | |||
| 919 | /*.on_consume_media */ channel_on_consume_media, | |||
| 920 | /*.on_hibernate */ channel_on_hibernate, | |||
| 921 | /*.on_reset */ channel_on_reset, | |||
| 922 | /*.on_park */ NULL((void*)0), | |||
| 923 | /*.on_reporting */ NULL((void*)0), | |||
| 924 | /*.on_destroy */ channel_on_destroy | |||
| 925 | }; | |||
| 926 | ||||
| 927 | static switch_io_routines_t channel_io_routines = { | |||
| 928 | /*.outgoing_channel */ channel_outgoing_channel, | |||
| 929 | /*.read_frame */ channel_read_frame, | |||
| 930 | /*.write_frame */ channel_write_frame, | |||
| 931 | /*.kill_channel */ channel_kill_channel, | |||
| 932 | /*.send_dtmf */ channel_send_dtmf, | |||
| 933 | /*.receive_message */ channel_receive_message | |||
| 934 | }; | |||
| 935 | ||||
| 936 | static switch_status_t create_session(switch_core_session_t **new_session, modem_t *modem) | |||
| 937 | { | |||
| 938 | switch_status_t status = SWITCH_STATUS_FALSE; | |||
| 939 | switch_core_session_t *session; | |||
| 940 | switch_channel_t *channel; | |||
| 941 | private_t *tech_pvt = NULL((void*)0); | |||
| 942 | char name[1024]; | |||
| 943 | switch_caller_profile_t *caller_profile; | |||
| 944 | char *ani = NULL((void*)0), *p, *digits = NULL((void*)0); | |||
| 945 | ||||
| 946 | if (!(session = switch_core_session_request(modem_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)switch_core_session_request_uuid(modem_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND , SOF_NONE, ((void*)0), ((void*)0)))) { | |||
| 947 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 947, (const char*)(session), SWITCH_LOG_CRIT, "Failure.\n"); | |||
| 948 | goto end; | |||
| 949 | } | |||
| 950 | ||||
| 951 | switch_core_session_add_stream(session, NULL((void*)0)); | |||
| 952 | channel = switch_core_session_get_channel(session); | |||
| 953 | tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(*tech_pvt))switch_core_perform_session_alloc(session, sizeof(*tech_pvt), "mod_spandsp_modem.c", (const char *)__func__, 953); | |||
| 954 | ||||
| 955 | p = switch_core_session_strdup(session, modem->digits)switch_core_perform_session_strdup(session, modem->digits, "mod_spandsp_modem.c", (const char *)__func__, 955); | |||
| 956 | ||||
| 957 | if (*p == '*') { | |||
| 958 | ani = p + 1; | |||
| 959 | if ((digits = strchr(ani, '*')(__extension__ (__builtin_constant_p ('*') && !__builtin_constant_p (ani) && ('*') == '\0' ? (char *) __rawmemchr (ani, '*' ) : __builtin_strchr (ani, '*'))))) { | |||
| 960 | *digits++ = '\0'; | |||
| 961 | } else { | |||
| 962 | ani = NULL((void*)0); | |||
| 963 | } | |||
| 964 | } | |||
| 965 | ||||
| 966 | if (zstr(digits)_zstr(digits)) { | |||
| 967 | digits = p; | |||
| 968 | } | |||
| 969 | ||||
| 970 | if (zstr(ani)_zstr(ani)) { | |||
| 971 | ani = modem->devlink + 5; | |||
| 972 | } | |||
| 973 | ||||
| 974 | switch_snprintf(name, sizeof(name), "modem/%d/%s", modem->slot, digits); | |||
| 975 | switch_channel_set_name(channel, name); | |||
| 976 | ||||
| 977 | if (tech_init(tech_pvt, session) != SWITCH_STATUS_SUCCESS) { | |||
| 978 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "mod_spandsp_modem.c", (const char *)__func__, 978, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
| 979 | switch_core_session_destroy(&session)switch_core_session_perform_destroy(&session, "mod_spandsp_modem.c" , (const char *)__func__, 979); | |||
| 980 | goto end; | |||
| 981 | } | |||
| 982 | ||||
| 983 | caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), | |||
| 984 | modem->devlink, | |||
| 985 | spandsp_globals.modem_dialplan, | |||
| 986 | "FSModem", | |||
| 987 | ani, | |||
| 988 | NULL((void*)0), | |||
| 989 | ani, | |||
| 990 | NULL((void*)0), | |||
| 991 | NULL((void*)0), | |||
| 992 | "mod_spandsp", | |||
| 993 | spandsp_globals.modem_context, | |||
| 994 | digits); | |||
| 995 | ||||
| 996 | caller_profile->source = switch_core_strdup(caller_profile->pool, "mod_spandsp")switch_core_perform_strdup(caller_profile->pool, "mod_spandsp" , "mod_spandsp_modem.c", (const char *)__func__, 996); | |||
| 997 | switch_channel_set_caller_profile(channel, caller_profile); | |||
| 998 | tech_pvt->caller_profile = caller_profile; | |||
| 999 | switch_channel_set_state(channel, CS_INIT)switch_channel_perform_set_state(channel, "mod_spandsp_modem.c" , (const char *)__func__, 999, CS_INIT); | |||
| 1000 | ||||
| 1001 | if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { | |||
| 1002 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session)SWITCH_CHANNEL_ID_SESSION, "mod_spandsp_modem.c", (const char *)__func__, 1002, (const char*)(session), SWITCH_LOG_CRIT, "Error spawning thread\n"); | |||
| 1003 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER)switch_channel_perform_hangup(channel, "mod_spandsp_modem.c", (const char *)__func__, 1003, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER ); | |||
| 1004 | goto end; | |||
| 1005 | } | |||
| 1006 | ||||
| 1007 | status = SWITCH_STATUS_SUCCESS; | |||
| 1008 | tech_attach(tech_pvt, modem); | |||
| 1009 | *new_session = session; | |||
| 1010 | ||||
| 1011 | end: | |||
| 1012 | ||||
| 1013 | return status; | |||
| 1014 | } | |||
| 1015 | ||||
| 1016 | static void wake_modem_thread(modem_t *modem) | |||
| 1017 | { | |||
| 1018 | if (switch_mutex_trylock(modem->cond_mutex) == SWITCH_STATUS_SUCCESS) { | |||
| 1019 | switch_thread_cond_signal(modem->cond); | |||
| 1020 | switch_mutex_unlock(modem->cond_mutex); | |||
| 1021 | } | |||
| 1022 | } | |||
| 1023 | ||||
| 1024 | static int control_handler(modem_t *modem, const char *num, int op) | |||
| 1025 | { | |||
| 1026 | switch_core_session_t *session = NULL((void*)0); | |||
| 1027 | ||||
| 1028 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1028, ((void*)0), SWITCH_LOG_DEBUG1, "Control Handler op:%d state:[%s] %s\n", | |||
| 1029 | op, modem_state2name(modem_get_state(modem)), modem->devlink); | |||
| 1030 | ||||
| 1031 | switch (op) { | |||
| 1032 | case AT_MODEM_CONTROL_ANSWER: | |||
| 1033 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1033, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1034 | "Modem %s [%s] - Answering\n", modem->devlink, modem_state2name(modem_get_state(modem))); | |||
| 1035 | modem_set_state(modem, MODEM_STATE_ANSWERED)_modem_set_state(modem, MODEM_STATE_ANSWERED, "mod_spandsp_modem.c" , (const char *)__func__, 1035); | |||
| 1036 | break; | |||
| 1037 | case AT_MODEM_CONTROL_CALL: | |||
| 1038 | { | |||
| 1039 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1039, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1040 | "Modem %s [%s] - Dialing '%s'\n", modem->devlink, modem_state2name(modem_get_state(modem)), num); | |||
| 1041 | modem_set_state(modem, MODEM_STATE_DIALING)_modem_set_state(modem, MODEM_STATE_DIALING, "mod_spandsp_modem.c" , (const char *)__func__, 1041); | |||
| 1042 | switch_clear_flag(modem, MODEM_FLAG_XOFF)(modem)->flags &= ~(MODEM_FLAG_XOFF); | |||
| 1043 | wake_modem_thread(modem); | |||
| 1044 | ||||
| 1045 | switch_set_string(modem->digits, num)switch_copy_string(modem->digits, num, sizeof(modem->digits )); | |||
| 1046 | ||||
| 1047 | if (create_session(&session, modem) != SWITCH_STATUS_SUCCESS) { | |||
| 1048 | t31_call_event(modem->t31_state, AT_CALL_EVENT_HANGUP); | |||
| 1049 | } else { | |||
| 1050 | switch_core_session_thread_launch(session); | |||
| 1051 | } | |||
| 1052 | } | |||
| 1053 | break; | |||
| 1054 | case AT_MODEM_CONTROL_OFFHOOK: | |||
| 1055 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1055, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1056 | "Modem %s [%s] - Going off hook\n", modem->devlink, modem_state2name(modem_get_state(modem))); | |||
| 1057 | modem_set_state(modem, MODEM_STATE_OFFHOOK)_modem_set_state(modem, MODEM_STATE_OFFHOOK, "mod_spandsp_modem.c" , (const char *)__func__, 1057); | |||
| 1058 | break; | |||
| 1059 | case AT_MODEM_CONTROL_ONHOOK: | |||
| 1060 | case AT_MODEM_CONTROL_HANGUP: | |||
| 1061 | { | |||
| 1062 | if (modem_get_state(modem) != MODEM_STATE_RINGING) { | |||
| 1063 | int set_state = 1; | |||
| 1064 | ||||
| 1065 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1065, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1066 | "Modem %s [%s] - Hanging up\n", modem->devlink, modem_state2name(modem_get_state(modem))); | |||
| 1067 | switch_clear_flag(modem, MODEM_FLAG_XOFF)(modem)->flags &= ~(MODEM_FLAG_XOFF); | |||
| 1068 | wake_modem_thread(modem); | |||
| 1069 | ||||
| 1070 | modem_set_state(modem, MODEM_STATE_HANGUP)_modem_set_state(modem, MODEM_STATE_HANGUP, "mod_spandsp_modem.c" , (const char *)__func__, 1070); | |||
| 1071 | ||||
| 1072 | if (!zstr(modem->uuid_str)_zstr(modem->uuid_str)) { | |||
| 1073 | switch_core_session_t *session; | |||
| 1074 | ||||
| 1075 | if ((session = switch_core_session_force_locate(modem->uuid_str)switch_core_session_perform_force_locate(modem->uuid_str, "mod_spandsp_modem.c" , (const char *)__func__, 1075))) { | |||
| 1076 | switch_channel_t *channel = switch_core_session_get_channel(session); | |||
| 1077 | ||||
| 1078 | if (switch_channel_up(channel)(switch_channel_check_signal(channel, SWITCH_TRUE) || switch_channel_get_state (channel) < CS_HANGUP)) { | |||
| 1079 | switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING)switch_channel_perform_hangup(channel, "mod_spandsp_modem.c", (const char *)__func__, 1079, SWITCH_CAUSE_NORMAL_CLEARING); | |||
| 1080 | set_state = 0; | |||
| 1081 | } | |||
| 1082 | switch_core_session_rwunlock(session); | |||
| 1083 | } | |||
| 1084 | } | |||
| 1085 | ||||
| 1086 | if (set_state) { | |||
| 1087 | modem_set_state(modem, MODEM_STATE_ONHOOK)_modem_set_state(modem, MODEM_STATE_ONHOOK, "mod_spandsp_modem.c" , (const char *)__func__, 1087); | |||
| 1088 | } | |||
| 1089 | } | |||
| 1090 | } | |||
| 1091 | break; | |||
| 1092 | case AT_MODEM_CONTROL_DTR: | |||
| 1093 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1093, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1094 | "Modem %s [%s] - DTR %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num); | |||
| 1095 | break; | |||
| 1096 | case AT_MODEM_CONTROL_RTS: | |||
| 1097 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1097, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1098 | "Modem %s [%s] - RTS %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num); | |||
| 1099 | break; | |||
| 1100 | case AT_MODEM_CONTROL_CTS: | |||
| 1101 | { | |||
| 1102 | u_char x[1]; | |||
| 1103 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1103, ((void*)0), SWITCH_LOG_DEBUG1, | |||
| 1104 | "Modem %s [%s] - CTS %s\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num ? "XON" : "XOFF"); | |||
| 1105 | if (num) { | |||
| 1106 | x[0] = 0x11; | |||
| 1107 | t31_at_tx_handler(modem, x, 1); | |||
| 1108 | switch_clear_flag(modem, MODEM_FLAG_XOFF)(modem)->flags &= ~(MODEM_FLAG_XOFF); | |||
| 1109 | wake_modem_thread(modem); | |||
| 1110 | } else { | |||
| 1111 | x[0] = 0x13; | |||
| 1112 | t31_at_tx_handler(modem, x, 1); | |||
| 1113 | switch_set_flag(modem, MODEM_FLAG_XOFF)(modem)->flags |= (MODEM_FLAG_XOFF); | |||
| 1114 | } | |||
| 1115 | } | |||
| 1116 | break; | |||
| 1117 | case AT_MODEM_CONTROL_CAR: | |||
| 1118 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1118, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1119 | "Modem %s [%s] - CAR %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num); | |||
| 1120 | break; | |||
| 1121 | case AT_MODEM_CONTROL_RNG: | |||
| 1122 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1122, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1123 | "Modem %s [%s] - RNG %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num); | |||
| 1124 | break; | |||
| 1125 | case AT_MODEM_CONTROL_DSR: | |||
| 1126 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1126, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1127 | "Modem %s [%s] - DSR %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), (int) (intptr_t) num); | |||
| 1128 | break; | |||
| 1129 | default: | |||
| 1130 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1130, ((void*)0), SWITCH_LOG_DEBUG, | |||
| 1131 | "Modem %s [%s] - operation %d\n", modem->devlink, modem_state2name(modem_get_state(modem)), op); | |||
| 1132 | break; | |||
| 1133 | } | |||
| 1134 | /*endswitch*/ | |||
| 1135 | return 0; | |||
| 1136 | } | |||
| 1137 | ||||
| 1138 | typedef enum { | |||
| 1139 | MODEM_POLL_READ = (1 << 0), | |||
| 1140 | MODEM_POLL_WRITE = (1 << 1), | |||
| 1141 | MODEM_POLL_ERROR = (1 << 2) | |||
| 1142 | } modem_poll_t; | |||
| 1143 | ||||
| 1144 | #ifndef WIN32 | |||
| 1145 | static int modem_wait_sock(int sock, uint32_t ms, modem_poll_t flags) | |||
| 1146 | { | |||
| 1147 | struct pollfd pfds[2] = { { 0 } }; | |||
| 1148 | int s = 0, r = 0; | |||
| 1149 | ||||
| 1150 | pfds[0].fd = sock; | |||
| 1151 | ||||
| 1152 | if ((flags & MODEM_POLL_READ)) { | |||
| 1153 | pfds[0].events |= POLLIN0x001; | |||
| 1154 | } | |||
| 1155 | ||||
| 1156 | if ((flags & MODEM_POLL_WRITE)) { | |||
| 1157 | pfds[0].events |= POLLOUT0x004; | |||
| 1158 | } | |||
| 1159 | ||||
| 1160 | if ((flags & MODEM_POLL_ERROR)) { | |||
| 1161 | pfds[0].events |= POLLERR0x008; | |||
| 1162 | } | |||
| 1163 | ||||
| 1164 | s = poll(pfds, 1, ms); | |||
| 1165 | ||||
| 1166 | if (s < 0) { | |||
| 1167 | r = s; | |||
| 1168 | } else if (s > 0) { | |||
| 1169 | if ((pfds[0].revents & POLLIN0x001)) { | |||
| 1170 | r |= MODEM_POLL_READ; | |||
| 1171 | } | |||
| 1172 | if ((pfds[0].revents & POLLOUT0x004)) { | |||
| 1173 | r |= MODEM_POLL_WRITE; | |||
| 1174 | } | |||
| 1175 | if ((pfds[0].revents & POLLERR0x008)) { | |||
| 1176 | r |= MODEM_POLL_ERROR; | |||
| 1177 | } | |||
| 1178 | } | |||
| 1179 | ||||
| 1180 | return r; | |||
| 1181 | } | |||
| 1182 | #else | |||
| 1183 | static int modem_wait_sock(modem_t *modem, int ms, modem_poll_t flags) | |||
| 1184 | { | |||
| 1185 | /* this method ignores ms and waits infinitely */ | |||
| 1186 | DWORD dwEvtMask, dwWait; | |||
| 1187 | OVERLAPPED o; | |||
| 1188 | BOOL result; | |||
| 1189 | int ret = MODEM_POLL_ERROR; | |||
| 1190 | HANDLE arHandles[2]; | |||
| 1191 | ||||
| 1192 | arHandles[0] = modem->threadAbort; | |||
| 1193 | ||||
| 1194 | o.hEvent = CreateEvent(NULL((void*)0), TRUE(!0), FALSE0, NULL((void*)0)); | |||
| 1195 | arHandles[1] = o.hEvent; | |||
| 1196 | ||||
| 1197 | /* Initialize the rest of the OVERLAPPED structure to zero. */ | |||
| 1198 | o.Internal = 0; | |||
| 1199 | o.InternalHigh = 0; | |||
| 1200 | o.Offset = 0; | |||
| 1201 | o.OffsetHigh = 0; | |||
| 1202 | assert(o.hEvent)((o.hEvent) ? (void) (0) : __assert_fail ("o.hEvent", "mod_spandsp_modem.c" , 1202, __PRETTY_FUNCTION__)); | |||
| 1203 | ||||
| 1204 | result = WaitCommEvent(modem->master, &dwEvtMask, &o); | |||
| 1205 | ||||
| 1206 | if (result == 0) | |||
| 1207 | { | |||
| 1208 | if (GetLastError() != ERROR_IO_PENDING) { | |||
| 1209 | /* something went horribly wrong with WaitCommEvent(), so | |||
| 1210 | clear all errors and try again */ | |||
| 1211 | DWORD comerrors; | |||
| 1212 | ClearCommError(modem->master, &comerrors, 0); | |||
| 1213 | } else { | |||
| 1214 | /* IO is pending, wait for it to finish */ | |||
| 1215 | dwWait = WaitForMultipleObjects(2, arHandles, FALSE0, INFINITE); | |||
| 1216 | if (dwWait == WAIT_OBJECT_0 + 1) { | |||
| 1217 | ret = MODEM_POLL_READ; | |||
| 1218 | } | |||
| 1219 | } | |||
| 1220 | } | |||
| 1221 | else { | |||
| 1222 | ret = MODEM_POLL_READ; | |||
| 1223 | } | |||
| 1224 | ||||
| 1225 | CloseHandle (o.hEvent); | |||
| 1226 | return ret; | |||
| 1227 | } | |||
| 1228 | #endif | |||
| 1229 | ||||
| 1230 | static void *SWITCH_THREAD_FUNC modem_thread(switch_thread_t *thread, void *obj) | |||
| 1231 | { | |||
| 1232 | modem_t *modem = obj; | |||
| 1233 | int r, avail; | |||
| 1234 | #ifdef WIN32 | |||
| 1235 | DWORD readBytes; | |||
| 1236 | OVERLAPPED o; | |||
| 1237 | #endif | |||
| 1238 | char buf[T31_TX_BUF_LEN(4096)]; | |||
| 1239 | ||||
| 1240 | switch_mutex_lock(globals.mutex); | |||
| 1241 | modem_init(modem, control_handler); | |||
| 
 | ||||
| 1242 | globals.THREADCOUNT++; | |||
| 1243 | switch_mutex_unlock(globals.mutex); | |||
| 1244 | ||||
| 1245 | if (switch_test_flag(modem, MODEM_FLAG_RUNNING)((modem)->flags & MODEM_FLAG_RUNNING)) { | |||
| 1246 | switch_mutex_lock(modem->cond_mutex); | |||
| 1247 | ||||
| 1248 | while (switch_test_flag(modem, MODEM_FLAG_RUNNING)((modem)->flags & MODEM_FLAG_RUNNING)) { | |||
| 1249 | ||||
| 1250 | #ifndef WIN32 | |||
| 1251 | r = modem_wait_sock(modem->master, -1, MODEM_POLL_READ | MODEM_POLL_ERROR); | |||
| 1252 | #else | |||
| 1253 | r = modem_wait_sock(modem, -1, MODEM_POLL_READ | MODEM_POLL_ERROR); | |||
| 1254 | #endif | |||
| 1255 | ||||
| 1256 | if (!switch_test_flag(modem, MODEM_FLAG_RUNNING)((modem)->flags & MODEM_FLAG_RUNNING)) { | |||
| 1257 | break; | |||
| 1258 | } | |||
| 1259 | ||||
| 1260 | if (r < 0 || !(r & MODEM_POLL_READ) || (r & MODEM_POLL_ERROR)) { | |||
| 1261 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1261, ((void*)0), SWITCH_LOG_WARNING, "Bad Read on master [%s] [%d]\n", modem->devlink, r); | |||
| 1262 | break; | |||
| 1263 | } | |||
| 1264 | ||||
| 1265 | modem->last_event = switch_time_now(); | |||
| 1266 | ||||
| 1267 | if (switch_test_flag(modem, MODEM_FLAG_XOFF)((modem)->flags & MODEM_FLAG_XOFF)) { | |||
| 1268 | switch_thread_cond_wait(modem->cond, modem->cond_mutex); | |||
| 1269 | modem->last_event = switch_time_now(); | |||
| 1270 | } | |||
| 1271 | ||||
| 1272 | avail = t31_at_rx_free_space(modem->t31_state); | |||
| 1273 | if (avail == 0) { | |||
| 1274 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1274, ((void*)0), SWITCH_LOG_WARNING, "Buffer Full, retrying....\n"); | |||
| 1275 | switch_yield(10000)switch_sleep(10000);; | |||
| 1276 | continue; | |||
| 1277 | } | |||
| 1278 | ||||
| 1279 | #ifndef WIN32 | |||
| 1280 | r = read(modem->master, buf, avail); | |||
| 1281 | #else | |||
| 1282 | o.hEvent = CreateEvent(NULL((void*)0), TRUE(!0), FALSE0, NULL((void*)0)); | |||
| 1283 | ||||
| 1284 | /* Initialize the rest of the OVERLAPPED structure to zero. */ | |||
| 1285 | o.Internal = 0; | |||
| 1286 | o.InternalHigh = 0; | |||
| 1287 | o.Offset = 0; | |||
| 1288 | o.OffsetHigh = 0; | |||
| 1289 | assert(o.hEvent)((o.hEvent) ? (void) (0) : __assert_fail ("o.hEvent", "mod_spandsp_modem.c" , 1289, __PRETTY_FUNCTION__)); | |||
| 1290 | if (!ReadFile(modem->master, buf, avail, &readBytes, &o)) { | |||
| 1291 | GetOverlappedResult(modem->master, &o, &readBytes,TRUE(!0)); | |||
| 1292 | } | |||
| 1293 | CloseHandle (o.hEvent); | |||
| 1294 | r = readBytes; | |||
| 1295 | #endif | |||
| 1296 | t31_at_rx(modem->t31_state, buf, r); | |||
| 1297 | ||||
| 1298 | if (!strncasecmp(buf, "AT", 2)) { | |||
| 1299 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1299, ((void*)0), SWITCH_LOG_DEBUG1, "Command on %s [%s]\n", modem->devlink, buf); | |||
| 1300 | } | |||
| 1301 | } | |||
| 1302 | ||||
| 1303 | switch_mutex_unlock(modem->cond_mutex); | |||
| 1304 | ||||
| 1305 | if (switch_test_flag(modem, MODEM_FLAG_RUNNING)((modem)->flags & MODEM_FLAG_RUNNING)) { | |||
| 1306 | modem_close(modem); | |||
| 1307 | } | |||
| 1308 | } | |||
| 1309 | ||||
| 1310 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1310, ((void*)0), SWITCH_LOG_INFO, "Thread ended for %s\n", modem->devlink); | |||
| 1311 | ||||
| 1312 | switch_mutex_lock(globals.mutex); | |||
| 1313 | globals.THREADCOUNT--; | |||
| 1314 | switch_mutex_unlock(globals.mutex); | |||
| 1315 | ||||
| 1316 | return NULL((void*)0); | |||
| 1317 | } | |||
| 1318 | ||||
| 1319 | static void launch_modem_thread(modem_t *modem) | |||
| 1320 | { | |||
| 1321 | switch_thread_t *thread; | |||
| 1322 | switch_threadattr_t *thd_attr = NULL((void*)0); | |||
| 1323 | ||||
| 1324 | switch_threadattr_create(&thd_attr, globals.pool); | |||
| 1325 | switch_threadattr_detach_set(thd_attr, 1); | |||
| 1326 | switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE240 * 1024); | |||
| 1327 | switch_thread_create(&thread, thd_attr, modem_thread, modem, globals.pool); | |||
| 1328 | } | |||
| 1329 | ||||
| 1330 | static void activate_modems(void) | |||
| 1331 | { | |||
| 1332 | int max = globals.SOFT_MAX_MODEMS; | |||
| 1333 | int x; | |||
| 1334 | ||||
| 1335 | switch_mutex_lock(globals.mutex); | |||
| 1336 | memset(globals.MODEM_POOL, 0, sizeof(globals.MODEM_POOL)); | |||
| 1337 | for (x = 0; x < max; x++) { | |||
| 1338 | ||||
| 1339 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1339, ((void*)0), SWITCH_LOG_INFO, "Starting Modem SLOT %d\n", x); | |||
| 1340 | ||||
| 1341 | globals.MODEM_POOL[x].slot = x; | |||
| 1342 | launch_modem_thread(&globals.MODEM_POOL[x]); | |||
| 1343 | } | |||
| 1344 | switch_mutex_unlock(globals.mutex); | |||
| 1345 | } | |||
| 1346 | ||||
| 1347 | static void deactivate_modems(void) | |||
| 1348 | { | |||
| 1349 | int max = globals.SOFT_MAX_MODEMS; | |||
| 1350 | int x; | |||
| 1351 | ||||
| 1352 | switch_mutex_lock(globals.mutex); | |||
| 1353 | ||||
| 1354 | for (x = 0; x < max; x++) { | |||
| 1355 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1355, ((void*)0), SWITCH_LOG_INFO, "Stopping Modem SLOT %d\n", x); | |||
| 1356 | modem_close(&globals.MODEM_POOL[x]); | |||
| 1357 | } | |||
| 1358 | ||||
| 1359 | switch_mutex_unlock(globals.mutex); | |||
| 1360 | ||||
| 1361 | /* Wait for Threads to die */ | |||
| 1362 | while (globals.THREADCOUNT) { | |||
| 1363 | switch_yield(100000)switch_sleep(100000);; | |||
| 1364 | } | |||
| 1365 | } | |||
| 1366 | ||||
| 1367 | static modem_t *acquire_modem(int index) | |||
| 1368 | { | |||
| 1369 | modem_t *modem = NULL((void*)0); | |||
| 1370 | switch_time_t now = switch_time_now(); | |||
| 1371 | int64_t idle_debounce = 2000000; | |||
| 1372 | ||||
| 1373 | switch_mutex_lock(globals.mutex); | |||
| 1374 | if (index > -1 && index < globals.SOFT_MAX_MODEMS) { | |||
| 1375 | modem = &globals.MODEM_POOL[index]; | |||
| 1376 | } else { | |||
| 1377 | int x; | |||
| 1378 | ||||
| 1379 | for (x = 0; x < globals.SOFT_MAX_MODEMS; x++) { | |||
| 1380 | if (globals.MODEM_POOL[x].state == MODEM_STATE_ONHOOK && (now - globals.MODEM_POOL[x].last_event) > idle_debounce) { | |||
| 1381 | modem = &globals.MODEM_POOL[x]; | |||
| 1382 | break; | |||
| 1383 | } | |||
| 1384 | } | |||
| 1385 | } | |||
| 1386 | ||||
| 1387 | if (modem && (modem->state != MODEM_STATE_ONHOOK || (now - modem->last_event) < idle_debounce)) { | |||
| 1388 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1388, ((void*)0), SWITCH_LOG_ERROR, "Modem %s In Use!\n", modem->devlink); | |||
| 1389 | modem = NULL((void*)0); | |||
| 1390 | } | |||
| 1391 | ||||
| 1392 | if (modem) { | |||
| 1393 | modem_set_state(modem, MODEM_STATE_ACQUIRED)_modem_set_state(modem, MODEM_STATE_ACQUIRED, "mod_spandsp_modem.c" , (const char *)__func__, 1393); | |||
| 1394 | modem->last_event = switch_time_now(); | |||
| 1395 | } else { | |||
| 1396 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1396, ((void*)0), SWITCH_LOG_ERROR, "No Modems Available!\n"); | |||
| 1397 | } | |||
| 1398 | ||||
| 1399 | switch_mutex_unlock(globals.mutex); | |||
| 1400 | ||||
| 1401 | return modem; | |||
| 1402 | } | |||
| 1403 | ||||
| 1404 | switch_status_t modem_global_init(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) | |||
| 1405 | { | |||
| 1406 | memset(&globals, 0, sizeof(globals)); | |||
| 1407 | globals.pool = pool; | |||
| 1408 | ||||
| 1409 | globals.SOFT_MAX_MODEMS = spandsp_globals.modem_count; | |||
| 1410 | ||||
| 1411 | switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED0x1, pool); | |||
| 1412 | ||||
| 1413 | modem_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); | |||
| 1414 | modem_endpoint_interface->interface_name = "modem"; | |||
| 1415 | modem_endpoint_interface->io_routines = &channel_io_routines; | |||
| 1416 | modem_endpoint_interface->state_handler = &channel_event_handlers; | |||
| 1417 | ||||
| 1418 | activate_modems(); | |||
| 1419 | ||||
| 1420 | return SWITCH_STATUS_SUCCESS; | |||
| 1421 | } | |||
| 1422 | ||||
| 1423 | void modem_global_shutdown(void) | |||
| 1424 | { | |||
| 1425 | deactivate_modems(); | |||
| 1426 | } | |||
| 1427 | ||||
| 1428 | #else | |||
| 1429 | ||||
| 1430 | void modem_global_init(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) | |||
| 1431 | { | |||
| 1432 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1432, ((void*)0), SWITCH_LOG_INFO, "Modem support disabled\n"); | |||
| 1433 | } | |||
| 1434 | ||||
| 1435 | void modem_global_shutdown(void) | |||
| 1436 | { | |||
| 1437 | switch_log_printf(SWITCH_CHANNEL_LOGSWITCH_CHANNEL_ID_LOG, "mod_spandsp_modem.c", (const char *)__func__ , 1437, ((void*)0), SWITCH_LOG_INFO, "Modem support disabled\n"); | |||
| 1438 | } | |||
| 1439 | ||||
| 1440 | #endif | |||
| 1441 | ||||
| 1442 | /* For Emacs: | |||
| 1443 | * Local Variables: | |||
| 1444 | * mode:c | |||
| 1445 | * indent-tabs-mode:nil | |||
| 1446 | * tab-width:4 | |||
| 1447 | * c-basic-offset:4 | |||
| 1448 | * End: | |||
| 1449 | * For VIM: | |||
| 1450 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: | |||
| 1451 | */ |