From 7242c94ea49582dd5ca4376017cb56d3e6d4fee3 Mon Sep 17 00:00:00 2001 From: Giovanni Maruzzelli <gmaruzz@gmail.com> Date: Fri, 6 Apr 2012 21:22:31 +0200 Subject: [PATCH] gsmopen: committed files from master (old gsmopen) in src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/ --- .../mod_gsmopen/gsmopen.h | 665 +++ .../mod_gsmopen/gsmopen_protocol.cpp | 4004 +++++++++++++++++ .../mod_gsmopen/mod_gsmopen.cpp | 3474 ++++++++++++++ 3 files changed, 8143 insertions(+) create mode 100644 src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen.h create mode 100644 src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.cpp create mode 100644 src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.cpp diff --git a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen.h b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen.h new file mode 100644 index 0000000000..37eb15741c --- /dev/null +++ b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen.h @@ -0,0 +1,665 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2011, Anthony Minessale II <anthm@freeswitch.org> + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II <anthm@freeswitch.org> + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * This module (mod_gsmopen) has been contributed by: + * + * Giovanni Maruzzelli (gmaruzz@gmail.com) + * + * + * Further Contributors: + * + * + * + * mod_gsmopen.c -- GSM compatible Endpoint Module + * + */ + +#define __STDC_LIMIT_MACROS + +#ifdef WIN32 +#define HAVE_VSNPRINTF +#pragma warning(disable: 4290) +#endif //WIN32 + +#define MY_EVENT_INCOMING_SMS "gsmopen::incoming_sms" +#define MY_EVENT_DUMP "gsmopen::dump_event" +#define MY_EVENT_ALARM "gsmopen::alarm" + +#define ALARM_FAILED_INTERFACE 0 +#define ALARM_NO_NETWORK_REGISTRATION 1 +#define ALARM_ROAMING_NETWORK_REGISTRATION 2 +#define ALARM_NETWORK_NO_SERVICE 3 +#define ALARM_NETWORK_NO_SIGNAL 4 +#define ALARM_NETWORK_LOW_SIGNAL 5 + + + + + +#undef GIOVA48 + +#ifndef GIOVA48 +#define SAMPLES_PER_FRAME 160 +#else // GIOVA48 +#define SAMPLES_PER_FRAME 960 +#endif // GIOVA48 + + +#ifndef GIOVA48 +#define GSMOPEN_FRAME_SIZE 160 +#else //GIOVA48 +#define GSMOPEN_FRAME_SIZE 960 +#endif //GIOVA48 +#define SAMPLERATE_GSMOPEN 8000 + +#ifndef NO_ALSA +#define GSMOPEN_ALSA +#endif // NO_ALSA +#include <switch.h> +#include <switch_version.h> +#ifndef WIN32 +#include <termios.h> +#include <sys/ioctl.h> +#include <iconv.h> +#endif //WIN32 +//#include <libteletone.h> + +#ifdef GSMOPEN_ALSA +#define ALSA_PCM_NEW_HW_PARAMS_API +#define ALSA_PCM_NEW_SW_PARAMS_API +#include <alsa/asoundlib.h> +#endif /* GSMOPEN_ALSA */ + +#ifdef GSMOPEN_PORTAUDIO +#include "pablio.h" +#undef WANT_SPEEX +#ifdef WANT_SPEEX +#include "speex/speex_preprocess.h" +#include "speex/speex_echo.h" +#endif /* WANT_SPEEX */ +#endif// GSMOPEN_PORTAUDIO + +//#include "celliax_spandsp.h" +#ifndef WIN32 +#include <sys/time.h> +//#include <X11/Xlib.h> +//#include <X11/Xlibint.h> +//#include <X11/Xatom.h> +#endif //WIN32 + +#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES +#include <spandsp.h> +#include <spandsp/version.h> + +#ifdef _MSC_VER +//Windows macro for FD_SET includes a warning C4127: conditional expression is constant +#pragma warning(push) +#pragma warning(disable:4127) +#endif + +#define PROTOCOL_ALSA_VOICEMODEM 4 +#define PROTOCOL_AT 2 +#define PROTOCOL_FBUS2 1 +#define PROTOCOL_NO_SERIAL 3 + +#define AT_BUFSIZ 8192 +//FIXME FIXME FIXME #define AT_MESG_MAX_LENGTH 2048 /* much more than 10 SMSs */ +#define AT_MESG_MAX_LENGTH 2048 /* much more than 10 SMSs */ +//FIXME FIXME FIXME #define AT_MESG_MAX_LINES 256 /* 256 lines, so it can contains the results of AT+CLAC, that gives all the AT commands the phone supports */ +#define AT_MESG_MAX_LINES 20 /* 256 lines, so it can contains the results of AT+CLAC, that gives all the AT commands the phone supports */ + +//#define SAMPLERATE_GSMOPEN 16000 +//#define SAMPLES_PER_FRAME SAMPLERATE_GSMOPEN/50 + +#ifndef GSMOPEN_SVN_VERSION +#define GSMOPEN_SVN_VERSION SWITCH_VERSION_REVISION +#endif /* GSMOPEN_SVN_VERSION */ + +typedef enum { + TFLAG_IO = (1 << 0), + TFLAG_INBOUND = (1 << 1), + TFLAG_OUTBOUND = (1 << 2), + TFLAG_DTMF = (1 << 3), + TFLAG_VOICE = (1 << 4), + TFLAG_HANGUP = (1 << 5), + TFLAG_LINEAR = (1 << 6), + TFLAG_CODEC = (1 << 7), + TFLAG_BREAK = (1 << 8) +} TFLAGS; + +typedef enum { + GFLAG_MY_CODEC_PREFS = (1 << 0) +} GFLAGS; + +#define DEBUGA_GSMOPEN(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rev "GSMOPEN_SVN_VERSION "[%p|%-7lx][DEBUG_GSMOPEN %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_CALL(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rev "GSMOPEN_SVN_VERSION "[%p|%-7lx][DEBUG_CALL %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define DEBUGA_PBX(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rev "GSMOPEN_SVN_VERSION "[%p|%-7lx][DEBUG_PBX %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define ERRORA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "rev "GSMOPEN_SVN_VERSION "[%p|%-7lx][ERRORA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define WARNINGA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "rev "GSMOPEN_SVN_VERSION "[%p|%-7lx][WARNINGA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); +#define NOTICA(...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "rev "GSMOPEN_SVN_VERSION "[%p|%-7lx][NOTICA %-5d][%-10s][%2d,%2d,%2d] " __VA_ARGS__ ); + +#define GSMOPEN_P_LOG (void *)NULL, (unsigned long)55, __LINE__, tech_pvt ? tech_pvt->name ? tech_pvt->name : "none" : "none", -1, tech_pvt ? tech_pvt->interface_state : -1, tech_pvt ? tech_pvt->phone_callflow : -1 + +/*********************************/ +#define GSMOPEN_CAUSE_NORMAL 1 +#define GSMOPEN_CAUSE_FAILURE 2 +#define GSMOPEN_CAUSE_NO_ANSWER 3 +/*********************************/ +#define GSMOPEN_FRAME_DTMF 1 +/*********************************/ +#define GSMOPEN_CONTROL_RINGING 1 +#define GSMOPEN_CONTROL_ANSWER 2 +#define GSMOPEN_CONTROL_HANGUP 3 +#define GSMOPEN_CONTROL_BUSY 4 + +/*********************************/ +#define GSMOPEN_STATE_IDLE 0 +#define GSMOPEN_STATE_DOWN 1 +#define GSMOPEN_STATE_RING 2 +#define GSMOPEN_STATE_DIALING 3 +#define GSMOPEN_STATE_BUSY 4 +#define GSMOPEN_STATE_UP 5 +#define GSMOPEN_STATE_RINGING 6 +#define GSMOPEN_STATE_PRERING 7 +#define GSMOPEN_STATE_ERROR_DOUBLE_CALL 8 +#define GSMOPEN_STATE_SELECTED 9 +#define GSMOPEN_STATE_HANGUP_REQUESTED 10 +#define GSMOPEN_STATE_PREANSWER 11 +/*********************************/ +/* call flow from the device */ +#define CALLFLOW_CALL_IDLE 0 +#define CALLFLOW_CALL_DOWN 1 +#define CALLFLOW_INCOMING_RING 2 +#define CALLFLOW_CALL_DIALING 3 +#define CALLFLOW_CALL_LINEBUSY 4 +#define CALLFLOW_CALL_ACTIVE 5 +#define CALLFLOW_INCOMING_HANGUP 6 +#define CALLFLOW_CALL_RELEASED 7 +#define CALLFLOW_CALL_NOCARRIER 8 +#define CALLFLOW_CALL_INFLUX 9 +#define CALLFLOW_CALL_INCOMING 10 +#define CALLFLOW_CALL_FAILED 11 +#define CALLFLOW_CALL_NOSERVICE 12 +#define CALLFLOW_CALL_OUTGOINGRESTRICTED 13 +#define CALLFLOW_CALL_SECURITYFAIL 14 +#define CALLFLOW_CALL_NOANSWER 15 +#define CALLFLOW_STATUS_FINISHED 16 +#define CALLFLOW_STATUS_CANCELLED 17 +#define CALLFLOW_STATUS_FAILED 18 +#define CALLFLOW_STATUS_REFUSED 19 +#define CALLFLOW_STATUS_RINGING 20 +#define CALLFLOW_STATUS_INPROGRESS 21 +#define CALLFLOW_STATUS_UNPLACED 22 +#define CALLFLOW_STATUS_ROUTING 23 +#define CALLFLOW_STATUS_EARLYMEDIA 24 +#define CALLFLOW_INCOMING_CALLID 25 +#define CALLFLOW_STATUS_REMOTEHOLD 26 +#define CALLFLOW_CALL_REMOTEANSWER 27 +#define CALLFLOW_CALL_HANGUP_REQUESTED 28 + +/*********************************/ + +#define AT_OK 0 +#define AT_ERROR 1 + +#define GSMOPEN_MAX_INTERFACES 64 + +#ifndef WIN32 +struct GSMopenHandles { + //Window gsmopen_win; + //Display *disp; + //Window win; + int currentuserhandle; + int api_connected; + int fdesc[2]; +}; +#else //WIN32 + +struct GSMopenHandles { + HWND win32_hInit_MainWindowHandle; + HWND win32_hGlobal_GSMAPIWindowHandle; + HINSTANCE win32_hInit_ProcessHandle; + char win32_acInit_WindowClassName[128]; + UINT win32_uiGlobal_MsgID_GSMControlAPIAttach; + UINT win32_uiGlobal_MsgID_GSMControlAPIDiscover; + int currentuserhandle; + int api_connected; + switch_file_t *fdesc[2]; +}; + +#endif //WIN32 + +/*! + * \brief structure for storing the results of AT commands, in an array of AT_MESG_MAX_LINES * AT_MESG_MAX_LENGTH chars + */ +struct s_result { + int elemcount; + char result[AT_MESG_MAX_LINES][AT_MESG_MAX_LENGTH]; +}; + +struct ciapa_struct { + int state; + int hangupcause; +}; +typedef struct ciapa_struct ciapa_t; + +struct private_object { + unsigned int flags; + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + char session_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_caller_profile_t *caller_profile; + switch_mutex_t *mutex; + switch_mutex_t *flag_mutex; + + char id[80]; + char name[80]; + char dialplan[80]; + char context[80]; + char dial_regex[256]; + char fail_dial_regex[256]; + char hold_music[256]; + char type[256]; + char X11_display[256]; +#ifdef WIN32 + unsigned short tcp_cli_port; + unsigned short tcp_srv_port; +#else + int tcp_cli_port; + int tcp_srv_port; +#endif + struct GSMopenHandles GSMopenHandles; + + int interface_state; /*!< \brief 'state' of the interface (channel) */ + char language[80]; /*!< \brief default Asterisk dialplan language for this interface */ + char exten[80]; /*!< \brief default Asterisk dialplan extension for this interface */ + int gsmopen_sound_rate; /*!< \brief rate of the sound device, in Hz, eg: 8000 */ + char callid_name[50]; + char callid_number[50]; + double playback_boost; + double capture_boost; + int stripmsd; + char gsmopen_call_id[512]; + int gsmopen_call_ongoing; + char gsmopen_friends[4096]; + char gsmopen_fullname[512]; + char gsmopen_displayname[512]; + int phone_callflow; /*!< \brief 'callflow' of the gsmopen interface (as opposed to phone interface) */ + int gsmopen; /*!< \brief config flag, bool, GSM support on this interface (0 if false, -1 if true) */ + int control_to_send; +#ifdef WIN32 + switch_file_t *audiopipe[2]; + switch_file_t *audiogsmopenpipe[2]; + switch_file_t *gsmopen_sound_capt_fd; /*!< \brief file descriptor for sound capture dev */ +#else /* WIN32 */ + int audiopipe[2]; + int audiogsmopenpipe[2]; + int gsmopen_sound_capt_fd; /*!< \brief file descriptor for sound capture dev */ +#endif /* WIN32 */ + switch_thread_t *tcp_srv_thread; + switch_thread_t *tcp_cli_thread; + switch_thread_t *gsmopen_signaling_thread; + switch_thread_t *gsmopen_api_thread; + //short audiobuf[SAMPLES_PER_FRAME]; + //int audiobuf_is_loaded; + + //int phonebook_listing; + //int phonebook_querying; + //int phonebook_listing_received_calls; + + //int phonebook_first_entry; + //int phonebook_last_entry; + //int phonebook_number_lenght; + //int phonebook_text_lenght; + int gsmopen_dir_entry_extension_prefix; + char gsmopen_user[256]; + char gsmopen_password[256]; + char destination[256]; + struct timeval answer_time; + + struct timeval transfer_time; + char transfer_callid_number[50]; + char gsmopen_transfer_call_id[512]; + int running; + unsigned long ib_calls; + unsigned long ob_calls; + unsigned long ib_failed_calls; + unsigned long ob_failed_calls; + + + char controldevice_name[50]; /*!< \brief name of the serial device controlling the interface, possibly none */ + int controldevprotocol; /*!< \brief which protocol is used for serial control of this interface */ + char controldevprotocolname[50]; /*!< \brief name of the serial device controlling protocol, one of "at" "fbus2" "no_serial" "alsa_voicemodem" */ + int controldevfd; /*!< \brief serial controlling file descriptor for this interface */ + //pthread_t controldev_thread; /*!< \brief serial control thread for this interface, running during the call */ +#ifdef WIN32 + int controldevice_speed; +#else + speed_t controldevice_speed; +#endif// WIN32 + int controldev_dead; + + char at_dial_pre_number[64]; + char at_dial_post_number[64]; + char at_dial_expect[64]; + unsigned int at_early_audio; + char at_hangup[64]; + char at_hangup_expect[64]; + char at_answer[64]; + char at_answer_expect[64]; + unsigned int at_initial_pause; + char at_preinit_1[64]; + char at_preinit_1_expect[64]; + char at_preinit_2[64]; + char at_preinit_2_expect[64]; + char at_preinit_3[64]; + char at_preinit_3_expect[64]; + char at_preinit_4[64]; + char at_preinit_4_expect[64]; + char at_preinit_5[64]; + char at_preinit_5_expect[64]; + unsigned int at_after_preinit_pause; + + char at_postinit_1[64]; + char at_postinit_1_expect[64]; + char at_postinit_2[64]; + char at_postinit_2_expect[64]; + char at_postinit_3[64]; + char at_postinit_3_expect[64]; + char at_postinit_4[64]; + char at_postinit_4_expect[64]; + char at_postinit_5[64]; + char at_postinit_5_expect[64]; + + char at_send_dtmf[64]; + + char at_query_battchg[64]; + char at_query_battchg_expect[64]; + char at_query_signal[64]; + char at_query_signal_expect[64]; + char at_call_idle[64]; + char at_call_incoming[64]; + char at_call_active[64]; + char at_call_failed[64]; + char at_call_calling[64]; + +#define CIEV_STRING_SIZE 64 + char at_indicator_noservice_string[64]; + char at_indicator_nosignal_string[64]; + char at_indicator_lowsignal_string[64]; + char at_indicator_lowbattchg_string[64]; + char at_indicator_nobattchg_string[64]; + char at_indicator_callactive_string[64]; + char at_indicator_nocallactive_string[64]; + char at_indicator_nocallsetup_string[64]; + char at_indicator_callsetupincoming_string[64]; + char at_indicator_callsetupoutgoing_string[64]; + char at_indicator_callsetupremoteringing_string[64]; + + int at_indicator_callp; + int at_indicator_callsetupp; + int at_indicator_roamp; + int at_indicator_battchgp; + int at_indicator_servicep; + int at_indicator_signalp; + + int at_has_clcc; + int at_has_ecam; + + char at_cmgw[16]; + int no_ucs2; + time_t gsmopen_serial_sync_period; + + time_t gsmopen_serial_synced_timestamp; + struct s_result line_array; + + + int unread_sms_msg_id; + int reading_sms_msg; + char sms_message[4800]; + char sms_sender[256]; + char sms_date[256]; + char sms_body[4800]; + char sms_datacodingscheme[256]; + char sms_servicecentreaddress[256]; + int sms_messagetype; + int sms_cnmi_not_supported; + int sms_pdu_not_supported; + //char sms_receiving_program[256]; + + + struct timeval call_incoming_time; + switch_mutex_t *controldev_lock; + + int phonebook_listing; + int phonebook_querying; + int phonebook_listing_received_calls; + + int phonebook_first_entry; + int phonebook_last_entry; + int phonebook_number_lenght; + int phonebook_text_lenght; + FILE *phonebook_writing_fp; + + struct timeval ringtime; + ciapa_t *owner; +#ifdef GSMOPEN_ALSA + snd_pcm_t *alsac; /*!< \brief handle of the ALSA capture audio device */ + snd_pcm_t *alsap; /*!< \brief handle of the ALSA playback audio device */ + char alsacname[50]; /*!< \brief name of the ALSA capture audio device */ + char alsapname[50]; /*!< \brief name of the ALSA playback audio device */ + int alsa_period_size; /*!< \brief ALSA period_size, in byte */ + int alsa_periods_in_buffer; /*!< \brief how many periods in ALSA buffer, to calculate buffer_size */ + unsigned long int alsa_buffer_size; /*!< \brief ALSA buffer_size, in byte */ + int alsawrite_filled; + int alsa_capture_is_mono; + int alsa_play_is_mono; + struct pollfd pfd; +#endif // GSMOPEN_ALSA + + time_t audio_play_reset_timestamp; + int audio_play_reset_period; + + switch_timer_t timer_read; + switch_timer_t timer_write; + teletone_dtmf_detect_state_t dtmf_detect; + switch_time_t old_dtmf_timestamp; + + int no_sound; + +#ifdef GSMOPEN_PORTAUDIO + int speexecho; + int speexpreprocess; + int portaudiocindex; /*!< \brief Index of the Portaudio capture audio device */ + int portaudiopindex; /*!< \brief Index of the Portaudio playback audio device */ + PABLIO_Stream *stream; + +#ifdef WANT_SPEEX + SpeexPreprocessState *preprocess; + SpeexEchoState *echo_state; +#endif// WANT_SPEEX +#endif// GSMOPEN_PORTAUDIO + dtmf_rx_state_t dtmf_state; + int active; + int home_network_registered; + int roaming_registered; + int not_registered; + int got_signal; + char imei[128]; + int requesting_imei; + char imsi[128]; + int requesting_imsi; + int network_creg_not_supported; + char creg[128]; + +}; + +typedef struct private_object private_t; + +void *SWITCH_THREAD_FUNC gsmopen_api_thread_func(switch_thread_t * thread, void *obj); +int gsmopen_audio_read(private_t * tech_pvt); +int gsmopen_audio_init(private_t * tech_pvt); +int gsmopen_signaling_read(private_t * tech_pvt); + +int gsmopen_call(private_t * tech_pvt, char *idest, int timeout); +int gsmopen_senddigit(private_t * tech_pvt, char digit); + +void *gsmopen_do_tcp_srv_thread_func(void *obj); +void *SWITCH_THREAD_FUNC gsmopen_do_tcp_srv_thread(switch_thread_t * thread, void *obj); + +void *gsmopen_do_tcp_cli_thread_func(void *obj); +void *SWITCH_THREAD_FUNC gsmopen_do_tcp_cli_thread(switch_thread_t * thread, void *obj); + +void *gsmopen_do_gsmopenapi_thread_func(void *obj); +void *SWITCH_THREAD_FUNC gsmopen_do_gsmopenapi_thread(switch_thread_t * thread, void *obj); +int dtmf_received(private_t * tech_pvt, char *value); +int start_audio_threads(private_t * tech_pvt); +int new_inbound_channel(private_t * tech_pvt); +int outbound_channel_answered(private_t * tech_pvt); +//int gsmopen_signaling_write(private_t * tech_pvt, char *msg_to_gsmopen); +#if defined(WIN32) && !defined(__CYGWIN__) +int gsmopen_pipe_read(switch_file_t * pipe, short *buf, int howmany); +int gsmopen_pipe_write(switch_file_t * pipe, short *buf, int howmany); +/* Visual C do not have strsep ? */ +char *strsep(char **stringp, const char *delim); +#else +int gsmopen_pipe_read(int pipe, short *buf, int howmany); +int gsmopen_pipe_write(int pipe, short *buf, int howmany); +#endif /* WIN32 */ +int gsmopen_close_socket(unsigned int fd); +private_t *find_available_gsmopen_interface_rr(private_t * tech_pvt_calling); +int remote_party_is_ringing(private_t * tech_pvt); +int remote_party_is_early_media(private_t * tech_pvt); +//int gsmopen_answer(private_t * tech_pvt, char *id, char *value); +#if 0 +int gsmopen_transfer(private_t * tech_pvt, char *id, char *value); +#endif //0 +int gsmopen_socket_create_and_bind(private_t * tech_pvt, int *which_port); + + + + + +void *gsmopen_do_controldev_thread(void *data); +#ifdef WIN32 +int gsmopen_serial_init(private_t * tech_pvt, int controldevice_speed); +#else +int gsmopen_serial_init(private_t * tech_pvt, speed_t controldevice_speed); +#endif //WIN32 +int gsmopen_serial_monitor(private_t * tech_pvt); +int gsmopen_serial_sync(private_t * tech_pvt); +int gsmopen_serial_sync_AT(private_t * tech_pvt); +int gsmopen_serial_config(private_t * tech_pvt); +int gsmopen_serial_config_AT(private_t * tech_pvt); + +#define gsmopen_serial_write_AT_expect(P, D, S) gsmopen_serial_write_AT_expect1(P, D, S, 1, 2) +#define gsmopen_serial_write_AT_expect_noexpcr(P, D, S) gsmopen_serial_write_AT_expect1(P, D, S, 0, 2) +#define gsmopen_serial_write_AT_expect_noexpcr_tout(P, D, S, T) gsmopen_serial_write_AT_expect1(P, D, S, 0, T) +// 20.5 sec timeout, used for querying the SIM and sending SMSs +#define gsmopen_serial_write_AT_expect_longtime(P, D, S) gsmopen_serial_write_AT_expect1(P, D, S, 1, 20) +#define gsmopen_serial_write_AT_expect_longtime_noexpcr(P, D, S) gsmopen_serial_write_AT_expect1(P, D, S, 0, 20) +int gsmopen_serial_write_AT(private_t * tech_pvt, const char *data); +int gsmopen_serial_write_AT_nocr(private_t * tech_pvt, const char *data); +int gsmopen_serial_write_AT_ack(private_t * tech_pvt, const char *data); +int gsmopen_serial_write_AT_ack_nocr_longtime(private_t * tech_pvt, const char *data); +int gsmopen_serial_write_AT_noack(private_t * tech_pvt, const char *data); +int gsmopen_serial_write_AT_expect1(private_t * tech_pvt, const char *data, const char *expected_string, int expect_crlf, int seconds); +int gsmopen_serial_AT_expect(private_t * tech_pvt, const char *expected_string, int expect_crlf, int seconds); +int gsmopen_serial_read_AT(private_t * tech_pvt, int look_for_ack, int timeout_usec, int timeout_sec, const char *expected_string, int expect_crlf); +int gsmopen_serial_read(private_t * tech_pvt); +#ifdef NOTDEF +int gsmopen_serial_getstatus(private_t * tech_pvt); +int gsmopen_serial_hangup(private_t * tech_pvt); +int gsmopen_serial_answer(private_t * tech_pvt); +int gsmopen_serial_answer_AT(private_t * tech_pvt); +int gsmopen_serial_hangup_AT(private_t * tech_pvt); +int gsmopen_serial_call_AT(private_t * tech_pvt, char *dstr); +int gsmopen_serial_getstatus_AT(private_t * tech_pvt); +#endif // NOTDEF +#define RESULT_FAILURE 0 +#define RESULT_SUCCESS 1 +int utf_to_ucs2(private_t * tech_pvt, char *utf_in, size_t inbytesleft, char *ucs2_out, size_t outbytesleft); +int ucs2_to_utf8(private_t * tech_pvt, char *ucs2_in, char *utf8_out, size_t outbytesleft); +//#define PUSHA_UNLOCKA(x) pthread_cleanup_push(gsmopen_unlocka_log, (void *) x); +//#define POPPA_UNLOCKA(x) pthread_cleanup_pop(0); + +#define PUSHA_UNLOCKA(x) if(option_debug > 100) ERRORA("PUSHA_UNLOCKA: %p\n", GSMOPEN_P_LOG, (void *)x); +#define POPPA_UNLOCKA(x) if(option_debug > 100) ERRORA("POPPA_UNLOCKA: %p\n", GSMOPEN_P_LOG, (void *)x); +//#define LOKKA(x) if(option_debug > 100) ERRORA("LOKKA: %p\n", GSMOPEN_P_LOG, (void *)x); +#define LOKKA(x) switch_mutex_lock(x); +#define UNLOCKA(x) switch_mutex_unlock(x); +//#define UNLOCKA(x) if(option_debug > 100) ERRORA("UNLOCKA: %p\n", GSMOPEN_P_LOG, (void *)x); + +#define gsmopen_queue_control(x, y) ERRORA("gsmopen_queue_control: %p, %d\n", GSMOPEN_P_LOG, (void *)x, y); + +#define ast_setstate(x, y) ERRORA("ast_setstate: %p, %d\n", GSMOPEN_P_LOG, (void *)x, y); + +int gsmopen_serial_read(private_t * tech_pvt); +int gsmopen_answer(private_t * tech_pvt); +int gsmopen_serial_answer(private_t * tech_pvt); +int gsmopen_serial_answer_AT(private_t * tech_pvt); +int gsmopen_serial_hangup(private_t * tech_pvt); +int gsmopen_serial_hangup_AT(private_t * tech_pvt); +int gsmopen_hangup(private_t * tech_pvt); +int gsmopen_serial_call(private_t * tech_pvt, char *dstr); +int gsmopen_serial_call_AT(private_t * tech_pvt, char *dstr); +int gsmopen_sendsms(private_t * tech_pvt, char *dest, char *text); + +#ifdef GSMOPEN_ALSA +int alsa_init(private_t * tech_pvt); +int alsa_shutdown(private_t * tech_pvt); +snd_pcm_t *alsa_open_dev(private_t * tech_pvt, snd_pcm_stream_t stream); +int alsa_write(private_t * tech_pvt, short *data, int datalen); +int alsa_read(private_t * tech_pvt, short *data, int datalen); + +#endif /* GSMOPEN_ALSA */ + + +void gsmopen_store_boost(char *s, double *boost); +int gsmopen_sound_boost(void *data, int samples_num, double boost); +int sms_incoming(private_t * tech_pvt); +int gsmopen_ring(private_t * tech_pvt); + +int iso_8859_1_to_utf8(private_t * tech_pvt, char *iso_8859_1_in, char *utf8_out, size_t outbytesleft); +int gsmopen_serial_getstatus_AT(private_t * tech_pvt); + + +#ifdef GSMOPEN_PORTAUDIO + +int gsmopen_portaudio_devlist(private_t *tech_pvt); + +int gsmopen_portaudio_init(private_t *tech_pvt); + +int gsmopen_portaudio_write(private_t * tech_pvt, short *data, int datalen); + +int gsmopen_portaudio_read(private_t * tech_pvt, short *data, int datalen); + + +int gsmopen_portaudio_shutdown(private_t *tech_pvt); + +#endif // GSMOPEN_PORTAUDIO +int dump_event(private_t *tech_pvt); +int alarm_event(private_t * tech_pvt, int alarm_code, const char *alarm_message); +int dump_event_full(private_t * tech_pvt, int is_alarm, int alarm_code, const char *alarm_message); diff --git a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.cpp b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.cpp new file mode 100644 index 0000000000..6834a1d880 --- /dev/null +++ b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/gsmopen_protocol.cpp @@ -0,0 +1,4004 @@ +#include "gsmopen.h" +//#include <iostream.h> + +#ifndef NO_GSMLIB +#include <gsmlib/gsm_sms.h> +#ifdef WIN32 +#include <gsmlib/gsm_win32_serial.h> +#else +#include <gsmlib/gsm_unix_serial.h> +#endif +#include <gsmlib/gsm_me_ta.h> +#include <iostream> + + +using namespace std; +using namespace gsmlib; +#endif// NO_GSMLIB + +#ifdef ASTERISK +#define gsmopen_sleep usleep +#define gsmopen_strncpy strncpy +#define tech_pvt p +extern int gsmopen_debug; +extern char *gsmopen_console_active; +#else /* FREESWITCH */ +#define gsmopen_sleep switch_sleep +#define gsmopen_strncpy switch_copy_string +extern switch_memory_pool_t *gsmopen_module_pool; +extern switch_endpoint_interface_t *gsmopen_endpoint_interface; +#endif /* ASTERISK */ +//int samplerate_gsmopen = SAMPLERATE_GSMOPEN; + +extern int running; +int gsmopen_dir_entry_extension = 1; + +int option_debug = 100; + + +#ifdef WIN32 +#define GSMLIBGIO +#else //WIN32 +#undef GSMLIBGIO +#endif //WIN32 + +#ifdef WIN32 +/***************/ +// from http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ + +#include <time.h> + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else /* */ +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif /* */ +struct sk_timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +int gettimeofday(struct timeval *tv, struct sk_timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch */ + tmpres /= 10; /*convert into microseconds */ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long) (tmpres / 1000000UL); + tv->tv_usec = (long) (tmpres % 1000000UL); + } + if (NULL != tz) { + if (!tzflag) { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + return 0; +} + +/***************/ +#endif /* WIN32 */ + +#ifdef GSMOPEN_PORTAUDIO +#include "pablio.h" + +#ifndef GIOVA48 +#define SAMPLES_PER_FRAME 160 +#else // GIOVA48 +#define SAMPLES_PER_FRAME 960 +#endif // GIOVA48 + +int gsmopen_portaudio_devlist(private_t *tech_pvt) +{ + int i, numDevices; + const PaDeviceInfo *deviceInfo; + + numDevices = Pa_GetDeviceCount(); + if (numDevices < 0) { + return 0; + } + for (i = 0; i < numDevices; i++) { + deviceInfo = Pa_GetDeviceInfo(i); + NOTICA + ("Found PORTAUDIO device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n", + GSMOPEN_P_LOG, i, deviceInfo->name, deviceInfo->maxInputChannels, + deviceInfo->maxOutputChannels); + } + + return numDevices; +} + +int gsmopen_portaudio_init(private_t *tech_pvt) +{ + PaError err; + int c; + PaStreamParameters inputParameters, outputParameters; + int numdevices; + const PaDeviceInfo *deviceInfo; + +#ifndef GIOVA48 + setenv("PA_ALSA_PLUGHW", "1", 1); +#endif // GIOVA48 + + err = Pa_Initialize(); + if (err != paNoError) + return err; + + numdevices = gsmopen_portaudio_devlist(tech_pvt); + + if (tech_pvt->portaudiocindex > (numdevices - 1)) { + ERRORA("Portaudio Capture id=%d is out of range: valid id are from 0 to %d\n", + GSMOPEN_P_LOG, tech_pvt->portaudiocindex, (numdevices - 1)); + return -1; + } + + if (tech_pvt->portaudiopindex > (numdevices - 1)) { + ERRORA("Portaudio Playback id=%d is out of range: valid id are from 0 to %d\n", + GSMOPEN_P_LOG, tech_pvt->portaudiopindex, (numdevices - 1)); + return -1; + } + //inputParameters.device = 0; + if (tech_pvt->portaudiocindex != -1) { + inputParameters.device = tech_pvt->portaudiocindex; + } else { + inputParameters.device = Pa_GetDefaultInputDevice(); + } + deviceInfo = Pa_GetDeviceInfo(inputParameters.device); + NOTICA + ("Using INPUT PORTAUDIO device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n", + GSMOPEN_P_LOG, inputParameters.device, deviceInfo->name, + deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); + if (deviceInfo->maxInputChannels == 0) { + ERRORA + ("No INPUT channels on device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n", + GSMOPEN_P_LOG, inputParameters.device, deviceInfo->name, + deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); + return -1; + } + inputParameters.channelCount = 1; + inputParameters.sampleFormat = paInt16; + //inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultHighInputLatency; + inputParameters.suggestedLatency = 0.1; + inputParameters.hostApiSpecificStreamInfo = NULL; + + //outputParameters.device = 3; + if (tech_pvt->portaudiopindex != -1) { + outputParameters.device = tech_pvt->portaudiopindex; + } else { + outputParameters.device = Pa_GetDefaultOutputDevice(); + } + deviceInfo = Pa_GetDeviceInfo(outputParameters.device); + NOTICA + ("Using OUTPUT PORTAUDIO device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n", + GSMOPEN_P_LOG, outputParameters.device, deviceInfo->name, + deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); + if (deviceInfo->maxOutputChannels == 0) { + ERRORA + ("No OUTPUT channels on device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n", + GSMOPEN_P_LOG, inputParameters.device, deviceInfo->name, + deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels); + return -1; + } +#ifndef GIOVA48 + outputParameters.channelCount = 1; +#else // GIOVA48 + outputParameters.channelCount = 2; +#endif // GIOVA48 + outputParameters.sampleFormat = paInt16; + //outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency; + outputParameters.suggestedLatency = 0.1; + outputParameters.hostApiSpecificStreamInfo = NULL; + +/* build the pipe that will be polled on by pbx */ + c = pipe(tech_pvt->audiopipe); + if (c) { + ERRORA("Unable to create audio pipe\n", GSMOPEN_P_LOG); + return -1; + } + fcntl(tech_pvt->audiopipe[0], F_SETFL, O_NONBLOCK); + fcntl(tech_pvt->audiopipe[1], F_SETFL, O_NONBLOCK); + + err = +#ifndef GIOVA48 + OpenAudioStream(&tech_pvt->stream, &inputParameters, &outputParameters, 8000, + paClipOff|paDitherOff, SAMPLES_PER_FRAME, 0); + //&tech_pvt->speexecho, &tech_pvt->speexpreprocess, &tech_pvt->owner); + +#else // GIOVA48 + OpenAudioStream(&tech_pvt->stream, &inputParameters, &outputParameters, 48000, + paDitherOff | paClipOff, SAMPLES_PER_FRAME, tech_pvt->audiopipe[1], + &tech_pvt->speexecho, &tech_pvt->speexpreprocess, &tech_pvt->owner); + + +#endif// GIOVA48 + if (err != paNoError) { + ERRORA("Unable to open audio stream: %s\n", GSMOPEN_P_LOG, Pa_GetErrorText(err)); + return -1; + } + +/* the pipe is our audio fd for pbx to poll on */ + tech_pvt->gsmopen_sound_capt_fd = tech_pvt->audiopipe[0]; + + return 0; +} +//int gsmopen_portaudio_write(private_t *tech_pvt, struct ast_frame *f) +int gsmopen_portaudio_write(private_t * tech_pvt, short *data, int datalen) +{ + int samples; +#ifdef GIOVA48 + //short buf[GSMOPEN_FRAME_SIZE * 2]; + short buf[3840]; + short *buf2; + + //ERRORA("1 f->datalen=: %d\n", GSMOPEN_P_LOG, f->datalen); + + + + + memset(buf, '\0', GSMOPEN_FRAME_SIZE *2); + + buf2 = f->data; + + int i=0, a=0; + + for(i=0; i< f->datalen / sizeof(short); i++){ +//stereo, 2 chan 48 -> mono 8 + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + buf[a] = buf2[i]; + a++; + /* + */ + } + f->data = &buf; + f->datalen = f->datalen * 6; + //ERRORA("2 f->datalen=: %d\n", GSMOPEN_P_LOG, f->datalen); + //f->datalen = f->datalen; +#endif // GIOVA48 + + + samples = + WriteAudioStream(tech_pvt->stream, (short *) data, (int) (datalen / sizeof(short)), &tech_pvt->timer_write); + + if (samples != (int) (datalen / sizeof(short))) + ERRORA("WriteAudioStream wrote: %d of %d\n", GSMOPEN_P_LOG, samples, + (int) (datalen / sizeof(short))); + + return samples; +} +//struct ast_frame *gsmopen_portaudio_read(private_t *tech_pvt) +#define AST_FRIENDLY_OFFSET 0 +int gsmopen_portaudio_read(private_t * tech_pvt, short *data, int datalen) +{ +#if 0 + //static struct ast_frame f; + static short __buf[GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2]; + short *buf; + static short __buf2[GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2]; + short *buf2; + int samples; + //char c; + + memset(__buf, '\0', (GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2)); + + buf = __buf + AST_FRIENDLY_OFFSET / 2; + + memset(__buf2, '\0', (GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2)); + + buf2 = __buf2 + AST_FRIENDLY_OFFSET / 2; + +#if 0 + f.frametype = AST_FRAME_NULL; + f.subclass = 0; + f.samples = 0; + f.datalen = 0; + +#ifdef ASTERISK_VERSION_1_6_1 + f.data.ptr = NULL; +#else + f.data = NULL; +#endif /* ASTERISK_VERSION_1_6_1 */ + f.offset = 0; + f.src = gsmopen_type; + f.mallocd = 0; + f.delivery.tv_sec = 0; + f.delivery.tv_usec = 0; +#endif //0 + + //if ((samples = ReadAudioStream(tech_pvt->stream, buf, SAMPLES_PER_FRAME)) == 0) + //if ((samples = ReadAudioStream(tech_pvt->stream, data, datalen/sizeof(short))) == 0) + if (samples = ReadAudioStream(tech_pvt->stream, (short *)data, datalen, &tech_pvt->timer_read) == 0) { + //do nothing + } else { +#ifdef GIOVA48 + int i=0, a=0; + + samples = samples / 6; + for(i=0; i< samples; i++){ + buf2[i] = buf[a]; + a = a + 6; //mono, 1 chan 48 -> 8 + } + buf = buf2; + +#if 0 + /* A real frame */ + f.frametype = AST_FRAME_VOICE; + f.subclass = AST_FORMAT_SLINEAR; + f.samples = GSMOPEN_FRAME_SIZE/6; + f.datalen = GSMOPEN_FRAME_SIZE * 2/6; +#endif //0 +#else// GIOVA48 +#if 0 + /* A real frame */ + f.frametype = AST_FRAME_VOICE; + f.subclass = AST_FORMAT_SLINEAR; + f.samples = GSMOPEN_FRAME_SIZE; + f.datalen = GSMOPEN_FRAME_SIZE * 2; +#endif //0 +#endif// GIOVA48 + +#if 0 +#ifdef ASTERISK_VERSION_1_6_1 + f.data.ptr = buf; +#else + f.data = buf; +#endif /* ASTERISK_VERSION_1_6_1 */ + f.offset = AST_FRIENDLY_OFFSET; + f.src = gsmopen_type; + f.mallocd = 0; +#endif //0 + } + +#if 0 + read(tech_pvt->audiopipe[0], &c, 1); + + return &f; +#endif //0 +#endif //0 + + int samples; + samples = ReadAudioStream(tech_pvt->stream, (short *)data, datalen, &tech_pvt->timer_read); + //WARNINGA("samples=%d\n", GSMOPEN_P_LOG, samples); + + return samples; +} +int gsmopen_portaudio_shutdown(private_t *tech_pvt) +{ + PaError err; + + err = CloseAudioStream(tech_pvt->stream); + + if (err != paNoError) + ERRORA("not able to CloseAudioStream\n", GSMOPEN_P_LOG); + + Pa_Terminate(); + return 0; +} + + + + +#endif // GSMOPEN_PORTAUDIO +#ifndef GSMLIBGIO +int gsmopen_serial_init(private_t * tech_pvt, speed_t controldevice_speed) +{ + int fd; + int rt; + struct termios tp; + unsigned int status = 0; + unsigned int flags = TIOCM_DTR; + +/* if there is a file descriptor, close it. But it is probably just an old value, so don't check for close success*/ + fd = tech_pvt->controldevfd; + if (fd) { + close(fd); + } +/* open the serial port */ +//#ifdef __CYGWIN__ + fd = open(tech_pvt->controldevice_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + sleep(1); + close(fd); +//#endif /* __CYGWIN__ */ + fd = open(tech_pvt->controldevice_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + perror("open error "); + DEBUGA_GSMOPEN("serial error: %s\n", GSMOPEN_P_LOG, strerror(errno)); + tech_pvt->controldevfd = fd; + return -1; + } +/* flush it */ + rt = tcflush(fd, TCIFLUSH); + if (rt == -1) { + ERRORA("serial error: %s", GSMOPEN_P_LOG, strerror(errno)); + } +/* attributes */ + tp.c_cflag = B0 | CS8 | CLOCAL | CREAD | HUPCL; + tp.c_iflag = IGNPAR; + tp.c_cflag &= ~CRTSCTS; + tp.c_oflag = 0; + tp.c_lflag = 0; + tp.c_cc[VMIN] = 1; + tp.c_cc[VTIME] = 0; +/* set controldevice_speed */ + rt = cfsetispeed(&tp, tech_pvt->controldevice_speed); + if (rt == -1) { + ERRORA("serial error: %s, speed was: %d", GSMOPEN_P_LOG, strerror(errno), tech_pvt->controldevice_speed); + } + rt = cfsetospeed(&tp, tech_pvt->controldevice_speed); + if (rt == -1) { + ERRORA("serial error: %s", GSMOPEN_P_LOG, strerror(errno)); + } +/* set port attributes */ + if (tcsetattr(fd, TCSADRAIN, &tp) == -1) { + ERRORA("serial error: %s", GSMOPEN_P_LOG, strerror(errno)); + } + rt = tcsetattr(fd, TCSANOW, &tp); + if (rt == -1) { + ERRORA("serial error: %s", GSMOPEN_P_LOG, strerror(errno)); + } +#ifndef __CYGWIN__ + ioctl(fd, TIOCMGET, &status); + status |= TIOCM_DTR; /* Set DTR high */ + status &= ~TIOCM_RTS; /* Set RTS low */ + ioctl(fd, TIOCMSET, &status); + ioctl(fd, TIOCMGET, &status); + ioctl(fd, TIOCMBIS, &flags); + flags = TIOCM_RTS; + ioctl(fd, TIOCMBIC, &flags); + ioctl(fd, TIOCMGET, &status); +#else /* __CYGWIN__ */ + ioctl(fd, TIOCMGET, &status); + status |= TIOCM_DTR; /* Set DTR high */ + status &= ~TIOCM_RTS; /* Set RTS low */ + ioctl(fd, TIOCMSET, &status); +#endif /* __CYGWIN__ */ + tech_pvt->controldevfd = fd; + DEBUGA_GSMOPEN("Syncing Serial, fd=%d, protocol=%d\n", GSMOPEN_P_LOG, fd, tech_pvt->controldevprotocol); + rt = gsmopen_serial_sync(tech_pvt); + if (rt == -1) { + ERRORA("Serial init error\n", GSMOPEN_P_LOG); + return -1; + } + return (fd); +} +#else //GSMLIBGIO +#ifdef WIN32 +int gsmopen_serial_init(private_t * tech_pvt, int controldevice_speed) +#else +int gsmopen_serial_init(private_t * tech_pvt, speed_t controldevice_speed) +#endif //WIN32 +{ + int i; + string ciapa; + SMSMessageRef sms; + char content2[1000]; + int size; + +#ifdef WIN32 + Ref <Port> port = new Win32SerialPort((string) tech_pvt->controldevice_name, 38400); +#else + //Ref<Port> port = new UnixSerialPort((string)argv[1], B38400); + Ref < Port > port = new UnixSerialPort((string) tech_pvt->controldevice_name, B115200); +#endif + MeTa m(port); + + //cout << "Creating GsmAt object" << endl; + Ref <GsmAt> gsmat = new GsmAt(m); + + //cout << "Using GsmAt object" << endl; + //cout << gsmat->chat("AT", "OK", false, false) << endl; + //cout << gsmat->chat("D3472665618;") << endl; + gsmat->putLine("AT+cgmm", true); + for (i = 0; i < 4; i++) { + ciapa = gsmat->getLine(); + //cout << "PRESO: |||" << ciapa << "|||" << endl; + NOTICA("PRESO %d |||%s|||\n", GSMOPEN_P_LOG, i, ciapa.c_str()); + //gsmopen_sleep(5000); + } + + sms = SMSMessage::decode("079194710167120004038571F1390099406180904480A0D41631067296EF7390383D07CD622E58CD95CB81D6EF39BDEC66BFE7207A794E2FBB4320AFB82C07E56020A8FC7D9687DBED32285C9F83A06F769A9E5EB340D7B49C3E1FA3C3663A0B24E4CBE76516680A7FCBE920725A5E5ED341F0B21C346D4E41E1BA790E4286DDE4BC0BD42CA3E5207258EE1797E5A0BA9B5E9683C86539685997EBEF61341B249BC966"); // dataCodingScheme = 0 + NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + sms = SMSMessage::decode("0791934329002000040C9193432766658100009001211133318004D4F29C0E"); // dataCodingScheme = 0 + NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + sms = SMSMessage::decode("0791934329002000040C919343276665810008900121612521801600CC00E800E900F900F200E00020006300690061006F"); // dataCodingScheme = 8 + NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + sms = SMSMessage::decode("0791934329002000040C919343276665810008900172002293404C006300690061006F0020003100320033002000620065006C00E80020043D043E0432043E044104420438002005DC05E7002005E805D005EA0020FE8EFEE0FEA0FEE4FECBFE9300204EBA5927"); // dataCodingScheme = 8 , text=ciao 123 belè новости לק ראת ﺎﻠﺠﻤﻋﺓ 人大 + NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + sms = SMSMessage::decode("07911497941902F00414D0E474989D769F5DE4320839001040122151820000"); // dataCodingScheme = 0 + NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + +#if 0 + size = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, 0); + UserName=(wchar_t*)GlobalAlloc(GME M_ZEROINIT, size); + ret = MultiByteToWideChar(CP_OEMCP, 0, username, strlen(username)+1, UserName, size); + if(ret == 0) + getError(GetLastError()); +#endif //0 + return (-1); +} + +#endif //GSMLIBGIO + + +int gsmopen_serial_read(private_t * tech_pvt) +{ + if (tech_pvt->controldevprotocol == PROTOCOL_AT) + return gsmopen_serial_read_AT(tech_pvt, 0, 100000, 0, NULL, 1); // a 10th of a second timeout +#ifdef GSMOPEN_FBUS2 + if (tech_pvt->controldevprotocol == PROTOCOL_FBUS2) + return gsmopen_serial_read_FBUS2(tech_pvt); +#endif /* GSMOPEN_FBUS2 */ +#ifdef GSMOPEN_CVM + if (tech_pvt->controldevprotocol == PROTOCOL_CVM_BUSMAIL) + return gsmopen_serial_read_CVM_BUSMAIL(tech_pvt); +#endif /* GSMOPEN_CVM */ + return -1; +} + + +int gsmopen_serial_sync(private_t * tech_pvt) +{ + if (tech_pvt->controldevprotocol == PROTOCOL_AT) + return gsmopen_serial_sync_AT(tech_pvt); +#ifdef GSMOPEN_FBUS2 + if (tech_pvt->controldevprotocol == PROTOCOL_FBUS2) + return gsmopen_serial_sync_FBUS2(tech_pvt); +#endif /* GSMOPEN_FBUS2 */ +#ifdef GSMOPEN_CVM + if (tech_pvt->controldevprotocol == PROTOCOL_CVM_BUSMAIL) + return gsmopen_serial_sync_CVM_BUSMAIL(tech_pvt); +#endif /* GSMOPEN_CVM */ + + return -1; +} + +int gsmopen_serial_config(private_t * tech_pvt) +{ +#ifndef NO_GSMLIB + SMSMessageRef sms; + char content2[1000]; + //sms = SMSMessage::decode("079194710167120004038571F1390099406180904480A0D41631067296EF7390383D07CD622E58CD95CB81D6EF39BDEC66BFE7207A794E2FBB4320AFB82C07E56020A8FC7D9687DBED32285C9F83A06F769A9E5EB340D7B49C3E1FA3C3663A0B24E4CBE76516680A7FCBE920725A5E5ED341F0B21C346D4E41E1BA790E4286DDE4BC0BD42CA3E5207258EE1797E5A0BA9B5E9683C86539685997EBEF61341B249BC966"); // dataCodingScheme = 0 + //sms = SMSMessage::decode("0791934329002000040C9193432766658100009001211133318004D4F29C0E"); // dataCodingScheme = 0 + //sms = SMSMessage::decode("0791934329002000040C919343276665810008900121612521801600CC00E800E900F900F200E00020006300690061006F"); // dataCodingScheme = 8 + sms = SMSMessage::decode("0791934329002000040C919343276665810008900172002293404C006300690061006F0020003100320033002000620065006C00E80020043D043E0432043E044104420438002005DC05E7002005E805D005EA0020FE8EFEE0FEA0FEE4FECBFE9300204EBA5927"); // dataCodingScheme = 8 , text=ciao 123 belè новости לק ראת ﺎﻠﺠﻤﻋﺓ 人大 + //sms = SMSMessage::decode("07911497941902F00414D0E474989D769F5DE4320839001040122151820000"); // dataCodingScheme = 0 + //NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + + memset(content2, '\0', sizeof(content2)); + if (sms->dataCodingScheme().getAlphabet() == DCS_DEFAULT_ALPHABET) { + iso_8859_1_to_utf8(tech_pvt, (char *) sms->userData().c_str(), content2, sizeof(content2)); + } else if (sms->dataCodingScheme().getAlphabet() == DCS_SIXTEEN_BIT_ALPHABET) { + ucs2_to_utf8(tech_pvt, (char *) bufToHex((unsigned char *) sms->userData().data(), sms->userData().length()).c_str(), content2, + sizeof(content2)); + } else { + ERRORA("dataCodingScheme not supported=%d\n", GSMOPEN_P_LOG, sms->dataCodingScheme().getAlphabet()); + + } + //NOTICA("dataCodingScheme=%d\n", GSMOPEN_P_LOG, sms->dataCodingScheme().getAlphabet()); + //NOTICA("userData= |||%s|||\n", GSMOPEN_P_LOG, content2); +#endif// NO_GSMLIB + + if (tech_pvt->controldevprotocol == PROTOCOL_AT) + return gsmopen_serial_config_AT(tech_pvt); +#ifdef GSMOPEN_FBUS2 + if (tech_pvt->controldevprotocol == PROTOCOL_FBUS2) + return gsmopen_serial_config_FBUS2(tech_pvt); +#endif /* GSMOPEN_FBUS2 */ +#ifdef GSMOPEN_CVM + if (tech_pvt->controldevprotocol == PROTOCOL_CVM_BUSMAIL) + return gsmopen_serial_config_CVM_BUSMAIL(tech_pvt); +#endif /* GSMOPEN_CVM */ + + return -1; +} + +int gsmopen_serial_config_AT(private_t * tech_pvt) +{ + int res; + char at_command[5]; + int i; + +/* initial_pause? */ + if (tech_pvt->at_initial_pause) { + DEBUGA_GSMOPEN("sleeping for %d usec\n", GSMOPEN_P_LOG, tech_pvt->at_initial_pause); + gsmopen_sleep(tech_pvt->at_initial_pause); + } + +/* go until first empty preinit string, or last preinit string */ + while (1) { + + if (strlen(tech_pvt->at_preinit_1)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_preinit_1, tech_pvt->at_preinit_1_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_preinit_1, tech_pvt->at_preinit_1_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_preinit_2)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_preinit_2, tech_pvt->at_preinit_2_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_preinit_2, tech_pvt->at_preinit_2_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_preinit_3)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_preinit_3, tech_pvt->at_preinit_3_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_preinit_3, tech_pvt->at_preinit_3_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_preinit_4)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_preinit_4, tech_pvt->at_preinit_4_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_preinit_4, tech_pvt->at_preinit_4_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_preinit_5)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_preinit_5, tech_pvt->at_preinit_5_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_preinit_5, tech_pvt->at_preinit_5_expect); + } + } else { + break; + } + + break; + } + +/* after_preinit_pause? */ + if (tech_pvt->at_after_preinit_pause) { + DEBUGA_GSMOPEN("sleeping for %d usec\n", GSMOPEN_P_LOG, tech_pvt->at_after_preinit_pause); + gsmopen_sleep(tech_pvt->at_after_preinit_pause); + } + + /* phone, brother, art you alive? */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT"); + if (res) { + ERRORA("no response to AT\n", GSMOPEN_P_LOG); + return -1; + } + /* for motorola, bring it back to "normal" mode if it happens to be in another mode */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+mode=0"); + if (res) { + DEBUGA_GSMOPEN("AT+mode=0 does not get OK from the phone. If it is NOT Motorola," " no problem.\n", GSMOPEN_P_LOG); + } + gsmopen_sleep(50000); + /* for motorola end */ + + /* reset AT configuration to phone default */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "ATZ"); + if (res) { + DEBUGA_GSMOPEN("ATZ failed\n", GSMOPEN_P_LOG); + } + + /* disable AT command echo */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "ATE0"); + if (res) { + DEBUGA_GSMOPEN("ATE0 failed\n", GSMOPEN_P_LOG); + } + + /* disable extended error reporting */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMEE=0"); + if (res) { + DEBUGA_GSMOPEN("AT+CMEE failed\n", GSMOPEN_P_LOG); + } + + /* various phone manufacturer identifier */ + for (i = 0; i < 10; i++) { + memset(at_command, 0, sizeof(at_command)); + sprintf(at_command, "ATI%d", i); + res = gsmopen_serial_write_AT_ack(tech_pvt, at_command); + if (res) { + DEBUGA_GSMOPEN("ATI%d command failed, continue\n", GSMOPEN_P_LOG, i); + } + } + + /* phone manufacturer */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CGMI"); + if (res) { + DEBUGA_GSMOPEN("AT+CGMI failed\n", GSMOPEN_P_LOG); + } + + /* phone model */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CGMM"); + if (res) { + DEBUGA_GSMOPEN("AT+CGMM failed\n", GSMOPEN_P_LOG); + } + + /* signal network registration with a +CREG unsolicited msg */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CREG=1"); + if (res) { + DEBUGA_GSMOPEN("AT+CREG=1 failed\n", GSMOPEN_P_LOG); + tech_pvt->network_creg_not_supported = 1; + } + if(!tech_pvt->network_creg_not_supported){ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CREG?"); + if (res) { + DEBUGA_GSMOPEN("AT+CREG? failed\n", GSMOPEN_P_LOG); + } + } + /* query signal strength */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSQ"); + if (res) { + DEBUGA_GSMOPEN("AT+CSQ failed\n", GSMOPEN_P_LOG); + } + /* IMEI */ + tech_pvt->requesting_imei = 1; + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+GSN"); + tech_pvt->requesting_imei = 0; + if (res) { + DEBUGA_GSMOPEN("AT+GSN failed\n", GSMOPEN_P_LOG); + tech_pvt->requesting_imei = 1; + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CGSN"); + tech_pvt->requesting_imei = 0; + if (res) { + DEBUGA_GSMOPEN("AT+CGSN failed\n", GSMOPEN_P_LOG); + } + } + /* IMSI */ + tech_pvt->requesting_imsi = 1; + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CIMI"); + tech_pvt->requesting_imsi = 0; + if (res) { + DEBUGA_GSMOPEN("AT+CIMI failed\n", GSMOPEN_P_LOG); + } + + /* signal incoming SMS with a +CMTI unsolicited msg */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CNMI=3,1,0,0,0"); + if (res) { + DEBUGA_GSMOPEN("AT+CNMI=3,1,0,0,0 failed, continue\n", GSMOPEN_P_LOG); + tech_pvt->sms_cnmi_not_supported = 1; + tech_pvt->gsmopen_serial_sync_period = 30; //FIXME in config + } + /* what is the Message Center address (number) to which the SMS has to be sent? */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCA?"); + if (res) { + DEBUGA_GSMOPEN("AT+CSCA? failed, continue\n", GSMOPEN_P_LOG); + } + /* what is the Message Format of SMSs? */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF?"); + if (res) { + DEBUGA_GSMOPEN("AT+CMGF? failed, continue\n", GSMOPEN_P_LOG); + } +#ifdef NO_GSMLIB + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF=1"); + if (res) { + ERRORA("Error setting SMS sending mode to TEXT on the cellphone, let's hope is TEXT by default. Continuing\n", GSMOPEN_P_LOG); + } + tech_pvt->sms_pdu_not_supported = 1; +#else // NO_GSMLIB + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF=0"); + if (res) { + WARNINGA("Error setting SMS sending mode to PDU on the cellphone, falling back to TEXT mode. Continuing\n", GSMOPEN_P_LOG); + tech_pvt->sms_pdu_not_supported = 1; + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF=1"); + if (res) { + ERRORA("Error setting SMS sending mode to TEXT on the cellphone, let's hope is TEXT by default. Continuing\n", GSMOPEN_P_LOG); + } + } +#endif // NO_GSMLIB + /* what is the Charset of SMSs? */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCS?"); + if (res) { + DEBUGA_GSMOPEN("AT+CSCS? failed, continue\n", GSMOPEN_P_LOG); + } + + tech_pvt->no_ucs2 = 0; + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCS=\"UCS2\""); + if (res) { + WARNINGA("AT+CSCS=\"UCS2\" (set TE messages to ucs2) do not got OK from the phone, let's try with 'GSM'\n", GSMOPEN_P_LOG); + tech_pvt->no_ucs2 = 1; + } + + if (tech_pvt->no_ucs2) { + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCS=\"GSM\""); + if (res) { + WARNINGA("AT+CSCS=\"GSM\" (set TE messages to GSM) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + //res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSMP=17,167,0,16"); //"flash", class 0 sms 7 bit + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSMP=17,167,0,0"); //normal, 7 bit message + if (res) { + WARNINGA("AT+CSMP do not got OK from the phone, continuing\n", GSMOPEN_P_LOG); + } + } else { + //res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSMP=17,167,0,20"); //"flash", class 0 sms 16 bit unicode + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSMP=17,167,0,8"); //unicode, 16 bit message + if (res) { + WARNINGA("AT+CSMP do not got OK from the phone, continuing\n", GSMOPEN_P_LOG); + } + } + + /* is the unsolicited reporting of mobile equipment event supported? */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMER=?"); + if (res) { + DEBUGA_GSMOPEN("AT+CMER=? failed, continue\n", GSMOPEN_P_LOG); + } + /* request unsolicited reporting of mobile equipment indicators' events, to be screened by categories reported by +CIND=? */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMER=3,0,0,1"); + if (res) { + DEBUGA_GSMOPEN("AT+CMER=? failed, continue\n", GSMOPEN_P_LOG); + } + + /* is the solicited reporting of mobile equipment indications supported? */ + + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CIND=?"); + if (res) { + DEBUGA_GSMOPEN("AT+CIND=? failed, continue\n", GSMOPEN_P_LOG); + } + + /* is the unsolicited reporting of call monitoring supported? sony-ericsson specific */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT*ECAM=?"); + if (res) { + DEBUGA_GSMOPEN("AT*ECAM=? failed, continue\n", GSMOPEN_P_LOG); + } + /* enable the unsolicited reporting of call monitoring. sony-ericsson specific */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT*ECAM=1"); + if (res) { + DEBUGA_GSMOPEN("AT*ECAM=1 failed, continue\n", GSMOPEN_P_LOG); + tech_pvt->at_has_ecam = 0; + } else { + tech_pvt->at_has_ecam = 1; + } + + /* disable unsolicited signaling of call list */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CLCC=0"); + if (res) { + DEBUGA_GSMOPEN("AT+CLCC=0 failed, continue\n", GSMOPEN_P_LOG); + tech_pvt->at_has_clcc = 0; + } else { + tech_pvt->at_has_clcc = 1; + } + + /* give unsolicited caller id when incoming call */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CLIP=1"); + if (res) { + DEBUGA_GSMOPEN("AT+CLIP failed, continue\n", GSMOPEN_P_LOG); + } + /* for motorola */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+MCST=1"); /* motorola call control codes + (to know when call is disconnected (they + don't give you "no carrier") */ + if (res) { + DEBUGA_GSMOPEN("AT+MCST=1 does not get OK from the phone. If it is NOT Motorola," " no problem.\n", GSMOPEN_P_LOG); + } + /* for motorola end */ + +/* go until first empty postinit string, or last postinit string */ + while (1) { + + if (strlen(tech_pvt->at_postinit_1)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_postinit_1, tech_pvt->at_postinit_1_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_postinit_1, tech_pvt->at_postinit_1_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_postinit_2)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_postinit_2, tech_pvt->at_postinit_2_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_postinit_2, tech_pvt->at_postinit_2_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_postinit_3)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_postinit_3, tech_pvt->at_postinit_3_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_postinit_3, tech_pvt->at_postinit_3_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_postinit_4)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_postinit_4, tech_pvt->at_postinit_4_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_postinit_4, tech_pvt->at_postinit_4_expect); + } + } else { + break; + } + + if (strlen(tech_pvt->at_postinit_5)) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_postinit_5, tech_pvt->at_postinit_5_expect); + if (res) { + DEBUGA_GSMOPEN("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, tech_pvt->at_postinit_5, tech_pvt->at_postinit_5_expect); + } + } else { + break; + } + + break; + } + + return 0; +} + + +int gsmopen_serial_sync_AT(private_t * tech_pvt) +{ + gsmopen_sleep(10000); /* 10msec */ + time(&tech_pvt->gsmopen_serial_synced_timestamp); + return 0; +} +int gsmopen_serial_read_AT(private_t * tech_pvt, int look_for_ack, int timeout_usec, int timeout_sec, const char *expected_string, int expect_crlf) +{ + int select_err = 1; + int res; + fd_set read_fds; + struct timeval timeout; + char tmp_answer[AT_BUFSIZ]; + char tmp_answer2[AT_BUFSIZ]; + char *tmp_answer_ptr; + char *last_line_ptr; + int i = 0; + int read_count = 0; + int la_counter = 0; + int at_ack = -1; + int la_read = 0; + + if(!running || !tech_pvt->running){ + return -1; + } + + FD_ZERO(&read_fds); + FD_SET(tech_pvt->controldevfd, &read_fds); + + //NOTICA (" INSIDE this gsmopen_serial_device %s \n", GSMOPEN_P_LOG, tech_pvt->controldevice_name); + tmp_answer_ptr = tmp_answer; + memset(tmp_answer, 0, sizeof(char) * AT_BUFSIZ); + + timeout.tv_sec = timeout_sec; + timeout.tv_usec = timeout_usec; + PUSHA_UNLOCKA(tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + + while ((!tech_pvt->controldev_dead) && ((select_err = select(tech_pvt->controldevfd + 1, &read_fds, NULL, NULL, &timeout)) > 0)) { + char *token_ptr; + timeout.tv_sec = timeout_sec; //reset the timeout, linux modify it + timeout.tv_usec = timeout_usec; //reset the timeout, linux modify it + read_count = read(tech_pvt->controldevfd, tmp_answer_ptr, AT_BUFSIZ - (tmp_answer_ptr - tmp_answer)); + + if (read_count == 0) { + ERRORA + ("read 0 bytes!!! Nenormalno! Marking this gsmopen_serial_device %s as dead, andif it is owned by a channel, hanging up. Maybe the phone is stuck, switched off, power down or battery exhausted\n", + GSMOPEN_P_LOG, tech_pvt->controldevice_name); + tech_pvt->controldev_dead = 1; + close(tech_pvt->controldevfd); + ERRORA("gsmopen_serial_monitor failed, declaring %s dead\n", GSMOPEN_P_LOG, tech_pvt->controldevice_name); + tech_pvt->running=0; + alarm_event(tech_pvt, ALARM_FAILED_INTERFACE, "gsmopen_serial_monitor failed, declaring interface dead"); + tech_pvt->active=0; + tech_pvt->name[0]='\0'; + + UNLOCKA(tech_pvt->controldev_lock); + if (tech_pvt->owner) { + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } + switch_sleep(1000000); + return -1; + } + + if (option_debug > 90) { + //DEBUGA_GSMOPEN("1 read %d bytes, --|%s|--\n", GSMOPEN_P_LOG, read_count, tmp_answer_ptr); + //DEBUGA_GSMOPEN("2 read %d bytes, --|%s|--\n", GSMOPEN_P_LOG, read_count, tmp_answer); + } + tmp_answer_ptr = tmp_answer_ptr + read_count; + + + la_counter = 0; + memset(tmp_answer2, 0, sizeof(char) * AT_BUFSIZ); + strcpy(tmp_answer2, tmp_answer); + if ((token_ptr = strtok(tmp_answer2, "\n\r"))) { + last_line_ptr = token_ptr; + strncpy(tech_pvt->line_array.result[la_counter], token_ptr, AT_MESG_MAX_LENGTH); + if (strlen(token_ptr) > AT_MESG_MAX_LENGTH) { + WARNINGA + ("AT mesg longer than buffer, original message was: |%s|, in buffer only: |%s|\n", + GSMOPEN_P_LOG, token_ptr, tech_pvt->line_array.result[la_counter]); + } + la_counter++; + while ((token_ptr = strtok(NULL, "\n\r"))) { + last_line_ptr = token_ptr; + strncpy(tech_pvt->line_array.result[la_counter], token_ptr, AT_MESG_MAX_LENGTH); + if (strlen(token_ptr) > AT_MESG_MAX_LENGTH) { + WARNINGA + ("AT mesg longer than buffer, original message was: |%s|, in buffer only: |%s|\n", + GSMOPEN_P_LOG, token_ptr, tech_pvt->line_array.result[la_counter]); + } + la_counter++; + } + } else { + last_line_ptr = tmp_answer; + } + + if (expected_string && !expect_crlf) { + DEBUGA_GSMOPEN + ("last_line_ptr=|%s|, expected_string=|%s|, expect_crlf=%d, memcmp(last_line_ptr, expected_string, strlen(expected_string)) = %d\n", + GSMOPEN_P_LOG, last_line_ptr, expected_string, expect_crlf, memcmp(last_line_ptr, expected_string, strlen(expected_string))); + } + + if (expected_string && !expect_crlf && !memcmp(last_line_ptr, expected_string, strlen(expected_string)) + ) { + strncpy(tech_pvt->line_array.result[la_counter], last_line_ptr, AT_MESG_MAX_LENGTH); + // match expected string -> accept it withtout CRLF + la_counter++; + + } + /* if the last line read was not a complete line, we'll read the rest in the future */ + else if (tmp_answer[strlen(tmp_answer) - 1] != '\r' && tmp_answer[strlen(tmp_answer) - 1] != '\n') + la_counter--; + + /* let's list the complete lines read so far, without re-listing the lines that has yet been listed */ + if (option_debug > 1) { + for (i = la_read; i < la_counter; i++) + DEBUGA_GSMOPEN("Read line %d: |%s|\n", GSMOPEN_P_LOG, i, tech_pvt->line_array.result[i]); + } + + /* let's interpret the complete lines read so far (WITHOUT looking for OK, ERROR, and EXPECTED_STRING), without re-interpreting the lines that has been yet interpreted, so we're sure we don't miss anything */ + for (i = la_read; i < la_counter; i++) { + + if ((strcmp(tech_pvt->line_array.result[i], "RING") == 0)) { + /* with first RING we wait for callid */ + gettimeofday(&(tech_pvt->ringtime), NULL); + /* give CALLID (+CLIP) a chance, wait for the next RING before answering */ + if (tech_pvt->phone_callflow == CALLFLOW_INCOMING_RING) { + /* we're at the second ring, set the interface state, will be answered by gsmopen_do_monitor */ + DEBUGA_GSMOPEN("|%s| got second RING\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + tech_pvt->interface_state = GSMOPEN_STATE_RING; + } else { + /* we're at the first ring, so there is no CALLID yet thus clean the previous one + just in case we don't receive the caller identification in this new call */ + memset(tech_pvt->callid_name, 0, sizeof(tech_pvt->callid_name)); + memset(tech_pvt->callid_number, 0, sizeof(tech_pvt->callid_number)); + /* only send AT+CLCC? if the device previously reported its support */ + if (tech_pvt->at_has_clcc != 0) { + /* we're at the first ring, try to get CALLID (with +CLCC) */ + DEBUGA_GSMOPEN("|%s| got first RING, sending AT+CLCC?\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + res = gsmopen_serial_write_AT_noack(tech_pvt, "AT+CLCC?"); + if (res) { + ERRORA("AT+CLCC? (call list) was not correctly sent to the phone\n", GSMOPEN_P_LOG); + } + } else { + DEBUGA_GSMOPEN("|%s| got first RING, but not sending AT+CLCC? as this device " + "seems not to support\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + } + tech_pvt->phone_callflow = CALLFLOW_INCOMING_RING; + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CLCC", 5) == 0)) { + int commacount = 0; + int a = 0; + int b = 0; + int c = 0; + /* with clcc we wait for clip */ + memset(tech_pvt->callid_name, 0, sizeof(tech_pvt->callid_name)); + memset(tech_pvt->callid_number, 0, sizeof(tech_pvt->callid_number)); + + for (a = 0; a < strlen(tech_pvt->line_array.result[i]); a++) { + + if (tech_pvt->line_array.result[i][a] == ',') { + commacount++; + } + if (commacount == 5) { + if (tech_pvt->line_array.result[i][a] != ',' && tech_pvt->line_array.result[i][a] != '"') { + tech_pvt->callid_number[b] = tech_pvt->line_array.result[i][a]; + b++; + } + } + if (commacount == 7) { + if (tech_pvt->line_array.result[i][a] != ',' && tech_pvt->line_array.result[i][a] != '"') { + tech_pvt->callid_name[c] = tech_pvt->line_array.result[i][a]; + c++; + } + } + } + + tech_pvt->phone_callflow = CALLFLOW_INCOMING_RING; + DEBUGA_GSMOPEN("|%s| CLCC CALLID: name is %s, number is %s\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i], + tech_pvt->callid_name[0] ? tech_pvt->callid_name : "not available", + tech_pvt->callid_number[0] ? tech_pvt->callid_number : "not available"); + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CLIP", 5) == 0)) { + int commacount = 0; + int a = 0; + int b = 0; + int c = 0; + /* with CLIP, we want to answer right away */ + memset(tech_pvt->callid_name, 0, sizeof(tech_pvt->callid_name)); + memset(tech_pvt->callid_number, 0, sizeof(tech_pvt->callid_number)); + + + for (a = 7; a < strlen(tech_pvt->line_array.result[i]); a++) { + if (tech_pvt->line_array.result[i][a] == ',') { + commacount++; + } + if (commacount == 0) { + if (tech_pvt->line_array.result[i][a] != ',' && tech_pvt->line_array.result[i][a] != '"') { + tech_pvt->callid_number[b] = tech_pvt->line_array.result[i][a]; + b++; + } + } + if (commacount == 4) { + if (tech_pvt->line_array.result[i][a] != ',' && tech_pvt->line_array.result[i][a] != '"') { + tech_pvt->callid_name[c] = tech_pvt->line_array.result[i][a]; + c++; + } + } + } + + if (tech_pvt->interface_state != GSMOPEN_STATE_RING) { + gettimeofday(&(tech_pvt->call_incoming_time), NULL); + DEBUGA_GSMOPEN("GSMOPEN_STATE_RING call_incoming_time.tv_sec=%ld\n", GSMOPEN_P_LOG, tech_pvt->call_incoming_time.tv_sec); + + } + + tech_pvt->interface_state = GSMOPEN_STATE_RING; + tech_pvt->phone_callflow = CALLFLOW_INCOMING_RING; + DEBUGA_GSMOPEN("|%s| CLIP INCOMING CALLID: name is %s, number is %s\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i], + (strlen(tech_pvt->callid_name) && tech_pvt->callid_name[0] != 1) ? tech_pvt->callid_name : "not available", + strlen(tech_pvt->callid_number) ? tech_pvt->callid_number : "not available"); + + if (!strlen(tech_pvt->callid_number)) { + strcpy(tech_pvt->callid_number, "not available"); + } + + if (!strlen(tech_pvt->callid_name) && tech_pvt->callid_name[0] != 1) { + strncpy(tech_pvt->callid_name, tech_pvt->callid_number, sizeof(tech_pvt->callid_name)); + //strncpy(tech_pvt->callid_name, tech_pvt->callid_number, sizeof(tech_pvt->callid_name)) ; + snprintf(tech_pvt->callid_name, sizeof(tech_pvt->callid_name), "GSMopen: %s", tech_pvt->callid_number); + } + + DEBUGA_GSMOPEN("|%s| CLIP INCOMING CALLID: NOW name is %s, number is %s\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i], tech_pvt->callid_name, tech_pvt->callid_number); + } + + if ((strcmp(tech_pvt->line_array.result[i], "BUSY") == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_LINEBUSY; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_LINEBUSY\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + //if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner && tech_pvt->phone_callflow != CALLFLOW_CALL_DOWN) { + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->phone_callflow != CALLFLOW_CALL_DOWN) { + //ast_setstate(tech_pvt->owner, GSMOPEN_STATE_BUSY); + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_BUSY); + //cicopet + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + tech_pvt->interface_state = GSMOPEN_STATE_DOWN; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if (session) { + channel = switch_core_session_get_channel(session); + //gsmopen_hangup(tech_pvt); + switch_core_session_rwunlock(session); + switch_channel_hangup(channel, SWITCH_CAUSE_NONE); + } + // + //tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + + } else { + ERRORA("Why BUSY now?\n", GSMOPEN_P_LOG); + } + } + if ((strcmp(tech_pvt->line_array.result[i], "NO ANSWER") == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_NOANSWER; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_NOANSWER\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner) { + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_NO_ANSWER; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } else { + ERRORA("Why NO ANSWER now?\n", GSMOPEN_P_LOG); + } + } + if ((strcmp(tech_pvt->line_array.result[i], "NO CARRIER") == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_NOCARRIER; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_NOCARRIER\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN) { + //cicopet + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + tech_pvt->interface_state = GSMOPEN_STATE_DOWN; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if (session) { + channel = switch_core_session_get_channel(session); + //gsmopen_hangup(tech_pvt); + switch_core_session_rwunlock(session); + switch_channel_hangup(channel, SWITCH_CAUSE_NONE); + } + // + //tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } else { + ERRORA("Why NO CARRIER now?\n", GSMOPEN_P_LOG); + } + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CBC:", 5) == 0)) { + int power_supply, battery_strenght, err; + + power_supply = battery_strenght = 0; + + err = sscanf(&tech_pvt->line_array.result[i][6], "%d,%d", &power_supply, &battery_strenght); + if (err < 2) { + DEBUGA_GSMOPEN("|%s| is not formatted as: |+CBC: xx,yy| now trying |+CBC:xx,yy|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + err = sscanf(&tech_pvt->line_array.result[i][5], "%d,%d", &power_supply, &battery_strenght); + DEBUGA_GSMOPEN("|%s| +CBC: Powered by %s, battery strenght=%d\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i], power_supply ? "power supply" : "battery", battery_strenght); + + } + + if (err < 2) { + DEBUGA_GSMOPEN("|%s| is not formatted as: |+CBC:xx,yy| giving up\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + else { + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| +CBC: Powered by %s, battery strenght=%d\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i], power_supply ? "power supply" : "battery", battery_strenght); + if (!power_supply) { + if (battery_strenght < 10) { + ERRORA("|%s| BATTERY ALMOST EXHAUSTED\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else if (battery_strenght < 20) { + WARNINGA("|%s| BATTERY LOW\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + } + + } + } + + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CSQ:", 5) == 0)) { + int signal_quality, ber, err; + + signal_quality = ber = 0; + + err = sscanf(&tech_pvt->line_array.result[i][6], "%d,%d", &signal_quality, &ber); + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| +CSQ: Signal Quality: %d, Error Rate=%d\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i], signal_quality, ber); + if (err < 2) { + ERRORA("|%s| is not formatted as: |+CSQ: xx,yy|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else { + if (signal_quality < 11 || signal_quality == 99) { + ERRORA + ("|%s| CELLPHONE GETS ALMOST NO SIGNAL, consider to move it or additional antenna\n", + GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + tech_pvt->got_signal=0; + alarm_event(tech_pvt, ALARM_NETWORK_NO_SIGNAL, "CELLPHONE GETS ALMOST NO SIGNAL, consider to move it or additional antenna"); + } else if (signal_quality < 15) { + WARNINGA("|%s| CELLPHONE GETS SIGNAL LOW\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + tech_pvt->got_signal=1; + alarm_event(tech_pvt, ALARM_NETWORK_LOW_SIGNAL, "CELLPHONE GETS SIGNAL LOW"); + } else { + tech_pvt->got_signal=2; + } + + } + + } + if ((strncmp(tech_pvt->line_array.result[i], "+CREG:", 6) == 0)) { + int n, stat, err; + + n = stat = 0; + + err = sscanf(&tech_pvt->line_array.result[i][6], "%d,%d", &n, &stat); + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| +CREG: Display: %d, Registration=%d\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i], n, stat); + if (err < 2) { + WARNINGA("|%s| is not formatted as: |+CREG: xx,yy|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + if (stat==0) { + ERRORA + ("|%s| CELLPHONE is not registered to network, consider to move it or additional antenna\n", + GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + tech_pvt->not_registered=1; + tech_pvt->home_network_registered=0; + tech_pvt->roaming_registered=0; + alarm_event(tech_pvt, ALARM_NO_NETWORK_REGISTRATION, "CELLPHONE is not registered to network, consider to move it or additional antenna"); + } else if (stat==1) { + DEBUGA_GSMOPEN("|%s| CELLPHONE is registered to the HOME network\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + tech_pvt->not_registered=0; + tech_pvt->home_network_registered=1; + tech_pvt->roaming_registered=0; + }else { + ERRORA("|%s| CELLPHONE is registered to a ROAMING network\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + tech_pvt->not_registered=0; + tech_pvt->home_network_registered=0; + tech_pvt->roaming_registered=1; + alarm_event(tech_pvt, ALARM_ROAMING_NETWORK_REGISTRATION, "CELLPHONE is registered to a ROAMING network"); + } + + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CMGW:", 6) == 0)) { + int err; + + err = sscanf(&tech_pvt->line_array.result[i][7], "%s", tech_pvt->at_cmgw); + DEBUGA_GSMOPEN("|%s| +CMGW: %s\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i], tech_pvt->at_cmgw); + if (err < 1) { + ERRORA("|%s| is not formatted as: |+CMGW: xxxx|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + } + + /* at_call_* are unsolicited messages sent by the modem to signal us about call processing activity and events */ + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_call_idle) == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_IDLE\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner) { + DEBUGA_GSMOPEN("just received a remote HANGUP\n", GSMOPEN_P_LOG); + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_NORMAL; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + DEBUGA_GSMOPEN("just sent GSMOPEN_CONTROL_HANGUP\n", GSMOPEN_P_LOG); + } + + tech_pvt->phone_callflow = CALLFLOW_CALL_NOCARRIER; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_NOCARRIER\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN) { + //cicopet + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + tech_pvt->interface_state = GSMOPEN_STATE_DOWN; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if (session) { + channel = switch_core_session_get_channel(session); + //gsmopen_hangup(tech_pvt); + switch_core_session_rwunlock(session); + switch_channel_hangup(channel, SWITCH_CAUSE_NONE); + } + // + //tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } else { + ERRORA("Why NO CARRIER now?\n", GSMOPEN_P_LOG); + } + + + + + + + + + + + + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_call_incoming) == 0)) { + + //char list_command[64]; + + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_INCOMING\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + if (tech_pvt->phone_callflow != CALLFLOW_CALL_INCOMING && tech_pvt->phone_callflow != CALLFLOW_INCOMING_RING) { + //mark the time of CALLFLOW_CALL_INCOMING + gettimeofday(&(tech_pvt->call_incoming_time), NULL); + tech_pvt->phone_callflow = CALLFLOW_CALL_INCOMING; + DEBUGA_GSMOPEN("CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld\n", GSMOPEN_P_LOG, tech_pvt->call_incoming_time.tv_sec); + + } + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_call_active) == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_ACTIVE; + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_ACTIVE\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + if (tech_pvt->interface_state == CALLFLOW_CALL_DIALING || tech_pvt->interface_state == CALLFLOW_STATUS_EARLYMEDIA) { + DEBUGA_PBX("just received a remote ANSWER\n", GSMOPEN_P_LOG); + if (tech_pvt->phone_callflow == GSMOPEN_STATE_UP) { + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_RINGING); + DEBUGA_PBX("just sent GSMOPEN_CONTROL_RINGING\n", GSMOPEN_P_LOG); + DEBUGA_PBX("going to send GSMOPEN_CONTROL_ANSWER\n", GSMOPEN_P_LOG); + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_ANSWER); + tech_pvt->interface_state = CALLFLOW_CALL_REMOTEANSWER; + DEBUGA_PBX("just sent GSMOPEN_CONTROL_ANSWER\n", GSMOPEN_P_LOG); + } + } else { + } + //tech_pvt->interface_state = GSMOPEN_STATE_UP; + //DEBUGA_PBX("just interface_state UP\n", GSMOPEN_P_LOG); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_call_calling) == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_DIALING; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_DIALING\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_call_failed) == 0)) { + tech_pvt->phone_callflow = CALLFLOW_CALL_FAILED; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_FAILED\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner) { + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CSCA:", 6) == 0)) { //TODO SMS FIXME in config! + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| +CSCA: Message Center Address!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CMGF:", 6) == 0)) { //TODO SMS FIXME in config! + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| +CMGF: Message Format!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CMTI:", 6) == 0)) { //TODO SMS FIXME in config! + int err; + int pos; + + //FIXME all the following commands in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMTI: Incoming SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + err = sscanf(&tech_pvt->line_array.result[i][12], "%d", &pos); + if (err < 1) { + ERRORA("|%s| is not formatted as: |+CMTI: \"MT\",xx|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else { + DEBUGA_GSMOPEN("|%s| +CMTI: Incoming SMS in position: %d!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i], pos); + tech_pvt->unread_sms_msg_id = pos; + gsmopen_sleep(1000); + + if (tech_pvt->unread_sms_msg_id) { + char at_command[256]; + + if (tech_pvt->no_ucs2 == 0) { + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCS=\"UCS2\""); + if (res) { + ERRORA("AT+CSCS=\"UCS2\" (set TE messages to ucs2) do not got OK from the phone, continuing\n", GSMOPEN_P_LOG); + //memset(tech_pvt->sms_message, 0, sizeof(tech_pvt->sms_message)); + } + } + + memset(at_command, 0, sizeof(at_command)); + sprintf(at_command, "AT+CMGR=%d", tech_pvt->unread_sms_msg_id); + //memset(tech_pvt->sms_message, 0, sizeof(tech_pvt->sms_message)); + + tech_pvt->reading_sms_msg = 1; + res = gsmopen_serial_write_AT_ack(tech_pvt, at_command); + tech_pvt->reading_sms_msg = 0; + if (res) { + ERRORA("AT+CMGR (read SMS) do not got OK from the phone, message sent was:|||%s|||\n", GSMOPEN_P_LOG, at_command); + } + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCS=\"GSM\""); + if (res) { + ERRORA("AT+CSCS=\"GSM\" (set TE messages to GSM) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + memset(at_command, 0, sizeof(at_command)); + sprintf(at_command, "AT+CMGD=%d", tech_pvt->unread_sms_msg_id); /* delete the message */ + tech_pvt->unread_sms_msg_id = 0; + res = gsmopen_serial_write_AT_ack(tech_pvt, at_command); + if (res) { + ERRORA("AT+CMGD (Delete SMS) do not got OK from the phone, message sent was:|||%s|||\n", GSMOPEN_P_LOG, at_command); + } + + res = sms_incoming(tech_pvt); + +#if 0 + if (strlen(tech_pvt->sms_message)) { + //FIXME manager_event(EVENT_FLAG_SYSTEM, "GSMOPENincomingsms", + //FIXME "Interface: %s\r\nSMS_Message: %s\r\n", tech_pvt->name, + //FIXME tech_pvt->sms_message); + + res = sms_incoming(tech_pvt, tech_pvt->sms_message); + + if (strlen(tech_pvt->sms_receiving_program)) { + int fd1[2]; + pid_t pid1; + char *arg1[] = { tech_pvt->sms_receiving_program, (char *) NULL }; + int i; + + DEBUGA_GSMOPEN("incoming SMS message:>>>%s<<<\n", GSMOPEN_P_LOG, tech_pvt->sms_message); + res = pipe(fd1); + pid1 = fork(); + + if (pid1 == 0) { //child + int err; + + dup2(fd1[0], 0); // Connect stdin to pipe output + close(fd1[1]); // close input pipe side + close(tech_pvt->controldevfd); + setsid(); //session id + err = execvp(arg1[0], arg1); //exec our program, with stdin connected to pipe output + if (err) { + ERRORA + ("'sms_receiving_program' is set in config file to '%s', and it gave us back this error: %d, (%s). SMS received was:---%s---\n", + GSMOPEN_P_LOG, tech_pvt->sms_receiving_program, err, strerror(errno), tech_pvt->sms_message); + } + close(fd1[0]); // close output pipe side + } +//starting here continue the parent + close(fd1[0]); // close output pipe side + // write the msg on the pipe input + for (i = 0; i < strlen(tech_pvt->sms_message); i++) { + res = write(fd1[1], &tech_pvt->sms_message[i], 1); + } + close(fd1[1]); // close pipe input, let our program know we've finished + } else { + ERRORA + ("got SMS incoming message, but 'sms_receiving_program' is not set in config file. SMS received was:---%s---\n", + GSMOPEN_P_LOG, tech_pvt->sms_message); + } + } +#endif //0 +#if 1 //is this one needed? maybe it can interrupt an incoming call that is just to announce itself + if (tech_pvt->phone_callflow == CALLFLOW_CALL_IDLE && tech_pvt->interface_state == GSMOPEN_STATE_DOWN && tech_pvt->owner == NULL) { + /* we're not in a call, neither calling */ + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CKPD=\"EEE\""); + if (res) { + ERRORA("AT+CKPD=\"EEE\" (cellphone screen back to user) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + } +#endif + } //unread_msg_id + + } //CMTI well formatted + + } //CMTI + + if ((strncmp(tech_pvt->line_array.result[i], "+MMGL:", 6) == 0)) { //TODO MOTOROLA SMS FIXME in config! + int err = 0; + //int unread_msg_id=0; + + if (option_debug) + DEBUGA_GSMOPEN("|%s| +MMGL: Listing Motorola SMSs!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + err = sscanf(&tech_pvt->line_array.result[i][7], "%d", &tech_pvt->unread_sms_msg_id); + if (err < 1) { + ERRORA("|%s| is not formatted as: |+MMGL: xx|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + } + if ((strncmp(tech_pvt->line_array.result[i], "+CMGL:", 6) == 0)) { //TODO SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMGL: Listing SMSs!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + if ((strncmp(tech_pvt->line_array.result[i], "+MMGR:", 6) == 0)) { //TODO MOTOROLA SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +MMGR: Reading Motorola SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->reading_sms_msg) + tech_pvt->reading_sms_msg++; + } + if ((strncmp(tech_pvt->line_array.result[i], "+CMGR: \"STO U", 13) == 0)) { //TODO SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMGR: Reading stored UNSENT SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else if ((strncmp(tech_pvt->line_array.result[i], "+CMGR: \"STO S", 13) == 0)) { //TODO SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMGR: Reading stored SENT SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else if ((strncmp(tech_pvt->line_array.result[i], "+CMGR: \"REC R", 13) == 0)) { //TODO SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMGR: Reading received READ SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else if ((strncmp(tech_pvt->line_array.result[i], "+CMGR: \"REC U", 13) == 0)) { //TODO SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMGR: Reading received UNREAD SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->reading_sms_msg) + tech_pvt->reading_sms_msg++; + } else if ((strncmp(tech_pvt->line_array.result[i], "+CMGR: ", 6) == 0)) { //TODO SMS FIXME in config! + if (option_debug) + DEBUGA_GSMOPEN("|%s| +CMGR: Reading SMS!\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->reading_sms_msg) + tech_pvt->reading_sms_msg++; + } + + + if ((strcmp(tech_pvt->line_array.result[i], "+MCST: 17") == 0)) { /* motorola call processing unsolicited messages */ + tech_pvt->phone_callflow = CALLFLOW_CALL_INFLUX; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_INFLUX\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], "+MCST: 68") == 0)) { /* motorola call processing unsolicited messages */ + tech_pvt->phone_callflow = CALLFLOW_CALL_NOSERVICE; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_NOSERVICE\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner) { + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } + } + if ((strcmp(tech_pvt->line_array.result[i], "+MCST: 70") == 0)) { /* motorola call processing unsolicited messages */ + tech_pvt->phone_callflow = CALLFLOW_CALL_OUTGOINGRESTRICTED; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_OUTGOINGRESTRICTED\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner) { + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } + } + if ((strcmp(tech_pvt->line_array.result[i], "+MCST: 72") == 0)) { /* motorola call processing unsolicited messages */ + tech_pvt->phone_callflow = CALLFLOW_CALL_SECURITYFAIL; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| CALLFLOW_CALL_SECURITYFAIL\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN && tech_pvt->owner) { + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } + } + + if ((strncmp(tech_pvt->line_array.result[i], "+CPBR", 5) == 0)) { /* phonebook stuff begins */ + + if (tech_pvt->phonebook_querying) { /* probably phonebook struct begins */ + int err, first_entry, last_entry, number_lenght, text_lenght; + + if (option_debug) + DEBUGA_GSMOPEN("phonebook struct: |%s|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + err = sscanf(&tech_pvt->line_array.result[i][8], "%d-%d),%d,%d", &first_entry, &last_entry, &number_lenght, &text_lenght); + if (err < 4) { + + err = sscanf(&tech_pvt->line_array.result[i][7], "%d-%d,%d,%d", &first_entry, &last_entry, &number_lenght, &text_lenght); + } + + if (err < 4) { + ERRORA + ("phonebook struct: |%s| is nor formatted as: |+CPBR: (1-750),40,14| neither as: |+CPBR: 1-750,40,14|\n", + GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } else { + + if (option_debug) + DEBUGA_GSMOPEN + ("First entry: %d, last entry: %d, phone number max lenght: %d, text max lenght: %d\n", + GSMOPEN_P_LOG, first_entry, last_entry, number_lenght, text_lenght); + tech_pvt->phonebook_first_entry = first_entry; + tech_pvt->phonebook_last_entry = last_entry; + tech_pvt->phonebook_number_lenght = number_lenght; + tech_pvt->phonebook_text_lenght = text_lenght; + } + + } else { /* probably phonebook entry begins */ + + if (tech_pvt->phonebook_listing) { + int err, entry_id, entry_type; + + char entry_number[256]; + char entry_text[256]; + + if (option_debug) + DEBUGA_GSMOPEN("phonebook entry: |%s|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + err = + sscanf(&tech_pvt->line_array.result[i][7], "%d,\"%255[0-9+]\",%d,\"%255[^\"]\"", &entry_id, entry_number, &entry_type, + entry_text); + if (err < 4) { + ERRORA + ("err=%d, phonebook entry: |%s| is not formatted as: |+CPBR: 504,\"+39025458068\",145,\"ciao a tutti\"|\n", + GSMOPEN_P_LOG, err, tech_pvt->line_array.result[i]); + } else { + //TODO: sanitize entry_text + if (option_debug) + DEBUGA_GSMOPEN("Number: %s, Text: %s, Type: %d\n", GSMOPEN_P_LOG, entry_number, entry_text, entry_type); + /* write entry in phonebook file */ + if (tech_pvt->phonebook_writing_fp) { + gsmopen_dir_entry_extension++; + + fprintf(tech_pvt->phonebook_writing_fp, + "%s => ,%sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcell=%s|phonebook_entry_owner=%s\n", + entry_number, entry_text, "no", + tech_pvt->gsmopen_dir_entry_extension_prefix, "2", gsmopen_dir_entry_extension, "yes", "not_specified"); + fprintf(tech_pvt->phonebook_writing_fp, + "%s => ,%sDO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcell=%s|phonebook_entry_owner=%s\n", + entry_number, entry_text, "no", + tech_pvt->gsmopen_dir_entry_extension_prefix, "3", gsmopen_dir_entry_extension, "yes", "not_specified"); + } + } + + } + + if (tech_pvt->phonebook_listing_received_calls) { + int err, entry_id, entry_type; + + char entry_number[256] = ""; + char entry_text[256] = ""; + + if (option_debug) + DEBUGA_GSMOPEN("phonebook entry: |%s|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + err = + sscanf(&tech_pvt->line_array.result[i][7], "%d,\"%255[0-9+]\",%d,\"%255[^\"]\"", &entry_id, entry_number, &entry_type, + entry_text); + if (err < 1) { //we match only on the progressive id, maybe the remote party has not sent its number, and/or there is no corresponding text entry in the phone directory + ERRORA + ("err=%d, phonebook entry: |%s| is not formatted as: |+CPBR: 504,\"+39025458068\",145,\"ciao a tutti\"|\n", + GSMOPEN_P_LOG, err, tech_pvt->line_array.result[i]); + } else { + //TODO: sanitize entry_text + + if (option_debug) + DEBUGA_GSMOPEN("Number: %s, Text: %s, Type: %d\n", GSMOPEN_P_LOG, entry_number, entry_text, entry_type); + memset(tech_pvt->callid_name, 0, sizeof(tech_pvt->callid_name)); + memset(tech_pvt->callid_number, 0, sizeof(tech_pvt->callid_number)); + strncpy(tech_pvt->callid_name, entry_text, sizeof(tech_pvt->callid_name)); + strncpy(tech_pvt->callid_number, entry_number, sizeof(tech_pvt->callid_number)); + if (option_debug) + DEBUGA_GSMOPEN("incoming callid: Text: %s, Number: %s\n", GSMOPEN_P_LOG, tech_pvt->callid_name, tech_pvt->callid_number); + + DEBUGA_GSMOPEN("|%s| CPBR INCOMING CALLID: name is %s, number is %s\n", + GSMOPEN_P_LOG, tech_pvt->line_array.result[i], + tech_pvt->callid_name[0] != 1 ? tech_pvt->callid_name : "not available", + tech_pvt->callid_number[0] ? tech_pvt->callid_number : "not available"); + + /* mark the time of RING */ + gettimeofday(&(tech_pvt->ringtime), NULL); + tech_pvt->interface_state = GSMOPEN_STATE_RING; + tech_pvt->phone_callflow = CALLFLOW_INCOMING_RING; + + } + + } + + else { + DEBUGA_GSMOPEN("phonebook entry: |%s|\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + + } + } + + } + + if ((strncmp(tech_pvt->line_array.result[i], "*ECAV", 5) == 0) || (strncmp(tech_pvt->line_array.result[i], "*ECAM", 5) == 0)) { /* sony-ericsson call processing unsolicited messages */ + int res, ccid, ccstatus, calltype, processid, exitcause, number, type; + res = ccid = ccstatus = calltype = processid = exitcause = number = type = 0; + res = + sscanf(&tech_pvt->line_array.result[i][6], "%d,%d,%d,%d,%d,%d,%d", &ccid, &ccstatus, &calltype, &processid, &exitcause, &number, + &type); + /* only changes the phone_callflow if enought parameters were parsed */ + if (res >= 3) { + switch (ccstatus) { + case 0: + if (tech_pvt->owner) { + ast_setstate(tech_pvt->owner, GSMOPEN_STATE_DOWN); + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_NORMAL; + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + } + tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE; + tech_pvt->interface_state = GSMOPEN_STATE_DOWN; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| Sony-Ericsson *ECAM/*ECAV: IDLE\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + break; + case 1: + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| Sony-Ericsson *ECAM/*ECAV: CALLING\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + break; + case 2: + if (tech_pvt->owner) { + ast_setstate(tech_pvt->owner, GSMOPEN_STATE_DIALING); + } + tech_pvt->interface_state = CALLFLOW_CALL_DIALING; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| Sony-Ericsson *ECAM/*ECAV: CONNECTING\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + break; + case 3: + if (tech_pvt->owner) { + ast_setstate(tech_pvt->owner, GSMOPEN_STATE_UP); + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_ANSWER); + } + tech_pvt->phone_callflow = CALLFLOW_CALL_ACTIVE; + tech_pvt->interface_state = GSMOPEN_STATE_UP; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| Sony-Ericsson *ECAM/*ECAV: ACTIVE\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + break; + case 4: + if (option_debug > 1) + DEBUGA_GSMOPEN + ("|%s| Sony-Ericsson *ECAM/*ECAV: don't know how to handle HOLD event\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + break; + case 5: + if (option_debug > 1) + DEBUGA_GSMOPEN + ("|%s| Sony-Ericsson *ECAM/*ECAV: don't know how to handle WAITING event\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i]); + break; + case 6: + if (option_debug > 1) + DEBUGA_GSMOPEN + ("|%s| Sony-Ericsson *ECAM/*ECAV: don't know how to handle ALERTING event\n", GSMOPEN_P_LOG, + tech_pvt->line_array.result[i]); + break; + case 7: + if (tech_pvt->owner) { + ast_setstate(tech_pvt->owner, GSMOPEN_STATE_BUSY); + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_BUSY); + } + tech_pvt->phone_callflow = CALLFLOW_CALL_LINEBUSY; + tech_pvt->interface_state = GSMOPEN_STATE_BUSY; + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| Sony-Ericsson *ECAM/*ECAV: BUSY\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + break; + } + } else { + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| Sony-Ericsson *ECAM/*ECAV: could not parse parameters\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + } + + /* at_indicator_* are unsolicited messages sent by the phone to signal us that some of its visual indicators on its screen has changed, based on CIND CMER ETSI docs */ + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_noservice_string) == 0)) { + ERRORA("|%s| at_indicator_noservice_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + alarm_event(tech_pvt, ALARM_NETWORK_NO_SERVICE, "at_indicator_noservice_string"); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_nosignal_string) == 0)) { + ERRORA("|%s| at_indicator_nosignal_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + alarm_event(tech_pvt, ALARM_NETWORK_NO_SIGNAL, "at_indicator_nosignal_string"); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_lowsignal_string) == 0)) { + WARNINGA("|%s| at_indicator_lowsignal_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + alarm_event(tech_pvt, ALARM_NETWORK_LOW_SIGNAL, "at_indicator_lowsignal_string"); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_lowbattchg_string) == 0)) { + WARNINGA("|%s| at_indicator_lowbattchg_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_nobattchg_string) == 0)) { + ERRORA("|%s| at_indicator_nobattchg_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_callactive_string) == 0)) { + DEBUGA_GSMOPEN("|%s| at_indicator_callactive_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_nocallactive_string) == 0)) { + DEBUGA_GSMOPEN("|%s| at_indicator_nocallactive_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_nocallsetup_string) == 0)) { + DEBUGA_GSMOPEN("|%s| at_indicator_nocallsetup_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_callsetupincoming_string) == 0)) { + DEBUGA_GSMOPEN("|%s| at_indicator_callsetupincoming_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_callsetupoutgoing_string) == 0)) { + DEBUGA_GSMOPEN("|%s| at_indicator_callsetupoutgoing_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_callsetupremoteringing_string) + == 0)) { + DEBUGA_GSMOPEN("|%s| at_indicator_callsetupremoteringing_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + } + + } + + /* let's look for OK, ERROR and EXPECTED_STRING in the complete lines read so far, without re-looking at the lines that has been yet looked at */ + for (i = la_read; i < la_counter; i++) { + if (expected_string) { + if ((strncmp(tech_pvt->line_array.result[i], expected_string, strlen(expected_string)) + == 0)) { + if (option_debug > 1) + DEBUGA_GSMOPEN("|%s| got what EXPECTED\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + at_ack = AT_OK; + } + } else { + if ((strcmp(tech_pvt->line_array.result[i], "OK") == 0)) { + if (option_debug > 1) + DEBUGA_GSMOPEN("got OK\n", GSMOPEN_P_LOG); + at_ack = AT_OK; + } + } + if ((strcmp(tech_pvt->line_array.result[i], "ERROR") == 0)) { + if (option_debug > 1) + DEBUGA_GSMOPEN("got ERROR\n", GSMOPEN_P_LOG); + at_ack = AT_ERROR; + } + + /* if we are requesting IMEI, put the line into the imei buffer if the line is not "OK" or "ERROR" */ + if (tech_pvt->requesting_imei && at_ack == -1) { + if (strlen(tech_pvt->line_array.result[i])) { /* we are reading the IMEI */ + strncpy(tech_pvt->imei, tech_pvt->line_array.result[i], sizeof(tech_pvt->imei)); + } + } + + /* if we are requesting IMSI, put the line into the imei buffer if the line is not "OK" or "ERROR" */ + if (tech_pvt->requesting_imsi && at_ack == -1) { + if (strlen(tech_pvt->line_array.result[i])) { /* we are reading the IMSI */ + strncpy(tech_pvt->imsi, tech_pvt->line_array.result[i], sizeof(tech_pvt->imsi)); + } + } + /* if we are reading an sms message from memory, put the line into the sms buffer if the line is not "OK" or "ERROR" */ + if (tech_pvt->reading_sms_msg > 1 && at_ack == -1) { + int c; + char sms_body[16000]; + int err = 0; + memset(sms_body, '\0', sizeof(sms_body)); + + if (strncmp(tech_pvt->line_array.result[i], "+CMGR", 5) == 0) { /* we are reading the "header" of an SMS */ +#if 1 + char content[512]; + char content2[512]; + int inside_comma = 0; + int inside_quote = 0; + int which_field = 0; + int d = 0; + + DEBUGA_GSMOPEN("HERE\n", GSMOPEN_P_LOG); + + memset(content, '\0', sizeof(content)); + + + for (c = 0; c < strlen(tech_pvt->line_array.result[i]); c++) { + if (tech_pvt->line_array.result[i][c] == ',' && tech_pvt->line_array.result[i][c - 1] != '\\' && inside_quote == 0) { + if (inside_comma) { + inside_comma = 0; + DEBUGA_GSMOPEN("inside_comma=%d, inside_quote=%d, we're at=%s\n", GSMOPEN_P_LOG, inside_comma, inside_quote, + &tech_pvt->line_array.result[i][c]); + } else { + inside_comma = 1; + DEBUGA_GSMOPEN("inside_comma=%d, inside_quote=%d, we're at=%s\n", GSMOPEN_P_LOG, inside_comma, inside_quote, + &tech_pvt->line_array.result[i][c]); + } + } + if (tech_pvt->line_array.result[i][c] == '"' && tech_pvt->line_array.result[i][c - 1] != '\\') { + if (inside_quote) { + inside_quote = 0; + DEBUGA_GSMOPEN("END_CONTENT inside_comma=%d, inside_quote=%d, we're at=%s\n", GSMOPEN_P_LOG, inside_comma, inside_quote, + &tech_pvt->line_array.result[i][c]); + DEBUGA_GSMOPEN("%d content=%s\n", GSMOPEN_P_LOG, which_field, content); + + //strncat(tech_pvt->sms_message, "---", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //strncat(tech_pvt->sms_message, content, ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //strncat(tech_pvt->sms_message, "|||", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + + memset(content2, '\0', sizeof(content2)); + if (which_field == 1) { + //FIXME why this? err = ucs2_to_utf8(tech_pvt, content, content2, sizeof(content2)); + //err = ucs2_to_utf8(tech_pvt, content, content2, sizeof(content2)); + err = 0; + strncpy(content2, content, sizeof(content2)); + } else { + err = 0; + strncpy(content2, content, sizeof(content2)); + } + DEBUGA_GSMOPEN("%d content2=%s\n", GSMOPEN_P_LOG, which_field, content2); + DEBUGA_GSMOPEN("%d content=%s\n", GSMOPEN_P_LOG, which_field, content2); + + //strncat(tech_pvt->sms_message, "---", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //if (!err) + //strncat(tech_pvt->sms_message, content2, ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //strncat(tech_pvt->sms_message, "|||", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + memset(content, '\0', sizeof(content)); + d = 0; + if (which_field == 1) { + strncpy(tech_pvt->sms_sender, content2, sizeof(tech_pvt->sms_sender)); + DEBUGA_GSMOPEN("%d content=%s\n", GSMOPEN_P_LOG, which_field, content2); + + } else if (which_field == 2) { + strncpy(tech_pvt->sms_date, content2, sizeof(tech_pvt->sms_date)); + DEBUGA_GSMOPEN("%d content=%s\n", GSMOPEN_P_LOG, which_field, content2); + } else if (which_field > 2) { + WARNINGA("WHY which_field is > 2 ? (which_field is %d)\n", GSMOPEN_P_LOG, which_field); + } + which_field++; + } else { + inside_quote = 1; + DEBUGA_GSMOPEN("START_CONTENT inside_comma=%d, inside_quote=%d, we're at=%s\n", GSMOPEN_P_LOG, inside_comma, inside_quote, + &tech_pvt->line_array.result[i][c]); + } + } + if (inside_quote && tech_pvt->line_array.result[i][c] != '"') { + + content[d] = tech_pvt->line_array.result[i][c]; + d++; + + } + + } +#endif //0 + } //it was the +CMGR answer from the cellphone + else { + DEBUGA_GSMOPEN("body=%s\n", GSMOPEN_P_LOG, sms_body); + DEBUGA_GSMOPEN("tech_pvt->line_array.result[i]=%s\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]); + if (tech_pvt->sms_pdu_not_supported) { + char content3[1000]; + strncpy(tech_pvt->sms_message, tech_pvt->line_array.result[i], sizeof(tech_pvt->sms_message)); + + //int howmanyleft; + + + DEBUGA_GSMOPEN("sms_message=%s\n", GSMOPEN_P_LOG, tech_pvt->sms_message); + ucs2_to_utf8(tech_pvt, tech_pvt->sms_message, content3, sizeof(content3)); + DEBUGA_GSMOPEN("content3=%s\n", GSMOPEN_P_LOG, content3); + strncpy(tech_pvt->sms_body, content3, sizeof(tech_pvt->sms_body)); + //sleep(10); + //cicopet + if (tech_pvt->sms_cnmi_not_supported) { + sms_incoming(tech_pvt); + DEBUGA_GSMOPEN("2 content3=%s\n", GSMOPEN_P_LOG, content3); + } + } else { +#ifndef NO_GSMLIB + char content2[1000]; + SMSMessageRef sms; +//MessageType messagetype; +//Address servicecentreaddress; +//Timestamp servicecentretimestamp; +//Address sender_recipient_address; + + sms = SMSMessage::decode(tech_pvt->line_array.result[i]); // dataCodingScheme = 8 , text=ciao 123 belè новости לק ראת ﺎﻠﺠﻤﻋﺓ 人大 + + DEBUGA_GSMOPEN("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + + memset(content2, '\0', sizeof(content2)); + if (sms->dataCodingScheme().getAlphabet() == DCS_DEFAULT_ALPHABET) { + iso_8859_1_to_utf8(tech_pvt, (char *) sms->userData().c_str(), content2, sizeof(content2)); + } else if (sms->dataCodingScheme().getAlphabet() == DCS_SIXTEEN_BIT_ALPHABET) { + ucs2_to_utf8(tech_pvt, (char *) bufToHex((unsigned char *) sms->userData().data(), sms->userData().length()).c_str(), content2, + sizeof(content2)); + } else { + ERRORA("dataCodingScheme not supported=%d\n", GSMOPEN_P_LOG, sms->dataCodingScheme().getAlphabet()); + + } + DEBUGA_GSMOPEN("dataCodingScheme=%d\n", GSMOPEN_P_LOG, sms->dataCodingScheme().getAlphabet()); + DEBUGA_GSMOPEN("dataCodingScheme=%s\n", GSMOPEN_P_LOG, sms->dataCodingScheme().toString().c_str()); + DEBUGA_GSMOPEN("address=%s\n", GSMOPEN_P_LOG, sms->address().toString().c_str()); + DEBUGA_GSMOPEN("serviceCentreAddress=%s\n", GSMOPEN_P_LOG, sms->serviceCentreAddress().toString().c_str()); + DEBUGA_GSMOPEN("serviceCentreTimestamp=%s\n", GSMOPEN_P_LOG, sms->serviceCentreTimestamp().toString().c_str()); + DEBUGA_GSMOPEN("messageType=%d\n", GSMOPEN_P_LOG, sms->messageType()); + DEBUGA_GSMOPEN("userData= |||%s|||\n", GSMOPEN_P_LOG, content2); + + + memset(sms_body, '\0', sizeof(sms_body)); + strncpy(sms_body, content2, sizeof(sms_body)); + DEBUGA_GSMOPEN("body=%s\n", GSMOPEN_P_LOG, sms_body); + strncpy(tech_pvt->sms_body, sms_body, sizeof(tech_pvt->sms_body)); + strncpy(tech_pvt->sms_sender, sms->address().toString().c_str(), sizeof(tech_pvt->sms_sender)); + strncpy(tech_pvt->sms_date, sms->serviceCentreTimestamp().toString().c_str(), sizeof(tech_pvt->sms_date)); + strncpy(tech_pvt->sms_datacodingscheme, sms->dataCodingScheme().toString().c_str(), sizeof(tech_pvt->sms_datacodingscheme)); + strncpy(tech_pvt->sms_servicecentreaddress, sms->serviceCentreAddress().toString().c_str(), + sizeof(tech_pvt->sms_servicecentreaddress)); + tech_pvt->sms_messagetype = sms->messageType(); +//messagetype = sms->messageType(); +//servicecentreaddress = sms->serviceCentreAddress(); +//servicecentretimestamp = sms->serviceCentreTimestamp(); +//sender_recipient_address = sms->address(); + +#endif// NO_GSMLIB + } + +#if 0 + //strncat(tech_pvt->sms_message, "---", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //strncat(tech_pvt->sms_message, tech_pvt->line_array.result[i], ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //strncat(tech_pvt->sms_message, "|||", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + + memset(sms_body, '\0', sizeof(sms_body)); + err = ucs2_to_utf8(tech_pvt, tech_pvt->line_array.result[i], sms_body, sizeof(sms_body)); + DEBUGA_GSMOPEN("body=%s\n", GSMOPEN_P_LOG, sms_body); + strncpy(tech_pvt->sms_body, sms_body, sizeof(tech_pvt->sms_body)); + + //strncat(tech_pvt->sms_message, "---", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //if (!err) + //strncat(tech_pvt->sms_message, sms_body, ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + //strncat(tech_pvt->sms_message, "|||", ((sizeof(tech_pvt->sms_message) - strlen(tech_pvt->sms_message)) - 1)); + + //DEBUGA_GSMOPEN("sms_message=%s\n", GSMOPEN_P_LOG, tech_pvt->sms_message); +#endif //0 + } //it was the UCS2 from cellphone + + } //we were reading the SMS + + } + + la_read = la_counter; + + if (look_for_ack && at_ack > -1) + break; + + if (la_counter > AT_MESG_MAX_LINES) { + ERRORA("Too many lines in result (>%d). Stopping reader.\n", GSMOPEN_P_LOG, AT_MESG_MAX_LINES); + at_ack = AT_ERROR; + break; + } + } + + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(tech_pvt->controldev_lock); + if (select_err == -1) { + ERRORA("select returned -1 on %s, setting controldev_dead, error was: %s\n", GSMOPEN_P_LOG, tech_pvt->controldevice_name, strerror(errno)); + tech_pvt->controldev_dead = 1; + close(tech_pvt->controldevfd); + + tech_pvt->running=0; + alarm_event(tech_pvt, ALARM_FAILED_INTERFACE, "select returned -1 on interface, setting controldev_dead"); + tech_pvt->active=0; + tech_pvt->name[0]='\0'; + if (tech_pvt->owner) + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + switch_sleep(1000000); + return -1; + } + + if (tech_pvt->phone_callflow == CALLFLOW_CALL_INCOMING && tech_pvt->call_incoming_time.tv_sec) { //after three sec of CALLFLOW_CALL_INCOMING, we assume the phone is incapable of notifying RING (eg: motorola c350), so we try to answer + char list_command[64]; + struct timeval call_incoming_timeout; + gettimeofday(&call_incoming_timeout, NULL); + call_incoming_timeout.tv_sec -= 3; + DEBUGA_GSMOPEN + ("CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n", + GSMOPEN_P_LOG, tech_pvt->call_incoming_time.tv_sec, call_incoming_timeout.tv_sec); + if (call_incoming_timeout.tv_sec > tech_pvt->call_incoming_time.tv_sec) { + + tech_pvt->call_incoming_time.tv_sec = 0; + tech_pvt->call_incoming_time.tv_usec = 0; + DEBUGA_GSMOPEN + ("CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n", + GSMOPEN_P_LOG, tech_pvt->call_incoming_time.tv_sec, call_incoming_timeout.tv_sec); + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CPBS=RC"); + if (res) { + ERRORA("AT+CPBS=RC (select memory of received calls) was not answered by the phone\n", GSMOPEN_P_LOG); + } + tech_pvt->phonebook_querying = 1; + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CPBR=?"); + if (res) { + ERRORA("AT+CPBS=RC (select memory of received calls) was not answered by the phone\n", GSMOPEN_P_LOG); + } + tech_pvt->phonebook_querying = 0; + sprintf(list_command, "AT+CPBR=%d,%d", tech_pvt->phonebook_first_entry, tech_pvt->phonebook_last_entry); + tech_pvt->phonebook_listing_received_calls = 1; + res = gsmopen_serial_write_AT_expect_longtime(tech_pvt, list_command, "OK"); + if (res) { + WARNINGA("AT+CPBR=%d,%d failed, continue\n", GSMOPEN_P_LOG, tech_pvt->phonebook_first_entry, tech_pvt->phonebook_last_entry); + } + tech_pvt->phonebook_listing_received_calls = 0; + } + } + + if (tech_pvt->phone_callflow == CALLFLOW_INCOMING_RING) { + struct timeval call_incoming_timeout; + gettimeofday(&call_incoming_timeout, NULL); + call_incoming_timeout.tv_sec -= 10; + // DEBUGA_GSMOPEN ("CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n", GSMOPEN_P_LOG, tech_pvt->call_incoming_time.tv_sec, call_incoming_timeout.tv_sec); + if (call_incoming_timeout.tv_sec > tech_pvt->ringtime.tv_sec) { + ERRORA("Ringing stopped and I have not answered. Why?\n", GSMOPEN_P_LOG); + DEBUGA_GSMOPEN + ("CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n", + GSMOPEN_P_LOG, tech_pvt->call_incoming_time.tv_sec, call_incoming_timeout.tv_sec); + if (tech_pvt->owner) { + gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP); + tech_pvt->owner->hangupcause = GSMOPEN_CAUSE_FAILURE; + } + } + } + tech_pvt->line_array.elemcount = la_counter; + //NOTICA (" OUTSIDE this gsmopen_serial_device %s \n", GSMOPEN_P_LOG, tech_pvt->controldevice_name); + if (look_for_ack) + return at_ack; + else + return 0; +} + +int gsmopen_serial_write_AT(private_t * tech_pvt, const char *data) +{ + int howmany; + int i; + int res; + int count; + + howmany = strlen(data); + + for (i = 0; i < howmany; i++) { + res = write(tech_pvt->controldevfd, &data[i], 1); + + if (res != 1) { + DEBUGA_GSMOPEN("Error sending (%.1s): %d (%s)\n", GSMOPEN_P_LOG, &data[i], res, strerror(errno)); + gsmopen_sleep(100000); + for (count = 0; count < 10; count++) { + res = write(tech_pvt->controldevfd, &data[i], 1); + if (res == 1) { + DEBUGA_GSMOPEN("Successfully RE-sent (%.1s): %d %d (%s)\n", GSMOPEN_P_LOG, &data[i], count, res, strerror(errno)); + break; + } else + DEBUGA_GSMOPEN("Error RE-sending (%.1s): %d %d (%s)\n", GSMOPEN_P_LOG, &data[i], count, res, strerror(errno)); + gsmopen_sleep(100000); + + } + if (res != 1) { + ERRORA("Error RE-sending (%.1s): %d %d (%s)\n", GSMOPEN_P_LOG, &data[i], count, res, strerror(errno)); + return -1; + } + } + if (option_debug > 1) + DEBUGA_GSMOPEN("sent data... (%.1s)\n", GSMOPEN_P_LOG, &data[i]); + gsmopen_sleep(1000); /* release the cpu */ + } + + res = write(tech_pvt->controldevfd, "\r", 1); + + if (res != 1) { + DEBUGA_GSMOPEN("Error sending (carriage return): %d (%s)\n", GSMOPEN_P_LOG, res, strerror(errno)); + gsmopen_sleep(100000); + for (count = 0; count < 10; count++) { + res = write(tech_pvt->controldevfd, "\r", 1); + + if (res == 1) { + DEBUGA_GSMOPEN("Successfully RE-sent carriage return: %d %d (%s)\n", GSMOPEN_P_LOG, count, res, strerror(errno)); + break; + } else + DEBUGA_GSMOPEN("Error RE-sending (carriage return): %d %d (%s)\n", GSMOPEN_P_LOG, count, res, strerror(errno)); + gsmopen_sleep(100000); + + } + if (res != 1) { + ERRORA("Error RE-sending (carriage return): %d %d (%s)\n", GSMOPEN_P_LOG, count, res, strerror(errno)); + return -1; + } + } + if (option_debug > 1) + DEBUGA_GSMOPEN("sent (carriage return)\n", GSMOPEN_P_LOG); + gsmopen_sleep(1000); /* release the cpu */ + + return howmany; +} + +int gsmopen_serial_write_AT_nocr(private_t * tech_pvt, const char *data) +{ + int howmany; + int i; + int res; + int count; + + howmany = strlen(data); + + for (i = 0; i < howmany; i++) { + res = write(tech_pvt->controldevfd, &data[i], 1); + + if (res != 1) { + DEBUGA_GSMOPEN("Error sending (%.1s): %d (%s)\n", GSMOPEN_P_LOG, &data[i], res, strerror(errno)); + gsmopen_sleep(100000); + for (count = 0; count < 10; count++) { + res = write(tech_pvt->controldevfd, &data[i], 1); + if (res == 1) + break; + else + DEBUGA_GSMOPEN("Error RE-sending (%.1s): %d %d (%s)\n", GSMOPEN_P_LOG, &data[i], count, res, strerror(errno)); + gsmopen_sleep(100000); + + } + if (res != 1) { + ERRORA("Error RE-sending (%.1s): %d %d (%s)\n", GSMOPEN_P_LOG, &data[i], count, res, strerror(errno)); + return -1; + } + } + if (option_debug > 1) + DEBUGA_GSMOPEN("sent data... (%.1s)\n", GSMOPEN_P_LOG, &data[i]); + gsmopen_sleep(1000); /* release the cpu */ + } + + gsmopen_sleep(1000); /* release the cpu */ + + return howmany; +} + +int gsmopen_serial_write_AT_noack(private_t * tech_pvt, const char *data) +{ + + if (option_debug > 1) + DEBUGA_GSMOPEN("gsmopen_serial_write_AT_noack: %s\n", GSMOPEN_P_LOG, data); + + PUSHA_UNLOCKA(tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + if (gsmopen_serial_write_AT(tech_pvt, data) != strlen(data)) { + + ERRORA("Error sending data... (%s)\n", GSMOPEN_P_LOG, strerror(errno)); + UNLOCKA(tech_pvt->controldev_lock); + return -1; + } + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(tech_pvt->controldev_lock); + + return 0; +} + +int gsmopen_serial_write_AT_ack(private_t * tech_pvt, const char *data) +{ + int at_result = AT_ERROR; + + PUSHA_UNLOCKA(tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + if (option_debug > 1) + DEBUGA_GSMOPEN("sending: %s\n", GSMOPEN_P_LOG, data); + if (gsmopen_serial_write_AT(tech_pvt, data) != strlen(data)) { + ERRORA("Error sending data... (%s) \n", GSMOPEN_P_LOG, strerror(errno)); + UNLOCKA(tech_pvt->controldev_lock); + return -1; + } + + at_result = gsmopen_serial_read_AT(tech_pvt, 1, 500000, 2, NULL, 1); // 2.5 sec timeout + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(tech_pvt->controldev_lock); + + return at_result; + +} + +int gsmopen_serial_write_AT_ack_nocr_longtime(private_t * tech_pvt, const char *data) +{ + int at_result = AT_ERROR; + + PUSHA_UNLOCKA(tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + if (option_debug > 1) + DEBUGA_GSMOPEN("sending: %s\n", GSMOPEN_P_LOG, data); + if (gsmopen_serial_write_AT_nocr(tech_pvt, data) != strlen(data)) { + ERRORA("Error sending data... (%s) \n", GSMOPEN_P_LOG, strerror(errno)); + UNLOCKA(tech_pvt->controldev_lock); + return -1; + } + + at_result = gsmopen_serial_read_AT(tech_pvt, 1, 500000, 20, NULL, 1); // 20.5 sec timeout + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(tech_pvt->controldev_lock); + + return at_result; + +} + +int gsmopen_serial_write_AT_expect1(private_t * tech_pvt, const char *data, const char *expected_string, int expect_crlf, int seconds) +{ + int at_result = AT_ERROR; + + PUSHA_UNLOCKA(tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + if (option_debug > 1) + DEBUGA_GSMOPEN("sending: %s, expecting: %s\n", GSMOPEN_P_LOG, data, expected_string); + if (gsmopen_serial_write_AT(tech_pvt, data) != strlen(data)) { + ERRORA("Error sending data... (%s) \n", GSMOPEN_P_LOG, strerror(errno)); + UNLOCKA(tech_pvt->controldev_lock); + return -1; + } + + at_result = gsmopen_serial_read_AT(tech_pvt, 1, 500000, seconds, expected_string, expect_crlf); // 20.5 sec timeout, used for querying the SIM and sending SMSs + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(tech_pvt->controldev_lock); + + return at_result; + +} + +int gsmopen_serial_AT_expect(private_t * tech_pvt, const char *expected_string, int expect_crlf, int seconds) +{ + int at_result = AT_ERROR; + + PUSHA_UNLOCKA(tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + if (option_debug > 1) + DEBUGA_GSMOPEN("expecting: %s\n", GSMOPEN_P_LOG, expected_string); + + at_result = gsmopen_serial_read_AT(tech_pvt, 1, 500000, seconds, expected_string, expect_crlf); // 20.5 sec timeout, used for querying the SIM and sending SMSs + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(tech_pvt->controldev_lock); + + return at_result; + +} + +int gsmopen_serial_answer(private_t * tech_pvt) +{ + if (tech_pvt->controldevprotocol == PROTOCOL_AT) + return gsmopen_serial_answer_AT(tech_pvt); +#ifdef GSMOPEN_FBUS2 + if (tech_pvt->controldevprotocol == PROTOCOL_FBUS2) + return gsmopen_serial_answer_FBUS2(tech_pvt); +#endif /* GSMOPEN_FBUS2 */ +#ifdef GSMOPEN_CVM + if (tech_pvt->controldevprotocol == PROTOCOL_CVM_BUSMAIL) + return gsmopen_serial_answer_CVM_BUSMAIL(tech_pvt); +#endif /* GSMOPEN_CVM */ + return -1; +} + + +int gsmopen_serial_answer_AT(private_t * tech_pvt) +{ + int res; + + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_answer, tech_pvt->at_answer_expect); + if (res) { + DEBUGA_GSMOPEN + ("at_answer command failed, command used: %s, expecting: %s, trying with AT+CKPD=\"S\"\n", + GSMOPEN_P_LOG, tech_pvt->at_answer, tech_pvt->at_answer_expect); + + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CKPD=\"S\""); + if (res) { + ERRORA("at_answer command failed, command used: 'AT+CKPD=\"S\"', giving up\n", GSMOPEN_P_LOG); + return -1; + } + } + //tech_pvt->interface_state = GSMOPEN_STATE_UP; + //tech_pvt->phone_callflow = CALLFLOW_CALL_ACTIVE; + DEBUGA_GSMOPEN("AT: call answered\n", GSMOPEN_P_LOG); + return 0; +} + +int gsmopen_serial_hangup(private_t * tech_pvt) +{ + if (tech_pvt->controldevprotocol == PROTOCOL_AT) + return gsmopen_serial_hangup_AT(tech_pvt); +#ifdef GSMOPEN_FBUS2 + if (tech_pvt->controldevprotocol == PROTOCOL_FBUS2) + return gsmopen_serial_hangup_FBUS2(tech_pvt); +#endif /* GSMOPEN_FBUS2 */ +#ifdef GSMOPEN_CVM + if (tech_pvt->controldevprotocol == PROTOCOL_CVM_BUSMAIL) + return gsmopen_serial_hangup_CVM_BUSMAIL(tech_pvt); +#endif /* GSMOPEN_CVM */ + return -1; +} + + +int gsmopen_serial_hangup_AT(private_t * tech_pvt) +{ + int res; + + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN) { + res = gsmopen_serial_write_AT_expect(tech_pvt, tech_pvt->at_hangup, tech_pvt->at_hangup_expect); + if (res) { + DEBUGA_GSMOPEN("at_hangup command failed, command used: %s, trying to use AT+CKPD=\"EEE\"\n", GSMOPEN_P_LOG, tech_pvt->at_hangup); + res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CKPD=\"EEE\""); + if (res) { + ERRORA("at_hangup command failed, command used: 'AT+CKPD=\"EEE\"'\n", GSMOPEN_P_LOG); + return -1; + } + } + } + tech_pvt->interface_state = GSMOPEN_STATE_DOWN; + tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE; + return 0; +} + + +int gsmopen_serial_call(private_t * tech_pvt, char *dstr) +{ + if (tech_pvt->controldevprotocol == PROTOCOL_AT) + return gsmopen_serial_call_AT(tech_pvt, dstr); +#ifdef GSMOPEN_FBUS2 + if (tech_pvt->controldevprotocol == PROTOCOL_FBUS2) + return gsmopen_serial_call_FBUS2(tech_pvt, dstr); +#endif /* GSMOPEN_FBUS2 */ + if (tech_pvt->controldevprotocol == PROTOCOL_NO_SERIAL) + return 0; +#ifdef GSMOPEN_CVM + if (tech_pvt->controldevprotocol == PROTOCOL_CVM_BUSMAIL) + return gsmopen_serial_call_CVM_BUSMAIL(tech_pvt, dstr); +#endif /* GSMOPEN_CVM */ + return -1; +} + +int gsmopen_serial_call_AT(private_t * tech_pvt, char *dstr) +{ + int res; + char at_command[256]; + + if (option_debug) + DEBUGA_PBX("Dialing %s\n", GSMOPEN_P_LOG, dstr); + memset(at_command, 0, sizeof(at_command)); + tech_pvt->phone_callflow = CALLFLOW_CALL_DIALING; + tech_pvt->interface_state = GSMOPEN_STATE_DIALING; + //ast_uri_decode(dstr); +/* + size_t fixdstr = strspn(dstr, AST_DIGIT_ANYDIG); + if (fixdstr == 0) { + ERRORA("dial command failed because of invalid dial number. dial string was: %s\n", + GSMOPEN_P_LOG, dstr); + return -1; + } +*/ + //dstr[fixdstr] = '\0'; + sprintf(at_command, "%s%s%s", tech_pvt->at_dial_pre_number, dstr, tech_pvt->at_dial_post_number); + DEBUGA_PBX("Dialstring %s\n", GSMOPEN_P_LOG, at_command); + res = gsmopen_serial_write_AT_expect(tech_pvt, at_command, tech_pvt->at_dial_expect); + if (res) { + ERRORA("dial command failed, dial string was: %s\n", GSMOPEN_P_LOG, at_command); + return -1; + } + // jet - early audio + //if (tech_pvt->at_early_audio) { + //ast_queue_control(tech_pvt->owner, AST_CONTROL_ANSWER); + //} + + return 0; +} + +int ucs2_to_utf8(private_t * tech_pvt, char *ucs2_in, char *utf8_out, size_t outbytesleft) +{ + char converted[16000]; +#ifndef WIN32 + iconv_t iconv_format; + int iconv_res; + char *outbuf; + char *inbuf; + size_t inbytesleft; + int c; + char stringa[5]; + double hexnum; + int i = 0; + + memset(converted, '\0', sizeof(converted)); + + DEBUGA_GSMOPEN("ucs2_in=%s\n", GSMOPEN_P_LOG, ucs2_in); + /* cicopet */ + for (c = 0; c < strlen(ucs2_in); c++) { + sprintf(stringa, "0x%c%c", ucs2_in[c], ucs2_in[c + 1]); + c++; + hexnum = strtod(stringa, NULL); + converted[i] = (char) hexnum; + i++; + } + + outbuf = utf8_out; + inbuf = converted; + + iconv_format = iconv_open("UTF8", "UCS-2BE"); + if (iconv_format == (iconv_t) - 1) { + ERRORA("error: %s\n", GSMOPEN_P_LOG, strerror(errno)); + return -1; + } + + inbytesleft = i; + DEBUGA_GSMOPEN("1 ciao in=%s, inleft=%d, out=%s, outleft=%d, converted=%s, utf8_out=%s\n", + GSMOPEN_P_LOG, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, converted, utf8_out); + + iconv_res = iconv(iconv_format, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + if (iconv_res == (size_t) -1) { + DEBUGA_GSMOPEN("2 ciao in=%s, inleft=%d, out=%s, outleft=%d, converted=%s, utf8_out=%s\n", + GSMOPEN_P_LOG, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, converted, utf8_out); + DEBUGA_GSMOPEN("3 error: %s %d\n", GSMOPEN_P_LOG, strerror(errno), errno); + iconv_close(iconv_format); + return -1; + } + DEBUGA_GSMOPEN + ("iconv_res=%d, in=%s, inleft=%d, out=%s, outleft=%d, converted=%s, utf8_out=%s\n", + GSMOPEN_P_LOG, iconv_res, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, converted, utf8_out); + iconv_close(iconv_format); + +#endif //WIN32 + return 0; +} + +int iso_8859_1_to_utf8(private_t * tech_pvt, char *iso_8859_1_in, char *utf8_out, size_t outbytesleft) +{ + char converted[16000]; +#ifndef WIN32 + iconv_t iconv_format; + int iconv_res; + char *outbuf; + char *inbuf; + size_t inbytesleft; + //int c; + //char stringa[5]; + //double hexnum; + //int i = 0; + + memset(converted, '\0', sizeof(converted)); + + DEBUGA_GSMOPEN("iso_8859_1_in=%s\n", GSMOPEN_P_LOG, iso_8859_1_in); + + outbuf = utf8_out; + inbuf = iso_8859_1_in; + + iconv_format = iconv_open("UTF8", "ISO_8859-1"); + if (iconv_format == (iconv_t) - 1) { + ERRORA("error: %s\n", GSMOPEN_P_LOG, strerror(errno)); + return -1; + } + + + inbytesleft = strlen(iso_8859_1_in) * 2; + iconv_res = iconv(iconv_format, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + if (iconv_res == (size_t) -1) { + DEBUGA_GSMOPEN("ciao in=%s, inleft=%d, out=%s, outleft=%d, utf8_out=%s\n", + GSMOPEN_P_LOG, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, utf8_out); + DEBUGA_GSMOPEN("error: %s %d\n", GSMOPEN_P_LOG, strerror(errno), errno); + return -1; + } + DEBUGA_GSMOPEN + (" strlen(iso_8859_1_in)=%d, iconv_res=%d, inbuf=%s, inleft=%d, out=%s, outleft=%d, utf8_out=%s\n", + GSMOPEN_P_LOG, (int) strlen(iso_8859_1_in), iconv_res, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, utf8_out); + + + + iconv_close(iconv_format); + +#endif //WIN32 + return 0; +} + + +int utf_to_ucs2(private_t * tech_pvt, char *utf_in, size_t inbytesleft, char *ucs2_out, size_t outbytesleft) +{ + /* cicopet */ +#ifndef WIN32 + iconv_t iconv_format; + int iconv_res; + char *outbuf; + char *inbuf; + char converted[16000]; + int i; + char stringa[16]; + char stringa2[16]; + + memset(converted, '\0', sizeof(converted)); + + outbuf = converted; + inbuf = utf_in; + + iconv_format = iconv_open("UCS-2BE", "UTF8"); + if (iconv_format == (iconv_t) - 1) { + ERRORA("error: %s\n", GSMOPEN_P_LOG, strerror(errno)); + return -1; + } + outbytesleft = 16000; + + DEBUGA_GSMOPEN("in=%s, inleft=%d, out=%s, outleft=%d, utf_in=%s, converted=%s\n", + GSMOPEN_P_LOG, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, utf_in, converted); + iconv_res = iconv(iconv_format, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + if (iconv_res == (size_t) -1) { + ERRORA("error: %s %d\n", GSMOPEN_P_LOG, strerror(errno), errno); + return -1; + } + DEBUGA_GSMOPEN + ("iconv_res=%d, in=%s, inleft=%d, out=%s, outleft=%d, utf_in=%s, converted=%s\n", + GSMOPEN_P_LOG, iconv_res, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, utf_in, converted); + iconv_close(iconv_format); + + for (i = 0; i < 16000 - outbytesleft; i++) { + memset(stringa, '\0', sizeof(stringa)); + memset(stringa2, '\0', sizeof(stringa2)); + sprintf(stringa, "%02X", converted[i]); + DEBUGA_GSMOPEN("character is |%02X|\n", GSMOPEN_P_LOG, converted[i]); + stringa2[0] = stringa[strlen(stringa) - 2]; + stringa2[1] = stringa[strlen(stringa) - 1]; + strncat(ucs2_out, stringa2, ((outbytesleft - strlen(ucs2_out)) - 1)); //add the received line to the buffer + DEBUGA_GSMOPEN("stringa=%s, stringa2=%s, ucs2_out=%s\n", GSMOPEN_P_LOG, stringa, stringa2, ucs2_out); + } +#endif //WIN32 + return 0; +} + + +/*! \brief Answer incoming call, + * Part of PBX interface */ +int gsmopen_answer(private_t * tech_pvt) +{ + int res; + + if (option_debug) { + DEBUGA_PBX("ENTERING FUNC\n", GSMOPEN_P_LOG); + } + /* do something to actually answer the call, if needed (eg. pick up the phone) */ + if (tech_pvt->controldevprotocol != PROTOCOL_NO_SERIAL) { + if (gsmopen_serial_answer(tech_pvt)) { + ERRORA("gsmopen_answer FAILED\n", GSMOPEN_P_LOG); + if (option_debug) { + DEBUGA_PBX("EXITING FUNC\n", GSMOPEN_P_LOG); + } + return -1; + } + } + tech_pvt->interface_state = GSMOPEN_STATE_UP; + tech_pvt->phone_callflow = CALLFLOW_CALL_ACTIVE; + + while (tech_pvt->interface_state == GSMOPEN_STATE_RING) { + gsmopen_sleep(10000); //10msec + } + if (tech_pvt->interface_state != GSMOPEN_STATE_UP) { + ERRORA("call answering failed\n", GSMOPEN_P_LOG); + res = -1; + } else { + if (option_debug) + DEBUGA_PBX("call answered\n", GSMOPEN_P_LOG); + res = 0; +#ifdef GSMOPEN_PORTAUDIO + //speex_echo_state_reset(tech_pvt->stream->echo_state); +#endif // GSMOPEN_PORTAUDIO + + new_inbound_channel(tech_pvt); + if (tech_pvt->owner) { + DEBUGA_PBX("going to send GSMOPEN_STATE_UP\n", GSMOPEN_P_LOG); + ast_setstate(tech_pvt->owner, GSMOPEN_STATE_UP); + //ast_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_ANSWER); + //gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_ANSWER); + DEBUGA_PBX("just sent GSMOPEN_STATE_UP\n", GSMOPEN_P_LOG); + } + } + if (option_debug) { + DEBUGA_PBX("EXITING FUNC\n", GSMOPEN_P_LOG); + } + return res; +} + +int gsmopen_ring(private_t * tech_pvt) +{ + int res = 0; + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (option_debug) { + //DEBUGA_PBX("ENTERING FUNC\n", GSMOPEN_P_LOG); + } + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if (session) { + switch_core_session_rwunlock(session); + return 0; + } + + new_inbound_channel(tech_pvt); + + gsmopen_sleep(10000); + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if (session) { + channel = switch_core_session_get_channel(session); + + switch_core_session_queue_indication(session, SWITCH_MESSAGE_INDICATE_RINGING); + if (channel) { + switch_channel_mark_ring_ready(channel); + } else { + ERRORA("no session\n", GSMOPEN_P_LOG); + } + switch_core_session_rwunlock(session); + } else { + ERRORA("no session\n", GSMOPEN_P_LOG); + + } + + + if (option_debug) { + DEBUGA_PBX("EXITING FUNC\n", GSMOPEN_P_LOG); + } + return res; +} + + +/*! \brief Hangup gsmopen call + * Part of PBX interface, called from ast_hangup */ + +int gsmopen_hangup(private_t * tech_pvt) +{ + + /* if there is not gsmopen pvt why we are here ? */ + if (!tech_pvt) { + ERRORA("Asked to hangup channel not connected\n", GSMOPEN_P_LOG); + return 0; + } + + DEBUGA_GSMOPEN("ENTERING FUNC\n", GSMOPEN_P_LOG); + + + if (tech_pvt->controldevprotocol != PROTOCOL_NO_SERIAL) { + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN) { + /* actually hangup through the serial port */ + if (tech_pvt->controldevprotocol != PROTOCOL_NO_SERIAL) { + int res; + res = gsmopen_serial_hangup(tech_pvt); + if (res) { + ERRORA("gsmopen_serial_hangup error: %d\n", GSMOPEN_P_LOG, res); + if (option_debug) { + DEBUGA_PBX("EXITING FUNC\n", GSMOPEN_P_LOG); + } + return -1; + } + } + + while (tech_pvt->interface_state != GSMOPEN_STATE_DOWN) { + gsmopen_sleep(10000); //10msec + } + if (tech_pvt->interface_state != GSMOPEN_STATE_DOWN) { + ERRORA("call hangup failed\n", GSMOPEN_P_LOG); + return -1; + } else { + DEBUGA_GSMOPEN("call hungup\n", GSMOPEN_P_LOG); + } + } + } else { + tech_pvt->interface_state = GSMOPEN_STATE_DOWN; + tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE; + } + + switch_set_flag(tech_pvt, TFLAG_HANGUP); + if (option_debug) { + DEBUGA_PBX("EXITING FUNC\n", GSMOPEN_P_LOG); + } + return 0; +} + + + +#ifdef GSMOPEN_ALSA +/*! \brief ALSA pcm format, according to endianess */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +snd_pcm_format_t gsmopen_format = SND_PCM_FORMAT_S16_LE; +#else +snd_pcm_format_t gsmopen_format = SND_PCM_FORMAT_S16_BE; +#endif + +/*! + * \brief Initialize the ALSA soundcard channels (capture AND playback) used by one interface (a multichannel soundcard can be used by multiple interfaces) + * \param p the gsmopen_pvt of the interface + * + * This function call alsa_open_dev to initialize the ALSA soundcard for each channel (capture AND playback) used by one interface (a multichannel soundcard can be used by multiple interfaces). Called by sound_init + * + * \return zero on success, -1 on error. + */ +int alsa_init(private_t * tech_pvt) +{ + tech_pvt->alsac = alsa_open_dev(tech_pvt, SND_PCM_STREAM_CAPTURE); + if (!tech_pvt->alsac) { + ERRORA("Failed opening ALSA capture device: %s\n", GSMOPEN_P_LOG, tech_pvt->alsacname); + if (alsa_shutdown(tech_pvt)) { + ERRORA("alsa_shutdown failed\n", GSMOPEN_P_LOG); + return -1; + } + return -1; + } + tech_pvt->alsap = alsa_open_dev(tech_pvt, SND_PCM_STREAM_PLAYBACK); + if (!tech_pvt->alsap) { + ERRORA("Failed opening ALSA playback device: %s\n", GSMOPEN_P_LOG, tech_pvt->alsapname); + if (alsa_shutdown(tech_pvt)) { + ERRORA("alsa_shutdown failed\n", GSMOPEN_P_LOG); + return -1; + } + return -1; + } + + /* make valgrind very happy */ + snd_config_update_free_global(); + return 0; +} + +/*! + * \brief Shutdown the ALSA soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces) + * \param p the gsmopen_pvt of the interface + * + * This function shutdown the ALSA soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces). Called by sound_init + * + * \return zero on success, -1 on error. + */ + +int alsa_shutdown(private_t * tech_pvt) +{ + + int err; + + if (tech_pvt->alsap) { + err = snd_pcm_drop(tech_pvt->alsap); + if (err < 0) { + ERRORA("device [%s], snd_pcm_drop failed with error '%s'\n", GSMOPEN_P_LOG, tech_pvt->alsapname, snd_strerror(err)); + return -1; + } + err = snd_pcm_close(tech_pvt->alsap); + if (err < 0) { + ERRORA("device [%s], snd_pcm_close failed with error '%s'\n", GSMOPEN_P_LOG, tech_pvt->alsapname, snd_strerror(err)); + return -1; + } + tech_pvt->alsap = NULL; + } + if (tech_pvt->alsac) { + err = snd_pcm_drop(tech_pvt->alsac); + if (err < 0) { + ERRORA("device [%s], snd_pcm_drop failed with error '%s'\n", GSMOPEN_P_LOG, tech_pvt->alsacname, snd_strerror(err)); + return -1; + } + err = snd_pcm_close(tech_pvt->alsac); + if (err < 0) { + ERRORA("device [%s], snd_pcm_close failed with error '%s'\n", GSMOPEN_P_LOG, tech_pvt->alsacname, snd_strerror(err)); + return -1; + } + tech_pvt->alsac = NULL; + } + + return 0; +} + +/*! + * \brief Setup and open the ALSA device (capture OR playback) + * \param p the gsmopen_pvt of the interface + * \param stream the ALSA capture/playback definition + * + * This function setup and open the ALSA device (capture OR playback). Called by alsa_init + * + * \return zero on success, -1 on error. + */ +snd_pcm_t *alsa_open_dev(private_t * tech_pvt, snd_pcm_stream_t stream) +{ + + snd_pcm_t *handle = NULL; + snd_pcm_hw_params_t *params; + snd_pcm_sw_params_t *swparams; + snd_pcm_uframes_t buffer_size; + int err; + size_t n; + //snd_pcm_uframes_t xfer_align; + unsigned int rate; + snd_pcm_uframes_t start_threshold, stop_threshold; + snd_pcm_uframes_t period_size = 0; + snd_pcm_uframes_t chunk_size = 0; + int start_delay = 0; + int stop_delay = 0; + snd_pcm_state_t state; + snd_pcm_info_t *info; + unsigned int chan_num; + + period_size = tech_pvt->alsa_period_size; + + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_sw_params_alloca(&swparams); + + if (stream == SND_PCM_STREAM_CAPTURE) { + err = snd_pcm_open(&handle, tech_pvt->alsacname, stream, 0 | SND_PCM_NONBLOCK); + } else { + err = snd_pcm_open(&handle, tech_pvt->alsapname, stream, 0 | SND_PCM_NONBLOCK); + } + if (err < 0) { + ERRORA + ("snd_pcm_open failed with error '%s' on device '%s', if you are using a plughw:n device please change it to be a default:n device (so to allow it to be shared with other concurrent programs), or maybe you are using an ALSA voicemodem and slmodemd" + " is running?\n", GSMOPEN_P_LOG, snd_strerror(err), stream == SND_PCM_STREAM_CAPTURE ? tech_pvt->alsacname : tech_pvt->alsapname); + return NULL; + } + + snd_pcm_info_alloca(&info); + + if ((err = snd_pcm_info(handle, info)) < 0) { + ERRORA("info error: %s", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + + err = snd_pcm_nonblock(handle, 1); + if (err < 0) { + ERRORA("nonblock setting error: %s", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) { + ERRORA("Broken configuration for this PCM, no configurations available: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + + err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + ERRORA("Access type not available: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + err = snd_pcm_hw_params_set_format(handle, params, gsmopen_format); + if (err < 0) { + ERRORA("Sample format non available: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + err = snd_pcm_hw_params_set_channels(handle, params, 1); + if (err < 0) { + DEBUGA_GSMOPEN("Channels count set failed: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } +#if 1 + err = snd_pcm_hw_params_get_channels(params, &chan_num); + if (err < 0) { + ERRORA("Channels count non available: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + if (chan_num < 1 || chan_num > 2) { + ERRORA("Channels count MUST BE 1 or 2, it is: %d\n", GSMOPEN_P_LOG, chan_num); + ERRORA("Channels count MUST BE 1 or 2, it is: %d on %s %s\n", GSMOPEN_P_LOG, chan_num, tech_pvt->alsapname, tech_pvt->alsacname); + return NULL; + } else { + if (chan_num == 1) { + if (stream == SND_PCM_STREAM_CAPTURE) + tech_pvt->alsa_capture_is_mono = 1; + else + tech_pvt->alsa_play_is_mono = 1; + } else { + if (stream == SND_PCM_STREAM_CAPTURE) + tech_pvt->alsa_capture_is_mono = 0; + else + tech_pvt->alsa_play_is_mono = 0; + } + } +#else + tech_pvt->alsa_capture_is_mono = 1; + tech_pvt->alsa_play_is_mono = 1; +#endif + +#if 1 + rate = tech_pvt->gsmopen_sound_rate; + err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); + if ((float) tech_pvt->gsmopen_sound_rate * 1.05 < rate || (float) tech_pvt->gsmopen_sound_rate * 0.95 > rate) { + WARNINGA("Rate is not accurate (requested = %iHz, got = %iHz)\n", GSMOPEN_P_LOG, tech_pvt->gsmopen_sound_rate, rate); + } + + if (err < 0) { + ERRORA("Error setting rate: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + tech_pvt->gsmopen_sound_rate = rate; + + err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, 0); + + if (err < 0) { + ERRORA("Error setting period_size: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + + tech_pvt->alsa_period_size = period_size; + + tech_pvt->alsa_buffer_size = tech_pvt->alsa_period_size * tech_pvt->alsa_periods_in_buffer; + + err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &tech_pvt->alsa_buffer_size); + + if (err < 0) { + ERRORA("Error setting buffer_size: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } +#endif + + err = snd_pcm_hw_params(handle, params); + if (err < 0) { + ERRORA("Unable to install hw params: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + + snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); + snd_pcm_hw_params_get_buffer_size(params, &buffer_size); + if (chunk_size == buffer_size) { + ERRORA("Can't use period equal to buffer size (%lu == %lu)\n", GSMOPEN_P_LOG, chunk_size, buffer_size); + return NULL; + } + + snd_pcm_sw_params_current(handle, swparams); + + /* + if (sleep_min) + xfer_align = 1; + err = snd_pcm_sw_params_set_sleep_min(handle, swparams, + 0); + + if (err < 0) { + ERRORA("Error setting slep_min: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } + */ + n = chunk_size; + err = snd_pcm_sw_params_set_avail_min(handle, swparams, n); + if (err < 0) { + ERRORA("Error setting avail_min: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } + if (stream == SND_PCM_STREAM_CAPTURE) { + start_delay = 1; + } + if (start_delay <= 0) { + start_threshold = n + (snd_pcm_uframes_t) rate *start_delay / 1000000; + } else { + start_threshold = (snd_pcm_uframes_t) rate *start_delay / 1000000; + } + if (start_threshold < 1) + start_threshold = 1; + if (start_threshold > n) + start_threshold = n; + err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); + if (err < 0) { + ERRORA("Error setting start_threshold: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } + + if (stop_delay <= 0) + stop_threshold = buffer_size + (snd_pcm_uframes_t) rate *stop_delay / 1000000; + else + stop_threshold = (snd_pcm_uframes_t) rate *stop_delay / 1000000; + + if (stream == SND_PCM_STREAM_CAPTURE) { + stop_threshold = -1; + } + + err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); + + if (err < 0) { + ERRORA("Error setting stop_threshold: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } + + if (snd_pcm_sw_params(handle, swparams) < 0) { + ERRORA("Error installing software parameters: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } + + err = snd_pcm_poll_descriptors_count(handle); + if (err <= 0) { + ERRORA("Unable to get a poll descriptors count, error is %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + + if (err != 1) { //number of poll descriptors + DEBUGA_GSMOPEN("Can't handle more than one device\n", GSMOPEN_P_LOG); + return NULL; + } + + err = snd_pcm_poll_descriptors(handle, &tech_pvt->pfd, err); + if (err != 1) { + ERRORA("snd_pcm_poll_descriptors failed, %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + DEBUGA_GSMOPEN("Acquired fd %d from the poll descriptor\n", GSMOPEN_P_LOG, tech_pvt->pfd.fd); + + if (stream == SND_PCM_STREAM_CAPTURE) { + tech_pvt->gsmopen_sound_capt_fd = tech_pvt->pfd.fd; + } + + state = snd_pcm_state(handle); + + if (state != SND_PCM_STATE_RUNNING) { + if (state != SND_PCM_STATE_PREPARED) { + err = snd_pcm_prepare(handle); + if (err) { + ERRORA("snd_pcm_prepare failed, %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + DEBUGA_GSMOPEN("prepared!\n", GSMOPEN_P_LOG); + } + if (stream == SND_PCM_STREAM_CAPTURE) { + err = snd_pcm_start(handle); + if (err) { + ERRORA("snd_pcm_start failed, %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + return NULL; + } + DEBUGA_GSMOPEN("started!\n", GSMOPEN_P_LOG); + } + } + if (option_debug > 1) { + snd_output_t *output = NULL; + err = snd_output_stdio_attach(&output, stdout, 0); + if (err < 0) { + ERRORA("snd_output_stdio_attach failed: %s\n", GSMOPEN_P_LOG, snd_strerror(err)); + } + snd_pcm_dump(handle, output); + +#ifndef NO_GSMLIB + SMSMessageRef sms; + char content2[1000]; + //sms = SMSMessage::decode("079194710167120004038571F1390099406180904480A0D41631067296EF7390383D07CD622E58CD95CB81D6EF39BDEC66BFE7207A794E2FBB4320AFB82C07E56020A8FC7D9687DBED32285C9F83A06F769A9E5EB340D7B49C3E1FA3C3663A0B24E4CBE76516680A7FCBE920725A5E5ED341F0B21C346D4E41E1BA790E4286DDE4BC0BD42CA3E5207258EE1797E5A0BA9B5E9683C86539685997EBEF61341B249BC966"); // dataCodingScheme = 0 + //sms = SMSMessage::decode("0791934329002000040C9193432766658100009001211133318004D4F29C0E"); // dataCodingScheme = 0 + //sms = SMSMessage::decode("0791934329002000040C919343276665810008900121612521801600CC00E800E900F900F200E00020006300690061006F"); // dataCodingScheme = 8 + sms = SMSMessage::decode("0791934329002000040C919343276665810008900172002293404C006300690061006F0020003100320033002000620065006C00E80020043D043E0432043E044104420438002005DC05E7002005E805D005EA0020FE8EFEE0FEA0FEE4FECBFE9300204EBA5927"); // dataCodingScheme = 8 , text=ciao 123 belè новости לק ראת ﺎﻠﺠﻤﻋﺓ 人大 + //sms = SMSMessage::decode("07911497941902F00414D0E474989D769F5DE4320839001040122151820000"); // dataCodingScheme = 0 + //NOTICA("SMS=\n%s\n", GSMOPEN_P_LOG, sms->toString().c_str()); + + memset(content2, '\0', sizeof(content2)); + if (sms->dataCodingScheme().getAlphabet() == DCS_DEFAULT_ALPHABET) { + iso_8859_1_to_utf8(tech_pvt, (char *) sms->userData().c_str(), content2, sizeof(content2)); + } else if (sms->dataCodingScheme().getAlphabet() == DCS_SIXTEEN_BIT_ALPHABET) { + ucs2_to_utf8(tech_pvt, (char *) bufToHex((unsigned char *) sms->userData().data(), sms->userData().length()).c_str(), content2, + sizeof(content2)); + } else { + ERRORA("dataCodingScheme not supported=%d\n", GSMOPEN_P_LOG, sms->dataCodingScheme().getAlphabet()); + + } + //NOTICA("dataCodingScheme=%d\n", GSMOPEN_P_LOG, sms->dataCodingScheme().getAlphabet()); + //NOTICA("userData= |||%s|||\n", GSMOPEN_P_LOG, content2); +#endif// NO_GSMLIB + + } + if (option_debug > 1) + DEBUGA_GSMOPEN("ALSA handle = %ld\n", GSMOPEN_P_LOG, (long int) handle); + return handle; + +} + +/*! \brief Write audio frames to interface */ +#endif /* GSMOPEN_ALSA */ + +int gsmopen_call(private_t * tech_pvt, char *rdest, int timeout) +{ + + //gsmopen_sleep(5000); + DEBUGA_GSMOPEN("Calling GSM, rdest is: %s\n", GSMOPEN_P_LOG, rdest); + //gsmopen_signaling_write(tech_pvt, "SET AGC OFF"); + //gsmopen_sleep(10000); + //gsmopen_signaling_write(tech_pvt, "SET AEC OFF"); + //gsmopen_sleep(10000); + + gsmopen_serial_call(tech_pvt, rdest); + //ERRORA("failed to communicate with GSM client, now exit\n", GSMOPEN_P_LOG); + //return -1; + //} + return 0; +} + + +int gsmopen_senddigit(private_t * tech_pvt, char digit) +{ + + DEBUGA_GSMOPEN("DIGIT received: %c\n", GSMOPEN_P_LOG, digit); + if (tech_pvt->controldevprotocol == PROTOCOL_AT && tech_pvt->at_send_dtmf[0]) { + int res = 0; + char at_command[256]; + + memset(at_command, '\0', 256); + sprintf(at_command, "%s=\"%c\"", tech_pvt->at_send_dtmf, digit); + res = gsmopen_serial_write_AT_ack(tech_pvt, at_command); + if (res) { + ERRORA("senddigit command failed, command used: '%s=\"%c\"', giving up\n", GSMOPEN_P_LOG, tech_pvt->at_send_dtmf, digit); + } + } + + return 0; +} + +#ifdef GSMOPEN_ALSA +/*! \brief Write audio frames to interface */ +int alsa_write(private_t * tech_pvt, short *data, int datalen) +{ + static char sizbuf[8000]; + static char sizbuf2[16000]; + static char silencebuf[8000]; + static int sizpos = 0; + int len = sizpos; + int pos; + int res = 0; + time_t now_timestamp; + /* size_t frames = 0; */ + snd_pcm_state_t state; + snd_pcm_sframes_t delayp1=0; + snd_pcm_sframes_t delayp2=0; + + if(tech_pvt->no_sound==1){ + return res; + } + + + memset(sizbuf, 255, sizeof(sizbuf)); + memset(sizbuf2, 255, sizeof(sizbuf)); + memset(silencebuf, 255, sizeof(sizbuf)); + + //ERRORA("data=%p, datalen=%d\n", GSMOPEN_P_LOG, (void *)data, datalen); + /* We have to digest the frame in 160-byte portions */ + if (datalen > sizeof(sizbuf) - sizpos) { + ERRORA("Frame too large\n", GSMOPEN_P_LOG); + res = -1; + } else { + memcpy(sizbuf + sizpos, data, datalen); + memset(data, 255, datalen); + len += datalen; + pos = 0; + + +#ifdef ALSA_MONITOR + alsa_monitor_write(sizbuf, len); +#endif + state = snd_pcm_state(tech_pvt->alsap); + if (state == SND_PCM_STATE_XRUN) { + int i; + + DEBUGA_GSMOPEN + ("You've got an ALSA write XRUN in the past (gsmopen can't fill the soundcard buffer fast enough). If this happens often (not after silence or after a pause in the speech, that's OK), and appear to damage the sound quality, first check if you have some IRQ problem, maybe sharing the soundcard IRQ with a broken or heavy loaded ethernet or graphic card. Then consider to increase the alsa_periods_in_buffer (now is set to %d) for this interface in the config file\n", + GSMOPEN_P_LOG, tech_pvt->alsa_periods_in_buffer); + res = snd_pcm_prepare(tech_pvt->alsap); + if (res) { + ERRORA("audio play prepare failed: %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + } else { + res = snd_pcm_format_set_silence(gsmopen_format, silencebuf, len / 2); + if (res < 0) { + DEBUGA_GSMOPEN("Silence error %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + res = -1; + } + for (i = 0; i < (tech_pvt->alsa_periods_in_buffer - 1); i++) { + res = snd_pcm_writei(tech_pvt->alsap, silencebuf, len / 2); + if (res != len / 2) { + DEBUGA_GSMOPEN("Write returned a different quantity: %d\n", GSMOPEN_P_LOG, res); + res = -1; + } else if (res < 0) { + DEBUGA_GSMOPEN("Write error %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + res = -1; + } + } + } + + } + + res = snd_pcm_delay(tech_pvt->alsap, &delayp1); + if (res < 0) { + DEBUGA_GSMOPEN("Error %d on snd_pcm_delay: \"%s\"\n", GSMOPEN_P_LOG, res, snd_strerror(res)); + res = snd_pcm_prepare(tech_pvt->alsap); + if (res) { + DEBUGA_GSMOPEN("snd_pcm_prepare failed: '%s'\n", GSMOPEN_P_LOG, snd_strerror(res)); + } + res = snd_pcm_delay(tech_pvt->alsap, &delayp1); + } + + delayp2 = snd_pcm_avail_update(tech_pvt->alsap); + if (delayp2 < 0) { + DEBUGA_GSMOPEN("Error %d on snd_pcm_avail_update: \"%s\"\n", GSMOPEN_P_LOG, (int) delayp2, snd_strerror(delayp2)); + + res = snd_pcm_prepare(tech_pvt->alsap); + if (res) { + DEBUGA_GSMOPEN("snd_pcm_prepare failed: '%s'\n", GSMOPEN_P_LOG, snd_strerror(res)); + } + delayp2 = snd_pcm_avail_update(tech_pvt->alsap); + } + + if ( /* delayp1 != 0 && delayp1 != 160 */ + delayp1 < 160 || delayp2 > tech_pvt->alsa_buffer_size) { + + res = snd_pcm_prepare(tech_pvt->alsap); + if (res) { + DEBUGA_GSMOPEN + ("snd_pcm_prepare failed while trying to prevent an ALSA write XRUN: %s, delayp1=%d, delayp2=%d\n", + GSMOPEN_P_LOG, snd_strerror(res), (int) delayp1, (int) delayp2); + } else { + + int i; + for (i = 0; i < (tech_pvt->alsa_periods_in_buffer - 1); i++) { + res = snd_pcm_format_set_silence(gsmopen_format, silencebuf, len / 2); + if (res < 0) { + DEBUGA_GSMOPEN("Silence error %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + res = -1; + } + res = snd_pcm_writei(tech_pvt->alsap, silencebuf, len / 2); + if (res < 0) { + DEBUGA_GSMOPEN("Write error %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + res = -1; + } else if (res != len / 2) { + DEBUGA_GSMOPEN("Write returned a different quantity: %d\n", GSMOPEN_P_LOG, res); + res = -1; + } + } + + DEBUGA_GSMOPEN + ("PREVENTING an ALSA write XRUN (gsmopen can't fill the soundcard buffer fast enough). If this happens often (not after silence or after a pause in the speech, that's OK), and appear to damage the sound quality, first check if you have some IRQ problem, maybe sharing the soundcard IRQ with a broken or heavy loaded ethernet or graphic card. Then consider to increase the alsa_periods_in_buffer (now is set to %d) for this interface in the config file. delayp1=%d, delayp2=%d\n", + GSMOPEN_P_LOG, tech_pvt->alsa_periods_in_buffer, (int) delayp1, (int) delayp2); + } + + } + + memset(sizbuf2, 0, sizeof(sizbuf2)); + if (tech_pvt->alsa_play_is_mono) { + res = snd_pcm_writei(tech_pvt->alsap, sizbuf, len / 2); + } else { + int a = 0; + int i = 0; + for (i = 0; i < 8000;) { + sizbuf2[a] = sizbuf[i]; + a++; + i++; + sizbuf2[a] = sizbuf[i]; + a++; + i--; + sizbuf2[a] = sizbuf[i]; // comment out this line to use only left + a++; + i++; + sizbuf2[a] = sizbuf[i]; // comment out this line to use only left + a++; + i++; + } + res = snd_pcm_writei(tech_pvt->alsap, sizbuf2, len); + } + if (res == -EPIPE) { + DEBUGA_GSMOPEN + ("ALSA write EPIPE (XRUN) (gsmopen can't fill the soundcard buffer fast enough). If this happens often (not after silence or after a pause in the speech, that's OK), and appear to damage the sound quality, first check if you have some IRQ problem, maybe sharing the soundcard IRQ with a broken or heavy loaded ethernet or graphic card. Then consider to increase the alsa_periods_in_buffer (now is set to %d) for this interface in the config file. delayp1=%d, delayp2=%d\n", + GSMOPEN_P_LOG, tech_pvt->alsa_periods_in_buffer, (int) delayp1, (int) delayp2); + res = snd_pcm_prepare(tech_pvt->alsap); + if (res) { + ERRORA("audio play prepare failed: %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + } else { + + if (tech_pvt->alsa_play_is_mono) { + res = snd_pcm_writei(tech_pvt->alsap, sizbuf, len / 2); + } else { + int a = 0; + int i = 0; + for (i = 0; i < 8000;) { + sizbuf2[a] = sizbuf[i]; + a++; + i++; + sizbuf2[a] = sizbuf[i]; + a++; + i--; + sizbuf2[a] = sizbuf[i]; + a++; + i++; + sizbuf2[a] = sizbuf[i]; + a++; + i++; + } + res = snd_pcm_writei(tech_pvt->alsap, sizbuf2, len); + } + + } + + } else { + if (res == -ESTRPIPE) { + ERRORA("You've got some big problems\n", GSMOPEN_P_LOG); + } else if (res == -EAGAIN) { + DEBUGA_GSMOPEN("Momentarily busy\n", GSMOPEN_P_LOG); + res = 0; + } else if (res < 0) { + ERRORA("Error %d on audio write: \"%s\"\n", GSMOPEN_P_LOG, res, snd_strerror(res)); + } + } + } + + if (tech_pvt->audio_play_reset_period) { + time(&now_timestamp); + if ((now_timestamp - tech_pvt->audio_play_reset_timestamp) > tech_pvt->audio_play_reset_period) { + if (option_debug) + DEBUGA_GSMOPEN("reset audio play\n", GSMOPEN_P_LOG); + res = snd_pcm_wait(tech_pvt->alsap, 1000); + if (res < 0) { + ERRORA("audio play wait failed: %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + } + res = snd_pcm_drop(tech_pvt->alsap); + if (res) { + ERRORA("audio play drop failed: %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + } + res = snd_pcm_prepare(tech_pvt->alsap); + if (res) { + ERRORA("audio play prepare failed: %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + } + res = snd_pcm_wait(tech_pvt->alsap, 1000); + if (res < 0) { + ERRORA("audio play wait failed: %s\n", GSMOPEN_P_LOG, snd_strerror(res)); + } + time(&tech_pvt->audio_play_reset_timestamp); + } + } + //res = 0; + //if (res > 0) + //res = 0; + return res; +} + +#define AST_FRIENDLY_OFFSET 0 +int alsa_read(private_t * tech_pvt, short *data, int datalen) +{ + //static struct ast_frame f; + static short __buf[GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2]; + static short __buf2[(GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2) * 2]; + short *buf; + short *buf2; + static int readpos = 0; + //static int left = GSMOPEN_FRAME_SIZE; + static int left; + snd_pcm_state_t state; + int r = 0; + int off = 0; + int error = 0; + //time_t now_timestamp; + + //DEBUGA_GSMOPEN("buf=%p, datalen=%d, left=%d\n", GSMOPEN_P_LOG, (void *)buf, datalen, left); + //memset(&f, 0, sizeof(struct ast_frame)); //giova + + + + if(tech_pvt->no_sound==1){ + return r; + } + + left = datalen; + + + state = snd_pcm_state(tech_pvt->alsac); + if (state != SND_PCM_STATE_RUNNING) { + DEBUGA_GSMOPEN("ALSA read state is not SND_PCM_STATE_RUNNING\n", GSMOPEN_P_LOG); + + if (state != SND_PCM_STATE_PREPARED) { + error = snd_pcm_prepare(tech_pvt->alsac); + if (error) { + ERRORA("snd_pcm_prepare failed, %s\n", GSMOPEN_P_LOG, snd_strerror(error)); + return r; + } + DEBUGA_GSMOPEN("prepared!\n", GSMOPEN_P_LOG); + } + gsmopen_sleep(1000); + error = snd_pcm_start(tech_pvt->alsac); + if (error) { + ERRORA("snd_pcm_start failed, %s\n", GSMOPEN_P_LOG, snd_strerror(error)); + return r; + } + DEBUGA_GSMOPEN("started!\n", GSMOPEN_P_LOG); + gsmopen_sleep(1000); + } + + buf = __buf + AST_FRIENDLY_OFFSET / 2; + buf2 = __buf2 + ((AST_FRIENDLY_OFFSET / 2) * 2); + + if (tech_pvt->alsa_capture_is_mono) { + r = snd_pcm_readi(tech_pvt->alsac, buf + readpos, left); + //DEBUGA_GSMOPEN("r=%d, buf=%p, buf+readpos=%p, datalen=%d, left=%d\n", GSMOPEN_P_LOG, r, (void *)buf, (void *)(buf + readpos), datalen, left); + } else { + int a = 0; + int i = 0; + r = snd_pcm_readi(tech_pvt->alsac, buf2 + (readpos * 2), left); + + for (i = 0; i < (GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2) * 2;) { + __buf[a] = (__buf2[i] + __buf2[i + 1]) / 2; //comment out this line to use only left + //__buf[a] = __buf2[i]; // enable this line to use only left + a++; + i++; + i++; + } + } + + if (r == -EPIPE) { + DEBUGA_GSMOPEN("ALSA XRUN on read\n", GSMOPEN_P_LOG); + return r; + } else if (r == -ESTRPIPE) { + ERRORA("-ESTRPIPE\n", GSMOPEN_P_LOG); + return r; + + } else if (r == -EAGAIN) { + int count=0; + while (r == -EAGAIN) { + gsmopen_sleep(10000); + DEBUGA_GSMOPEN("%d ALSA read -EAGAIN, the soundcard is not ready to be read by gsmopen\n", GSMOPEN_P_LOG, count); + count++; + + if (tech_pvt->alsa_capture_is_mono) { + r = snd_pcm_readi(tech_pvt->alsac, buf + readpos, left); + } else { + int a = 0; + int i = 0; + r = snd_pcm_readi(tech_pvt->alsac, buf2 + (readpos * 2), left); + + for (i = 0; i < (GSMOPEN_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2) * 2;) { + __buf[a] = (__buf2[i] + __buf2[i + 1]) / 2; + a++; + i++; + i++; + } + } + + } + } else if (r < 0) { + WARNINGA("ALSA Read error: %s\n", GSMOPEN_P_LOG, snd_strerror(r)); + } else if (r >= 0) { + //DEBUGA_GSMOPEN("read: r=%d, readpos=%d, left=%d, off=%d\n", GSMOPEN_P_LOG, r, readpos, left, off); + off -= r; //what is the meaning of this? a leftover, probably + } + /* Update positions */ + readpos += r; + left -= r; + + if (readpos >= GSMOPEN_FRAME_SIZE) { + int i; + /* A real frame */ + readpos = 0; + left = GSMOPEN_FRAME_SIZE; + for (i = 0; i < r; i++) + data[i] = buf[i]; + + } + return r; +} + +#endif // GSMOPEN_ALSA + + + + + +int gsmopen_sendsms(private_t * tech_pvt, char *dest, char *text) +{ + //char *idest = data; + //char rdest[256]; + //private_t *p = NULL; + //char *device; + //char *dest; + //char *text; + //char *stringp = NULL; + //int found = 0; + int failed = 0; + int err = 0; + + //strncpy(rdest, idest, sizeof(rdest) - 1); + DEBUGA_GSMOPEN("GSMopenSendsms: dest=%s text=%s\n", GSMOPEN_P_LOG, dest, text); + DEBUGA_GSMOPEN("START\n", GSMOPEN_P_LOG); + /* we can use gsmopen_request to get the channel, but gsmopen_request would look for onowned channels, and probably we can send SMSs while a call is ongoing + * + */ + + if (tech_pvt->controldevprotocol != PROTOCOL_AT) { + ERRORA(", GSMOPEN_P_LOGGSMopenSendsms supports only AT command cellphones at the moment :-( !\n", GSMOPEN_P_LOG); + return RESULT_FAILURE; + } + + if (tech_pvt->controldevprotocol == PROTOCOL_AT) { + char smscommand[16000]; + memset(smscommand, '\0', sizeof(smscommand)); + + PUSHA_UNLOCKA(&tech_pvt->controldev_lock); + LOKKA(tech_pvt->controldev_lock); + + err = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF=1"); + if (err) { + ERRORA("AT+CMGF=1 (set message sending to TEXT (as opposed to PDU) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + + + if (tech_pvt->no_ucs2) { + sprintf(smscommand, "AT+CMGS=\"%s\"", dest); //TODO: support phones that only accept pdu mode + } else { + char dest2[1048]; + + err = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CSCS=\"UCS2\""); + if (err) { + ERRORA("AT+CSCS=\"UCS2\" (set TE messages to ucs2) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + + memset(dest2, '\0', sizeof(dest2)); + utf_to_ucs2(tech_pvt, dest, strlen(dest), dest2, sizeof(dest2)); + sprintf(smscommand, "AT+CMGS=\"%s\"", dest2); //TODO: support phones that only accept pdu mode + } + //TODO: support phones that only accept pdu mode + //TODO would be better to lock controldev here + //sprintf(smscommand, "AT+CMGS=\"%s\"", dest); //FIXME: nokia e63 want this + err = gsmopen_serial_write_AT_noack(tech_pvt, smscommand); + if (err) { + ERRORA("Error sending SMS\n", GSMOPEN_P_LOG); + failed = 1; + goto uscita; + } + err = gsmopen_serial_AT_expect(tech_pvt, "> ", 0, 1); // wait 1.5s for the prompt, no crlf +#if 1 + if (err) { + DEBUGA_GSMOPEN + ("Error or timeout getting prompt '> ' for sending sms directly to the remote party. BTW, seems that we cannot do that with Motorola c350, so we'll write to cellphone memory, then send from memory\n", + GSMOPEN_P_LOG); + + err = gsmopen_serial_write_AT_ack(tech_pvt, "ATE1"); //motorola (at least c350) do not echo the '>' prompt when in ATE0... go figure!!!! + if (err) { + ERRORA("Error activating echo from modem\n", GSMOPEN_P_LOG); + } + tech_pvt->at_cmgw[0] = '\0'; + sprintf(smscommand, "AT+CMGW=\"%s\"", dest); //TODO: support phones that only accept pdu mode + err = gsmopen_serial_write_AT_noack(tech_pvt, smscommand); + if (err) { + ERRORA("Error writing SMS destination to the cellphone memory\n", GSMOPEN_P_LOG); + failed = 1; + goto uscita; + } + err = gsmopen_serial_AT_expect(tech_pvt, "> ", 0, 1); // wait 1.5s for the prompt, no crlf + if (err) { + ERRORA("Error or timeout getting prompt '> ' for writing sms text in cellphone memory\n", GSMOPEN_P_LOG); + failed = 1; + goto uscita; + } + } +#endif + + //sprintf(text,"ciao 123 belè новости לק ראת ﺎﻠﺠﻤﻋﺓ 人大"); //let's test the beauty of utf + memset(smscommand, '\0', sizeof(smscommand)); + if (tech_pvt->no_ucs2) { + sprintf(smscommand, "%s", text); + } else { + utf_to_ucs2(tech_pvt, text, strlen(text), smscommand, sizeof(smscommand)); + } + + smscommand[strlen(smscommand)] = 0x1A; + DEBUGA_GSMOPEN("smscommand len is: %d, text is:|||%s|||\n", GSMOPEN_P_LOG, (int) strlen(smscommand), smscommand); + + err = gsmopen_serial_write_AT_ack_nocr_longtime(tech_pvt, smscommand); + //TODO would be better to unlock controldev here + if (err) { + ERRORA("Error writing SMS text to the cellphone memory\n", GSMOPEN_P_LOG); + //return RESULT_FAILURE; + failed = 1; + goto uscita; + } + if (tech_pvt->at_cmgw[0]) { + sprintf(smscommand, "AT+CMSS=%s", tech_pvt->at_cmgw); + err = gsmopen_serial_write_AT_expect_longtime(tech_pvt, smscommand, "OK"); + if (err) { + ERRORA("Error sending SMS from the cellphone memory\n", GSMOPEN_P_LOG); + //return RESULT_FAILURE; + failed = 1; + goto uscita; + } + + err = gsmopen_serial_write_AT_ack(tech_pvt, "ATE0"); //motorola (at least c350) do not echo the '>' prompt when in ATE0... go figure!!!! + if (err) { + ERRORA("Error de-activating echo from modem\n", GSMOPEN_P_LOG); + } + } + uscita: + gsmopen_sleep(1000); + + if (tech_pvt->at_cmgw[0]) { + + /* let's see what we've sent, just for check TODO: Motorola it's not reliable! Motorola c350 tells that all was sent, but is not true! It just sends how much it fits into one SMS FIXME: need an algorithm to calculate how many ucs2 chars fits into an SMS. It make difference based, probably, on the GSM alphabet translation, or so */ + sprintf(smscommand, "AT+CMGR=%s", tech_pvt->at_cmgw); + err = gsmopen_serial_write_AT_ack(tech_pvt, smscommand); + if (err) { + ERRORA("Error reading SMS back from the cellphone memory\n", GSMOPEN_P_LOG); + } + + /* let's delete from cellphone memory what we've sent */ + sprintf(smscommand, "AT+CMGD=%s", tech_pvt->at_cmgw); + err = gsmopen_serial_write_AT_ack(tech_pvt, smscommand); + if (err) { + ERRORA("Error deleting SMS from the cellphone memory\n", GSMOPEN_P_LOG); + } + + tech_pvt->at_cmgw[0] = '\0'; + } + //gsmopen_sleep(500000); //.5 secs + UNLOCKA(tech_pvt->controldev_lock); + POPPA_UNLOCKA(&tech_pvt->controldev_lock); + } + + err = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF=0"); + if (err) { + DEBUGA_GSMOPEN("AT+CMGF=0 (set message sending to PDU (as opposed to TEXT) do not got OK from the phone, continuing\n", GSMOPEN_P_LOG); + } + + + DEBUGA_GSMOPEN("FINISH\n", GSMOPEN_P_LOG); + if (failed) + return -1; + else + return RESULT_SUCCESS; +} + +/************************************************/ + +/* LUIGI RIZZO's magic */ +/* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must + * be representable in 16 bits to avoid overflows. + */ +#define BOOST_SCALE (1<<9) +#define BOOST_MAX 40 /* slightly less than 7 bits */ + +/* + * store the boost factor + */ +void gsmopen_store_boost(char *s, double *boost) +{ + private_t *tech_pvt = NULL; + + if (sscanf(s, "%lf", boost) != 1) { + ERRORA("invalid boost <%s>\n", GSMOPEN_P_LOG, s); + return; + } + if (*boost < -BOOST_MAX) { + WARNINGA("boost %s too small, using %d\n", GSMOPEN_P_LOG, s, -BOOST_MAX); + *boost = -BOOST_MAX; + } else if (*boost > BOOST_MAX) { + WARNINGA("boost %s too large, using %d\n", GSMOPEN_P_LOG, s, BOOST_MAX); + *boost = BOOST_MAX; + } +#ifdef WIN32 + *boost = exp(log ((double)10) * *boost / 20) * BOOST_SCALE; +#else + *boost = exp(log(10) * *boost / 20) * BOOST_SCALE; +#endif //WIN32 + if (option_debug > 1) + DEBUGA_GSMOPEN("setting boost %s to %f\n", GSMOPEN_P_LOG, s, *boost); +} + + +int gsmopen_sound_boost(void *data, int samples_num, double boost) +{ +/* LUIGI RIZZO's magic */ + if (boost != 0 && (boost < 511 || boost > 513)) { /* scale and clip values */ + int i, x; + + int16_t *ptr = (int16_t *) data; + + for (i = 0; i < samples_num; i++) { + x = (int) (ptr[i] * boost) / BOOST_SCALE; + if (x > 32767) { + x = 32767; + } else if (x < -32768) { + x = -32768; + } + ptr[i] = x; + } + } else { + //printf("BOOST=%f\n", boost); + } + + return 0; +} + + +int gsmopen_serial_getstatus_AT(private_t * tech_pvt) +{ + int res; + private_t *p = tech_pvt; + +#if 0 + if (p->owner) { + if (p->owner->_state != AST_STATE_UP && p->owner->_state != AST_STATE_DOWN) { + DEBUGA_AT("No getstatus, we're neither UP nor DOWN\n", GSMOPEN_P_LOG); + return 0; + } + } +#endif + + + PUSHA_UNLOCKA(p->controldev_lock); + LOKKA(p->controldev_lock); + res = gsmopen_serial_write_AT_ack(p, "AT"); + if (res) { + ERRORA("AT was not acknowledged, continuing but maybe there is a problem\n", GSMOPEN_P_LOG); + } + gsmopen_sleep(1000); + + if (strlen(p->at_query_battchg)) { + res = gsmopen_serial_write_AT_expect(p, p->at_query_battchg, p->at_query_battchg_expect); + if (res) { + WARNINGA("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, p->at_query_battchg, p->at_query_battchg_expect); + } + gsmopen_sleep(1000); + } + + if (strlen(p->at_query_signal)) { + res = gsmopen_serial_write_AT_expect(p, p->at_query_signal, p->at_query_signal_expect); + if (res) { + WARNINGA("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, p->at_query_signal, p->at_query_signal_expect); + } + gsmopen_sleep(1000); + } + + if (!p->network_creg_not_supported) { + res = gsmopen_serial_write_AT_ack(p, "AT+CREG?"); + if (res) { + WARNINGA("%s does not get %s from the phone. Continuing.\n", GSMOPEN_P_LOG, "AT+CREG?", "OK"); + } + gsmopen_sleep(1000); + } + + //FIXME all the following commands in config! + + if (p->sms_cnmi_not_supported) { + res = gsmopen_serial_write_AT_ack(p, "AT+MMGL=\"HEADER ONLY\""); + if (res) { + WARNINGA + ("%s does not get %s from the phone. If your phone is not Motorola, please contact the gsmopen developers. Else, if your phone IS a Motorola, probably a long msg was incoming and ther first part was read and then deleted. The second part is now orphan. If you got this warning repeatedly, and you cannot correctly receive SMSs from this interface, please manually clean all messages (and the residual parts of them) from the cellphone/SIM. Continuing.\n", + GSMOPEN_P_LOG, "AT+MMGL=\"HEADER ONLY\"", "OK"); + } else { + gsmopen_sleep(1000); + if (p->unread_sms_msg_id) { + char at_command[256]; + + res = gsmopen_serial_write_AT_ack(p, "AT+CSCS=\"UCS2\""); + if (res) { + ERRORA("AT+CSCS=\"UCS2\" (set TE messages to ucs2) do not got OK from the phone\n", GSMOPEN_P_LOG); + memset(p->sms_message, 0, sizeof(p->sms_message)); + } + + memset(at_command, 0, sizeof(at_command)); + sprintf(at_command, "AT+CMGR=%d", p->unread_sms_msg_id); + memset(p->sms_message, 0, sizeof(p->sms_message)); + + p->reading_sms_msg = 1; + res = gsmopen_serial_write_AT_ack(p, at_command); + p->reading_sms_msg = 0; + if (res) { + ERRORA("AT+CMGR (read SMS) do not got OK from the phone, message sent was:|||%s|||\n", GSMOPEN_P_LOG, at_command); + } + res = gsmopen_serial_write_AT_ack(p, "AT+CSCS=\"GSM\""); + if (res) { + ERRORA("AT+CSCS=\"GSM\" (set TE messages to GSM) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + memset(at_command, 0, sizeof(at_command)); + sprintf(at_command, "AT+CMGD=%d", p->unread_sms_msg_id); /* delete the message */ + p->unread_sms_msg_id = 0; + res = gsmopen_serial_write_AT_ack(p, at_command); + if (res) { + ERRORA("AT+CMGD (Delete SMS) do not got OK from the phone, message sent was:|||%s|||\n", GSMOPEN_P_LOG, at_command); + } + + if (strlen(p->sms_message)) { +#if 0 + + manager_event(EVENT_FLAG_SYSTEM, "GSMOPENincomingsms", "Interface: %s\r\nSMS_Message: %s\r\n", p->name, p->sms_message); + + if (strlen(p->sms_receiving_program)) { + int fd1[2]; + pid_t pid1; + char *arg1[] = { p->sms_receiving_program, (char *) NULL }; + int i; + + DEBUGA_AT("incoming SMS message:---%s---\n", GSMOPEN_P_LOG, p->sms_message); + pipe(fd1); + pid1 = fork(); + + if (pid1 == 0) { //child + int err; + + dup2(fd1[0], 0); // Connect stdin to pipe output + close(fd1[1]); // close input pipe side + setsid(); //session id + err = execvp(arg1[0], arg1); //exec our program, with stdin connected to pipe output + if (err) { + ERRORA + ("'sms_receiving_program' is set in config file to '%s', and it gave us back this error: %d, (%s). SMS received was:---%s---\n", + GSMOPEN_P_LOG, p->sms_receiving_program, err, strerror(errno), p->sms_message); + } + close(fd1[0]); // close output pipe side + } //starting here continue the parent + close(fd1[0]); // close output pipe side + // write the msg on the pipe input + for (i = 0; i < strlen(p->sms_message); i++) { + write(fd1[1], &p->sms_message[i], 1); + } + close(fd1[1]); // close pipe input, let our program know we've finished + } else { + ERRORA + ("got SMS incoming message, but 'sms_receiving_program' is not set in config file. SMS received was:---%s---\n", + GSMOPEN_P_LOG, p->sms_message); + } +#endif //0 + DEBUGA_GSMOPEN("got SMS incoming message. SMS received was:---%s---\n", GSMOPEN_P_LOG, p->sms_message); + } +#if 0 //is this one needed? maybe it can interrupt an incoming call that is just to announce itself + if (p->phone_callflow == CALLFLOW_CALL_IDLE && p->interface_state == AST_STATE_DOWN && p->owner == NULL) { + /* we're not in a call, neither calling */ + res = gsmopen_serial_write_AT_ack(p, "AT+CKPD=\"EEE\""); + if (res) { + ERRORA("AT+CKPD=\"EEE\" (cellphone screen back to user) do not got OK from the phone\n", GSMOPEN_P_LOG); + } + } +#endif + } + } + } + + UNLOCKA(p->controldev_lock); + POPPA_UNLOCKA(p->controldev_lock); + return 0; +} diff --git a/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.cpp b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.cpp new file mode 100644 index 0000000000..7db2452a95 --- /dev/null +++ b/src/mod/endpoints/mod_gsmopen/alsa_nogsmlib_nocplusplus/mod_gsmopen/mod_gsmopen.cpp @@ -0,0 +1,3474 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2011, Anthony Minessale II <anthm@freeswitch.org> + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II <anthm@freeswitch.org> + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * This module (mod_gsmopen) has been contributed by: + * + * Giovanni Maruzzelli (gmaruzz@gmail.com) + * + * + * Further Contributors: + * + * + * + * mod_gsmopen.c -- GSM compatible Endpoint Module + * + */ + +#include "gsmopen.h" + +#if 0 +#ifdef WIN32 +/***************/ +// from http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/ + +#include <time.h> + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else /* */ +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif /* */ +struct sk_timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +int gettimeofday(struct timeval *tv, struct sk_timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch */ + tmpres /= 10; /*convert into microseconds */ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long) (tmpres / 1000000UL); + tv->tv_usec = (long) (tmpres % 1000000UL); + } + if (NULL != tz) { + if (!tzflag) { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + return 0; +} + +/***************/ +#endif /* WIN32 */ +#endif //0 +SWITCH_BEGIN_EXTERN_C +SWITCH_MODULE_LOAD_FUNCTION(mod_gsmopen_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_gsmopen_shutdown); +SWITCH_MODULE_DEFINITION(mod_gsmopen, mod_gsmopen_load, mod_gsmopen_shutdown, NULL); +SWITCH_END_EXTERN_C +#define GSMOPEN_CHAT_PROTO "sms" +#if 1 +SWITCH_STANDARD_API(gsm_function); +/* BEGIN: Changes here */ +#define GSM_SYNTAX "list [full] || console || AT_command || remove < interface_name | interface_id > || reload" +/* END: Changes heres */ +SWITCH_STANDARD_API(gsmopen_function); +#define GSMOPEN_SYNTAX "interface_name AT_command" +#endif //0 + +SWITCH_STANDARD_API(gsmopen_boost_audio_function); +#define GSMOPEN_BOOST_AUDIO_SYNTAX "interface_name [<play|capt> <in decibels: -40 ... 0 ... +40>]" +SWITCH_STANDARD_API(sendsms_function); +#define SENDSMS_SYNTAX "gsmopen_sendsms interface_name destination_number SMS_text" +SWITCH_STANDARD_API(gsmopen_dump_function); +#define GSMOPEN_DUMP_SYNTAX "gsmopen_dump <interface_name|list>" +/* BEGIN: Changes here */ +#define FULL_RELOAD 0 +#define SOFT_RELOAD 1 +/* END: Changes heres */ + +const char *interface_status[] = { /* should match GSMOPEN_STATE_xxx in gsmopen.h */ + "IDLE", + "DOWN", + "RING", + "DIALING", + "BUSY", + "UP", + "RINGING", + "PRERING", + "DOUBLE", + "SELECTD", + "HANG_RQ", + "PREANSW" +}; +const char *phone_callflow[] = { /* should match CALLFLOW_XXX in gsmopen.h */ + "CALL_IDLE", + "CALL_DOWN", + "INCOMING_RNG", + "CALL_DIALING", + "CALL_LINEBUSY", + "CALL_ACTIVE", + "INCOMING_HNG", + "CALL_RLEASD", + "CALL_NOCARR", + "CALL_INFLUX", + "CALL_INCOMING", + "CALL_FAILED", + "CALL_NOSRVC", + "CALL_OUTRESTR", + "CALL_SECFAIL", + "CALL_NOANSWER", + "STATUS_FNSHED", + "STATUS_CANCLED", + "STATUS_FAILED", + "STATUS_REFUSED", + "STATUS_RINGING", + "STATUS_INPROGRS", + "STATUS_UNPLACD", + "STATUS_ROUTING", + "STATUS_EARLYMD", + "INCOMING_CLID", + "STATUS_RMTEHOLD" +}; + + +static struct { + int debug; + char *ip; + int port; + char *dialplan; + char *destination; + char *context; + char *codec_string; + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last; + char *codec_rates_string; + char *codec_rates[SWITCH_MAX_CODECS]; + int codec_rates_last; + unsigned int flags; + int fd; + int calls; + int real_interfaces; + int next_interface; + char hold_music[256]; + private_t GSMOPEN_INTERFACES[GSMOPEN_MAX_INTERFACES]; + switch_mutex_t *mutex; + private_t *gsm_console; +} globals; + +switch_endpoint_interface_t *gsmopen_endpoint_interface; +switch_memory_pool_t *gsmopen_module_pool = NULL; +int running = 0; + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, globals.context); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_destination, globals.destination); +//SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string); +//SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_rates_string, globals.codec_rates_string); + +/* BEGIN: Changes here */ +static switch_status_t interface_exists(char *the_interface); +#if 1 +static switch_status_t remove_interface(char *the_interface); +#endif //0 +/* END: Changes here */ + +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_consume_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); +static switch_status_t gsmopen_tech_init(private_t * tech_pvt, switch_core_session_t *session); + +static switch_status_t gsmopen_codec(private_t * tech_pvt, int sample_rate, int codec_ms) +{ + switch_core_session_t *session = NULL; + + if (switch_core_codec_init + (&tech_pvt->read_codec, "L16", NULL, sample_rate, codec_ms, 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + ERRORA("Can't load codec?\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + if (switch_core_codec_init + (&tech_pvt->write_codec, "L16", NULL, sample_rate, codec_ms, 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + ERRORA("Can't load codec?\n", GSMOPEN_P_LOG); + switch_core_codec_destroy(&tech_pvt->read_codec); + return SWITCH_STATUS_FALSE; + } + + tech_pvt->read_frame.rate = sample_rate; + tech_pvt->read_frame.codec = &tech_pvt->read_codec; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + + if (session) { + switch_core_session_set_read_codec(session, &tech_pvt->read_codec); + switch_core_session_set_write_codec(session, &tech_pvt->write_codec); + switch_core_session_rwunlock(session); + } else { + ERRORA("no session\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t gsmopen_tech_init(private_t * tech_pvt, switch_core_session_t *session) +{ + +#ifdef WANT_SPEEX + int ciapa; + long level; + int tmp; +#endif// WANT_SPEEX + switch_assert(tech_pvt != NULL); + switch_assert(session != NULL); + tech_pvt->read_frame.data = tech_pvt->databuf; + tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); + switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_core_session_set_private(session, tech_pvt); + switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(session), sizeof(tech_pvt->session_uuid_str)); + if (!strlen(tech_pvt->session_uuid_str)) { + ERRORA("no tech_pvt->session_uuid_str\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + if (gsmopen_codec(tech_pvt, SAMPLERATE_GSMOPEN, 20) != SWITCH_STATUS_SUCCESS) { + ERRORA("gsmopen_codec FAILED\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + //teletone_dtmf_detect_init(&tech_pvt->dtmf_detect, tech_pvt->read_codec.implementation->actual_samples_per_second); + //teletone_dtmf_detect_init(&tech_pvt->dtmf_detect, 8000); + dtmf_rx_init(&tech_pvt->dtmf_state, NULL, NULL); + dtmf_rx_parms(&tech_pvt->dtmf_state, 0, 10, 10, -99); + +#ifdef GSMOPEN_ALSA + if(tech_pvt->no_sound==0){ + if (alsa_init(tech_pvt)) { + ERRORA("alsa_init failed\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + + } + } +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + if(tech_pvt->no_sound==0){ + if (gsmopen_portaudio_init(tech_pvt)) { + ERRORA("gsmopen_portaudio_init failed\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + + } + } +#endif// GSMOPEN_PORTAUDIO + + if (switch_core_timer_init(&tech_pvt->timer_read, "soft", 20, tech_pvt->read_codec.implementation->samples_per_packet, gsmopen_module_pool) != + SWITCH_STATUS_SUCCESS) { + ERRORA("setup timer failed\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + switch_core_timer_sync(&tech_pvt->timer_read); + + if (switch_core_timer_init(&tech_pvt->timer_write, "soft", 20, tech_pvt->write_codec.implementation->samples_per_packet, gsmopen_module_pool) != + SWITCH_STATUS_SUCCESS) { + ERRORA("setup timer failed\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + switch_core_timer_sync(&tech_pvt->timer_write); + +#ifdef WANT_SPEEX + /* Echo canceller with 100 ms tail length */ +#ifndef GIOVA48 + tech_pvt->echo_state = speex_echo_state_init(160, 1024); + ciapa = 8000; +#else// GIOVA48 + tech_pvt->echo_state = speex_echo_state_init(960, 4800); + ciapa = 48000; +#endif // GIOVA48 + speex_echo_ctl(tech_pvt->echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &ciapa); + +#if 1 //NO MORE + /* Setup preprocessor and associate with echo canceller for residual echo suppression */ +#ifndef GIOVA48 + tech_pvt->preprocess = speex_preprocess_state_init(160, 8000); +#else// GIOVA48 + tech_pvt->preprocess = speex_preprocess_state_init(960, 48000); +#endif // GIOVA48 + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, + tech_pvt->echo_state); + +#if 0 + /* Setup preprocessor various other goodies */ + tmp = 0; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_AGC, &tmp); + //level=8000.1; + //speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_AGC_LEVEL, &level); + + // Let's turn off all of the 'denoisers' (eg denoise and dereverb, and vad too) because they start automatic gain on mic input on cm108 usb, also if it (the agc on usb) disbled through mixer + tmp = 0; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_DENOISE, &tmp); + tmp = 0; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, &tmp); + tmp = 0; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_VAD, &tmp); +#endif + + tmp = 0; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_SET_DENOISE, &tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_AGC, &tmp); + fprintf(stderr, "AGC is: %d\n", tmp); + level = 1.0; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_AGC_LEVEL, &level); + fprintf(stderr, "AGC_LEVEL is: %f\n", level); + //tmp=1; + //speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_AGC_TARGET, &tmp); + //fprintf( stderr, "AGC_TARGET is: %d\n", tmp ); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_DENOISE, &tmp); + fprintf(stderr, "DENOISE is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_DEREVERB, &tmp); + fprintf(stderr, "DEREVERB is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_VAD, &tmp); + fprintf(stderr, "VAD is: %d\n", tmp); + +#if 0 + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_NOISE_SUPPRESS, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_NOISE_SUPPRESS is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_ECHO_SUPPRESS, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_ECHO_SUPPRESS is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE, + &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_AGC_MAX_GAIN, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_AGC_MAX_GAIN is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_AGC_INCREMENT, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_AGC_INCREMENT is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_AGC_DECREMENT, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_AGC_DECREMENT is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_PROB_START, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_PROB_START is: %d\n", tmp); + tmp = 1; + speex_preprocess_ctl(tech_pvt->preprocess, SPEEX_PREPROCESS_GET_PROB_CONTINUE, &tmp); + fprintf(stderr, "SPEEX_PREPROCESS_GET_PROB_CONTINUE is: %d\n", tmp); +#endif //0 +#endif// 0 //NO MORE + +#endif // WANT_SPEEX + + + + switch_clear_flag(tech_pvt, TFLAG_HANGUP); + DEBUGA_GSMOPEN("gsmopen_codec SUCCESS\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; +} + +/* BEGIN: Changes here */ +static switch_status_t interface_exists(char *the_interface) +{ + int i; + int interface_id; + + if (*the_interface == '#') { /* look by interface id or interface name */ + the_interface++; + switch_assert(the_interface); + interface_id = atoi(the_interface); + + /* take a number as interface id */ + if (interface_id > 0 || (interface_id == 0 && strcmp(the_interface, "0") == 0)) { + if (strlen(globals.GSMOPEN_INTERFACES[interface_id].name)) { + return SWITCH_STATUS_SUCCESS; + } + } else { + /* interface name */ + for (interface_id = 0; interface_id < GSMOPEN_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.GSMOPEN_INTERFACES[interface_id].name, the_interface) == 0) { + return SWITCH_STATUS_SUCCESS; + break; + } + } + } + } else { /* look by gsmopen_user */ + + + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].gsmopen_user)) { + if (strcmp(globals.GSMOPEN_INTERFACES[i].gsmopen_user, the_interface) == 0) { + return SWITCH_STATUS_SUCCESS; + } + } + } + } + return SWITCH_STATUS_FALSE; +} + +#if 1 +static switch_status_t remove_interface(char *the_interface) +{ + int x = 10; + unsigned int howmany = 8; + int interface_id = -1; + private_t *tech_pvt = NULL; + switch_status_t status; + + //running = 0; + + + //XXX if (*the_interface == '#') { /* remove by interface id or interface name */ + //XXX the_interface++; + switch_assert(the_interface); + interface_id = atoi(the_interface); + + if (interface_id > 0 || (interface_id == 0 && strcmp(the_interface, "0") == 0)) { + /* take a number as interface id */ + tech_pvt = &globals.GSMOPEN_INTERFACES[interface_id]; + } else { + + for (interface_id = 0; interface_id < GSMOPEN_MAX_INTERFACES; interface_id++) { + if (strcmp(globals.GSMOPEN_INTERFACES[interface_id].name, the_interface) == 0) { + tech_pvt = &globals.GSMOPEN_INTERFACES[interface_id]; + break; + } + } + } + //XXX } //else { /* remove by gsmopen_user */ + //for (interface_id = 0; interface_id < GSMOPEN_MAX_INTERFACES; interface_id++) { + //if (strcmp(globals.GSMOPEN_INTERFACES[interface_id].gsmopen_user, the_interface) == 0) { + //tech_pvt = &globals.GSMOPEN_INTERFACES[interface_id]; + //break; + //} + //} + //} + + if (!tech_pvt) { + DEBUGA_GSMOPEN("interface '%s' does not exist\n", GSMOPEN_P_LOG, the_interface); + goto end; + } + + if (strlen(globals.GSMOPEN_INTERFACES[interface_id].session_uuid_str)) { + DEBUGA_GSMOPEN("interface '%s' is busy\n", GSMOPEN_P_LOG, the_interface); + goto end; + } + + globals.GSMOPEN_INTERFACES[interface_id].running = 0; + + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_signaling_thread) { +#if 1 +#ifdef WIN32 + switch_file_write(tech_pvt->GSMopenHandles.fdesc[1], "sciutati", &howmany); // let's the controldev_thread die +#else /* WIN32 */ + howmany = write(tech_pvt->GSMopenHandles.fdesc[1], "sciutati", howmany); +#endif /* WIN32 */ +#endif //0 + DEBUGA_GSMOPEN("HERE will shutdown gsmopen_signaling_thread of '%s'\n", GSMOPEN_P_LOG, the_interface); + } + + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread) { +#if 0 +#ifdef WIN32 + if (SendMessage(tech_pvt->GSMopenHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) { // let's the gsmopen_api_thread_func die + DEBUGA_GSMOPEN("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", GSMOPEN_P_LOG, GetLastError()); + globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread = NULL; + } +#else + XEvent e; + Atom atom1 = XInternAtom(tech_pvt->GSMopenHandles.disp, "GSMOPENCONTROLAPI_MESSAGE_BEGIN", False); + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom1; /* leading message */ + e.xclient.display = tech_pvt->GSMopenHandles.disp; + e.xclient.window = tech_pvt->GSMopenHandles.gsmopen_win; + e.xclient.format = 8; + + XSendEvent(tech_pvt->GSMopenHandles.disp, tech_pvt->GSMopenHandles.win, False, 0, &e); + XSync(tech_pvt->GSMopenHandles.disp, False); +#endif //WIN32 +#endif //0 + + DEBUGA_GSMOPEN("HERE will shutdown gsmopen_api_thread of '%s'\n", GSMOPEN_P_LOG, the_interface); + } + + while (x) { + x--; + switch_yield(50000); + } + + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_signaling_thread) { + switch_thread_join(&status, globals.GSMOPEN_INTERFACES[interface_id].gsmopen_signaling_thread); + } + + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread) { + switch_thread_join(&status, globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread); + } + + switch_mutex_lock(globals.mutex); + if (globals.gsm_console == &globals.GSMOPEN_INTERFACES[interface_id]) { + DEBUGA_GSMOPEN("interface '%s' no more console\n", GSMOPEN_P_LOG, the_interface); + globals.gsm_console = NULL; + } else { + DEBUGA_GSMOPEN("interface '%s' STILL console\n", GSMOPEN_P_LOG, the_interface); + } + memset(&globals.GSMOPEN_INTERFACES[interface_id], '\0', sizeof(private_t)); + globals.real_interfaces--; + switch_mutex_unlock(globals.mutex); + + DEBUGA_GSMOPEN("interface '%s' deleted successfully\n", GSMOPEN_P_LOG, the_interface); + globals.GSMOPEN_INTERFACES[interface_id].running = 1; + end: + //running = 1; + return SWITCH_STATUS_SUCCESS; +} +#endif //0 + +/* END: Changes here */ + +/* + 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; + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + //ERRORA("%s CHANNEL INIT\n", GSMOPEN_P_LOG, tech_pvt->name); + switch_set_flag(tech_pvt, TFLAG_IO); + + /* Move channel's state machine to ROUTING. This means the call is trying + to get from the initial start where the call because, to the point + where a destination has been identified. If the channel is simply + left in the initial state, nothing will happen. */ + switch_channel_set_state(channel, CS_ROUTING); + switch_mutex_lock(globals.mutex); + globals.calls++; + + switch_mutex_unlock(globals.mutex); + DEBUGA_GSMOPEN("%s CHANNEL INIT %s\n", GSMOPEN_P_LOG, tech_pvt->name, switch_core_session_get_uuid(session)); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_destroy(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + + tech_pvt = (private_t *) switch_core_session_get_private(session); + + + if (tech_pvt) { + DEBUGA_GSMOPEN("%s CHANNEL DESTROY %s\n", GSMOPEN_P_LOG, tech_pvt->name, switch_core_session_get_uuid(session)); + + if (switch_core_codec_ready(&tech_pvt->read_codec)) { + switch_core_codec_destroy(&tech_pvt->read_codec); + } + + if (switch_core_codec_ready(&tech_pvt->write_codec)) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + + switch_core_timer_destroy(&tech_pvt->timer_read); + switch_core_timer_destroy(&tech_pvt->timer_write); + +#ifdef GSMOPEN_ALSA + if(tech_pvt->no_sound==0){ + alsa_shutdown(tech_pvt); + } +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + if(tech_pvt->no_sound==0){ + if (gsmopen_portaudio_shutdown(tech_pvt)) { + ERRORA("gsmopen_portaudio_shutdown failed\n", GSMOPEN_P_LOG); + + } + } +#endif// GSMOPEN_PORTAUDIO + + + *tech_pvt->session_uuid_str = '\0'; + tech_pvt->interface_state = GSMOPEN_STATE_IDLE; + if (tech_pvt->phone_callflow == CALLFLOW_STATUS_FINISHED) { + tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE; + } + switch_core_session_set_private(session, NULL); + } else { + DEBUGA_GSMOPEN("!!!!!!NO tech_pvt!!!! CHANNEL DESTROY %s\n", GSMOPEN_P_LOG, switch_core_session_get_uuid(session)); + } + + 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 = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + tech_pvt->phone_callflow = CALLFLOW_CALL_HANGUP_REQUESTED; + + if (!switch_channel_test_flag(channel, CF_ANSWERED)) { + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + tech_pvt->ob_failed_calls++; + } else { + tech_pvt->ib_failed_calls++; + } + } + + + DEBUGA_GSMOPEN("%s CHANNEL HANGUP\n", GSMOPEN_P_LOG, tech_pvt->name); + switch_clear_flag(tech_pvt, TFLAG_IO); + switch_clear_flag(tech_pvt, TFLAG_VOICE); + switch_set_flag(tech_pvt, TFLAG_HANGUP); + + gsmopen_hangup(tech_pvt); + + //memset(tech_pvt->session_uuid_str, '\0', sizeof(tech_pvt->session_uuid_str)); + //*tech_pvt->session_uuid_str = '\0'; + DEBUGA_GSMOPEN("%s CHANNEL HANGUP\n", GSMOPEN_P_LOG, tech_pvt->name); + switch_mutex_lock(globals.mutex); + globals.calls--; + if (globals.calls < 0) { + globals.calls = 0; + } + + tech_pvt->interface_state = GSMOPEN_STATE_IDLE; + //FIXME if (tech_pvt->phone_callflow == CALLFLOW_STATUS_FINISHED) { + tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE; + //FIXME } + switch_mutex_unlock(globals.mutex); + + 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); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_GSMOPEN("%s CHANNEL ROUTING\n", GSMOPEN_P_LOG, tech_pvt->name); + + 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); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_GSMOPEN("%s CHANNEL EXECUTE\n", GSMOPEN_P_LOG, tech_pvt->name); + + 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 = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_GSMOPEN("%s CHANNEL KILL_CHANNEL\n", GSMOPEN_P_LOG, tech_pvt->name); + switch (sig) { + case SWITCH_SIG_KILL: + DEBUGA_GSMOPEN("%s CHANNEL got SWITCH_SIG_KILL\n", GSMOPEN_P_LOG, switch_channel_get_name(channel)); + //switch_mutex_lock(tech_pvt->flag_mutex); + switch_clear_flag(tech_pvt, TFLAG_IO); + switch_clear_flag(tech_pvt, TFLAG_VOICE); + switch_set_flag(tech_pvt, TFLAG_HANGUP); + //switch_mutex_unlock(tech_pvt->flag_mutex); + break; + case SWITCH_SIG_BREAK: + DEBUGA_GSMOPEN("%s CHANNEL got SWITCH_SIG_BREAK\n", GSMOPEN_P_LOG, switch_channel_get_name(channel)); + //switch_set_flag(tech_pvt, TFLAG_BREAK); + //switch_mutex_lock(tech_pvt->flag_mutex); + switch_set_flag(tech_pvt, TFLAG_BREAK); + //switch_mutex_unlock(tech_pvt->flag_mutex); + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} +static switch_status_t channel_on_consume_media(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + + tech_pvt = (private_t *) switch_core_session_get_private(session); + + DEBUGA_GSMOPEN("%s CHANNEL CONSUME_MEDIA\n", GSMOPEN_P_LOG, tech_pvt->name); + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t channel_on_exchange_media(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + tech_pvt = (private_t *) switch_core_session_get_private(session); + DEBUGA_GSMOPEN("%s CHANNEL EXCHANGE_MEDIA\n", GSMOPEN_P_LOG, tech_pvt->name); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_on_soft_execute(switch_core_session_t *session) +{ + private_t *tech_pvt = NULL; + tech_pvt = (private_t *) switch_core_session_get_private(session); + DEBUGA_GSMOPEN("%s CHANNEL SOFT_EXECUTE\n", GSMOPEN_P_LOG, tech_pvt->name); + 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 = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + DEBUGA_GSMOPEN("%s CHANNEL SEND_DTMF\n", GSMOPEN_P_LOG, tech_pvt->name); + DEBUGA_GSMOPEN("DTMF: %c\n", GSMOPEN_P_LOG, dtmf->digit); + + gsmopen_senddigit(tech_pvt, dtmf->digit); + + 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_byte_t *data; +#if defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + int samples; + char digit_str[256]; +#endif // defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) +#ifdef GSMOPEN_PORTAUDIO +#ifdef WANT_SPEEX + spx_int16_t *speexptr; + spx_int16_t pcm2[160]; + int i; +#endif// GSMOPEN_ALSA +#endif// WANT_SPEEX + + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + if (!switch_channel_ready(channel) || !switch_test_flag(tech_pvt, TFLAG_IO)) { + ERRORA("channel not ready \n", GSMOPEN_P_LOG); + //TODO: kill the bastard + return SWITCH_STATUS_FALSE; + } + + + tech_pvt->read_frame.flags = SFF_NONE; + *frame = NULL; + + if (switch_test_flag(tech_pvt, TFLAG_HANGUP)) { + return SWITCH_STATUS_FALSE; + } + +#ifndef GSMOPEN_PORTAUDIO + switch_core_timer_next(&tech_pvt->timer_read); +#endif// GSMOPEN_PORTAUDIO + + if(tech_pvt->no_sound==1){ + goto cng; + } +#if defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) +#ifdef GSMOPEN_ALSA + //if ((samples = snd_pcm_readi(tech_pvt->alsac, tech_pvt->read_frame.data, tech_pvt->read_codec.implementation->samples_per_packet)) > 0) + if ((samples = alsa_read(tech_pvt, (short *) tech_pvt->read_frame.data, tech_pvt->read_codec.implementation->samples_per_packet)) > 0) +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + if ((samples = gsmopen_portaudio_read(tech_pvt, (short *) tech_pvt->read_frame.data, tech_pvt->read_codec.implementation->samples_per_packet)) > 0) +#endif// GSMOPEN_PORTAUDIO + { + +#ifdef GSMOPEN_PORTAUDIO +#ifdef WANT_SPEEX + + if (tech_pvt->speexecho) { + speexptr = ((spx_int16_t *) tech_pvt->read_frame.data); + /* Perform echo cancellation */ + speex_echo_capture(tech_pvt->echo_state, speexptr, pcm2); +#ifndef GIOVA48 + for (i = 0; i < 160; i++) +#else //GIOVA48 + for (i = 0; i < 960; i++) +#endif //GIOVA48 + speexptr[i] = pcm2[i]; + } + /* Apply noise/echo residual suppression */ + if (tech_pvt->speexpreprocess) { + speex_preprocess_run(tech_pvt->preprocess, speexptr); + } + + DEBUGA_GSMOPEN("read\n", GSMOPEN_P_LOG); +#endif //WANT_SPEEX +#endif // GSMOPEN_PORTAUDIO + + + + + + tech_pvt->read_frame.datalen = samples * 2; + tech_pvt->read_frame.samples = samples; + +#ifndef GSMOPEN_PORTAUDIO + tech_pvt->read_frame.timestamp = tech_pvt->timer_read.samplecount; +#endif// GSMOPEN_PORTAUDIO + + *frame = &tech_pvt->read_frame; + + //status = SWITCH_STATUS_SUCCESS; + switch_set_flag(tech_pvt, TFLAG_VOICE); + } + + //WARNINGA("samples=%d\n", GSMOPEN_P_LOG, samples); + if (samples != 160) { + ERRORA("samples=%d\n", GSMOPEN_P_LOG, samples); + goto cng; + } +//DEBUGA_GSMOPEN("samples=%d tech_pvt->read_frame.timestamp=%d\n", GSMOPEN_P_LOG, samples, tech_pvt->read_frame.timestamp); + +//usleep(17000); +//usleep(17000); + + + + + + memset(digit_str, 0, sizeof(digit_str)); + //teletone_dtmf_detect(&tech_pvt->dtmf_detect, (int16_t *) tech_pvt->read_frame.data, tech_pvt->read_frame.samples); + //teletone_dtmf_get(&tech_pvt->dtmf_detect, digit_str, sizeof(digit_str)); + dtmf_rx(&tech_pvt->dtmf_state, (int16_t *) tech_pvt->read_frame.data, tech_pvt->read_frame.samples); + dtmf_rx_get(&tech_pvt->dtmf_state, digit_str, sizeof(digit_str)); + + gsmopen_sound_boost(tech_pvt->read_frame.data, tech_pvt->read_frame.samples, tech_pvt->capture_boost); + + if (digit_str[0]) { + switch_time_t new_dtmf_timestamp = switch_time_now(); + if ((new_dtmf_timestamp - tech_pvt->old_dtmf_timestamp) > 350000) { //FIXME: make it configurable + char *p = digit_str; + switch_channel_t *channel = switch_core_session_get_channel(session); + + while (p && *p) { + switch_dtmf_t dtmf = {0}; + dtmf.digit = *p; + dtmf.duration = SWITCH_DEFAULT_DTMF_DURATION; + switch_channel_queue_dtmf(channel, &dtmf); + p++; + } + NOTICA("DTMF DETECTED: [%s] new_dtmf_timestamp: %u, delta_t: %u\n", GSMOPEN_P_LOG, digit_str, (unsigned int) new_dtmf_timestamp, + (unsigned int) (new_dtmf_timestamp - tech_pvt->old_dtmf_timestamp)); + tech_pvt->old_dtmf_timestamp = new_dtmf_timestamp; + } + } + while (switch_test_flag(tech_pvt, TFLAG_IO)) { + if (switch_test_flag(tech_pvt, TFLAG_BREAK)) { + switch_clear_flag(tech_pvt, TFLAG_BREAK); + DEBUGA_GSMOPEN("CHANNEL READ FRAME goto CNG\n", GSMOPEN_P_LOG); + goto cng; + } + + if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + DEBUGA_GSMOPEN("CHANNEL READ FRAME not IO\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(tech_pvt, TFLAG_IO) && switch_test_flag(tech_pvt, TFLAG_VOICE)) { + switch_clear_flag(tech_pvt, TFLAG_VOICE); + if (!tech_pvt->read_frame.datalen) { + DEBUGA_GSMOPEN("CHANNEL READ CONTINUE\n", GSMOPEN_P_LOG); + continue; + } + *frame = &tech_pvt->read_frame; +#ifdef BIGENDIAN + if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) { + switch_swap_linear((*frame)->data, (int) (*frame)->datalen / 2); + } +#endif + //WARNINGA("HERE\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; + } + + WARNINGA("HERE\n", GSMOPEN_P_LOG); + DEBUGA_GSMOPEN("CHANNEL READ no TFLAG_VOICE\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; + + } + + DEBUGA_GSMOPEN("CHANNEL READ FALSE\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_FALSE; +#endif // defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + cng: + data = (switch_byte_t *) tech_pvt->read_frame.data; + data[0] = 65; + data[1] = 0; + tech_pvt->read_frame.datalen = 2; + tech_pvt->read_frame.flags = SFF_CNG; + *frame = &tech_pvt->read_frame; +#ifdef GSMOPEN_PORTAUDIO + //speex_echo_state_reset(tech_pvt->stream->echo_state); +#endif // GSMOPEN_PORTAUDIO + return SWITCH_STATUS_SUCCESS; + +} + +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; +#if defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + unsigned int sent; +#endif // defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) +#ifdef GSMOPEN_PORTAUDIO +#ifdef WANT_SPEEX + spx_int16_t *speexptr; +#endif// GSMOPEN_ALSA +#endif// WANT_SPEEX + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + if (!switch_channel_ready(channel) || !switch_test_flag(tech_pvt, TFLAG_IO)) { + ERRORA("channel not ready \n", GSMOPEN_P_LOG); + //TODO: kill the bastard + return SWITCH_STATUS_FALSE; + } +#ifdef BIGENDIAN + if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) { +#ifdef WIN32 + switch_swap_linear((int16_t *)frame->data, (int) frame->datalen / 2); +#else + switch_swap_linear(frame->data, (int) frame->datalen / 2); +#endif //WIN32 + } +#endif + + //switch_core_timer_next(&tech_pvt->timer_write); + //sent = frame->datalen; + + //ERRORA("PLAY \n", GSMOPEN_P_LOG); + //snd_pcm_writei(tech_pvt->alsap, (short *) frame->data, (int) (frame->datalen / 2)); + + gsmopen_sound_boost(frame->data, frame->samples, tech_pvt->playback_boost); +#ifdef GSMOPEN_ALSA + + switch_core_timer_next(&tech_pvt->timer_write); + sent = alsa_write(tech_pvt, (short *) frame->data, (int) (frame->datalen)); +//DEBUGA_GSMOPEN("sent=%d \n", GSMOPEN_P_LOG, sent); + + if (sent && sent != frame->datalen / 2 && sent != -1) { + DEBUGA_GSMOPEN("sent %d\n", GSMOPEN_P_LOG, sent); + } +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + sent = gsmopen_portaudio_write(tech_pvt, (short *) frame->data, (int) (frame->datalen)); +//DEBUGA_GSMOPEN("sent=%d \n", GSMOPEN_P_LOG, sent); + + if (sent && sent != frame->datalen / 2 && sent != -1) { + DEBUGA_GSMOPEN("sent %d\n", GSMOPEN_P_LOG, sent); + } + +#ifdef WANT_SPEEX + if (tech_pvt->speexecho) { + speexptr = (spx_int16_t *) frame->data; + /* Put frame into playback buffer */ + speex_echo_playback(tech_pvt->echo_state, speexptr); + DEBUGA_GSMOPEN("write\n", GSMOPEN_P_LOG); + } +#endif //WANT_SPEEX +#endif // GSMOPEN_PORTAUDIO + //NOTICA("sent=%d\n", GSMOPEN_P_LOG, sent); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_answer_channel(switch_core_session_t *session) +{ + private_t *tech_pvt; + switch_channel_t *channel = NULL; + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + //ERRORA("%s CHANNEL INIT\n", GSMOPEN_P_LOG, tech_pvt->name); + switch_set_flag(tech_pvt, TFLAG_IO); + gsmopen_serial_answer(tech_pvt); + + /* Move channel's state machine to ROUTING. This means the call is trying + to get from the initial start where the call because, to the point + where a destination has been identified. If the channel is simply + left in the initial state, nothing will happen. */ + switch_channel_set_state(channel, CS_ROUTING); + switch_mutex_lock(globals.mutex); + globals.calls++; + + switch_mutex_unlock(globals.mutex); + DEBUGA_GSMOPEN("%s CHANNEL ANSWER %s\n", GSMOPEN_P_LOG, tech_pvt->name, switch_core_session_get_uuid(session)); + + + + + + + + + + + + + + + + + DEBUGA_GSMOPEN("ANSWERED! \n", GSMOPEN_P_LOG); + + return SWITCH_STATUS_SUCCESS; +} + +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; +#if defined(GSMOPEN_ALSA) + int samples; + short tmp_buffer[1280]; +#endif // defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + + tech_pvt = (private_t *) switch_core_session_get_private(session); + switch_assert(tech_pvt != NULL); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_ANSWER: + { + DEBUGA_GSMOPEN("MSG_ID=%d, TO BE ANSWERED!\n", GSMOPEN_P_LOG, msg->message_id); + channel_answer_channel(session); + } + break; + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + + DEBUGA_GSMOPEN("%s CHANNEL got SWITCH_MESSAGE_INDICATE_AUDIO_SYNC\n", GSMOPEN_P_LOG, switch_channel_get_name(channel)); + switch_core_timer_sync(&tech_pvt->timer_read); + switch_core_timer_sync(&tech_pvt->timer_write); + +#ifdef GSMOPEN_ALSA + while ((samples = alsa_read(tech_pvt, tmp_buffer, tech_pvt->read_codec.implementation->samples_per_packet * 4)) > 160) { + //WARNINGA("read %d samples\n", GSMOPEN_P_LOG, samples); + } +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + //while ((samples = gsmopen_portaudio_read(tech_pvt, tmp_buffer, tech_pvt->read_codec.implementation->samples_per_packet * 2)) > 160) { + //WARNINGA("read %d samples\n", GSMOPEN_P_LOG, samples); + //} +#ifdef WANT_SPEEX + speex_echo_state_reset(tech_pvt->echo_state); +#endif// WANT_SPEEX +#endif// GSMOPEN_PORTAUDIO + break; + + + default: + { + DEBUGA_GSMOPEN("MSG_ID=%d\n", GSMOPEN_P_LOG, msg->message_id); + } + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event) +{ + struct private_object *tech_pvt = (struct private_object *) switch_core_session_get_private(session); + char *body = switch_event_get_body(event); + switch_assert(tech_pvt != NULL); + + if (!body) { + body = (char *) ""; + } + + WARNINGA("event: |||%s|||\n", GSMOPEN_P_LOG, body); + + return SWITCH_STATUS_SUCCESS; +} + +switch_state_handler_table_t gsmopen_state_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 */ NULL, + /*.on_reset */ NULL, + /*.on_park */ NULL, + /*.on_reporting */ NULL, + /*.on_destroy */ channel_on_destroy +}; + +switch_io_routines_t gsmopen_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, + /*.receive_event */ channel_receive_event +}; + +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) +{ + private_t *tech_pvt = NULL; + if ((*new_session = switch_core_session_request(gsmopen_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool)) != 0) { + switch_channel_t *channel = NULL; + switch_caller_profile_t *caller_profile; + char *rdest; + int found = 0; + char interface_name[256]; + + DEBUGA_GSMOPEN("1 SESSION_REQUEST %s\n", GSMOPEN_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_add_stream(*new_session, NULL); + + + if (!zstr(outbound_profile->destination_number)) { + int i; + char *slash; + + switch_copy_string(interface_name, outbound_profile->destination_number, 255); + slash = strrchr(interface_name, '/'); + *slash = '\0'; + + switch_mutex_lock(globals.mutex); + if (strncmp("ANY", interface_name, strlen(interface_name)) == 0 || strncmp("RR", interface_name, strlen(interface_name)) == 0) { + /* we've been asked for the "ANY" interface, let's find the first idle interface */ + //DEBUGA_GSMOPEN("Finding one available gsmopen interface\n", GSMOPEN_P_LOG); + //tech_pvt = find_available_gsmopen_interface(NULL); + //if (tech_pvt) + //found = 1; + //} else if (strncmp("RR", interface_name, strlen(interface_name)) == 0) { + /* Find the first idle interface using Round Robin */ + DEBUGA_GSMOPEN("Finding one available gsmopen interface RR\n", GSMOPEN_P_LOG); + tech_pvt = find_available_gsmopen_interface_rr(NULL); + if (tech_pvt) { + found = 1; + DEBUGA_GSMOPEN("FOUND one available gsmopen interface RR\n", GSMOPEN_P_LOG); + } + } + + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, interface_name, strlen(interface_name)) == 0)) { + if (strlen(globals.GSMOPEN_INTERFACES[i].session_uuid_str)) { + DEBUGA_GSMOPEN + ("globals.GSMOPEN_INTERFACES[%d].name=|||%s||| session_uuid_str=|||%s||| is BUSY\n", + GSMOPEN_P_LOG, i, globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].session_uuid_str); + DEBUGA_GSMOPEN("1 SESSION_DESTROY %s\n", GSMOPEN_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + + DEBUGA_GSMOPEN("globals.GSMOPEN_INTERFACES[%d].name=|||%s|||?\n", GSMOPEN_P_LOG, i, globals.GSMOPEN_INTERFACES[i].name); + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + found = 1; + break; + } + + } + + } else { + ERRORA("Doh! no destination number?\n", GSMOPEN_P_LOG); + switch_core_session_destroy(new_session); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + if (!found) { + DEBUGA_GSMOPEN("Doh! no available interface for |||%s|||?\n", GSMOPEN_P_LOG, interface_name); + DEBUGA_GSMOPEN("2 SESSION_DESTROY %s\n", GSMOPEN_P_LOG, switch_core_session_get_uuid(*new_session)); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + //return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + return SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION; + } + + channel = switch_core_session_get_channel(*new_session); + if (!channel) { + ERRORA("Doh! no channel?\n", GSMOPEN_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + if (gsmopen_tech_init(tech_pvt, *new_session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Doh! no tech_init?\n", GSMOPEN_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + + if (outbound_profile) { + char name[128]; + + snprintf(name, sizeof(name), "gsmopen/%s", outbound_profile->destination_number); + //snprintf(name, sizeof(name), "gsmopen/%s", tech_pvt->name); + switch_channel_set_name(channel, name); + caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); + switch_channel_set_caller_profile(channel, caller_profile); + tech_pvt->caller_profile = caller_profile; + } else { + ERRORA("Doh! no caller profile\n", GSMOPEN_P_LOG); + switch_core_session_destroy(new_session); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } + + tech_pvt->ob_calls++; + + rdest = strchr(caller_profile->destination_number, '/'); + *rdest++ = '\0'; + + //gsmopen_call(tech_pvt, rdest, 30); + + switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(*new_session), sizeof(tech_pvt->session_uuid_str)); + caller_profile = tech_pvt->caller_profile; + caller_profile->destination_number = rdest; + + switch_set_flag(tech_pvt, TFLAG_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + gsmopen_call(tech_pvt, rdest, 30); + switch_mutex_unlock(globals.mutex); + return SWITCH_CAUSE_SUCCESS; + } + + ERRORA("Doh! no new_session\n", GSMOPEN_P_LOG); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; +} + +/*! + * \brief This thread runs during a call, and monitor the interface for signaling, like hangup, caller id, etc most of signaling is handled inside the gsmopen_signaling_read function + * + */ + +static switch_status_t load_config(int reload_type) +{ + const char *cf = "gsmopen.conf"; + switch_xml_t cfg, xml, global_settings, param, interfaces, myinterface; + private_t *tech_pvt = NULL; + + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, gsmopen_module_pool); + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + ERRORA("open of %s failed\n", GSMOPEN_P_LOG, cf); + running = 0; + switch_xml_free(xml); + return SWITCH_STATUS_TERM; + } + + switch_mutex_lock(globals.mutex); + if ((global_settings = switch_xml_child(cfg, "global_settings"))) { + for (param = switch_xml_child(global_settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "debug")) { + DEBUGA_GSMOPEN("globals.debug=%d\n", GSMOPEN_P_LOG, globals.debug); + globals.debug = atoi(val); + DEBUGA_GSMOPEN("globals.debug=%d\n", GSMOPEN_P_LOG, globals.debug); + } else if (!strcasecmp(var, "hold-music")) { + switch_set_string(globals.hold_music, val); + DEBUGA_GSMOPEN("globals.hold_music=%s\n", GSMOPEN_P_LOG, globals.hold_music); + } else if (!strcmp(var, "dialplan")) { + set_global_dialplan(val); + DEBUGA_GSMOPEN("globals.dialplan=%s\n", GSMOPEN_P_LOG, globals.dialplan); + } else if (!strcmp(var, "destination")) { + set_global_destination(val); + DEBUGA_GSMOPEN("globals.destination=%s\n", GSMOPEN_P_LOG, globals.destination); + } else if (!strcmp(var, "context")) { + set_global_context(val); + DEBUGA_GSMOPEN("globals.context=%s\n", GSMOPEN_P_LOG, globals.context); + + } + + } + } + + if ((interfaces = switch_xml_child(cfg, "per_interface_settings"))) { + int i = 0; + + for (myinterface = switch_xml_child(interfaces, "interface"); myinterface; myinterface = myinterface->next) { + char *id = (char *) switch_xml_attr(myinterface, "id"); + char *name = (char *) switch_xml_attr(myinterface, "name"); + const char *context = "default"; + const char *dialplan = "XML"; + const char *destination = "5000"; + const char *controldevice_name = "/dev/ttyACM0"; + char *digit_timeout = NULL; + char *max_digits = NULL; + char *hotline = NULL; + char *dial_regex = NULL; + char *hold_music = NULL; + char *fail_dial_regex = NULL; + const char *enable_callerid = "true"; + + + const char *at_dial_pre_number = "ATD"; + const char *at_dial_post_number = ";"; + const char *at_dial_expect = "OK"; + const char *at_hangup = "ATH"; + const char *at_hangup_expect = "OK"; + const char *at_answer = "ATA"; + const char *at_answer_expect = "OK"; + const char *at_send_dtmf = "AT+VTS"; + const char *at_preinit_1 = ""; + const char *at_preinit_1_expect = ""; + const char *at_preinit_2 = ""; + const char *at_preinit_2_expect = ""; + const char *at_preinit_3 = ""; + const char *at_preinit_3_expect = ""; + const char *at_preinit_4 = ""; + const char *at_preinit_4_expect = ""; + const char *at_preinit_5 = ""; + const char *at_preinit_5_expect = ""; + const char *at_postinit_1 = "at+cmic=0,9"; + const char *at_postinit_1_expect = "OK"; + const char *at_postinit_2 = "AT+CKPD=\"EEE\""; + const char *at_postinit_2_expect = "OK"; + const char *at_postinit_3 = "AT+CSSN=1,0"; + const char *at_postinit_3_expect = "OK"; + const char *at_postinit_4 = "at+sidet=0"; + const char *at_postinit_4_expect = "OK"; + const char *at_postinit_5 = "at+clvl=99"; + const char *at_postinit_5_expect = "OK"; + const char *at_query_battchg = "AT+CBC"; + const char *at_query_battchg_expect = "OK"; + const char *at_query_signal = "AT+CSQ"; + const char *at_query_signal_expect = "OK"; + const char *at_call_idle = "+MCST: 1"; + const char *at_call_incoming = "+MCST: 2"; + const char *at_call_active = "+CSSI: 7"; + const char *at_call_failed = "+MCST: 65"; + const char *at_call_calling = "+CSSI: 1"; + const char *at_indicator_noservice_string = "CIEV: 2;0"; + const char *at_indicator_nosignal_string = "CIEV: 5;0"; + const char *at_indicator_lowsignal_string = "CIEV: 5;1"; + const char *at_indicator_lowbattchg_string = "CIEV: 0;1"; + const char *at_indicator_nobattchg_string = "CIEV: 0;0"; + const char *at_indicator_callactive_string = "CIEV: 3;1"; + const char *at_indicator_nocallactive_string = "CIEV: 3;0"; + const char *at_indicator_nocallsetup_string = "CIEV: 6;0"; + const char *at_indicator_callsetupincoming_string = "CIEV: 6;1"; + const char *at_indicator_callsetupoutgoing_string = "CIEV: 6;2"; + const char *at_indicator_callsetupremoteringing_string = "CIEV: 6;3"; + //const char *sms_receiving_program = "/usr/local/bin/ciapalo"; + const char *alsacname = "plughw:1"; + const char *alsapname = "plughw:1"; + const char *at_early_audio = "0"; + const char *at_after_preinit_pause = "500000"; + const char *at_initial_pause = "500000"; + const char *at_has_clcc = "0"; + const char *at_has_ecam = "0"; + const char *alsa_period_size = "160"; + const char *alsa_periods_in_buffer = "4"; + const char *gsmopen_sound_rate = "8000"; + const char *alsa_play_is_mono = "1"; + const char *alsa_capture_is_mono = "1"; + const char *capture_boost = "5"; + const char *playback_boost = "10"; +#if defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + const char *no_sound = "0"; +#else + const char *no_sound = "1"; +#endif // defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + const char *portaudiocindex = "1"; + const char *portaudiopindex = "1"; + const char *speexecho = "1"; + const char *speexpreprocess = "1"; + + uint32_t interface_id = 0; +#ifdef WIN32 + int controldevice_speed = 115200; //FIXME TODO +#else + uint32_t controldevice_speed = B115200; //FIXME TODO +#endif //WIN32 + uint32_t controldevprotocol = PROTOCOL_AT; //FIXME TODO + uint32_t running = 1; //FIXME TODO + const char *gsmopen_serial_sync_period = "300"; //FIXME TODO + + + + tech_pvt = NULL; + + for (param = switch_xml_child(myinterface, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "id")) { + id = val; + } else if (!strcasecmp(var, "name")) { + name = val; + } else if (!strcasecmp(var, "context")) { + context = val; + } else if (!strcasecmp(var, "dialplan")) { + dialplan = val; + } else if (!strcasecmp(var, "destination")) { + destination = val; + } else if (!strcasecmp(var, "controldevice_name")) { + controldevice_name = val; + } else if (!strcasecmp(var, "digit_timeout")) { + digit_timeout = val; + } else if (!strcasecmp(var, "max_digits")) { + max_digits = val; + } else if (!strcasecmp(var, "hotline")) { + hotline = val; + } else if (!strcasecmp(var, "dial_regex")) { + dial_regex = val; + } else if (!strcasecmp(var, SWITCH_HOLD_MUSIC_VARIABLE)) { + hold_music = val; + } else if (!strcasecmp(var, "fail_dial_regex")) { + fail_dial_regex = val; + } else if (!strcasecmp(var, "enable_callerid")) { + enable_callerid = val; + } else if (!strcasecmp(var, "at_dial_pre_number")) { + at_dial_pre_number = val; + } else if (!strcasecmp(var, "at_dial_post_number")) { + at_dial_post_number = val; + } else if (!strcasecmp(var, "at_dial_expect")) { + at_dial_expect = val; + } else if (!strcasecmp(var, "at_hangup")) { + at_hangup = val; + } else if (!strcasecmp(var, "at_hangup_expect")) { + at_hangup_expect = val; + } else if (!strcasecmp(var, "at_answer")) { + at_answer = val; + } else if (!strcasecmp(var, "at_answer_expect")) { + at_answer_expect = val; + } else if (!strcasecmp(var, "at_send_dtmf")) { + at_send_dtmf = val; + } else if (!strcasecmp(var, "at_preinit_1")) { + at_preinit_1 = val; + } else if (!strcasecmp(var, "at_preinit_1_expect")) { + at_preinit_1_expect = val; + } else if (!strcasecmp(var, "at_preinit_2")) { + at_preinit_2 = val; + } else if (!strcasecmp(var, "at_preinit_2_expect")) { + at_preinit_2_expect = val; + } else if (!strcasecmp(var, "at_preinit_3")) { + at_preinit_3 = val; + } else if (!strcasecmp(var, "at_preinit_3_expect")) { + at_preinit_3_expect = val; + } else if (!strcasecmp(var, "at_preinit_4")) { + at_preinit_4 = val; + } else if (!strcasecmp(var, "at_preinit_4_expect")) { + at_preinit_4_expect = val; + } else if (!strcasecmp(var, "at_preinit_5")) { + at_preinit_5 = val; + } else if (!strcasecmp(var, "at_preinit_5_expect")) { + at_preinit_5_expect = val; + } else if (!strcasecmp(var, "at_postinit_1")) { + at_postinit_1 = val; + } else if (!strcasecmp(var, "at_postinit_1_expect")) { + at_postinit_1_expect = val; + } else if (!strcasecmp(var, "at_postinit_2")) { + at_postinit_2 = val; + } else if (!strcasecmp(var, "at_postinit_2_expect")) { + at_postinit_2_expect = val; + } else if (!strcasecmp(var, "at_postinit_3")) { + at_postinit_3 = val; + } else if (!strcasecmp(var, "at_postinit_3_expect")) { + at_postinit_3_expect = val; + } else if (!strcasecmp(var, "at_postinit_4")) { + at_postinit_4 = val; + } else if (!strcasecmp(var, "at_postinit_4_expect")) { + at_postinit_4_expect = val; + } else if (!strcasecmp(var, "at_postinit_5")) { + at_postinit_5 = val; + } else if (!strcasecmp(var, "at_postinit_5_expect")) { + at_postinit_5_expect = val; + } else if (!strcasecmp(var, "at_query_battchg")) { + at_query_battchg = val; + } else if (!strcasecmp(var, "at_query_battchg_expect")) { + at_query_battchg_expect = val; + } else if (!strcasecmp(var, "at_query_signal")) { + at_query_signal = val; + } else if (!strcasecmp(var, "at_query_signal_expect")) { + at_query_signal_expect = val; + } else if (!strcasecmp(var, "at_call_idle")) { + at_call_idle = val; + } else if (!strcasecmp(var, "at_call_incoming")) { + at_call_incoming = val; + } else if (!strcasecmp(var, "at_call_active")) { + at_call_active = val; + } else if (!strcasecmp(var, "at_call_failed")) { + at_call_failed = val; + } else if (!strcasecmp(var, "at_call_calling")) { + at_call_calling = val; + } else if (!strcasecmp(var, "at_indicator_noservice_string")) { + at_indicator_noservice_string = val; + } else if (!strcasecmp(var, "at_indicator_nosignal_string")) { + at_indicator_nosignal_string = val; + } else if (!strcasecmp(var, "at_indicator_lowsignal_string")) { + at_indicator_lowsignal_string = val; + } else if (!strcasecmp(var, "at_indicator_lowbattchg_string")) { + at_indicator_lowbattchg_string = val; + } else if (!strcasecmp(var, "at_indicator_nobattchg_string")) { + at_indicator_nobattchg_string = val; + } else if (!strcasecmp(var, "at_indicator_callactive_string")) { + at_indicator_callactive_string = val; + } else if (!strcasecmp(var, "at_indicator_nocallactive_string")) { + at_indicator_nocallactive_string = val; + } else if (!strcasecmp(var, "at_indicator_nocallsetup_string")) { + at_indicator_nocallsetup_string = val; + } else if (!strcasecmp(var, "at_indicator_callsetupincoming_string")) { + at_indicator_callsetupincoming_string = val; + } else if (!strcasecmp(var, "at_indicator_callsetupoutgoing_string")) { + at_indicator_callsetupoutgoing_string = val; + } else if (!strcasecmp(var, "at_indicator_callsetupremoteringing_string")) { + at_indicator_callsetupremoteringing_string = val; + //} else if (!strcasecmp(var, "sms_receiving_program")) { + //sms_receiving_program = val; + } else if (!strcasecmp(var, "alsacname")) { + alsacname = val; + } else if (!strcasecmp(var, "alsapname")) { + alsapname = val; + } else if (!strcasecmp(var, "portaudiocindex")) { + portaudiocindex = val; + } else if (!strcasecmp(var, "portaudiopindex")) { + portaudiopindex = val; + } else if (!strcasecmp(var, "speexecho")) { + speexecho = val; + } else if (!strcasecmp(var, "speexpreprocess")) { + speexpreprocess = val; + } else if (!strcasecmp(var, "at_early_audio")) { + at_early_audio = val; + } else if (!strcasecmp(var, "at_after_preinit_pause")) { + at_after_preinit_pause = val; + } else if (!strcasecmp(var, "at_initial_pause")) { + at_initial_pause = val; + } else if (!strcasecmp(var, "at_has_clcc")) { + at_has_clcc = val; + } else if (!strcasecmp(var, "at_has_ecam")) { + at_has_ecam = val; + } else if (!strcasecmp(var, "alsa_period_size")) { + alsa_period_size = val; + } else if (!strcasecmp(var, "alsa_periods_in_buffer")) { + alsa_periods_in_buffer = val; + } else if (!strcasecmp(var, "gsmopen_sound_rate")) { + gsmopen_sound_rate = val; + } else if (!strcasecmp(var, "alsa_play_is_mono")) { + alsa_play_is_mono = val; + } else if (!strcasecmp(var, "alsa_capture_is_mono")) { + alsa_capture_is_mono = val; + } else if (!strcasecmp(var, "capture_boost")) { + capture_boost = val; + } else if (!strcasecmp(var, "playback_boost")) { + playback_boost = val; + } else if (!strcasecmp(var, "no_sound")) { + no_sound = val; + } else if (!strcasecmp(var, "gsmopen_serial_sync_period")) { + gsmopen_serial_sync_period = val; + } + + + } + + /* BEGIN: Changes here */ + if (reload_type == SOFT_RELOAD) { + char the_interface[256]; + sprintf(the_interface, "#%s", name); + + if (interface_exists(the_interface) == SWITCH_STATUS_SUCCESS) { + continue; + } + } + /* END: Changes here */ + + if (!id) { + ERRORA("interface missing REQUIRED param 'id'\n", GSMOPEN_P_LOG); + continue; + } + + if (switch_is_number(id)) { + interface_id = atoi(id); + } else { + ERRORA("interface param 'id' MUST be a number, now id='%s'\n", GSMOPEN_P_LOG, id); + continue; + } + + if (!switch_is_number(at_early_audio)) { + ERRORA("interface param 'at_early_audio' MUST be a number, now at_early_audio='%s'\n", GSMOPEN_P_LOG, at_early_audio); + continue; + } + if (!switch_is_number(at_after_preinit_pause)) { + ERRORA("interface param 'at_after_preinit_pause' MUST be a number, now at_after_preinit_pause='%s'\n", GSMOPEN_P_LOG, + at_after_preinit_pause); + continue; + } + if (!switch_is_number(at_initial_pause)) { + ERRORA("interface param 'at_initial_pause' MUST be a number, now at_initial_pause='%s'\n", GSMOPEN_P_LOG, at_initial_pause); + continue; + } + if (!switch_is_number(at_has_clcc)) { + ERRORA("interface param 'at_has_clcc' MUST be a number, now at_has_clcc='%s'\n", GSMOPEN_P_LOG, at_has_clcc); + continue; + } + if (!switch_is_number(at_has_ecam)) { + ERRORA("interface param 'at_has_ecam' MUST be a number, now at_has_ecam='%s'\n", GSMOPEN_P_LOG, at_has_ecam); + continue; + } + if (!switch_is_number(alsa_period_size)) { + ERRORA("interface param 'alsa_period_size' MUST be a number, now alsa_period_size='%s'\n", GSMOPEN_P_LOG, alsa_period_size); + continue; + } + if (!switch_is_number(alsa_periods_in_buffer)) { + ERRORA("interface param 'alsa_periods_in_buffer' MUST be a number, now alsa_periods_in_buffer='%s'\n", GSMOPEN_P_LOG, + alsa_periods_in_buffer); + continue; + } + if (!switch_is_number(gsmopen_sound_rate)) { + ERRORA("interface param 'gsmopen_sound_rate' MUST be a number, now gsmopen_sound_rate='%s'\n", GSMOPEN_P_LOG, gsmopen_sound_rate); + continue; + } + if (!switch_is_number(alsa_play_is_mono)) { + ERRORA("interface param 'alsa_play_is_mono' MUST be a number, now alsa_play_is_mono='%s'\n", GSMOPEN_P_LOG, alsa_play_is_mono); + continue; + } + if (!switch_is_number(alsa_capture_is_mono)) { + ERRORA("interface param 'alsa_capture_is_mono' MUST be a number, now alsa_capture_is_mono='%s'\n", GSMOPEN_P_LOG, alsa_capture_is_mono); + continue; + } + if (!switch_is_number(capture_boost)) { + ERRORA("interface param 'capture_boost' MUST be a number, now capture_boost='%s'\n", GSMOPEN_P_LOG, capture_boost); + continue; + } + if (!switch_is_number(playback_boost)) { + ERRORA("interface param 'playback_boost' MUST be a number, now playback_boost='%s'\n", GSMOPEN_P_LOG, playback_boost); + continue; + } + if (!switch_is_number(no_sound)) { + ERRORA("interface param 'no_sound' MUST be a number, now no_sound='%s'\n", GSMOPEN_P_LOG, no_sound); + continue; + } + if (!switch_is_number(gsmopen_serial_sync_period)) { + ERRORA("interface param 'gsmopen_serial_sync_period' MUST be a number, now gsmopen_serial_sync_period='%s'\n", GSMOPEN_P_LOG, gsmopen_serial_sync_period); + continue; + } + + + if (interface_id && interface_id < GSMOPEN_MAX_INTERFACES) { + private_t newconf; + switch_threadattr_t *gsmopen_api_thread_attr = NULL; + int res = 0; + + memset(&newconf, '\0', sizeof(newconf)); + globals.GSMOPEN_INTERFACES[interface_id] = newconf; + + + tech_pvt = &globals.GSMOPEN_INTERFACES[interface_id]; + + switch_mutex_init(&globals.GSMOPEN_INTERFACES[interface_id].controldev_lock, SWITCH_MUTEX_NESTED, gsmopen_module_pool); + + + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].id, id); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].name, name); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].context, context); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].dialplan, dialplan); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].destination, destination); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].controldevice_name, controldevice_name); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].dial_regex, dial_regex); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].hold_music, hold_music); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].fail_dial_regex, fail_dial_regex); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_dial_pre_number, at_dial_pre_number); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_dial_post_number, at_dial_post_number); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_dial_expect, at_dial_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_hangup, at_hangup); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_hangup_expect, at_hangup_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_answer, at_answer); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_answer_expect, at_answer_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_send_dtmf, at_send_dtmf); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_1, at_preinit_1); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_1_expect, at_preinit_1_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_2, at_preinit_2); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_2_expect, at_preinit_2_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_3, at_preinit_3); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_3_expect, at_preinit_3_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_4, at_preinit_4); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_4_expect, at_preinit_4_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_5, at_preinit_5); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_preinit_5_expect, at_preinit_5_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_1, at_postinit_1); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_1_expect, at_postinit_1_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_2, at_postinit_2); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_2_expect, at_postinit_2_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_3, at_postinit_3); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_3_expect, at_postinit_3_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_4, at_postinit_4); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_4_expect, at_postinit_4_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_5, at_postinit_5); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_postinit_5_expect, at_postinit_5_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_query_battchg, at_query_battchg); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_query_battchg_expect, at_query_battchg_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_query_signal, at_query_signal); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_query_signal_expect, at_query_signal_expect); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_call_idle, at_call_idle); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_call_incoming, at_call_incoming); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_call_active, at_call_active); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_call_failed, at_call_failed); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_call_calling, at_call_calling); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_noservice_string, at_indicator_noservice_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_nosignal_string, at_indicator_nosignal_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_lowsignal_string, at_indicator_lowsignal_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_lowbattchg_string, at_indicator_lowbattchg_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_nobattchg_string, at_indicator_nobattchg_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_callactive_string, at_indicator_callactive_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_nocallactive_string, at_indicator_nocallactive_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_nocallsetup_string, at_indicator_nocallsetup_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_callsetupincoming_string, at_indicator_callsetupincoming_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_callsetupoutgoing_string, at_indicator_callsetupoutgoing_string); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].at_indicator_callsetupremoteringing_string, + at_indicator_callsetupremoteringing_string); + //switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].sms_receiving_program, sms_receiving_program); +#ifdef GSMOPEN_ALSA + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].alsacname, alsacname); + switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].alsapname, alsapname); +#endif// GSMOPEN_ALSA + +#ifdef GSMOPEN_PORTAUDIO + globals.GSMOPEN_INTERFACES[interface_id].portaudiocindex = atoi(portaudiocindex); + globals.GSMOPEN_INTERFACES[interface_id].portaudiopindex = atoi(portaudiopindex); + globals.GSMOPEN_INTERFACES[interface_id].speexecho = atoi(speexecho); + globals.GSMOPEN_INTERFACES[interface_id].speexpreprocess = atoi(speexpreprocess); +#endif// GSMOPEN_PORTAUDIO + globals.GSMOPEN_INTERFACES[interface_id].at_early_audio = atoi(at_early_audio); + globals.GSMOPEN_INTERFACES[interface_id].at_after_preinit_pause = atoi(at_after_preinit_pause); + globals.GSMOPEN_INTERFACES[interface_id].at_initial_pause = atoi(at_initial_pause); + globals.GSMOPEN_INTERFACES[interface_id].at_has_clcc = atoi(at_has_clcc); + globals.GSMOPEN_INTERFACES[interface_id].at_has_ecam = atoi(at_has_ecam); +#ifdef GSMOPEN_ALSA + globals.GSMOPEN_INTERFACES[interface_id].alsa_period_size = atoi(alsa_period_size); + globals.GSMOPEN_INTERFACES[interface_id].alsa_periods_in_buffer = atoi(alsa_periods_in_buffer); + globals.GSMOPEN_INTERFACES[interface_id].gsmopen_sound_rate = atoi(gsmopen_sound_rate); + globals.GSMOPEN_INTERFACES[interface_id].alsa_play_is_mono = atoi(alsa_play_is_mono); + globals.GSMOPEN_INTERFACES[interface_id].alsa_capture_is_mono = atoi(alsa_capture_is_mono); +#endif// GSMOPEN_ALSA + globals.GSMOPEN_INTERFACES[interface_id].capture_boost = atoi(capture_boost); + globals.GSMOPEN_INTERFACES[interface_id].playback_boost = atoi(playback_boost); +#if defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + globals.GSMOPEN_INTERFACES[interface_id].no_sound = atoi(no_sound); +#else + globals.GSMOPEN_INTERFACES[interface_id].no_sound = 1; +#endif // defined(GSMOPEN_ALSA) || defined(GSMOPEN_PORTAUDIO) + globals.GSMOPEN_INTERFACES[interface_id].gsmopen_serial_sync_period = atoi(gsmopen_serial_sync_period); + + + + globals.GSMOPEN_INTERFACES[interface_id].controldevice_speed = controldevice_speed; //FIXME + globals.GSMOPEN_INTERFACES[interface_id].controldevprotocol = controldevprotocol; //FIXME + globals.GSMOPEN_INTERFACES[interface_id].running = running; //FIXME + + + + WARNINGA("STARTING interface_id=%d\n", GSMOPEN_P_LOG, interface_id); + DEBUGA_GSMOPEN("id=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].id); + DEBUGA_GSMOPEN("name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].name); + DEBUGA_GSMOPEN("hold-music=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].hold_music); + DEBUGA_GSMOPEN("context=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].context); + DEBUGA_GSMOPEN("dialplan=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].dialplan); + DEBUGA_GSMOPEN("destination=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].destination); + DEBUGA_GSMOPEN("controldevice_name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].controldevice_name); +#ifdef GSMOPEN_ALSA + DEBUGA_GSMOPEN("alsacname=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].alsacname); + DEBUGA_GSMOPEN("alsapname=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].alsapname); +#endif// GSMOPEN_ALSA + + +#ifdef GSMOPEN_PORTAUDIO + //FIXME + //globals.GSMOPEN_INTERFACES[interface_id].portaudiocindex = 1; + //globals.GSMOPEN_INTERFACES[interface_id].portaudiopindex = 1; + //globals.GSMOPEN_INTERFACES[interface_id].speexecho = 1; + //globals.GSMOPEN_INTERFACES[interface_id].speexpreprocess = 1; + DEBUGA_GSMOPEN("portaudiocindex=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].portaudiocindex); + DEBUGA_GSMOPEN("portaudiocindex=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].portaudiocindex); + DEBUGA_GSMOPEN("speexecho=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].speexecho); + DEBUGA_GSMOPEN("speexpreprocess=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[interface_id].speexpreprocess); +#endif// GSMOPEN_PORTAUDIO + DEBUGA_GSMOPEN("gsmopen_serial_sync_period=%d\n", GSMOPEN_P_LOG, (int)globals.GSMOPEN_INTERFACES[interface_id].gsmopen_serial_sync_period); + /* init the serial port */ + if (globals.GSMOPEN_INTERFACES[interface_id].controldevprotocol != PROTOCOL_NO_SERIAL) { + globals.GSMOPEN_INTERFACES[interface_id].controldevfd = + gsmopen_serial_init(&globals.GSMOPEN_INTERFACES[interface_id], globals.GSMOPEN_INTERFACES[interface_id].controldevice_speed); + if (globals.GSMOPEN_INTERFACES[interface_id].controldevfd == -1) { + ERRORA("gsmopen_serial_init failed\n", GSMOPEN_P_LOG); + ERRORA("STARTING interface_id=%d FAILED\n", GSMOPEN_P_LOG, interface_id); + //return SWITCH_STATUS_FALSE; + globals.GSMOPEN_INTERFACES[interface_id].running=0; + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "gsmopen_serial_init failed"); + globals.GSMOPEN_INTERFACES[interface_id].active=0; + globals.GSMOPEN_INTERFACES[interface_id].name[0]='\0'; + continue; + } + } + + /* config the phone/modem on the serial port */ + if (globals.GSMOPEN_INTERFACES[interface_id].controldevprotocol != PROTOCOL_NO_SERIAL) { + res = gsmopen_serial_config(&globals.GSMOPEN_INTERFACES[interface_id]); + if (res) { + int count = 0; + ERRORA("gsmopen_serial_config failed, let's try again\n", GSMOPEN_P_LOG); + while(res && count < 5){ + switch_sleep(100000); //0.1 seconds + res = gsmopen_serial_config(&globals.GSMOPEN_INTERFACES[interface_id]); + count++; + if (res) { + ERRORA("%d: gsmopen_serial_config failed, let's try again\n", GSMOPEN_P_LOG, count); + } + } + if (res) { + ERRORA("STARTING interface_id=%d FAILED\n", GSMOPEN_P_LOG, interface_id); + //return SWITCH_STATUS_FALSE; + globals.GSMOPEN_INTERFACES[interface_id].running=0; + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "gsmopen_serial_config failed"); + globals.GSMOPEN_INTERFACES[interface_id].active=0; + globals.GSMOPEN_INTERFACES[interface_id].name[0]='\0'; + continue; + } + } + } + + if(globals.GSMOPEN_INTERFACES[interface_id].no_sound==0){ +#ifdef GSMOPEN_ALSA + if (alsa_init(&globals.GSMOPEN_INTERFACES[interface_id])) { + ERRORA("alsa_init failed\n", GSMOPEN_P_LOG); + ERRORA("STARTING interface_id=%d FAILED\n", GSMOPEN_P_LOG, interface_id); + //return SWITCH_STATUS_FALSE; + globals.GSMOPEN_INTERFACES[interface_id].running=0; + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "alsa_init failed"); + globals.GSMOPEN_INTERFACES[interface_id].active=0; + globals.GSMOPEN_INTERFACES[interface_id].name[0]='\0'; + continue; + + } + + if (alsa_shutdown(&globals.GSMOPEN_INTERFACES[interface_id])) { + ERRORA("alsa_shutdown failed\n", GSMOPEN_P_LOG); + ERRORA("STARTING interface_id=%d FAILED\n", GSMOPEN_P_LOG, interface_id); + //return SWITCH_STATUS_FALSE; + globals.GSMOPEN_INTERFACES[interface_id].running=0; + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "alsa_shutdown failed"); + globals.GSMOPEN_INTERFACES[interface_id].active=0; + globals.GSMOPEN_INTERFACES[interface_id].name[0]='\0'; + continue; + + } +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + if (gsmopen_portaudio_init(&globals.GSMOPEN_INTERFACES[interface_id])) { + ERRORA("gsmopen_portaudio_init failed\n", GSMOPEN_P_LOG); + ERRORA("STARTING interface_id=%d FAILED\n", GSMOPEN_P_LOG, interface_id); + //return SWITCH_STATUS_FALSE; + globals.GSMOPEN_INTERFACES[interface_id].running=0; + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "gsmopen_portaudio_init failed"); + globals.GSMOPEN_INTERFACES[interface_id].active=0; + globals.GSMOPEN_INTERFACES[interface_id].name[0]='\0'; + continue; + + } + + if (gsmopen_portaudio_shutdown(&globals.GSMOPEN_INTERFACES[interface_id])) { + ERRORA("gsmopen_portaudio_shutdown failed\n", GSMOPEN_P_LOG); + ERRORA("STARTING interface_id=%d FAILED\n", GSMOPEN_P_LOG, interface_id); + //return SWITCH_STATUS_FALSE; + globals.GSMOPEN_INTERFACES[interface_id].running=0; + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "gsmopen_portaudio_shutdown failed"); + globals.GSMOPEN_INTERFACES[interface_id].active=0; + globals.GSMOPEN_INTERFACES[interface_id].name[0]='\0'; + continue; + + } +#endif// GSMOPEN_PORTAUDIO + } + + globals.GSMOPEN_INTERFACES[interface_id].active=1; + + //gsmopen_store_boost((char *)"5", &globals.GSMOPEN_INTERFACES[interface_id].capture_boost); //FIXME + //gsmopen_store_boost((char *)"10", &globals.GSMOPEN_INTERFACES[interface_id].playback_boost); //FIXME + gsmopen_store_boost((char *) capture_boost, &globals.GSMOPEN_INTERFACES[interface_id].capture_boost); //FIXME + gsmopen_store_boost((char *) playback_boost, &globals.GSMOPEN_INTERFACES[interface_id].playback_boost); //FIXME + + switch_sleep(100000); + switch_threadattr_create(&gsmopen_api_thread_attr, gsmopen_module_pool); + switch_threadattr_stacksize_set(gsmopen_api_thread_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread, gsmopen_api_thread_attr, gsmopen_do_gsmopenapi_thread, + &globals.GSMOPEN_INTERFACES[interface_id], gsmopen_module_pool); + + switch_sleep(100000); + WARNINGA("STARTED interface_id=%d\n", GSMOPEN_P_LOG, interface_id); + + } else { + ERRORA("interface id %d is higher than GSMOPEN_MAX_INTERFACES (%d)\n", GSMOPEN_P_LOG, interface_id, GSMOPEN_MAX_INTERFACES); + alarm_event(&globals.GSMOPEN_INTERFACES[interface_id], ALARM_FAILED_INTERFACE, "interface id is higher than GSMOPEN_MAX_INTERFACES"); + continue; + } + + } + + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { + /* How many real intterfaces */ + globals.real_interfaces = i + 1; + + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + + DEBUGA_GSMOPEN("id=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].id); + DEBUGA_GSMOPEN("name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].name); + DEBUGA_GSMOPEN("context=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].context); + DEBUGA_GSMOPEN("hold-music=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].hold_music); + DEBUGA_GSMOPEN("dialplan=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].dialplan); + DEBUGA_GSMOPEN("destination=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].destination); + DEBUGA_GSMOPEN("controldevice_name=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].controldevice_name); +#ifdef GSMOPEN_ALSA + DEBUGA_GSMOPEN("alsacname=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].alsacname); + DEBUGA_GSMOPEN("alsapname=%s\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].alsapname); +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + DEBUGA_GSMOPEN("portaudiocindex=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].portaudiocindex); + DEBUGA_GSMOPEN("portaudiopindex=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].portaudiopindex); + DEBUGA_GSMOPEN("speexecho=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].speexecho); + DEBUGA_GSMOPEN("speexpreprocess=%d\n", GSMOPEN_P_LOG, globals.GSMOPEN_INTERFACES[i].speexpreprocess); +#endif// GSMOPEN_PORTAUDIO + DEBUGA_GSMOPEN("gsmopen_serial_sync_period=%d\n", GSMOPEN_P_LOG, (int)globals.GSMOPEN_INTERFACES[i].gsmopen_serial_sync_period); + + } + } + } + + switch_mutex_unlock(globals.mutex); + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + +//static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type, const char *hint) +static switch_status_t chat_send(switch_event_t *message_event) +{ + char *user, *host, *f_user = NULL, *f_host = NULL, *f_resource = NULL; + private_t *tech_pvt = NULL; + int i = 0, found = 0; + + const char *proto; + const char *from; + const char *to; + const char *subject; + const char *body; + //const char *type; + const char *hint; + + proto = switch_event_get_header(message_event, "proto"); + from = switch_event_get_header(message_event, "from"); + to = switch_event_get_header(message_event, "to"); + subject = switch_event_get_header(message_event, "subject"); + body = switch_event_get_body(message_event); + //type = switch_event_get_header(message_event, "type"); + hint = switch_event_get_header(message_event, "hint"); + + switch_assert(proto != NULL); + + DEBUGA_GSMOPEN("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, hint=%s)\n", GSMOPEN_P_LOG, proto, from, to, subject, body, + hint ? hint : "NULL"); + + if (!to || !strlen(to)) { + ERRORA("Missing To: header.\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; + } + + if ((!from && !hint) || (!strlen(from) && !strlen(hint))) { + ERRORA("Missing From: AND Hint: headers.\n", GSMOPEN_P_LOG); + return SWITCH_STATUS_SUCCESS; + } + + if (from && (f_user = strdup(from))) { + if ((f_host = strchr(f_user, '@'))) { + *f_host++ = '\0'; + if ((f_resource = strchr(f_host, '/'))) { + *f_resource++ = '\0'; + } + } + } + + if (!strlen(hint)) { //FIXME FIXME FIXME + hint = from; + } + if (to && (user = strdup(to))) { + if ((host = strchr(user, '@'))) { + *host++ = '\0'; + } + + DEBUGA_GSMOPEN("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, hint=%s)\n", GSMOPEN_P_LOG, proto, from, to, subject, body, + hint ? hint : "NULL"); + if (hint && strlen(hint)) { + //in hint we receive the interface name to use + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, hint, strlen(hint)) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + DEBUGA_GSMOPEN("Using interface: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", GSMOPEN_P_LOG, i, + globals.GSMOPEN_INTERFACES[i].name); + found = 1; + break; + } + } + } /* FIXME add a tech_pvt member for the SIM telephone number //else { + //we have no a predefined interface name to use (hint is NULL), so let's choose an interface from the username (from) + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].skype_user, from, strlen(from)) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + DEBUGA_GSMOPEN("Using interface: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", GSMOPEN_P_LOG, i, globals.GSMOPEN_INTERFACES[i].name); + found = 1; + break; + } + } + } + */ + if (!found) { + ERRORA("ERROR: A GSMopen interface with name='%s' or one with SIM_number='%s' was not found\n", GSMOPEN_P_LOG, hint ? hint : "NULL", + from ? from : "NULL"); + goto end; + } else { + gsmopen_sendsms(tech_pvt, (char *) to, (char *) body); + } + } + end: + switch_safe_free(user); + switch_safe_free(f_user); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t compat_chat_send(const char *proto, const char *from, const char *to, + const char *subject, const char *body, const char *type, const char *hint) +{ + switch_event_t *message_event; + switch_status_t status; + + if (switch_event_create(&message_event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "proto", proto); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "from", from); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "to", to); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "subject", subject); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "type", type); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "hint", hint); + + if (body) { + switch_event_add_body(message_event, "%s", body); + } + } else { + abort(); + } + + status = chat_send(message_event); + switch_event_destroy(&message_event); + + return status; + +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_gsmopen_load) +{ + switch_api_interface_t *commands_api_interface; + switch_chat_interface_t *chat_interface; + + gsmopen_module_pool = pool; + memset(&globals, '\0', sizeof(globals)); + + running = 1; + + if (load_config(FULL_RELOAD) != SWITCH_STATUS_SUCCESS) { + running = 0; + return SWITCH_STATUS_FALSE; + } + + if (switch_event_reserve_subclass(MY_EVENT_INCOMING_SMS) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n"); + return SWITCH_STATUS_GENERR; + } + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + gsmopen_endpoint_interface = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + gsmopen_endpoint_interface->interface_name = "gsmopen"; + gsmopen_endpoint_interface->io_routines = &gsmopen_io_routines; + gsmopen_endpoint_interface->state_handler = &gsmopen_state_handlers; + + if (running) { + +#if 1 + SWITCH_ADD_API(commands_api_interface, "gsm", "gsm console AT_command", gsm_function, GSM_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "gsmopen", "gsmopen interface AT_command", gsmopen_function, GSMOPEN_SYNTAX); +#endif //0 + SWITCH_ADD_API(commands_api_interface, "gsmopen_boost_audio", "gsmopen_boost_audio interface AT_command", gsmopen_boost_audio_function, GSMOPEN_BOOST_AUDIO_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "gsmopen_dump", "gsmopen_dump interface", gsmopen_dump_function, GSMOPEN_DUMP_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "gsmopen_sendsms", "gsmopen_sendsms interface destination_number SMS_text", sendsms_function, + SENDSMS_SYNTAX); + SWITCH_ADD_CHAT(chat_interface, GSMOPEN_CHAT_PROTO, chat_send); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; + } else + return SWITCH_STATUS_FALSE; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_gsmopen_shutdown) +{ + int x; + private_t *tech_pvt = NULL; + switch_status_t status; + unsigned int howmany = 8; + int interface_id; + int fd; + + running = 0; + + for (interface_id = 0; interface_id < GSMOPEN_MAX_INTERFACES; interface_id++) { + tech_pvt = &globals.GSMOPEN_INTERFACES[interface_id]; + + if (strlen(globals.GSMOPEN_INTERFACES[interface_id].name)) { + WARNINGA("SHUTDOWN interface_id=%d\n", GSMOPEN_P_LOG, interface_id); + globals.GSMOPEN_INTERFACES[interface_id].running = 0; + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_signaling_thread) { +#ifdef WIN32 + switch_file_write(tech_pvt->GSMopenHandles.fdesc[1], "sciutati", &howmany); // let's the controldev_thread die +#else /* WIN32 */ + howmany = write(tech_pvt->GSMopenHandles.fdesc[1], "sciutati", howmany); +#endif /* WIN32 */ + } + x = 10; + while (x) { //FIXME 0.5 seconds? + x--; + switch_yield(50000); + } + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_signaling_thread) { + switch_thread_join(&status, globals.GSMOPEN_INTERFACES[interface_id].gsmopen_signaling_thread); + } + if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread) { + switch_thread_join(&status, globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread); + } + + x = 10; + while (x) { //FIXME 0.5 seconds? + x--; + switch_yield(50000); + } + fd = tech_pvt->controldevfd; + //DEBUGA_GSMOPEN("SHUTDOWN tech_pvt->controldevfd=%d\n", GSMOPEN_P_LOG, tech_pvt->controldevfd); + if (fd) { + //close(fd); + //tech_pvt->controldevfd = -1; + DEBUGA_GSMOPEN("SHUTDOWN tech_pvt->controldevfd=%d\n", GSMOPEN_P_LOG, tech_pvt->controldevfd); + } +#ifndef WIN32 + shutdown(tech_pvt->audiogsmopenpipe[0], 2); + close(tech_pvt->audiogsmopenpipe[0]); + shutdown(tech_pvt->audiogsmopenpipe[1], 2); + close(tech_pvt->audiogsmopenpipe[1]); + shutdown(tech_pvt->audiopipe[0], 2); + close(tech_pvt->audiopipe[0]); + shutdown(tech_pvt->audiopipe[1], 2); + close(tech_pvt->audiopipe[1]); + shutdown(tech_pvt->GSMopenHandles.fdesc[0], 2); + close(tech_pvt->GSMopenHandles.fdesc[0]); + shutdown(tech_pvt->GSMopenHandles.fdesc[1], 2); + close(tech_pvt->GSMopenHandles.fdesc[1]); +#endif /* WIN32 */ + } + + } + + switch_event_free_subclass(MY_EVENT_INCOMING_SMS); + + switch_safe_free(globals.dialplan); + switch_safe_free(globals.context); + switch_safe_free(globals.destination); + switch_safe_free(globals.codec_string); + switch_safe_free(globals.codec_rates_string); + + return SWITCH_STATUS_SUCCESS; +} + + +void *SWITCH_THREAD_FUNC gsmopen_do_gsmopenapi_thread(switch_thread_t * thread, void *obj) +{ + return gsmopen_do_gsmopenapi_thread_func(obj); +} + +int dtmf_received(private_t * tech_pvt, char *value) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + channel = switch_core_session_get_channel(session); + + if (channel) { + + if (!switch_channel_test_flag(channel, CF_BRIDGED)) { + + switch_dtmf_t dtmf = { (char) value[0], switch_core_default_dtmf_duration(0) }; + DEBUGA_GSMOPEN("received DTMF %c on channel %s\n", GSMOPEN_P_LOG, dtmf.digit, switch_channel_get_name(channel)); + switch_mutex_lock(tech_pvt->flag_mutex); + //FIXME: why sometimes DTMFs from here do not seems to be get by FS? + switch_channel_queue_dtmf(channel, &dtmf); + switch_set_flag(tech_pvt, TFLAG_DTMF); + switch_mutex_unlock(tech_pvt->flag_mutex); + } else { + DEBUGA_GSMOPEN + ("received a DTMF on channel %s, but we're BRIDGED, so let's NOT relay it out of band\n", GSMOPEN_P_LOG, switch_channel_get_name(channel)); + } + } else { + WARNINGA("received %c DTMF, but no channel?\n", GSMOPEN_P_LOG, value[0]); + } + switch_core_session_rwunlock(session); + + return 0; +} + +int new_inbound_channel(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + switch_assert(tech_pvt != NULL); + tech_pvt->ib_calls++; + if ((session = switch_core_session_request(gsmopen_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) != 0) { + DEBUGA_GSMOPEN("2 SESSION_REQUEST %s\n", GSMOPEN_P_LOG, switch_core_session_get_uuid(session)); + switch_core_session_add_stream(session, NULL); + channel = switch_core_session_get_channel(session); + if (!channel) { + ERRORA("Doh! no channel?\n", GSMOPEN_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + if (gsmopen_tech_init(tech_pvt, session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Doh! no tech_init?\n", GSMOPEN_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + + if ((tech_pvt->caller_profile = + switch_caller_profile_new(switch_core_session_get_pool(session), "gsmopen", + tech_pvt->dialplan, tech_pvt->callid_name, + tech_pvt->callid_number, NULL, NULL, NULL, NULL, "mod_gsmopen", tech_pvt->context, tech_pvt->destination)) != 0) { + char name[128]; + //switch_snprintf(name, sizeof(name), "gsmopen/%s/%s", tech_pvt->name, tech_pvt->caller_profile->destination_number); + switch_snprintf(name, sizeof(name), "gsmopen/%s", tech_pvt->name); + switch_channel_set_name(channel, name); + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + } + switch_channel_set_state(channel, CS_INIT); + if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { + ERRORA("Error spawning thread\n", GSMOPEN_P_LOG); + switch_core_session_destroy(&session); + return 0; + } + } + if (channel) { + //switch_channel_mark_answered(channel); + } + + DEBUGA_GSMOPEN("new_inbound_channel\n", GSMOPEN_P_LOG); + + return 0; +} + +int remote_party_is_ringing(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n", GSMOPEN_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No session???\n", GSMOPEN_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_ring_ready(channel); + DEBUGA_GSMOPEN("gsmopen_call: REMOTE PARTY RINGING\n", GSMOPEN_P_LOG); + } else { + ERRORA("No channel???\n", GSMOPEN_P_LOG); + } + + switch_core_session_rwunlock(session); + + done: + return 0; +} + +int remote_party_is_early_media(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n\n\n", GSMOPEN_P_LOG); + //TODO: kill the bastard + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + switch_core_session_add_stream(session, NULL); + } else { + ERRORA("No session???\n", GSMOPEN_P_LOG); + //TODO: kill the bastard + goto done; + } + if (channel) { + switch_channel_mark_pre_answered(channel); + DEBUGA_GSMOPEN("gsmopen_call: REMOTE PARTY EARLY MEDIA\n", GSMOPEN_P_LOG); + } else { + ERRORA("No channel???\n", GSMOPEN_P_LOG); + //TODO: kill the bastard + } + + switch_core_session_rwunlock(session); + + done: + return 0; +} + +int outbound_channel_answered(private_t * tech_pvt) +{ + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } else { + ERRORA("No session???\n", GSMOPEN_P_LOG); + goto done; + } + if (session) { + channel = switch_core_session_get_channel(session); + } else { + ERRORA("No channel???\n", GSMOPEN_P_LOG); + goto done; + } + if (channel) { + switch_channel_mark_answered(channel); + tech_pvt->phone_callflow = GSMOPEN_STATE_UP; + tech_pvt->interface_state = GSMOPEN_STATE_UP; + //DEBUGA_GSMOPEN("gsmopen_call: %s, answered\n", GSMOPEN_P_LOG, id); + } else { + ERRORA("No channel???\n", GSMOPEN_P_LOG); + } + + switch_core_session_rwunlock(session); + + done: + DEBUGA_GSMOPEN("outbound_channel_answered!\n", GSMOPEN_P_LOG); + + return 0; +} + +private_t *find_available_gsmopen_interface_rr(private_t * tech_pvt_calling) +{ + private_t *tech_pvt = NULL; + int i; + //int num_interfaces = GSMOPEN_MAX_INTERFACES; + //int num_interfaces = globals.real_interfaces; + + switch_mutex_lock(globals.mutex); + + /* Fact is the real interface start from 1 */ + //XXX no, is just a convention, but you can have it start from 0. I do not, for aestetic reasons :-) + //if (globals.next_interface == 0) globals.next_interface = 1; + + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + int interface_id; + + interface_id = globals.next_interface; + //interface_id = interface_id < GSMOPEN_MAX_INTERFACES ? interface_id : interface_id - GSMOPEN_MAX_INTERFACES + 1; + globals.next_interface = interface_id + 1 < GSMOPEN_MAX_INTERFACES ? interface_id + 1 : 0; + + if (strlen(globals.GSMOPEN_INTERFACES[interface_id].name)) { + int gsmopen_state = 0; + + tech_pvt = &globals.GSMOPEN_INTERFACES[interface_id]; + gsmopen_state = tech_pvt->interface_state; + DEBUGA_GSMOPEN("gsmopen interface: %d, name: %s, state: %d\n", GSMOPEN_P_LOG, interface_id, globals.GSMOPEN_INTERFACES[interface_id].name, + gsmopen_state); + if ((tech_pvt_calling ? strcmp(tech_pvt->gsmopen_user, tech_pvt_calling->gsmopen_user) : 1) + && (GSMOPEN_STATE_DOWN == gsmopen_state || 0 == gsmopen_state) && (tech_pvt->phone_callflow == CALLFLOW_STATUS_FINISHED + || 0 == tech_pvt->phone_callflow)) { + DEBUGA_GSMOPEN("returning as available gsmopen interface name: %s, state: %d callflow: %d\n", GSMOPEN_P_LOG, tech_pvt->name, gsmopen_state, + tech_pvt->phone_callflow); + /*set to Dialing state to avoid other thread fint it, don't know if it is safe */ + //XXX no, it's not safe + if (tech_pvt_calling == NULL) { + tech_pvt->interface_state = GSMOPEN_STATE_SELECTED; + } + + switch_mutex_unlock(globals.mutex); + return tech_pvt; + } + } // else { + //DEBUGA_GSMOPEN("GSM interface: %d blank!! A hole here means we cannot hunt the last interface.\n", GSMOPEN_P_LOG, interface_id); + //} + } + + switch_mutex_unlock(globals.mutex); + return NULL; +} + +#if 1 +SWITCH_STANDARD_API(gsm_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + + if (globals.gsm_console) + stream->write_function(stream, "gsm console is: |||%s|||\n", globals.gsm_console->name); + else + stream->write_function(stream, "gsm console is NOT yet assigned\n"); + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc || !argv[0]) { + stream->write_function(stream, "%s", GSM_SYNTAX); + goto end; + } + + if (!strcasecmp(argv[0], "list")) { + int i; + char next_flag_char = ' '; + + stream->write_function(stream, "F ID\t Name \tIB (F/T) OB (F/T)\tState\tCallFlw\t\tUUID\n"); + stream->write_function(stream, "= ====\t ======== \t======= =======\t======\t============\t======\n"); + + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + next_flag_char = i == globals.next_interface ? '*' : ' '; + + if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { + stream->write_function(stream, + "%c %d\t[%s]\t%3ld/%ld\t%6ld/%ld\t%s\t%s\t%s\n", + next_flag_char, + i, globals.GSMOPEN_INTERFACES[i].name, + globals.GSMOPEN_INTERFACES[i].ib_failed_calls, + globals.GSMOPEN_INTERFACES[i].ib_calls, + globals.GSMOPEN_INTERFACES[i].ob_failed_calls, + globals.GSMOPEN_INTERFACES[i].ob_calls, + interface_status[globals.GSMOPEN_INTERFACES[i].interface_state], + phone_callflow[globals.GSMOPEN_INTERFACES[i].phone_callflow], globals.GSMOPEN_INTERFACES[i].session_uuid_str); + } else if (argc > 1 && !strcasecmp(argv[1], "full")) { + stream->write_function(stream, "%c\t%d\n", next_flag_char, i); + } + + } + stream->write_function(stream, "\nTotal: %d\n", globals.real_interfaces - 1); + + } else if (!strcasecmp(argv[0], "console")) { + int i; + int found = 0; + + if (argc == 2) { + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, argv[1], strlen(argv[1])) == 0)) { + globals.gsm_console = &globals.GSMOPEN_INTERFACES[i]; + stream->write_function(stream, "gsm console is now: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", i, + globals.GSMOPEN_INTERFACES[i].name); + stream->write_function(stream, "gsm console is: |||%s|||\n", globals.gsm_console->name); + found = 1; + break; + } + + } + if (!found) + stream->write_function(stream, "ERROR: A GSMopen interface with name='%s' was not found\n", argv[1]); + } else { + + stream->write_function(stream, "-ERR Usage: gsm console interface_name\n"); + goto end; + } + + } else if (!strcasecmp(argv[0], "ciapalino")) { + +/* BEGIN: Changes heres */ + } else if (!strcasecmp(argv[0], "reload")) { + if (load_config(SOFT_RELOAD) != SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "gsm reload failed\n"); + } else { + stream->write_function(stream, "gsm reload success\n"); + } + } else if (!strcasecmp(argv[0], "remove")) { + if (argc == 2) { + if (remove_interface(argv[1]) == SWITCH_STATUS_SUCCESS) { + if (interface_exists(argv[1]) == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "gsm remove '%s' failed\n", argv[1]); + } else { + stream->write_function(stream, "gsm remove '%s' success\n", argv[1]); + } + } + } else { + stream->write_function(stream, "-ERR Usage: gsm remove interface_name\n"); + goto end; + } +/* END: Changes heres */ + + } else { + if (globals.gsm_console) + gsmopen_serial_write_AT_noack(globals.gsm_console, (char *) cmd); + else + stream->write_function(stream, "gsm console is NOT yet assigned\n"); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_STANDARD_API(gsmopen_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_SYNTAX); + goto end; + } + + if (argc < 2) { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_SYNTAX); + goto end; + } + + if (argv[0]) { + int i; + int found = 0; + + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.GSMOPEN_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A GSMopen interface with name='%s' was not found\n", argv[0]); + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; + } else { + gsmopen_serial_write_AT_noack(tech_pvt, (char *) &cmd[strlen(argv[0]) + 1]); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_SYNTAX); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} +#endif //0 +SWITCH_STANDARD_API(gsmopen_dump_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + char value[512]; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_DUMP_SYNTAX); + goto end; + } + if (argc == 1) { + int i; + int found = 0; + + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + //stream->write_function(stream, "Using interface: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.GSMOPEN_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found && (strcmp("list", argv[0]) == 0)) { + int i; + stream->write_function(stream, "gsmopen_dump LIST\n\n"); + for (i = 0; i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { + stream->write_function(stream, "dumping interface '%s'\n\n", globals.GSMOPEN_INTERFACES[i].name); + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + + + stream->write_function(stream, "interface_name = %s\n", tech_pvt->name); + stream->write_function(stream, "interface_id = %s\n", tech_pvt->id); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->active); + stream->write_function(stream, "active = %s\n", value); + if(!tech_pvt->network_creg_not_supported){ + snprintf(value, sizeof(value)-1, "%d", tech_pvt->not_registered); + stream->write_function(stream, "not_registered = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->home_network_registered); + stream->write_function(stream, "home_network_registered = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->roaming_registered); + stream->write_function(stream, "roaming_registered = %s\n", value); + }else{ + stream->write_function(stream, "not_registered = %s\n", "N/A"); + stream->write_function(stream, "home_network_registered = %s\n", "N/A"); + stream->write_function(stream, "roaming_registered = %s\n", "N/A"); + } + snprintf(value, sizeof(value)-1, "%d", tech_pvt->got_signal); + stream->write_function(stream, "got_signal = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->running); + stream->write_function(stream, "running = %s\n", value); + stream->write_function(stream, "imei = %s\n", tech_pvt->imei); + stream->write_function(stream, "imsi = %s\n", tech_pvt->imsi); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->controldev_dead); + stream->write_function(stream, "controldev_dead = %s\n", value); + stream->write_function(stream, "controldevice_name = %s\n", tech_pvt->controldevice_name); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->no_sound); + stream->write_function(stream, "no_sound = %s\n", value); +#ifdef GSMOPEN_ALSA + stream->write_function(stream, "alsacname = %s\n", tech_pvt->alsacname); + stream->write_function(stream, "alsapname = %s\n", tech_pvt->alsapname); +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + snprintf(value, sizeof(value)-1, "%d", tech_pvt->portaudiocindex); + stream->write_function(stream, "portaudiocindex = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->portaudiopindex); + stream->write_function(stream, "portaudiopindex = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->speexecho); + stream->write_function(stream, "speexecho = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->speexpreprocess); + stream->write_function(stream, "speexpreprocess = %s\n", value); +#endif// GSMOPEN_PORTAUDIO + snprintf(value, sizeof(value)-1, "%f", tech_pvt->playback_boost); + stream->write_function(stream, "playback_boost = %s\n", value); + snprintf(value, sizeof(value)-1, "%f", tech_pvt->capture_boost); + stream->write_function(stream, "capture_boost = %s\n", value); + stream->write_function(stream, "dialplan = %s\n", tech_pvt->dialplan); + stream->write_function(stream, "context = %s\n", tech_pvt->context); + stream->write_function(stream, "destination = %s\n", tech_pvt->destination); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ib_calls); + stream->write_function(stream, "ib_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ob_calls); + stream->write_function(stream, "ob_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ib_failed_calls); + stream->write_function(stream, "ib_failed_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ob_failed_calls); + stream->write_function(stream, "ob_failed_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->interface_state); + stream->write_function(stream, "interface_state = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->phone_callflow); + stream->write_function(stream, "phone_callflow = %s\n", value); + stream->write_function(stream, "session_uuid_str = %s\n", tech_pvt->session_uuid_str); + stream->write_function(stream, "\n"); + + dump_event(tech_pvt); + } + + } + + } else if(found){ + stream->write_function(stream, "dumping interface '%s'\n\n", argv[0]); + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + + + stream->write_function(stream, "interface_name = %s\n", tech_pvt->name); + stream->write_function(stream, "interface_id = %s\n", tech_pvt->id); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->active); + stream->write_function(stream, "active = %s\n", value); + if(!tech_pvt->network_creg_not_supported){ + snprintf(value, sizeof(value)-1, "%d", tech_pvt->not_registered); + stream->write_function(stream, "not_registered = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->home_network_registered); + stream->write_function(stream, "home_network_registered = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->roaming_registered); + stream->write_function(stream, "roaming_registered = %s\n", value); + }else{ + stream->write_function(stream, "not_registered = %s\n", "N/A"); + stream->write_function(stream, "home_network_registered = %s\n", "N/A"); + stream->write_function(stream, "roaming_registered = %s\n", "N/A"); + } + snprintf(value, sizeof(value)-1, "%d", tech_pvt->got_signal); + stream->write_function(stream, "got_signal = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->running); + stream->write_function(stream, "running = %s\n", value); + stream->write_function(stream, "imei = %s\n", tech_pvt->imei); + stream->write_function(stream, "imsi = %s\n", tech_pvt->imsi); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->controldev_dead); + stream->write_function(stream, "controldev_dead = %s\n", value); + stream->write_function(stream, "controldevice_name = %s\n", tech_pvt->controldevice_name); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->no_sound); + stream->write_function(stream, "no_sound = %s\n", value); +#ifdef GSMOPEN_ALSA + stream->write_function(stream, "alsacname = %s\n", tech_pvt->alsacname); + stream->write_function(stream, "alsapname = %s\n", tech_pvt->alsapname); +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + snprintf(value, sizeof(value)-1, "%d", tech_pvt->portaudiocindex); + stream->write_function(stream, "portaudiocindex = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->portaudiopindex); + stream->write_function(stream, "portaudiopindex = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->speexecho); + stream->write_function(stream, "speexecho = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->speexpreprocess); + stream->write_function(stream, "speexpreprocess = %s\n", value); +#endif// GSMOPEN_PORTAUDIO + snprintf(value, sizeof(value)-1, "%f", tech_pvt->playback_boost); + stream->write_function(stream, "playback_boost = %s\n", value); + snprintf(value, sizeof(value)-1, "%f", tech_pvt->capture_boost); + stream->write_function(stream, "capture_boost = %s\n", value); + stream->write_function(stream, "dialplan = %s\n", tech_pvt->dialplan); + stream->write_function(stream, "context = %s\n", tech_pvt->context); + stream->write_function(stream, "destination = %s\n", tech_pvt->destination); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ib_calls); + stream->write_function(stream, "ib_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ob_calls); + stream->write_function(stream, "ob_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ib_failed_calls); + stream->write_function(stream, "ib_failed_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ob_failed_calls); + stream->write_function(stream, "ob_failed_calls = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->interface_state); + stream->write_function(stream, "interface_state = %s\n", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->phone_callflow); + stream->write_function(stream, "phone_callflow = %s\n", value); + stream->write_function(stream, "session_uuid_str = %s\n", tech_pvt->session_uuid_str); + stream->write_function(stream, "\n"); + + dump_event(tech_pvt); + } else{ + stream->write_function(stream, "interface '%s' was not found\n", argv[0]); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_DUMP_SYNTAX); + } +end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} +SWITCH_STANDARD_API(gsmopen_boost_audio_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc == 1 || argc==3) { + int i; + int found = 0; + + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.GSMOPEN_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A GSMopen interface with name='%s' was not found\n", argv[0]); + + } else { + if (argc == 1) { + stream->write_function(stream,"[%s] capture boost is %f\n", globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].capture_boost); + stream->write_function(stream,"[%s] playback boost is %f\n", globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].playback_boost); + stream->write_function(stream, "%s usage: %s", argv[0], GSMOPEN_BOOST_AUDIO_SYNTAX); + goto end; + } else if ((strncmp("play", argv[1], strlen(argv[1])) == 0)) { + if (switch_is_number(argv[2])) { + stream->write_function(stream,"[%s] playback boost was %f\n", globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].playback_boost); + gsmopen_store_boost(argv[2], &globals.GSMOPEN_INTERFACES[i].playback_boost); //FIXME + stream->write_function(stream,"[%s] playback boost is now %f\n", globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].playback_boost); + } + }else if ((strncmp("capt", argv[1], strlen(argv[1])) == 0)) { + if (switch_is_number(argv[2])) { + stream->write_function(stream,"[%s] capture boost was %f\n", globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].capture_boost); + gsmopen_store_boost(argv[2], &globals.GSMOPEN_INTERFACES[i].capture_boost); //FIXME + stream->write_function(stream,"[%s] capture boost is now %f\n", globals.GSMOPEN_INTERFACES[i].name, globals.GSMOPEN_INTERFACES[i].capture_boost); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_BOOST_AUDIO_SYNTAX); + } + } + } else { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_BOOST_AUDIO_SYNTAX); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + + +#if 0 +int gsmopen_transfer(private_t * tech_pvt, char *id, char *value) +{ + char msg_to_gsmopen[1024]; + int i; + int found = 0; + private_t *giovatech; + struct timeval timenow; + + switch_mutex_lock(globals.mutex); + + gettimeofday(&timenow, NULL); + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { + + giovatech = &globals.GSMOPEN_INTERFACES[i]; + //NOTICA("gsmopen interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->gsmopen_user=%s\n", GSMOPEN_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->gsmopen_user); + //FIXME check a timestamp here + if (strlen(giovatech->gsmopen_call_id) && (giovatech->interface_state != GSMOPEN_STATE_DOWN) && (!strcmp(giovatech->gsmopen_user, tech_pvt->gsmopen_user)) && (!strcmp(giovatech->callid_number, value)) && ((((timenow.tv_sec - giovatech->answer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->answer_time.tv_usec)) < 500000)) { //0.5sec + found = 1; + DEBUGA_GSMOPEN + ("FOUND (name=%s, giovatech->interface_state=%d != GSMOPEN_STATE_DOWN) && (giovatech->gsmopen_user=%s == tech_pvt->gsmopen_user=%s) && (giovatech->callid_number=%s == value=%s)\n", + GSMOPEN_P_LOG, giovatech->name, giovatech->interface_state, giovatech->gsmopen_user, tech_pvt->gsmopen_user, giovatech->callid_number, + value) + break; + } + } + } + + if (found) { + //tech_pvt->callid_number[0]='\0'; + //sprintf(msg_to_gsmopen, "ALTER CALL %s END HANGUP", id); + //gsmopen_signaling_write(tech_pvt, msg_to_gsmopen); + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_GSMOPEN("NOT FOUND\n", GSMOPEN_P_LOG); + + if (!tech_pvt || !tech_pvt->gsmopen_call_id || !strlen(tech_pvt->gsmopen_call_id)) { + /* we are not inside an active call */ + DEBUGA_GSMOPEN("We're NO MORE in a call now %s\n", GSMOPEN_P_LOG, (tech_pvt && tech_pvt->gsmopen_call_id) ? tech_pvt->gsmopen_call_id : ""); + switch_mutex_unlock(globals.mutex); + + } else { + + /* we're owned, we're in a call, let's try to transfer */ + /************************** TODO + Checking here if it is possible to transfer this call to Test2 + -> GET CALL 288 CAN_TRANSFER Test2 + <- CALL 288 CAN_TRANSFER test2 TRUE + **********************************/ + + private_t *available_gsmopen_interface = NULL; + + gettimeofday(&timenow, NULL); + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + if (strlen(globals.GSMOPEN_INTERFACES[i].name)) { + + giovatech = &globals.GSMOPEN_INTERFACES[i]; + //NOTICA("gsmopen interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->gsmopen_user=%s\n", GSMOPEN_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->gsmopen_user); + //FIXME check a timestamp here + if (strlen(giovatech->gsmopen_transfer_call_id) && (giovatech->interface_state != GSMOPEN_STATE_DOWN) && (!strcmp(giovatech->gsmopen_user, tech_pvt->gsmopen_user)) && (!strcmp(giovatech->transfer_callid_number, value)) && ((((timenow.tv_sec - giovatech->transfer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->transfer_time.tv_usec)) < 1000000)) { //1.0 sec + found = 1; + DEBUGA_GSMOPEN + ("FOUND (name=%s, giovatech->interface_state=%d != GSMOPEN_STATE_DOWN) && (giovatech->gsmopen_user=%s == tech_pvt->gsmopen_user=%s) && (giovatech->transfer_callid_number=%s == value=%s)\n", + GSMOPEN_P_LOG, giovatech->name, giovatech->interface_state, + giovatech->gsmopen_user, tech_pvt->gsmopen_user, giovatech->transfer_callid_number, value) + break; + } + } + } + + if (found) { + //tech_pvt->callid_number[0]='\0'; + //sprintf(msg_to_gsmopen, "ALTER CALL %s END HANGUP", id); + //gsmopen_signaling_write(tech_pvt, msg_to_gsmopen); + switch_mutex_unlock(globals.mutex); + return 0; + } + DEBUGA_GSMOPEN("NOT FOUND\n", GSMOPEN_P_LOG); + + available_gsmopen_interface = find_available_gsmopen_interface_rr(tech_pvt); + if (available_gsmopen_interface) { + /* there is a gsmopen interface idle, let's transfer the call to it */ + + //FIXME write a timestamp here + gettimeofday(&tech_pvt->transfer_time, NULL); + switch_copy_string(tech_pvt->gsmopen_transfer_call_id, id, sizeof(tech_pvt->gsmopen_transfer_call_id) - 1); + + switch_copy_string(tech_pvt->transfer_callid_number, value, sizeof(tech_pvt->transfer_callid_number) - 1); + + DEBUGA_GSMOPEN + ("Let's transfer the gsmopen_call %s to %s interface (with gsmopen_user: %s), because we are already in a gsmopen call(%s)\n", + GSMOPEN_P_LOG, tech_pvt->gsmopen_call_id, available_gsmopen_interface->name, available_gsmopen_interface->gsmopen_user, id); + + //FIXME why this? the inbound call will come, eventually, on that other interface + //available_gsmopen_interface->ib_calls++; + + sprintf(msg_to_gsmopen, "ALTER CALL %s TRANSFER %s", id, available_gsmopen_interface->gsmopen_user); + //gsmopen_signaling_write(tech_pvt, msg_to_gsmopen); + if (tech_pvt->interface_state == GSMOPEN_STATE_SELECTED) { + tech_pvt->interface_state = GSMOPEN_STATE_IDLE; //we marked it GSMOPEN_STATE_SELECTED just in case it has to make an outbound call + } + } else { + /* no gsmopen interfaces idle, do nothing */ + DEBUGA_GSMOPEN + ("Not answering the gsmopen_call %s, because we are already in a gsmopen call(%s) and not transferring, because no other gsmopen interfaces are available\n", + GSMOPEN_P_LOG, id, tech_pvt->gsmopen_call_id); + sprintf(msg_to_gsmopen, "ALTER CALL %s END HANGUP", id); + //gsmopen_signaling_write(tech_pvt, msg_to_gsmopen); + } + switch_sleep(10000); + DEBUGA_GSMOPEN + ("We have NOT answered a GSM RING from gsmopen_call %s, because we are already in a gsmopen call (%s)\n", + GSMOPEN_P_LOG, id, tech_pvt->gsmopen_call_id); + + switch_mutex_unlock(globals.mutex); + } + return 0; +} +#endif //0 + +void *gsmopen_do_gsmopenapi_thread_func(void *obj) +{ + + private_t *tech_pvt = (private_t *) obj; + time_t now_timestamp; + + //if (gsmopen_present(GSMopenHandles)) + while (running && tech_pvt->running) { + int res; + //gsmopen_sleep(1000000); //1 sec + //DEBUGA_GSMOPEN("ciao!\n", GSMOPEN_P_LOG); + res = gsmopen_serial_read(tech_pvt); + if (res == -1) { //manage the graceful interface shutdown + tech_pvt->controldev_dead = 1; + close(tech_pvt->controldevfd); + ERRORA("gsmopen_serial_monitor failed, declaring %s dead\n", GSMOPEN_P_LOG, tech_pvt->controldevice_name); + tech_pvt->running=0; + alarm_event(tech_pvt, ALARM_FAILED_INTERFACE, "gsmopen_serial_monitor failed, declaring interface dead"); + tech_pvt->active=0; + tech_pvt->name[0]='\0'; + switch_sleep(1000000); + } else if (tech_pvt->controldevprotocol != PROTOCOL_NO_SERIAL && tech_pvt->interface_state == GSMOPEN_STATE_RING + && tech_pvt->phone_callflow != CALLFLOW_CALL_HANGUP_REQUESTED) { + //WARNINGA("INCOMING RING\n", GSMOPEN_P_LOG); + + gsmopen_ring(tech_pvt); + + //FIXME gsmopen_answer(tech_pvt); + //new_inbound_channel(tech_pvt); + //FIXME if (!gsmopen_new(p, AST_STATE_RING, tech_pvt->context)) { + //FIXME ERRORA("gsmopen_new failed! BAD BAD BAD\n", GSMOPEN_P_LOG); + //FIXME } + + + } else if (tech_pvt->controldevprotocol != PROTOCOL_NO_SERIAL && tech_pvt->interface_state == GSMOPEN_STATE_DIALING) { + WARNINGA("WE'RE DIALING, let's take the earlymedia\n", GSMOPEN_P_LOG); + tech_pvt->interface_state = CALLFLOW_STATUS_EARLYMEDIA; + remote_party_is_early_media(tech_pvt); + //new_inbound_channel(tech_pvt); + //FIXME if (!gsmopen_new(p, AST_STATE_RING, tech_pvt->context)) { + //FIXME ERRORA("gsmopen_new failed! BAD BAD BAD\n", GSMOPEN_P_LOG); + //FIXME } + + + + + } else if (tech_pvt->interface_state == CALLFLOW_CALL_REMOTEANSWER) { + WARNINGA("REMOTE PARTY ANSWERED\n", GSMOPEN_P_LOG); + outbound_channel_answered(tech_pvt); + //new_inbound_channel(tech_pvt); + //FIXME if (!gsmopen_new(p, AST_STATE_RING, tech_pvt->context)) { + //FIXME ERRORA("gsmopen_new failed! BAD BAD BAD\n", GSMOPEN_P_LOG); + //FIXME } + } + switch_sleep(100); //give other threads a chance + time(&now_timestamp); + + if ((now_timestamp - tech_pvt->gsmopen_serial_synced_timestamp) > tech_pvt->gsmopen_serial_sync_period) { //TODO find a sensible period. 5min? in config? + gsmopen_serial_sync(tech_pvt); + gsmopen_serial_getstatus_AT(tech_pvt); + } + } + DEBUGA_GSMOPEN("EXIT\n", GSMOPEN_P_LOG); + //running = 0; + return NULL; + +} + + +SWITCH_STANDARD_API(sendsms_function) +{ + char *mycmd = NULL, *argv[3] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "ERROR, usage: %s", SENDSMS_SYNTAX); + goto end; + } + + if (argc < 3) { + stream->write_function(stream, "ERROR, usage: %s", SENDSMS_SYNTAX); + goto end; + } + + if (argv[0]) { + int i; + int found = 0; + + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + stream->write_function(stream, "Trying to send your SMS: interface=%s, dest=%s, text=%s\n", argv[0], argv[1], argv[2]); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A GSMopen interface with name='%s' was not found\n", argv[0]); + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; + } else { + //gsmopen_sendsms(tech_pvt, (char *) argv[1], (char *) argv[2]); + NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", GSMOPEN_P_LOG, GSMOPEN_CHAT_PROTO, tech_pvt->name, + argv[1], "SIMPLE MESSAGE", switch_str_nil(argv[2]), tech_pvt->name); + + compat_chat_send(GSMOPEN_CHAT_PROTO, tech_pvt->name, argv[1], "SIMPLE MESSAGE", switch_str_nil(argv[2]), NULL, tech_pvt->name); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", SENDSMS_SYNTAX); + } + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} + +int dump_event_full(private_t * tech_pvt, int is_alarm, int alarm_code, const char *alarm_message) +{ + switch_event_t *event; + char value[512]; + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + + session = switch_core_session_locate(tech_pvt->session_uuid_str); + if(session){ + channel = switch_core_session_get_channel(session); + } + + if (is_alarm){ + ERRORA("ALARM on interface %s: \n", GSMOPEN_P_LOG, tech_pvt->name ); + status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ALARM); + }else{ + DEBUGA_GSMOPEN("DUMP on interface %s: \n", GSMOPEN_P_LOG, tech_pvt->name ); + status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_DUMP); + } + if (status == SWITCH_STATUS_SUCCESS) { + if (is_alarm){ + snprintf(value, sizeof(value)-1, "%d", alarm_code); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm_code", value); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm_message", alarm_message); + } + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "interface_name", tech_pvt->name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "interface_id", tech_pvt->id); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->active); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "active", value); + if(!tech_pvt->network_creg_not_supported){ + snprintf(value, sizeof(value)-1, "%d", tech_pvt->not_registered); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "not_registered", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->home_network_registered); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "home_network_registered", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->roaming_registered); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "roaming_registered", value); + }else{ + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "not_registered", "N/A"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "home_network_registered", "N/A"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "roaming_registered", "N/A"); + } + snprintf(value, sizeof(value)-1, "%d", tech_pvt->got_signal); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "got_signal", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->running); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "running", value); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "imei", tech_pvt->imei); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "imsi", tech_pvt->imsi); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->controldev_dead); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "controldev_dead", value); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "controldevice_name", tech_pvt->controldevice_name); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->no_sound); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "no_sound", value); +#ifdef GSMOPEN_ALSA + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alsacname", tech_pvt->alsacname); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alsapname", tech_pvt->alsapname); +#endif// GSMOPEN_ALSA +#ifdef GSMOPEN_PORTAUDIO + snprintf(value, sizeof(value)-1, "%d", tech_pvt->portaudiocindex); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "portaudiocindex", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->portaudiopindex); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "portaudiopindex", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->speexecho); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "speexecho", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->speexpreprocess); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "speexpreprocess", value); +#endif// GSMOPEN_PORTAUDIO + snprintf(value, sizeof(value)-1, "%f", tech_pvt->playback_boost); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "playback_boost", value); + snprintf(value, sizeof(value)-1, "%f", tech_pvt->capture_boost); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "capture_boost", value); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "dialplan", tech_pvt->dialplan); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "context", tech_pvt->context); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "destination", tech_pvt->destination); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ib_calls); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "ib_calls", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ob_calls); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "ob_calls", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ib_failed_calls); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "ib_failed_calls", value); + snprintf(value, sizeof(value)-1, "%lu", tech_pvt->ob_failed_calls); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "ob_failed_calls", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->interface_state); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "interface_state", value); + snprintf(value, sizeof(value)-1, "%d", tech_pvt->phone_callflow); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "phone_callflow", value); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "session_uuid_str", tech_pvt->session_uuid_str); + if (strlen(tech_pvt->session_uuid_str)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "true"); + } else { //no session + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "false"); + } + if (channel) { + switch_channel_event_set_data(channel, event); + } + switch_event_fire(&event); + } else { + ERRORA("cannot create event on interface %s. WHY?????\n", GSMOPEN_P_LOG, tech_pvt->name); + } + + if (session) { + switch_core_session_rwunlock(session); + } + return 0; +} + +int dump_event(private_t * tech_pvt) +{ + return dump_event_full(tech_pvt, 0, 0, NULL); +} + + +int alarm_event(private_t * tech_pvt, int alarm_code, const char *alarm_message) +{ + return dump_event_full(tech_pvt, 1, alarm_code, alarm_message); +} + +int sms_incoming(private_t * tech_pvt) +{ + switch_event_t *event; + switch_core_session_t *session = NULL; + int event_sent_to_esl = 0; + + //DEBUGA_GSMOPEN("received SMS on interface %s: %s\n", GSMOPEN_P_LOG, tech_pvt->name, tech_pvt->sms_message); + DEBUGA_GSMOPEN("received SMS on interface %s: DATE=%s, SENDER=%s, BODY=%s|\n", GSMOPEN_P_LOG, tech_pvt->name, tech_pvt->sms_date, tech_pvt->sms_sender, + tech_pvt->sms_body); + + if (!zstr(tech_pvt->session_uuid_str)) { + session = switch_core_session_locate(tech_pvt->session_uuid_str); + } + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", GSMOPEN_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->sms_sender); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "date", tech_pvt->sms_date); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "datacodingscheme", tech_pvt->sms_datacodingscheme); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "servicecentreaddress", tech_pvt->sms_servicecentreaddress); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "messagetype", "%d", tech_pvt->sms_messagetype); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "chatname", tech_pvt->chatmessages[which].chatname); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "id", tech_pvt->chatmessages[which].id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); + switch_event_add_body(event, "%s\n", tech_pvt->sms_body); + if (session) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "true"); + if (switch_core_session_queue_event(session, &event) != SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } else { //no session + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "false"); + switch_event_fire(&event); + event_sent_to_esl = 1; + } + + } else { + ERRORA("cannot create event on interface %s. WHY?????\n", GSMOPEN_P_LOG, tech_pvt->name); + } + + if (!event_sent_to_esl) { + + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", GSMOPEN_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->sms_sender); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "date", tech_pvt->sms_date); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "datacodingscheme", tech_pvt->sms_datacodingscheme); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "servicecentreaddress", tech_pvt->sms_servicecentreaddress); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "messagetype", "%d", tech_pvt->sms_messagetype); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "chatname", tech_pvt->chatmessages[which].chatname); + //switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "id", tech_pvt->chatmessages[which].id); + switch_event_add_body(event, "%s\n", tech_pvt->sms_body); + if (session) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "true"); + } else { //no session + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "during-call", "false"); + } + switch_event_fire(&event); + } else { + ERRORA("cannot create event on interface %s. WHY?????\n", GSMOPEN_P_LOG, tech_pvt->name); + } + } + + if (session) { + switch_core_session_rwunlock(session); + } + //memset(&tech_pvt->chatmessages[which], '\0', sizeof(&tech_pvt->chatmessages[which]) ); + //memset(tech_pvt->sms_message, '\0', sizeof(tech_pvt->sms_message)); + return 0; +} + + +#ifdef NOTDEF +SWITCH_STANDARD_API(gsmopen_chat_function) +{ + char *mycmd = NULL, *argv[10] = { 0 }; + int argc = 0; + private_t *tech_pvt = NULL; + //int tried =0; + int i; + int found = 0; + //char skype_msg[1024]; + + if (!zstr(cmd) && (mycmd = strdup(cmd))) { + argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (!argc) { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_CHAT_SYNTAX); + goto end; + } + + if (argc < 3) { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_CHAT_SYNTAX); + goto end; + } + + if (argv[0]) { + for (i = 0; !found && i < GSMOPEN_MAX_INTERFACES; i++) { + /* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */ + if (strlen(globals.GSMOPEN_INTERFACES[i].name) + && (strncmp(globals.GSMOPEN_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) { + tech_pvt = &globals.GSMOPEN_INTERFACES[i]; + stream->write_function(stream, "Using interface: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.GSMOPEN_INTERFACES[i].name); + found = 1; + break; + } + + } + if (!found) { + stream->write_function(stream, "ERROR: A GSMopen interface with name='%s' was not found\n", argv[0]); + goto end; + } else { + + //chat_send(const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type, const char *hint); + //chat_send(p*roto, const char *from, const char *to, const char *subject, const char *body, const char *type, const char *hint); + //chat_send(GSMOPEN_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE", switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, hint); + + NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", GSMOPEN_P_LOG, GSMOPEN_CHAT_PROTO, tech_pvt->skype_user, + argv[1], "SIMPLE MESSAGE", switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), tech_pvt->name); + + chat_send(GSMOPEN_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE", + switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, tech_pvt->name); + + //NOTICA("TEXT is: %s\n", GSMOPEN_P_LOG, (char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1] ); + //snprintf(skype_msg, sizeof(skype_msg), "CHAT CREATE %s", argv[1]); + //gsmopen_signaling_write(tech_pvt, skype_msg); + //switch_sleep(100); + } + } else { + stream->write_function(stream, "ERROR, usage: %s", GSMOPEN_CHAT_SYNTAX); + goto end; + } + +#ifdef NOTDEF + + found = 0; + + while (!found) { + for (i = 0; i < MAX_CHATS; i++) { + if (!strcmp(tech_pvt->chats[i].dialog_partner, argv[1])) { + snprintf(skype_msg, sizeof(skype_msg), "CHATMESSAGE %s %s", tech_pvt->chats[i].chatname, + (char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]); + gsmopen_signaling_write(tech_pvt, skype_msg); + found = 1; + break; + } + } + if (found) { + break; + } + if (tried > 1000) { + stream->write_function(stream, "ERROR: no chat with dialog_partner='%s' was found\n", argv[1]); + break; + } + switch_sleep(1000); + } +#endif //NOTDEF + + end: + switch_safe_free(mycmd); + + return SWITCH_STATUS_SUCCESS; +} +#endif // NOTDEF + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */