Revision
15047
Author
gmaruzz
Date
2009-10-02 08:19:23 -0500 (Fri, 02 Oct 2009)

Log Message

gsmopen: renaming filenames to gsmopen

Added Paths

Removed Paths

Diff

Deleted: freeswitch/branches/gmaruzz/mod_gsmopen/celliax.conf.xml

Deleted: freeswitch/branches/gmaruzz/mod_gsmopen/celliax.h

Deleted: freeswitch/branches/gmaruzz/mod_gsmopen/celliax_protocol.c

Copied: freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen.conf.xml (from rev 15046, freeswitch/branches/gmaruzz/mod_gsmopen/celliax.conf.xml) (0 => 15047)


--- freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen.conf.xml	                        (rev 0)
+++ freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen.conf.xml	2009-10-02 13:19:23 UTC (rev 15047)
@@ -0,0 +1,20 @@
+<configuration name="gsmopen.conf" description="GSMopen Configuration">
+  <global_settings>
+    <param name="debug" value="8"/>
+    <param name="dialplan" value="XML"/>
+    <param name="context" value="default"/>
+    <param name="hold-music" value="$${moh_uri}"/>
+    <param name="destination" value="9999"/>
+  </global_settings>
+  <!-- one entry here per gsmopen interface -->
+  <per_interface_settings>
+    <interface id="1" name="interface1">
+	<param name="hold-music" value="$${moh_uri}"/>
+	<param name="dialplan" value="XML"/>
+	<param name="context" value="default"/>
+    	<param name="destination" value="5000"/>
+    </interface>
+    <!-- following interfaces are commented out!
+    -->
+  </per_interface_settings>
+</configuration>

Copied: freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen.h (from rev 15046, freeswitch/branches/gmaruzz/mod_gsmopen/celliax.h) (0 => 15047)


--- freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen.h	                        (rev 0)
+++ freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen.h	2009-10-02 13:19:23 UTC (rev 15047)
@@ -0,0 +1,565 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * 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 <anthmct@yahoo.com>
+ * 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 MY_EVENT_INCOMING_SMS "gsmopen::incoming_sms"
+
+#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
+
+#define GSMOPEN_ALSA
+#include <switch.h>
+#include <switch_version.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <iconv.h>
+#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 */
+
+
+#ifndef WIN32
+#include <sys/time.h>
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/Xatom.h>
+#endif //WIN32
+
+#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 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 interface_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 */
+	speed_t controldevice_speed;
+	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];
+	int sms_cnmi_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;
+
+};
+
+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);
+int gsmopen_transfer(private_t * tech_pvt, char *id, char *value);
+int gsmopen_socket_create_and_bind(private_t * tech_pvt, int *which_port);
+
+
+
+
+
+void *gsmopen_do_controldev_thread(void *data);
+int gsmopen_serial_init(private_t * tech_pvt, speed_t controldevice_speed);
+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, char *value);
+int gsmopen_ring(private_t * tech_pvt);

Copied: freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen_protocol.c (from rev 15046, freeswitch/branches/gmaruzz/mod_gsmopen/celliax_protocol.c) (0 => 15047)


--- freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen_protocol.c	                        (rev 0)
+++ freeswitch/branches/gmaruzz/mod_gsmopen/gsmopen_protocol.c	2009-10-02 13:19:23 UTC (rev 15047)
@@ -0,0 +1,3002 @@
+#include "gsmopen.h"
+
+#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;
+
+
+
+int gsmopen_serial_init(private_t * tech_pvt, speed_t controldevice_speed)
+{
+	int fd;
+	int rt;
+	struct termios tp;
+
+/* 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) {
+		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));
+	}
+	unsigned int status = 0;
+#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);
+	unsigned int flags = TIOCM_DTR;
+	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\n", GSMOPEN_P_LOG);
+	rt = gsmopen_serial_sync(tech_pvt);
+	if (rt == -1) {
+		ERRORA("Serial init error\n", GSMOPEN_P_LOG);
+		return -1;
+	}
+	return (fd);
+}
+
+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)
+{
+	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;
+
+/* initial_pause? */
+	if (tech_pvt->at_initial_pause) {
+		DEBUGA_GSMOPEN("sleeping for %d usec\n", GSMOPEN_P_LOG, tech_pvt->at_initial_pause);
+		usleep(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);
+		usleep(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);
+	}
+	usleep(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 */
+	char at_command[5];
+	int i;
+	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);
+	}
+
+	res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CGSN");
+	if (res) {
+		DEBUGA_GSMOPEN("AT+CGSN failed\n", GSMOPEN_P_LOG);
+	}
+
+/* this take a lot of time to complete on devices with slow serial link (eg.: 9600bps) */
+	/* 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;
+	}
+	/* 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);
+	}
+	res = gsmopen_serial_write_AT_ack(tech_pvt, "AT+CMGF=1");	//TODO: support phones that only accept pdu mode
+	if (res) {
+		ERRORA("Error setting SMS sending mode to TEXT on the cellphone\n", GSMOPEN_P_LOG);
+		return RESULT_FAILURE;
+	}
+	/* 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)
+{
+	usleep(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;
+
+	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) ) {
+		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);
+			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);
+			}
+			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;
+
+		char *token_ptr;
+
+		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)) {
+				/* 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));
+				int commacount = 0;
+				int a = 0;
+				int b = 0;
+				int c = 0;
+
+				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)) {
+				/* 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));
+
+				int commacount = 0;
+				int a = 0;
+				int b = 0;
+				int c = 0;
+
+				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) {
+					ast_setstate(tech_pvt->owner, GSMOPEN_STATE_BUSY);
+					gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_BUSY);
+				} 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, 0);
+					}
+					//
+					//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) {
+						WARNINGA
+							("|%s| CELLPHONE GETS ALMOST NO SIGNAL, consider to move it or additional antenna\n",
+							 GSMOPEN_P_LOG, tech_pvt->line_array.result[i]);
+					} else if (signal_quality < 15) {
+						WARNINGA("|%s| CELLPHONE GETS SIGNAL LOW\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]);
+
+					}
+
+				}
+
+			}
+			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);
+				}
+			}
+
+			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;
+					usleep(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);
+						}
+
+						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);
+							}
+						}
+#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++;
+			}
+
+			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)) {
+				if (option_debug > 1)
+					ERRORA("|%s| at_indicator_noservice_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]);
+			}
+
+			if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_nosignal_string) == 0)) {
+				if (option_debug > 1)
+					ERRORA("|%s| at_indicator_nosignal_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]);
+			}
+
+			if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_lowsignal_string) == 0)) {
+				if (option_debug > 1)
+					WARNINGA("|%s| at_indicator_lowsignal_string\n", GSMOPEN_P_LOG, tech_pvt->line_array.result[i]);
+			}
+
+			if ((strcmp(tech_pvt->line_array.result[i], tech_pvt->at_indicator_lowbattchg_string) == 0)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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)) {
+				if (option_debug > 1)
+					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 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;
+
+				if (strncmp(tech_pvt->line_array.result[i], "+CMGR", 5) == 0) {	/* we are reading the "header" of an SMS */
+					char content[512];
+					char content2[512];
+
+					memset(content, '\0', sizeof(content));
+
+					int inside_comma = 0;
+					int inside_quote = 0;
+					int which_field = 0;
+					int d = 0;
+
+					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;
+								//NOTICA("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;
+								//NOTICA("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;
+								//ERRORA("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){
+									err = ucs2_to_utf8(tech_pvt, content, content2, sizeof(content2));
+								} else {
+									err=0;
+									strncpy(content2, content, sizeof(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));
+
+								} else if(which_field==2){
+									strncpy(tech_pvt->sms_date, content2, sizeof(tech_pvt->sms_date));
+								} 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;
+								//WARNINGA("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++;
+
+						}
+
+					}
+				}				//it was the +CMGR answer from the cellphone
+				else {
+					DEBUGA_GSMOPEN("body=%s\n", GSMOPEN_P_LOG, sms_body);
+					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);
+				}				//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);
+		if (tech_pvt->owner)
+			gsmopen_queue_control(tech_pvt->owner, GSMOPEN_CONTROL_HANGUP);
+		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));
+			usleep(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));
+				usleep(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]);
+		usleep(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));
+		usleep(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));
+			usleep(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);
+	usleep(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));
+			usleep(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));
+				usleep(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]);
+		usleep(1000);			/* release the cpu */
+	}
+
+	usleep(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];
+	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] = 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;
+	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, converted=%s, utf8_out=%s\n",
+					   GSMOPEN_P_LOG, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, converted, utf8_out);
+		DEBUGA_GSMOPEN("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, converted=%s, utf8_out=%s\n",
+		 GSMOPEN_P_LOG, iconv_res, inbuf, (int) inbytesleft, outbuf, (int) outbytesleft, converted, utf8_out);
+	iconv_close(iconv_format);
+
+	return 0;
+}
+
+int utf_to_ucs2(private_t * tech_pvt, char *utf_in, size_t inbytesleft, char *ucs2_out, size_t outbytesleft)
+{
+	/* cicopet */
+	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);
+	}
+	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) {
+		usleep(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);
+
+	usleep(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) {
+				usleep(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;
+}
+
+
+
+#define GSMOPEN_ALSA
+#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;
+
+	period_size = tech_pvt->alsa_period_size;
+
+	snd_pcm_hw_params_alloca(&params);
+	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
+	unsigned int chan_num;
+	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 + (double) rate *start_delay / 1000000;
+	} else {
+		start_threshold = (double) 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 + (double) rate *stop_delay / 1000000;
+	else
+		stop_threshold = (double) 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);
+	}
+	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;
+}
+
+/*! \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;
+	snd_pcm_sframes_t delayp2;
+
+	//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);
+		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) {
+				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
+
+
+
+
+	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);
+		}
+		usleep(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);
+		usleep(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 {
+		r = snd_pcm_readi(tech_pvt->alsac, buf2 + (readpos * 2), left);
+
+		int a = 0;
+		int i = 0;
+		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) {
+		DEBUGA_GSMOPEN("ALSA read -EAGAIN, the soundcard is not ready to be read by gsmopen\n", GSMOPEN_P_LOG);
+		while (r == -EAGAIN) {
+			usleep(1000);
+
+			if (tech_pvt->alsa_capture_is_mono) {
+				r = snd_pcm_readi(tech_pvt->alsac, buf + readpos, left);
+			} else {
+				r = snd_pcm_readi(tech_pvt->alsac, buf2 + (readpos * 2), left);
+
+				int a = 0;
+				int i = 0;
+				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) {
+		/* A real frame */
+		readpos = 0;
+		left = GSMOPEN_FRAME_SIZE;
+		int i;
+		for (i = 0; i < r; i++)
+			data[i] = buf[i];
+
+	}
+	return r;
+}
+
+
+
+
+
+
+int gsmopen_sendsms(private_t * tech_pvt, char *dest, char *text)
+{
+	//char *idest = data;
+	//char rdest[256];
+	//struct gsmopen_pvt *p = NULL;
+	//char *device;
+	//char *dest;
+	//char *text;
+	//char *stringp = NULL;
+	//int found = 0;
+	int failed = 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) {
+		int err = 0;
+		char smscommand[16000];
+		memset(smscommand, '\0', sizeof(smscommand));
+
+		PUSHA_UNLOCKA(&tech_pvt->controldev_lock);
+		LOKKA(tech_pvt->controldev_lock);
+
+		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
+		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, 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:
+		usleep(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';
+		}
+		//usleep(500000);             //.5 secs
+		UNLOCKA(tech_pvt->controldev_lock);
+		POPPA_UNLOCKA(&tech_pvt->controldev_lock);
+	}
+
+	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;
+	}
+	*boost = exp(log(10) * *boost / 20) * BOOST_SCALE;
+	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) {			/* scale and clip values */
+		int i, x;
+
+		int16_t *ptr = (int16_t *) data;
+
+		for (i = 0; i < samples_num; i++) {
+			x = (ptr[i] * boost) / BOOST_SCALE;
+			if (x > 32767) {
+				x = 32767;
+			} else if (x < -32768) {
+				x = -32768;
+			}
+			ptr[i] = x;
+		}
+	}
+	return 0;
+}

Deleted: freeswitch/branches/gmaruzz/mod_gsmopen/mod_celliax.c

Copied: freeswitch/branches/gmaruzz/mod_gsmopen/mod_gsmopen.c (from rev 15046, freeswitch/branches/gmaruzz/mod_gsmopen/mod_celliax.c) (0 => 15047)


--- freeswitch/branches/gmaruzz/mod_gsmopen/mod_gsmopen.c	                        (rev 0)
+++ freeswitch/branches/gmaruzz/mod_gsmopen/mod_gsmopen.c	2009-10-02 13:19:23 UTC (rev 15047)
@@ -0,0 +1,2687 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
+ *
+ * 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 <anthmct@yahoo.com>
+ * 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"
+#define MDL_CHAT_PROTO "SMS"
+
+#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 */
+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);
+#if 0
+SWITCH_STANDARD_API(sk_function);
+/* BEGIN: Changes here */
+#define SK_SYNTAX "list [full] || console || gsmopen_API_msg || remove < gsmopenusername | #interface_name | #interface_id > || reload"
+/* END: Changes heres */
+SWITCH_STANDARD_API(gsmopen_function);
+#define GSMOPEN_SYNTAX "interface_name gsmopen_API_msg"
+#endif //0
+
+SWITCH_STANDARD_API(sendsms_function);
+#define SENDSMS_SYNTAX "gsmopen_sendsms interface_name destination_number SMS_text"
+/* BEGIN: Changes here */
+#define FULL_RELOAD 0
+#define SOFT_RELOAD 1
+/* END: Changes heres */
+
+char *interface_status[] = {	/* should match GSMOPEN_STATE_xxx in gsmopen.h */
+	"IDLE",
+	"DOWN",
+	"RING",
+	"DIALING",
+	"BUSY",
+	"UP",
+	"RINGING",
+	"PRERING",
+	"DOUBLE",
+	"SELECTD",
+	"HANG_RQ",
+	"PREANSW"
+};
+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 *sk_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 0
+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);
+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)
+{
+
+	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);
+
+	if (alsa_init(tech_pvt)) {
+		ERRORA("alsa_init failed\n", GSMOPEN_P_LOG);
+		return SWITCH_STATUS_FALSE;
+
+	}
+	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);
+
+
+	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 0
+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;
+
+
+	if (*the_interface == '#') {	/* remove by interface id or interface name */
+		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;
+				}
+			}
+		}
+	} 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) {
+#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 */
+	}
+
+	if (globals.GSMOPEN_INTERFACES[interface_id].gsmopen_api_thread) {
+#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
+	}
+
+	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.sk_console == &globals.GSMOPEN_INTERFACES[interface_id]) {
+		DEBUGA_GSMOPEN("interface '%s' no more console\n", GSMOPEN_P_LOG, the_interface);
+		globals.sk_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 = 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 = 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);
+
+		alsa_shutdown(tech_pvt);
+
+		*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 = 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;
+	if (tech_pvt->phone_callflow == CALLFLOW_STATUS_FINISHED) {
+		tech_pvt->phone_callflow = CALLFLOW_CALL_IDLE;
+	}
+	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 = 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 = 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 = 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 = 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 = 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 = 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 = 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;
+
+	channel = switch_core_session_get_channel(session);
+	switch_assert(channel != NULL);
+
+	tech_pvt = switch_core_session_get_private(session);
+	switch_assert(tech_pvt != NULL);
+
+	if (!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;
+	}
+
+
+	switch_core_timer_next(&tech_pvt->timer_read);
+
+	int samples;
+
+	//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, tech_pvt->read_frame.data, tech_pvt->read_codec.implementation->samples_per_packet)) > 0) {
+
+		tech_pvt->read_frame.datalen = samples * 2;
+		tech_pvt->read_frame.samples = samples;
+
+		//switch_core_timer_check(&tech_pvt->timer, SWITCH_TRUE);
+		tech_pvt->read_frame.timestamp = tech_pvt->timer_read.samplecount;
+
+		gsmopen_sound_boost(tech_pvt->read_frame.data, tech_pvt->read_frame.samples, tech_pvt->capture_boost);
+
+
+
+
+		*frame = &tech_pvt->read_frame;
+
+		//status = SWITCH_STATUS_SUCCESS;
+		switch_set_flag(tech_pvt, TFLAG_VOICE);
+	}
+
+	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);
+
+
+
+	char digit_str[256];
+
+
+	memset(digit_str, 0, sizeof(digit_str));
+	teletone_dtmf_detect(&tech_pvt->dtmf_detect, tech_pvt->read_frame.data, tech_pvt->read_frame.samples);
+	teletone_dtmf_get(&tech_pvt->dtmf_detect, digit_str, sizeof(digit_str));
+
+	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;
+				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;
+
+  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;
+	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;
+	unsigned int sent;
+
+	channel = switch_core_session_get_channel(session);
+	switch_assert(channel != NULL);
+
+	tech_pvt = switch_core_session_get_private(session);
+	switch_assert(tech_pvt != NULL);
+
+	if (!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)) {
+		switch_swap_linear(frame->data, (int) frame->datalen / 2);
+	}
+#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);
+	sent = alsa_write(tech_pvt, 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);
+	}
+	//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 = 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;
+	int samples;
+	short tmp_buffer[1280];
+
+	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:
+
+		WARNINGA("%s CHANNEL got SWITCH_MESSAGE_INDICATE_AUDIO_SYNC\n", GSMOPEN_P_LOG, switch_channel_get_name(channel));
+		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);
+		}
+		switch_core_timer_sync(&tech_pvt->timer_read);
+		switch_core_timer_sync(&tech_pvt->timer_write);
+
+		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 = switch_core_session_get_private(session);
+	char *body = switch_event_get_body(event);
+	switch_assert(tech_pvt != NULL);
+
+	if (!body) {
+		body = "";
+	}
+
+	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)
+{
+	private_t *tech_pvt = NULL;
+	if ((*new_session = switch_core_session_request(gsmopen_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 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 (!switch_strlen_zero(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_DESTINATION_OUT_OF_ORDER;
+					}
+
+					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_channel_set_flag(channel, CF_OUTBOUND);
+		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
+ *
+ */
+/* BEGIN: Changes heres */
+
+
+static switch_status_t load_config(int reload_type)
+/* END: Changes heres */
+{
+	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");
+			char *context = "default";
+			char *dialplan = "XML";
+			char *destination = "5000";
+			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;
+			char *enable_callerid = "true";
+
+
+			char *at_dial_pre_number= "ATD";
+			char *at_dial_post_number= ";";
+			char *at_dial_expect= "OK";
+			char *at_hangup= "ATH";
+			char *at_hangup_expect= "OK";
+			char *at_answer= "ATA";
+			char *at_answer_expect= "OK";
+			char *at_send_dtmf= "AT+VTS";
+			char *at_preinit_1= "";
+			char *at_preinit_1_expect= "";
+			char *at_preinit_2= "";
+			char *at_preinit_2_expect= "";
+			char *at_preinit_3= "";
+			char *at_preinit_3_expect= "";
+			char *at_preinit_4= "";
+			char *at_preinit_4_expect= "";
+			char *at_preinit_5= "";
+			char *at_preinit_5_expect= "";
+			char *at_postinit_1= "at+cmic=0,9";
+			char *at_postinit_1_expect= "OK";
+			char *at_postinit_2= "AT+CKPD=\"EEE\"";
+			char *at_postinit_2_expect= "OK";
+			char *at_postinit_3= "AT+CSSN=1,0";
+			char *at_postinit_3_expect= "OK";
+			char *at_postinit_4= "at+sidet=0";
+			char *at_postinit_4_expect= "OK";
+			char *at_postinit_5= "at+clvl=99";
+			char *at_postinit_5_expect= "OK";
+			char *at_query_battchg= "AT+CBC";
+			char *at_query_battchg_expect= "OK";
+			char *at_query_signal= "AT+CSQ";
+			char *at_query_signal_expect= "OK";
+			char *at_call_idle= "+MCST: 1";
+			char *at_call_incoming= "+MCST: 2";
+			char *at_call_active= "+CSSI: 7";
+			char *at_call_failed= "+MCST: 65";
+			char *at_call_calling= "+CSSI: 1";
+			char *at_indicator_noservice_string= "CIEV: 2;0";
+			char *at_indicator_nosignal_string= "CIEV: 5;0";
+			char *at_indicator_lowsignal_string= "CIEV: 5;1";
+			char *at_indicator_lowbattchg_string= "CIEV: 0;1";
+			char *at_indicator_nobattchg_string= "CIEV: 0;0";
+			char *at_indicator_callactive_string= "CIEV: 3;1";
+			char *at_indicator_nocallactive_string= "CIEV: 3;0";
+			char *at_indicator_nocallsetup_string= "CIEV: 6;0";
+			char *at_indicator_callsetupincoming_string= "CIEV: 6;1";
+			char *at_indicator_callsetupoutgoing_string= "CIEV: 6;2";
+			char *at_indicator_callsetupremoteringing_string= "CIEV: 6;3";
+			char *sms_receiving_program= "/usr/local/bin/ciapalo";
+			char *alsacname= "plughw:1";
+			char *alsapname= "plughw:1";
+			char *at_early_audio ="0";
+			char *at_after_preinit_pause="500000";
+			char *at_initial_pause="500000";
+			char *at_has_clcc="0";
+			char *at_has_ecam="0";
+			char *alsa_period_size="160";
+			char *alsa_periods_in_buffer="4";
+			char *gsmopen_sound_rate="8000";
+			char *alsa_play_is_mono="1";
+			char *alsa_capture_is_mono="1";
+			char *capture_boost="5";
+			char *playback_boost="10";
+
+			uint32_t interface_id = 0;
+			//uint32_t to=0;
+			//uint32_t max=0;
+			uint32_t controldevice_speed = B115200; //FIXME TODO
+			uint32_t controldevprotocol = PROTOCOL_AT; //FIXME TODO
+			uint32_t running =1; //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, "hold_music")) {
+					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, "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;
+
+
+				}
+
+			}
+
+			/* 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 (name) {
+				DEBUGA_GSMOPEN("name=%s\n", GSMOPEN_P_LOG, name);
+			}
+			if (interface_id && interface_id < GSMOPEN_MAX_INTERFACES) {
+				private_t newconf;
+				switch_threadattr_t *gsmopen_api_thread_attr = NULL;
+
+				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);
+
+
+				//FIXME 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 );
+				//FIXME switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].digit_timeout , digit_timeout );
+				//FIXME switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].max_digits , max_digits );
+				//FIXME switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].hotline , hotline );
+				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 );
+				//FIXME switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].enable_callerid , enable_callerid );
+				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);
+				switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].alsacname, alsacname);
+				switch_set_string(globals.GSMOPEN_INTERFACES[interface_id].alsapname, alsapname);
+
+				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);
+				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);
+				globals.GSMOPEN_INTERFACES[interface_id].capture_boost = atoi(capture_boost);
+				globals.GSMOPEN_INTERFACES[interface_id].playback_boost = atoi(playback_boost);
+
+
+
+				globals.GSMOPEN_INTERFACES[interface_id].controldevice_speed = controldevice_speed;
+				globals.GSMOPEN_INTERFACES[interface_id].controldevprotocol = controldevprotocol;
+				//globals.GSMOPEN_INTERFACES[interface_id].at_early_audio = at_early_audio;
+				//globals.GSMOPEN_INTERFACES[interface_id].at_after_preinit_pause = at_after_preinit_pause;
+				//globals.GSMOPEN_INTERFACES[interface_id].at_initial_pause = at_initial_pause;
+				//globals.GSMOPEN_INTERFACES[interface_id].at_has_clcc = at_has_clcc;
+				//globals.GSMOPEN_INTERFACES[interface_id].at_has_ecam = at_has_ecam;
+				//globals.GSMOPEN_INTERFACES[interface_id].alsa_period_size = alsa_period_size;
+				//globals.GSMOPEN_INTERFACES[interface_id].alsa_periods_in_buffer = alsa_periods_in_buffer;
+				//globals.GSMOPEN_INTERFACES[interface_id].gsmopen_sound_rate = gsmopen_sound_rate;
+				//globals.GSMOPEN_INTERFACES[interface_id].alsa_play_is_mono = alsa_play_is_mono;
+				//globals.GSMOPEN_INTERFACES[interface_id].alsa_capture_is_mono = alsa_capture_is_mono;
+				//globals.GSMOPEN_INTERFACES[interface_id].capture_boost = capture_boost;
+				//globals.GSMOPEN_INTERFACES[interface_id].playback_boost = playback_boost;
+				globals.GSMOPEN_INTERFACES[interface_id].running = running; //FIXME
+
+				gsmopen_store_boost("5", &globals.GSMOPEN_INTERFACES[interface_id].capture_boost); //FIXME
+				gsmopen_store_boost("10", &globals.GSMOPEN_INTERFACES[interface_id].playback_boost); //FIXME
+
+
+				DEBUGA_GSMOPEN("interface_id=%d globals.GSMOPEN_INTERFACES[interface_id].name=%s\n",
+						GSMOPEN_P_LOG, interface_id, globals.GSMOPEN_INTERFACES[interface_id].name);
+				DEBUGA_GSMOPEN
+					("interface_id=%d globals.GSMOPEN_INTERFACES[interface_id].dialplan=%s\n",
+					 GSMOPEN_P_LOG, interface_id, globals.GSMOPEN_INTERFACES[interface_id].dialplan);
+				DEBUGA_GSMOPEN
+					("interface_id=%d globals.GSMOPEN_INTERFACES[interface_id].destination=%s\n",
+					 GSMOPEN_P_LOG, interface_id, globals.GSMOPEN_INTERFACES[interface_id].destination);
+				DEBUGA_GSMOPEN
+					("interface_id=%d globals.GSMOPEN_INTERFACES[interface_id].context=%s\n",
+					 GSMOPEN_P_LOG, interface_id, globals.GSMOPEN_INTERFACES[interface_id].context);
+				WARNINGA("STARTING interface_id=%d\n", GSMOPEN_P_LOG, interface_id);
+
+				int res = 0;
+
+				/* 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);
+						return SWITCH_STATUS_FALSE;
+						//gsmopen_sound_shutdown(tmp);
+						//if (tmp)
+						//free(tmp);
+						//return NULL;
+					}
+				}
+
+				/* config the phone/modem on the serial port */
+				if (globals.GSMOPEN_INTERFACES[interface_id].controldevprotocol != PROTOCOL_NO_SERIAL) {
+					//int res;
+					res = gsmopen_serial_config(&globals.GSMOPEN_INTERFACES[interface_id]);
+					if (res) {
+						ERRORA("gsmopen_serial_config failed\n", GSMOPEN_P_LOG);
+						return SWITCH_STATUS_FALSE;
+						//gsmopen_sound_shutdown(tmp);
+						//if (tmp)
+						//free(tmp);
+						//return NULL;
+					}
+				}
+
+
+				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);
+
+			} else {
+				ERRORA("interface id %d is higher than GSMOPEN_MAX_INTERFACES (%d)\n", GSMOPEN_P_LOG, interface_id, 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("i=%d globals.GSMOPEN_INTERFACES[%d].interface_id=%s\n", GSMOPEN_P_LOG, i, i, globals.GSMOPEN_INTERFACES[i].interface_id);
+				DEBUGA_GSMOPEN("i=%d globals.GSMOPEN_INTERFACES[%d].name=%s\n", GSMOPEN_P_LOG, i, i, globals.GSMOPEN_INTERFACES[i].name);
+				DEBUGA_GSMOPEN("i=%d globals.GSMOPEN_INTERFACES[%d].context=%s\n", GSMOPEN_P_LOG, i, i, globals.GSMOPEN_INTERFACES[i].context);
+				DEBUGA_GSMOPEN("i=%d globals.GSMOPEN_INTERFACES[%d].dialplan=%s\n", GSMOPEN_P_LOG, i, i, globals.GSMOPEN_INTERFACES[i].dialplan);
+				DEBUGA_GSMOPEN("i=%d globals.GSMOPEN_INTERFACES[%d].destination=%s\n", GSMOPEN_P_LOG, i, i, globals.GSMOPEN_INTERFACES[i].destination);
+			}
+		}
+	}
+
+	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)
+{
+	char *user, *host, *f_user = NULL, *f_host = NULL, *f_resource = NULL;
+	private_t * tech_pvt=NULL;
+	int i=0, found=0;
+
+	switch_assert(proto != NULL);
+
+	DEBUGA_GSMOPEN("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", GSMOPEN_P_LOG, proto, from, to, subject, body, type, 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, type=%s, hint=%s)\n", GSMOPEN_P_LOG, proto, from, to, subject, body, type, 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;
+}
+
+
+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_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 0
+		SWITCH_ADD_API(commands_api_interface, "sk", "GSMopen console commands", sk_function, SK_SYNTAX);
+		SWITCH_ADD_API(commands_api_interface, "gsmopen", "GSMopen interface commands", gsmopen_function, GSMOPEN_SYNTAX);
+#endif //0
+		SWITCH_ADD_API(commands_api_interface, "gsmopen_sendsms", "gsmopen_sendsms interface destination_number SMS_text", sendsms_function, SENDSMS_SYNTAX);
+		SWITCH_ADD_CHAT(chat_interface, MDL_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;
+
+	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)) {
+
+
+
+			//alsa_shutdown(&globals.GSMOPEN_INTERFACES[interface_id]);
+			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);
+			}
+#ifndef WIN32
+			WARNINGA("SHUTDOWN interface_id=%d\n", GSMOPEN_P_LOG, interface_id);
+			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;
+
+	if ((session = switch_core_session_request(gsmopen_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, 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 (!switch_strlen_zero(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 (!switch_strlen_zero(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 (!switch_strlen_zero(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 0
+SWITCH_STANDARD_API(sk_function)
+{
+	char *mycmd = NULL, *argv[10] = { 0 };
+	int argc = 0;
+
+	if (globals.sk_console)
+		stream->write_function(stream, "sk console is: |||%s|||\n", globals.sk_console->name);
+	else
+		stream->write_function(stream, "sk console is NOT yet assigned\n");
+
+	if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) {
+		argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+	}
+
+	if (!argc || !argv[0]) {
+		stream->write_function(stream, "%s", SK_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.sk_console = &globals.GSMOPEN_INTERFACES[i];
+					stream->write_function(stream, "sk console is now: globals.GSMOPEN_INTERFACES[%d].name=|||%s|||\n", i, globals.GSMOPEN_INTERFACES[i].name);
+					stream->write_function(stream, "sk console is: |||%s|||\n", globals.sk_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: sk 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, "sk reload failed\n");
+		} else {
+			stream->write_function(stream, "sk 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, "sk remove '%s' failed\n", argv[1]);
+				} else {
+					stream->write_function(stream, "sk remove '%s' success\n", argv[1]);
+				}
+			}
+		} else {
+			stream->write_function(stream, "-ERR Usage: sk remove interface_name\n");
+			goto end;
+		}
+/* END: Changes heres */
+
+	} else {
+	}
+  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 (!switch_strlen_zero(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_signaling_write(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
+
+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;
+}
+
+void *gsmopen_do_gsmopenapi_thread_func(void *obj)
+{
+
+	private_t *tech_pvt = obj;
+	//if (gsmopen_present(GSMopenHandles)) 
+	if (1) {
+		while (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);
+				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 }
+			}
+			usleep(100);		//give other threads a chance
+		}
+	} else {
+		ERRORA("GSM is not running, maybe crashed. Please run/restart GSM and relaunch GSMopen\n", GSMOPEN_P_LOG);
+		running = 0;
+		return NULL;
+	}
+	//running = 0;
+	return NULL;
+
+}
+#ifdef NOTDEF
+int sms_incoming(private_t * tech_pvt, char *value)
+{
+	char sms_buf[512];
+	switch_event_t *event;
+
+	DEBUGA_GSMOPEN("received SMS >>>%s<<< on interface %s\n", GSMOPEN_P_LOG, value, tech_pvt->name);
+
+	switch_snprintf(sms_buf, sizeof(sms_buf), "incoming SMS on %s\n", tech_pvt->name);
+
+	if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_INCOMING_SMS) == SWITCH_STATUS_SUCCESS) {
+		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_info", sms_buf);
+		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "interface_name", tech_pvt->name);
+		switch_event_add_body(event, "%s\n\n", tech_pvt->sms_message);
+		switch_event_fire(&event);
+	}
+
+	return 0;
+}
+#endif// NOTDEF
+
+
+SWITCH_STANDARD_API(sendsms_function)
+{
+	char *mycmd = NULL, *argv[3] = { 0 };
+	int argc = 0;
+	private_t *tech_pvt = NULL;
+
+	if (!switch_strlen_zero(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, MDL_CHAT_PROTO, tech_pvt->name, argv[1], "SIMPLE MESSAGE", switch_str_nil(argv[2]), tech_pvt->name);
+
+			chat_send(MDL_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 sms_incoming(private_t * tech_pvt, char *value)
+{
+	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 (!switch_strlen_zero(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", MDL_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, "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", 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", MDL_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, "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", 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(skypiax_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 (!switch_strlen_zero(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 Skypiax 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(MDL_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, MDL_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(MDL_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]);
+			//skypiax_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]);
+				skypiax_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:
+ */