<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[Freeswitch-trunk][14803] </title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<div id="header">FreeSWITCH Subversion</div>
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://fisheye.freeswitch.org/changelog/FreeSWITCH?cs=14803">14803</a></dd>
<dt>Author</dt> <dd>gmaruzz</dd>
<dt>Date</dt> <dd>2009-09-10 03:20:20 -0500 (Thu, 10 Sep 2009)</dd>
</dl>

<h3>Log Message</h3>
<pre>celliax: some cleaning, and added 'asterisk' directory, with chan_celliax files</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxMakefile">freeswitch/branches/gmaruzz/mod_celliax/Makefile</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxmod_celliaxc">freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>freeswitch/branches/gmaruzz/mod_celliax/asterisk/</li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskMakefile">freeswitch/branches/gmaruzz/mod_celliax/asterisk/Makefile</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskREADME">freeswitch/branches/gmaruzz/mod_celliax/asterisk/README</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskasoundconf">freeswitch/branches/gmaruzz/mod_celliax/asterisk/asound.conf</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliaxconf">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.conf</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliaxh">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.h</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_additionalc">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_additional.c</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_libcsvc">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.c</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_libcsvh">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.h</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_spandspc">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.c</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_spandsph">freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.h</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskchan_celliaxc">freeswitch/branches/gmaruzz/mod_celliax/asterisk/chan_celliax.c</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxasteriskciapalo">freeswitch/branches/gmaruzz/mod_celliax/asterisk/ciapalo</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="freeswitchbranchesgmaruzzmod_celliaxMakefile"></a>
<div class="modfile"><h4>Modified: freeswitch/branches/gmaruzz/mod_celliax/Makefile (14802 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/Makefile        2009-09-10 05:49:25 UTC (rev 14802)
+++ freeswitch/branches/gmaruzz/mod_celliax/Makefile        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> MODNAME=mod_celliax
</span><del>-SVNDEF := -D'SKYPIAX_SVN_VERSION=&quot;$(shell svnversion -n .)&quot;'
</del><ins>+SVNDEF := -D'CELLIAX_SVN_VERSION=&quot;$(shell svnversion -n .)&quot;'
</ins><span class="cx"> LOCAL_CFLAGS += $(SVNDEF)
</span><span class="cx"> LOCAL_LDFLAGS=-lX11 -lasound
</span><span class="cx"> LOCAL_OBJS=celliax_protocol.o
</span></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskMakefile"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/Makefile (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/Makefile                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/Makefile        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,81 @@
</span><ins>+#
+# Asterisk -- A telephony toolkit for Linux.
+# 
+# Makefile for channel drivers
+#
+# Copyright (C) 1999-2005, Mark Spencer
+#
+# Mark Spencer &lt;markster@digium.com&gt;
+#
+# Edited By Belgarath &lt;&gt; Aug 28 2004
+# Added bare bones ultrasparc-linux support.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+#ASTERISK INCLUDE FILES
+#The directory that contains the Asterisk include files (eg: /usr/include or /usr/include/asterisk or /usr/src/asterisk/include or ...)
+#AST_INCLUDE_DIR=/usr/src/off_dev/asterisk-1.2.rev137401/include
+AST_INCLUDE_DIR=/usr/src/asterisk-1.4.26/include
+#AST_INCLUDE_DIR=/usr/src/asterisk-1.6.0.10/include
+
+#ASTERISK VERSION
+#Uncomment one of the following lines to match your Asterisk series
+#CFLAGS+=-DASTERISK_VERSION_1_2
+CFLAGS+=-DASTERISK_VERSION_1_4
+#CFLAGS+=-DASTERISK_VERSION_1_6_0
+
+CFLAGS+=-pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
+CFLAGS+=-g3
+
+
+
+CFLAGS+=-I$(AST_INCLUDE_DIR) -I.
+CFLAGS+=-D_REENTRANT -D_GNU_SOURCE
+#CFLAGS+=-O6
+#CFLAGS+=-march=i586
+CFLAGS+=-fomit-frame-pointer
+ifeq ($(shell uname -m),x86_64)
+CFLAGS+=-fPIC
+endif
+
+SVNDEF := -D'CELLIAX_SVN_VERSION=&quot;$(shell svnversion -n .)&quot;'
+CFLAGS += $(SVNDEF)
+
+
+SOLINK=-shared -Xlinker -x 
+CHANNEL_LIBS=chan_celliax.so
+CC=gcc
+
+OSARCH=$(shell uname -s)
+
+ifeq ($(findstring CYGWIN,$(OSARCH)),CYGWIN)
+# definition of pthread_kill as a printf (or as a noop) is required for Asterisk (and celliax) to run on Cygwin
+# without it, each time (often) pthread_kill is called (by any thread, with any signal, URG included), bad things happen 
+CC=gcc -D pthread_kill=cyg_no_pthreadkill
+AST_DLL_DIR=/home/maruzz/devel/svn_asterisk_branches_12
+CYGSOLINK=-Wl,--out-implib=lib$@.a -Wl,--export-all-symbols cyg_no_pthread_kill.o
+CYGSOLIB=-L/usr/lib/w32api -lrpcrt4 -L/lib/mingw -lwinmm -L$(AST_DLL_DIR) -lasterisk.dll -L$(AST_DLL_DIR)/res -lres_features.so 
+CHANNEL_LIBS=cyg_no_pthread_kill.o chan_celliax.so
+endif
+
+all: $(CHANNEL_LIBS) 
+
+clean:
+        rm -f *.so *.o *.so.a
+
+
+#chan_celliax section begins
+
+#to debug threads and lock on 1.4 uncomment the following
+#CFLAGS+=-include /usr/src/asterisk/include/asterisk/autoconfig.h
+
+cyg_no_pthread_kill.o: cyg_no_pthread_kill.c
+        $(CC) $(CFLAGS) -c -o cyg_no_pthread_kill.o cyg_no_pthread_kill.c
+chan_celliax.o: chan_celliax.c
+        $(CC) $(CFLAGS) -c -o chan_celliax.o chan_celliax.c
+chan_celliax.so: chan_celliax.o celliax_spandsp.o celliax_libcsv.o celliax_additional.o
+        $(CC) $(SOLINK) -o $@ ${CYGSOLINK} chan_celliax.o celliax_spandsp.o celliax_libcsv.o celliax_additional.o -lm -ldl -lasound ${CYGSOLIB}
+#chan_celliax section ends
+
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskREADME"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/README (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/README                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/README        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,93 @@
</span><ins>+**************************************************************
+* CHAN_CELLIAX DRIVER FOR AN EXISTING ASTERISK INSTALLATION
+*   (works for Asterisk 1.2.xx 1.4.xx 1.6.0.xx series)
+*      (do NOT works for Asterisk 1.6.1.xx series)
+**************************************************************
+==============================================================
+1) To build celliax you need the ALSA development libraries
+==============================================================
+
+To build celliax on Linux you need to install ALSA-dev libs
+
+You can do it on Debian/Ubuntu with:
+# apt-get install libasound2-dev
+
+You can do it on Fedora/CentOS with:
+# yum --nogpgcheck -y install alsa-lib-devel
+
+===============================================================
+2) build chan_celliax
+===============================================================
+
+go into the celliax source directory, edit the Makefile, 
+insert the asterisk version (1.2.x or 1.4.x or 1.6.0.x, 
+NOT 1.6.1.x), the location of your asterisk sources, then
+
+# make clean
+# make
+
+copy chan_celliax.so in the asterisk modules directory
+
+===============================================================
+2) configure chan_celliax and asterisk
+===============================================================
+
+edit and copy celliax.conf in the asterisk configuration 
+directory
+
+copy ciapalo (or your program that accepts text in stdini) in 
+the location defined in celliax.conf (sms_receiving_program)
+
+edit modules.conf and put noload in front of *BOTH* chan_oss.so
+AND chan_alsa.so AND chan_console.so
+
+copy asound.conf in /etc/asound.conf
+
+if you enable debug in logger.conf and &quot;set debug 100&quot; on 
+Asterisk, you'll get *a lot* of info on celliax inner workings
+
+===============================================================
+3) Troubleshooting
+===============================================================
+Check that you set the correct serial device in celliax.conf
+You can check how the device is called on your platform with:
+dmesg | grep ttyU
+dmesg | grep ttyA
+
+Check that you set the correct audio device in celliax.conf
+You can check how the device is called on your platform with:
+aplay -l
+it will give you the number to put on &quot;plughw:N&quot; in celliax.conf
+
+Check the volume for both play and capture, and that capture 
+is activated (press spacebar when alsamixer in capture mode)
+alsamixer -c[soundcard_number] -Vplay
+alsamixer -c[soundcard_number] -Vcapture
+we found good results with capt(31) autogain(on) speaker(75)
+
+If you get double digits (bounces) on DTMFs, check if the capture
+volume is too high
+
+The SMSs are managed in Unicode (UTF8) for international characters
+compatibility. Check that your console and/or text editor displays
+UTF8 characters
+
+If you have bad sound, it's a timing problem. Use a 1000HZ kernel,
+or add a timing device (ztdummy or zaptel interfaces)
+***************************************************************
+* END CHAN_CELLIAX DRIVER FOR AN EXISTING ASTERISK INSTALLATION
+***************************************************************
+
+
+
+chan_celliax adds to Asterisk the DIALPLAN application:
+
+          CelliaxSendsms
+
+You can type at the Asterisk CLI 'show application [application]' 
+to obtain more specific info on usage.
+
+
+
+Enjoy!
+
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskasoundconf"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/asound.conf (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/asound.conf                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/asound.conf        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,2 @@
</span><ins>+#defaults.pcm.rate_converter &quot;speexrate&quot;
+defaults.pcm.rate_converter &quot;linear&quot;
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliaxconf"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.conf (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.conf                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.conf        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,205 @@
</span><ins>+;; 
+;; 
+; Celliax Asterisk Driver 
+; 
+; Configuration file 
+; lines beginning with semicolon (&quot; are ignored (commented out)
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; The first interface (named line0)
+[line0] 
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;
+; general settings, valid on all platforms
+;
+; 
+; Default language 
+; 
+language=en 
+; 
+; Default context (is overridden with @context syntax) 
+; 
+context=default 
+; 
+; Default extension (in extensions.conf) where incoming calls land
+; 
+extension=s 
+; 
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;
+; Debugging settings, valid globally for all interfaces on all platforms
+;
+; the debug values are global for all the interfaces.
+;
+; default is no celliax debugging output, you **have** to activate debugging here to obtain debugging from celliax
+;
+; To see the debugging output you have to &quot;set debug 100&quot; from the Asterisk CLI or launch
+; Asterisk with -ddddddddddd option, and have the logger.conf file activating debug info for console and messages
+;
+; You can activate each of the following separately, but you can't disactivate. Eg: debug_at=no does not subtract debug_at from debug_all
+; debug_all activate all possible debugging info
+;
+;debug_all=yes
+debug_at=yes
+;debug_fbus2=yes
+debug_serial=yes
+debug_pbx=yes
+debug_sound=yes
+;debug_locks=yes
+debug_call=yes
+;debug_monitorlocks=yes
+;debug_cvm=yes
+
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;
+; This is the program that will receive in stdin the incoming SMSs
+sms_receiving_program=/usr/local/asterisk/usr/sbin/ciapalo
+
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;
+; serial settings, valid for all platforms
+;
+;control_device_protocol can be AT or FBUS2 or NO_SERIAL (with NO_SERIAL the speed and name of the port are ignored)
+control_device_protocol=at 
+
+;speed of the serial port
+control_device_speed=115200
+
+;name of the serial port device
+control_device_name=/dev/ttyACM0 ; this is a Celliax Official Device, recognized as a modem by Linux
+;control_device_name=/dev/ttyUSB0 ; this is an alternative form of a Celliax Official Device, recognized as a modem by Linux
+
+;watch the soundcard for noise (ring), because the serial port do not tell us about incoming calls (eg 3310nokia), NO_SERIAL protocol watch for acoustic ring in any case
+need_acoustic_ring=0
+
+;audio noise threshold beyond which we declare there is a ring (512 is default, put it to 1024 or 2048 if you have false positive), ignored if not watching for ring
+dsp_silence_threshold=1024
+
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;
+; audio boost settings, valid for all platforms, to compensate for different soundcard/phone input/output signal levels
+; tweak it if you get horrible (or not hearable) sound
+;
+;boost can be positive or negative (-40 to +40) in db
+;experiment to find which values are best for your soundcard
+playback_boost=0 ; 
+capture_boost=0 ; 
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; which audio device to use
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;names of the sound devices in linux
+;if you don't use skype on this interface (eg don't need to share the audio device with other applications while celliax is running), use the plughw:n devices (plughw:0 is the first, plughw:1 is the second soundcard, etc). They have the best latency
+;if you use skype on this interface use the default:n devices (default:0 is the first, default:1 is the second soundcard, etc). They have worst latency, but you can share them
+
+alsa_capture_device_name=plughw:1
+alsa_playback_device_name=plughw:1
+
+alsa_period_size=160
+alsa_periods_in_buffer=4
+
+;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;
+; at &quot;modem&quot; commands settings for this interface (if controldevice_protocol is not AT they are ignored)
+;
+;what the modem is expecting in the part of the dial command before the number to be dialed (eg: ATD)
+;at_dial_pre_number=AT+CKPD=&quot;EEE
+at_dial_pre_number=ATD
+;what the modem is expecting in the part of the dial command after the number to be dialed. If you want it to wait for a semicolon (;), just comment out the followin line. Wait for semicolon is the default
+;at_dial_post_number=S&quot;
+;what the modem will answer after succesful execution of the dial command
+at_dial_expect=OK
+
+;command to hangup the current call
+;at_hangup=AT+CKPD=&quot;EEE&quot;
+at_hangup=ATH
+;what the modem will answer after succesful execution of the hangup command
+at_hangup_expect=OK
+
+;command to answer an incoming call
+at_answer=ATA
+;what the modem will answer after succesful execution of the answer command
+at_answer_expect=OK
+
+;pause right after serial port opening, before any command is sent, in usecs (1million usec= 1sec)
+at_initial_pause=500000
+;custom commands to be sent after the initial pause and before the &quot;built in&quot; initialization commands, and what the modem is expected to send as reply
+;the first empty string stop the preinit sending
+at_preinit_1=atciapa ; nonsense entry, just to show the preinit
+at_preinit_1_expect=OK
+at_preinit_2=
+at_preinit_2_expect=
+at_preinit_3=
+at_preinit_3_expect=
+at_preinit_4=
+at_preinit_4_expect=
+at_preinit_5=
+at_preinit_5_expect=
+;pause right after the custom preinit commands, before any &quot;built in&quot; command is sent, in usecs (1million usec= 1sec)
+at_after_preinit_pause=500000
+;custom commands to be sent after the &quot;built in&quot; initialization commands, and what the modem is expected to send as reply
+;the first empty string stop the postinit sending
+;at_postinit_1=atcucu ; nonsense entry, just to show the postinit
+at_postinit_1=at+cmic=0,9 ; modem's microphone sensitivity (our spk)
+at_postinit_1_expect=OK
+at_postinit_2=AT+CKPD=&quot;EEE&quot; ;send three &quot;end&quot; buttonpress, to have the phone in a sane state, ready to dialing with furter CKPDs ***THIS IS IMPORTANT, needed on c650***
+at_postinit_2_expect=OK
+at_postinit_3=AT+CSSN=1,0
+at_postinit_3_expect=OK
+at_postinit_4=at+sidet=0 ; no sidetone in modem, please
+at_postinit_4_expect=OK
+at_postinit_5=at+clvl=99 ; modem's speaker level, out mic
+at_postinit_5_expect=OK
+
+;what command to query the battery status, and what the modem is expected to send as reply
+at_query_battchg=AT+CBC
+at_query_battchg_expect=OK
+;what command to query the signal status, and what the modem is expected to send as reply
+at_query_signal=AT+CSQ
+at_query_signal_expect=OK
+
+;what command to send a DTMF
+at_send_dtmf=AT+VTS 
+
+;the modem will send us the following messages to signal that the visual indicators on the phone has changed because of events (without us to ask for them), loosely based on ETSI standard (see CIND/CIEV/CMER in ETSI). Variable by manufacturer and phone model
+; no service
+at_indicator_noservice_string=+CIEV: 2,0
+; no signal
+at_indicator_nosignal_string=+CIEV: 5,0
+; low signal
+at_indicator_lowsignal_string=+CIEV: 5,1
+; low battery
+at_indicator_lowbattchg_string=+CIEV: 0,1
+; no battery battery
+at_indicator_nobattchg_string=+CIEV: 0,0
+; call is up
+at_indicator_callactive_string=+CIEV: 3,1
+; call is down
+at_indicator_nocallactive_string=+CIEV: 3,0
+; call is no more in process
+at_indicator_nocallsetup_string=+CIEV: 6,0
+; call incoming is in process
+at_indicator_callsetupincoming_string=+CIEV: 6,1
+; call outgoing is in process
+at_indicator_callsetupoutgoing_string=+CIEV: 6,2
+; remote party is ringing because of our call outgoing 
+at_indicator_callsetupremoteringing_string=+CIEV: 6,3
+
+;call processing unsolicited messages, proprietary for each phone manufacturer
+;the modem will send us the following mesage to signal that the line is idle (eg. after an outgoing call has failed, or after hangup)
+at_call_idle=+MCST: 1
+;the modem will send us the following mesage to signal that there is an incoming voice call
+at_call_incoming=+MCST: 2
+;the modem will send us the following mesage to signal that there is an active call (eg. the remote party has answered us, or we answered them)
+;at_call_active=+MCST: 3
+at_call_active=+CSSI: 7
+;the modem will send us the following mesage to signal that our outgoing call has failed
+at_call_failed=+MCST: 65
+;the modem will send us the following mesage to signal that our outgoing call is in the calling phase
+;at_call_calling=+MCST: 64
+at_call_calling=+CSSI: 1
+
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliaxh"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.h (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.h                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax.h        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,908 @@
</span><ins>+//indent -gnu -ts4 -br -brs -cdw -lp -ce -nbfda -npcs -nprs -npsl -nbbo -saf -sai -saw -cs -bbo -nhnl -nut -sob -l90 
+#undef GIOVA48
+#define CELLIAX_ALSA
+#ifndef _CELLIAX_H_
+#define _CELLIAX_H_
+
+#ifndef CELLIAX_SVN_VERSION
+#define CELLIAX_SVN_VERSION &quot;????NO_REVISION???&quot;
+#endif
+
+#include &lt;asterisk/version.h&gt;   /* needed here for conditional compilation on version.h */
+  /* the following #defs are for LINUX */
+#ifndef __CYGWIN__
+#ifndef ASTERISK_VERSION_1_6_0
+#ifndef ASTERISK_VERSION_1_4
+#ifndef ASTERISK_VERSION_1_2
+#define ASTERISK_VERSION_1_4
+#if(ASTERISK_VERSION_NUM == 999999)
+#undef ASTERISK_VERSION_1_4
+#elif(ASTERISK_VERSION_NUM &lt; 10400)
+#undef ASTERISK_VERSION_1_4
+#endif /* ASTERISK_VERSION_NUM == 999999 || ASTERISK_VERSION_NUM &lt; 10400 */
+#endif /* ASTERISK_VERSION_1_2 */
+#endif /* ASTERISK_VERSION_1_4 */
+#endif /* ASTERISK_VERSION_1_6_0 */
+#ifdef ASTERISK_VERSION_1_2
+#undef ASTERISK_VERSION_1_4
+#endif /* ASTERISK_VERSION_1_2 */
+#ifdef ASTERISK_VERSION_1_6_0
+#define ASTERISK_VERSION_1_4
+#endif /* ASTERISK_VERSION_1_6_0 */
+#define CELLIAX_DIR
+#undef CELLIAX_LIBCSV
+#endif /* NOT __CYGWIN__ */
+  /* the following #defs are for WINDOWS */
+#ifdef __CYGWIN__
+#undef ASTERISK_VERSION_1_4
+#undef ASTERISK_VERSION_1_6_0
+#define CELLIAX_DIR
+#undef CELLIAX_LIBCSV
+#endif /* __CYGWIN__ */
+
+/* CELLIAX_CVM */
+#undef CELLIAX_CVM
+/* CELLIAX_CVM */
+
+#undef CELLIAX_FBUS2
+#define CELLIAX_DIR
+#define CELLIAX_LIBCSV
+
+/* INCLUDES */
+#ifdef ASTERISK_VERSION_1_6_0
+#include &lt;asterisk.h&gt;           /* some asterisk-devel package do not contains asterisk.h, but seems that is needed for the 1.6 series, at least from trunk */
+#endif /* ASTERISK_VERSION_1_6_0 */
+#include &lt;unistd.h&gt;
+#include &lt;termios.h&gt;
+#include &lt;fcntl.h&gt;
+#include &lt;errno.h&gt;
+#include &lt;math.h&gt;
+#include &lt;sys/ioctl.h&gt;
+#include &lt;sys/time.h&gt;
+#include &lt;string.h&gt;
+#include &lt;sys/types.h&gt;
+#include &lt;signal.h&gt;
+#include &lt;ctype.h&gt;
+#ifndef ASTERISK_VERSION_1_4
+#include &lt;stdlib.h&gt;
+#include &lt;stdio.h&gt;
+#endif /* ASTERISK_VERSION_1_4 */
+#ifndef CELLIAX_ALSA
+#include &quot;pablio.h&quot;
+#endif /* CELLIAX_ALSA */
+//#include &lt;asterisk/frame.h&gt;
+#include &lt;asterisk/channel.h&gt;
+#include &lt;asterisk/module.h&gt;
+#include &lt;asterisk/pbx.h&gt;
+#include &lt;asterisk/causes.h&gt;
+#include &lt;asterisk/cli.h&gt;
+#include &lt;asterisk/options.h&gt;
+#include &lt;asterisk/config.h&gt;
+#include &lt;asterisk/endian.h&gt;
+#include &lt;asterisk/dsp.h&gt;
+#include &lt;asterisk/lock.h&gt;
+#include &lt;asterisk/devicestate.h&gt;
+#include &lt;asterisk/file.h&gt;
+#include &lt;asterisk/say.h&gt;
+#include &lt;asterisk/manager.h&gt;
+#ifdef ASTERISK_VERSION_1_6_0
+#include &lt;asterisk/astobj2.h&gt;
+#include &lt;asterisk/paths.h&gt;
+#endif /* ASTERISK_VERSION_1_6_0 */
+#ifdef ASTERISK_VERSION_1_4
+#include &lt;asterisk/stringfields.h&gt;
+#include &lt;asterisk/abstract_jb.h&gt;
+#include &lt;asterisk/logger.h&gt;
+#include &lt;asterisk/utils.h&gt;
+#endif /* ASTERISK_VERSION_1_4 */
+#ifdef ASTERISK_VERSION_1_2
+#include &lt;asterisk/utils.h&gt;
+#include &lt;asterisk/logger.h&gt;
+#endif /* ASTERISK_VERSION_1_2 */
+#ifdef HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+#include &quot;celliax_spandsp.h&quot;
+#ifdef CELLIAX_LIBCSV
+#include &quot;celliax_libcsv.h&quot;
+#endif /* CELLIAX_LIBCSV */
+#ifdef __CYGWIN__
+#include &lt;windows.h&gt;
+#endif /* __CYGWIN__ */
+#ifndef AST_DIGIT_ANYDIG
+#define AST_DIGIT_ANYDIG &quot;0123456789*#&quot;
+#else
+#warning Please review Celliax AST_DIGIT_ANYDIG
+#endif
+#ifndef _ASTERISK_H
+#define AST_CONFIG_MAX_PATH 255 /* defined in asterisk.h, but some asterisk-devel package do not contains asterisk.h */
+extern char ast_config_AST_CONFIG_DIR[AST_CONFIG_MAX_PATH];
+int ast_register_atexit(void (*func) (void));   /* in asterisk.h, but some asterisk-devel package do not contains asterisk.h */
+void ast_unregister_atexit(void (*func) (void));    /* in asterisk.h, but some asterisk-devel package do not contains asterisk.h */
+#endif
+#ifdef CELLIAX_ALSA
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include &lt;alsa/asoundlib.h&gt;
+#endif /* CELLIAX_ALSA */
+
+/* DEFINITIONS */
+/* 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&lt;&lt;9)
+#define        BOOST_MAX        40          /* slightly less than 7 bits */
+/* call flow from the device */
+#define         FBUS2_OUTGOING_ACK   999
+#define         FBUS2_SECURITY_COMMAND_ON   444
+#define         CALLFLOW_CALL_IDLE  AST_STATE_DOWN
+#define         CALLFLOW_INCOMING_RING  AST_STATE_RING
+#define         CALLFLOW_CALL_DIALING   AST_STATE_DIALING
+#define         CALLFLOW_CALL_LINEBUSY   AST_STATE_BUSY
+#define         CALLFLOW_CALL_ACTIVE   300
+#define         CALLFLOW_INCOMING_HANGUP   100
+#define         CALLFLOW_CALL_RELEASED   101
+#define         CALLFLOW_CALL_NOCARRIER   102
+#define         CALLFLOW_CALL_INFLUX   103
+#define         CALLFLOW_CALL_INCOMING   104
+#define         CALLFLOW_CALL_FAILED   105
+#define         CALLFLOW_CALL_NOSERVICE   106
+#define         CALLFLOW_CALL_OUTGOINGRESTRICTED   107
+#define         CALLFLOW_CALL_SECURITYFAIL   108
+#define         CALLFLOW_CALL_NOANSWER   109
+  //fixme CALLFLOW_GOT_IMEI to be removed
+#define         CALLFLOW_GOT_IMEI   1009
+  //fixme CALLFLOW_INCOMING_CALLID to be removed
+#define         CALLFLOW_INCOMING_CALLID   1019
+#define         AT_OK   0
+#define         AT_ERROR   1
+/* FBUS2 (old Nokia phones) undocumented proprietary protocol */
+#define         FBUS2_ACK_BYTE   0x7f
+#define         FBUS2_CALL_CALLID   0x05
+#define         FBUS2_CALL_HANGUP   0x04
+#define         FBUS2_CALL_STATUS_OFF   0x01
+#define         FBUS2_CALL_STATUS_ON   0x02
+#define         FBUS2_COMMAND_BYTE_1   0x00
+#define         FBUS2_COMMAND_BYTE_2   0x01
+#define         FBUS2_DEVICE_PC   0x0c
+#define         FBUS2_DEVICE_PHONE   0x00
+#define         FBUS2_IRDA_FRAME_ID   0x1c
+#define         FBUS2_IS_LAST_FRAME   0x01
+#define         FBUS2_MAX_TRANSMIT_LENGTH   120
+#define         FBUS2_NETWORK_STATUS_REGISTERED   0x71
+#define         FBUS2_SECURIY_CALL_COMMAND_ANSWER   0x02
+#define         FBUS2_SECURIY_CALL_COMMAND_CALL   0x01
+#define         FBUS2_SECURIY_CALL_COMMAND_RELEASE   0x03
+#define         FBUS2_SECURIY_CALL_COMMANDS   0x7c
+#define         FBUS2_SECURIY_EXTENDED_COMMAND_ON   0x01
+#define         FBUS2_SECURIY_EXTENDED_COMMANDS   0x64
+#define         FBUS2_SECURIY_IMEI_COMMAND_GET   0x00
+#define         FBUS2_SECURIY_IMEI_COMMANDS   0x66
+#define         FBUS2_SEQNUM_MAX   0x47
+#define         FBUS2_SEQNUM_MIN   0x40
+#define         FBUS2_SERIAL_FRAME_ID   0x1e
+#define         FBUS2_SMS_INCOMING   0x10
+#define         FBUS2_TYPE_CALL   0x01
+#define         FBUS2_TYPE_CALL_DIVERT   0x06
+#define         FBUS2_TYPE_CALL_STATUS   0x0d
+#define         FBUS2_TYPE_NETWORK_STATUS   0x0a
+#define         FBUS2_TYPE_SECURITY   0x40
+#define         FBUS2_TYPE_SMS   0x02
+#define         FBUS2_TYPE_MODEL_ASK   0xd1
+#define   FBUS2_TYPE_MODEL_ANSWER   0xd2
+//#define   FBUS2_TYPE_MODEL_ANSWER   0xffffffd2
+#ifdef CELLIAX_CVM
+#define   CVM_BUSMAIL_SEQNUM_MAX   0x7
+#endif /* CELLIAX_CVM */
+/* debugging bitmask */
+#define DEBUG_SOUND 1
+#define DEBUG_SERIAL 2
+#define DEBUG_SKYPE 4
+#define DEBUG_AT 8
+#define DEBUG_FBUS2 16
+#define DEBUG_CALL 32
+#define DEBUG_LOCKS 64
+#define DEBUG_PBX 128
+#define DEBUG_MONITORLOCKS 256
+#ifndef CELLIAX_CVM
+#define DEBUG_ALL DEBUG_SOUND|DEBUG_SERIAL|DEBUG_SKYPE|DEBUG_AT|DEBUG_FBUS2|DEBUG_CALL|DEBUG_PBX|DEBUG_LOCKS|DEBUG_MONITORLOCKS
+#else
+#define DEBUG_CVM 512
+#define DEBUG_ALL DEBUG_SOUND|DEBUG_SERIAL|DEBUG_SKYPE|DEBUG_AT|DEBUG_FBUS2|DEBUG_CALL|DEBUG_PBX|DEBUG_LOCKS|DEBUG_MONITORLOCKS|DEBUG_CVM
+#endif /* CELLIAX_CVM */
+/* wrappers for ast_log */
+#define DEBUGA_SOUND(...)  if (celliax_debug &amp; DEBUG_SOUND) ast_log(LOG_DEBUG,                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_SOUND  %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define DEBUGA_SERIAL(...)  if (celliax_debug &amp; DEBUG_SERIAL) ast_log(LOG_DEBUG,         &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_SERIAL %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define DEBUGA_SKYPE(...)  if (celliax_debug &amp; DEBUG_SKYPE) ast_log(LOG_DEBUG,                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_SKYPE  %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define DEBUGA_AT(...)  if (celliax_debug &amp; DEBUG_AT) ast_log(LOG_DEBUG,                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_AT     %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define DEBUGA_FBUS2(...)  if (celliax_debug &amp; DEBUG_FBUS2) ast_log(LOG_DEBUG,                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_FBUS2  %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define DEBUGA_CALL(...)  if (celliax_debug &amp; DEBUG_CALL) ast_log(LOG_DEBUG,                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_CALL   %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define DEBUGA_PBX(...)  if (celliax_debug &amp; DEBUG_PBX) ast_log(LOG_DEBUG,                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_PBX    %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#ifdef CELLIAX_CVM
+#define DEBUGA_CVM(...)  if (celliax_debug &amp; DEBUG_CVM) ast_log(LOG_DEBUG,  &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_CVM    %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#endif /* CELLIAX_CVM */
+#define ERRORA(...)  ast_log(LOG_ERROR,                                                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][ERROR        %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define NOTICA(...)  ast_log(LOG_NOTICE,                                                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][NOTICE      %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+#define WARNINGA(...)  ast_log(LOG_WARNING,                                                 &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][WARNING    %-5d][%-10s][%2d,%2d,%2d] &quot; __VA_ARGS__ );
+/* macros for logging */
+#define CELLIAX_P_LOG p ? p-&gt;owner : NULL, (unsigned long)pthread_self(), __LINE__, p ? p-&gt;name ? p-&gt;name : &quot;none&quot; : &quot;none&quot;, p ? p-&gt;owner ? p-&gt;owner-&gt;_state : -1 : -1,  p ? p-&gt;interface_state : -1, p ? p-&gt;phone_callflow : -1
+#define CELLIAX_TMP_LOG tmp ? tmp-&gt;owner : NULL, (unsigned long)pthread_self(), __LINE__, tmp ? tmp-&gt;name ? tmp-&gt;name : &quot;none&quot; : &quot;none&quot;, tmp ? tmp-&gt;owner ? tmp-&gt;owner-&gt;_state : -1 : -1,  tmp ? tmp-&gt;interface_state : -1, tmp ? tmp-&gt;phone_callflow : -1
+/* logging wrappers for ast_mutex_lock and ast_mutex_unlock */
+#define LOKKA(x)  if (celliax_debug &amp; DEBUG_LOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] going to lock %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; :  x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;); if (ast_mutex_lock(x)) ast_log(LOG_ERROR, &quot;ast_mutex_lock failed, BAD\n&quot;);   if (celliax_debug &amp; DEBUG_LOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] locked %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;);
+#define UNLOCKA(x)  if (celliax_debug &amp; DEBUG_LOCKS)  ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] going to unlock %p (%s)\n&quot;,  CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;); if (ast_mutex_unlock(x)) ast_log(LOG_ERROR, &quot;ast_mutex_lock failed, BAD\n&quot;);   if (celliax_debug &amp; DEBUG_LOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] unlocked %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;);
+#define CVM_LOKKA(x)  if (celliax_debug &amp; DEBUG_LOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] going to lock %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; :  x == &amp;p-&gt;cvm_busmail_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;); if (ast_mutex_lock(x)) ast_log(LOG_ERROR, &quot;ast_mutex_lock failed, BAD\n&quot;);   if (celliax_debug &amp; DEBUG_LOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] locked %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;cvm_busmail_outgoing_list_lock ? &quot;CVM_BUSMAIL_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;);
+#define CVM_UNLOCKA(x)  if (celliax_debug &amp; DEBUG_LOCKS)  ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] going to unlock %p (%s)\n&quot;,  CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;cvm_busmail_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;); if (ast_mutex_unlock(x)) ast_log(LOG_ERROR, &quot;ast_mutex_lock failed, BAD\n&quot;);   if (celliax_debug &amp; DEBUG_LOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_LOCKS  %-5d][%-10s][%2d,%2d,%2d] unlocked %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;cvm_busmail_outgoing_list_lock ? &quot;CVM_BUSMAIL_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;);
+#define PUSHA_UNLOCKA(x)    pthread_cleanup_push(celliax_unlocka_log, (void *) x);
+#define POPPA_UNLOCKA(x)    pthread_cleanup_pop(0);
+#define MONITORLOKKA(x)  if (celliax_debug &amp; DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_MONITORLOCKS  %-5d][%-10s][%2d,%2d,%2d] going to lock %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; :  x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;); if (ast_mutex_lock(x)) ast_log(LOG_ERROR, &quot;ast_mutex_lock failed, BAD\n&quot;);   if (celliax_debug &amp; DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_MONITORLOCKS  %-5d][%-10s][%2d,%2d,%2d] locked %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;);
+#define MONITORUNLOCKA(x)  if (celliax_debug &amp; DEBUG_MONITORLOCKS)  ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_MONITORLOCKS  %-5d][%-10s][%2d,%2d,%2d] going to unlock %p (%s)\n&quot;,  CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;); if (ast_mutex_unlock(x)) ast_log(LOG_ERROR, &quot;ast_mutex_lock failed, BAD\n&quot;);   if (celliax_debug &amp; DEBUG_MONITORLOCKS) ast_log(LOG_DEBUG, &quot;rev &quot;CELLIAX_SVN_VERSION &quot;[%p|%-7lx][DEBUG_MONITORLOCKS  %-5d][%-10s][%2d,%2d,%2d] unlocked %p (%s)\n&quot;, CELLIAX_P_LOG, x, x == &amp;celliax_monlock ? &quot;MONLOCK&quot; : x == &amp;celliax_iflock ? &quot;IFLOCK&quot; : x == &amp;celliax_usecnt_lock ? &quot;USECNT_LOCK&quot; : x == &amp;p-&gt;controldev_lock ? &quot;CONTROLDEV_LOCK&quot; : x == &amp;p-&gt;fbus2_outgoing_list_lock ? &quot;FBUS2_OUTGOING_LIST_LOCK&quot; : &quot;?????&quot;);
+/* macros used for config file parsing */
+#define         M_BOOL(tag, dst)   M_F(tag, (dst) = ast_true(__val) )
+#define         M_END(x)   x;
+#define         M_F(tag, f)   if (!strcasecmp((__s), tag)) { f; } else
+#ifdef ASTERISK_VERSION_1_6_0
+#define         M_START(var, val)   const char *__s = var; const char *__val = val;
+#else
+#define         M_START(var, val)   char *__s = var; char *__val = val;
+#endif /* ASTERISK_VERSION_1_6_0 */
+#define         M_STR(tag, dst)   M_F(tag, ast_copy_string(dst, __val, sizeof(dst)))
+#define         M_UINT(tag, dst)   M_F(tag, (dst) = strtoul(__val, NULL, 0) )
+/* which protocol we use to control the phone through serial device */
+#ifdef CELLIAX_CVM
+#define   PROTOCOL_CVM_BUSMAIL   5
+#endif /* CELLIAX_CVM */
+#define         PROTOCOL_ALSA_VOICEMODEM   4
+#define         PROTOCOL_AT   2
+#define         PROTOCOL_FBUS2   1
+#define         PROTOCOL_NO_SERIAL   3
+#ifndef GIOVA48
+#define         CELLIAX_FRAME_SIZE   160
+#else //GIOVA48
+#define         CELLIAX_FRAME_SIZE   960
+#endif //GIOVA48
+#define                AT_BUFSIZ 8192
+#define AT_MESG_MAX_LENGTH 2048 /* much more than 10 SMSs */
+#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 */
+
+#ifdef CELLIAX_CVM
+/* MAIL PRIMITIVES */
+/* CVM -&gt; CELLIAX */
+#define API_PP_LOCKED_IND 0x8558    //PP locked with FP
+#define API_PP_UNLOCKED_IND 0x8559  //PP out of service, unlocked from FP
+
+#define API_PP_SETUP_IND 0x8574 //Incoming call to PP
+
+#define API_PP_SETUP_IND_CALL_TYPE_OFFSET 0x0
+#define API_PP_SETUP_IND_RING_TYPE_OFFSET 0x1
+
+#define API_PP_SETUP_IND_CALL_EXT 0x0
+#define API_PP_SETUP_IND_CALL_INT 0x1
+
+#define API_PP_SETUP_IND_RING_INT_CALL 0x40
+#define API_PP_SETUP_IND_RING_PAGE_ALL 0x46
+
+#define API_PP_SETUP_ACK_IND 0x857F //internal connection established with FPs, waiting for handsetnumber
+
+#define API_PP_CONNECT_IND 0x8576   //air-link established with FPs
+#define API_PP_CONNECT_CFM 0x8578   //PP answered incoming call
+
+#define API_PP_ALERT_IND 0x8581
+#define API_PP_ALERT_ON_IND 0x857D
+#define API_PP_ALERT_OFF_IND 0x857E
+
+#define API_PP_SIGNAL_ON_IND 0x2F9C
+#define API_PP_SIGNAL_OFF_IND 0x2F9D
+
+#define API_PP_RELEASE_IND 0x857B
+#define API_PP_RELEASE_CFM 0x857A
+#define API_PP_REJECT_IND 0x8564
+
+#define API_PP_ACCESS_RIGHTS_CFM 0x8568
+#define API_PP_ACCESS_RIGHTS_REJ 0x8569
+
+#define API_PP_DELETE_SUBS_CFM 0x8561
+#define API_PP_REMOTE_DELETE_SUBS_CFM 0x2F9F
+
+#define API_PP_CLIP_IND 0x2F93
+#define API_PP_SW_STATUS_IND 0x2FC2
+#define API_PP_MESSAGE_WAITING_IND 0x2FA1
+
+#define CVM_PP_PLUG_STATUS_IND 0x2F4F
+#define CVM_PP_LINE_STATUS_IND 0x2F53
+#define CVM_PP_ON_KEY_IND 0x2F64
+
+#define API_PP_READ_RSSI_CFM 0x2FC7
+#define API_PP_ALERT_BROADCAST_IND 0x2FA3
+
+/* CELLIAX -&gt; CVM */
+#define API_PP_LOCK_REQ 0x8554  //select FP to lock once
+#define API_PP_SETUP_REQ 0x8571 //setup air-link PP&lt;-&gt;FP
+
+#define API_PP_KEYPAD_REQ 0x858A    //send string for dialing
+
+#define API_PP_CONNECT_REQ 0x8577   //answer incoming call
+
+#define API_PP_ALERT_REQ 0x2F8D //inform FP that alering is started
+
+#define API_PP_RELEASE_REQ 0x8579   //release connection
+#define API_PP_RELEASE_RES 0x857C   //confirm FP initiated release of connection
+#define API_PP_REJECT_REQ 0x8565    //PP reject incoming call
+
+#define API_PP_ACCESS_RIGHTS_REQ 0x8566 //init registration to FP
+#define API_PP_DELETE_SUBS_REQ 0x8560   //deregister from FP (locally only in PP)
+#define API_PP_REMOTE_DELETE_SUBS_REQ 0x2F9E    //remotly deregister from FP
+
+#define API_PP_STOP_PROTOCOL_REQ 0x2FC4 //stop protocol from running  (even registration)
+
+#define API_PP_READ_RSSI_REQ 0x2FC6 //RSSI readout request
+#define API_PP_READ_RSSI_CFM 0x2FC7 //RSSI readout result
+
+#define CVM_PP_AUDIO_OPEN_REQ 0x2F0E    //Enable audio
+#define CVM_PP_AUDIO_CLOSE_REQ 0x2F0F   //Disable audio
+
+#define CVM_PP_AUDIO_SET_VOLUME_REQ 0x2F1D  //set volume
+
+#define CVM_PP_AUDIO_UNMUTE_MIC_REQ 0x2F1A  //unmute mic
+#define CVM_PP_AUDIO_MUTE_MIC_REQ 0x2F19    //mute mic
+
+#define CVM_PP_AUDIO_HS_PLUG_IND 0x2F1C //mute mic
+
+#define CVM_PP_AUDIO_OPEN_ADPCM_OFF_REQ 0x2F68  //open audio even before making connection
+
+/* END OF MAIL PRIMITIVES */
+
+enum CvmLockState {
+  CVM_UNKNOWN_LOCK_STATE = 0,
+  CVM_UNLOCKED_TO_FP,
+  CVM_LOCKED_TO_FP
+};
+
+enum CvmRegisterState {
+  CVM_UNKNOWN_REGISTER_STATE = 0,
+  CVM_UNREGISTERED_TO_FP,
+  CVM_REGISTERED_TO_FP
+};
+
+#define BUSMAIL_MAIL_MAX_PARAMS_LENGTH 128
+#define BUSMAIL_MAX_FRAME_LENGTH (BUSMAIL_MAIL_MAX_PARAMS_LENGTH + 9)
+
+#define BUSMAIL_OFFSET_SOF 0
+#define BUSMAIL_OFFSET_LEN_MSB 1
+#define BUSMAIL_OFFSET_LEN_LSB 2
+#define BUSMAIL_OFFSET_HEADER 3
+#define BUSMAIL_OFFSET_MAIL BUSMAIL_OFFSET_MAIL_PROGRAM_ID
+#define BUSMAIL_OFFSET_MAIL_PROGRAM_ID 4
+#define BUSMAIL_OFFSET_MAIL_TASK_ID 5
+#define BUSMAIL_OFFSET_MAIL_PRIMITIVE_MSB 7
+#define BUSMAIL_OFFSET_MAIL_PRIMITIVE_LSB 6
+#define BUSMAIL_OFFSET_MAIL_PARAMS 8
+
+#define BUSMAIL_MAIL_PRIMITIVE_MSB 1
+#define BUSMAIL_MAIL_PRIMITIVE_LSB 0
+#define BUSMAIL_LEN_MSB 0
+#define BUSMAIL_LEN_LSB 1
+
+#define BUSMAIL_SOF 0x10
+
+#define BUSMAIL_MAIL_PROGRAM_ID 0x0
+#define BUSMAIL_MAIL_USERTASK_TASK_ID 0x0f
+#define BUSMAIL_MAIL_TBHANDLE_TASK_ID 0x0
+#define BUSMAIL_MAIL_TASK_ID BUSMAIL_MAIL_USERTASK_TASK_ID
+
+#define BUSMAIL_HEADER_IC_BIT_MASK 0x80
+#define BUSMAIL_HEADER_SU_BIT_MASK 0x40
+#define BUSMAIL_HEADER_PF_BIT_MASK 0x08
+#define BUSMAIL_HEADER_TXSEQ_MASK 0x70
+#define BUSMAIL_HEADER_RXSEQ_MASK 0x07
+#define BUSMAIL_HEADER_SUID_MASK 0x30
+#define BUSMAIL_HEADER_UNID_MASK BUSMAIL_HEADER_SUID_MASK
+
+#define BUSMAIL_HEADER_INFO_FRAME 0x0
+#define BUSMAIL_HEADER_CTRL_FRAME 0x80
+#define BUSMAIL_HEADER_CTRL_SU_FRAME 0x0
+#define BUSMAIL_HEADER_CTRL_UN_FRAME 0x40
+
+#define BUSMAIL_HEADER_UNID_SABM 0x0
+#define BUSMAIL_HEADER_SUID_RR 0x0
+#define BUSMAIL_HEADER_SUID_REJ 0x10
+#define BUSMAIL_HEADER_SUID_RNR 0x20
+
+#define BUSMAIL_HEADER_SABM_MASK (BUSMAIL_HEADER_IC_BIT_MASK | BUSMAIL_HEADER_SU_BIT_MASK | BUSMAIL_HEADER_UNID_MASK)
+#define BUSMAIL_HEADER_SABM (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_UN_FRAME | BUSMAIL_HEADER_UNID_SABM)
+
+#define BUSMAIL_HEADER_REJ_MASK (BUSMAIL_HEADER_IC_BIT_MASK | BUSMAIL_HEADER_SU_BIT_MASK | BUSMAIL_HEADER_SUID_MASK)
+#define BUSMAIL_HEADER_REJ (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME | BUSMAIL_HEADER_SUID_REJ)
+
+#define BUSMAIL_HEADER_SU_FRAME_MASK (BUSMAIL_HEADER_IC_BIT_MASK | BUSMAIL_HEADER_SU_BIT_MASK)
+#define BUSMAIL_HEADER_SU_FRAME (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME)
+
+/*! 
+ * \brief structure holding raw data to be send throught serial
+ */
+struct cvm_busmail_msg {
+  int valid;
+  unsigned char busmail_msg_buffer[BUSMAIL_MAX_FRAME_LENGTH];
+  unsigned int busmail_msg_len;
+
+  unsigned int tv_sec;
+  unsigned int tv_usec;
+
+  unsigned char txseqno;
+  int acknowledged;
+  int how_many_sent;
+  int sent;
+
+  struct cvm_busmail_msg *next;
+  struct cvm_busmail_msg *previous;
+};
+
+/*! 
+ * \brief structure holding busmail frame, for internal use
+ */
+struct cvm_busmail_frame {
+  unsigned char busmail_sof;
+  unsigned char busmail_len[2];
+  unsigned char busmail_header;
+  unsigned char busmail_mail_program_id;
+  unsigned char busmail_mail_task_id;
+  unsigned char busmail_mail_primitive[2];
+  unsigned char busmail_mail_params_buffer[BUSMAIL_MAIL_MAX_PARAMS_LENGTH];
+  unsigned int busmail_mail_params_buffer_len;
+  unsigned char busmail_crc;
+};
+#endif /* CELLIAX_CVM */
+
+/* CELLIAX INTERNAL STRUCTS */
+
+/*! 
+ * \brief structure for the linked list of FBUS2 Nokia proprietary protocol messages
+ */
+struct fbus2_msg {
+  int msg;
+  int seqnum;
+  int len;
+  int acknowledged;
+  int how_many_sent;
+  int sent;
+  unsigned int tv_sec;
+  unsigned int tv_usec;
+  unsigned char buffer[FBUS2_MAX_TRANSMIT_LENGTH + 10];
+  struct fbus2_msg *next;
+  struct fbus2_msg *previous;
+};
+
+/*! 
+ * \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];
+};
+
+/*! 
+ * \brief PVT structure for a celliax interface (channel), created by celliax_mkif
+ */
+struct celliax_pvt {
+  char *name;                   /*!&lt; \brief 'name' of the interface (channel) */
+  int interface_state;          /*!&lt; \brief 'state' of the interface (channel) */
+  int phone_callflow;           /*!&lt; \brief 'callflow' of the phone interface (as opposed to skype interface) */
+  struct ast_channel *owner;    /*!&lt; \brief channel we belong to, possibly NULL */
+  struct celliax_pvt *next;     /*!&lt; \brief Next interface (channel) in list */
+  int readpos;                  /*!&lt; \brief read position above */
+  struct ast_frame read_f;      /*!&lt; \brief returned by oss_read */
+  char context[AST_MAX_EXTENSION];  /*!&lt; \brief default Asterisk dialplan context for this interface */
+  char language[MAX_LANGUAGE];  /*!&lt; \brief default Asterisk dialplan language for this interface */
+  char exten[AST_MAX_EXTENSION];    /*!&lt; \brief default Asterisk dialplan extension for this interface */
+  struct ast_dsp *dsp;          /*!&lt; \brief Used for in-band DTMF detection */
+  int celliax_sound_rate;       /*!&lt; \brief rate of the sound device, in Hz, eg: 8000 */
+  int celliax_sound_capt_fd;    /*!&lt; \brief file descriptor for sound capture dev */
+  char controldevice_name[50];  /*!&lt; \brief name of the serial device controlling the interface, possibly none */
+  int controldevprotocol;       /*!&lt; \brief which protocol is used for serial control of this interface */
+  char controldevprotocolname[50];  /*!&lt; \brief name of the serial device controlling protocol, one of &quot;at&quot; &quot;fbus2&quot; &quot;no_serial&quot; &quot;alsa_voicemodem&quot; */
+  int controldevfd;             /*!&lt; \brief serial controlling file descriptor for this interface */
+  char callid_name[50];
+  char callid_number[50];
+  unsigned char rxm[255];       /*!&lt; \brief read buffer for FBUS2 serial protocol controlling Nokia phones */
+  unsigned char array[255];     /*!&lt; \brief read buffer for FBUS2 serial protocol controlling Nokia phones */
+  int arraycounter;             /*!&lt; \brief position in the 'array' read buffer for FBUS2 serial protocol controlling Nokia phones */
+  int seqnumfbus;               /*!&lt; \brief sequential number of FBUS2 messages, hex, revolving */
+  pthread_t controldev_thread;  /*!&lt; \brief serial control thread for this interface, running during the call */
+  struct fbus2_msg *fbus2_outgoing_list;    /*!&lt; \brief list used to track FBUS2 traffic acknowledgement and resending */
+  ast_mutex_t fbus2_outgoing_list_lock;
+  int dsp_silence_threshold;
+  int need_acoustic_ring;       /*!&lt; \brief bool, this interface get the incoming ring from soundcard, not serial */
+  char oss_write_buf[CELLIAX_FRAME_SIZE * 2];
+  int oss_write_dst;
+  char oss_read_buf[CELLIAX_FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];  /*!&lt; in bytes */
+  time_t celliax_serial_synced_timestamp;
+  time_t celliax_serial_sync_period;
+  time_t audio_play_reset_timestamp;
+  time_t audio_capture_reset_timestamp;
+  speed_t controldevice_speed;
+  struct s_result line_array;
+  struct timeval ringtime;
+  struct timeval call_incoming_time;
+  int at_result;
+
+  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;
+
+  double playback_boost;
+  double capture_boost;
+  int stripmsd;
+  int controldev_dead;
+  ast_mutex_t controldev_lock;
+  struct timeval fbus2_list_tv;
+  struct timezone fbus2_list_tz;
+  dtmf_rx_state_t dtmf_state;
+  int dtmf_inited;
+  pthread_t sync_thread;
+  pthread_t celliax_sound_monitor_thread;
+  pthread_t celliax_serial_monitor_thread;
+  int celliax_serial_monitoring;
+  int skype;                    /*!&lt; \brief config flag, bool, Skype support on this interface (0 if false, -1 if true) */
+  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;
+  int celliax_dir_entry_extension_prefix;
+#ifdef CELLIAX_CVM
+  char cvm_subsc_1_pin[20];
+  char cvm_subsc_2_pin[20];
+  int cvm_subsc_no;
+  int cvm_lock_state;
+  int cvm_register_state;
+  int cvm_volume_level;
+  int cvm_celliax_serial_delay;
+  unsigned char cvm_handset_no;
+  unsigned char cvm_fp_is_cvm;
+  unsigned char cvm_rssi;
+
+  unsigned char busmail_rxseq_cvm_last; /*!&lt; \brief sequential number of BUSMAIL messages, (0-7) */
+  unsigned char busmail_txseq_celliax_last; /*!&lt; \brief sequential number of BUSMAIL messages, (0-7) */
+
+  struct cvm_busmail_msg *cvm_busmail_outgoing_list;    /*!&lt; \brief list used to track CVM BUSMAIL traffic acknowledgement and resending */
+  ast_mutex_t cvm_busmail_outgoing_list_lock;
+
+  struct timeval cvm_busmail_list_tv;
+  struct timezone cvm_busmail_list_tz;
+#endif                          /* CELLIAX_CVM */
+#ifdef CELLIAX_LIBCSV
+  int csv_separator_is_semicolon;
+  int csv_complete_name_pos;
+  int csv_email_pos;
+  int csv_home_phone_pos;
+  int csv_mobile_phone_pos;
+  int csv_business_phone_pos;
+  int csv_first_row_is_title;
+  int csv_fields;
+  int csv_rows;
+  char csv_complete_name[256];
+  char csv_email[256];
+  char csv_home_phone[256];
+  char csv_mobile_phone[256];
+  char csv_business_phone[256];
+#endif                          /* CELLIAX_LIBCSV */
+  int audio_play_reset_period;
+
+  char at_cmgw[16];
+
+  int isInputInterleaved;
+  int isOutputInterleaved;
+  int numInputChannels;
+  int numOutputChannels;
+  int framesPerCallback;
+#ifndef CELLIAX_ALSA
+  PABLIO_Stream *stream;
+#endif /* CELLIAX_ALSA */
+  int audiopipe[2];
+  int speexecho;
+  int speexpreprocess;
+  int portaudiocindex;          /*!&lt; \brief Index of the Portaudio capture audio device */
+  int portaudiopindex;          /*!&lt; \brief Index of the Portaudio playback audio device */
+  int control_to_send;
+  int unread_sms_msg_id;
+  int reading_sms_msg;
+  char sms_message[4800];
+  int sms_cnmi_not_supported;
+  char sms_receiving_program[256];
+  int celliax_dir_prefix;
+  int no_ucs2;
+#ifdef CELLIAX_ALSA
+  snd_pcm_t *alsac;             /*!&lt; \brief handle of the ALSA capture audio device */
+  snd_pcm_t *alsap;             /*!&lt; \brief handle of the ALSA playback audio device */
+  char alsacname[50];           /*!&lt; \brief name of the ALSA capture audio device */
+  char alsapname[50];           /*!&lt; \brief name of the ALSA playback audio device */
+  int alsa_period_size;         /*!&lt; \brief ALSA period_size, in byte */
+  int alsa_periods_in_buffer;   /*!&lt; \brief how many periods in ALSA buffer, to calculate buffer_size */
+  unsigned long int alsa_buffer_size;   /*!&lt; \brief ALSA buffer_size, in byte */
+  int alsawrite_filled;
+  int alsa_capture_is_mono;
+  int alsa_play_is_mono;
+  struct pollfd pfd;
+#endif                          /* CELLIAX_ALSA */
+
+  struct timeval dtmf_timestamp;
+};
+
+/* LOCKS */
+/*! \brief Protect the celliax_usecnt */
+AST_MUTEX_DEFINE_STATIC(celliax_usecnt_lock);
+/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
+ *    when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(celliax_monlock);
+/*! \brief Protect the interfaces list */
+AST_MUTEX_DEFINE_STATIC(celliax_iflock);
+
+/* FUNCTIONS */
+
+/* module helpers functions */
+int load_module(void);
+int unload_module(void);
+int usecount(void);
+char *description(void);
+char *key(void);
+
+/* chan_celliax internal functions */
+void celliax_unlocka_log(void *x);
+#ifdef CELLIAX_FBUS2
+int celliax_serial_sync_FBUS2(struct celliax_pvt *p);
+int celliax_serial_answer_FBUS2(struct celliax_pvt *p);
+int celliax_serial_call_FBUS2(struct celliax_pvt *p, char *dstr);
+int celliax_serial_hangup_FBUS2(struct celliax_pvt *p);
+int celliax_serial_config_FBUS2(struct celliax_pvt *p);
+int celliax_serial_read_FBUS2(struct celliax_pvt *p);
+int celliax_serial_getstatus_FBUS2(struct celliax_pvt *p);
+int celliax_serial_get_seqnum_FBUS2(struct celliax_pvt *p);
+int celliax_serial_security_command_FBUS2(struct celliax_pvt *p);
+int celliax_serial_send_FBUS2(struct celliax_pvt *p, int len, unsigned char *buffer2);
+int celliax_serial_list_acknowledge_FBUS2(struct celliax_pvt *p, int seqnum);
+int celliax_serial_send_if_time_FBUS2(struct celliax_pvt *p);
+int celliax_serial_write_FBUS2(struct celliax_pvt *p, unsigned char *MsgBuffer,
+                               int MsgLength, unsigned char MsgType);
+int celliax_serial_send_ack_FBUS2(struct celliax_pvt *p, unsigned char MsgType,
+                                  unsigned char MsgSequence);
+struct fbus2_msg *celliax_serial_list_init_FBUS2(struct celliax_pvt *p);
+int celliax_serial_list_print_FBUS2(struct celliax_pvt *p, struct fbus2_msg *list);
+
+#endif /* CELLIAX_FBUS2 */
+
+#ifdef CELLIAX_CVM
+int celliax_serial_sync_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_answer_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_call_CVM_BUSMAIL(struct celliax_pvt *p, char *dstr);
+int celliax_serial_hangup_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_config_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_read_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_getstatus_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_send_CVM_BUSMAIL(struct celliax_pvt *p, int len,
+                                    unsigned char *mesg_ptr);
+int celliax_serial_list_acknowledge_CVM_BUSMAIL(struct celliax_pvt *p,
+                                                unsigned char TxSeqNo);
+int celliax_serial_send_if_time_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_write_CVM_BUSMAIL(struct celliax_pvt *p,
+                                     struct cvm_busmail_frame *busmail_frame);
+int celliax_serial_send_ctrl_frame_CVM_BUSMAIL(struct celliax_pvt *p,
+                                               unsigned char FrameType);
+int celliax_serial_send_info_frame_CVM_BUSMAIL(struct celliax_pvt *p, int FrameType,
+                                               unsigned char ParamsLen,
+                                               unsigned char *Params);
+struct cvm_busmail_msg *celliax_serial_list_init_CVM_BUSMAIL(struct celliax_pvt *p);
+int celliax_serial_list_print_CVM_BUSMAIL(struct celliax_pvt *p,
+                                          struct cvm_busmail_msg *list);
+int celliax_serial_lists_free_CVM_BUSMAIL(struct celliax_pvt *p);
+
+#endif /* CELLIAX_CVM */
+
+/* CHAN_CELLIAX.C */
+int celliax_queue_control(struct ast_channel *chan, int control);
+struct celliax_pvt *celliax_console_find_desc(char *dev);
+int celliax_serial_call(struct celliax_pvt *p, char *dstr);
+
+/* FUNCTIONS */
+/* PBX interface functions */
+struct ast_channel *celliax_request(const char *type, int format, void *data, int *cause);
+int celliax_answer(struct ast_channel *c);
+int celliax_hangup(struct ast_channel *c);
+int celliax_call(struct ast_channel *c, char *idest, int timeout);
+struct ast_frame *celliax_read(struct ast_channel *chan);
+int celliax_write(struct ast_channel *c, struct ast_frame *f);
+int celliax_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+#ifndef ASTERISK_VERSION_1_4
+int celliax_indicate(struct ast_channel *c, int cond);
+#else
+int celliax_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen);
+#endif
+int celliax_devicestate(void *data);
+#ifdef ASTERISK_VERSION_1_4
+int celliax_senddigit_begin(struct ast_channel *ast, char digit);
+int celliax_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
+#else /* ASTERISK_VERSION_1_4 */
+int celliax_senddigit(struct ast_channel *ast, char digit);
+#endif /* ASTERISK_VERSION_1_4 */
+
+/* chan_celliax internal functions */
+
+struct celliax_pvt *celliax_mkif(struct ast_config *cfg, char *ctg,
+                                 int is_first_category);
+struct ast_channel *celliax_new(struct celliax_pvt *p, int state, char *context);
+int celliax_restart_monitor(void);
+void *celliax_do_monitor(void *data);
+void *celliax_do_audio_monitor(void *data);
+int celliax_sound_boost(struct ast_frame *f, double boost);
+int celliax_sound_init(struct celliax_pvt *p);
+int celliax_sound_shutdown(struct celliax_pvt *p);
+struct ast_frame *celliax_sound_dsp_analize(struct celliax_pvt *p, struct ast_frame *f,
+                                            int dsp_silence_threshold);
+int celliax_sound_dsp_set(struct celliax_pvt *p, int dsp_silence_threshold,
+                          int silence_suppression);
+struct ast_frame *celliax_sound_read(struct celliax_pvt *p);
+int celliax_sound_write(struct celliax_pvt *p, struct ast_frame *f);
+int celliax_sound_monitor(struct celliax_pvt *p);
+void *celliax_do_controldev_thread(void *data);
+int celliax_serial_init(struct celliax_pvt *p, speed_t controldevice_speed);
+int celliax_serial_monitor(struct celliax_pvt *p);
+int celliax_serial_read(struct celliax_pvt *p);
+int celliax_serial_sync(struct celliax_pvt *p);
+int celliax_serial_getstatus(struct celliax_pvt *p);
+int celliax_serial_config(struct celliax_pvt *p);
+int celliax_serial_hangup(struct celliax_pvt *p);
+int celliax_serial_answer(struct celliax_pvt *p);
+
+#define celliax_serial_write_AT_expect(P, D, S) celliax_serial_write_AT_expect1(P, D, S, 1, 2)
+#define celliax_serial_write_AT_expect_noexpcr(P, D, S) celliax_serial_write_AT_expect1(P, D, S, 0, 2)
+#define celliax_serial_write_AT_expect_noexpcr_tout(P, D, S, T) celliax_serial_write_AT_expect1(P, D, S, 0, T)
+// 20.5 sec timeout, used for querying the SIM and sending SMSs
+#define celliax_serial_write_AT_expect_longtime(P, D, S) celliax_serial_write_AT_expect1(P, D, S, 1, 20)
+#define celliax_serial_write_AT_expect_longtime_noexpcr(P, D, S) celliax_serial_write_AT_expect1(P, D, S, 0, 20)
+int celliax_serial_write_AT(struct celliax_pvt *p, const char *data);
+int celliax_serial_write_AT_nocr(struct celliax_pvt *p, const char *data);
+int celliax_serial_write_AT_ack(struct celliax_pvt *p, const char *data);
+int celliax_serial_write_AT_ack_nocr_longtime(struct celliax_pvt *p, const char *data);
+int celliax_serial_write_AT_noack(struct celliax_pvt *p, const char *data);
+int celliax_serial_write_AT_expect1(struct celliax_pvt *p, const char *data,
+                                    const char *expected_string, int expect_crlf,
+                                    int seconds);
+int celliax_serial_AT_expect(struct celliax_pvt *p, const char *expected_string,
+                             int expect_crlf, int seconds);
+int celliax_serial_read_AT(struct celliax_pvt *p, int look_for_ack, int timeout_usec,
+                           int timeout_sec, const char *expected_string, int expect_crlf);
+int celliax_serial_answer_AT(struct celliax_pvt *p);
+int celliax_serial_hangup_AT(struct celliax_pvt *p);
+int celliax_serial_config_AT(struct celliax_pvt *p);
+int celliax_serial_call_AT(struct celliax_pvt *p, char *dstr);
+int celliax_serial_sync_AT(struct celliax_pvt *p);
+int celliax_serial_getstatus_AT(struct celliax_pvt *p);
+
+#ifdef ASTERISK_VERSION_1_6_0
+void celliax_store_boost(const char *s, double *boost);
+#else
+void celliax_store_boost(char *s, double *boost);
+#endif /* ASTERISK_VERSION_1_6_0 */
+int celliax_console_set_active(int fd, int argc, char *argv[]);
+#ifndef ASTERISK_VERSION_1_6_0
+int celliax_console_hangup(int fd, int argc, char *argv[]);
+#else /* ASTERISK_VERSION_1_6_0 */
+char *celliax_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+#endif /*  ASTERISK_VERSION_1_6_0 */
+int celliax_console_playback_boost(int fd, int argc, char *argv[]);
+int celliax_console_capture_boost(int fd, int argc, char *argv[]);
+int celliax_console_celliax(int fd, int argc, char *argv[]);
+#ifdef CELLIAX_DIR
+int celliax_console_celliax_dir_import(int fd, int argc, char *argv[]);
+int celliax_console_celliax_dir_export(int fd, int argc, char *argv[]);
+#endif /* CELLIAX_DIR */
+int celliax_console_dial(int fd, int argc, char *argv[]);
+int celliax_console_sendsms(int fd, int argc, char *argv[]);
+int celliax_portaudio_init(struct celliax_pvt *p);
+int celliax_portaudio_shutdown(struct celliax_pvt *p);
+struct ast_frame *celliax_portaudio_read(struct celliax_pvt *p);
+int celliax_portaudio_write(struct celliax_pvt *p, struct ast_frame *f);
+int celliax_portaudio_devlist(struct celliax_pvt *p);
+#ifdef CELLIAX_DIR
+int celliax_dir_exec(struct ast_channel *chan, void *data);
+int celliax_dir_create_extensions(void);
+int celliax_dir_play_mailbox_owner(struct ast_channel *chan, char *context,
+                                   char *dialcontext, char *ext, char *name);
+struct ast_config *celliax_dir_realtime(char *context);
+int celliax_dir_do(struct ast_channel *chan, struct ast_config *cfg, char *context,
+                   char *dialcontext, char digit, int last);
+#endif /* CELLIAX_DIR */
+#ifdef CELLIAX_LIBCSV
+void celliax_cb1(char *s, size_t len, void *data);
+void celliax_cb2(char c, void *data);
+#endif /* CELLIAX_LIBCSV */
+int celliax_sendsms(struct ast_channel *c, void *data);
+int celliax_console_echo(int fd, int argc, char *argv[]);
+int celliax_console_at(int fd, int argc, char *argv[]);
+#ifdef ASTERISK_VERSION_1_2
+int celliax_manager_sendsms(struct mansession *s, struct message *m);
+#endif //ASTERISK_VERSION_1_2
+#ifdef ASTERISK_VERSION_1_4
+int celliax_manager_sendsms(struct mansession *s, const struct message *m);
+#endif //ASTERISK_VERSION_1_4
+int utf_to_ucs2(struct celliax_pvt *p, char *utf_in, size_t inbytesleft, char *ucs2_out,
+                size_t outbytesleft);
+int ucs2_to_utf8(struct celliax_pvt *p, char *ucs2_in, char *utf8_out,
+                 size_t outbytesleft);
+#endif /* _CELLIAX_H_ */
+#ifdef CELLIAX_ALSA
+int console_alsa_period(int fd, int argc, char *argv[]);
+#endif /* CELLIAX_ALSA */
+#ifdef CELLIAX_ALSA
+int alsa_init(struct celliax_pvt *p);
+int alsa_shutdown(struct celliax_pvt *p);
+snd_pcm_t *alsa_open_dev(struct celliax_pvt *p, snd_pcm_stream_t stream);
+struct ast_frame *alsa_read(struct celliax_pvt *p);
+int alsa_write(struct celliax_pvt *p, struct ast_frame *f);
+#endif /* CELLIAX_ALSA */
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_additionalc"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_additional.c (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_additional.c                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_additional.c        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,7022 @@
</span><ins>+//indent -gnu -ts4 -br -brs -cdw -lp -ce -nbfda -npcs -nprs -npsl -nbbo -saf -sai -saw -cs -bbo -nhnl -nut -sob -l90 
+#include &quot;celliax.h&quot;
+#include &quot;iconv.h&quot;
+
+extern int celliax_debug;
+extern char *celliax_console_active;
+extern char celliax_type[];
+extern struct celliax_pvt *celliax_iflist;
+extern int celliax_dir_entry_extension;
+
+#ifndef GIOVA48
+#define SAMPLES_PER_FRAME 160
+#else // GIOVA48
+#define SAMPLES_PER_FRAME 960
+#endif // GIOVA48
+
+#ifdef CELLIAX_ALSA
+/*! \brief ALSA pcm format, according to endianess  */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+snd_pcm_format_t celliax_format = SND_PCM_FORMAT_S16_LE;
+#else
+snd_pcm_format_t celliax_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 celliax_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(struct celliax_pvt *p)
+{
+  p-&gt;alsac = alsa_open_dev(p, SND_PCM_STREAM_CAPTURE);
+  if (!p-&gt;alsac) {
+    ERRORA(&quot;Failed opening ALSA capture device: %s\n&quot;, CELLIAX_P_LOG, p-&gt;alsacname);
+    if (alsa_shutdown(p)) {
+      ERRORA(&quot;alsa_shutdown failed\n&quot;, CELLIAX_P_LOG);
+      return -1;
+    }
+    return -1;
+  }
+  p-&gt;alsap = alsa_open_dev(p, SND_PCM_STREAM_PLAYBACK);
+  if (!p-&gt;alsap) {
+    ERRORA(&quot;Failed opening ALSA playback device: %s\n&quot;, CELLIAX_P_LOG, p-&gt;alsapname);
+    if (alsa_shutdown(p)) {
+      ERRORA(&quot;alsa_shutdown failed\n&quot;, CELLIAX_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 celliax_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(struct celliax_pvt *p)
+{
+
+  int err;
+
+  if (p-&gt;alsap) {
+    err = snd_pcm_drop(p-&gt;alsap);
+    if (err &lt; 0) {
+      ERRORA(&quot;device [%s], snd_pcm_drop failed with error '%s'\n&quot;, CELLIAX_P_LOG,
+             p-&gt;alsapname, snd_strerror(err));
+      return -1;
+    }
+    err = snd_pcm_close(p-&gt;alsap);
+    if (err &lt; 0) {
+      ERRORA(&quot;device [%s], snd_pcm_close failed with error '%s'\n&quot;, CELLIAX_P_LOG,
+             p-&gt;alsapname, snd_strerror(err));
+      return -1;
+    }
+  }
+  if (p-&gt;alsac) {
+    err = snd_pcm_drop(p-&gt;alsac);
+    if (err &lt; 0) {
+      ERRORA(&quot;device [%s], snd_pcm_drop failed with error '%s'\n&quot;, CELLIAX_P_LOG,
+             p-&gt;alsacname, snd_strerror(err));
+      return -1;
+    }
+    err = snd_pcm_close(p-&gt;alsac);
+    if (err &lt; 0) {
+      ERRORA(&quot;device [%s], snd_pcm_close failed with error '%s'\n&quot;, CELLIAX_P_LOG,
+             p-&gt;alsacname, snd_strerror(err));
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+/*!
+ * \brief Setup and open the ALSA device (capture OR playback) 
+ * \param p the celliax_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(struct celliax_pvt * p, 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 = p-&gt;alsa_period_size;
+
+  snd_pcm_hw_params_alloca(&amp;params);
+  snd_pcm_sw_params_alloca(&amp;swparams);
+
+  if (stream == SND_PCM_STREAM_CAPTURE) {
+    err = snd_pcm_open(&amp;handle, p-&gt;alsacname, stream, 0 | SND_PCM_NONBLOCK);
+  } else {
+    err = snd_pcm_open(&amp;handle, p-&gt;alsapname, stream, 0 | SND_PCM_NONBLOCK);
+  }
+  if (err &lt; 0) {
+    ERRORA
+      (&quot;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&quot;
+       &quot; is running?\n&quot;, CELLIAX_P_LOG, snd_strerror(err),
+       stream == SND_PCM_STREAM_CAPTURE ? p-&gt;alsacname : p-&gt;alsapname);
+    return NULL;
+  }
+
+  snd_pcm_info_alloca(&amp;info);
+
+  if ((err = snd_pcm_info(handle, info)) &lt; 0) {
+    ERRORA(&quot;info error: %s&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+
+  err = snd_pcm_nonblock(handle, 1);
+  if (err &lt; 0) {
+    ERRORA(&quot;nonblock setting error: %s&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+
+  err = snd_pcm_hw_params_any(handle, params);
+  if (err &lt; 0) {
+    ERRORA(&quot;Broken configuration for this PCM, no configurations available: %s\n&quot;,
+           CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+
+  err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
+  if (err &lt; 0) {
+    ERRORA(&quot;Access type not available: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+  err = snd_pcm_hw_params_set_format(handle, params, celliax_format);
+  if (err &lt; 0) {
+    ERRORA(&quot;Sample format non available: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+  err = snd_pcm_hw_params_set_channels(handle, params, 1);
+  if (err &lt; 0) {
+    DEBUGA_SOUND(&quot;Channels count set failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+#if 1
+  unsigned int chan_num;
+  err = snd_pcm_hw_params_get_channels(params, &amp;chan_num);
+  if (err &lt; 0) {
+    ERRORA(&quot;Channels count non available: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+  if (chan_num &lt; 1 || chan_num &gt; 2) {
+    ERRORA(&quot;Channels count MUST BE 1 or 2, it is: %d\n&quot;, CELLIAX_P_LOG, chan_num);
+    ERRORA(&quot;Channels count MUST BE 1 or 2, it is: %d on %s %s\n&quot;, CELLIAX_P_LOG, chan_num,
+           p-&gt;alsapname, p-&gt;alsacname);
+    return NULL;
+  } else {
+    if (chan_num == 1) {
+      if (stream == SND_PCM_STREAM_CAPTURE)
+        p-&gt;alsa_capture_is_mono = 1;
+      else
+        p-&gt;alsa_play_is_mono = 1;
+    } else {
+      if (stream == SND_PCM_STREAM_CAPTURE)
+        p-&gt;alsa_capture_is_mono = 0;
+      else
+        p-&gt;alsa_play_is_mono = 0;
+    }
+  }
+#else
+  p-&gt;alsa_capture_is_mono = 1;
+  p-&gt;alsa_play_is_mono = 1;
+#endif
+
+#if 0
+  unsigned int buffer_time = 0;
+  unsigned int period_time = 0;
+  snd_pcm_uframes_t period_frames = 0;
+  snd_pcm_uframes_t buffer_frames = 0;
+
+  if (buffer_time == 0 &amp;&amp; buffer_frames == 0) {
+    err = snd_pcm_hw_params_get_buffer_time_max(params, &amp;buffer_time, 0);
+    assert(err &gt;= 0);
+    if (buffer_time &gt; 500000)
+      buffer_time = 500000;
+  }
+  if (period_time == 0 &amp;&amp; period_frames == 0) {
+    if (buffer_time &gt; 0)
+      period_time = buffer_time / 4;
+    else
+      period_frames = buffer_frames / 4;
+  }
+  if (period_time &gt; 0)
+    err = snd_pcm_hw_params_set_period_time_near(handle, params, &amp;period_time, 0);
+  else
+    err = snd_pcm_hw_params_set_period_size_near(handle, params, &amp;period_frames, 0);
+  assert(err &gt;= 0);
+  if (buffer_time &gt; 0) {
+    err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &amp;buffer_time, 0);
+  } else {
+    err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &amp;buffer_frames);
+  }
+#endif
+
+#if 1
+  rate = p-&gt;celliax_sound_rate;
+  err = snd_pcm_hw_params_set_rate_near(handle, params, &amp;rate, 0);
+  if ((float) p-&gt;celliax_sound_rate * 1.05 &lt; rate
+      || (float) p-&gt;celliax_sound_rate * 0.95 &gt; rate) {
+    WARNINGA(&quot;Rate is not accurate (requested = %iHz, got = %iHz)\n&quot;, CELLIAX_P_LOG,
+             p-&gt;celliax_sound_rate, rate);
+  }
+
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting rate: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+  p-&gt;celliax_sound_rate = rate;
+
+  err = snd_pcm_hw_params_set_period_size_near(handle, params, &amp;period_size, 0);
+
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting period_size: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+
+  p-&gt;alsa_period_size = period_size;
+
+  p-&gt;alsa_buffer_size = p-&gt;alsa_period_size * p-&gt;alsa_periods_in_buffer;
+
+  err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &amp;p-&gt;alsa_buffer_size);
+
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting buffer_size: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+#endif
+
+  err = snd_pcm_hw_params(handle, params);
+  if (err &lt; 0) {
+    ERRORA(&quot;Unable to install hw params: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+
+  snd_pcm_hw_params_get_period_size(params, &amp;chunk_size, 0);
+  snd_pcm_hw_params_get_buffer_size(params, &amp;buffer_size);
+  if (chunk_size == buffer_size) {
+    ERRORA(&quot;Can't use period equal to buffer size (%lu == %lu)\n&quot;, CELLIAX_P_LOG,
+           chunk_size, buffer_size);
+    return NULL;
+  }
+
+  snd_pcm_sw_params_current(handle, swparams);
+
+#if 0
+  err = snd_pcm_sw_params_get_xfer_align(swparams, &amp;xfer_align);
+  if (err &lt; 0) {
+    ERRORA(&quot;Unable to obtain xfer align: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+  NOTICA(&quot;xfer_align: %d\n&quot;, CELLIAX_P_LOG, xfer_align);
+  /* for some reason, on some platforms, xfer_align here is zero, that gives a floating point exception later. So, let's try to force it to 160, the frame size used by celliax */
+  xfer_align = p-&gt;alsa_period_size;
+  NOTICA(&quot;xfer_align: %d\n&quot;, CELLIAX_P_LOG, xfer_align);
+
+  err = snd_pcm_sw_params_set_xfer_align(handle, swparams, xfer_align);
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting xfer_align: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+  NOTICA(&quot;xfer_align: %d\n&quot;, CELLIAX_P_LOG, xfer_align);
+
+  err = snd_pcm_sw_params_get_xfer_align(swparams, &amp;xfer_align);
+  if (err &lt; 0) {
+    ERRORA(&quot;Unable to obtain xfer align: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+  NOTICA(&quot;xfer_align: %d\n&quot;, CELLIAX_P_LOG, xfer_align);
+#endif
+
+  /*
+     if (sleep_min)
+     xfer_align = 1;
+     err = snd_pcm_sw_params_set_sleep_min(handle, swparams,
+     0);
+
+     if (err &lt; 0) {
+     ERRORA(&quot;Error setting slep_min: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+     }
+   */
+  n = chunk_size;
+  err = snd_pcm_sw_params_set_avail_min(handle, swparams, n);
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting avail_min: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+#if 0
+  /* round up to closest transfer boundary */
+  if (xfer_align == 0) {        //so to avoid floating point exception ????
+    xfer_align = 160;
+  }
+  //original n = (buffer_size / xfer_align) * xfer_align;
+  n = (chunk_size / xfer_align) * xfer_align;
+#endif
+  if (stream == SND_PCM_STREAM_CAPTURE) {
+    start_delay = 1;
+  }
+  if (start_delay &lt;= 0) {
+    start_threshold = n + (double) rate *start_delay / 1000000;
+  } else {
+    start_threshold = (double) rate *start_delay / 1000000;
+  }
+  if (start_threshold &lt; 1)
+    start_threshold = 1;
+  if (start_threshold &gt; n)
+    start_threshold = n;
+  err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting start_threshold: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+
+  if (stop_delay &lt;= 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 &lt; 0) {
+    ERRORA(&quot;Error setting stop_threshold: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+#if 0
+  err = snd_pcm_sw_params_set_xfer_align(handle, swparams, xfer_align);
+
+  if (err &lt; 0) {
+    ERRORA(&quot;Error setting xfer_align: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+  }
+#endif
+
+  if (snd_pcm_sw_params(handle, swparams) &lt; 0) {
+    ERRORA(&quot;Error installing software parameters: %s\n&quot;, CELLIAX_P_LOG,
+           snd_strerror(err));
+  }
+
+  err = snd_pcm_poll_descriptors_count(handle);
+  if (err &lt;= 0) {
+    ERRORA(&quot;Unable to get a poll descriptors count, error is %s\n&quot;, CELLIAX_P_LOG,
+           snd_strerror(err));
+    return NULL;
+  }
+
+  if (err != 1) {               //number of poll descriptors
+    DEBUGA_SOUND(&quot;Can't handle more than one device\n&quot;, CELLIAX_P_LOG);
+    return NULL;
+  }
+
+  err = snd_pcm_poll_descriptors(handle, &amp;p-&gt;pfd, err);
+  if (err != 1) {
+    ERRORA(&quot;snd_pcm_poll_descriptors failed, %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    return NULL;
+  }
+  DEBUGA_SOUND(&quot;Acquired fd %d from the poll descriptor\n&quot;, CELLIAX_P_LOG, p-&gt;pfd.fd);
+
+  if (stream == SND_PCM_STREAM_CAPTURE) {
+    p-&gt;celliax_sound_capt_fd = p-&gt;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(&quot;snd_pcm_prepare failed, %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+        return NULL;
+      }
+      DEBUGA_SOUND(&quot;prepared!\n&quot;, CELLIAX_P_LOG);
+    }
+    if (stream == SND_PCM_STREAM_CAPTURE) {
+      err = snd_pcm_start(handle);
+      if (err) {
+        ERRORA(&quot;snd_pcm_start failed, %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+        return NULL;
+      }
+      DEBUGA_SOUND(&quot;started!\n&quot;, CELLIAX_P_LOG);
+    }
+  }
+  if (option_debug &gt; 1) {
+    snd_output_t *output = NULL;
+    err = snd_output_stdio_attach(&amp;output, stdout, 0);
+    if (err &lt; 0) {
+      ERRORA(&quot;snd_output_stdio_attach failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(err));
+    }
+    snd_pcm_dump(handle, output);
+  }
+  if (option_debug &gt; 1)
+    DEBUGA_SOUND(&quot;ALSA handle = %ld\n&quot;, CELLIAX_P_LOG, (long int) handle);
+  return handle;
+
+}
+
+/*! \brief Read audio frames from interface */
+
+struct ast_frame *alsa_read(struct celliax_pvt *p)
+{
+  static struct ast_frame f;
+  static short __buf[CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2];
+  static short __buf2[(CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2) * 2];
+  short *buf;
+  short *buf2;
+  static int readpos = 0;
+  static int left = CELLIAX_FRAME_SIZE;
+  snd_pcm_state_t state;
+  int r = 0;
+  int off = 0;
+  int error = 0;
+  //time_t now_timestamp;
+
+  //memset(&amp;f, 0, sizeof(struct ast_frame)); //giova
+
+  f.frametype = AST_FRAME_NULL;
+  f.subclass = 0;
+  f.samples = 0;
+  f.datalen = 0;
+  f.data = NULL;
+  f.offset = 0;
+  f.src = celliax_type;
+  f.mallocd = 0;
+  f.delivery.tv_sec = 0;
+  f.delivery.tv_usec = 0;
+
+  state = snd_pcm_state(p-&gt;alsac);
+  if (state != SND_PCM_STATE_RUNNING) {
+    DEBUGA_SOUND(&quot;ALSA read state is not SND_PCM_STATE_RUNNING\n&quot;, CELLIAX_P_LOG);
+
+    if (state != SND_PCM_STATE_PREPARED) {
+      error = snd_pcm_prepare(p-&gt;alsac);
+      if (error) {
+        ERRORA(&quot;snd_pcm_prepare failed, %s\n&quot;, CELLIAX_P_LOG, snd_strerror(error));
+        return &amp;f;
+      }
+      DEBUGA_SOUND(&quot;prepared!\n&quot;, CELLIAX_P_LOG);
+    }
+    usleep(1000);
+    error = snd_pcm_start(p-&gt;alsac);
+    if (error) {
+      ERRORA(&quot;snd_pcm_start failed, %s\n&quot;, CELLIAX_P_LOG, snd_strerror(error));
+      return &amp;f;
+    }
+    DEBUGA_SOUND(&quot;started!\n&quot;, CELLIAX_P_LOG);
+    usleep(1000);
+  }
+
+  buf = __buf + AST_FRIENDLY_OFFSET / 2;
+  buf2 = __buf2 + ((AST_FRIENDLY_OFFSET / 2) * 2);
+
+  if (p-&gt;alsa_capture_is_mono) {
+    r = snd_pcm_readi(p-&gt;alsac, buf + readpos, left);
+  } else {
+    r = snd_pcm_readi(p-&gt;alsac, buf2 + (readpos * 2), left);
+
+    int a = 0;
+    int i = 0;
+    for (i = 0; i &lt; (CELLIAX_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) {
+    ERRORA(&quot;XRUN read\n\n\n\n\n&quot;, CELLIAX_P_LOG);
+    return &amp;f;
+  } else if (r == -ESTRPIPE) {
+    ERRORA(&quot;-ESTRPIPE\n&quot;, CELLIAX_P_LOG);
+    return &amp;f;
+
+  } else if (r == -EAGAIN) {
+    DEBUGA_SOUND(&quot;ALSA read -EAGAIN, the soundcard is not ready to be read by celliax\n&quot;,
+                 CELLIAX_P_LOG);
+    while (r == -EAGAIN) {
+      usleep(1000);
+
+      if (p-&gt;alsa_capture_is_mono) {
+        r = snd_pcm_readi(p-&gt;alsac, buf + readpos, left);
+      } else {
+        r = snd_pcm_readi(p-&gt;alsac, buf2 + (readpos * 2), left);
+
+        int a = 0;
+        int i = 0;
+        for (i = 0; i &lt; (CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2) * 2;) {
+          __buf[a] = (__buf2[i] + __buf2[i + 1]) / 2;
+          a++;
+          i++;
+          i++;
+        }
+      }
+
+    }
+  } else if (r &lt; 0) {
+    WARNINGA(&quot;ALSA Read error: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(r));
+  } else if (r &gt;= 0) {
+    //DEBUGA_SOUND(&quot;read: r=%d, readpos=%d, left=%d, off=%d\n&quot;, CELLIAX_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 &gt;= CELLIAX_FRAME_SIZE) {
+    /* A real frame */
+    readpos = 0;
+    left = CELLIAX_FRAME_SIZE;
+
+    f.frametype = AST_FRAME_VOICE;
+    f.subclass = AST_FORMAT_SLINEAR;
+    f.samples = CELLIAX_FRAME_SIZE;
+    f.datalen = CELLIAX_FRAME_SIZE * 2;
+    f.data = buf;
+    f.offset = AST_FRIENDLY_OFFSET;
+    f.src = celliax_type;
+    f.mallocd = 0;
+#ifdef ALSA_MONITOR
+    alsa_monitor_read((char *) buf, CELLIAX_FRAME_SIZE * 2);
+#endif
+
+  }
+  return &amp;f;
+}
+
+/*! \brief Write audio frames to interface */
+int alsa_write(struct celliax_pvt *p, struct ast_frame *f)
+{
+  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;
+
+  /* We have to digest the frame in 160-byte portions */
+  if (f-&gt;datalen &gt; sizeof(sizbuf) - sizpos) {
+    ERRORA(&quot;Frame too large\n&quot;, CELLIAX_P_LOG);
+    res = -1;
+  } else {
+    memcpy(sizbuf + sizpos, f-&gt;data, f-&gt;datalen);
+    len += f-&gt;datalen;
+    pos = 0;
+#ifdef ALSA_MONITOR
+    alsa_monitor_write(sizbuf, len);
+#endif
+    state = snd_pcm_state(p-&gt;alsap);
+    if (state == SND_PCM_STATE_XRUN) {
+      int i;
+
+      DEBUGA_SOUND
+        (&quot;You've got an ALSA write XRUN in the past (celliax 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&quot;,
+         CELLIAX_P_LOG, p-&gt;alsa_periods_in_buffer);
+      res = snd_pcm_prepare(p-&gt;alsap);
+      if (res) {
+        ERRORA(&quot;audio play prepare failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      } else {
+        res = snd_pcm_format_set_silence(celliax_format, silencebuf, len / 2);
+        if (res &lt; 0) {
+          DEBUGA_SOUND(&quot;Silence error %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+          res = -1;
+        }
+        for (i = 0; i &lt; (p-&gt;alsa_periods_in_buffer - 1); i++) {
+          res = snd_pcm_writei(p-&gt;alsap, silencebuf, len / 2);
+          if (res != len / 2) {
+            DEBUGA_SOUND(&quot;Write returned a different quantity: %d\n&quot;, CELLIAX_P_LOG, res);
+            res = -1;
+          } else if (res &lt; 0) {
+            DEBUGA_SOUND(&quot;Write error %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+            res = -1;
+          }
+        }
+      }
+
+    }
+
+    res = snd_pcm_delay(p-&gt;alsap, &amp;delayp1);
+    if (res &lt; 0) {
+      DEBUGA_SOUND(&quot;Error %d on snd_pcm_delay: \&quot;%s\&quot;\n&quot;, CELLIAX_P_LOG, res,
+                   snd_strerror(res));
+      res = snd_pcm_prepare(p-&gt;alsap);
+      if (res) {
+        DEBUGA_SOUND(&quot;snd_pcm_prepare failed: '%s'\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      }
+      res = snd_pcm_delay(p-&gt;alsap, &amp;delayp1);
+    }
+
+    delayp2 = snd_pcm_avail_update(p-&gt;alsap);
+    if (delayp2 &lt; 0) {
+      DEBUGA_SOUND(&quot;Error %d on snd_pcm_avail_update: \&quot;%s\&quot;\n&quot;, CELLIAX_P_LOG,
+                   (int) delayp2, snd_strerror(delayp2));
+
+      res = snd_pcm_prepare(p-&gt;alsap);
+      if (res) {
+        DEBUGA_SOUND(&quot;snd_pcm_prepare failed: '%s'\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      }
+      delayp2 = snd_pcm_avail_update(p-&gt;alsap);
+    }
+
+    if (                        /* delayp1 != 0 &amp;&amp; delayp1 != 160 */
+         delayp1 &lt; 160 || delayp2 &gt; p-&gt;alsa_buffer_size) {
+
+      res = snd_pcm_prepare(p-&gt;alsap);
+      if (res) {
+        DEBUGA_SOUND
+          (&quot;snd_pcm_prepare failed while trying to prevent an ALSA write XRUN: %s, delayp1=%d, delayp2=%d\n&quot;,
+           CELLIAX_P_LOG, snd_strerror(res), (int) delayp1, (int) delayp2);
+      } else {
+
+        int i;
+        for (i = 0; i &lt; (p-&gt;alsa_periods_in_buffer - 1); i++) {
+          res = snd_pcm_format_set_silence(celliax_format, silencebuf, len / 2);
+          if (res &lt; 0) {
+            DEBUGA_SOUND(&quot;Silence error %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+            res = -1;
+          }
+          res = snd_pcm_writei(p-&gt;alsap, silencebuf, len / 2);
+          if (res &lt; 0) {
+            DEBUGA_SOUND(&quot;Write error %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+            res = -1;
+          } else if (res != len / 2) {
+            DEBUGA_SOUND(&quot;Write returned a different quantity: %d\n&quot;, CELLIAX_P_LOG, res);
+            res = -1;
+          }
+        }
+
+        DEBUGA_SOUND
+          (&quot;PREVENTING an ALSA write XRUN (celliax 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&quot;,
+           CELLIAX_P_LOG, p-&gt;alsa_periods_in_buffer, (int) delayp1, (int) delayp2);
+      }
+
+    }
+
+    memset(sizbuf2, 0, sizeof(sizbuf2));
+    if (p-&gt;alsa_play_is_mono) {
+      res = snd_pcm_writei(p-&gt;alsap, sizbuf, len / 2);
+    } else {
+      int a = 0;
+      int i = 0;
+      for (i = 0; i &lt; 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(p-&gt;alsap, sizbuf2, len);
+    }
+    if (res == -EPIPE) {
+      DEBUGA_SOUND
+        (&quot;ALSA write EPIPE (XRUN) (celliax 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&quot;,
+         CELLIAX_P_LOG, p-&gt;alsa_periods_in_buffer, (int) delayp1, (int) delayp2);
+      res = snd_pcm_prepare(p-&gt;alsap);
+      if (res) {
+        ERRORA(&quot;audio play prepare failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      } else {
+
+        if (p-&gt;alsa_play_is_mono) {
+          res = snd_pcm_writei(p-&gt;alsap, sizbuf, len / 2);
+        } else {
+          int a = 0;
+          int i = 0;
+          for (i = 0; i &lt; 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(p-&gt;alsap, sizbuf2, len);
+        }
+
+      }
+
+    } else {
+      if (res == -ESTRPIPE) {
+        ERRORA(&quot;You've got some big problems\n&quot;, CELLIAX_P_LOG);
+      } else if (res == -EAGAIN) {
+        res = 0;
+      } else if (res &lt; 0) {
+        ERRORA(&quot;Error %d on audio write: \&quot;%s\&quot;\n&quot;, CELLIAX_P_LOG, res,
+               snd_strerror(res));
+      }
+    }
+  }
+
+  if (p-&gt;audio_play_reset_period) {
+    time(&amp;now_timestamp);
+    if ((now_timestamp - p-&gt;audio_play_reset_timestamp) &gt; p-&gt;audio_play_reset_period) {
+      if (option_debug)
+        DEBUGA_SOUND(&quot;reset audio play\n&quot;, CELLIAX_P_LOG);
+      res = snd_pcm_wait(p-&gt;alsap, 1000);
+      if (res &lt; 0) {
+        ERRORA(&quot;audio play wait failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      }
+      res = snd_pcm_drop(p-&gt;alsap);
+      if (res) {
+        ERRORA(&quot;audio play drop failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      }
+      res = snd_pcm_prepare(p-&gt;alsap);
+      if (res) {
+        ERRORA(&quot;audio play prepare failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      }
+      res = snd_pcm_wait(p-&gt;alsap, 1000);
+      if (res &lt; 0) {
+        ERRORA(&quot;audio play wait failed: %s\n&quot;, CELLIAX_P_LOG, snd_strerror(res));
+      }
+      time(&amp;p-&gt;audio_play_reset_timestamp);
+    }
+  }
+  res = 0;
+  if (res &gt; 0)
+    res = 0;
+  return res;
+}
+
+
+/*! \brief Write audio frames to interface */
+#endif /* CELLIAX_ALSA */
+
+#ifdef CELLIAX_PORTAUDIO
+int celliax_portaudio_devlist(struct celliax_pvt *p)
+{
+  int i, numDevices;
+  const PaDeviceInfo *deviceInfo;
+
+  numDevices = Pa_GetDeviceCount();
+  if (numDevices &lt; 0) {
+    return 0;
+  }
+  for (i = 0; i &lt; numDevices; i++) {
+    deviceInfo = Pa_GetDeviceInfo(i);
+    NOTICA
+      (&quot;Found PORTAUDIO device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n&quot;,
+       CELLIAX_P_LOG, i, deviceInfo-&gt;name, deviceInfo-&gt;maxInputChannels,
+       deviceInfo-&gt;maxOutputChannels);
+  }
+
+  return numDevices;
+}
+
+int celliax_portaudio_init(struct celliax_pvt *p)
+{
+  PaError err;
+  int c;
+  PaStreamParameters inputParameters, outputParameters;
+  int numdevices;
+  const PaDeviceInfo *deviceInfo;
+
+#ifndef GIOVA48
+  setenv(&quot;PA_ALSA_PLUGHW&quot;, &quot;1&quot;, 1);
+#endif // GIOVA48
+
+  err = Pa_Initialize();
+  if (err != paNoError)
+    return err;
+
+  numdevices = celliax_portaudio_devlist(p);
+
+  if (p-&gt;portaudiocindex &gt; (numdevices - 1)) {
+    ERRORA(&quot;Portaudio Capture id=%d is out of range: valid id are from 0 to %d\n&quot;,
+           CELLIAX_P_LOG, p-&gt;portaudiocindex, (numdevices - 1));
+    return -1;
+  }
+
+  if (p-&gt;portaudiopindex &gt; (numdevices - 1)) {
+    ERRORA(&quot;Portaudio Playback id=%d is out of range: valid id are from 0 to %d\n&quot;,
+           CELLIAX_P_LOG, p-&gt;portaudiopindex, (numdevices - 1));
+    return -1;
+  }
+  //inputParameters.device = 0;
+  if (p-&gt;portaudiocindex != -1) {
+    inputParameters.device = p-&gt;portaudiocindex;
+  } else {
+    inputParameters.device = Pa_GetDefaultInputDevice();
+  }
+  deviceInfo = Pa_GetDeviceInfo(inputParameters.device);
+  NOTICA
+    (&quot;Using INPUT PORTAUDIO device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n&quot;,
+     CELLIAX_P_LOG, inputParameters.device, deviceInfo-&gt;name,
+     deviceInfo-&gt;maxInputChannels, deviceInfo-&gt;maxOutputChannels);
+  if (deviceInfo-&gt;maxInputChannels == 0) {
+    ERRORA
+      (&quot;No INPUT channels on device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n&quot;,
+       CELLIAX_P_LOG, inputParameters.device, deviceInfo-&gt;name,
+       deviceInfo-&gt;maxInputChannels, deviceInfo-&gt;maxOutputChannels);
+    return -1;
+  }
+  inputParameters.channelCount = 1;
+  inputParameters.sampleFormat = paInt16;
+  //inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)-&gt;defaultHighInputLatency;
+  inputParameters.suggestedLatency = 0.1;
+  inputParameters.hostApiSpecificStreamInfo = NULL;
+
+  //outputParameters.device = 3;
+  if (p-&gt;portaudiopindex != -1) {
+    outputParameters.device = p-&gt;portaudiopindex;
+  } else {
+    outputParameters.device = Pa_GetDefaultOutputDevice();
+  }
+  deviceInfo = Pa_GetDeviceInfo(outputParameters.device);
+  NOTICA
+    (&quot;Using OUTPUT PORTAUDIO device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n&quot;,
+     CELLIAX_P_LOG, outputParameters.device, deviceInfo-&gt;name,
+     deviceInfo-&gt;maxInputChannels, deviceInfo-&gt;maxOutputChannels);
+  if (deviceInfo-&gt;maxOutputChannels == 0) {
+    ERRORA
+      (&quot;No OUTPUT channels on device: id=%d\tname=%s\tmax input channels=%d\tmax output channels=%d\n&quot;,
+       CELLIAX_P_LOG, inputParameters.device, deviceInfo-&gt;name,
+       deviceInfo-&gt;maxInputChannels, deviceInfo-&gt;maxOutputChannels);
+    return -1;
+  }
+#ifndef GIOVA48
+  outputParameters.channelCount = 1;
+#else // GIOVA48
+  outputParameters.channelCount = 2;
+#endif // GIOVA48
+  outputParameters.sampleFormat = paInt16;
+  //outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)-&gt;defaultHighOutputLatency;
+  outputParameters.suggestedLatency = 0.1;
+  outputParameters.hostApiSpecificStreamInfo = NULL;
+
+/* build the pipe that will be polled on by pbx */
+  c = pipe(p-&gt;audiopipe);
+  if (c) {
+    ERRORA(&quot;Unable to create audio pipe\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  fcntl(p-&gt;audiopipe[0], F_SETFL, O_NONBLOCK);
+  fcntl(p-&gt;audiopipe[1], F_SETFL, O_NONBLOCK);
+
+  err =
+#ifndef GIOVA48
+    OpenAudioStream(&amp;p-&gt;stream, &amp;inputParameters, &amp;outputParameters, 8000,
+                    paDitherOff | paClipOff, SAMPLES_PER_FRAME, p-&gt;audiopipe[1],
+                    &amp;p-&gt;speexecho, &amp;p-&gt;speexpreprocess, &amp;p-&gt;owner);
+
+#else // GIOVA48
+    OpenAudioStream(&amp;p-&gt;stream, &amp;inputParameters, &amp;outputParameters, 48000,
+                    paDitherOff | paClipOff, SAMPLES_PER_FRAME, p-&gt;audiopipe[1],
+                    &amp;p-&gt;speexecho, &amp;p-&gt;speexpreprocess, &amp;p-&gt;owner);
+
+#endif // GIOVA48
+  if (err != paNoError) {
+    ERRORA(&quot;Unable to open audio stream: %s\n&quot;, CELLIAX_P_LOG, Pa_GetErrorText(err));
+    return -1;
+  }
+
+/* the pipe is our audio fd for pbx to poll on */
+  p-&gt;celliax_sound_capt_fd = p-&gt;audiopipe[0];
+
+  return 0;
+}
+
+int celliax_portaudio_write(struct celliax_pvt *p, struct ast_frame *f)
+{
+  int samples;
+#ifdef GIOVA48
+  //short buf[CELLIAX_FRAME_SIZE * 2];
+  short buf[3840];
+  short *buf2;
+
+  //ERRORA(&quot;1 f-&gt;datalen=: %d\n&quot;, CELLIAX_P_LOG, f-&gt;datalen);
+
+  memset(buf, '\0', CELLIAX_FRAME_SIZE * 2);
+
+  buf2 = f-&gt;data;
+
+  int i = 0, a = 0;
+
+  for (i = 0; i &lt; f-&gt;datalen / sizeof(short); i++) {
+//stereo, 2 chan 48 -&gt; mono 8
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    buf[a] = buf2[i];
+    a++;
+    /*
+     */
+  }
+  f-&gt;data = &amp;buf;
+  f-&gt;datalen = f-&gt;datalen * 6;
+  //ERRORA(&quot;2 f-&gt;datalen=: %d\n&quot;, CELLIAX_P_LOG, f-&gt;datalen);
+  //f-&gt;datalen = f-&gt;datalen;
+#endif // GIOVA48
+
+#ifdef ASTERISK_VERSION_1_6_0_1
+  samples =
+    WriteAudioStream(p-&gt;stream, (short *) f-&gt;data.ptr,
+                     (int) (f-&gt;datalen / sizeof(short)));
+#else
+  samples =
+    WriteAudioStream(p-&gt;stream, (short *) f-&gt;data, (int) (f-&gt;datalen / sizeof(short)));
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+
+  if (samples != (int) (f-&gt;datalen / sizeof(short)))
+    ERRORA(&quot;WriteAudioStream wrote: %d of %d\n&quot;, CELLIAX_P_LOG, samples,
+           (int) (f-&gt;datalen / sizeof(short)));
+
+  return 0;
+}
+
+struct ast_frame *celliax_portaudio_read(struct celliax_pvt *p)
+{
+  static struct ast_frame f;
+  static short __buf[CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2];
+  short *buf;
+  static short __buf2[CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2];
+  short *buf2;
+  int samples;
+  char c;
+
+  memset(__buf, '\0', (CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2));
+
+  buf = __buf + AST_FRIENDLY_OFFSET / 2;
+
+  memset(__buf2, '\0', (CELLIAX_FRAME_SIZE + AST_FRIENDLY_OFFSET / 2));
+
+  buf2 = __buf2 + AST_FRIENDLY_OFFSET / 2;
+
+  f.frametype = AST_FRAME_NULL;
+  f.subclass = 0;
+  f.samples = 0;
+  f.datalen = 0;
+
+#ifdef ASTERISK_VERSION_1_6_0_1
+  f.data.ptr = NULL;
+#else
+  f.data = NULL;
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+  f.offset = 0;
+  f.src = celliax_type;
+  f.mallocd = 0;
+  f.delivery.tv_sec = 0;
+  f.delivery.tv_usec = 0;
+
+  if ((samples = ReadAudioStream(p-&gt;stream, buf, SAMPLES_PER_FRAME)) == 0) {
+    //do nothing
+  } else {
+#ifdef GIOVA48
+    int i = 0, a = 0;
+
+    samples = samples / 6;
+    for (i = 0; i &lt; samples; i++) {
+      buf2[i] = buf[a];
+      a = a + 6;                //mono, 1 chan 48 -&gt; 8
+    }
+    buf = buf2;
+
+    /* A real frame */
+    f.frametype = AST_FRAME_VOICE;
+    f.subclass = AST_FORMAT_SLINEAR;
+    f.samples = CELLIAX_FRAME_SIZE / 6;
+    f.datalen = CELLIAX_FRAME_SIZE * 2 / 6;
+#else // GIOVA48
+    /* A real frame */
+    f.frametype = AST_FRAME_VOICE;
+    f.subclass = AST_FORMAT_SLINEAR;
+    f.samples = CELLIAX_FRAME_SIZE;
+    f.datalen = CELLIAX_FRAME_SIZE * 2;
+#endif // GIOVA48
+
+#ifdef ASTERISK_VERSION_1_6_0_1
+    f.data.ptr = buf;
+#else
+    f.data = buf;
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+    f.offset = AST_FRIENDLY_OFFSET;
+    f.src = celliax_type;
+    f.mallocd = 0;
+  }
+
+  read(p-&gt;audiopipe[0], &amp;c, 1);
+
+  return &amp;f;
+}
+
+int celliax_portaudio_shutdown(struct celliax_pvt *p)
+{
+  PaError err;
+
+  err = CloseAudioStream(p-&gt;stream);
+
+  if (err != paNoError)
+    ERRORA(&quot;not able to CloseAudioStream\n&quot;, CELLIAX_P_LOG);
+
+  Pa_Terminate();
+  return 0;
+}
+#endif // CELLIAX_PORTAUDIO
+
+int celliax_serial_sync_AT(struct celliax_pvt *p)
+{
+  usleep(10000);                /* 10msec */
+  time(&amp;p-&gt;celliax_serial_synced_timestamp);
+  return 0;
+}
+
+int celliax_serial_getstatus_AT(struct celliax_pvt *p)
+{
+  int res;
+
+  if (p-&gt;owner) {
+    if (p-&gt;owner-&gt;_state != AST_STATE_UP &amp;&amp; p-&gt;owner-&gt;_state != AST_STATE_DOWN) {
+      DEBUGA_AT(&quot;No getstatus, we're neither UP nor DOWN\n&quot;, CELLIAX_P_LOG);
+      return 0;
+    }
+  }
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  res = celliax_serial_write_AT_ack(p, &quot;AT&quot;);
+  if (res) {
+    ERRORA(&quot;AT was not acknowledged, continuing but maybe there is a problem\n&quot;,
+           CELLIAX_P_LOG);
+  }
+  usleep(1000);
+
+  if (strlen(p-&gt;at_query_battchg)) {
+    res =
+      celliax_serial_write_AT_expect(p, p-&gt;at_query_battchg, p-&gt;at_query_battchg_expect);
+    if (res) {
+      WARNINGA(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+               p-&gt;at_query_battchg, p-&gt;at_query_battchg_expect);
+    }
+    usleep(1000);
+  }
+
+  if (strlen(p-&gt;at_query_signal)) {
+    res =
+      celliax_serial_write_AT_expect(p, p-&gt;at_query_signal, p-&gt;at_query_signal_expect);
+    if (res) {
+      WARNINGA(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+               p-&gt;at_query_signal, p-&gt;at_query_signal_expect);
+    }
+    usleep(1000);
+  }
+  //FIXME all the following commands in config!
+
+  if (p-&gt;sms_cnmi_not_supported) {
+    res = celliax_serial_write_AT_ack(p, &quot;AT+MMGL=\&quot;HEADER ONLY\&quot;&quot;);
+    if (res) {
+      WARNINGA
+        (&quot;%s does not get %s from the modem, maybe a long msg is incoming. If this cellmodem is not a Motorola, you are arriving here because your cellmodem do not supports CNMI kind of incoming SMS alert; please let it know to the developers of Celliax. If this cellmodem is a Motorola and this message keeps repeating, and you cannot correctly receive SMSs from this interface, please manually clean all messages from the cellmodem/SIM. Continuing.\n&quot;,
+         CELLIAX_P_LOG, &quot;AT+MMGL=\&quot;HEADER ONLY\&quot;&quot;, &quot;OK&quot;);
+    } else {
+      usleep(1000);
+      if (p-&gt;unread_sms_msg_id) {
+        char at_command[256];
+
+        if (p-&gt;no_ucs2 == 0) {
+          res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;UCS2\&quot;&quot;);
+          if (res) {
+            ERRORA
+              (&quot;AT+CSCS=\&quot;UCS2\&quot; (set TE messages to ucs2)  do not got OK from the phone\n&quot;,
+               CELLIAX_P_LOG);
+            memset(p-&gt;sms_message, 0, sizeof(p-&gt;sms_message));
+          }
+        }
+
+        memset(at_command, 0, sizeof(at_command));
+        sprintf(at_command, &quot;AT+CMGR=%d&quot;, p-&gt;unread_sms_msg_id);
+        memset(p-&gt;sms_message, 0, sizeof(p-&gt;sms_message));
+
+        p-&gt;reading_sms_msg = 1;
+        res = celliax_serial_write_AT_ack(p, at_command);
+        p-&gt;reading_sms_msg = 0;
+        if (res) {
+          ERRORA
+            (&quot;AT+CMGR (read SMS) do not got OK from the phone, message sent was:|||%s|||\n&quot;,
+             CELLIAX_P_LOG, at_command);
+        }
+        res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;GSM\&quot;&quot;);
+        if (res) {
+          ERRORA
+            (&quot;AT+CSCS=\&quot;GSM\&quot; (set TE messages to GSM) do not got OK from the phone\n&quot;,
+             CELLIAX_P_LOG);
+        }
+        memset(at_command, 0, sizeof(at_command));
+        sprintf(at_command, &quot;AT+CMGD=%d&quot;, p-&gt;unread_sms_msg_id);    /* delete the message */
+        p-&gt;unread_sms_msg_id = 0;
+        res = celliax_serial_write_AT_ack(p, at_command);
+        if (res) {
+          ERRORA
+            (&quot;AT+CMGD (Delete SMS) do not got OK from the phone, message sent was:|||%s|||\n&quot;,
+             CELLIAX_P_LOG, at_command);
+        }
+
+        if (strlen(p-&gt;sms_message)) {
+
+          manager_event(EVENT_FLAG_SYSTEM, &quot;CELLIAXincomingsms&quot;,
+                        &quot;Interface: %s\r\nSMS_Message: %s\r\n&quot;, p-&gt;name, p-&gt;sms_message);
+
+          if (strlen(p-&gt;sms_receiving_program)) {
+            int fd1[2];
+            pid_t pid1;
+            char *arg1[] = { p-&gt;sms_receiving_program, (char *) NULL };
+            int i;
+
+            NOTICA(&quot;incoming SMS message:&gt;&gt;&gt;%s&lt;&lt;&lt;\n&quot;, CELLIAX_P_LOG, p-&gt;sms_message);
+            pipe(fd1);
+            pid1 = fork();
+
+            if (pid1 == 0) {    //child
+              int err;
+
+              dup2(fd1[0], 0);  // Connect stdin to pipe output
+              close(fd1[1]);    // close input pipe side
+              setsid();         //session id
+              err = execvp(arg1[0], arg1);  //exec our program, with stdin connected to pipe output
+              if (err) {
+                ERRORA
+                  (&quot;'sms_receiving_program' is set in config file to '%s', and it gave us back this error: %d, (%s). SMS received was:---%s---\n&quot;,
+                   CELLIAX_P_LOG, p-&gt;sms_receiving_program, err, strerror(errno),
+                   p-&gt;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 &lt; strlen(p-&gt;sms_message); i++) {
+              write(fd1[1], &amp;p-&gt;sms_message[i], 1);
+            }
+            close(fd1[1]);      // close pipe input, let our program know we've finished
+          } else {
+            ERRORA
+              (&quot;got SMS incoming message, but 'sms_receiving_program' is not set in config file. SMS received was:---%s---\n&quot;,
+               CELLIAX_P_LOG, p-&gt;sms_message);
+          }
+        }
+#if 1                           //is this one needed? maybe it can interrupt an incoming call that is just to announce itself
+        if (p-&gt;phone_callflow == CALLFLOW_CALL_IDLE
+            &amp;&amp; p-&gt;interface_state == AST_STATE_DOWN &amp;&amp; p-&gt;owner == NULL) {
+          /* we're not in a call, neither calling */
+          res = celliax_serial_write_AT_ack(p, &quot;AT+CKPD=\&quot;EEE\&quot;&quot;);
+          if (res) {
+            ERRORA
+              (&quot;AT+CKPD=\&quot;EEE\&quot; (cellphone screen back to user) do not got OK from the phone\n&quot;,
+               CELLIAX_P_LOG);
+          }
+        }
+#endif
+      }
+    }
+  }
+
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  return 0;
+}
+
+int celliax_serial_read_AT(struct celliax_pvt *p, int look_for_ack, int timeout_usec,
+                           int timeout_sec, const char *expected_string, int expect_crlf)
+{
+  int select_err;
+  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(&amp;read_fds);
+  FD_SET(p-&gt;controldevfd, &amp;read_fds);
+
+  //NOTICA (&quot; INSIDE this celliax_serial_device %s \n&quot;, CELLIAX_P_LOG, p-&gt;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(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+
+  while ((select_err = select(p-&gt;controldevfd + 1, &amp;read_fds, NULL, NULL, &amp;timeout)) &gt; 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(p-&gt;controldevfd, tmp_answer_ptr, AT_BUFSIZ - (tmp_answer_ptr - tmp_answer));
+
+    if (read_count == 0) {
+      ERRORA
+        (&quot;read 0 bytes!!! Nenormalno! Marking this celliax_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&quot;,
+         CELLIAX_P_LOG, p-&gt;controldevice_name);
+      p-&gt;controldev_dead = 1;
+      close(p-&gt;controldevfd);
+      UNLOCKA(&amp;p-&gt;controldev_lock);
+      if (p-&gt;owner) {
+        p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+        celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+      }
+      return -1;
+    }
+
+    if (option_debug &gt; 90) {
+      //DEBUGA_AT(&quot;1 read %d bytes, --|%s|--\n&quot;, CELLIAX_P_LOG, read_count, tmp_answer_ptr);
+      //DEBUGA_AT(&quot;2 read %d bytes, --|%s|--\n&quot;, CELLIAX_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, &quot;\n\r&quot;))) {
+      last_line_ptr = token_ptr;
+      strncpy(p-&gt;line_array.result[la_counter], token_ptr, AT_MESG_MAX_LENGTH);
+      if (strlen(token_ptr) &gt; AT_MESG_MAX_LENGTH) {
+        WARNINGA
+          (&quot;AT mesg longer than buffer, original message was: |%s|, in buffer only: |%s|\n&quot;,
+           CELLIAX_P_LOG, token_ptr, p-&gt;line_array.result[la_counter]);
+      }
+      la_counter++;
+      while ((token_ptr = strtok(NULL, &quot;\n\r&quot;))) {
+        last_line_ptr = token_ptr;
+        strncpy(p-&gt;line_array.result[la_counter], token_ptr, AT_MESG_MAX_LENGTH);
+        if (strlen(token_ptr) &gt; AT_MESG_MAX_LENGTH) {
+          WARNINGA
+            (&quot;AT mesg longer than buffer, original message was: |%s|, in buffer only: |%s|\n&quot;,
+             CELLIAX_P_LOG, token_ptr, p-&gt;line_array.result[la_counter]);
+        }
+        la_counter++;
+      }
+    } else {
+      last_line_ptr = tmp_answer;
+    }
+
+    if (expected_string &amp;&amp; !expect_crlf) {
+      DEBUGA_AT
+        (&quot;last_line_ptr=|%s|, expected_string=|%s|, expect_crlf=%d, memcmp(last_line_ptr, expected_string, strlen(expected_string)) = %d\n&quot;,
+         CELLIAX_P_LOG, last_line_ptr, expected_string, expect_crlf, memcmp(last_line_ptr,
+                                                                            expected_string,
+                                                                            strlen
+                                                                            (expected_string)));
+    }
+
+    if (expected_string &amp;&amp; !expect_crlf
+        &amp;&amp; !memcmp(last_line_ptr, expected_string, strlen(expected_string))
+      ) {
+      strncpy(p-&gt;line_array.result[la_counter], last_line_ptr, AT_MESG_MAX_LENGTH);
+      // match expected string -&gt; 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'
+             &amp;&amp; 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 &gt; 1) {
+      for (i = la_read; i &lt; la_counter; i++)
+        DEBUGA_AT(&quot;Read line %d: |%s|\n&quot;, CELLIAX_P_LOG, i, p-&gt;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 &lt; la_counter; i++) {
+
+      if ((strcmp(p-&gt;line_array.result[i], &quot;RING&quot;) == 0)) {
+        /* with first RING we wait for callid */
+        gettimeofday(&amp;(p-&gt;ringtime), NULL);
+        /* give CALLID (+CLIP) a chance, wait for the next RING before answering */
+        if (p-&gt;phone_callflow == CALLFLOW_INCOMING_RING) {
+          /* we're at the second ring, set the interface state, will be answered by celliax_do_monitor */
+          DEBUGA_AT(&quot;|%s| got second RING\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+          p-&gt;interface_state = AST_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(p-&gt;callid_name, 0, sizeof(p-&gt;callid_name));
+          memset(p-&gt;callid_number, 0, sizeof(p-&gt;callid_number));
+          /* only send AT+CLCC? if the device previously reported its support */
+          if (p-&gt;at_has_clcc != 0) {
+            /* we're at the first ring, try to get CALLID (with +CLCC) */
+            DEBUGA_AT(&quot;|%s| got first RING, sending AT+CLCC?\n&quot;, CELLIAX_P_LOG,
+                      p-&gt;line_array.result[i]);
+            res = celliax_serial_write_AT_noack(p, &quot;AT+CLCC?&quot;);
+            if (res) {
+              ERRORA(&quot;AT+CLCC? (call list) was not correctly sent to the phone\n&quot;,
+                     CELLIAX_P_LOG);
+            }
+          } else {
+            DEBUGA_AT(&quot;|%s| got first RING, but not sending AT+CLCC? as this device &quot;
+                      &quot;seems not to support\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+          }
+        }
+        p-&gt;phone_callflow = CALLFLOW_INCOMING_RING;
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CLCC&quot;, 5) == 0)) {
+        /* with clcc we wait for clip */
+        memset(p-&gt;callid_name, 0, sizeof(p-&gt;callid_name));
+        memset(p-&gt;callid_number, 0, sizeof(p-&gt;callid_number));
+        int commacount = 0;
+        int a = 0;
+        int b = 0;
+        int c = 0;
+
+        for (a = 0; a &lt; strlen(p-&gt;line_array.result[i]); a++) {
+
+          if (p-&gt;line_array.result[i][a] == ',') {
+            commacount++;
+          }
+          if (commacount == 5) {
+            if (p-&gt;line_array.result[i][a] != ',' &amp;&amp; p-&gt;line_array.result[i][a] != '&quot;') {
+              p-&gt;callid_number[b] = p-&gt;line_array.result[i][a];
+              b++;
+            }
+          }
+          if (commacount == 7) {
+            if (p-&gt;line_array.result[i][a] != ',' &amp;&amp; p-&gt;line_array.result[i][a] != '&quot;') {
+              p-&gt;callid_name[c] = p-&gt;line_array.result[i][a];
+              c++;
+            }
+          }
+        }
+
+        p-&gt;phone_callflow = CALLFLOW_INCOMING_RING;
+        DEBUGA_AT(&quot;|%s| CLCC CALLID: name is %s, number is %s\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;line_array.result[i],
+                  p-&gt;callid_name[0] ? p-&gt;callid_name : &quot;not available&quot;,
+                  p-&gt;callid_number[0] ? p-&gt;callid_number : &quot;not available&quot;);
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CLIP&quot;, 5) == 0)) {
+        /* with CLIP, we want to answer right away */
+        memset(p-&gt;callid_name, 0, sizeof(p-&gt;callid_name));
+        memset(p-&gt;callid_number, 0, sizeof(p-&gt;callid_number));
+
+        int commacount = 0;
+        int a = 0;
+        int b = 0;
+        int c = 0;
+
+        for (a = 7; a &lt; strlen(p-&gt;line_array.result[i]); a++) {
+          if (p-&gt;line_array.result[i][a] == ',') {
+            commacount++;
+          }
+          if (commacount == 0) {
+            if (p-&gt;line_array.result[i][a] != ',' &amp;&amp; p-&gt;line_array.result[i][a] != '&quot;') {
+              p-&gt;callid_number[b] = p-&gt;line_array.result[i][a];
+              b++;
+            }
+          }
+          if (commacount == 4) {
+            if (p-&gt;line_array.result[i][a] != ',' &amp;&amp; p-&gt;line_array.result[i][a] != '&quot;') {
+              p-&gt;callid_name[c] = p-&gt;line_array.result[i][a];
+              c++;
+            }
+          }
+        }
+
+        if (p-&gt;interface_state != AST_STATE_RING) {
+          gettimeofday(&amp;(p-&gt;call_incoming_time), NULL);
+          DEBUGA_AT(&quot;AST_STATE_RING call_incoming_time.tv_sec=%ld\n&quot;,
+                    CELLIAX_P_LOG, p-&gt;call_incoming_time.tv_sec);
+
+        }
+
+        p-&gt;interface_state = AST_STATE_RING;
+        p-&gt;phone_callflow = CALLFLOW_INCOMING_RING;
+        DEBUGA_AT(&quot;|%s| CLIP INCOMING CALLID: name is %s, number is %s\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;line_array.result[i],
+                  p-&gt;callid_name[0] != 1 ? p-&gt;callid_name : &quot;not available&quot;,
+                  p-&gt;callid_number[0] ? p-&gt;callid_number : &quot;not available&quot;);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], &quot;BUSY&quot;) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_LINEBUSY;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_LINEBUSY\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          ast_setstate(p-&gt;owner, AST_STATE_BUSY);
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_BUSY);
+        } else {
+          ERRORA(&quot;Why BUSY now?\n&quot;, CELLIAX_P_LOG);
+        }
+      }
+      if ((strcmp(p-&gt;line_array.result[i], &quot;NO ANSWER&quot;) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_NOANSWER;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_NOANSWER\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_NO_ANSWER;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        } else {
+          ERRORA(&quot;Why NO ANSWER now?\n&quot;, CELLIAX_P_LOG);
+        }
+      }
+      if ((strcmp(p-&gt;line_array.result[i], &quot;NO CARRIER&quot;) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_NOCARRIER;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_NOCARRIER\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        } else {
+          ERRORA(&quot;Why NO CARRIER now?\n&quot;, CELLIAX_P_LOG);
+        }
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CBC:&quot;, 5) == 0)) {
+        int power_supply, battery_strenght, err;
+
+        power_supply = battery_strenght = 0;
+
+        err =
+          sscanf(&amp;p-&gt;line_array.result[i][6], &quot;%d,%d&quot;, &amp;power_supply, &amp;battery_strenght);
+        if (err &lt; 2) {
+          DEBUGA_AT(&quot;|%s| is not formatted as: |+CBC: xx,yy| now trying  |+CBC:xx,yy|\n&quot;,
+                    CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+
+          err =
+            sscanf(&amp;p-&gt;line_array.result[i][5], &quot;%d,%d&quot;, &amp;power_supply,
+                   &amp;battery_strenght);
+          DEBUGA_AT(&quot;|%s| +CBC: Powered by %s, battery strenght=%d\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i], power_supply ? &quot;power supply&quot; : &quot;battery&quot;,
+                    battery_strenght);
+
+        }
+
+        if (err &lt; 2) {
+          DEBUGA_AT(&quot;|%s| is not formatted as: |+CBC:xx,yy| giving up\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        }
+
+        else {
+          if (option_debug &gt; 1)
+            DEBUGA_AT(&quot;|%s| +CBC: Powered by %s, battery strenght=%d\n&quot;, CELLIAX_P_LOG,
+                      p-&gt;line_array.result[i], power_supply ? &quot;power supply&quot; : &quot;battery&quot;,
+                      battery_strenght);
+          if (!power_supply) {
+            if (battery_strenght &lt; 10) {
+              ERRORA(&quot;|%s| BATTERY ALMOST EXHAUSTED\n&quot;, CELLIAX_P_LOG,
+                     p-&gt;line_array.result[i]);
+            } else if (battery_strenght &lt; 20) {
+              WARNINGA(&quot;|%s| BATTERY LOW\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+
+            }
+
+          }
+        }
+
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CSQ:&quot;, 5) == 0)) {
+        int signal_quality, ber, err;
+
+        signal_quality = ber = 0;
+
+        err = sscanf(&amp;p-&gt;line_array.result[i][6], &quot;%d,%d&quot;, &amp;signal_quality, &amp;ber);
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| +CSQ: Signal Quality: %d, Error Rate=%d\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i], signal_quality, ber);
+        if (err &lt; 2) {
+          ERRORA(&quot;|%s| is not formatted as: |+CSQ: xx,yy|\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+        } else {
+          if (signal_quality &lt; 11 || signal_quality == 99) {
+            WARNINGA
+              (&quot;|%s| CELLPHONE GETS ALMOST NO SIGNAL, consider to move it or additional antenna\n&quot;,
+               CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+          } else if (signal_quality &lt; 15) {
+            WARNINGA(&quot;|%s| CELLPHONE GETS SIGNAL LOW\n&quot;, CELLIAX_P_LOG,
+                     p-&gt;line_array.result[i]);
+
+          }
+
+        }
+
+      }
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGW:&quot;, 6) == 0)) {
+        int err;
+
+        err = sscanf(&amp;p-&gt;line_array.result[i][7], &quot;%s&quot;, p-&gt;at_cmgw);
+        DEBUGA_AT(&quot;|%s| +CMGW: %s\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i], p-&gt;at_cmgw);
+        if (err &lt; 1) {
+          ERRORA(&quot;|%s| is not formatted as: |+CMGW: xxxx|\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+        }
+
+      }
+
+      /* at_call_* are unsolicited messages sent by the modem to signal us about call processing activity and events */
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_call_idle) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_IDLE\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          DEBUGA_AT(&quot;just received a remote HANGUP\n&quot;, CELLIAX_P_LOG);
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_NORMAL;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+          DEBUGA_AT(&quot;just sent AST_CONTROL_HANGUP\n&quot;, CELLIAX_P_LOG);
+        }
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_call_incoming) == 0)) {
+
+        //char list_command[64];
+
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_INCOMING\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+
+        if (p-&gt;phone_callflow != CALLFLOW_CALL_INCOMING
+            &amp;&amp; p-&gt;phone_callflow != CALLFLOW_INCOMING_RING) {
+          //mark the time of CALLFLOW_CALL_INCOMING
+          gettimeofday(&amp;(p-&gt;call_incoming_time), NULL);
+          p-&gt;phone_callflow = CALLFLOW_CALL_INCOMING;
+          DEBUGA_AT(&quot;CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld\n&quot;,
+                    CELLIAX_P_LOG, p-&gt;call_incoming_time.tv_sec);
+
+        }
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_call_active) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_ACTIVE;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_ACTIVE\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+
+        if (p-&gt;owner &amp;&amp; p-&gt;interface_state == CALLFLOW_CALL_DIALING) {
+          DEBUGA_PBX(&quot;just received a remote ANSWER\n&quot;, CELLIAX_P_LOG);
+          if (p-&gt;owner-&gt;_state != AST_STATE_UP) {
+            celliax_queue_control(p-&gt;owner, AST_CONTROL_RINGING);
+            DEBUGA_PBX(&quot;just sent AST_CONTROL_RINGING\n&quot;, CELLIAX_P_LOG);
+            DEBUGA_PBX(&quot;going to send AST_CONTROL_ANSWER\n&quot;, CELLIAX_P_LOG);
+            celliax_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+            DEBUGA_PBX(&quot;just sent AST_CONTROL_ANSWER\n&quot;, CELLIAX_P_LOG);
+          }
+        } else {
+        }
+        p-&gt;interface_state = AST_STATE_UP;
+        DEBUGA_PBX(&quot;just interface_state UP\n&quot;, CELLIAX_P_LOG);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_call_calling) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_DIALING;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_DIALING\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_call_failed) == 0)) {
+        p-&gt;phone_callflow = CALLFLOW_CALL_FAILED;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_FAILED\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        }
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CSCA:&quot;, 6) == 0)) {   //TODO SMS FIXME in config!
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| +CSCA: Message Center Address!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGF:&quot;, 6) == 0)) {   //TODO SMS FIXME in config!
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| +CMGF: Message Format!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CMTI:&quot;, 6) == 0)) {   //TODO SMS FIXME in config!
+        int err;
+        int pos;
+
+        //FIXME all the following commands in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +CMTI: Incoming SMS!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+
+        err = sscanf(&amp;p-&gt;line_array.result[i][12], &quot;%d&quot;, &amp;pos);
+        if (err &lt; 1) {
+          ERRORA(&quot;|%s| is not formatted as: |+CMTI: \&quot;MT\&quot;,xx|\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+        } else {
+          DEBUGA_AT(&quot;|%s| +CMTI: Incoming SMS in position: %d!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i], pos);
+          p-&gt;unread_sms_msg_id = pos;
+          usleep(1000);
+
+          if (p-&gt;unread_sms_msg_id) {
+            char at_command[256];
+
+            if (p-&gt;no_ucs2 == 0) {
+              res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;UCS2\&quot;&quot;);
+              if (res) {
+                ERRORA
+                  (&quot;AT+CSCS=\&quot;UCS2\&quot; (set TE messages to ucs2)  do not got OK from the phone, continuing\n&quot;,
+                   CELLIAX_P_LOG);
+                //memset(p-&gt;sms_message, 0, sizeof(p-&gt;sms_message));
+              }
+            }
+
+            memset(at_command, 0, sizeof(at_command));
+            sprintf(at_command, &quot;AT+CMGR=%d&quot;, p-&gt;unread_sms_msg_id);
+            memset(p-&gt;sms_message, 0, sizeof(p-&gt;sms_message));
+
+            p-&gt;reading_sms_msg = 1;
+            res = celliax_serial_write_AT_ack(p, at_command);
+            p-&gt;reading_sms_msg = 0;
+            if (res) {
+              ERRORA
+                (&quot;AT+CMGR (read SMS) do not got OK from the phone, message sent was:|||%s|||\n&quot;,
+                 CELLIAX_P_LOG, at_command);
+            }
+            res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;GSM\&quot;&quot;);
+            if (res) {
+              ERRORA
+                (&quot;AT+CSCS=\&quot;GSM\&quot; (set TE messages to GSM) do not got OK from the phone\n&quot;,
+                 CELLIAX_P_LOG);
+            }
+            memset(at_command, 0, sizeof(at_command));
+            sprintf(at_command, &quot;AT+CMGD=%d&quot;, p-&gt;unread_sms_msg_id);    /* delete the message */
+            p-&gt;unread_sms_msg_id = 0;
+            res = celliax_serial_write_AT_ack(p, at_command);
+            if (res) {
+              ERRORA
+                (&quot;AT+CMGD (Delete SMS) do not got OK from the phone, message sent was:|||%s|||\n&quot;,
+                 CELLIAX_P_LOG, at_command);
+            }
+
+            if (strlen(p-&gt;sms_message)) {
+              manager_event(EVENT_FLAG_SYSTEM, &quot;CELLIAXincomingsms&quot;,
+                            &quot;Interface: %s\r\nSMS_Message: %s\r\n&quot;, p-&gt;name,
+                            p-&gt;sms_message);
+              if (strlen(p-&gt;sms_receiving_program)) {
+                int fd1[2];
+                pid_t pid1;
+                char *arg1[] = { p-&gt;sms_receiving_program, (char *) NULL };
+                int i;
+
+                NOTICA(&quot;incoming SMS message:&gt;&gt;&gt;%s&lt;&lt;&lt;\n&quot;, CELLIAX_P_LOG, p-&gt;sms_message);
+                pipe(fd1);
+                pid1 = fork();
+
+                if (pid1 == 0) {    //child
+                  int err;
+
+                  dup2(fd1[0], 0);  // Connect stdin to pipe output
+                  close(fd1[1]);    // close input pipe side
+                close(p-&gt;controldevfd);
+                  setsid();     //session id
+                  err = execvp(arg1[0], arg1);  //exec our program, with stdin connected to pipe output
+                  if (err) {
+                    ERRORA
+                      (&quot;'sms_receiving_program' is set in config file to '%s', and it gave us back this error: %d, (%s). SMS received was:---%s---\n&quot;,
+                       CELLIAX_P_LOG, p-&gt;sms_receiving_program, err, strerror(errno),
+                       p-&gt;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 &lt; strlen(p-&gt;sms_message); i++) {
+                  write(fd1[1], &amp;p-&gt;sms_message[i], 1);
+                }
+                close(fd1[1]);  // close pipe input, let our program know we've finished
+              } else {
+                ERRORA
+                  (&quot;got SMS incoming message, but 'sms_receiving_program' is not set in config file. SMS received was:---%s---\n&quot;,
+                   CELLIAX_P_LOG, p-&gt;sms_message);
+              }
+            }
+#if 1                           //is this one needed? maybe it can interrupt an incoming call that is just to announce itself
+            if (p-&gt;phone_callflow == CALLFLOW_CALL_IDLE
+                &amp;&amp; p-&gt;interface_state == AST_STATE_DOWN &amp;&amp; p-&gt;owner == NULL) {
+              /* we're not in a call, neither calling */
+              res = celliax_serial_write_AT_ack(p, &quot;AT+CKPD=\&quot;EEE\&quot;&quot;);
+              if (res) {
+                ERRORA
+                  (&quot;AT+CKPD=\&quot;EEE\&quot; (cellphone screen back to user) do not got OK from the phone\n&quot;,
+                   CELLIAX_P_LOG);
+              }
+            }
+#endif
+          }                     //unread_msg_id
+
+        }                       //CMTI well formatted
+
+      }                         //CMTI
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+MMGL:&quot;, 6) == 0)) {   //TODO MOTOROLA SMS FIXME in config!
+        int err = 0;
+        //int unread_msg_id=0;
+
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +MMGL: Listing Motorola SMSs!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+
+        err = sscanf(&amp;p-&gt;line_array.result[i][7], &quot;%d&quot;, &amp;p-&gt;unread_sms_msg_id);
+        if (err &lt; 1) {
+          ERRORA(&quot;|%s| is not formatted as: |+MMGL: xx|\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+        }
+      }
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGL:&quot;, 6) == 0)) {   //TODO  SMS FIXME in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +CMGL: Listing SMSs!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+MMGR:&quot;, 6) == 0)) {   //TODO MOTOROLA SMS FIXME in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +MMGR: Reading Motorola SMS!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;reading_sms_msg)
+          p-&gt;reading_sms_msg++;
+      }
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGR: \&quot;STO U&quot;, 13) == 0)) {  //TODO  SMS FIXME in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +CMGR: Reading stored UNSENT SMS!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      } else if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGR: \&quot;STO S&quot;, 13) == 0)) {   //TODO  SMS FIXME in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +CMGR: Reading stored SENT SMS!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      } else if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGR: \&quot;REC R&quot;, 13) == 0)) {   //TODO  SMS FIXME in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +CMGR: Reading received READ SMS!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      } else if ((strncmp(p-&gt;line_array.result[i], &quot;+CMGR: \&quot;REC U&quot;, 13) == 0)) {   //TODO  SMS FIXME in config!
+        if (option_debug)
+          DEBUGA_AT(&quot;|%s| +CMGR: Reading received UNREAD SMS!\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;reading_sms_msg)
+          p-&gt;reading_sms_msg++;
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], &quot;+MCST: 17&quot;) == 0)) {    /* motorola call processing unsolicited messages */
+        p-&gt;phone_callflow = CALLFLOW_CALL_INFLUX;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_INFLUX\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], &quot;+MCST: 68&quot;) == 0)) {    /* motorola call processing unsolicited messages */
+        p-&gt;phone_callflow = CALLFLOW_CALL_NOSERVICE;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_NOSERVICE\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        }
+      }
+      if ((strcmp(p-&gt;line_array.result[i], &quot;+MCST: 70&quot;) == 0)) {    /* motorola call processing unsolicited messages */
+        p-&gt;phone_callflow = CALLFLOW_CALL_OUTGOINGRESTRICTED;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_OUTGOINGRESTRICTED\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        }
+      }
+      if ((strcmp(p-&gt;line_array.result[i], &quot;+MCST: 72&quot;) == 0)) {    /* motorola call processing unsolicited messages */
+        p-&gt;phone_callflow = CALLFLOW_CALL_SECURITYFAIL;
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| CALLFLOW_CALL_SECURITYFAIL\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+        if (p-&gt;interface_state != AST_STATE_DOWN &amp;&amp; p-&gt;owner) {
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        }
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;+CPBR&quot;, 5) == 0)) {    /* phonebook stuff begins */
+
+        if (p-&gt;phonebook_querying) {    /* probably phonebook struct begins */
+          int err, first_entry, last_entry, number_lenght, text_lenght;
+
+          if (option_debug)
+            DEBUGA_AT(&quot;phonebook struct: |%s|\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+
+          err =
+            sscanf(&amp;p-&gt;line_array.result[i][8], &quot;%d-%d),%d,%d&quot;, &amp;first_entry, &amp;last_entry,
+                   &amp;number_lenght, &amp;text_lenght);
+          if (err &lt; 4) {
+
+            err =
+              sscanf(&amp;p-&gt;line_array.result[i][7], &quot;%d-%d,%d,%d&quot;, &amp;first_entry,
+                     &amp;last_entry, &amp;number_lenght, &amp;text_lenght);
+          }
+
+          if (err &lt; 4) {
+            ERRORA
+              (&quot;phonebook struct: |%s| is nor formatted as: |+CPBR: (1-750),40,14| neither as: |+CPBR: 1-750,40,14|\n&quot;,
+               CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+          } else {
+
+            if (option_debug)
+              DEBUGA_AT
+                (&quot;First entry: %d, last entry: %d, phone number max lenght: %d, text max lenght: %d\n&quot;,
+                 CELLIAX_P_LOG, first_entry, last_entry, number_lenght, text_lenght);
+            p-&gt;phonebook_first_entry = first_entry;
+            p-&gt;phonebook_last_entry = last_entry;
+            p-&gt;phonebook_number_lenght = number_lenght;
+            p-&gt;phonebook_text_lenght = text_lenght;
+          }
+
+        } else {                /* probably phonebook entry begins */
+
+          if (p-&gt;phonebook_listing) {
+            int err, entry_id, entry_type;
+
+            char entry_number[256];
+            char entry_text[256];
+
+            if (option_debug)
+              DEBUGA_AT(&quot;phonebook entry: |%s|\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+
+            err =
+              sscanf(&amp;p-&gt;line_array.result[i][7], &quot;%d,\&quot;%255[0-9+]\&quot;,%d,\&quot;%255[^\&quot;]\&quot;&quot;,
+                     &amp;entry_id, entry_number, &amp;entry_type, entry_text);
+            if (err &lt; 4) {
+              ERRORA
+                (&quot;err=%d, phonebook entry: |%s| is not formatted as: |+CPBR: 504,\&quot;+39025458068\&quot;,145,\&quot;ciao a tutti\&quot;|\n&quot;,
+                 CELLIAX_P_LOG, err, p-&gt;line_array.result[i]);
+            } else {
+              //TODO: sanitize entry_text
+              if (option_debug)
+                DEBUGA_AT(&quot;Number: %s, Text: %s, Type: %d\n&quot;, CELLIAX_P_LOG, entry_number,
+                          entry_text, entry_type);
+              /* write entry in phonebook file */
+              if (p-&gt;phonebook_writing_fp) {
+                celliax_dir_entry_extension++;
+
+                fprintf(p-&gt;phonebook_writing_fp,
+                        &quot;%s  =&gt; ,%sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcell=%s|phonebook_entry_owner=%s\n&quot;,
+                        entry_number, entry_text, &quot;no&quot;,
+                        p-&gt;celliax_dir_entry_extension_prefix, &quot;2&quot;,
+                        celliax_dir_entry_extension, &quot;yes&quot;, &quot;not_specified&quot;);
+                fprintf(p-&gt;phonebook_writing_fp,
+                        &quot;%s  =&gt; ,%sDO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcell=%s|phonebook_entry_owner=%s\n&quot;,
+                        entry_number, entry_text, &quot;no&quot;,
+                        p-&gt;celliax_dir_entry_extension_prefix, &quot;3&quot;,
+                        celliax_dir_entry_extension, &quot;yes&quot;, &quot;not_specified&quot;);
+              }
+            }
+
+          }
+
+          if (p-&gt;phonebook_listing_received_calls) {
+            int err, entry_id, entry_type;
+
+            char entry_number[256] = &quot;&quot;;
+            char entry_text[256] = &quot;&quot;;
+
+            if (option_debug)
+              DEBUGA_AT(&quot;phonebook entry: |%s|\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+
+            err =
+              sscanf(&amp;p-&gt;line_array.result[i][7], &quot;%d,\&quot;%255[0-9+]\&quot;,%d,\&quot;%255[^\&quot;]\&quot;&quot;,
+                     &amp;entry_id, entry_number, &amp;entry_type, entry_text);
+            if (err &lt; 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
+                (&quot;err=%d, phonebook entry: |%s| is not formatted as: |+CPBR: 504,\&quot;+39025458068\&quot;,145,\&quot;ciao a tutti\&quot;|\n&quot;,
+                 CELLIAX_P_LOG, err, p-&gt;line_array.result[i]);
+            } else {
+              //TODO: sanitize entry_text
+
+              if (option_debug)
+                DEBUGA_AT(&quot;Number: %s, Text: %s, Type: %d\n&quot;, CELLIAX_P_LOG, entry_number,
+                          entry_text, entry_type);
+              memset(p-&gt;callid_name, 0, sizeof(p-&gt;callid_name));
+              memset(p-&gt;callid_number, 0, sizeof(p-&gt;callid_number));
+              strncpy(p-&gt;callid_name, entry_text, sizeof(p-&gt;callid_name));
+              strncpy(p-&gt;callid_number, entry_number, sizeof(p-&gt;callid_number));
+              if (option_debug)
+                DEBUGA_AT(&quot;incoming callid: Text: %s, Number: %s\n&quot;, CELLIAX_P_LOG,
+                          p-&gt;callid_name, p-&gt;callid_number);
+
+              DEBUGA_AT(&quot;|%s| CPBR INCOMING CALLID: name is %s, number is %s\n&quot;,
+                        CELLIAX_P_LOG, p-&gt;line_array.result[i],
+                        p-&gt;callid_name[0] != 1 ? p-&gt;callid_name : &quot;not available&quot;,
+                        p-&gt;callid_number[0] ? p-&gt;callid_number : &quot;not available&quot;);
+
+              /* mark the time of RING */
+              gettimeofday(&amp;(p-&gt;ringtime), NULL);
+              p-&gt;interface_state = AST_STATE_RING;
+              p-&gt;phone_callflow = CALLFLOW_INCOMING_RING;
+
+            }
+
+          }
+
+          else {
+            DEBUGA_AT(&quot;phonebook entry: |%s|\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+
+          }
+        }
+
+      }
+
+      if ((strncmp(p-&gt;line_array.result[i], &quot;*ECAV&quot;, 5) == 0) || (strncmp(p-&gt;line_array.result[i], &quot;*ECAM&quot;, 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(&amp;p-&gt;line_array.result[i][6], &quot;%d,%d,%d,%d,%d,%d,%d&quot;, &amp;ccid, &amp;ccstatus,
+                 &amp;calltype, &amp;processid, &amp;exitcause, &amp;number, &amp;type);
+        /* only changes the phone_callflow if enought parameters were parsed */
+        if (res &gt;= 3) {
+          switch (ccstatus) {
+          case 0:
+            if (p-&gt;owner) {
+              ast_setstate(p-&gt;owner, AST_STATE_DOWN);
+              p-&gt;owner-&gt;hangupcause = AST_CAUSE_NORMAL;
+              celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+            }
+            p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+            p-&gt;interface_state = AST_STATE_DOWN;
+            if (option_debug &gt; 1)
+              DEBUGA_AT(&quot;|%s| Sony-Ericsson *ECAM/*ECAV: IDLE\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+            break;
+          case 1:
+            if (option_debug &gt; 1)
+              DEBUGA_AT(&quot;|%s| Sony-Ericsson *ECAM/*ECAV: CALLING\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+            break;
+          case 2:
+            if (p-&gt;owner) {
+              ast_setstate(p-&gt;owner, AST_STATE_DIALING);
+            }
+            p-&gt;interface_state = CALLFLOW_CALL_DIALING;
+            if (option_debug &gt; 1)
+              DEBUGA_AT(&quot;|%s| Sony-Ericsson *ECAM/*ECAV: CONNECTING\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+            break;
+          case 3:
+            if (p-&gt;owner) {
+              ast_setstate(p-&gt;owner, AST_STATE_UP);
+              celliax_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+            }
+            p-&gt;phone_callflow = CALLFLOW_CALL_ACTIVE;
+            p-&gt;interface_state = AST_STATE_UP;
+            if (option_debug &gt; 1)
+              DEBUGA_AT(&quot;|%s| Sony-Ericsson *ECAM/*ECAV: ACTIVE\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+            break;
+          case 4:
+            if (option_debug &gt; 1)
+              DEBUGA_AT
+                (&quot;|%s| Sony-Ericsson *ECAM/*ECAV: don't know how to handle HOLD event\n&quot;,
+                 CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+            break;
+          case 5:
+            if (option_debug &gt; 1)
+              DEBUGA_AT
+                (&quot;|%s| Sony-Ericsson *ECAM/*ECAV: don't know how to handle WAITING event\n&quot;,
+                 CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+            break;
+          case 6:
+            if (option_debug &gt; 1)
+              DEBUGA_AT
+                (&quot;|%s| Sony-Ericsson *ECAM/*ECAV: don't know how to handle ALERTING event\n&quot;,
+                 CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+            break;
+          case 7:
+            if (p-&gt;owner) {
+              ast_setstate(p-&gt;owner, AST_STATE_BUSY);
+              celliax_queue_control(p-&gt;owner, AST_CONTROL_BUSY);
+            }
+            p-&gt;phone_callflow = CALLFLOW_CALL_LINEBUSY;
+            p-&gt;interface_state = AST_STATE_BUSY;
+            if (option_debug &gt; 1)
+              DEBUGA_AT(&quot;|%s| Sony-Ericsson *ECAM/*ECAV: BUSY\n&quot;, CELLIAX_P_LOG,
+                        p-&gt;line_array.result[i]);
+            break;
+          }
+        } else {
+          if (option_debug &gt; 1)
+            DEBUGA_AT(&quot;|%s| Sony-Ericsson *ECAM/*ECAV: could not parse parameters\n&quot;,
+                      CELLIAX_P_LOG, p-&gt;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(p-&gt;line_array.result[i], p-&gt;at_indicator_noservice_string) == 0)) {
+        if (option_debug &gt; 1)
+          ERRORA(&quot;|%s| at_indicator_noservice_string\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_nosignal_string) == 0)) {
+        if (option_debug &gt; 1)
+          ERRORA(&quot;|%s| at_indicator_nosignal_string\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_lowsignal_string) == 0)) {
+        if (option_debug &gt; 1)
+          WARNINGA(&quot;|%s| at_indicator_lowsignal_string\n&quot;, CELLIAX_P_LOG,
+                   p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_lowbattchg_string) == 0)) {
+        if (option_debug &gt; 1)
+          WARNINGA(&quot;|%s| at_indicator_lowbattchg_string\n&quot;, CELLIAX_P_LOG,
+                   p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_nobattchg_string) == 0)) {
+        if (option_debug &gt; 1)
+          ERRORA(&quot;|%s| at_indicator_nobattchg_string\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_callactive_string) == 0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| at_indicator_callactive_string\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_nocallactive_string) == 0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| at_indicator_nocallactive_string\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_nocallsetup_string) == 0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| at_indicator_nocallsetup_string\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_callsetupincoming_string) ==
+           0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| at_indicator_callsetupincoming_string\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_callsetupoutgoing_string) ==
+           0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| at_indicator_callsetupoutgoing_string\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;line_array.result[i]);
+      }
+
+      if ((strcmp(p-&gt;line_array.result[i], p-&gt;at_indicator_callsetupremoteringing_string)
+           == 0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;|%s| at_indicator_callsetupremoteringing_string\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;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 &lt; la_counter; i++) {
+      if (expected_string) {
+        if ((strncmp(p-&gt;line_array.result[i], expected_string, strlen(expected_string))
+             == 0)) {
+          if (option_debug &gt; 1)
+            DEBUGA_AT(&quot;|%s| got what EXPECTED\n&quot;, CELLIAX_P_LOG, p-&gt;line_array.result[i]);
+          at_ack = AT_OK;
+        }
+      } else {
+        if ((strcmp(p-&gt;line_array.result[i], &quot;OK&quot;) == 0)) {
+          if (option_debug &gt; 1)
+            DEBUGA_AT(&quot;got OK\n&quot;, CELLIAX_P_LOG);
+          at_ack = AT_OK;
+        }
+      }
+      if ((strcmp(p-&gt;line_array.result[i], &quot;ERROR&quot;) == 0)) {
+        if (option_debug &gt; 1)
+          DEBUGA_AT(&quot;got ERROR\n&quot;, CELLIAX_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 &quot;OK&quot; or &quot;ERROR&quot; */
+      if (p-&gt;reading_sms_msg &gt; 1 &amp;&amp; at_ack == -1) {
+        int c;
+        char sms_body[16000];
+        int err;
+
+        if (strncmp(p-&gt;line_array.result[i], &quot;+CMGR&quot;, 5) == 0) {    /* we are reading the &quot;header&quot; of an SMS */
+          char content[512];
+          char content2[512];
+
+          memset(content, '\0', sizeof(content));
+
+          int inside_comma = 0;
+          int inside_quote = 0;
+          int d = 0;
+
+          for (c = 0; c &lt; strlen(p-&gt;line_array.result[i]); c++) {
+            if (p-&gt;line_array.result[i][c] == ','
+                &amp;&amp; p-&gt;line_array.result[i][c - 1] != '\\' &amp;&amp; inside_quote == 0) {
+              if (inside_comma) {
+                inside_comma = 0;
+                //NOTICA(&quot;inside_comma=%d, inside_quote=%d, we're at=%s\n&quot;, CELLIAX_P_LOG, inside_comma, inside_quote, &amp;p-&gt;line_array.result[i][c]);
+              } else {
+                inside_comma = 1;
+                //NOTICA(&quot;inside_comma=%d, inside_quote=%d, we're at=%s\n&quot;, CELLIAX_P_LOG, inside_comma, inside_quote, &amp;p-&gt;line_array.result[i][c]);
+              }
+            }
+            if (p-&gt;line_array.result[i][c] == '&quot;'
+                &amp;&amp; p-&gt;line_array.result[i][c - 1] != '\\') {
+              if (inside_quote) {
+                inside_quote = 0;
+                //ERRORA(&quot;END_CONTENT inside_comma=%d, inside_quote=%d, we're at=%s\n&quot;, CELLIAX_P_LOG, inside_comma, inside_quote, &amp;p-&gt;line_array.result[i][c]);
+                DEBUGA_AT(&quot;content=%s\n&quot;, CELLIAX_P_LOG, content);
+
+                strncat(p-&gt;sms_message, &quot;---&quot;,
+                        ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+                strncat(p-&gt;sms_message, content,
+                        ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+                strncat(p-&gt;sms_message, &quot;|||&quot;,
+                        ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+
+                memset(content2, '\0', sizeof(content2));
+                err = ucs2_to_utf8(p, content, content2, sizeof(content2));
+
+                strncat(p-&gt;sms_message, &quot;---&quot;,
+                        ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+                if (!err)
+                  strncat(p-&gt;sms_message, content2,
+                          ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+                strncat(p-&gt;sms_message, &quot;|||&quot;,
+                        ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+                memset(content, '\0', sizeof(content));
+                d = 0;
+              } else {
+                inside_quote = 1;
+                //WARNINGA(&quot;START_CONTENT inside_comma=%d, inside_quote=%d, we're at=%s\n&quot;, CELLIAX_P_LOG, inside_comma, inside_quote, &amp;p-&gt;line_array.result[i][c]);
+              }
+            }
+            if (inside_quote &amp;&amp; p-&gt;line_array.result[i][c] != '&quot;') {
+
+              content[d] = p-&gt;line_array.result[i][c];
+              d++;
+
+            }
+
+          }
+        }                       //it was the +CMGR answer from the cellphone
+        else {
+          strncat(p-&gt;sms_message, &quot;---&quot;,
+                  ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+          strncat(p-&gt;sms_message, p-&gt;line_array.result[i],
+                  ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+          strncat(p-&gt;sms_message, &quot;|||&quot;,
+                  ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+
+          memset(sms_body, '\0', sizeof(sms_body));
+          err = ucs2_to_utf8(p, p-&gt;line_array.result[i], sms_body, sizeof(sms_body));
+
+          strncat(p-&gt;sms_message, &quot;---&quot;,
+                  ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+          if (!err)
+            strncat(p-&gt;sms_message, sms_body,
+                    ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+          strncat(p-&gt;sms_message, &quot;|||&quot;,
+                  ((sizeof(p-&gt;sms_message) - strlen(p-&gt;sms_message)) - 1));
+
+          DEBUGA_AT(&quot;sms_message=%s\n&quot;, CELLIAX_P_LOG, p-&gt;sms_message);
+
+        }                       //it was the UCS2 from cellphone
+
+      }                         //we were reading the SMS
+
+    }
+
+    la_read = la_counter;
+
+    if (look_for_ack &amp;&amp; at_ack &gt; -1)
+      break;
+
+    if (la_counter &gt; AT_MESG_MAX_LINES) {
+      ERRORA(&quot;Too many lines in result (&gt;%d). Stopping reader.\n&quot;, CELLIAX_P_LOG,
+             AT_MESG_MAX_LINES);
+      at_ack = AT_ERROR;
+      break;
+    }
+  }
+
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  if (select_err == -1) {
+    ERRORA(&quot;select returned -1 on %s, setting controldev_dead, error was: %s\n&quot;,
+           CELLIAX_P_LOG, p-&gt;controldevice_name, strerror(errno));
+    p-&gt;controldev_dead = 1;
+    close(p-&gt;controldevfd);
+    if (p-&gt;owner)
+      celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+    return -1;
+  }
+
+  if (p-&gt;phone_callflow == CALLFLOW_CALL_INCOMING &amp;&amp; p-&gt;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(&amp;call_incoming_timeout, NULL);
+    call_incoming_timeout.tv_sec -= 3;
+    DEBUGA_AT
+      (&quot;CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n&quot;,
+       CELLIAX_P_LOG, p-&gt;call_incoming_time.tv_sec, call_incoming_timeout.tv_sec);
+    if (call_incoming_timeout.tv_sec &gt; p-&gt;call_incoming_time.tv_sec) {
+
+      p-&gt;call_incoming_time.tv_sec = 0;
+      p-&gt;call_incoming_time.tv_usec = 0;
+      DEBUGA_AT
+        (&quot;CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n&quot;,
+         CELLIAX_P_LOG, p-&gt;call_incoming_time.tv_sec, call_incoming_timeout.tv_sec);
+      res = celliax_serial_write_AT_ack(p, &quot;AT+CPBS=RC&quot;);
+      if (res) {
+        ERRORA
+          (&quot;AT+CPBS=RC (select memory of received calls) was not answered by the phone\n&quot;,
+           CELLIAX_P_LOG);
+      }
+      p-&gt;phonebook_querying = 1;
+      res = celliax_serial_write_AT_ack(p, &quot;AT+CPBR=?&quot;);
+      if (res) {
+        ERRORA
+          (&quot;AT+CPBS=RC (select memory of received calls) was not answered by the phone\n&quot;,
+           CELLIAX_P_LOG);
+      }
+      p-&gt;phonebook_querying = 0;
+      sprintf(list_command, &quot;AT+CPBR=%d,%d&quot;, p-&gt;phonebook_first_entry,
+              p-&gt;phonebook_last_entry);
+      p-&gt;phonebook_listing_received_calls = 1;
+      res = celliax_serial_write_AT_expect_longtime(p, list_command, &quot;OK&quot;);
+      if (res) {
+        WARNINGA(&quot;AT+CPBR=%d,%d failed, continue\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;phonebook_first_entry, p-&gt;phonebook_last_entry);
+      }
+      p-&gt;phonebook_listing_received_calls = 0;
+    }
+  }
+
+  if (p-&gt;phone_callflow == CALLFLOW_INCOMING_RING) {
+    struct timeval call_incoming_timeout;
+    gettimeofday(&amp;call_incoming_timeout, NULL);
+    call_incoming_timeout.tv_sec -= 10;
+    DEBUGA_AT
+      (&quot;CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n&quot;,
+       CELLIAX_P_LOG, p-&gt;call_incoming_time.tv_sec, call_incoming_timeout.tv_sec);
+    if (call_incoming_timeout.tv_sec &gt; p-&gt;ringtime.tv_sec) {
+      ERRORA(&quot;Ringing stopped and I have not answered. Why?\n&quot;, CELLIAX_P_LOG);
+      DEBUGA_AT
+        (&quot;CALLFLOW_CALL_INCOMING call_incoming_time.tv_sec=%ld, call_incoming_timeout.tv_sec=%ld\n&quot;,
+         CELLIAX_P_LOG, p-&gt;call_incoming_time.tv_sec, call_incoming_timeout.tv_sec);
+      if (p-&gt;owner) {
+        celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+      }
+    }
+  }
+  p-&gt;line_array.elemcount = la_counter;
+  //NOTICA (&quot; OUTSIDE this celliax_serial_device %s \n&quot;, CELLIAX_P_LOG, p-&gt;controldevice_name);
+  if (look_for_ack)
+    return at_ack;
+  else
+    return 0;
+}
+
+int celliax_serial_write_AT(struct celliax_pvt *p, const char *data)
+{
+  int howmany;
+  int i;
+  int res;
+  int count;
+
+  howmany = strlen(data);
+
+  for (i = 0; i &lt; howmany; i++) {
+    res = write(p-&gt;controldevfd, &amp;data[i], 1);
+
+    if (res != 1) {
+      DEBUGA_AT(&quot;Error sending (%.1s): %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i], res,
+                strerror(errno));
+      usleep(100000);
+      for (count = 0; count &lt; 10; count++) {
+        res = write(p-&gt;controldevfd, &amp;data[i], 1);
+        if (res == 1) {
+          DEBUGA_AT(&quot;Successfully RE-sent (%.1s): %d %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i],
+                    count, res, strerror(errno));
+          break;
+        } else
+          DEBUGA_AT(&quot;Error RE-sending (%.1s): %d %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i],
+                    count, res, strerror(errno));
+        usleep(100000);
+
+      }
+      if (res != 1) {
+        ERRORA(&quot;Error RE-sending (%.1s): %d %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i], count,
+               res, strerror(errno));
+        return -1;
+      }
+    }
+    if (option_debug &gt; 1)
+      DEBUGA_AT(&quot;sent data... (%.1s)\n&quot;, CELLIAX_P_LOG, &amp;data[i]);
+    usleep(1000);               /* release the cpu */
+  }
+
+  res = write(p-&gt;controldevfd, &quot;\r&quot;, 1);
+
+  if (res != 1) {
+    DEBUGA_AT(&quot;Error sending (carriage return): %d (%s)\n&quot;, CELLIAX_P_LOG, res,
+              strerror(errno));
+    usleep(100000);
+    for (count = 0; count &lt; 10; count++) {
+      res = write(p-&gt;controldevfd, &quot;\r&quot;, 1);
+
+      if (res == 1) {
+        DEBUGA_AT(&quot;Successfully RE-sent carriage return: %d %d (%s)\n&quot;, CELLIAX_P_LOG,
+                  count, res, strerror(errno));
+        break;
+      } else
+        DEBUGA_AT(&quot;Error RE-sending (carriage return): %d %d (%s)\n&quot;, CELLIAX_P_LOG,
+                  count, res, strerror(errno));
+      usleep(100000);
+
+    }
+    if (res != 1) {
+      ERRORA(&quot;Error RE-sending (carriage return): %d %d (%s)\n&quot;, CELLIAX_P_LOG, count,
+             res, strerror(errno));
+      return -1;
+    }
+  }
+  if (option_debug &gt; 1)
+    DEBUGA_AT(&quot;sent (carriage return)\n&quot;, CELLIAX_P_LOG);
+  usleep(1000);                 /* release the cpu */
+
+  return howmany;
+}
+
+int celliax_serial_write_AT_nocr(struct celliax_pvt *p, const char *data)
+{
+  int howmany;
+  int i;
+  int res;
+  int count;
+
+  howmany = strlen(data);
+
+  for (i = 0; i &lt; howmany; i++) {
+    res = write(p-&gt;controldevfd, &amp;data[i], 1);
+
+    if (res != 1) {
+      DEBUGA_AT(&quot;Error sending (%.1s): %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i], res,
+                strerror(errno));
+      usleep(100000);
+      for (count = 0; count &lt; 10; count++) {
+        res = write(p-&gt;controldevfd, &amp;data[i], 1);
+        if (res == 1)
+          break;
+        else
+          DEBUGA_AT(&quot;Error RE-sending (%.1s): %d %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i],
+                    count, res, strerror(errno));
+        usleep(100000);
+
+      }
+      if (res != 1) {
+        ERRORA(&quot;Error RE-sending (%.1s): %d %d (%s)\n&quot;, CELLIAX_P_LOG, &amp;data[i], count,
+               res, strerror(errno));
+        return -1;
+      }
+    }
+    if (option_debug &gt; 1)
+      DEBUGA_AT(&quot;sent data... (%.1s)\n&quot;, CELLIAX_P_LOG, &amp;data[i]);
+    usleep(1000);               /* release the cpu */
+  }
+
+  usleep(1000);                 /* release the cpu */
+
+  return howmany;
+}
+
+int celliax_serial_write_AT_noack(struct celliax_pvt *p, const char *data)
+{
+
+  if (option_debug &gt; 1)
+    DEBUGA_AT(&quot;celliax_serial_write_AT_noack: %s\n&quot;, CELLIAX_P_LOG, data);
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  if (celliax_serial_write_AT(p, data) != strlen(data)) {
+
+    ERRORA(&quot;Error sending data... (%s)\n&quot;, CELLIAX_P_LOG, strerror(errno));
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  return 0;
+}
+
+int celliax_serial_write_AT_ack(struct celliax_pvt *p, const char *data)
+{
+  int at_result = AT_ERROR;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  if (option_debug &gt; 1)
+    DEBUGA_AT(&quot;sending: %s\n&quot;, CELLIAX_P_LOG, data);
+  if (celliax_serial_write_AT(p, data) != strlen(data)) {
+    ERRORA(&quot;Error sending data... (%s) \n&quot;, CELLIAX_P_LOG, strerror(errno));
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+
+  at_result = celliax_serial_read_AT(p, 1, 500000, 2, NULL, 1); // 2.5 sec timeout
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  return at_result;
+
+}
+
+int celliax_serial_write_AT_ack_nocr_longtime(struct celliax_pvt *p, const char *data)
+{
+  int at_result = AT_ERROR;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  if (option_debug &gt; 1)
+    DEBUGA_AT(&quot;sending: %s\n&quot;, CELLIAX_P_LOG, data);
+  if (celliax_serial_write_AT_nocr(p, data) != strlen(data)) {
+    ERRORA(&quot;Error sending data... (%s) \n&quot;, CELLIAX_P_LOG, strerror(errno));
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+
+  at_result = celliax_serial_read_AT(p, 1, 500000, 20, NULL, 1);    // 20.5 sec timeout
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  return at_result;
+
+}
+
+int celliax_serial_write_AT_expect1(struct celliax_pvt *p, const char *data,
+                                    const char *expected_string, int expect_crlf,
+                                    int seconds)
+{
+  int at_result = AT_ERROR;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  if (option_debug &gt; 1)
+    DEBUGA_AT(&quot;sending: %s, expecting: %s\n&quot;, CELLIAX_P_LOG, data, expected_string);
+  if (celliax_serial_write_AT(p, data) != strlen(data)) {
+    ERRORA(&quot;Error sending data... (%s) \n&quot;, CELLIAX_P_LOG, strerror(errno));
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+
+  at_result = celliax_serial_read_AT(p, 1, 500000, seconds, expected_string, expect_crlf);  // 20.5 sec timeout, used for querying the SIM and sending SMSs
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  return at_result;
+
+}
+
+int celliax_serial_AT_expect(struct celliax_pvt *p, const char *expected_string,
+                             int expect_crlf, int seconds)
+{
+  int at_result = AT_ERROR;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  if (option_debug &gt; 1)
+    DEBUGA_AT(&quot;expecting: %s\n&quot;, CELLIAX_P_LOG, expected_string);
+
+  at_result = celliax_serial_read_AT(p, 1, 500000, seconds, expected_string, expect_crlf);  // 20.5 sec timeout, used for querying the SIM and sending SMSs
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  return at_result;
+
+}
+
+int celliax_serial_answer_AT(struct celliax_pvt *p)
+{
+  int res;
+
+  res = celliax_serial_write_AT_expect(p, p-&gt;at_answer, p-&gt;at_answer_expect);
+  if (res) {
+    DEBUGA_AT
+      (&quot;at_answer command failed, command used: %s, expecting: %s, trying with AT+CKPD=\&quot;S\&quot;\n&quot;,
+       CELLIAX_P_LOG, p-&gt;at_answer, p-&gt;at_answer_expect);
+
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CKPD=\&quot;S\&quot;&quot;);
+    if (res) {
+      ERRORA(&quot;at_answer command failed, command used: 'AT+CKPD=\&quot;S\&quot;', giving up\n&quot;,
+             CELLIAX_P_LOG);
+      return -1;
+    }
+  }
+  //p-&gt;interface_state = AST_STATE_UP;
+  //p-&gt;phone_callflow = CALLFLOW_CALL_ACTIVE;
+  DEBUGA_AT(&quot;AT: call answered\n&quot;, CELLIAX_P_LOG);
+  return 0;
+}
+
+int celliax_serial_hangup_AT(struct celliax_pvt *p)
+{
+  int res;
+
+  if (p-&gt;interface_state != AST_STATE_DOWN) {
+    res = celliax_serial_write_AT_expect(p, p-&gt;at_hangup, p-&gt;at_hangup_expect);
+    if (res) {
+      DEBUGA_AT
+        (&quot;at_hangup command failed, command used: %s, trying to use AT+CKPD=\&quot;EEE\&quot;\n&quot;,
+         CELLIAX_P_LOG, p-&gt;at_hangup);
+      res = celliax_serial_write_AT_ack(p, &quot;AT+CKPD=\&quot;EEE\&quot;&quot;);
+      if (res) {
+        ERRORA(&quot;at_hangup command failed, command used: 'AT+CKPD=\&quot;EEE\&quot;'\n&quot;,
+               CELLIAX_P_LOG);
+        return -1;
+      }
+    }
+  }
+  p-&gt;interface_state = AST_STATE_DOWN;
+  p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+  return 0;
+}
+
+int celliax_serial_config_AT(struct celliax_pvt *p)
+{
+  int res;
+
+/* initial_pause? */
+  if (p-&gt;at_initial_pause) {
+    DEBUGA_AT(&quot;sleeping for %d usec\n&quot;, CELLIAX_P_LOG, p-&gt;at_initial_pause);
+    usleep(p-&gt;at_initial_pause);
+  }
+
+/* go until first empty preinit string, or last preinit string */
+  while (1) {
+
+    if (strlen(p-&gt;at_preinit_1)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_preinit_1, p-&gt;at_preinit_1_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_preinit_1, p-&gt;at_preinit_1_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_preinit_2)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_preinit_2, p-&gt;at_preinit_2_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_preinit_2, p-&gt;at_preinit_2_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_preinit_3)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_preinit_3, p-&gt;at_preinit_3_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_preinit_3, p-&gt;at_preinit_3_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_preinit_4)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_preinit_4, p-&gt;at_preinit_4_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_preinit_4, p-&gt;at_preinit_4_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_preinit_5)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_preinit_5, p-&gt;at_preinit_5_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_preinit_5, p-&gt;at_preinit_5_expect);
+      }
+    } else {
+      break;
+    }
+
+    break;
+  }
+
+/* after_preinit_pause? */
+  if (p-&gt;at_after_preinit_pause) {
+    DEBUGA_AT(&quot;sleeping for %d usec\n&quot;, CELLIAX_P_LOG, p-&gt;at_after_preinit_pause);
+    usleep(p-&gt;at_after_preinit_pause);
+  }
+
+  /* phone, brother, art you alive? */
+  res = celliax_serial_write_AT_ack(p, &quot;AT&quot;);
+  if (res) {
+    ERRORA(&quot;no response to AT\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  /* for motorola, bring it back to &quot;normal&quot; mode if it happens to be in another mode */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+mode=0&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+mode=0 does not get OK from the phone. If it is NOT Motorola,&quot;
+              &quot; no problem.\n&quot;, CELLIAX_P_LOG);
+  }
+  usleep(50000);
+  /* for motorola end */
+
+  /* reset AT configuration to phone default */
+  res = celliax_serial_write_AT_ack(p, &quot;ATZ&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;ATZ failed\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* disable AT command echo */
+  res = celliax_serial_write_AT_ack(p, &quot;ATE0&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;ATE0 failed\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* disable extended error reporting */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CMEE=0&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CMEE failed\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* various phone manufacturer identifier */
+  char at_command[5];
+  int i;
+  for (i = 0; i &lt; 10; i++) {
+    memset(at_command, 0, sizeof(at_command));
+    sprintf(at_command, &quot;ATI%d&quot;, i);
+    res = celliax_serial_write_AT_ack(p, at_command);
+    if (res) {
+      DEBUGA_AT(&quot;ATI%d command failed, continue\n&quot;, CELLIAX_P_LOG, i);
+    }
+  }
+
+  /* phone manufacturer */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CGMI&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CGMI failed\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* phone model */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CGMM&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CGMM failed\n&quot;, CELLIAX_P_LOG);
+  }
+
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CGSN&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CGSN failed\n&quot;, CELLIAX_P_LOG);
+  }
+
+/* this take a lot of time to complete on devices with slow serial link (eg.: 9600bps) */
+#if 0
+  /* ask for the list of supported AT commands, useful to implement new models and debugging */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CLAC&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CLAC failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+#endif
+  /* signal incoming SMS with a +CMTI unsolicited msg */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CNMI=3,1,0,0,0&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CNMI=3,1,0,0,0 failed, continue\n&quot;, CELLIAX_P_LOG);
+    p-&gt;sms_cnmi_not_supported = 1;
+    p-&gt;celliax_serial_sync_period = 30;
+  }
+  /* what is the Message Center address (number) to which the SMS has to be sent? */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CSCA?&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CSCA? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+  /* what is the Message Format of SMSs? */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CMGF?&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CMGF? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CMGF=1&quot;);    //TODO: support phones that only accept pdu mode
+  if (res) {
+    ERRORA(&quot;Error setting SMS sending mode to TEXT on the cellphone\n&quot;, CELLIAX_P_LOG);
+    return RESULT_FAILURE;
+  }
+  /* what is the Charset of SMSs? */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS?&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CSCS? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+
+  p-&gt;no_ucs2 = 0;
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;UCS2\&quot;&quot;);
+  if (res) {
+    WARNINGA
+      (&quot;AT+CSCS=\&quot;UCS2\&quot; (set TE messages to ucs2)  do not got OK from the phone, let's try with 'GSM'\n&quot;,
+       CELLIAX_P_LOG);
+    p-&gt;no_ucs2 = 1;
+  }
+
+  if (p-&gt;no_ucs2) {
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;GSM\&quot;&quot;);
+    if (res) {
+      WARNINGA(&quot;AT+CSCS=\&quot;GSM\&quot; (set TE messages to GSM)  do not got OK from the phone\n&quot;,
+               CELLIAX_P_LOG);
+    }
+    //res = celliax_serial_write_AT_ack(p, &quot;AT+CSMP=17,167,0,16&quot;); //&quot;flash&quot;, class 0  sms 7 bit
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CSMP=17,167,0,0&quot;); //normal, 7 bit message
+    if (res) {
+      WARNINGA(&quot;AT+CSMP do not got OK from the phone, continuing\n&quot;, CELLIAX_P_LOG);
+    }
+  } else {
+    //res = celliax_serial_write_AT_ack(p, &quot;AT+CSMP=17,167,0,20&quot;); //&quot;flash&quot;, class 0 sms 16 bit unicode
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CSMP=17,167,0,8&quot;); //unicode, 16 bit message
+    if (res) {
+      WARNINGA(&quot;AT+CSMP do not got OK from the phone, continuing\n&quot;, CELLIAX_P_LOG);
+    }
+  }
+
+  /* is the unsolicited reporting of mobile equipment event supported? */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CMER=?&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CMER=? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+  /* request unsolicited reporting of mobile equipment indicators' events, to be screened by categories reported by +CIND=? */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CMER=3,0,0,1&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CMER=? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* is the solicited reporting of mobile equipment indications supported? */
+
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CIND=?&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CIND=? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* is the unsolicited reporting of call monitoring supported? sony-ericsson specific */
+  res = celliax_serial_write_AT_ack(p, &quot;AT*ECAM=?&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT*ECAM=? failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+  /* enable the unsolicited reporting of call monitoring. sony-ericsson specific */
+  res = celliax_serial_write_AT_ack(p, &quot;AT*ECAM=1&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT*ECAM=1 failed, continue\n&quot;, CELLIAX_P_LOG);
+    p-&gt;at_has_ecam = 0;
+  } else {
+    p-&gt;at_has_ecam = 1;
+  }
+
+  /* disable unsolicited signaling of call list */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CLCC=0&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CLCC=0 failed, continue\n&quot;, CELLIAX_P_LOG);
+    p-&gt;at_has_clcc = 0;
+  } else {
+    p-&gt;at_has_clcc = 1;
+  }
+
+  /* give unsolicited caller id when incoming call */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+CLIP=1&quot;);
+  if (res) {
+    DEBUGA_AT(&quot;AT+CLIP failed, continue\n&quot;, CELLIAX_P_LOG);
+  }
+  /* for motorola */
+  res = celliax_serial_write_AT_ack(p, &quot;AT+MCST=1&quot;);    /* motorola call control codes
+                                                           (to know when call is disconnected (they
+                                                           don't give you &quot;no carrier&quot;) */
+  if (res) {
+    DEBUGA_AT(&quot;AT+MCST=1 does not get OK from the phone. If it is NOT Motorola,&quot;
+              &quot; no problem.\n&quot;, CELLIAX_P_LOG);
+  }
+  /* for motorola end */
+
+/* go until first empty postinit string, or last postinit string */
+  while (1) {
+
+    if (strlen(p-&gt;at_postinit_1)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_postinit_1, p-&gt;at_postinit_1_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_postinit_1, p-&gt;at_postinit_1_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_postinit_2)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_postinit_2, p-&gt;at_postinit_2_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_postinit_2, p-&gt;at_postinit_2_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_postinit_3)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_postinit_3, p-&gt;at_postinit_3_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_postinit_3, p-&gt;at_postinit_3_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_postinit_4)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_postinit_4, p-&gt;at_postinit_4_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_postinit_4, p-&gt;at_postinit_4_expect);
+      }
+    } else {
+      break;
+    }
+
+    if (strlen(p-&gt;at_postinit_5)) {
+      res = celliax_serial_write_AT_expect(p, p-&gt;at_postinit_5, p-&gt;at_postinit_5_expect);
+      if (res) {
+        DEBUGA_AT(&quot;%s does not get %s from the phone. Continuing.\n&quot;, CELLIAX_P_LOG,
+                  p-&gt;at_postinit_5, p-&gt;at_postinit_5_expect);
+      }
+    } else {
+      break;
+    }
+
+    break;
+  }
+
+  return 0;
+}
+
+int celliax_serial_call_AT(struct celliax_pvt *p, char *dstr)
+{
+  int res;
+  char at_command[256];
+
+  if (option_debug)
+    DEBUGA_PBX(&quot;Dialing %s\n&quot;, CELLIAX_P_LOG, dstr);
+  memset(at_command, 0, sizeof(at_command));
+  p-&gt;phone_callflow = CALLFLOW_CALL_DIALING;
+  p-&gt;interface_state = AST_STATE_DIALING;
+  ast_uri_decode(dstr);
+  size_t fixdstr = strspn(dstr, AST_DIGIT_ANYDIG);
+  if (fixdstr == 0) {
+    ERRORA(&quot;dial command failed because of invalid dial number. dial string was: %s\n&quot;,
+           CELLIAX_P_LOG, dstr);
+    return -1;
+  }
+  dstr[fixdstr] = '\0';
+  sprintf(at_command, &quot;%s%s%s&quot;, p-&gt;at_dial_pre_number, dstr, p-&gt;at_dial_post_number);
+  res = celliax_serial_write_AT_expect(p, at_command, p-&gt;at_dial_expect);
+  if (res) {
+    ERRORA(&quot;dial command failed, dial string was: %s\n&quot;, CELLIAX_P_LOG, at_command);
+    return -1;
+  }
+  // jet - early audio
+  if (p-&gt;at_early_audio) {
+    ast_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+  }
+
+  return 0;
+}
+
+int celliax_console_at(int fd, int argc, char *argv[])
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+  char at_cmd[1024];
+  int i, a, c;
+
+  if (argc == 1)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; console for celliax_at, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+  if (p-&gt;controldevprotocol != PROTOCOL_AT) {
+    ast_cli(fd,
+            &quot;The \&quot;current\&quot; console is not connected to an 'AT modem' (cellphone)\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  memset(at_cmd, 0, sizeof(at_cmd));
+  c = 0;
+  for (i = 1; i &lt; argc; i++) {
+    for (a = 0; a &lt; strlen(argv[i]); a++) {
+      at_cmd[c] = argv[i][a];
+      c++;
+      if (c == 1022)
+        break;
+    }
+    if (i != argc - 1) {
+      at_cmd[c] = ' ';
+      c++;
+    }
+    if (c == 1023)
+      break;
+  }
+  celliax_serial_write_AT_noack(p, at_cmd);
+  return RESULT_SUCCESS;
+}
+
+#ifdef ASTERISK_VERSION_1_2
+int celliax_manager_sendsms(struct mansession *s, struct message *m)
+#endif //ASTERISK_VERSION_1_2
+#ifdef ASTERISK_VERSION_1_4
+int celliax_manager_sendsms(struct mansession *s, const struct message *m)
+#endif //ASTERISK_VERSION_1_4
+{
+  int ret;
+  char command[512];
+  const char *interfacename = astman_get_header(m, &quot;Interface&quot;);
+  const char *destinationnumber = astman_get_header(m, &quot;Number&quot;);
+  const char *text = astman_get_header(m, &quot;Text&quot;);
+  const char *action_id = astman_get_header(m, &quot;ActionID&quot;);
+
+  if (ast_strlen_zero(interfacename)) {
+    astman_send_error(s, m, &quot;Interface: missing.\n&quot;);
+    return 0;
+  }
+  if (ast_strlen_zero(destinationnumber)) {
+    astman_send_error(s, m, &quot;Number: missing.\n&quot;);
+    return 0;
+  }
+  if (ast_strlen_zero(text)) {
+    astman_send_error(s, m, &quot;Text: missing.\n&quot;);
+    return 0;
+  }
+  if (ast_strlen_zero(action_id)) {
+    astman_send_error(s, m, &quot;ActionID: missing.\n&quot;);
+    return 0;
+  }
+
+  memset(command, 0, sizeof(command));
+
+  sprintf(command, &quot;%s/%s|%s|&quot;, interfacename, destinationnumber, text);
+
+  ret = celliax_sendsms(NULL, (void *) &amp;command);
+
+#ifndef ASTERISK_VERSION_1_4
+  if (!ret) {
+    ast_cli(s-&gt;fd, &quot;Response: Success\r\n&quot;);
+    if (!ast_strlen_zero(action_id))
+      ast_cli(s-&gt;fd, &quot;ActionID: %s\r\n&quot;, action_id);
+    ast_cli(s-&gt;fd, &quot;\r\n&quot;);
+    return RESULT_SUCCESS;
+  } else {
+    ast_cli(s-&gt;fd, &quot;Response: Error\r\n&quot;);
+    if (!ast_strlen_zero(action_id))
+      ast_cli(s-&gt;fd, &quot;ActionID: %s\r\n&quot;, action_id);
+    ast_cli(s-&gt;fd, &quot;Message: celliax_manager_sendsms failed\r\n&quot;);
+    ast_cli(s-&gt;fd, &quot;\r\n&quot;);
+    return 0;
+  }
+#else /* ASTERISK_VERSION_1_4 */
+  if (!ret) {
+    astman_append(s, &quot;Response: Success\r\n&quot;);
+    if (!ast_strlen_zero(action_id))
+      astman_append(s, &quot;ActionID: %s\r\n&quot;, action_id);
+    astman_append(s, &quot;\r\n&quot;);
+    return RESULT_SUCCESS;
+  } else {
+    astman_append(s, &quot;Response: Error\r\n&quot;);
+    if (!ast_strlen_zero(action_id))
+      astman_append(s, &quot;ActionID: %s\r\n&quot;, action_id);
+    astman_append(s, &quot;Message: celliax_manager_sendsms failed\r\n&quot;);
+    astman_append(s, &quot;\r\n&quot;);
+    return 0;
+  }
+#endif /* ASTERISK_VERSION_1_4 */
+
+  return RESULT_SUCCESS;        //never reached
+}
+
+int ucs2_to_utf8(struct celliax_pvt *p, 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_AT(&quot;ucs2_in=%s\n&quot;, CELLIAX_P_LOG, ucs2_in);
+  /* cicopet */
+  for (c = 0; c &lt; strlen(ucs2_in); c++) {
+    sprintf(stringa, &quot;0x%c%c&quot;, 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(&quot;UTF8&quot;, &quot;UCS-2BE&quot;);
+  if (iconv_format == (iconv_t) - 1) {
+    ERRORA(&quot;error: %s\n&quot;, CELLIAX_P_LOG, strerror(errno));
+    return -1;
+  }
+
+  inbytesleft = i;
+  iconv_res = iconv(iconv_format, &amp;inbuf, &amp;inbytesleft, &amp;outbuf, &amp;outbytesleft);
+  if (iconv_res == (size_t) - 1) {
+    DEBUGA_AT(&quot;ciao in=%s, inleft=%d, out=%s, outleft=%d, converted=%s, utf8_out=%s\n&quot;,
+              CELLIAX_P_LOG, inbuf, inbytesleft, outbuf, outbytesleft, converted,
+              utf8_out);
+    DEBUGA_AT(&quot;error: %s %d\n&quot;, CELLIAX_P_LOG, strerror(errno), errno);
+    return -1;
+  }
+  DEBUGA_AT
+    (&quot;iconv_res=%d,  in=%s, inleft=%d, out=%s, outleft=%d, converted=%s, utf8_out=%s\n&quot;,
+     CELLIAX_P_LOG, iconv_res, inbuf, inbytesleft, outbuf, outbytesleft, converted,
+     utf8_out);
+  iconv_close(iconv_format);
+
+  return 0;
+}
+
+int utf_to_ucs2(struct celliax_pvt *p, 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(&quot;UCS-2BE&quot;, &quot;UTF8&quot;);
+  if (iconv_format == (iconv_t) - 1) {
+    ERRORA(&quot;error: %s\n&quot;, CELLIAX_P_LOG, strerror(errno));
+    return -1;
+  }
+  outbytesleft = 16000;
+
+  DEBUGA_AT(&quot;in=%s, inleft=%d, out=%s, outleft=%d, utf_in=%s, converted=%s\n&quot;,
+            CELLIAX_P_LOG, inbuf, inbytesleft, outbuf, outbytesleft, utf_in, converted);
+  iconv_res = iconv(iconv_format, &amp;inbuf, &amp;inbytesleft, &amp;outbuf, &amp;outbytesleft);
+  if (iconv_res == (size_t) - 1) {
+    ERRORA(&quot;error: %s %d\n&quot;, CELLIAX_P_LOG, strerror(errno), errno);
+    return -1;
+  }
+  DEBUGA_AT
+    (&quot;iconv_res=%d,  in=%s, inleft=%d, out=%s, outleft=%d, utf_in=%s, converted=%s\n&quot;,
+     CELLIAX_P_LOG, iconv_res, inbuf, inbytesleft, outbuf, outbytesleft, utf_in,
+     converted);
+  iconv_close(iconv_format);
+
+  for (i = 0; i &lt; 16000 - outbytesleft; i++) {
+    memset(stringa, '\0', sizeof(stringa));
+    memset(stringa2, '\0', sizeof(stringa2));
+    sprintf(stringa, &quot;%02X&quot;, converted[i]);
+    DEBUGA_AT(&quot;character is |%02X|\n&quot;, CELLIAX_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_AT(&quot;stringa=%s, stringa2=%s, ucs2_out=%s\n&quot;, CELLIAX_P_LOG, stringa, stringa2,
+              ucs2_out);
+  }
+  return 0;
+}
+
+int celliax_sendsms(struct ast_channel *c, void *data)
+{
+  char *idest = data;
+  char rdest[256];
+  struct celliax_pvt *p = NULL;
+  char *device;
+  char *dest;
+  char *text;
+  char *stringp = NULL;
+  int found = 0;
+  int failed = 0;
+
+  strncpy(rdest, idest, sizeof(rdest) - 1);
+  ast_log(LOG_DEBUG, &quot;CelliaxSendsms: %s\n&quot;, rdest);
+  ast_log(LOG_DEBUG, &quot;START\n&quot;);
+  /* we can use celliax_request to get the channel, but celliax_request would look for onowned channels, and probably we can send SMSs while a call is ongoing
+   *
+   */
+
+  stringp = rdest;
+  device = strsep(&amp;stringp, &quot;/&quot;);
+  dest = strsep(&amp;stringp, &quot;|&quot;);
+  text = strsep(&amp;stringp, &quot;|&quot;);
+
+  if (!device) {
+    ast_log(LOG_ERROR,
+            &quot;CelliaxSendsms app do not recognize '%s'. Requires a destination with slashes (interfacename/destinationnumber, TEXT)\n&quot;,
+            idest);
+    return -1;
+  }
+
+  if (!dest) {
+    ast_log(LOG_ERROR,
+            &quot;CelliaxSendsms app do not recognize '%s'. Requires a destination with slashes (interfacename/destinationnumber, TEXT)\n&quot;,
+            idest);
+    return -1;
+  }
+
+  if (!text) {
+    ast_log(LOG_ERROR,
+            &quot;CelliaxSendsms app do not recognize '%s'. Requires a destination with slashes (interfacename/destinationnumber, TEXT)\n&quot;,
+            idest);
+    return -1;
+  }
+
+  ast_log(LOG_DEBUG, &quot;interfacename:%s, destinationnumber:%s, text:%s\n&quot;, device, dest,
+          text);
+
+  /* lock the interfaces' list */
+  LOKKA(&amp;celliax_iflock);
+  /* make a pointer to the first interface in the interfaces list */
+  p = celliax_iflist;
+  /* Search for the requested interface and verify if is unowned */
+  //TODO implement groups a la chan_zap
+  while (p) {
+    size_t length = strlen(p-&gt;name);
+    /* is this the requested interface? */
+    if (strncmp(device, p-&gt;name, length) == 0) {
+      /* this is the requested interface! */
+      if (option_debug)
+        DEBUGA_AT(&quot;FOUND! interfacename:%s, destinationnumber:%s, text:%s, p-&gt;name=%s\n&quot;,
+                  CELLIAX_P_LOG, device, dest, text, p-&gt;name);
+      found = 1;
+      break;
+
+    }
+    /* not yet found, next please */
+    p = p-&gt;next;
+  }
+  /* unlock the interfaces' list */
+  UNLOCKA(&amp;celliax_iflock);
+
+  if (!found) {
+    ast_log(LOG_ERROR, &quot;Interface '%s' requested by CelliaxSendsms NOT FOUND\n&quot;, device);
+    return RESULT_FAILURE;
+  }
+
+  if (p-&gt;controldevprotocol != PROTOCOL_AT) {
+    ERRORA(&quot;CelliaxSendsms supports only AT command cellphones at the moment :-( !\n&quot;,
+           CELLIAX_P_LOG);
+    return RESULT_FAILURE;
+  }
+
+  if (p-&gt;controldevprotocol == PROTOCOL_AT) {
+    int err = 0;
+    char smscommand[16000];
+    memset(smscommand, '\0', sizeof(smscommand));
+
+    PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+    LOKKA(&amp;p-&gt;controldev_lock);
+
+    if (p-&gt;no_ucs2) {
+      sprintf(smscommand, &quot;AT+CMGS=\&quot;%s\&quot;&quot;, dest);  //TODO: support phones that only accept pdu mode
+    } else {
+      char dest2[1048];
+
+          err = celliax_serial_write_AT_ack(p, &quot;AT+CSCS=\&quot;UCS2\&quot;&quot;);
+          if (err) {
+            ERRORA
+              (&quot;AT+CSCS=\&quot;UCS2\&quot; (set TE messages to ucs2)  do not got OK from the phone\n&quot;,
+               CELLIAX_P_LOG);
+          }
+
+      memset(dest2, '\0', sizeof(dest2));
+      utf_to_ucs2(p, dest, strlen(dest), dest2, sizeof(dest2));
+      sprintf(smscommand, &quot;AT+CMGS=\&quot;%s\&quot;&quot;, 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 = celliax_serial_write_AT_noack(p, smscommand);
+    if (err) {
+      ERRORA(&quot;Error sending SMS\n&quot;, CELLIAX_P_LOG);
+      failed = 1;
+      goto uscita;
+    }
+    err = celliax_serial_AT_expect(p, &quot;&gt; &quot;, 0, 1);  // wait 1.5s for the prompt, no  crlf
+#if 1
+    if (err) {
+      DEBUGA_AT
+        (&quot;Error or timeout getting prompt '&gt; ' 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&quot;,
+         CELLIAX_P_LOG);
+
+      err = celliax_serial_write_AT_ack(p, &quot;ATE1&quot;); //motorola (at least c350) do not echo the '&gt;' prompt when in ATE0... go figure!!!!
+      if (err) {
+        ERRORA(&quot;Error activating echo from modem\n&quot;, CELLIAX_P_LOG);
+      }
+      p-&gt;at_cmgw[0] = '\0';
+      sprintf(smscommand, &quot;AT+CMGW=\&quot;%s\&quot;&quot;, dest);  //TODO: support phones that only accept pdu mode
+      err = celliax_serial_write_AT_noack(p, smscommand);
+      if (err) {
+        ERRORA(&quot;Error writing SMS destination to the cellphone memory\n&quot;, CELLIAX_P_LOG);
+        failed = 1;
+        goto uscita;
+      }
+      err = celliax_serial_AT_expect(p, &quot;&gt; &quot;, 0, 1);    // wait 1.5s for the prompt, no  crlf
+      if (err) {
+        ERRORA
+          (&quot;Error or timeout getting prompt '&gt; ' for writing sms text in cellphone memory\n&quot;,
+           CELLIAX_P_LOG);
+        failed = 1;
+        goto uscita;
+      }
+    }
+#endif
+
+    //sprintf(text,&quot;ciao 123 belè новости לק ראת ﺎﻠﺠﻤﻋﺓ 人大&quot;); //let's test the beauty of utf
+    memset(smscommand, '\0', sizeof(smscommand));
+    if (p-&gt;no_ucs2) {
+      sprintf(smscommand, &quot;%s&quot;, text);
+    } else {
+      utf_to_ucs2(p, text, strlen(text), smscommand, sizeof(smscommand));
+    }
+
+    smscommand[strlen(smscommand)] = 0x1A;
+    DEBUGA_AT(&quot;smscommand len is: %d, text is:|||%s|||\n&quot;, CELLIAX_P_LOG,
+              strlen(smscommand), smscommand);
+
+    err = celliax_serial_write_AT_ack_nocr_longtime(p, smscommand);
+    //TODO would be better to unlock controldev here
+    if (err) {
+      ERRORA(&quot;Error writing SMS text to the cellphone memory\n&quot;, CELLIAX_P_LOG);
+      //return RESULT_FAILURE;
+      failed = 1;
+      goto uscita;
+    }
+    if (p-&gt;at_cmgw[0]) {
+      sprintf(smscommand, &quot;AT+CMSS=%s&quot;, p-&gt;at_cmgw);
+      err = celliax_serial_write_AT_expect_longtime(p, smscommand, &quot;OK&quot;);
+      if (err) {
+        ERRORA(&quot;Error sending SMS from the cellphone memory\n&quot;, CELLIAX_P_LOG);
+        //return RESULT_FAILURE;
+        failed = 1;
+        goto uscita;
+      }
+
+      err = celliax_serial_write_AT_ack(p, &quot;ATE0&quot;); //motorola (at least c350) do not echo the '&gt;' prompt when in ATE0... go figure!!!!
+      if (err) {
+        ERRORA(&quot;Error de-activating echo from modem\n&quot;, CELLIAX_P_LOG);
+      }
+    }
+  uscita:
+    usleep(1000);
+
+    if (p-&gt;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, &quot;AT+CMGR=%s&quot;, p-&gt;at_cmgw);
+      err = celliax_serial_write_AT_ack(p, smscommand);
+      if (err) {
+        ERRORA(&quot;Error reading SMS back from the cellphone memory\n&quot;, CELLIAX_P_LOG);
+      }
+
+      /* let's delete from cellphone memory what we've sent */
+      sprintf(smscommand, &quot;AT+CMGD=%s&quot;, p-&gt;at_cmgw);
+      err = celliax_serial_write_AT_ack(p, smscommand);
+      if (err) {
+        ERRORA(&quot;Error deleting SMS from the cellphone memory\n&quot;, CELLIAX_P_LOG);
+      }
+
+      p-&gt;at_cmgw[0] = '\0';
+    }
+    //usleep(500000);             //.5 secs
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  }
+
+  ast_log(LOG_DEBUG, &quot;FINISH\n&quot;);
+  if (failed)
+    return -1;
+  else
+    return RESULT_SUCCESS;
+}
+
+#ifdef CELLIAX_DIR
+/* For simplicity, I'm keeping the format compatible with the voicemail config,
+   but i'm open to suggestions for isolating it */
+#define CELLIAX_DIR_CONFIG &quot;directoriax.conf&quot;
+
+/* How many digits to read in */
+#define CELLIAX_DIR_NUMDIGITS 3
+
+struct ast_config *celliax_dir_realtime(char *context)
+{
+  //TODO: all the realtime stuff has to be re-made
+  struct ast_config *cfg;
+  struct celliax_pvt *p = NULL;
+#ifdef ASTERISK_VERSION_1_6_0
+  struct ast_flags config_flags = { 0 };
+#endif /* ASTERISK_VERSION_1_6_0 */
+
+  /* Load flat file config. */
+#ifdef ASTERISK_VERSION_1_6_0
+  cfg = ast_config_load(CELLIAX_DIR_CONFIG, config_flags);
+#else
+  cfg = ast_config_load(CELLIAX_DIR_CONFIG);
+#endif /* ASTERISK_VERSION_1_6_0 */
+
+  if (!cfg) {
+    /* Loading config failed. */
+    WARNINGA
+      (&quot;Loading directoriax.conf config file failed. It's not necessary, continuing.\n&quot;,
+       CELLIAX_P_LOG);
+    return NULL;
+  }
+  return cfg;
+}
+
+static char *celliax_dir_convert(char *lastname)
+{
+  char *tmp;
+  int lcount = 0;
+  tmp = malloc(CELLIAX_DIR_NUMDIGITS + 1);
+  if (tmp) {
+    while ((*lastname &gt; 32) &amp;&amp; lcount &lt; CELLIAX_DIR_NUMDIGITS) {
+      switch (toupper(*lastname)) {
+      case '1':
+        tmp[lcount++] = '1';
+        break;
+      case '2':
+      case 'A':
+      case 'B':
+      case 'C':
+        tmp[lcount++] = '2';
+        break;
+      case '3':
+      case 'D':
+      case 'E':
+      case 'F':
+        tmp[lcount++] = '3';
+        break;
+      case '4':
+      case 'G':
+      case 'H':
+      case 'I':
+        tmp[lcount++] = '4';
+        break;
+      case '5':
+      case 'J':
+      case 'K':
+      case 'L':
+        tmp[lcount++] = '5';
+        break;
+      case '6':
+      case 'M':
+      case 'N':
+      case 'O':
+        tmp[lcount++] = '6';
+        break;
+      case '7':
+      case 'P':
+      case 'Q':
+      case 'R':
+      case 'S':
+        tmp[lcount++] = '7';
+        break;
+      case '8':
+      case 'T':
+      case 'U':
+      case 'V':
+        tmp[lcount++] = '8';
+        break;
+      case '9':
+      case 'W':
+      case 'X':
+      case 'Y':
+      case 'Z':
+        tmp[lcount++] = '9';
+        break;
+      }
+      lastname++;
+    }
+    tmp[lcount] = '\0';
+  }
+  return tmp;
+}
+
+int celliax_console_celliax_dir_export(int fd, int argc, char *argv[])
+{
+  struct ast_config *cfg;
+
+  struct ast_variable *v;
+  char *start, *pos, *stringp, *space, *options = NULL, *conv = NULL;
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+  char *context = &quot;default&quot;;
+  char *s;
+  char *var, *value;
+  int fromcell = 0;
+  int fromskype = 0;
+  char name[256] = &quot;&quot;;
+  char phonebook_direct_calling_ext[7] = &quot;&quot;;
+  char write_entry_command[256] = &quot;&quot;;
+  char entry_number[256] = &quot;&quot;;
+  char entry_text[256] = &quot;&quot;;
+  char final_entry_text[256] = &quot;&quot;;
+  int res;
+  int tocell = 0;
+#ifdef CELLIAX_LIBCSV
+  int tocsv = 0;
+  int tovcf = 0;
+#endif /* CELLIAX_LIBCSV */
+
+  if (argc &lt; 3 || argc &gt; 4)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd, &quot;No \&quot;current\&quot; console ???, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (!strcasecmp(argv[1], &quot;tocell&quot;))
+    tocell = 1;
+#ifdef CELLIAX_LIBCSV
+  else if (!strcasecmp(argv[1], &quot;tocsv&quot;))
+    tocsv = 1;
+  else if (!strcasecmp(argv[1], &quot;tovcf&quot;))
+    tovcf = 1;
+#endif /* CELLIAX_LIBCSV */
+  else {
+    ast_cli(fd,
+#ifdef CELLIAX_LIBCSV
+            &quot;\n\nYou have neither specified 'tocell' nor 'tocsv'\n\n&quot;);
+#else /* CELLIAX_LIBCSV */
+            &quot;\n\nYou have not specified 'tocell'\n\n&quot;);
+#endif /* CELLIAX_LIBCSV */
+    return RESULT_SHOWUSAGE;
+  }
+  if (tocell)
+    if (p-&gt;controldevprotocol != PROTOCOL_AT) {
+      ast_cli(fd,
+              &quot;Exporting to the cellphone phonebook is currently supported only on \&quot;AT\&quot; cellphones :( !\n&quot;);
+      return RESULT_SUCCESS;
+    }
+#ifdef CELLIAX_LIBCSV
+  if (tocsv || tovcf)
+    if (argc != 4) {
+      ast_cli(fd, &quot;\n\nYou have to specify a filename with 'tocsv'\n\n&quot;);
+      return RESULT_SHOWUSAGE;
+    }
+#endif /* CELLIAX_LIBCSV */
+
+  if (option_debug)
+    NOTICA(&quot;celliax_cellphonenumber is: %s\n&quot;, CELLIAX_P_LOG, argv[2]);
+
+#ifdef CELLIAX_LIBCSV
+  if (tocsv) {
+    if (option_debug)
+      NOTICA(&quot;filename is: %s\n&quot;, CELLIAX_P_LOG, argv[3]);
+    //ast_cli(fd, &quot;\n\nnot yet implemented :P \n&quot;);
+    //return RESULT_SUCCESS;
+  }
+  if (tovcf) {
+    if (option_debug)
+      NOTICA(&quot;filename is: %s\n&quot;, CELLIAX_P_LOG, argv[3]);
+    ast_cli(fd, &quot;\n\nnot yet implemented :P \n&quot;);
+    return RESULT_SUCCESS;
+  }
+#endif /* CELLIAX_LIBCSV */
+
+  cfg = celliax_dir_realtime(context);
+  if (!cfg) {
+    return -1;
+  }
+
+  if (tocell) {
+    /* which phonebook to use, use the SIM  */
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CPBS=SM&quot;);
+    if (res) {
+      WARNINGA(&quot;AT+CPBS=SM failed, continue\n&quot;, CELLIAX_P_LOG);
+    }
+    /* which phonebook to use, trying to use phone, not SIM  */
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CPBS=ME&quot;);
+    if (res) {
+      WARNINGA(&quot;AT+CPBS=ME failed, continue\n&quot;, CELLIAX_P_LOG);
+    }
+    /* retrieve the fields lenght in the selected phonebook  */
+    p-&gt;phonebook_querying = 1;
+    res = celliax_serial_write_AT_ack(p, &quot;AT+CPBR=?&quot;);
+    if (res) {
+      WARNINGA(&quot;AT+CPBR=? failed, continue\n&quot;, CELLIAX_P_LOG);
+    }
+    p-&gt;phonebook_querying = 0;
+
+    v = ast_variable_browse(cfg, context);
+    /* Find all candidate extensions */
+    while (v) {
+      /* Find a candidate extension */
+      start = strdup(v-&gt;value);
+      if (strcasestr(start, &quot;fromcell=yes&quot;)) {
+        fromcell = 1;
+        fromskype = 0;
+
+      }
+      if (strcasestr(start, &quot;fromskype=yes&quot;)) {
+        fromcell = 0;
+        fromskype = 1;
+
+      }
+
+      if (start &amp;&amp; !strcasestr(start, &quot;hidefromdir=yes&quot;)) {
+        memset(name, 0, sizeof(name));
+        memset(phonebook_direct_calling_ext, 0, sizeof(phonebook_direct_calling_ext));
+        memset(write_entry_command, 0, sizeof(write_entry_command));
+        memset(entry_number, 0, sizeof(entry_number));
+        memset(entry_text, 0, sizeof(entry_text));
+        memset(final_entry_text, 0, sizeof(final_entry_text));
+
+        DEBUGA_AT(&quot;v-&gt;name=%s\n&quot;, CELLIAX_P_LOG, v-&gt;name);
+        DEBUGA_AT(&quot;v-&gt;value=%s\n&quot;, CELLIAX_P_LOG, v-&gt;value);
+
+        stringp = start;
+        strsep(&amp;stringp, &quot;,&quot;);
+        pos = strsep(&amp;stringp, &quot;,&quot;);
+        if (pos) {
+          ast_copy_string(name, pos, sizeof(name));
+          if (strchr(pos, ' ')) {
+            space = strchr(pos, ' ');
+            *space = '\0';
+          }
+          if (pos) {
+            conv = celliax_dir_convert(pos);
+            DEBUGA_AT(&quot;&lt;pos=&gt;%s&lt;conv=&gt;%s&lt;\n&quot;, CELLIAX_P_LOG, pos, conv);
+
+            options = strdup(v-&gt;value);
+            strsep(&amp;options, &quot;,&quot;);
+            strsep(&amp;options, &quot;,&quot;);
+            strsep(&amp;options, &quot;,&quot;);
+            strsep(&amp;options, &quot;,&quot;);
+            DEBUGA_AT(&quot;options=%s\n&quot;, CELLIAX_P_LOG, options);
+
+            while ((s = strsep(&amp;options, &quot;|&quot;))) {
+              value = s;
+              if ((var = strsep(&amp;value, &quot;=&quot;)) &amp;&amp; value) {
+                DEBUGA_AT(&quot;var=%s value=%s\n&quot;, CELLIAX_P_LOG, var, value);
+                if (!strcmp(var, &quot;phonebook_direct_calling_ext&quot;))
+                  strncpy(phonebook_direct_calling_ext, value, 6);
+              }
+            }
+
+            res =
+              snprintf(entry_number, p-&gt;phonebook_number_lenght + 1, &quot;%s%s%d%s%s&quot;,
+                       argv[2], &quot;p&quot;, p-&gt;celliax_dir_prefix, &quot;p&quot;,
+                       phonebook_direct_calling_ext);
+            if (res == (p-&gt;phonebook_number_lenght + 1)
+                || res &gt; (p-&gt;phonebook_number_lenght + 1)) {
+              ERRORA(&quot;entry_number truncated, was: '%s%s%d%s%s', now is: '%s'\n&quot;,
+                     CELLIAX_P_LOG, argv[2], &quot;p&quot;, p-&gt;celliax_dir_prefix, &quot;p&quot;,
+                     phonebook_direct_calling_ext, entry_number);
+              //FIXME: abort ???
+
+            }
+
+            res = snprintf(final_entry_text, p-&gt;phonebook_text_lenght + 1, &quot;%s&quot;, name); //FIXME result not checked
+
+            res =
+              snprintf(write_entry_command, sizeof(write_entry_command) - 1,
+                       &quot;AT+CPBW=,\&quot;%s\&quot;,,\&quot;%s\&quot;&quot;, entry_number, final_entry_text);
+            if (res == (sizeof(write_entry_command) - 1)
+                || res &gt; (sizeof(write_entry_command) - 1)) {
+              WARNINGA
+                (&quot;write_entry_command truncated, was supposed: 'AT+CPBW=,\&quot;%s\&quot;,,\&quot;%s\&quot;', now is: '%s'\n&quot;,
+                 CELLIAX_P_LOG, entry_number, final_entry_text, write_entry_command);
+            }
+            //if (option_debug)
+            NOTICA(&quot;%s\n&quot;, CELLIAX_P_LOG, write_entry_command);
+          }
+        }
+        if (conv)
+          free(conv);
+        if (start)
+          free(start);
+        if (options)
+          free(options);
+      }
+      v = v-&gt;next;
+    }
+  }
+#ifdef CELLIAX_LIBCSV
+  if (tocsv) {
+
+    v = ast_variable_browse(cfg, context);
+    /* Find all candidate extensions */
+    while (v) {
+      /* Find a candidate extension */
+      start = strdup(v-&gt;value);
+      if (strcasestr(start, &quot;fromcell=yes&quot;)) {
+        fromcell = 1;
+        fromskype = 0;
+
+      }
+      if (strcasestr(start, &quot;fromskype=yes&quot;)) {
+        fromcell = 0;
+        fromskype = 1;
+
+      }
+
+      if (start &amp;&amp; !strcasestr(start, &quot;hidefromdir=yes&quot;)) {
+        memset(name, 0, sizeof(name));
+        memset(phonebook_direct_calling_ext, 0, sizeof(phonebook_direct_calling_ext));
+        memset(write_entry_command, 0, sizeof(write_entry_command));
+        memset(entry_number, 0, sizeof(entry_number));
+        memset(entry_text, 0, sizeof(entry_text));
+        memset(final_entry_text, 0, sizeof(final_entry_text));
+
+        DEBUGA_AT(&quot;v-&gt;name=%s\n&quot;, CELLIAX_P_LOG, v-&gt;name);
+        DEBUGA_AT(&quot;v-&gt;value=%s\n&quot;, CELLIAX_P_LOG, v-&gt;value);
+
+        stringp = start;
+        strsep(&amp;stringp, &quot;,&quot;);
+        pos = strsep(&amp;stringp, &quot;,&quot;);
+        if (pos) {
+          ast_copy_string(name, pos, sizeof(name));
+          if (strchr(pos, ' ')) {
+            space = strchr(pos, ' ');
+            *space = '\0';
+          }
+          if (pos) {
+            conv = celliax_dir_convert(pos);
+            DEBUGA_AT(&quot;&lt;pos=&gt;%s&lt;conv=&gt;%s&lt;\n&quot;, CELLIAX_P_LOG, pos, conv);
+
+            options = strdup(v-&gt;value);
+            strsep(&amp;options, &quot;,&quot;);
+            strsep(&amp;options, &quot;,&quot;);
+            strsep(&amp;options, &quot;,&quot;);
+            strsep(&amp;options, &quot;,&quot;);
+            DEBUGA_AT(&quot;options=%s\n&quot;, CELLIAX_P_LOG, options);
+
+            while ((s = strsep(&amp;options, &quot;|&quot;))) {
+              value = s;
+              if ((var = strsep(&amp;value, &quot;=&quot;)) &amp;&amp; value) {
+                DEBUGA_AT(&quot;var=%s value=%s\n&quot;, CELLIAX_P_LOG, var, value);
+                if (!strcmp(var, &quot;phonebook_direct_calling_ext&quot;))
+                  strncpy(phonebook_direct_calling_ext, value, 6);
+              }
+            }
+
+            //FIXME choose a logic for fields maximum lenght
+            res =
+              snprintf(entry_number, sizeof(entry_number) - 1, &quot;%s%s%d%s%s&quot;, argv[2], &quot;p&quot;,
+                       p-&gt;celliax_dir_prefix, &quot;p&quot;, phonebook_direct_calling_ext);
+            if (res == (sizeof(entry_number) - 1)
+                || res &gt; (sizeof(entry_number) - 1)) {
+              ERRORA(&quot;entry_number truncated, was: '%s%s%d%s%s', now is: '%s'\n&quot;,
+                     CELLIAX_P_LOG, argv[2], &quot;p&quot;, p-&gt;celliax_dir_prefix, &quot;p&quot;,
+                     phonebook_direct_calling_ext, entry_number);
+              //FIXME: abort ???
+
+            }
+
+            res = snprintf(final_entry_text, sizeof(final_entry_text) - 1, &quot;%s&quot;, name); //FIXME result not checked
+
+            int i, a;
+
+            a = 0;
+            for (i = 0; i &lt; p-&gt;csv_complete_name_pos - 1; i++) {
+              if (p-&gt;csv_separator_is_semicolon)
+                write_entry_command[a] = ';';
+              else
+                write_entry_command[a] = ',';
+              a++;
+            }
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+
+            write_entry_command[a] = '&quot;';
+            a++;
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+            for (i = 0; i &lt; strlen(final_entry_text); i++) {
+              write_entry_command[a] = final_entry_text[i];
+              a++;
+            }
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+            write_entry_command[a] = '&quot;';
+            a++;
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+            for (i = 0; i &lt; (p-&gt;csv_business_phone_pos - p-&gt;csv_complete_name_pos); i++) {
+              if (p-&gt;csv_separator_is_semicolon)
+                write_entry_command[a] = ';';
+              else
+                write_entry_command[a] = ',';
+              a++;
+            }
+
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+
+            write_entry_command[a] = '&quot;';
+            a++;
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+            for (i = 0; i &lt; strlen(entry_number); i++) {
+              write_entry_command[a] = entry_number[i];
+              a++;
+            }
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+            write_entry_command[a] = '&quot;';
+            a++;
+            //NOTICA(&quot;i=%d a=%d\n&quot;, CELLIAX_P_LOG, i, a);
+
+            if (option_debug)
+              NOTICA(&quot;%s\n&quot;, CELLIAX_P_LOG, write_entry_command);
+          }
+        }
+        if (conv)
+          free(conv);
+        if (start)
+          free(start);
+        if (options)
+          free(options);
+      }
+      v = v-&gt;next;
+    }
+
+  }
+  if (tovcf) {
+//TODO implementation here
+  }
+#endif /*  CELLIAX_LIBCSV */
+  ast_config_destroy(cfg);
+  return 0;
+}
+
+#ifdef CELLIAX_LIBCSV
+
+void celliax_cb1(char *s, size_t len, void *data)
+{
+  struct celliax_pvt *p = data;
+  char field_content[256];
+
+  p-&gt;csv_fields++;
+  memset(field_content, 0, sizeof(field_content));
+  strncpy(field_content, s,
+          sizeof(field_content) &gt; (len + 1) ? len : (sizeof(field_content) - 1));
+  if (p-&gt;csv_fields == p-&gt;csv_complete_name_pos) {
+    strncpy(p-&gt;csv_complete_name, field_content, sizeof(p-&gt;csv_complete_name) - 1);
+  }
+  if (p-&gt;csv_fields == p-&gt;csv_email_pos) {
+    strncpy(p-&gt;csv_email, field_content, sizeof(p-&gt;csv_email) - 1);
+  }
+  if (p-&gt;csv_fields == p-&gt;csv_home_phone_pos) {
+    strncpy(p-&gt;csv_home_phone, field_content, sizeof(p-&gt;csv_home_phone) - 1);
+  }
+  if (p-&gt;csv_fields == p-&gt;csv_mobile_phone_pos) {
+    strncpy(p-&gt;csv_mobile_phone, field_content, sizeof(p-&gt;csv_mobile_phone) - 1);
+  }
+  if (p-&gt;csv_fields == p-&gt;csv_business_phone_pos) {
+    strncpy(p-&gt;csv_business_phone, field_content, sizeof(p-&gt;csv_business_phone) - 1);
+  }
+}
+
+void celliax_cb2(char c, void *data)
+{
+  struct celliax_pvt *p = data;
+
+  p-&gt;csv_rows++;
+  p-&gt;csv_fields = 0;
+
+  if (p-&gt;csv_first_row_is_title &amp;&amp; p-&gt;csv_rows == 1) {
+    //do nothing
+  } else {
+    if (strlen(p-&gt;csv_complete_name)) {
+      if (option_debug)
+        NOTICA
+          (&quot;ROW %d ENDED, complete_name=%s, email=%s, home_phone=%s, mobile_phone=%s, business_phone=%s\n&quot;,
+           CELLIAX_P_LOG, p-&gt;csv_rows,
+           strlen(p-&gt;csv_complete_name) ? p-&gt;csv_complete_name : &quot;N/A&quot;,
+           strlen(p-&gt;csv_email) ? p-&gt;csv_email : &quot;N/A&quot;,
+           strlen(p-&gt;csv_home_phone) ? p-&gt;csv_home_phone : &quot;N/A&quot;,
+           strlen(p-&gt;csv_mobile_phone) ? p-&gt;csv_mobile_phone : &quot;N/A&quot;,
+           strlen(p-&gt;csv_business_phone) ? p-&gt;csv_business_phone : &quot;N/A&quot;);
+    }
+
+    /* write entries in phonebook file */
+    if (p-&gt;phonebook_writing_fp) {
+      celliax_dir_entry_extension++;
+
+      if (strlen(p-&gt;csv_complete_name)) {
+        /* let's start with home_phone */
+        if (strlen(p-&gt;csv_home_phone)) {
+          fprintf(p-&gt;phonebook_writing_fp,
+                  &quot;%s  =&gt; ,%s %sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcsv=%s|phonebook_entry_owner=%s\n&quot;,
+                  p-&gt;csv_home_phone, p-&gt;csv_complete_name, &quot;HOME&quot;, &quot;no&quot;,
+                  p-&gt;celliax_dir_entry_extension_prefix, &quot;2&quot;, celliax_dir_entry_extension,
+                  &quot;yes&quot;, &quot;not_specified&quot;);
+          fprintf(p-&gt;phonebook_writing_fp,
+                  &quot;%s  =&gt; ,%s %sDO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcsv=%s|phonebook_entry_owner=%s\n&quot;,
+                  p-&gt;csv_home_phone, p-&gt;csv_complete_name, &quot;HOME&quot;, &quot;no&quot;,
+                  p-&gt;celliax_dir_entry_extension_prefix, &quot;3&quot;, celliax_dir_entry_extension,
+                  &quot;yes&quot;, &quot;not_specified&quot;);
+        }
+
+        /* now business_phone */
+        if (strlen(p-&gt;csv_business_phone)) {
+          fprintf(p-&gt;phonebook_writing_fp,
+                  &quot;%s  =&gt; ,%s %sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcsv=%s|phonebook_entry_owner=%s\n&quot;,
+                  p-&gt;csv_business_phone, p-&gt;csv_complete_name, &quot;BIZ&quot;, &quot;no&quot;,
+                  p-&gt;celliax_dir_entry_extension_prefix, &quot;2&quot;, celliax_dir_entry_extension,
+                  &quot;yes&quot;, &quot;not_specified&quot;);
+          fprintf(p-&gt;phonebook_writing_fp,
+                  &quot;%s  =&gt; ,%s %sDO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcsv=%s|phonebook_entry_owner=%s\n&quot;,
+                  p-&gt;csv_business_phone, p-&gt;csv_complete_name, &quot;BIZ&quot;, &quot;no&quot;,
+                  p-&gt;celliax_dir_entry_extension_prefix, &quot;3&quot;, celliax_dir_entry_extension,
+                  &quot;yes&quot;, &quot;not_specified&quot;);
+        }
+
+        /* let's end with mobile_phone */
+        if (strlen(p-&gt;csv_mobile_phone)) {
+          fprintf(p-&gt;phonebook_writing_fp,
+                  &quot;%s  =&gt; ,%s %sSKO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcsv=%s|phonebook_entry_owner=%s\n&quot;,
+                  p-&gt;csv_mobile_phone, p-&gt;csv_complete_name, &quot;CELL&quot;, &quot;no&quot;,
+                  p-&gt;celliax_dir_entry_extension_prefix, &quot;2&quot;, celliax_dir_entry_extension,
+                  &quot;yes&quot;, &quot;not_specified&quot;);
+          fprintf(p-&gt;phonebook_writing_fp,
+                  &quot;%s  =&gt; ,%s %sDO,,,hidefromdir=%s|phonebook_direct_calling_ext=%d%s%.4d|phonebook_entry_fromcsv=%s|phonebook_entry_owner=%s\n&quot;,
+                  p-&gt;csv_mobile_phone, p-&gt;csv_complete_name, &quot;CELL&quot;, &quot;no&quot;,
+                  p-&gt;celliax_dir_entry_extension_prefix, &quot;3&quot;, celliax_dir_entry_extension,
+                  &quot;yes&quot;, &quot;not_specified&quot;);
+        }
+      }
+
+    }
+
+  }
+}
+
+#endif /* CELLIAX_LIBCSV */
+
+int celliax_console_celliax_dir_import(int fd, int argc, char *argv[])
+{
+  int res;
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+  char list_command[64];
+  char fn[256];
+  char date[256] = &quot;&quot;;
+  time_t t;
+  char *configfile = CELLIAX_DIR_CONFIG;
+  int add_to_celliax_dir_conf = 1;
+  //int fromskype = 0;
+  int fromcell = 0;
+#ifdef CELLIAX_LIBCSV
+  int fromcsv = 0;
+  int fromvcf = 0;
+#endif /* CELLIAX_LIBCSV */
+
+  if (argc &lt; 3 || argc &gt; 4)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd, &quot;No \&quot;current\&quot; console ???, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (!strcasecmp(argv[1], &quot;add&quot;))
+    add_to_celliax_dir_conf = 1;
+  else if (!strcasecmp(argv[1], &quot;replace&quot;))
+    add_to_celliax_dir_conf = 0;
+  else {
+    ast_cli(fd, &quot;\n\nYou have neither specified 'add' nor 'replace'\n\n&quot;);
+    return RESULT_SHOWUSAGE;
+  }
+
+  //if (!strcasecmp(argv[2], &quot;fromskype&quot;))
+  //fromskype = 1;
+  //else 
+
+  if (!strcasecmp(argv[2], &quot;fromcell&quot;))
+    fromcell = 1;
+#ifdef CELLIAX_LIBCSV
+  else if (!strcasecmp(argv[2], &quot;fromcsv&quot;))
+    fromcsv = 1;
+  else if (!strcasecmp(argv[2], &quot;fromvcf&quot;))
+    fromvcf = 1;
+#endif /* CELLIAX_LIBCSV */
+  else {
+    ast_cli(fd, &quot;\n\nYou have neither specified 'fromcell' neither 'fromcsv'\n\n&quot;);
+    return RESULT_SHOWUSAGE;
+  }
+
+#ifdef CELLIAX_LIBCSV
+  if (fromcsv || fromvcf)
+    if (argc != 4) {
+      ast_cli(fd,
+              &quot;\n\nYou have to specify a filename with 'fromcsv' or with 'fromvcf'\n\n&quot;);
+      return RESULT_SHOWUSAGE;
+    }
+#endif /* CELLIAX_LIBCSV */
+  if (fromcell)
+    if (p-&gt;controldevprotocol != PROTOCOL_AT) {
+      ast_cli(fd,
+              &quot;Importing from cellphone is currently supported only on \&quot;AT\&quot; cellphones :( !\n&quot;);
+      //fclose(p-&gt;phonebook_writing_fp);
+      //celliax_dir_create_extensions();
+      return RESULT_SUCCESS;
+    }
+
+  if (fromcell)
+    if (argc != 3) {
+      ast_cli(fd, &quot;\n\nYou don't have to specify a filename with 'fromcell'\n\n&quot;);
+      return RESULT_SHOWUSAGE;
+    }
+#ifdef CELLIAX_LIBCSV
+  if (fromvcf) {
+    if (option_debug)
+      NOTICA(&quot;filename is: %s\n&quot;, CELLIAX_P_LOG, argv[3]);
+    ast_cli(fd, &quot;\n\nnot yet implemented :P \n&quot;);
+    return RESULT_SUCCESS;
+  }
+#endif /* CELLIAX_LIBCSV */
+
+  /*******************************************************************************************/
+
+  if (configfile[0] == '/') {
+    ast_copy_string(fn, configfile, sizeof(fn));
+  } else {
+    snprintf(fn, sizeof(fn), &quot;%s/%s&quot;, ast_config_AST_CONFIG_DIR, configfile);
+  }
+  if (option_debug)
+    NOTICA(&quot;Opening '%s'\n&quot;, CELLIAX_P_LOG, fn);
+  time(&amp;t);
+  ast_copy_string(date, ctime(&amp;t), sizeof(date));
+
+  if (add_to_celliax_dir_conf)
+    p-&gt;phonebook_writing_fp = fopen(fn, &quot;a+&quot;);
+  else
+    p-&gt;phonebook_writing_fp = fopen(fn, &quot;w+&quot;);
+
+  if (p-&gt;phonebook_writing_fp) {
+    if (add_to_celliax_dir_conf) {
+      if (option_debug)
+        NOTICA(&quot;Opened '%s' for appending \n&quot;, CELLIAX_P_LOG, fn);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;!\n&quot;);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;! Update Date: %s&quot;, date);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;! Updated by: %s, %d\n&quot;, __FILE__, __LINE__);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;!\n&quot;);
+    } else {
+      if (option_debug)
+        NOTICA(&quot;Opened '%s' for writing \n&quot;, CELLIAX_P_LOG, fn);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;!\n&quot;);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;! Automatically generated configuration file\n&quot;);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;! Filename: %s (%s)\n&quot;, configfile, fn);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;! Creation Date: %s&quot;, date);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;! Generated by: %s, %d\n&quot;, __FILE__, __LINE__);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;;!\n&quot;);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;[general]\n\n&quot;);
+      fprintf(p-&gt;phonebook_writing_fp, &quot;[default]\n&quot;);
+    }
+
+#ifdef CELLIAX_LIBCSV
+    //FIXME: if add_to_celliax_dir_conf parse the &quot;old&quot; config file, so to have the correct next entry id-exten
+    if (fromcsv) {
+      if (option_debug)
+        NOTICA(&quot;filename is: %s\n&quot;, CELLIAX_P_LOG, argv[3]);
+
+/************************/
+      FILE *fp;
+      struct csv_parser *csvp;
+      char buf[1024];
+      size_t bytes_read;
+      unsigned char options = 0;
+
+      p-&gt;csv_rows = 0;
+      p-&gt;csv_fields = 0;
+
+      if (p-&gt;csv_separator_is_semicolon) {
+        if (csv_init(&amp;csvp, options | CSV_USE_SEMICOLON_SEPARATOR) != 0) {
+          ERRORA(&quot;Failed to initialize csv parser\n&quot;, CELLIAX_P_LOG);
+          return RESULT_SUCCESS;
+        }
+      } else {
+        if (csv_init(&amp;csvp, options) != 0) {
+          ERRORA(&quot;Failed to initialize csv parser\n&quot;, CELLIAX_P_LOG);
+          return RESULT_SUCCESS;
+        }
+
+      }
+
+      fp = fopen(argv[3], &quot;rb&quot;);
+      if (!fp) {
+        ERRORA(&quot;Failed to open %s: %s\n&quot;, CELLIAX_P_LOG, argv[3], strerror(errno));
+        return RESULT_SUCCESS;
+      }
+      while ((bytes_read = fread(buf, 1, 1024, fp)) &gt; 0) {
+        if (csv_parse(csvp, buf, bytes_read, celliax_cb1, celliax_cb2, p) != bytes_read) {
+          ERRORA(&quot;Error while parsing file: %s\n&quot;, CELLIAX_P_LOG,
+                 csv_strerror(csv_error(csvp)));
+        }
+      }
+
+      csv_fini(csvp, celliax_cb1, celliax_cb2, p);
+
+      if (ferror(fp)) {
+        ERRORA(&quot;Error while reading file %s\n&quot;, CELLIAX_P_LOG, argv[3]);
+        fclose(fp);
+        return RESULT_SUCCESS;
+      }
+
+      fclose(fp);
+      if (option_debug)
+        NOTICA(&quot;%s: %d fields, %d rows\n&quot;, CELLIAX_P_LOG, argv[3], p-&gt;csv_fields,
+               p-&gt;csv_rows);
+
+      csv_free(csvp);
+
+    /**************************/
+    }
+#endif /* CELLIAX_LIBCSV */
+
+  /*******************************************************************************************/
+    //if (fromskype) {
+    //ast_cli(fd,
+    //&quot;Skype not supported in celliax_dir. Load chan_skypiax and use skypiax_dir!\n&quot;);
+    //}
+
+  /*******************************************************************************************/
+    if (fromcell) {
+      /* which phonebook to use, use the SIM  */
+      res = celliax_serial_write_AT_ack(p, &quot;AT+CPBS=SM&quot;);
+      if (res) {
+        WARNINGA(&quot;AT+CPBS=SM failed, continue\n&quot;, CELLIAX_P_LOG);
+      }
+      /* which phonebook to use, trying to use combined phone+SIM  */
+      res = celliax_serial_write_AT_ack(p, &quot;AT+CPBS=MT&quot;);
+      if (res) {
+        WARNINGA(&quot;AT+CPBS=MT failed, continue\n&quot;, CELLIAX_P_LOG);
+      }
+      /* How many entries in phonebook  */
+      p-&gt;phonebook_querying = 1;
+      res = celliax_serial_write_AT_ack(p, &quot;AT+CPBR=?&quot;);
+      if (res) {
+        WARNINGA(&quot;AT+CPBR=? failed, continue\n&quot;, CELLIAX_P_LOG);
+      }
+      p-&gt;phonebook_querying = 0;
+      /* list entries in phonebook, give the SIM the time to answer  */
+      WARNINGA
+        (&quot;About to querying the cellphone phonebook, if the SIM do not answer may stuck here for 20 seconds... Don't worry.\n&quot;,
+         CELLIAX_P_LOG);
+      sprintf(list_command, &quot;AT+CPBR=%d,%d&quot;, p-&gt;phonebook_first_entry,
+              p-&gt;phonebook_last_entry);
+      p-&gt;phonebook_listing = 1;
+      res = celliax_serial_write_AT_expect_longtime(p, list_command, &quot;OK&quot;);
+      if (res) {
+        WARNINGA(&quot;AT+CPBR=%d,%d failed, continue\n&quot;, CELLIAX_P_LOG,
+                 p-&gt;phonebook_first_entry, p-&gt;phonebook_last_entry);
+      }
+      p-&gt;phonebook_listing = 0;
+    }
+  /*******************************************************************************************/
+#ifdef CELLIAX_LIBCSV
+    if (fromvcf) {
+      //TODO implementation here
+    }
+#endif /* CELLIAX_LIBCSV */
+
+  } else {
+    ast_cli(fd, &quot;\n\nfailed to open the directoriax.conf configuration file: %s\n&quot;, fn);
+    ERRORA(&quot;failed to open the directoriax.conf configuration file: %s\n&quot;, CELLIAX_P_LOG,
+           fn);
+    return RESULT_FAILURE;
+  }
+
+  fclose(p-&gt;phonebook_writing_fp);
+  //celliax_dir_create_extensions();
+
+  return RESULT_SUCCESS;
+}
+
+#endif /* CELLIAX_DIR */
+
+#ifdef CELLIAX_FBUS2
+
+int celliax_serial_getstatus_FBUS2(struct celliax_pvt *p)
+{
+  unsigned char MsgBuffer[7];
+  int res;
+  int how_many_reads = 0;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+
+  MsgBuffer[0] = FBUS2_COMMAND_BYTE_1;
+  MsgBuffer[1] = FBUS2_COMMAND_BYTE_2;
+  MsgBuffer[2] = 0x00;
+  MsgBuffer[3] = 0x03;
+  MsgBuffer[4] = 0x00;
+  MsgBuffer[5] = FBUS2_IS_LAST_FRAME;
+  MsgBuffer[6] = celliax_serial_get_seqnum_FBUS2(p);
+
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;asking model, outseqnum %.2X \n&quot;, CELLIAX_P_LOG, MsgBuffer[6]);
+  celliax_serial_write_FBUS2(p, MsgBuffer, 7, FBUS2_TYPE_MODEL_ASK);
+  usleep(1000);
+  res = celliax_serial_read_FBUS2(p);   //we don't have no monitor neither do_controldev_thread
+  if (res == -1) {
+    ERRORA(&quot;failed celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG);
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+  while (res != MsgBuffer[6] &amp;&amp; res != FBUS2_TYPE_MODEL_ANSWER) {
+    usleep(1000);
+    res = celliax_serial_read_FBUS2(p);
+    how_many_reads++;
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG);
+      UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+    if (how_many_reads &gt; 10) {
+      ERRORA(&quot;no expected results in %d celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG,
+             how_many_reads);
+      UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+  }
+
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  return 0;
+}
+
+int celliax_serial_sync_FBUS2(struct celliax_pvt *p)
+{
+  unsigned char initc = 0x55;   /* FBUS2 initialization char */
+  int c, rt;
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  /*  init the link (sync receive uart) */
+  for (c = 0; c &lt; 55; c++) {    /* 55 times */
+    usleep(10000);
+    rt = write(p-&gt;controldevfd, &amp;initc, 1);
+    if (rt != 1) {
+      ERRORA(&quot;serial error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+      UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+  }
+  time(&amp;p-&gt;celliax_serial_synced_timestamp);
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  return 0;
+}
+
+int celliax_serial_answer_FBUS2(struct celliax_pvt *p)
+{
+  unsigned char MsgBuffer[6];
+
+  celliax_serial_security_command_FBUS2(p);
+
+  MsgBuffer[0] = FBUS2_COMMAND_BYTE_1;
+  MsgBuffer[1] = FBUS2_COMMAND_BYTE_2;
+  MsgBuffer[2] = FBUS2_SECURIY_CALL_COMMANDS;
+  MsgBuffer[3] = FBUS2_SECURIY_CALL_COMMAND_ANSWER;
+  MsgBuffer[4] = FBUS2_IS_LAST_FRAME;
+  MsgBuffer[5] = celliax_serial_get_seqnum_FBUS2(p);
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;celliax_serial_answer_FBUS2, outseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                 MsgBuffer[5]);
+  celliax_serial_write_FBUS2(p, MsgBuffer, 6, FBUS2_TYPE_SECURITY);
+  DEBUGA_FBUS2(&quot;FBUS2: sent commands to answer the call\n&quot;, CELLIAX_P_LOG);
+  p-&gt;interface_state = AST_STATE_UP;    //FIXME
+
+  return 0;
+}
+
+int celliax_serial_call_FBUS2(struct celliax_pvt *p, char *dstr)
+{
+  unsigned char MsgBufferNum[255];
+  int i;
+
+  celliax_serial_security_command_FBUS2(p);
+
+  MsgBufferNum[0] = FBUS2_COMMAND_BYTE_1;
+  MsgBufferNum[1] = FBUS2_COMMAND_BYTE_2;
+  MsgBufferNum[2] = FBUS2_SECURIY_CALL_COMMANDS;
+  MsgBufferNum[3] = FBUS2_SECURIY_CALL_COMMAND_CALL;
+  for (i = 0; i &lt; strlen(dstr); i++) {
+    MsgBufferNum[4 + i] = dstr[i];
+  }
+  MsgBufferNum[4 + strlen(dstr)] = 0x00;    /* required by FBUS2 prot */
+  MsgBufferNum[4 + strlen(dstr) + 1] = FBUS2_IS_LAST_FRAME;
+  MsgBufferNum[4 + strlen(dstr) + 2] = celliax_serial_get_seqnum_FBUS2(p);
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;celliax_serial_call_FBUS2, outseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                 MsgBufferNum[4 + strlen(dstr) + 2]);
+  celliax_serial_write_FBUS2(p, MsgBufferNum, 5 + strlen(dstr) + 2, FBUS2_TYPE_SECURITY);
+
+  p-&gt;phone_callflow = CALLFLOW_CALL_DIALING;
+  p-&gt;interface_state = AST_STATE_DIALING;
+  if (option_debug)
+    DEBUGA_FBUS2(&quot;FBUS2: sent commands to call\n&quot;, CELLIAX_P_LOG);
+  return 0;
+}
+
+int celliax_serial_hangup_FBUS2(struct celliax_pvt *p)
+{
+  unsigned char MsgBuffer[6];
+
+  if (p-&gt;interface_state != AST_STATE_DOWN) {
+    celliax_serial_security_command_FBUS2(p);
+
+    MsgBuffer[0] = FBUS2_COMMAND_BYTE_1;
+    MsgBuffer[1] = FBUS2_COMMAND_BYTE_2;
+    MsgBuffer[2] = FBUS2_SECURIY_CALL_COMMANDS;
+    MsgBuffer[3] = FBUS2_SECURIY_CALL_COMMAND_RELEASE;
+    MsgBuffer[4] = FBUS2_IS_LAST_FRAME;
+    MsgBuffer[5] = celliax_serial_get_seqnum_FBUS2(p);
+
+    if (option_debug &gt; 1)
+      DEBUGA_FBUS2(&quot;celliax_serial_hangup_FBUS2, outseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                   MsgBuffer[5]);
+    celliax_serial_write_FBUS2(p, MsgBuffer, 6, FBUS2_TYPE_SECURITY);
+
+    DEBUGA_FBUS2(&quot;FBUS2: sent commands to hangup the call\n&quot;, CELLIAX_P_LOG);
+
+  }
+  p-&gt;interface_state = AST_STATE_DOWN;  //FIXME
+  p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;   //FIXME
+  return 0;
+}
+
+int celliax_serial_config_FBUS2(struct celliax_pvt *p)
+{
+  unsigned char MsgBuffer[6];
+  int res;
+  int how_many_reads = 0;
+
+  MsgBuffer[0] = FBUS2_COMMAND_BYTE_1;
+  MsgBuffer[1] = FBUS2_COMMAND_BYTE_2;
+  MsgBuffer[2] = FBUS2_SECURIY_EXTENDED_COMMANDS;
+  MsgBuffer[3] = FBUS2_SECURIY_EXTENDED_COMMAND_ON;
+  MsgBuffer[4] = FBUS2_IS_LAST_FRAME;
+  MsgBuffer[5] = celliax_serial_get_seqnum_FBUS2(p);
+
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;activating security commands for getting IMEI, outseqnum %.2X \n&quot;,
+                 CELLIAX_P_LOG, MsgBuffer[5]);
+  celliax_serial_write_FBUS2(p, MsgBuffer, 6, FBUS2_TYPE_SECURITY);
+  res = celliax_serial_read_FBUS2(p);   //we don't have no monitor neither do_controldev_thread
+  if (res == -1) {
+    ERRORA(&quot;failed celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  while (res != MsgBuffer[5] &amp;&amp; res != FBUS2_SECURIY_EXTENDED_COMMAND_ON) {
+    usleep(1000);
+    res = celliax_serial_read_FBUS2(p);
+    how_many_reads++;
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG);
+      return -1;
+    }
+    if (how_many_reads &gt; 10) {
+      ERRORA(&quot;no expected results in %d celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG,
+             how_many_reads);
+      return -1;
+    }
+  }
+
+  MsgBuffer[0] = FBUS2_COMMAND_BYTE_1;
+  MsgBuffer[1] = FBUS2_COMMAND_BYTE_2;
+  MsgBuffer[2] = FBUS2_SECURIY_IMEI_COMMANDS;
+  MsgBuffer[3] = FBUS2_SECURIY_IMEI_COMMAND_GET;
+  MsgBuffer[4] = FBUS2_IS_LAST_FRAME;
+  MsgBuffer[5] = celliax_serial_get_seqnum_FBUS2(p);
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;celliax_serial_get_IMEI_init_FBUS2, outseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                 MsgBuffer[5]);
+  celliax_serial_write_FBUS2(p, MsgBuffer, 6, FBUS2_TYPE_SECURITY);
+  res = celliax_serial_read_FBUS2(p);   //we don't have no monitor neither do_controldev_thread
+  if (res == -1) {
+    ERRORA(&quot;failed celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  how_many_reads = 0;
+  while (res != MsgBuffer[5] &amp;&amp; res != CALLFLOW_GOT_IMEI) {
+    usleep(1000);
+    res = celliax_serial_read_FBUS2(p);
+    how_many_reads++;
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG);
+      return -1;
+    }
+    if (how_many_reads &gt; 10) {
+      ERRORA(&quot;no expected results in %d celliax_serial_read_FBUS2\n&quot;, CELLIAX_P_LOG,
+             how_many_reads);
+      //FIXME return -1;
+      return 0;
+    }
+  }
+
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;xxxxx GOT IMEI xxxxx res=%d %.2X \n&quot;, CELLIAX_P_LOG, res, res);
+
+  return 0;
+}
+
+int celliax_serial_get_seqnum_FBUS2(struct celliax_pvt *p)
+{
+  if (p-&gt;seqnumfbus &gt; FBUS2_SEQNUM_MAX || p-&gt;seqnumfbus &lt; FBUS2_SEQNUM_MIN) {
+    ERRORA(&quot;p-&gt;seqnumfbus: %2.X\n&quot;, CELLIAX_P_LOG, p-&gt;seqnumfbus);
+    p-&gt;seqnumfbus = FBUS2_SEQNUM_MIN;
+  }
+
+  if (p-&gt;seqnumfbus == FBUS2_SEQNUM_MAX) {
+    p-&gt;seqnumfbus = FBUS2_SEQNUM_MIN;
+  } else {
+    p-&gt;seqnumfbus++;
+  }
+  if (option_debug &gt; 10)
+    DEBUGA_FBUS2(&quot;sqnum: %2.X\n&quot;, CELLIAX_P_LOG, p-&gt;seqnumfbus);
+  return p-&gt;seqnumfbus;
+}
+
+int celliax_serial_security_command_FBUS2(struct celliax_pvt *p)
+{
+  unsigned char MsgBuffer[6];
+
+  MsgBuffer[0] = FBUS2_COMMAND_BYTE_1;
+  MsgBuffer[1] = FBUS2_COMMAND_BYTE_2;
+  MsgBuffer[2] = FBUS2_SECURIY_EXTENDED_COMMANDS;
+  MsgBuffer[3] = FBUS2_SECURIY_EXTENDED_COMMAND_ON;
+  MsgBuffer[4] = FBUS2_IS_LAST_FRAME;
+  MsgBuffer[5] = celliax_serial_get_seqnum_FBUS2(p);
+
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;activating security commands, outseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                 MsgBuffer[5]);
+  celliax_serial_write_FBUS2(p, MsgBuffer, 6, FBUS2_TYPE_SECURITY);
+  return 0;
+}
+
+/*!
+ * \brief Write on the serial port for all the FBUS2 (old Nokia) functions
+ * \param p celliax_pvt
+ * \param len lenght of buffer2
+ * \param buffer2 chars to be written
+ *
+ * Write on the serial port for all the FBUS2 (old Nokia) functions
+ *
+ * \return the number of chars written on the serial, 
+ * that can be different from len (or negative) in case of errors.
+ */
+int celliax_serial_send_FBUS2(struct celliax_pvt *p, int len, unsigned char *mesg_ptr)
+{
+  int ret;
+  size_t actual = 0;
+  unsigned char *mesg_ptr2 = mesg_ptr;
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  LOKKA(&amp;p-&gt;controldev_lock);
+  do {
+    ret = write(p-&gt;controldevfd, mesg_ptr, len - actual);
+    if (ret &lt; 0 &amp;&amp; errno == EAGAIN)
+      continue;
+    if (ret &lt; 0) {
+      if (actual != len)
+        ERRORA(&quot;celliax_serial_write error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+      UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+    actual += ret;
+    mesg_ptr += ret;
+    usleep(10000);
+  } while (actual &lt; len);
+
+  UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  if (option_debug &gt; 10) {
+    int i;
+    char debug_buf[1024];
+    char *debug_buf_pos;
+
+    memset(debug_buf, 0, 1024);
+    debug_buf_pos = debug_buf;
+
+    for (i = 0; i &lt; len; i++) {
+      debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, mesg_ptr2[i]);
+      if (debug_buf_pos &gt; ((char *) &amp;debug_buf + 1000))
+        break;
+    }
+    DEBUGA_FBUS2(&quot;%s was sent down the wire\n&quot;, CELLIAX_P_LOG, debug_buf);
+  }
+
+  return 0;
+}
+
+/*!
+ * \brief Flags as acknowledged an FBUS2 message previously sent
+ * \param p celliax_pvt
+ * \param seqnum identifier of the message to be acknowledged
+ *
+ * Called upon receiving an FBUS2 acknoledgement message, browse the fbus2_outgoing_list 
+ * looking for the seqnum sent FBUS2 message, and flags it as acknowledged.
+ * (if an outgoing FBUS2 message is not aknowledged by the cellphone in a while,
+ * it will be retransmitted)
+ *
+ * \return 0 on error, 1 otherwise
+ */
+int celliax_serial_list_acknowledge_FBUS2(struct celliax_pvt *p, int seqnum)
+{
+  struct fbus2_msg *ptr;
+
+  ptr = p-&gt;fbus2_outgoing_list;
+  if (ptr == NULL) {
+    ERRORA(&quot;fbus2_outgoing_list is NULL ?\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  PUSHA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  LOKKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  while (ptr-&gt;next != NULL)
+    ptr = ptr-&gt;next;
+  while (ptr-&gt;acknowledged == 0) {
+    if (ptr-&gt;seqnum == seqnum) {
+      ptr-&gt;acknowledged = 1;
+      if (option_debug &gt; 1)
+        DEBUGA_FBUS2(&quot;Acknowledgment to %.2X\n&quot;, CELLIAX_P_LOG, seqnum);
+
+      DEBUGA_FBUS2(&quot;PREFREE OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+      celliax_serial_list_print_FBUS2(p, p-&gt;fbus2_outgoing_list);
+      if (ptr-&gt;previous) {
+        if (ptr-&gt;next) {
+          ptr-&gt;previous-&gt;next = ptr-&gt;next;
+        } else {
+          ptr-&gt;previous-&gt;next = NULL;
+        }
+      }
+      if (ptr-&gt;next) {
+        if (ptr-&gt;previous) {
+          ptr-&gt;next-&gt;previous = ptr-&gt;previous;
+        } else {
+          ptr-&gt;next-&gt;previous = NULL;
+        }
+      }
+
+      if ((NULL == ptr-&gt;next) &amp;&amp; (NULL == ptr-&gt;previous)) { /* bug catched by Wojciech Andralojc */
+        if (option_debug &gt; 1)
+          DEBUGA_FBUS2(&quot;FREEING LAST\n&quot;, CELLIAX_P_LOG);
+        p-&gt;fbus2_outgoing_list = NULL;
+        p-&gt;fbus2_outgoing_list = celliax_serial_list_init_FBUS2(p);
+      }
+
+      free(ptr);
+      DEBUGA_FBUS2(&quot;POSTFREE OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+      celliax_serial_list_print_FBUS2(p, p-&gt;fbus2_outgoing_list);
+
+      break;
+    }
+    if (ptr-&gt;previous != NULL) {
+      ptr = ptr-&gt;previous;
+    } else {
+      ERRORA
+        (&quot;The phone sent us an acknowledgement referring to a msg with a seqnum that is not in our sent list: %.2X\n&quot;,
+         CELLIAX_P_LOG, seqnum);
+      break;
+    }
+  }
+  UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  return 0;
+}
+
+/*!
+ * \brief Sends an FBUS2 message or resends it if it was not acknowledged
+ * \param p celliax_pvt
+ *
+ * Called by celliax_serial_read_FBUS2, browse the fbus2_outgoing_list looking for FBUS2 messages to be sent, 
+ * or for FBUS2 messages previously sent but not yet acknoledged.
+ * (if an outgoing FBUS2 message is not aknowledged by the cellphone in a while,
+ * it will be retransmitted)
+ *
+ * \return 0 on error, 1 otherwise
+ */
+int celliax_serial_send_if_time_FBUS2(struct celliax_pvt *p)
+{
+  struct fbus2_msg *ptr;
+  struct timeval tv;
+  struct timezone tz;
+
+  gettimeofday(&amp;tv, &amp;tz);
+  ptr = p-&gt;fbus2_outgoing_list;
+  if (ptr == NULL) {
+    ERRORA(&quot;fbus2_outgoing_list is NULL ?\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  while (ptr-&gt;next != NULL) {
+    WARNINGA(&quot;fbus2_outgoing_list-&gt;next is not null ?\n&quot;, CELLIAX_P_LOG);
+    ptr = ptr-&gt;next;            //FIXME what to do?
+  }
+  while (ptr-&gt;sent == 0 &amp;&amp; ptr-&gt;acknowledged == 0) {
+    if (ptr-&gt;previous != NULL) {
+      ptr = ptr-&gt;previous;
+    } else
+      break;
+  }
+  while (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 0) {
+    if (ptr-&gt;previous != NULL) {
+      ptr = ptr-&gt;previous;
+    } else
+      break;
+  }
+  if (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 1) {
+    if (ptr-&gt;next != NULL) {
+      ptr = ptr-&gt;next;
+    }
+  }
+  if (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;msg &gt; 0) {
+    if ((tv.tv_sec * 1000 + tv.tv_usec / 1000) &gt;
+        ((ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000) + 1000)) {
+
+      PUSHA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+      LOKKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+
+      if (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;msg &gt; 0) {   //retest, maybe has been changed?
+        if ((tv.tv_sec * 1000 + tv.tv_usec / 1000) &gt; ((ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000) + 1000)) { //retest, maybe has been changed?
+
+          if (option_debug &gt; 1)
+            DEBUGA_FBUS2(&quot;RESEND %.2X, passed %ld ms, sent %d times\n&quot;, CELLIAX_P_LOG,
+                         ptr-&gt;seqnum,
+                         ((tv.tv_sec * 1000 + tv.tv_usec / 1000) -
+                          (ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000)),
+                         ptr-&gt;how_many_sent);
+          if (ptr-&gt;how_many_sent &gt; 9) {
+            ERRORA(&quot;RESEND %.2X, passed %ld ms, sent %d times\n&quot;, CELLIAX_P_LOG,
+                   ptr-&gt;seqnum,
+                   ((tv.tv_sec * 1000 + tv.tv_usec / 1000) -
+                    (ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000)), ptr-&gt;how_many_sent);
+
+            UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+            return -1;
+          }
+
+          celliax_serial_send_FBUS2(p, ptr-&gt;len, ptr-&gt;buffer);
+          if (ptr-&gt;buffer[3] == FBUS2_ACK_BYTE) {
+            if (option_debug &gt; 1)
+              DEBUGA_FBUS2(&quot;RESEND ACK, passed %ld ms, sent %d times\n&quot;, CELLIAX_P_LOG,
+                           ((tv.tv_sec * 1000 + tv.tv_usec / 1000) -
+                            (ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000)),
+                           ptr-&gt;how_many_sent);
+            ptr-&gt;acknowledged = 1;
+            ptr-&gt;msg = FBUS2_OUTGOING_ACK;
+          }
+          ptr-&gt;tv_sec = tv.tv_sec;
+          ptr-&gt;tv_usec = tv.tv_usec;
+          ptr-&gt;sent = 1;
+          ptr-&gt;how_many_sent++;
+          if (option_debug &gt; 1) {
+            DEBUGA_FBUS2(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+            celliax_serial_list_print_FBUS2(p, p-&gt;fbus2_outgoing_list);
+            DEBUGA_FBUS2(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);
+          }
+
+        }
+      }
+
+      UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+      POPPA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+    }
+  }
+  if (ptr-&gt;sent == 0 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;msg &gt; 0) {
+
+    PUSHA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+    LOKKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+
+    if (ptr-&gt;sent == 0 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;msg &gt; 0) { //retest, maybe has been changed?
+
+      if (option_debug &gt; 1)
+        DEBUGA_FBUS2(&quot;SENDING 1st TIME %.2X\n&quot;, CELLIAX_P_LOG, ptr-&gt;seqnum);
+      celliax_serial_send_FBUS2(p, ptr-&gt;len, ptr-&gt;buffer);
+      if (ptr-&gt;buffer[3] == FBUS2_ACK_BYTE) {
+        if (option_debug &gt; 1)
+          DEBUGA_FBUS2(&quot;SENDING 1st TIME ACK\n&quot;, CELLIAX_P_LOG);
+        ptr-&gt;acknowledged = 1;
+        ptr-&gt;msg = FBUS2_OUTGOING_ACK;
+      }
+      ptr-&gt;tv_sec = tv.tv_sec;
+      ptr-&gt;tv_usec = tv.tv_usec;
+      ptr-&gt;sent = 1;
+      ptr-&gt;how_many_sent++;
+      if (option_debug &gt; 1) {
+        DEBUGA_FBUS2(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+        celliax_serial_list_print_FBUS2(p, p-&gt;fbus2_outgoing_list);
+        DEBUGA_FBUS2(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);
+      }
+
+    }
+
+    UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+    POPPA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+
+  }
+  return 0;
+}
+
+int celliax_serial_write_FBUS2(struct celliax_pvt *p, unsigned char *MsgBuffer,
+                               int MsgLength, unsigned char MsgType)
+{
+  unsigned char buffer2[FBUS2_MAX_TRANSMIT_LENGTH + 10];
+  unsigned char checksum = 0;
+  int i, len;
+  struct timeval tv;
+  struct timezone tz;
+
+  buffer2[0] = FBUS2_SERIAL_FRAME_ID;
+  buffer2[1] = FBUS2_DEVICE_PHONE;  /* destination */
+  buffer2[2] = FBUS2_DEVICE_PC; /* source */
+  buffer2[3] = MsgType;
+  buffer2[4] = 0x00;            /* required by protocol */
+  buffer2[5] = MsgLength;
+
+  memcpy(buffer2 + 6, MsgBuffer, MsgLength);
+  len = MsgLength + 6;
+
+  /* Odd messages require additional padding 0x00 byte */
+  if (MsgLength % 2)
+    buffer2[len++] = 0x00;      /* optional PaddingByte */
+
+  checksum = 0;
+  for (i = 0; i &lt; len; i += 2)
+    checksum ^= buffer2[i];
+  buffer2[len++] = checksum;    /* ChkSum1 */
+
+  checksum = 0;
+  for (i = 1; i &lt; len; i += 2)
+    checksum ^= buffer2[i];
+  buffer2[len++] = checksum;    /* ChkSum2 */
+
+  if (option_debug &gt; 10) {
+    int i;
+    char debug_buf[1024];
+    char *debug_buf_pos;
+
+    memset(debug_buf, 0, 1024);
+    debug_buf_pos = debug_buf;
+
+    for (i = 0; i &lt; len; i++) {
+      debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, buffer2[i]);
+      if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+        break;
+    }
+    if (buffer2[3] == FBUS2_ACK_BYTE) {
+      DEBUGA_FBUS2(&quot;%s to be written, ACK\n&quot;, CELLIAX_P_LOG, debug_buf);
+    } else {
+      DEBUGA_FBUS2(&quot;%s to be written\n&quot;, CELLIAX_P_LOG, debug_buf);
+    }
+  }
+
+  gettimeofday(&amp;tv, &amp;tz);
+
+  if (buffer2[3] != FBUS2_ACK_BYTE) {
+    p-&gt;fbus2_outgoing_list = celliax_serial_list_init_FBUS2(p);
+    p-&gt;fbus2_outgoing_list-&gt;msg = 11;
+
+    p-&gt;fbus2_outgoing_list-&gt;len = len;
+    for (i = 0; i &lt; len; i++) {
+      p-&gt;fbus2_outgoing_list-&gt;buffer[i] = buffer2[i];
+    }
+    p-&gt;fbus2_outgoing_list-&gt;seqnum = MsgBuffer[MsgLength - 1];
+    if (option_debug &gt; 1) {
+      DEBUGA_FBUS2(&quot;OUTGOING LIST seqnum is %2.X\n&quot;, CELLIAX_P_LOG,
+                   MsgBuffer[MsgLength - 1]);
+
+      DEBUGA_FBUS2(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+      celliax_serial_list_print_FBUS2(p, p-&gt;fbus2_outgoing_list);
+      DEBUGA_FBUS2(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);
+    }
+  } else {
+    usleep(100);
+    celliax_serial_send_FBUS2(p, len, buffer2);
+  }
+
+  return 0;
+}
+
+int celliax_serial_send_ack_FBUS2(struct celliax_pvt *p, unsigned char MsgType,
+                                  unsigned char MsgSequence)
+{
+  unsigned char buffer2[2];
+
+  buffer2[0] = MsgType;
+  buffer2[1] = (MsgSequence - FBUS2_SEQNUM_MIN);
+
+  if (option_debug &gt; 1)
+    DEBUGA_FBUS2(&quot;SENDING ACK to %2.X, seqack %2.X \n&quot;, CELLIAX_P_LOG, MsgSequence,
+                 (MsgSequence - FBUS2_SEQNUM_MIN));
+  /* Sending to phone */
+  return celliax_serial_write_FBUS2(p, buffer2, 2, FBUS2_ACK_BYTE);
+}
+
+struct fbus2_msg *celliax_serial_list_init_FBUS2(struct celliax_pvt *p)
+{
+  struct fbus2_msg *list;
+  list = p-&gt;fbus2_outgoing_list;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  LOKKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  if (list == NULL) {
+    list = malloc(sizeof(*(list)));
+    list-&gt;msg = 0;
+    list-&gt;seqnum = 0;
+    list-&gt;len = 0;
+    list-&gt;acknowledged = 0;
+    list-&gt;how_many_sent = 0;
+    list-&gt;sent = 0;
+    list-&gt;tv_sec = 0;
+    list-&gt;tv_usec = 0;
+    list-&gt;next = NULL;
+    list-&gt;previous = NULL;
+  }
+  if (list-&gt;msg != 0) {
+    struct fbus2_msg *new;
+    new = malloc(sizeof(*new));
+    new-&gt;msg = 0;
+    new-&gt;seqnum = 0;
+    new-&gt;len = 0;
+    new-&gt;acknowledged = 0;
+    new-&gt;how_many_sent = 0;
+    new-&gt;sent = 0;
+    new-&gt;tv_sec = 0;
+    new-&gt;tv_usec = 0;
+    new-&gt;next = NULL;
+    new-&gt;previous = list;
+    list-&gt;next = new;
+    list = new;
+  }
+  UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;fbus2_outgoing_list_lock);
+  return list;
+}
+
+int celliax_serial_list_print_FBUS2(struct celliax_pvt *p, struct fbus2_msg *list)
+{
+  struct fbus2_msg *ptr;
+  ptr = list;
+  while (ptr) {
+    if (option_debug &gt; 3)
+      DEBUGA_FBUS2
+        (&quot;PTR msg is: %d, seqnum is %.2X, tv_sec is %d, tv_usec is %d, acknowledged is: %d,&quot;
+         &quot; sent is:%d, how_many_sent is: %d\n&quot;, CELLIAX_P_LOG, ptr-&gt;msg, ptr-&gt;seqnum,
+         ptr-&gt;tv_sec, ptr-&gt;tv_usec, ptr-&gt;acknowledged, ptr-&gt;sent, ptr-&gt;how_many_sent);
+    ptr = ptr-&gt;previous;
+  }
+  return 0;
+}
+
+int celliax_serial_read_FBUS2(struct celliax_pvt *p)
+{
+  int read_count;
+  int select_err;
+  fd_set read_fds;
+  struct timeval timeout;
+  int fbus_mesg = 0;
+  int i;
+
+  FD_ZERO(&amp;read_fds);
+  FD_SET(p-&gt;controldevfd, &amp;read_fds);
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 50000;
+
+  if ((select_err = select(p-&gt;controldevfd + 1, &amp;read_fds, NULL, NULL, &amp;timeout)) &gt; 0) {
+    timeout.tv_sec = 0;         //reset the timeout, linux modify it
+    timeout.tv_usec = 50000;    //reset the timeout, linux modify it
+    PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+    LOKKA(&amp;p-&gt;controldev_lock);
+    while ((select_err =
+            select(p-&gt;controldevfd + 1, &amp;read_fds, NULL, NULL, &amp;timeout)) &gt; 0) {
+      gettimeofday(&amp;p-&gt;fbus2_list_tv, &amp;p-&gt;fbus2_list_tz);
+      read_count = read(p-&gt;controldevfd, p-&gt;rxm, 255);
+
+      if (read_count == 0) {
+        ERRORA
+          (&quot;read 0 bytes!!! Nenormalno! Marking this celliax_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&quot;,
+           CELLIAX_P_LOG, p-&gt;controldevice_name);
+        p-&gt;controldev_dead = 1;
+        close(p-&gt;controldevfd);
+        UNLOCKA(&amp;p-&gt;controldev_lock);
+        if (p-&gt;owner) {
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+          p-&gt;owner-&gt;hangupcause = AST_CAUSE_FAILURE;
+        }
+        return -1;
+      }
+      if (option_debug &gt; 10) {
+        int c;
+        char debug_buf[1024];
+        char *debug_buf_pos;
+
+        memset(debug_buf, 0, 1024);
+        debug_buf_pos = debug_buf;
+        for (c = 0; c &lt; read_count; c++) {
+          debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, p-&gt;rxm[c]);
+          if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+            break;
+        }
+        DEBUGA_FBUS2(&quot;%s READ AT seconds=%ld usec=%6ld read_count=%d\n&quot;, CELLIAX_P_LOG,
+                     debug_buf, p-&gt;fbus2_list_tv.tv_sec, p-&gt;fbus2_list_tv.tv_usec,
+                     read_count);
+      }
+
+      for (i = 0; i &lt; read_count; i++) {
+        if (p-&gt;rxm[i] == FBUS2_DEVICE_PHONE &amp;&amp; p-&gt;rxm[i - 1] == FBUS2_DEVICE_PC
+            &amp;&amp; p-&gt;rxm[i - 2] == FBUS2_SERIAL_FRAME_ID) {
+          /* if we have identified the start of an fbus2 frame sent to us by the phone */
+          /* clean the array, copy into it the beginning of the frame, move the counter in the array after the last byte copied */
+          memset(p-&gt;array, 0, 255);
+          p-&gt;array[0] = FBUS2_SERIAL_FRAME_ID;
+          p-&gt;array[1] = FBUS2_DEVICE_PC;
+          p-&gt;arraycounter = 2;
+        }
+        if (p-&gt;rxm[i] == FBUS2_SERIAL_FRAME_ID &amp;&amp; read_count == 1) {    /* quick hack to try to identify the lone char 
+                                                                           at the beginning a frame, often returned by 
+                                                                           ark3116 based datacables */
+          /* if we have identified the start of an fbus2 frame sent to us by the phone */
+          /* clean the array, copy into it the beginning of the frame, move the counter in the array after the last byte copied */
+          memset(p-&gt;array, 0, 255);
+          p-&gt;arraycounter = 0;
+        }
+
+        /* continue copying into the array, until... */
+        p-&gt;array[p-&gt;arraycounter] = p-&gt;rxm[i];
+        /* we reach the end of the incoming frame, its lenght is in the p-&gt;array[5] byte, plus overhead */
+        if (p-&gt;arraycounter == p-&gt;array[5] + 7) {
+          /* start categorizing frames */
+          int seqnum;
+          int known = 0;
+
+          /* ACK frames are always of lenght 10, without padding */
+          seqnum = p-&gt;array[p-&gt;arraycounter - 2];
+          /* first step in categorizing frames, look at the general kind of frame, in p-&gt;array[3] */
+          switch (p-&gt;array[3]) {
+/****************************************************************/
+          case FBUS2_ACK_BYTE:
+            /* this is an ACKnowledgement frame sent to us in reply to an item we sent, take note we were ACKnowledged, no need to resend the item */
+            if (option_debug &gt; 1)
+              DEBUGA_FBUS2(&quot;INCOMING ACK, seqack %.2X \n&quot;, CELLIAX_P_LOG, seqnum);
+            if (seqnum == 0x80) {   /* reset */
+              seqnum = 0x00;
+              DEBUGA_FBUS2
+                (&quot;seqack was 0x80, interpreting as 0x00, first acknowledgement (session begin?) of our first sent item 0x40\n&quot;,
+                 CELLIAX_P_LOG);
+            }
+            /* an ACK frame has the same seqnum as the item it acknowledge, minus 0x40, so here we obtain the seqnum of the item that has been ACKnowledged */
+            fbus_mesg = seqnum + FBUS2_SEQNUM_MIN;
+            /* take note that the item sent was ACKnowledged, so no need to resend it */
+            celliax_serial_list_acknowledge_FBUS2(p, fbus_mesg);
+            /* this frame has been categorized, bail out from the loop */
+            known = 1;
+            break;
+/****************************************************************/
+          case FBUS2_TYPE_CALL_DIVERT:
+            if (option_debug &gt; 1)
+              DEBUGA_FBUS2(&quot;CALL DIVERT SIGNALING seqnum %.2X \n&quot;, CELLIAX_P_LOG, seqnum);
+            fbus_mesg = FBUS2_TYPE_CALL_DIVERT;
+            /* this signal us that we have some settings in line divert, let's use it as activation of the line when we call */
+            if (p-&gt;interface_state == AST_STATE_DIALING) {
+              p-&gt;interface_state = AST_STATE_UP;
+              p-&gt;phone_callflow = CALLFLOW_CALL_ACTIVE;
+              ast_setstate(p-&gt;owner, AST_STATE_RINGING);
+              celliax_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+              if (option_debug)
+                DEBUGA_FBUS2
+                  (&quot;call is active, I know it's not yet true, but 3310 do not give us remote answer signaling\n&quot;,
+                   CELLIAX_P_LOG);
+            }
+            /* this frame has been categorized, bail out from the loop */
+            known = 1;
+            break;
+
+/****************************************************************/
+            /* this kind of frames is an answer to &quot;ask model&quot; actions */
+          case FBUS2_TYPE_MODEL_ANSWER:
+            if (1) {
+              int newline = 0;
+              int c = i = 0;
+              unsigned char model[10];
+              for (i = 10; i &lt; p-&gt;arraycounter; i++) {
+                if (p-&gt;array[i] == '\n')
+                  newline++;
+                if (newline == 2) {
+                  if (p-&gt;array[i] != '\n') {
+                    model[c] = p-&gt;array[i];
+                    c++;
+                  }
+                }
+                if (newline == 3) {
+                  break;
+                }
+                if (c == 9)
+                  break;
+              }
+              model[c] = '\0';
+              DEBUGA_FBUS2(&quot;FBUS2 PHONE MODEL is: %s, inseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                           model, seqnum);
+            }
+            known = 1;
+            fbus_mesg = FBUS2_TYPE_MODEL_ANSWER;
+            break;
+/****************************************************************/
+            /* this kind of frames is an answer to &quot;security enabled&quot; actions */
+          case FBUS2_TYPE_SECURITY:
+            switch (p-&gt;array[8]) {
+              /* this subkind of frames is an answer to &quot;security enabled&quot; CALL actions */
+            case FBUS2_SECURIY_CALL_COMMANDS:
+              switch (p-&gt;array[9]) {
+                /* this sub-subkind of frames tell us that we answered the call */
+              case FBUS2_SECURIY_CALL_COMMAND_ANSWER:
+                p-&gt;interface_state = AST_STATE_UP;
+                p-&gt;phone_callflow = CALLFLOW_CALL_ACTIVE;
+
+                /* set the channel state to UP, we've answered */
+                if (ast_setstate(p-&gt;owner, AST_STATE_UP)) {
+                  ERRORA(&quot;ast_setstate failed, BAD\n&quot;, CELLIAX_P_LOG);
+                }
+
+                if (option_debug &gt; 1)
+                  DEBUGA_FBUS2(&quot;ANSWERED CALL, inseqnum %.2X \n&quot;, CELLIAX_P_LOG, seqnum);
+                known = 1;
+                break;
+                /* this sub-subkind of frames tell us that we released the call */
+              case FBUS2_SECURIY_CALL_COMMAND_RELEASE:
+                p-&gt;interface_state = AST_STATE_DOWN;
+                p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+                if (option_debug &gt; 1)
+                  DEBUGA_FBUS2(&quot;RELEASED CALL, inseqnum %.2X \n&quot;, CELLIAX_P_LOG, seqnum);
+                fbus_mesg = CALLFLOW_CALL_RELEASED;
+                known = 1;
+                break;
+              }
+              break;
+              /* this subkind of frames is an answer to &quot;enable security commands&quot; action */
+            case FBUS2_SECURIY_EXTENDED_COMMANDS:
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;SECURITY EXTENDED COMMANDS ON, inseqnum %.2X \n&quot;,
+                             CELLIAX_P_LOG, seqnum);
+              fbus_mesg = FBUS2_SECURIY_EXTENDED_COMMAND_ON;
+              known = 1;
+              break;
+              /* this subkind of frames is an answer to &quot;get IMEI&quot; action */
+            case FBUS2_SECURIY_IMEI_COMMANDS:
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;CALLFLOW_GOT_IMEI, inseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                             seqnum);
+              fbus_mesg = CALLFLOW_GOT_IMEI;
+              known = 1;
+              break;
+            }
+            break;
+/****************************************************************/
+            /* this kind of frames is about SMSs */
+          case FBUS2_TYPE_SMS:
+            switch (p-&gt;array[9]) {
+              /* this subkind of frames is about an INCOMING SMS */
+            case FBUS2_SMS_INCOMING:
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;SMS, inseqnum %.2X \n&quot;, CELLIAX_P_LOG, seqnum);
+              known = 1;
+              break;
+            }
+            break;
+/****************************************************************/
+            /* this kind of frames is about PHONE CALLs */
+          case FBUS2_TYPE_CALL:
+            switch (p-&gt;array[9]) {
+              int a;
+              /* this subkind of frame is about the CALL has been HUNGUP */
+            case FBUS2_CALL_HANGUP:
+              p-&gt;interface_state = AST_STATE_DOWN;
+              p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;REMOTE PARTY HANG UP, inseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                             seqnum);
+              fbus_mesg = CALLFLOW_INCOMING_HANGUP;
+              known = 1;
+              break;
+              /* this subkind of frame is about the remote CALLID (not signaled by 3310) */
+            case FBUS2_CALL_CALLID:
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;CALLID, inseqnum %.2X \n&quot;, CELLIAX_P_LOG, seqnum);
+              memset(p-&gt;callid_name, 0, sizeof(p-&gt;callid_name));
+              memset(p-&gt;callid_number, 0, sizeof(p-&gt;callid_number));
+              for (a = 0; a &lt; p-&gt;array[12]; a++) {
+                p-&gt;callid_number[a] = p-&gt;array[12 + a + 1];
+              }
+              for (a = 0; a &lt; p-&gt;array[12 + 1 + p-&gt;array[12]] + 1; a++) {
+                p-&gt;callid_name[a] = p-&gt;array[12 + 1 + a + p-&gt;array[12] + 1];
+              }
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;CALLFLOW_INCOMING_CALLID: name is %s, number is %s\n&quot;,
+                             CELLIAX_P_LOG,
+                             p-&gt;callid_name[0] != 1 ? p-&gt;callid_name : &quot;not available&quot;,
+                             p-&gt;callid_number[0] ? p-&gt;callid_number : &quot;not available&quot;);
+              fbus_mesg = CALLFLOW_INCOMING_CALLID;
+              p-&gt;phone_callflow = CALLFLOW_INCOMING_RING;
+              p-&gt;interface_state = AST_STATE_RING;
+              known = 1;
+              break;
+            }
+            break;
+/****************************************************************/
+            /* this kind of frames is about NETWORK STATUS */
+          case FBUS2_TYPE_NETWORK_STATUS:
+            switch (p-&gt;array[9]) {
+              /* this subkind of frames is NETWORK STATUS REGISTERED */
+            case FBUS2_NETWORK_STATUS_REGISTERED:
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;NETWORK STATUS REGISTERED, inseqnum %.2X \n&quot;, CELLIAX_P_LOG,
+                             seqnum);
+              if (p-&gt;callid_name[0] == 0 &amp;&amp; p-&gt;owner
+                  &amp;&amp; p-&gt;interface_state != AST_STATE_DOWN) {
+                p-&gt;interface_state = AST_STATE_DOWN;
+                p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+                if (option_debug)
+                  NOTICA(&quot;We think we are using a nokia3310, so NETWORK STATUS REGISTERED&quot;
+                         &quot; is interpreted as REMOTE PARTY HANG UP during a call, because&quot;
+                         &quot; Nokia 3310 give no hint about remote hangup. Nokia 3310&quot;
+                         &quot; does not signal the CALLID, while other nokias at least put&quot;
+                         &quot; callid_name[0]=1 (also if no callid was transmitted by remote&quot;
+                         &quot; party), we use this lack of CALLID as a sign of 3310nness.&quot;
+                         &quot; Outside a call, or when CALLID has been signaled, NETWORK STATUS&quot;
+                         &quot; REGISTERED is ignored.\n&quot;, CELLIAX_P_LOG);
+                fbus_mesg = CALLFLOW_INCOMING_HANGUP;
+              }
+              known = 1;
+              break;
+            }
+            break;
+/****************************************************************/
+            /* this kind of frames is about CALL STATUS */
+          case FBUS2_TYPE_CALL_STATUS:
+            switch (p-&gt;array[12]) {
+              /* this subkind of frames is about CALL STATUS OFF */
+            case FBUS2_CALL_STATUS_OFF:
+              p-&gt;interface_state = AST_STATE_DOWN;
+              p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;STATUS call in progress OFF, inseqnum %.2X \n&quot;,
+                             CELLIAX_P_LOG, seqnum);
+              fbus_mesg = CALLFLOW_INCOMING_HANGUP;
+              known = 1;
+              break;
+              /* this subkind of frames is about CALL STATUS ON */
+            case FBUS2_CALL_STATUS_ON:
+              if (option_debug &gt; 1)
+                DEBUGA_FBUS2(&quot;STATUS call in progress ON, inseqnum %.2X \n&quot;,
+                             CELLIAX_P_LOG, seqnum);
+              known = 1;
+              break;
+            }
+/****************************************************************/
+            break;
+          }
+
+          /* categorization of frame is ended, if it has not been recognized, whine */
+          if (!known) {
+            WARNINGA(&quot;FBUS2 MSG UNKNOWN, inseqnum %.2X\n&quot;, CELLIAX_P_LOG, seqnum);
+          }
+
+          /* let's print our frame */
+          if (option_debug &gt; 1) {
+            int i;
+            char debug_buf[1024];
+            char *debug_buf_pos;
+
+            memset(debug_buf, 0, 1024);
+            debug_buf_pos = debug_buf;
+            for (i = 0; i &lt; p-&gt;arraycounter + 1; i++) {
+              debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, p-&gt;array[i]);
+              if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+                break;
+            }
+            DEBUGA_FBUS2(&quot;%s is the RECEIVED FRAME inseqnum %.2X\n&quot;, CELLIAX_P_LOG,
+                         debug_buf, seqnum);
+          }
+
+          /* if the frame we received is not an ACK frame, let's ACKnowledge it */
+          if (p-&gt;array[0] == FBUS2_SERIAL_FRAME_ID &amp;&amp; p-&gt;array[3] != FBUS2_ACK_BYTE) {
+            celliax_serial_send_ack_FBUS2(p, p-&gt;array[3], seqnum);
+          }
+        }
+        p-&gt;arraycounter++;
+      }
+    }
+    UNLOCKA(&amp;p-&gt;controldev_lock);
+    POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  }
+  /* oooops, select returned error, got a kill/cancel or problems with the serial file descriptor */
+  if (select_err == -1) {
+    if (errno != EINTR) {
+      ERRORA
+        (&quot;select returned -1 on %s, marking controldev as dead, errno was: %d, error was: %s\n&quot;,
+         CELLIAX_P_LOG, p-&gt;controldevice_name, errno, strerror(errno));
+      p-&gt;controldev_dead = 1;
+      close(p-&gt;controldevfd);
+      if (p-&gt;owner)
+        celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+      return -1;
+    } else {
+      WARNINGA(&quot;select returned -1 on %s, errno was: %d, EINTR, error was: %s\n&quot;,
+               CELLIAX_P_LOG, p-&gt;controldevice_name, errno, strerror(errno));
+      return 0;
+    }
+  }
+  /* OK, reading done, let's browse the list of pending frames to be sent, and act on it */
+  if (celliax_serial_send_if_time_FBUS2(p)) {
+    ERRORA(&quot;celliax_serial_send_if_time_FBUS2 failed!\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+
+  if (fbus_mesg == CALLFLOW_INCOMING_HANGUP) {
+    if (p-&gt;owner) {
+      celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+      DEBUGA_FBUS2(&quot;phone call ended\n&quot;, CELLIAX_P_LOG);
+    }
+  }
+
+  return fbus_mesg;             //FIXME breaks the convention of returning 0 on success
+}
+
+#endif /* CELLIAX_FBUS2 */
+
+#ifdef CELLIAX_CVM
+
+int celliax_serial_sync_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  usleep(1000);                 /* 1msec */
+  time(&amp;p-&gt;celliax_serial_synced_timestamp);
+  return 0;
+}
+
+int celliax_serial_answer_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  if (AST_STATE_RING == p-&gt;interface_state) {
+    DEBUGA_CVM(&quot;Sending commands to answer an incomming call...\n&quot;, CELLIAX_P_LOG);
+    celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_CONNECT_REQ, 0, NULL);
+
+  } else {
+    DEBUGA_CVM
+      (&quot;SKIPPING Sending commands to answer an incomming call, because: !AST_STATE_RING\n&quot;,
+       CELLIAX_P_LOG);
+  }
+
+  return 0;
+}
+
+int celliax_serial_call_CVM_BUSMAIL(struct celliax_pvt *p, char *dstr)
+{
+  unsigned char bCallType = 0x01;   /* INTERNAL */
+
+  unsigned char DialReqBuff[2];
+
+  celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_SETUP_REQ, sizeof(bCallType),
+                                             &amp;bCallType);
+
+  while (AST_STATE_DOWN == p-&gt;interface_state) {
+    usleep(10000);              //10msec
+  }
+
+  if (AST_STATE_DIALING == p-&gt;interface_state) {
+    /* as for now, we only support internal calls */
+    /* &quot;0&quot; - call speaker phone */
+    /* &quot;1&quot; - call handset #1 */
+    /* &quot;2&quot; - call handset #2 */
+    /* ... */
+
+    DialReqBuff[0] = 1;         /* number of digits to send */
+    DialReqBuff[1] = dstr[0];   /* digit to send */
+
+    celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_KEYPAD_REQ, 2, DialReqBuff);
+  }
+
+  if (option_debug)
+    NOTICA(&quot;CVM_BUSMAIL: sent commands to call\n&quot;, CELLIAX_P_LOG);
+  return 0;
+}
+
+int celliax_serial_hangup_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  unsigned char bReason = 0x0;  /* Normal hang-up */
+
+  if (p-&gt;interface_state != AST_STATE_DOWN) {
+    celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_RELEASE_REQ, sizeof(bReason),
+                                               &amp;bReason);
+
+    DEBUGA_CVM(&quot;CVM_BUSMAIL: sent commands to hangup the call\n&quot;, CELLIAX_P_LOG);
+
+  } else {
+    DEBUGA_CVM(&quot;CVM_BUSMAIL: sent commands to hangup skipped because: AST_STATE_DOWN\n&quot;,
+               CELLIAX_P_LOG);
+  }
+
+  return 0;
+}
+
+int celliax_serial_config_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  int res;
+  int how_many_reads = 0;
+  unsigned char SubcriptionNo = p-&gt;cvm_subsc_no;
+  unsigned char RegistartionData[5];
+
+  p-&gt;cvm_lock_state = CVM_UNKNOWN_LOCK_STATE;
+  p-&gt;cvm_register_state = CVM_UNKNOWN_REGISTER_STATE;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  CVM_LOKKA(&amp;p-&gt;controldev_lock);
+
+  if (option_debug &gt; 1)
+    DEBUGA_CVM(&quot;Try to init communication with CVM...\n&quot;, CELLIAX_P_LOG);
+
+  /* CVM after reset sends SABM CTRL frame, let's assume that CVM already sent it, that's the reply... */
+  celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p,
+                                             BUSMAIL_HEADER_CTRL_FRAME |
+                                             BUSMAIL_HEADER_CTRL_UN_FRAME |
+                                             BUSMAIL_HEADER_UNID_SABM);
+  /*  usleep(10000);   *//* 10ms */
+
+  /* Now we are sending SABM CTRL frame, if CVM is out there, it should reply... */
+  celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p,
+                                             BUSMAIL_HEADER_CTRL_FRAME |
+                                             BUSMAIL_HEADER_CTRL_UN_FRAME |
+                                             BUSMAIL_HEADER_UNID_SABM |
+                                             (BUSMAIL_HEADER_PF_BIT_MASK &amp; 0xFF));
+//  usleep(1000);
+
+  res = celliax_serial_read_CVM_BUSMAIL(p); //we don't have no monitor neither do_controldev_thread
+
+  DEBUGA_CVM(&quot;celliax_serial_read_CVM_BUSMAIL res= %X, expected %X\n&quot;, CELLIAX_P_LOG, res,
+             (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_UN_FRAME |
+              BUSMAIL_HEADER_SABM));
+
+  if (res == -1) {
+    ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+    CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+
+  how_many_reads = 0;
+
+  while ((res &amp; 0xF0) !=
+         (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_UN_FRAME | BUSMAIL_HEADER_SABM))
+  {
+
+    usleep(1000);
+    res = celliax_serial_read_CVM_BUSMAIL(p);
+    how_many_reads++;
+
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+
+    if (how_many_reads &gt; 10) {
+      ERRORA(&quot;no expected results in %d celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG,
+             how_many_reads);
+
+      ERRORA(&quot;Unable to initialize cmmunication with CVM...\n&quot;, CELLIAX_P_LOG);
+
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+  }
+
+  DEBUGA_CVM(&quot;Communication with CVM initialized successfully...\n&quot;, CELLIAX_P_LOG);
+
+  DEBUGA_CVM(&quot;Attempt to lock to FP...\n&quot;, CELLIAX_P_LOG);
+
+  /* Try to connect to FP, try to lock to FP, maybe we registered with it in the past... */
+  /* CVM can hold up to 2 subscriptions in its EEPROM, celliax.conf contains number we should try */
+  /* eg. cvm_subscription_no = 1 */
+
+  celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_LOCK_REQ, sizeof(SubcriptionNo),
+                                             &amp;SubcriptionNo);
+
+  usleep(10000);
+
+  res = celliax_serial_read_CVM_BUSMAIL(p); //we don't have no monitor neither do_controldev_thread
+
+  if (res == -1) {
+    ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+    CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+
+  how_many_reads = 0;
+
+  while (CVM_UNKNOWN_LOCK_STATE == p-&gt;cvm_lock_state) {
+
+/*    
+    if (0 == (how_many_reads % 10))
+    {
+      DEBUGA_CVM(&quot;Attempt to lock to FP... %d\n&quot;, CELLIAX_P_LOG, how_many_reads/10 );
+      celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_LOCK_REQ, sizeof(SubcriptionNo) ,&amp;SubcriptionNo);
+    }
+*/
+
+    usleep(100000);
+
+    res = celliax_serial_read_CVM_BUSMAIL(p);
+    how_many_reads++;
+
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+
+    if (how_many_reads &gt; 50) {
+      ERRORA(&quot;no expected results in %d celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG,
+             how_many_reads);
+
+      ERRORA(&quot;Unable to lock to FP...\n&quot;, CELLIAX_P_LOG);
+      break;
+    }
+  }
+
+  if (CVM_LOCKED_TO_FP == p-&gt;cvm_lock_state) {
+    DEBUGA_CVM(&quot;CVM locked to FP successfully...\n&quot;, CELLIAX_P_LOG);
+  } else {
+    DEBUGA_CVM(&quot;Lock to FP failed, Attempt to register to FP...\n&quot;, CELLIAX_P_LOG);
+
+    RegistartionData[0] = SubcriptionNo;
+    RegistartionData[1] = 0xFF;
+    RegistartionData[2] = 0xFF;
+
+    if (1 == SubcriptionNo) {
+      RegistartionData[3] =
+        (((p-&gt;cvm_subsc_1_pin[3] - 0x30) &amp; 0x0F) &lt;&lt; 4) | ((p-&gt;cvm_subsc_1_pin[2] -
+                                                           0x30) &amp; 0x0F);
+      RegistartionData[4] =
+        (((p-&gt;cvm_subsc_1_pin[1] - 0x30) &amp; 0x0F) &lt;&lt; 4) | ((p-&gt;cvm_subsc_1_pin[0] -
+                                                           0x30) &amp; 0x0F);
+    } else {
+      RegistartionData[3] =
+        (((p-&gt;cvm_subsc_2_pin[3] - 0x30) &amp; 0x0F) &lt;&lt; 4) | ((p-&gt;cvm_subsc_2_pin[2] -
+                                                           0x30) &amp; 0x0F);
+      RegistartionData[4] =
+        (((p-&gt;cvm_subsc_2_pin[1] - 0x30) &amp; 0x0F) &lt;&lt; 4) | ((p-&gt;cvm_subsc_2_pin[0] -
+                                                           0x30) &amp; 0x0F);
+    }
+
+    celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_ACCESS_RIGHTS_REQ,
+                                               sizeof(RegistartionData),
+                                               RegistartionData);
+
+    usleep(100000);
+
+    res = celliax_serial_read_CVM_BUSMAIL(p);   //we don't have no monitor neither do_controldev_thread
+
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+
+    how_many_reads = 0;
+
+    while (CVM_UNKNOWN_REGISTER_STATE == p-&gt;cvm_register_state) {
+
+      if (0 == (how_many_reads % 50)) {
+        DEBUGA_CVM(&quot;Attempt to register to FP... %d\n&quot;, CELLIAX_P_LOG,
+                   how_many_reads / 10);
+        celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_ACCESS_RIGHTS_REQ,
+                                                   sizeof(RegistartionData),
+                                                   RegistartionData);
+      }
+
+      /* up to 5 minutes for registration.... */
+      usleep(1000000);
+      res = celliax_serial_read_CVM_BUSMAIL(p);
+      how_many_reads++;
+
+      if (res == -1) {
+        ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+        CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+        return -1;
+      }
+
+      if (how_many_reads &gt; 300) {
+        ERRORA(&quot;no expected results in %d celliax_serial_read_CVM_BUSMAIL\n&quot;,
+               CELLIAX_P_LOG, how_many_reads);
+
+        ERRORA(&quot;Unable to communication with CVM...\n&quot;, CELLIAX_P_LOG);
+
+        CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+        return -1;
+      }
+    }
+
+    if (CVM_REGISTERED_TO_FP != p-&gt;cvm_register_state) {
+      ERRORA(&quot;Unable to register to FP...\n&quot;, CELLIAX_P_LOG);
+
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+
+    } else {
+      DEBUGA_CVM(&quot;CVM registered to FP successfully...\n&quot;, CELLIAX_P_LOG);
+      DEBUGA_CVM(&quot;Attempt to lock to FP...\n&quot;, CELLIAX_P_LOG);
+
+      /* Try to connect to FP, try to lock to FP, maybe we registered with it in the past... */
+      /* CVM can hold up to 2 subscriptions in its EEPROM, celliax.conf contains number we should try */
+      /* eg. cvm_subscription_no = 1 */
+
+      celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_LOCK_REQ,
+                                                 sizeof(SubcriptionNo), &amp;SubcriptionNo);
+
+      usleep(10000);
+
+      res = celliax_serial_read_CVM_BUSMAIL(p); //we don't have no monitor neither do_controldev_thread
+
+      if (res == -1) {
+        ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+        CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+        return -1;
+      }
+
+      how_many_reads = 0;
+
+      while (CVM_UNKNOWN_LOCK_STATE == p-&gt;cvm_lock_state) {
+
+        if (0 == (how_many_reads % 10)) {
+          DEBUGA_CVM(&quot;Attempt to lock to FP... %d\n&quot;, CELLIAX_P_LOG, how_many_reads / 10);
+          celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_ACCESS_RIGHTS_REQ,
+                                                     sizeof(RegistartionData),
+                                                     RegistartionData);
+        }
+
+        usleep(10000);
+        res = celliax_serial_read_CVM_BUSMAIL(p);
+        how_many_reads++;
+
+        if (res == -1) {
+          ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+          CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+          return -1;
+        }
+
+        if (how_many_reads &gt; 100) {
+          ERRORA(&quot;no expected results in %d celliax_serial_read_CVM_BUSMAIL\n&quot;,
+                 CELLIAX_P_LOG, how_many_reads);
+
+          ERRORA(&quot;Unable to communication with CVM...\n&quot;, CELLIAX_P_LOG);
+
+          CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+          return -1;
+        }
+      }
+
+      if (CVM_LOCKED_TO_FP != p-&gt;cvm_lock_state) {
+        ERRORA(&quot;Unable to lock to FP...\n&quot;, CELLIAX_P_LOG);
+
+        CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+        return -1;
+      } else {
+        DEBUGA_CVM(&quot;CVM locked to FP successfully...\n&quot;, CELLIAX_P_LOG);
+      }
+    }
+  }
+
+  usleep(100000);
+
+  CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  return 0;
+
+}
+
+int celliax_serial_read_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  int read_count;
+  int select_err;
+  fd_set read_fds;
+  struct timeval timeout;
+  int cvm_busmail_mesg = 0;
+  unsigned char busmail_crc = 0;
+  unsigned char MsgCrc = 0;
+  unsigned char MsgHeader = 0;
+  unsigned char MsgTxSeqNo = 0;
+  unsigned char MsgRxSeqNo = 0;
+  unsigned char MsgTaskId = 0;
+  unsigned char MsgProgId = 0;
+  unsigned char MsgPrimitiveLSB = 0;
+  unsigned char MsgPrimitiveMSB = 0;
+  unsigned int MsgPrimitive = 0;
+
+  int i = 0;
+
+  FD_ZERO(&amp;read_fds);
+  FD_SET(p-&gt;controldevfd, &amp;read_fds);
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 10000;
+
+  if ((select_err = select(p-&gt;controldevfd + 1, &amp;read_fds, NULL, NULL, &amp;timeout)) &gt; 0) {
+    timeout.tv_sec = 0;         //reset the timeout, linux modify it
+    timeout.tv_usec = 10000;    //reset the timeout, linux modify it
+    PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+    CVM_LOKKA(&amp;p-&gt;controldev_lock);
+
+    while ((select_err =
+            select(p-&gt;controldevfd + 1, &amp;read_fds, NULL, NULL, &amp;timeout)) &gt; 0) {
+      gettimeofday(&amp;p-&gt;cvm_busmail_list_tv, &amp;p-&gt;cvm_busmail_list_tz);
+      read_count = read(p-&gt;controldevfd, p-&gt;rxm, 255);
+
+      if (read_count == 0) {
+        ERRORA
+          (&quot;read 0 bytes!!! Nenormalno! Marking this celliax_serial_device %s as dead, andif it is owned by a channel, hanging up. Maybe the CVM is stuck, switched off or power down.\n&quot;,
+           CELLIAX_P_LOG, p-&gt;controldevice_name);
+
+        p-&gt;controldev_dead = 1;
+        close(p-&gt;controldevfd);
+        CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+        if (p-&gt;owner)
+          celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+        return -1;
+      }
+
+      if (option_debug &gt; 10) {
+        char debug_buf[1024];
+        char *debug_buf_pos;
+
+        memset(debug_buf, 0, 1024);
+        debug_buf_pos = debug_buf;
+        for (i = 0; i &lt; read_count; i++) {
+          debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, p-&gt;rxm[i]);
+          if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+            break;
+        }
+
+        DEBUGA_CVM(&quot;%s READ AT seconds=%ld usec=%6ld read_count=%d\n&quot;, CELLIAX_P_LOG,
+                   debug_buf, p-&gt;cvm_busmail_list_tv.tv_sec,
+                   p-&gt;cvm_busmail_list_tv.tv_usec, read_count);
+      }
+
+      for (i = 0; i &lt; read_count; i++) {
+        if (p-&gt;rxm[i] == BUSMAIL_SOF) {
+          /* if we have identified the start of an busmail frame sent to us by the CVM */
+          /* clean the array, copy into it the beginning of the frame, move the counter in the array after the last byte copied */
+          memset(p-&gt;array, 0, 255);
+          p-&gt;array[0] = p-&gt;rxm[i];
+          p-&gt;arraycounter = 1;
+        }
+
+        /* buffer overload protection */
+        if (255 == p-&gt;arraycounter) {
+          p-&gt;arraycounter = 1;
+        }
+
+        /* continue copying into the array, until... */
+        p-&gt;array[p-&gt;arraycounter - 1] = p-&gt;rxm[i];
+
+        /* we reach the end of the incoming frame, its lenght is in the p-&gt;array[BUSMAIL_OFFSET_LEN_LSB] byte, plus overhead */
+        if (p-&gt;arraycounter == p-&gt;array[BUSMAIL_OFFSET_LEN_LSB] + 4) {
+
+          tcflush(p-&gt;controldevfd, TCIFLUSH);   /* PL2303HX bug? */
+          /* start categorizing frames */
+
+          if (option_debug &gt; 10) {
+            char debug_buf[1024];
+            char *debug_buf_pos;
+
+            memset(debug_buf, 0, 1024);
+            debug_buf_pos = debug_buf;
+
+            for (i = 0; i &lt; p-&gt;arraycounter; i++) {
+              debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, p-&gt;array[i]);
+              if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+                break;
+            }
+
+            DEBUGA_CVM(&quot;%s was received, Starting to categorize this frame\n&quot;,
+                       CELLIAX_P_LOG, debug_buf);
+          }
+
+          int known = 0;
+          int j = 0;
+
+          busmail_crc = 0;
+          MsgCrc = p-&gt;array[p-&gt;arraycounter - 1];
+
+          busmail_crc = (unsigned char) (p-&gt;array[BUSMAIL_OFFSET_HEADER] + busmail_crc);
+
+          for (j = BUSMAIL_OFFSET_MAIL; j &lt; (p-&gt;arraycounter - 1); j++) {
+            busmail_crc = (unsigned char) (p-&gt;array[j] + busmail_crc);
+          }
+
+          if (busmail_crc != MsgCrc) {
+            WARNINGA(&quot;BUSMAIL MSG CRC FAILED!, MsgCrc %.2X, calcd %.2X, dropping frame\n&quot;,
+                     CELLIAX_P_LOG, MsgCrc, busmail_crc);
+          } else {
+            /* first step in categorizing frames, look at the general kind of frame, in p-&gt;array[BUSMAIL_OFFSET_HEADER] */
+            if (option_debug &gt; 1)
+              DEBUGA_CVM(&quot;BUSMAIL MSG CRC, MsgCrc %.2X, calcd %.2X...\n&quot;, CELLIAX_P_LOG,
+                         MsgCrc, busmail_crc);
+
+            MsgHeader = p-&gt;array[BUSMAIL_OFFSET_HEADER];
+            cvm_busmail_mesg = MsgHeader;
+
+            switch (MsgHeader &amp; BUSMAIL_HEADER_IC_BIT_MASK) {
+            case BUSMAIL_HEADER_INFO_FRAME:
+              /* analyzis of frame header */
+              MsgTxSeqNo = ((MsgHeader &amp; BUSMAIL_HEADER_TXSEQ_MASK) &gt;&gt; 4);
+              MsgRxSeqNo = ((MsgHeader &amp; BUSMAIL_HEADER_RXSEQ_MASK));
+
+              if (option_debug &gt; 1)
+                DEBUGA_CVM(&quot;BUSMAIL_HEADER_INFO_FRAME TxSeq %X, RxSeq %X\n&quot;,
+                           CELLIAX_P_LOG, MsgTxSeqNo, MsgRxSeqNo);
+
+              if (((p-&gt;busmail_rxseq_cvm_last + 1) &amp; 0x7) != MsgTxSeqNo) {
+                /* some CVM frames are missing, TxSeq of this frame is higher then expected */
+                /* reject, I expected p-&gt;busmail_rxseq_cvm_last + 1, resend it to me, please */
+
+                WARNINGA(&quot;CVM TxSeq %X, does not match expected value %X\n&quot;,
+                         CELLIAX_P_LOG, MsgTxSeqNo,
+                         (p-&gt;busmail_rxseq_cvm_last + 1) &amp; 0x7);
+
+//                celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p, BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME | BUSMAIL_HEADER_SUID_REJ);
+
+                if (((p-&gt;busmail_rxseq_cvm_last) &amp; 0x7) == MsgTxSeqNo) {
+
+                  WARNINGA
+                    (&quot;It looks like our ACK to this frame was MIA :), lets ACK the frame one more time...\n&quot;,
+                     CELLIAX_P_LOG);
+
+                  /* if the frame we received informs us that other side is waiting for ACK, let's ACK it */
+                  /* even if it is unknown to us */
+                  if (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_PF_BIT_MASK) {
+                    if (BUSMAIL_HEADER_SABM ==
+                        (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_SABM_MASK)) {
+                      celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p, (unsigned char)
+                                                                 (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_UN_FRAME | BUSMAIL_HEADER_UNID_SABM));
+                    } else {
+                      celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p, (unsigned char)
+                                                                 (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME | BUSMAIL_HEADER_SUID_RR));
+                    }
+                  }
+                }
+
+                break;
+              } else {
+                /* we expected packet with this seq no. */
+                /* CVM ACKed our frames with info frame */
+                celliax_serial_list_acknowledge_CVM_BUSMAIL(p, MsgRxSeqNo);
+
+                /* save it but limit it to 3 bits only (valid values: 0-7) */
+                p-&gt;busmail_rxseq_cvm_last = MsgTxSeqNo;
+                p-&gt;busmail_rxseq_cvm_last &amp;= 0x7;
+
+                /* if the frame we received informs us that other side is waiting for ACK, let's ACK it */
+                /* even if it is unknown to us */
+                if (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_PF_BIT_MASK) {
+                  if (BUSMAIL_HEADER_SABM ==
+                      (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_SABM_MASK)) {
+                    celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p, (unsigned char)
+                                                               (BUSMAIL_HEADER_CTRL_FRAME
+                                                                |
+                                                                BUSMAIL_HEADER_CTRL_UN_FRAME
+                                                                |
+                                                                BUSMAIL_HEADER_UNID_SABM));
+                  } else {
+                    celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p, (unsigned char)
+                                                               (BUSMAIL_HEADER_CTRL_FRAME
+                                                                |
+                                                                BUSMAIL_HEADER_CTRL_SU_FRAME
+                                                                |
+                                                                BUSMAIL_HEADER_SUID_RR));
+                  }
+                }
+
+              }
+
+              /* frame header OK, let's see what's inside mail field */
+              MsgTaskId = p-&gt;array[BUSMAIL_OFFSET_MAIL_TASK_ID];
+              MsgProgId = p-&gt;array[BUSMAIL_OFFSET_MAIL_PROGRAM_ID];
+              MsgPrimitiveLSB = p-&gt;array[BUSMAIL_OFFSET_MAIL_PRIMITIVE_LSB];
+              MsgPrimitiveMSB = p-&gt;array[BUSMAIL_OFFSET_MAIL_PRIMITIVE_MSB];
+              MsgPrimitive = MsgPrimitiveMSB &lt;&lt; 8 | MsgPrimitiveLSB;
+
+              if (option_debug &gt; 1)
+                DEBUGA_CVM
+                  (&quot;BUSMAIL_HEADER_INFO_FRAME ProgId %X, TaskId %X, Primitive %X %X\n&quot;,
+                   CELLIAX_P_LOG, MsgProgId, MsgTaskId, MsgPrimitiveMSB, MsgPrimitiveLSB);
+
+              switch (MsgPrimitive) {
+
+              case API_PP_ACCESS_RIGHTS_REJ:
+                /* FP rejected our registration... */
+                WARNINGA(&quot;API_PP_ACCESS_RIGHTS_REJ, FP rejected our registration...\n&quot;,
+                         CELLIAX_P_LOG);
+
+                p-&gt;cvm_register_state = CVM_UNREGISTERED_TO_FP;
+                p-&gt;cvm_lock_state = CVM_UNKNOWN_LOCK_STATE;
+                known = 1;
+                break;
+
+              case API_PP_ACCESS_RIGHTS_CFM:
+                /* FP accepted our registration... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_ACCESS_RIGHTS_CFM, FP accepted our registration...\n&quot;,
+                     CELLIAX_P_LOG);
+
+                p-&gt;cvm_register_state = CVM_REGISTERED_TO_FP;
+                p-&gt;cvm_lock_state = CVM_UNKNOWN_LOCK_STATE;
+                p-&gt;cvm_handset_no = p-&gt;array[BUSMAIL_OFFSET_MAIL_PARAMS + 0];
+                p-&gt;cvm_fp_is_cvm = p-&gt;array[BUSMAIL_OFFSET_MAIL_PARAMS + 1];
+
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_ACCESS_RIGHTS_CFM, FP accepted our registration, Our handset no. is %d, CVM? %X\n&quot;,
+                     CELLIAX_P_LOG, p-&gt;cvm_handset_no, p-&gt;cvm_fp_is_cvm);
+
+                known = 1;
+                break;
+
+              case API_PP_LOCKED_IND:
+                /* CVM is connected to FP */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_LOCKED_IND, Connection to FP completed...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                p-&gt;cvm_register_state = CVM_REGISTERED_TO_FP;
+                p-&gt;cvm_lock_state = CVM_LOCKED_TO_FP;
+                known = 1;
+                break;
+
+              case API_PP_UNLOCKED_IND:
+                /* CVM is unlocked with FP, Out of service */
+                WARNINGA
+                  (&quot;API_PP_UNLOCKED_IND, CVM is unlocked with FP, Out of service !!!\n&quot;,
+                   CELLIAX_P_LOG);
+
+                p-&gt;cvm_lock_state = CVM_UNLOCKED_TO_FP;
+                known = 1;
+                break;
+
+              case API_PP_SETUP_ACK_IND:
+                /* Outgoing call, connection to FP established, FP is waiting for a number to dial */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_SETUP_ACK_IND, connection to FP established, FP is waiting for a numer to dial...\n&quot;,
+                     CELLIAX_P_LOG);
+
+                if (AST_STATE_DOWN == p-&gt;interface_state) {
+                  p-&gt;interface_state = AST_STATE_DIALING;
+                }
+
+                known = 1;
+                break;
+
+              case API_PP_ALERT_IND:
+                /* Outgoing call, Remote end is ringing */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_ALERT_IND, remote end is ringing...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                if (AST_STATE_DIALING == p-&gt;interface_state) {
+                  p-&gt;interface_state = AST_STATE_RINGING;
+                }
+
+                known = 1;
+                break;
+
+              case API_PP_CONNECT_IND:
+                /* Outgoing call, the remote end answered our call */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_CONNECT_IND, our call was answered...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                if (AST_STATE_RINGING == p-&gt;interface_state) {
+
+                  /* let's open audio and have a chat */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_OPEN_REQ, 0,
+                                                             NULL);
+
+                  unsigned char volume = (unsigned char) p-&gt;cvm_volume_level;
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p,
+                                                             CVM_PP_AUDIO_SET_VOLUME_REQ,
+                                                             sizeof(volume), &amp;volume);
+
+                  /* let's unmute mic and have a chat */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p,
+                                                             CVM_PP_AUDIO_UNMUTE_MIC_REQ,
+                                                             0, NULL);
+
+                  /* let's switch to headset, because we fried normal output.... */
+/*                  unsigned char headset_on = (unsigned char) 1;
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_HS_PLUG_IND, sizeof(headset_on), &amp;headset_on);
+*/
+                  p-&gt;interface_state = AST_STATE_UP;
+                  ast_setstate(p-&gt;owner, AST_STATE_RINGING);
+                  celliax_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+                }
+
+                known = 1;
+                break;
+
+              case API_PP_REJECT_IND:
+                /* Outgoing/Incoming call, FP rejected our connection... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_REJECT_IND, FP or ther PP rejected our connection...\n&quot;,
+                     CELLIAX_P_LOG);
+
+                if (AST_STATE_RING == p-&gt;interface_state &amp;&amp; p-&gt;owner) {
+                  /* Attempt to answer incoming call rejected by FP or PP */
+                  if (option_debug &gt; 1)
+                    DEBUGA_CVM(&quot;Was it PAGE_ALL CALL, that we should not answered?\n&quot;,
+                               CELLIAX_P_LOG);
+
+                  p-&gt;interface_state = AST_STATE_DOWN;
+                  celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+
+                } else if (AST_STATE_DOWN != p-&gt;interface_state &amp;&amp; p-&gt;owner) {
+                  /* Outgoing call rejected by other PP or FP */
+                  p-&gt;interface_state = AST_STATE_BUSY;
+                  ast_setstate(p-&gt;owner, AST_STATE_BUSY);
+                  celliax_queue_control(p-&gt;owner, AST_CONTROL_BUSY);
+                }
+
+                known = 1;
+                break;
+
+              case API_PP_SIGNAL_ON_IND:
+                /* Ringback, ignore it... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_SIGNAL_ON_IND, Ringback, ignore it...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                known = 1;
+                break;
+
+              case API_PP_SIGNAL_OFF_IND:
+                /* Ringback, ignore it... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_SIGNAL_OFF_IND, Ringback, ignore it...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                known = 1;
+                break;
+
+              case API_PP_SETUP_IND:
+                /* Incoming call, Somebody is calling us */
+
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_SETUP_IND, somebody is calling us...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                if (AST_STATE_DOWN == p-&gt;interface_state) {
+
+                  if (API_PP_SETUP_IND_CALL_INT ==
+                      p-&gt;array[BUSMAIL_OFFSET_MAIL_PARAMS +
+                               API_PP_SETUP_IND_CALL_TYPE_OFFSET]) {
+                    DEBUGA_CVM(&quot;INTERNAL CALL, receive it...\n&quot;, CELLIAX_P_LOG);
+
+                    p-&gt;interface_state = AST_STATE_RING;
+
+                    /* inform calling end, that we know about his call, and that we are alerting */
+                    celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_ALERT_REQ, 0,
+                                                               NULL);
+
+                    /* let's open audio before valid mac, to remove noise... */
+                    celliax_serial_send_info_frame_CVM_BUSMAIL(p,
+                                                               CVM_PP_AUDIO_OPEN_ADPCM_OFF_REQ,
+                                                               0, NULL);
+
+                  } else {
+                    DEBUGA_CVM(&quot;NOT an INTERNAL CALL, CALL TYPE %X, just ignore it...\n&quot;,
+                               CELLIAX_P_LOG,
+                               p-&gt;array[BUSMAIL_OFFSET_MAIL_PARAMS +
+                                        API_PP_SETUP_IND_CALL_TYPE_OFFSET]);
+
+                    /* inform calling end, that we know about his call, and that we are alerting OR not :) */
+                    /* probably it is needed so FP does not remove us from PP list :) */
+                    celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_ALERT_REQ, 0,
+                                                               NULL);
+                  }
+
+                } else {
+                  WARNINGA
+                    (&quot;Ignore incoming call, Wrong interface state, current state %X\n&quot;,
+                     CELLIAX_P_LOG, p-&gt;interface_state);
+                }
+
+                known = 1;
+                break;
+
+              case API_PP_ALERT_OFF_IND:
+                /* Incoming call, We should stop alerting about incoming call... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_ALERT_OFF_IND, Ringback, stop alerting about incoming call...\n&quot;,
+                     CELLIAX_P_LOG);
+
+                known = 1;
+                break;
+
+              case API_PP_ALERT_ON_IND:
+                /* Incoming call, We should stop alerting about incoming call... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_ALERT_ON_IND, Ringback, start alerting about incoming call...\n&quot;,
+                     CELLIAX_P_LOG);
+/*
+                if (AST_STATE_DOWN == p-&gt;interface_state) {
+                  DEBUGA_CVM(&quot;Somebody is calling us, we see a PP_ALERT_ON_IND, receive it...\n&quot;, CELLIAX_P_LOG);
+                  p-&gt;interface_state = AST_STATE_RING;
+                }
+*/
+                known = 1;
+                break;
+
+              case API_PP_CONNECT_CFM:
+                /* Incoming call, Confirmation for request to answer incoming call... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_CONNECT_CFM, Confirmation for request to answer incoming call...\n&quot;,
+                     CELLIAX_P_LOG);
+
+                if (AST_STATE_RING == p-&gt;interface_state &amp;&amp; p-&gt;owner) {
+
+                  p-&gt;interface_state = AST_STATE_UP;
+                  ast_setstate(p-&gt;owner, AST_STATE_UP);
+
+                  /* let's open audio and have a chat */
+//                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_OPEN_ADPCM_OFF_REQ, 0, NULL);
+
+                  /* let's open audio and have a chat */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_OPEN_REQ, 0,
+                                                             NULL);
+
+                  unsigned char volume = (unsigned char) p-&gt;cvm_volume_level;
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p,
+                                                             CVM_PP_AUDIO_SET_VOLUME_REQ,
+                                                             sizeof(volume), &amp;volume);
+
+                  /* let's unmute mic and have a chat */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p,
+                                                             CVM_PP_AUDIO_UNMUTE_MIC_REQ,
+                                                             0, NULL);
+
+                  /* let's switch to headset, because we fried normal output.... */
+/*                  unsigned char headset_on = (unsigned char) 1;
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_HS_PLUG_IND, sizeof(headset_on), &amp;headset_on);
+*/
+                } else {
+                  WARNINGA
+                    (&quot;Ignore connection cfm, Wrong interface state, current state %X\n&quot;,
+                     CELLIAX_P_LOG, p-&gt;interface_state);
+                }
+
+                known = 1;
+                break;
+
+              case API_PP_RELEASE_CFM:
+                /* Confirmation for request to hangup a call... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM
+                    (&quot;API_PP_RELEASE_CFM, Confirmation for request to hangup a call..\n&quot;,
+                     CELLIAX_P_LOG);
+
+                if (AST_STATE_UP == p-&gt;interface_state) {
+                  /* let's close audio */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_CLOSE_REQ, 0,
+                                                             NULL);
+
+                  /* let's unmute mic and have a chat */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_MUTE_MIC_REQ,
+                                                             0, NULL);
+                }
+
+                p-&gt;interface_state = AST_STATE_DOWN;
+
+                known = 1;
+                break;
+
+              case API_PP_RELEASE_IND:
+                /* FP releases connection to CVM... */
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_RELEASE_IND, FP releases connection to CVM...\n&quot;,
+                             CELLIAX_P_LOG);
+
+                if (AST_STATE_UP == p-&gt;interface_state &amp;&amp; p-&gt;owner) {
+                  /* let's close audio */
+                  celliax_serial_send_info_frame_CVM_BUSMAIL(p, CVM_PP_AUDIO_CLOSE_REQ, 0,
+                                                             NULL);
+                  p-&gt;interface_state = AST_STATE_DOWN;
+                  celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+
+                } else if (AST_STATE_RING == p-&gt;interface_state &amp;&amp; p-&gt;owner) {
+                  /* workaround for PAGE ALL CALL, FIXME!!!! */
+                  if (option_debug &gt; 1)
+                    DEBUGA_CVM(&quot;WAS IT A PAGE ALL ???...\n&quot;, CELLIAX_P_LOG);
+
+                  p-&gt;interface_state = AST_STATE_UP;
+                  usleep(100000);
+
+                  p-&gt;interface_state = AST_STATE_DOWN;
+                  celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+                }
+
+                /* we need to ACK release */
+                celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_RELEASE_RES, 0,
+                                                           NULL);
+
+                known = 1;
+                break;
+
+              case API_PP_READ_RSSI_CFM:
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;API_PP_READ_RSSI_CFM, RSSI readout...\n&quot;, CELLIAX_P_LOG);
+
+                p-&gt;cvm_rssi = p-&gt;array[BUSMAIL_OFFSET_MAIL_PARAMS + 0];
+                int rssi_percent = p-&gt;cvm_rssi * 100 / 0x3F;
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;RSSI is %X, %d%%...\n&quot;, CELLIAX_P_LOG, p-&gt;cvm_rssi,
+                             rssi_percent);
+
+                known = 1;
+                break;
+              default:
+                WARNINGA(&quot;UNKNOWN MsgPrimitive!!! %X\n&quot;, CELLIAX_P_LOG, MsgPrimitive);
+                break;
+              }
+
+              break;
+
+            case BUSMAIL_HEADER_CTRL_FRAME:
+              if (option_debug &gt; 1)
+                DEBUGA_CVM(&quot;BUSMAIL_HEADER_CTRL_FRAME\n&quot;, CELLIAX_P_LOG);
+
+              switch (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_SU_BIT_MASK) {
+              case BUSMAIL_HEADER_CTRL_SU_FRAME:
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;BUSMAIL_HEADER_CTRL_SU_FRAME\n&quot;, CELLIAX_P_LOG);
+
+                switch (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_SUID_MASK) {
+                case BUSMAIL_HEADER_SUID_REJ:
+                  /* CVM Reject, CVM missed one of our packets, it will be resend, do nothing */
+                  MsgRxSeqNo =
+                    ((p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_RXSEQ_MASK));
+
+                  if (option_debug &gt; 1)
+                    DEBUGA_CVM(&quot;BUSMAIL_HEADER_SUID_REJ, RxSeq %X\n&quot;, CELLIAX_P_LOG,
+                               MsgRxSeqNo);
+
+                  /* Even that it is CVM Reject packet, it still ACKs some packets */
+                  celliax_serial_list_acknowledge_CVM_BUSMAIL(p, MsgRxSeqNo);
+
+                  known = 1;
+                  break;
+                case BUSMAIL_HEADER_SUID_RNR:
+                  /* CVM Receiver Not Ready, answer to packet that we sent, do nothing, it will be resend later */
+                  MsgRxSeqNo =
+                    ((p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_RXSEQ_MASK));
+
+                  if (option_debug &gt; 1)
+                    DEBUGA_CVM(&quot;BUSMAIL_HEADER_SUID_RNR, RxSeq %X\n&quot;, CELLIAX_P_LOG,
+                               MsgRxSeqNo);
+
+                  known = 1;
+                  break;
+                case BUSMAIL_HEADER_SUID_RR:
+                  /* CVM ACKs our packets */
+                  MsgRxSeqNo =
+                    ((p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_RXSEQ_MASK));
+
+                  if (option_debug &gt; 1)
+                    DEBUGA_CVM(&quot;BUSMAIL_HEADER_SUID_RR, RxSeq %X\n&quot;, CELLIAX_P_LOG,
+                               MsgRxSeqNo);
+
+                  /* CVM ACKed our frames with RR frame */
+                  celliax_serial_list_acknowledge_CVM_BUSMAIL(p, MsgRxSeqNo);
+
+                  known = 1;
+                  break;
+
+                default:
+                  WARNINGA(&quot;BUSMAIL_HEADER_SUID_UNKNOWN!!!\n&quot;, CELLIAX_P_LOG);
+                  break;
+                }
+                break;
+
+              case BUSMAIL_HEADER_CTRL_UN_FRAME:
+                if (option_debug &gt; 1)
+                  DEBUGA_CVM(&quot;BUSMAIL_HEADER_CTRL_UN_FRAME\n&quot;, CELLIAX_P_LOG);
+
+                switch (p-&gt;array[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_UNID_MASK) {
+
+                case BUSMAIL_HEADER_UNID_SABM:
+                  if (option_debug &gt; 1)
+                    DEBUGA_CVM(&quot;BUSMAIL_HEADER_UNID_SABM\n&quot;, CELLIAX_P_LOG);
+                  /* reset seq counters */
+                  p-&gt;busmail_txseq_celliax_last = 0xFF;
+                  p-&gt;busmail_rxseq_cvm_last = 0xFF;
+
+                  celliax_serial_lists_free_CVM_BUSMAIL(p);
+                  /* if needed, reply will be send by code at the end of switch statements */
+                  known = 1;
+                  break;
+
+                default:
+                  WARNINGA(&quot;BUSMAIL_HEADER_UNID_UNKNOWN!!!\n&quot;, CELLIAX_P_LOG);
+                  break;
+                }
+                break;
+
+              default:
+                WARNINGA(&quot;BUSMAIL_HEADER_CTRL_UNKNOWN!!!\n&quot;, CELLIAX_P_LOG);
+                break;
+              }
+              break;
+
+            default:
+              WARNINGA(&quot;BUSMAIL_HEADER_UNKNOWN!!!\n&quot;, CELLIAX_P_LOG);
+              break;
+            }
+
+          }
+
+          /* categorization of frame is ended, if it has not been recognized, whine */
+          if (!known) {
+            WARNINGA(&quot;BUSMAIL MSG UNKNOWN or REJECTED!\n&quot;, CELLIAX_P_LOG);
+          }
+        }
+        p-&gt;arraycounter++;
+      }
+    }
+    CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+    POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  }
+
+  /* oooops, select returned error, got a kill/cancel or problems with the serial file descriptor */
+  if (select_err == -1) {
+    if (errno != EINTR) {
+      ERRORA
+        (&quot;select returned -1 on %s, marking controldev as dead, errno was: %d, error was: %s\n&quot;,
+         CELLIAX_P_LOG, p-&gt;controldevice_name, errno, strerror(errno));
+
+      p-&gt;controldev_dead = 1;
+      close(p-&gt;controldevfd);
+
+      if (p-&gt;owner)
+        celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+      return -1;
+
+    } else {
+      WARNINGA(&quot;select returned -1 on %s, errno was: %d, EINTR, error was: %s\n&quot;,
+               CELLIAX_P_LOG, p-&gt;controldevice_name, errno, strerror(errno));
+      return 0;
+    }
+  }
+  /* OK, reading done, let's browse the list of pending frames to be sent, and act on it */
+  if (celliax_serial_send_if_time_CVM_BUSMAIL(p)) {
+    ERRORA(&quot;celliax_serial_send_if_time_CVM_BUSMAIL failed!\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+
+  return cvm_busmail_mesg;      //FIXME breaks the convention of returning 0 on success
+}
+
+int celliax_serial_getstatus_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  int res;
+  int how_many_reads = 0;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  CVM_LOKKA(&amp;p-&gt;controldev_lock);
+
+  if (option_debug &gt; 1)
+    DEBUGA_CVM(&quot;Sending RR CTRL frame wit PF bit set\n&quot;, CELLIAX_P_LOG);
+
+  /* this ctrl frame can be used as low level keep alive */
+  celliax_serial_send_ctrl_frame_CVM_BUSMAIL(p,
+                                             BUSMAIL_HEADER_CTRL_FRAME |
+                                             BUSMAIL_HEADER_CTRL_SU_FRAME |
+                                             BUSMAIL_HEADER_SUID_RR |
+                                             (BUSMAIL_HEADER_PF_BIT_MASK &amp; 0xFF));
+
+  //usleep(1000);
+
+  res = celliax_serial_read_CVM_BUSMAIL(p); //we don't have no monitor neither do_controldev_thread
+
+  if (res == -1) {
+    ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+    CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+    return -1;
+  }
+
+  while ((res &amp; 0xF0) !=
+         (BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME |
+          BUSMAIL_HEADER_SUID_RR)) {
+
+    usleep(1000);
+    res = celliax_serial_read_CVM_BUSMAIL(p);
+    how_many_reads++;
+
+    if (res == -1) {
+      ERRORA(&quot;failed celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG);
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+
+    if (how_many_reads &gt; 10) {
+      ERRORA(&quot;no expected results in %d celliax_serial_read_CVM_BUSMAIL\n&quot;, CELLIAX_P_LOG,
+             how_many_reads);
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+  }
+
+  //celliax_serial_send_info_frame_CVM_BUSMAIL(p, API_PP_READ_RSSI_REQ, 0, NULL);
+
+  CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  return 0;
+
+}
+
+/*!
+ * \brief Write on the serial port for all the CVM_BUSMAIL functions
+ * \param p celliax_pvt
+ * \param len lenght of buffer2
+ * \param buffer2 chars to be written
+ *
+ * Write on the serial port for all the CVM_BUSMAIL functions
+ *
+ * \return the number of chars written on the serial, 
+ * that can be different from len (or negative) in case of errors.
+ */
+int celliax_serial_send_CVM_BUSMAIL(struct celliax_pvt *p, int len,
+                                    unsigned char *mesg_ptr)
+{
+  int ret;
+  size_t actual = 0;
+  unsigned char *mesg_ptr2 = mesg_ptr;
+  PUSHA_UNLOCKA(&amp;p-&gt;controldev_lock);
+  CVM_LOKKA(&amp;p-&gt;controldev_lock);
+  do {
+    ret = write(p-&gt;controldevfd, mesg_ptr, len - actual);
+    if (ret &lt; 0 &amp;&amp; errno == EAGAIN)
+      continue;
+    if (ret &lt; 0) {
+      if (actual != len)
+        ERRORA(&quot;celliax_serial_write error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+      CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+      return -1;
+    }
+    actual += ret;
+    mesg_ptr += ret;
+    usleep(10000);
+//    usleep(p-&gt;cvm_celliax_serial_delay*1000);
+  } while (actual &lt; len);
+
+  usleep(p-&gt;cvm_celliax_serial_delay * 1000);
+
+//  tcdrain(p-&gt;controldevfd);
+
+  CVM_UNLOCKA(&amp;p-&gt;controldev_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;controldev_lock);
+
+  if (option_debug &gt; 10) {
+    int i;
+    char debug_buf[1024];
+    char *debug_buf_pos;
+
+    memset(debug_buf, 0, 1024);
+    debug_buf_pos = debug_buf;
+
+    for (i = 0; i &lt; len; i++) {
+      debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, mesg_ptr2[i]);
+      if (debug_buf_pos &gt; ((char *) &amp;debug_buf + 1000))
+        break;
+    }
+    DEBUGA_CVM(&quot;%s was sent down the wire\n&quot;, CELLIAX_P_LOG, debug_buf);
+  }
+
+  return 0;
+}
+
+/*!
+ * \brief Flags as acknowledged an BUSMAIL message previously sent
+ * \param p celliax_pvt
+ * \param seqnum identifier of the message to be acknowledged
+ *
+ * Called upon receiving an BUSMAIL acknoledgement message, browse the cvm_busmail_outgoing_list 
+ * looking for the seqnum sent BUSMAIL message, and flags it as acknowledged.
+ * (if an outgoing BUSMAIL message is not aknowledged by the cellphone in a while,
+ * it will be retransmitted)
+ *
+ * \return 0 on error, 1 otherwise
+ */
+int celliax_serial_list_acknowledge_CVM_BUSMAIL(struct celliax_pvt *p,
+                                                unsigned char AckTxSeqNo)
+{
+  struct cvm_busmail_msg *ptr = NULL;
+  struct cvm_busmail_msg *old = NULL;
+
+  unsigned char MsgTxSeqNo;
+  unsigned char MsgRxSeqNo;
+
+  ptr = p-&gt;cvm_busmail_outgoing_list;
+
+  if (ptr == NULL) {
+    ERRORA(&quot;cvm_busmail_outgoing_list is NULL ?\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+
+  PUSHA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+  CVM_LOKKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+/*
+  DEBUGA_CVM(&quot;PREFREE OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+  celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+*/
+  while (ptr-&gt;next != NULL)
+    ptr = ptr-&gt;next;
+
+  while (ptr) {
+
+    if ((1 == ptr-&gt;valid) &amp;&amp; (0 == ptr-&gt;acknowledged) &amp;&amp; (0 != ptr-&gt;sent)) {
+      MsgTxSeqNo =
+        ((ptr-&gt;busmail_msg_buffer[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_TXSEQ_MASK) &gt;&gt;
+         4);
+      MsgRxSeqNo =
+        ((ptr-&gt;busmail_msg_buffer[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_RXSEQ_MASK));
+
+/*
+    if (option_debug &gt; 1) 
+      DEBUGA_CVM(&quot;OUTGOING LIST TxSeq is %X, RxSeq is %X\n&quot;, CELLIAX_P_LOG, MsgTxSeqNo, MsgRxSeqNo);
+*/
+      unsigned char TxToAck = 0;
+
+      if (0 == AckTxSeqNo) {
+        TxToAck = 7;
+      } else {
+        TxToAck = AckTxSeqNo - 1;
+      }
+
+      if (MsgTxSeqNo &lt;= TxToAck) {
+
+        if (option_debug &gt; 1)
+          DEBUGA_CVM(&quot;Msg with TxSeq=%X ACKed with CvmRxSeq=%X\n&quot;, CELLIAX_P_LOG,
+                     MsgTxSeqNo, AckTxSeqNo);
+
+        old = ptr;
+        old-&gt;acknowledged = 1;
+        old-&gt;valid = 0;
+        ptr = old-&gt;previous;
+
+        if (old-&gt;previous) {
+          if (old-&gt;next) {
+            old-&gt;previous-&gt;next = old-&gt;next;
+          } else {
+            old-&gt;previous-&gt;next = NULL;
+          }
+        }
+
+        if (old-&gt;next) {
+          if (old-&gt;previous) {
+            old-&gt;next-&gt;previous = old-&gt;previous;
+          } else {
+            old-&gt;next-&gt;previous = NULL;
+          }
+        }
+
+        if ((NULL == old-&gt;next) &amp;&amp; (NULL == old-&gt;previous)) {
+          if (option_debug &gt; 1) {
+            DEBUGA_CVM(&quot;FREEING LAST\n&quot;, CELLIAX_P_LOG);
+          }
+
+          p-&gt;cvm_busmail_outgoing_list = NULL;
+          p-&gt;cvm_busmail_outgoing_list = celliax_serial_list_init_CVM_BUSMAIL(p);
+        }
+
+/*
+      if (option_debug &gt; 1)
+        DEBUGA_CVM(&quot;FREEING TxSeq is %X, RxSeq is %X\n&quot;, CELLIAX_P_LOG, MsgTxSeqNo, MsgRxSeqNo);
+*/
+
+        free(old);
+
+      } else {
+        ptr = ptr-&gt;previous;
+      }
+
+    } else {
+      ptr = ptr-&gt;previous;
+    }
+  }
+
+/*
+  DEBUGA_CVM(&quot;POSTFREE OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+  celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+*/
+
+  CVM_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+  return 0;
+}
+
+/*!
+ * \brief Sends an FBUS2 message or resends it if it was not acknowledged
+ * \param p celliax_pvt
+ *
+ * Called by celliax_serial_read_CVM_BUSMAIL, browse the fbus2_outgoing_list looking for FBUS2 messages to be sent, 
+ * or for FBUS2 messages previously sent but not yet acknoledged.
+ * (if an outgoing FBUS2 message is not aknowledged by the cellphone in a while,
+ * it will be retransmitted)
+ *
+ * \return 0 on error, 1 otherwise
+ */
+int celliax_serial_send_if_time_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  struct cvm_busmail_msg *ptr;
+  struct timeval tv;
+  struct timezone tz;
+
+  gettimeofday(&amp;tv, &amp;tz);
+  ptr = p-&gt;cvm_busmail_outgoing_list;
+
+  if (ptr == NULL) {
+/*    ERRORA(&quot;cvm_busmail_outgoing_list is NULL ?\n&quot;, CELLIAX_P_LOG); */
+    WARNINGA(&quot;cvm_busmail_outgoing_list is NULL, nothing to send...\n&quot;, CELLIAX_P_LOG);
+
+/*    return -1; */
+    return 0;
+
+  }
+
+  while (ptr-&gt;next != NULL) {
+    WARNINGA(&quot;cvm_busmail_outgoing_list-&gt;next is not null ?\n&quot;, CELLIAX_P_LOG);
+    ptr = ptr-&gt;next;            //FIXME what to do?
+  }
+
+  while (ptr-&gt;sent == 0 &amp;&amp; ptr-&gt;acknowledged == 0) {
+    if (ptr-&gt;previous != NULL) {
+      ptr = ptr-&gt;previous;
+    } else
+      break;
+  }
+
+  while (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 0) {
+    if (ptr-&gt;previous != NULL) {
+      ptr = ptr-&gt;previous;
+    } else
+      break;
+  }
+
+  if (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 1) {
+    if (ptr-&gt;next != NULL) {
+      ptr = ptr-&gt;next;
+    }
+  }
+
+  if (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;valid == 1) {
+    if ((tv.tv_sec * 1000 + tv.tv_usec / 1000) &gt;
+        ((ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000) + 1000)) {
+
+      PUSHA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+      CVM_LOKKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+
+      if (ptr-&gt;sent == 1 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;valid == 1) {    //retest, maybe has been changed?
+        if ((tv.tv_sec * 1000 + tv.tv_usec / 1000) &gt; ((ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000) + 1000)) { //retest, maybe has been changed?
+
+          if (option_debug &gt; 1)
+            DEBUGA_CVM(&quot;RESEND TxSeq=%X, passed %ld ms, sent %d times\n&quot;, CELLIAX_P_LOG,
+                       ptr-&gt;txseqno,
+                       ((tv.tv_sec * 1000 + tv.tv_usec / 1000) -
+                        (ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000)), ptr-&gt;how_many_sent);
+
+          if (ptr-&gt;how_many_sent &gt; 9) {
+            ERRORA(&quot;RESEND TxSeq=%X, passed %ld ms, sent %d times\n&quot;, CELLIAX_P_LOG,
+                   ptr-&gt;txseqno,
+                   ((tv.tv_sec * 1000 + tv.tv_usec / 1000) -
+                    (ptr-&gt;tv_sec * 1000 + ptr-&gt;tv_usec / 1000)), ptr-&gt;how_many_sent);
+
+            CVM_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+            return -1;
+          }
+
+          celliax_serial_send_CVM_BUSMAIL(p, ptr-&gt;busmail_msg_len,
+                                          ptr-&gt;busmail_msg_buffer);
+
+          ptr-&gt;tv_sec = tv.tv_sec;
+          ptr-&gt;tv_usec = tv.tv_usec;
+          ptr-&gt;sent = 1;
+          ptr-&gt;how_many_sent++;
+/*
+          if (option_debug &gt; 1) {
+            DEBUGA_CVM(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+            celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+            DEBUGA_CVM(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);
+          }
+*/
+        }
+      }
+
+      CVM_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+      POPPA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+    }
+  }
+
+  if (ptr-&gt;sent == 0 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;valid == 1) {
+
+    PUSHA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+    CVM_LOKKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+
+    if (ptr-&gt;sent == 0 &amp;&amp; ptr-&gt;acknowledged == 0 &amp;&amp; ptr-&gt;valid == 1) {  //retest, maybe has been changed?
+
+      if (option_debug &gt; 1)
+        DEBUGA_CVM(&quot;SENDING 1st TIME TxSeq=%X\n&quot;, CELLIAX_P_LOG, ptr-&gt;txseqno);
+
+      celliax_serial_send_CVM_BUSMAIL(p, ptr-&gt;busmail_msg_len, ptr-&gt;busmail_msg_buffer);
+
+      ptr-&gt;tv_sec = tv.tv_sec;
+      ptr-&gt;tv_usec = tv.tv_usec;
+      ptr-&gt;sent = 1;
+      ptr-&gt;how_many_sent++;
+/*
+      if (option_debug &gt; 1) {
+        DEBUGA_CVM(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+        celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+        DEBUGA_CVM(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);        
+      }
+*/
+    }
+
+    CVM_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+    POPPA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+
+  }
+  return 0;
+}
+
+int celliax_serial_write_CVM_BUSMAIL(struct celliax_pvt *p,
+                                     struct cvm_busmail_frame *busmail_frame)
+{
+  unsigned char buffer2[BUSMAIL_MAX_FRAME_LENGTH];
+  int i = 0;
+  int len = 0;
+  unsigned int busmail_len_total = 0;
+
+  busmail_frame-&gt;busmail_sof = BUSMAIL_SOF;
+  busmail_frame-&gt;busmail_crc = 0;
+
+/* because of Rx Tx SEQ HEADER fields problem, update when these fields are filled with correct data
+  busmail_frame-&gt;busmail_crc = (unsigned char)(busmail_frame-&gt;busmail_header + busmail_frame-&gt;busmail_crc);
+*/
+
+  buffer2[BUSMAIL_OFFSET_SOF] = busmail_frame-&gt;busmail_sof;
+  buffer2[BUSMAIL_OFFSET_HEADER] = busmail_frame-&gt;busmail_header;
+
+  if ((buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_IC_BIT_MASK) ==
+      BUSMAIL_HEADER_INFO_FRAME) {
+    len =
+      BUSMAIL_OFFSET_MAIL_PARAMS + busmail_frame-&gt;busmail_mail_params_buffer_len +
+      sizeof(busmail_frame-&gt;busmail_crc);
+    busmail_len_total =
+      busmail_frame-&gt;busmail_mail_params_buffer_len +
+      sizeof(busmail_frame-&gt;busmail_header)
+      + sizeof(busmail_frame-&gt;busmail_mail_program_id) +
+      sizeof(busmail_frame-&gt;busmail_mail_task_id) + 2;
+
+    if (option_debug &gt; 1)
+      DEBUGA_CVM(&quot;INFO frame to send\n&quot;, CELLIAX_P_LOG);
+
+    buffer2[BUSMAIL_OFFSET_MAIL_PROGRAM_ID] = busmail_frame-&gt;busmail_mail_program_id;
+    buffer2[BUSMAIL_OFFSET_MAIL_TASK_ID] = busmail_frame-&gt;busmail_mail_task_id;
+    buffer2[BUSMAIL_OFFSET_MAIL_PRIMITIVE_LSB] =
+      busmail_frame-&gt;busmail_mail_primitive[BUSMAIL_MAIL_PRIMITIVE_LSB];
+    buffer2[BUSMAIL_OFFSET_MAIL_PRIMITIVE_MSB] =
+      busmail_frame-&gt;busmail_mail_primitive[BUSMAIL_MAIL_PRIMITIVE_MSB];
+
+    if (busmail_frame-&gt;busmail_mail_params_buffer_len) {
+      memcpy(buffer2 + BUSMAIL_OFFSET_MAIL_PARAMS,
+             busmail_frame-&gt;busmail_mail_params_buffer,
+             busmail_frame-&gt;busmail_mail_params_buffer_len);
+    }
+
+    for (i = 0; i &lt; busmail_frame-&gt;busmail_mail_params_buffer_len; i++) {
+      busmail_frame-&gt;busmail_crc =
+        (unsigned char) (busmail_frame-&gt;busmail_mail_params_buffer[i] +
+                         busmail_frame-&gt;busmail_crc);
+    }
+
+    busmail_frame-&gt;busmail_crc += busmail_frame-&gt;busmail_mail_program_id;
+    busmail_frame-&gt;busmail_crc += busmail_frame-&gt;busmail_mail_task_id;
+    busmail_frame-&gt;busmail_crc +=
+      busmail_frame-&gt;busmail_mail_primitive[BUSMAIL_MAIL_PRIMITIVE_LSB];
+    busmail_frame-&gt;busmail_crc +=
+      busmail_frame-&gt;busmail_mail_primitive[BUSMAIL_MAIL_PRIMITIVE_MSB];
+  } else {
+    busmail_len_total = sizeof(busmail_frame-&gt;busmail_header);
+    len = BUSMAIL_OFFSET_MAIL + sizeof(busmail_frame-&gt;busmail_crc);
+
+    if (option_debug &gt; 1)
+      DEBUGA_CVM(&quot;CTRL frame to send\n&quot;, CELLIAX_P_LOG);
+  }
+
+/*
+  DEBUGA_CVM(&quot;Its len=%d\n&quot;, CELLIAX_P_LOG, len);      
+*/
+
+  busmail_frame-&gt;busmail_len[BUSMAIL_LEN_LSB] =
+    (unsigned char) (busmail_len_total &amp; 0xFF);
+  busmail_frame-&gt;busmail_len[BUSMAIL_LEN_MSB] = (unsigned char) (busmail_len_total &gt;&gt; 8);
+  buffer2[BUSMAIL_OFFSET_LEN_MSB] = busmail_frame-&gt;busmail_len[BUSMAIL_LEN_MSB];
+  buffer2[BUSMAIL_OFFSET_LEN_LSB] = busmail_frame-&gt;busmail_len[BUSMAIL_LEN_LSB];
+
+/*
+  buffer2[len-1] = busmail_frame-&gt;busmail_crc;
+*/
+  buffer2[len - 1] = 0xFF;
+
+  if ((buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_IC_BIT_MASK) ==
+      BUSMAIL_HEADER_INFO_FRAME) {
+    /* if it is INFO frame, queue it */
+
+    /* update TxSeq and RxSeq bits */
+    /* clear TxSeq and RxSeq bits */
+    buffer2[BUSMAIL_OFFSET_HEADER] &amp;=
+      ~(BUSMAIL_HEADER_RXSEQ_MASK | BUSMAIL_HEADER_TXSEQ_MASK);
+
+    buffer2[BUSMAIL_OFFSET_HEADER] |=
+      (p-&gt;busmail_rxseq_cvm_last + 1) &amp; BUSMAIL_HEADER_RXSEQ_MASK;
+
+    p-&gt;busmail_txseq_celliax_last++;
+    p-&gt;busmail_txseq_celliax_last &amp;= 0x07;
+
+    buffer2[BUSMAIL_OFFSET_HEADER] |=
+      ((p-&gt;busmail_txseq_celliax_last) &lt;&lt; 4) &amp; BUSMAIL_HEADER_TXSEQ_MASK;
+
+    /* update CRC */
+    busmail_frame-&gt;busmail_crc += buffer2[BUSMAIL_OFFSET_HEADER];
+    buffer2[len - 1] = busmail_frame-&gt;busmail_crc;
+
+    p-&gt;cvm_busmail_outgoing_list = celliax_serial_list_init_CVM_BUSMAIL(p);
+    p-&gt;cvm_busmail_outgoing_list-&gt;busmail_msg_len = len;
+
+    for (i = 0; i &lt; len; i++) {
+      p-&gt;cvm_busmail_outgoing_list-&gt;busmail_msg_buffer[i] = buffer2[i];
+    }
+
+    if (option_debug &gt; 10) {
+      char debug_buf[1024];
+      char *debug_buf_pos;
+
+      memset(debug_buf, 0, 1024);
+      debug_buf_pos = debug_buf;
+
+      for (i = 0; i &lt; len; i++) {
+        debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, buffer2[i]);
+        if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+          break;
+      }
+
+      if (option_debug &gt; 1)
+        DEBUGA_CVM(&quot;INFO: %s was prepared to send\n&quot;, CELLIAX_P_LOG, debug_buf);
+    }
+
+    if (option_debug &gt; 1) {
+      DEBUGA_CVM(&quot;OUTGOING INFO Frame TxSeq is %X, RxSeq is %X\n&quot;, CELLIAX_P_LOG,
+                 (buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_TXSEQ_MASK) &gt;&gt; 4,
+                 buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_RXSEQ_MASK);
+/*
+      DEBUGA_CVM(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+      celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+      DEBUGA_CVM(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG); */
+    }
+    p-&gt;cvm_busmail_outgoing_list-&gt;txseqno =
+      (unsigned char) (buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_TXSEQ_MASK) &gt;&gt; 4;
+    p-&gt;cvm_busmail_outgoing_list-&gt;valid = 1;    /* ready to send (?) */
+
+  } else {
+    /* if it is CTRL frame, send it straight to the wire */
+    if (BUSMAIL_HEADER_SABM !=
+        (buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_SABM_MASK)) {
+      /*SABM ctrl frames have no RxSeq bits */
+
+      buffer2[BUSMAIL_OFFSET_HEADER] &amp;= ~BUSMAIL_HEADER_RXSEQ_MASK;
+
+      if (BUSMAIL_HEADER_REJ ==
+          (buffer2[BUSMAIL_OFFSET_HEADER] &amp; BUSMAIL_HEADER_REJ_MASK)) {
+
+        if (option_debug &gt; 1)
+          DEBUGA_CVM(&quot;CTRL REJ frame...\n&quot;, CELLIAX_P_LOG);
+
+        if (0xFF != p-&gt;busmail_rxseq_cvm_last) {
+          buffer2[BUSMAIL_OFFSET_HEADER] |=
+            (p-&gt;busmail_rxseq_cvm_last + 1) &amp; BUSMAIL_HEADER_RXSEQ_MASK;
+        } else {
+          if (option_debug &gt; 1)
+            DEBUGA_CVM
+              (&quot;Skipping sending REJ, because we just cleared RxSeq counter, and probably it was a packet that is invalid now...\n&quot;,
+               CELLIAX_P_LOG);
+          return 0;
+        }
+
+      } else {
+        buffer2[BUSMAIL_OFFSET_HEADER] |=
+          (p-&gt;busmail_rxseq_cvm_last + 1) &amp; BUSMAIL_HEADER_RXSEQ_MASK;
+      }
+    }
+
+    /* update CRC */
+    busmail_frame-&gt;busmail_crc += buffer2[BUSMAIL_OFFSET_HEADER];
+    buffer2[len - 1] = busmail_frame-&gt;busmail_crc;
+
+    if (option_debug &gt; 10) {
+      char debug_buf[1024];
+      char *debug_buf_pos;
+
+      memset(debug_buf, 0, 1024);
+      debug_buf_pos = debug_buf;
+
+      for (i = 0; i &lt; len; i++) {
+        debug_buf_pos += sprintf(debug_buf_pos, &quot;[%.2X] &quot;, buffer2[i]);
+        if (debug_buf_pos &gt; (char *) (&amp;debug_buf + 1000))
+          break;
+      }
+
+      if (option_debug &gt; 1)
+        DEBUGA_CVM(&quot;CTRL: %s was prepared to send\n&quot;, CELLIAX_P_LOG, debug_buf);
+    }
+//    usleep(100);
+    celliax_serial_send_CVM_BUSMAIL(p, len, buffer2);
+  }
+
+  return 0;
+}
+
+int celliax_serial_send_ctrl_frame_CVM_BUSMAIL(struct celliax_pvt *p,
+                                               unsigned char FrameType)
+{
+  /*FrameType parameter is really a busmail header with info neeeded to tell the frame type to send */
+  struct cvm_busmail_frame busmail_frame;
+
+  switch (FrameType &amp; 0xF0) {
+    /* only higher nibble is important for us, do not take PF bit into considration */
+
+  case BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME | BUSMAIL_HEADER_SUID_RR:
+  case BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME | BUSMAIL_HEADER_SUID_REJ:
+  case BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_SU_FRAME | BUSMAIL_HEADER_SUID_RNR:
+  case BUSMAIL_HEADER_CTRL_FRAME | BUSMAIL_HEADER_CTRL_UN_FRAME | BUSMAIL_HEADER_UNID_SABM:
+
+    busmail_frame.busmail_header =
+      (FrameType &amp; 0xF8);
+
+    break;
+
+  default:
+    WARNINGA(&quot;UNKNOWN CTRL TYPE specified, sending nothing!!!\n&quot;, CELLIAX_P_LOG);
+    return -1;
+    break;
+  }
+
+  busmail_frame.busmail_mail_params_buffer_len = 0;
+
+  /* Sending to CVM */
+  return celliax_serial_write_CVM_BUSMAIL(p, &amp;busmail_frame);
+}
+
+int celliax_serial_send_info_frame_CVM_BUSMAIL(struct celliax_pvt *p, int FrameType,
+                                               unsigned char ParamsLen,
+                                               unsigned char *Params)
+{
+  /*FrameType parameter is really a Primitive ID */
+  struct cvm_busmail_frame busmail_frame;
+  int i = 0;
+  busmail_frame.busmail_header =
+    (BUSMAIL_HEADER_PF_BIT_MASK &amp; 0xFF) | BUSMAIL_HEADER_INFO_FRAME;
+
+  busmail_frame.busmail_mail_primitive[BUSMAIL_MAIL_PRIMITIVE_LSB] = FrameType &amp; 0xFF;
+  busmail_frame.busmail_mail_primitive[BUSMAIL_MAIL_PRIMITIVE_MSB] =
+    (FrameType &gt;&gt; 8) &amp; 0xFF;
+
+  busmail_frame.busmail_mail_program_id = BUSMAIL_MAIL_PROGRAM_ID;
+  busmail_frame.busmail_mail_task_id = BUSMAIL_MAIL_TASK_ID;
+
+  for (i = 0; i &lt; ParamsLen; i++) {
+    busmail_frame.busmail_mail_params_buffer[i] = Params[i];
+  }
+
+  busmail_frame.busmail_mail_params_buffer_len = ParamsLen;
+
+  /* Sending to CVM */
+  return celliax_serial_write_CVM_BUSMAIL(p, &amp;busmail_frame);
+}
+
+int celliax_serial_lists_free_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  struct cvm_busmail_msg *ptr, *prev;
+/*
+  if (option_debug &gt; 1) {
+    DEBUGA_CVM(&quot;START FREEING OUTGOING\n&quot;, CELLIAX_P_LOG);
+    DEBUGA_CVM(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+    celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+    DEBUGA_CVM(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);
+  }
+*/
+  ptr = p-&gt;cvm_busmail_outgoing_list;
+
+  if (ptr) {
+    while (ptr-&gt;next != NULL)
+      ptr = ptr-&gt;next;
+
+    while (ptr-&gt;previous != NULL) {
+
+      if (option_debug &gt; 1)
+        DEBUGA_CVM(&quot;FREED \n&quot;, CELLIAX_P_LOG);
+
+      prev = ptr-&gt;previous;
+      free(ptr);
+      ptr = prev;
+    }
+
+    free(ptr);
+  }
+
+  if (option_debug &gt; 1)
+    DEBUGA_CVM(&quot;LAST FREED \n&quot;, CELLIAX_P_LOG);
+
+  p-&gt;cvm_busmail_outgoing_list = NULL;
+  p-&gt;cvm_busmail_outgoing_list = celliax_serial_list_init_CVM_BUSMAIL(p);
+
+  if (option_debug &gt; 1) {
+    DEBUGA_CVM(&quot;OUTGOING list:\n&quot;, CELLIAX_P_LOG);
+    celliax_serial_list_print_CVM_BUSMAIL(p, p-&gt;cvm_busmail_outgoing_list);
+    DEBUGA_CVM(&quot;OUTGOING list END\n&quot;, CELLIAX_P_LOG);
+    DEBUGA_CVM(&quot;STARTING FREE INGOING\n&quot;, CELLIAX_P_LOG);
+  }
+
+  return 0;
+}
+
+struct cvm_busmail_msg *celliax_serial_list_init_CVM_BUSMAIL(struct celliax_pvt *p)
+{
+  struct cvm_busmail_msg *list;
+  list = p-&gt;cvm_busmail_outgoing_list;
+
+  PUSHA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+  CVM_LOKKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+
+  if (list == NULL) {
+    list = malloc(sizeof(*(list)));
+    list-&gt;valid = 0;
+    list-&gt;busmail_msg_len = 0;
+    list-&gt;acknowledged = 0;
+    list-&gt;how_many_sent = 0;
+    list-&gt;sent = 0;
+    list-&gt;tv_sec = 0;
+    list-&gt;tv_usec = 0;
+    list-&gt;next = NULL;
+    list-&gt;previous = NULL;
+  }
+
+  if (list-&gt;valid != 0) {
+    struct cvm_busmail_msg *new;
+    new = malloc(sizeof(*new));
+    new-&gt;valid = 0;
+    new-&gt;busmail_msg_len = 0;
+    new-&gt;acknowledged = 0;
+    new-&gt;how_many_sent = 0;
+    new-&gt;sent = 0;
+    new-&gt;tv_sec = 0;
+    new-&gt;tv_usec = 0;
+    new-&gt;next = NULL;
+    new-&gt;previous = list;
+    list-&gt;next = new;
+    list = new;
+  }
+
+  CVM_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+  POPPA_UNLOCKA(&amp;p-&gt;cvm_busmail_outgoing_list_lock);
+
+  return list;
+}
+
+int celliax_serial_list_print_CVM_BUSMAIL(struct celliax_pvt *p,
+                                          struct cvm_busmail_msg *list)
+{
+  struct cvm_busmail_msg *ptr;
+  ptr = list;
+
+  if (ptr) {
+    while (ptr-&gt;next != NULL)
+      ptr = ptr-&gt;next;
+
+    while (ptr) {
+
+      if (option_debug &gt; 3)
+        DEBUGA_CVM
+          (&quot;PTR msg is: %d, seqnum is %.2X, tv_sec is %d, tv_usec is %d, acknowledged is: %d,&quot;
+           &quot; sent is:%d, how_many_sent is: %d\n&quot;, CELLIAX_P_LOG, ptr-&gt;valid,
+           /*ptr-&gt;seqnum */ 44,
+           ptr-&gt;tv_sec, ptr-&gt;tv_usec, ptr-&gt;acknowledged, ptr-&gt;sent, ptr-&gt;how_many_sent);
+
+      ptr = ptr-&gt;previous;
+    }
+  }
+
+  return 0;
+}
+
+#endif /* CELLIAX_CVM */
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_libcsvc"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.c (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.c                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.c        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,356 @@
</span><ins>+/*
+libcsv - parse and write csv data
+Copyright (C) 2007  Robert Gamble
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#if ___STDC_VERSION__ &gt;= 199901L
+#  include &lt;stdint.h&gt;
+#else
+#  define SIZE_MAX ((size_t)-1) /* C89 doesn't have stdint.h or SIZE_MAX */
+#endif
+
+#include &quot;celliax_libcsv.h&quot;
+
+#define VERSION &quot;1.0.0&quot;
+
+#define ROW_NOT_BEGUN           0
+#define FIELD_NOT_BEGUN         1
+#define FIELD_BEGUN             2
+#define FIELD_MIGHT_HAVE_ENDED  3
+
+/*
+  Explanation of states
+  ROW_NOT_BEGUN    There have not been any fields encountered for this row
+  FIELD_NOT_BEGUN  There have been fields but we are currently not in one
+  FIELD_BEGUN      We are in a field
+  FIELD_MIGHT_HAVE_ENDED
+                   We encountered a double quote inside a quoted field, the
+                   field is either ended or the quote is literal
+*/
+
+#define MEM_BLK_SIZE 128
+
+#define SUBMIT_FIELD(p) \
+  do { \
+   if (!(p)-&gt;quoted) \
+     (p)-&gt;entry_pos -= (p)-&gt;spaces; \
+   if (cb1) \
+     cb1(p-&gt;entry_buf, (p)-&gt;entry_pos, data); \
+   (p)-&gt;pstate = FIELD_NOT_BEGUN; \
+   (p)-&gt;entry_pos = (p)-&gt;quoted = (p)-&gt;spaces = 0; \
+ } while (0)
+
+#define SUBMIT_ROW(p, c) \
+  do { \
+    if (cb2) \
+      cb2(c, data); \
+    (p)-&gt;pstate = ROW_NOT_BEGUN; \
+    (p)-&gt;entry_pos = (p)-&gt;quoted = (p)-&gt;spaces = 0; \
+  } while (0)
+
+#define SUBMIT_CHAR(p, c) ((p)-&gt;entry_buf[(p)-&gt;entry_pos++] = (c))
+
+static char *csv_errors[] = {&quot;success&quot;,
+                             &quot;error parsing data while strict checking enabled&quot;,
+                             &quot;memory exhausted while increasing buffer size&quot;,
+                             &quot;data size too large&quot;,
+                             &quot;invalid status code&quot;};
+
+int
+csv_error(struct csv_parser *p)
+{
+  return p-&gt;status;
+}
+
+char *
+csv_strerror(int status)
+{
+  if (status &gt;= CSV_EINVALID || status &lt; 0)
+    return csv_errors[CSV_EINVALID];
+  else
+    return csv_errors[status];
+}
+
+int
+csv_opts(struct csv_parser *p, unsigned char options)
+{
+  if (p == NULL)
+    return -1;
+
+  p-&gt;options = options;
+  return 0;
+}
+
+int
+csv_init(struct csv_parser **p, unsigned char options)
+{
+  /* Initialize a csv_parser object returns 0 on success, -1 on error */
+  if (p == NULL)
+    return -1;
+
+  if ((*p = malloc(sizeof(struct csv_parser))) == NULL)
+    return -1;
+
+  if ( ((*p)-&gt;entry_buf = malloc(MEM_BLK_SIZE)) == NULL ) {
+    free(*p);
+    return -1;
+  }
+  (*p)-&gt;pstate = ROW_NOT_BEGUN;
+  (*p)-&gt;quoted = 0;
+  (*p)-&gt;spaces = 0;
+  (*p)-&gt;entry_pos = 0;
+  (*p)-&gt;entry_size = MEM_BLK_SIZE;
+  (*p)-&gt;status = 0;
+  (*p)-&gt;options = options;
+
+  return 0;
+}
+
+void
+csv_free(struct csv_parser *p)
+{
+  /* Free the entry_buffer and the csv_parser object */
+  if (p == NULL)
+    return;
+
+  if (p-&gt;entry_buf)
+    free(p-&gt;entry_buf);
+
+  free(p);
+  return;
+}
+
+int
+csv_fini(struct csv_parser *p, void (*cb1)(char *, size_t, void *), void (*cb2)(char c, void *), void *data)
+{
+  /* Finalize parsing.  Needed, for example, when file does not end in a newline */
+  if (p == NULL)
+    return -1;
+
+  switch (p-&gt;pstate) {
+    case FIELD_MIGHT_HAVE_ENDED:
+      p-&gt;entry_pos -= p-&gt;spaces + 1;  /* get rid of spaces and original quote */
+    case FIELD_NOT_BEGUN:
+    case FIELD_BEGUN:
+      SUBMIT_FIELD(p);
+      SUBMIT_ROW(p, 0);
+    case ROW_NOT_BEGUN: /* Already ended properly */
+      ;
+  }
+
+  p-&gt;spaces = p-&gt;quoted = p-&gt;entry_pos = p-&gt;status = 0;
+  p-&gt;pstate = ROW_NOT_BEGUN;
+
+  return 0;
+}
+  
+size_t
+csv_parse(struct csv_parser *p, const char *s, size_t len, void (*cb1)(char *, size_t, void *), void (*cb2)(char c, void *), void *data)
+{
+  char c;  /* The character we are currently processing */
+  size_t pos = 0;  /* The number of characters we have processed in this call */
+
+  while (pos &lt; len) {
+    /* Check memory usage */
+    if (p-&gt;entry_pos == p-&gt;entry_size) {
+      size_t to_add = MEM_BLK_SIZE;
+      void *vp;
+      while ( p-&gt;entry_size &gt;= SIZE_MAX - to_add )
+        to_add /= 2;
+      if (!to_add) {
+        p-&gt;status = CSV_ETOOBIG;
+        return pos;
+      }
+      while ((vp = realloc(p-&gt;entry_buf, p-&gt;entry_size + to_add)) == NULL) {
+        to_add /= 2;
+        if (!to_add) {
+          p-&gt;status = CSV_ENOMEM;
+          return pos;
+        }
+      }
+      p-&gt;entry_buf = vp;
+      p-&gt;entry_size += to_add;
+    }
+
+    c = s[pos++];
+    switch (p-&gt;pstate) {
+      case ROW_NOT_BEGUN:
+      case FIELD_NOT_BEGUN:
+        if (c == CSV_SPACE || c == CSV_TAB) { /* Space or Tab */
+          continue;
+        } else if (c == CSV_CR || c == CSV_LF) { /* Carriage Return or Line Feed */
+          if (p-&gt;pstate == FIELD_NOT_BEGUN) {
+            SUBMIT_FIELD(p);
+            SUBMIT_ROW(p, c); 
+          } else {  /* ROW_NOT_BEGUN */
+            /* Don't submit empty rows by default */
+            if (p-&gt;options &amp; CSV_REPALL_NL) {
+              SUBMIT_ROW(p, c);
+            }
+          }
+          continue;
+        } else if ( (!(p-&gt;options &amp; CSV_USE_SEMICOLON_SEPARATOR) &amp;&amp; (c == CSV_COMMA)) || ((p-&gt;options &amp; CSV_USE_SEMICOLON_SEPARATOR) &amp;&amp; (c == CSV_SEMICOLON)) ) { /* Comma or SemiColon */
+          SUBMIT_FIELD(p);
+          break;
+        } else if (c == CSV_QUOTE) { /* Quote */
+          p-&gt;pstate = FIELD_BEGUN;
+          p-&gt;quoted = 1;
+        } else {               /* Anything else */
+          p-&gt;pstate = FIELD_BEGUN;
+          p-&gt;quoted = 0;
+          SUBMIT_CHAR(p, c);
+        }
+        break;
+      case FIELD_BEGUN:
+        if (c == CSV_QUOTE) {         /* Quote */
+          if (p-&gt;quoted) {
+            SUBMIT_CHAR(p, c);
+            p-&gt;pstate = FIELD_MIGHT_HAVE_ENDED;
+          } else {
+            /* STRICT ERROR - double quote inside non-quoted field */
+            if (p-&gt;options &amp; CSV_STRICT) {
+              p-&gt;status = CSV_EPARSE;
+              return pos-1;
+            }
+            SUBMIT_CHAR(p, c);
+            p-&gt;spaces = 0;
+          }
+        } else if ((!(p-&gt;options &amp; CSV_USE_SEMICOLON_SEPARATOR) &amp;&amp; (c == CSV_COMMA)) || ((p-&gt;options &amp; CSV_USE_SEMICOLON_SEPARATOR) &amp;&amp; (c == CSV_SEMICOLON))) {  /* Comma or SemiColon */
+          if (p-&gt;quoted) {
+            SUBMIT_CHAR(p, c);
+          } else {
+            SUBMIT_FIELD(p);
+          }
+        } else if (c == CSV_CR || c == CSV_LF) {  /* Carriage Return or Line Feed */
+          if (!p-&gt;quoted) {
+            SUBMIT_FIELD(p);
+            SUBMIT_ROW(p, c);
+          } else {
+            SUBMIT_CHAR(p, c);
+          }
+        } else if (!p-&gt;quoted &amp;&amp; (c == CSV_SPACE || c == CSV_TAB)) { /* Tab or space for non-quoted field */
+            SUBMIT_CHAR(p, c);
+            p-&gt;spaces++;
+        } else {  /* Anything else */
+          SUBMIT_CHAR(p, c);
+          p-&gt;spaces = 0;
+        }
+        break;
+      case FIELD_MIGHT_HAVE_ENDED:
+        /* This only happens when a quote character is encountered in a quoted field */
+        if ((!(p-&gt;options &amp; CSV_USE_SEMICOLON_SEPARATOR) &amp;&amp; (c == CSV_COMMA)) || ((p-&gt;options &amp; CSV_USE_SEMICOLON_SEPARATOR) &amp;&amp; (c == CSV_SEMICOLON))) {  /* Comma or SemiColon */
+          p-&gt;entry_pos -= p-&gt;spaces + 1;  /* get rid of spaces and original quote */
+          SUBMIT_FIELD(p);
+        } else if (c == CSV_CR || c == CSV_LF) {  /* Carriage Return or Line Feed */
+          p-&gt;entry_pos -= p-&gt;spaces + 1;  /* get rid of spaces and original quote */
+          SUBMIT_FIELD(p);
+          SUBMIT_ROW(p, c);
+        } else if (c == CSV_SPACE || c == CSV_TAB) {  /* Space or Tab */
+          SUBMIT_CHAR(p, c);
+          p-&gt;spaces++;
+        } else if (c == CSV_QUOTE) {  /* Quote */
+          if (p-&gt;spaces) {
+            /* STRICT ERROR - unescaped double quote */
+            if (p-&gt;options &amp; CSV_STRICT) {
+              p-&gt;status = CSV_EPARSE;
+              return pos-1;
+            }
+            p-&gt;spaces = 0;
+            SUBMIT_CHAR(p, c);
+          } else {
+            /* Two quotes in a row */
+            p-&gt;pstate = FIELD_BEGUN;
+          }
+        } else {  /* Anything else */
+          /* STRICT ERROR - unescaped double quote */
+          if (p-&gt;options &amp; CSV_STRICT) {
+            p-&gt;status = CSV_EPARSE;
+            return pos-1;
+          }
+          p-&gt;pstate = FIELD_BEGUN;
+          p-&gt;spaces = 0;
+          SUBMIT_CHAR(p, c);
+        }
+        break;
+     default:
+       break;
+    }
+  }
+  return pos;
+}
+
+size_t
+csv_write (char *dest, size_t dest_size, const char *src, size_t src_size)
+{
+  size_t chars = 0;
+
+  if (src == NULL)
+    return 0;
+
+  if (dest == NULL)
+    dest_size = 0;
+
+  if (dest_size &gt; 0)
+    *dest++ = '&quot;';
+  chars++;
+
+  while (src_size) {
+    if (*src == '&quot;') {
+      if (dest_size &gt; chars)
+        *dest++ = '&quot;';
+      if (chars &lt; SIZE_MAX) chars++;
+    }
+    if (dest_size &gt; chars)
+      *dest++ = *src;
+    if (chars &lt; SIZE_MAX) chars++;
+    src_size--;
+    src++;
+  }
+
+  if (dest_size &gt; chars)
+    *dest = '&quot;';
+  if (chars &lt; SIZE_MAX) chars++;
+
+  return chars;
+}
+
+int
+csv_fwrite (FILE *fp, const char *src, size_t src_size)
+{
+  if (fp == NULL || src == NULL)
+    return 0;
+
+  if (fputc('&quot;', fp) == EOF)
+    return EOF;
+
+  while (src_size) {
+    if (*src == '&quot;') {
+      if (fputc('&quot;', fp) == EOF)
+        return EOF;
+    }
+    if (fputc(*src, fp) == EOF)
+      return EOF;
+    src_size--;
+    src++;
+  }
+
+  if (fputc('&quot;', fp) == EOF) {
+    return EOF;
+  }
+
+  return 0;
+}
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_libcsvh"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.h (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.h                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_libcsv.h        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+#ifndef LIBCSV_H__
+#define LIBCSV_H__
+#include &lt;stdlib.h&gt;
+#include &lt;stdio.h&gt;
+
+/* Error Codes */
+#define CSV_SUCCESS 0
+#define CSV_EPARSE 1   /* Parse error in strict mode */
+#define CSV_ENOMEM 2   /* Out of memory while increasing buffer size */
+#define CSV_ETOOBIG 3  /* Buffer larger than SIZE_MAX needed */
+#define CSV_EINVALID 4 /* Invalid code, should never receive this from csv_error */
+
+/* parser options */
+#define CSV_STRICT 1    /* enable strict mode */
+#define CSV_REPALL_NL 2 /* report all unquoted carriage returns and linefeeds */
+#define CSV_USE_SEMICOLON_SEPARATOR 4 /* use CSV_SEMICOLON as separator instead of CSV_COMMA */
+
+/* Character values */
+#define CSV_TAB    0x09
+#define CSV_SPACE  0x20
+#define CSV_CR     0x0d
+#define CSV_LF     0x0a
+#define CSV_COMMA  0x2c
+#define CSV_SEMICOLON  0x3b /* ; */
+#define CSV_QUOTE  0x22
+
+struct csv_parser {
+  int pstate;         /* Parser state */
+  int quoted;         /* Is the current field a quoted field? */
+  size_t spaces;      /* Number of continious spaces after quote or in a non-quoted field */
+  char * entry_buf;   /* Entry buffer */
+  size_t entry_pos;   /* Current position in entry_buf (and current size of entry) */
+  size_t entry_size;  /* Size of buffer */
+  int status;         /* Operation status */
+  unsigned char options;
+};
+
+int csv_init(struct csv_parser **p, unsigned char options);
+int csv_fini(struct csv_parser *p, void (*cb1)(char *, size_t, void *), void (*cb2)(char, void *), void *data);
+void csv_free(struct csv_parser *p);
+int csv_error(struct csv_parser *p);
+char * csv_strerror(int error);
+size_t csv_parse(struct csv_parser *p, const char *s, size_t len, void (*cb1)(char *, size_t, void *), void (*cb2)(char, void *), void *data);
+size_t csv_write(char *dest, size_t dest_size, const char *src, size_t src_size);
+int csv_fwrite(FILE *fp, const char *src, size_t src_size);
+int csv_opts(struct csv_parser *p, unsigned char options);
+
+#endif
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_spandspc"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.c (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.c                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.c        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,1059 @@
</span><ins>+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * echo.c - An echo cancellor, suitable for electrical and acoustic
+ *          cancellation. This code does not currently comply with
+ *          any relevant standards (e.g. G.164/5/7/8). One day....
+ *
+ * Written by Steve Underwood &lt;steveu@coppice.org&gt;
+ *
+ * Copyright (C) 2001, 2003 Steve Underwood
+ *
+ * Based on a bit from here, a bit from there, eye of toad,
+ * ear of bat, etc - plus, of course, my own 2 cents.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: echo.c,v 1.20 2006/12/01 18:00:48 steveu Exp $
+ */
+
+/*! \file */
+
+/* TODO:
+   Finish the echo suppressor option, however nasty suppression may be.
+   Add an option to reintroduce side tone at -24dB under appropriate conditions.
+   Improve double talk detector (iterative!)
+*/
+
+/* We need to differentiate between transmitted energy which will train the echo
+   canceller well (voice, white noise, and other broadband sources) and energy
+   which will train it badly (supervisory tones, DTMF, whistles, and other
+   narrowband sources). There are many ways this might be done. This canceller uses
+   a method based on the autocorrelation qualities of the transmitted signal. A rather
+   peaky autocorrelation function is a clear sign of a narrowband signal. We only need
+   perform the autocorrelation at well spaced intervals, so the compute load is not too
+   great. Multiple successive autocorrelation functions with a similar peaky shape are a
+   clear indication of a stationary narrowband signal. Using TKEO, it should be possible to
+   greatly reduce the compute requirement for narrowband detection. */
+
+/* The FIR taps must be adapted as 32 bit values, to get the necessary finesse
+   in the adaption process. However, they are applied as 16 bit values (bits 30-15
+   of the 32 bit values) in the FIR. For the working 16 bit values, we need 4 sets.
+   
+   3 of the 16 bit sets are used on a rotating basis. Normally the canceller steps
+   round these 3 sets at regular intervals. Any time we detect double talk, we can go
+   back to the set from two steps ago with reasonable assurance it is a well adapted
+   set. We cannot just go back one step, as we may have rotated the sets just before
+   double talk or tone was detected, and that set may already be somewhat corrupted.
+   
+   When narrowband energy is detected we need to continue adapting to it, to echo
+   cancel it. However, the adaption will almost certainly be going astray. Broadband
+   (or even complex sequences of narrowband) energy will normally lead to a well
+   trained cancellor, with taps matching the impulse response of the channel.
+   For stationary narrowband energy, there is usually has an infinite number of
+   alternative tap sets which will cancel it well. A previously well trained set of
+   taps will tend to drift amongst the alternatives. When broadband energy resumes, the
+   taps may be a total mismatch for the signal, and could even amplify rather than
+   attenuate the echo. The solution is to use a fourth set of 16 bit taps. When we first
+   detect the narrowband energy we save the oldest of the group of three sets, but do
+   not change back to an older set. We let the canceller cancel, and it adaption drift
+   while the narrowband energy is present. When we detect the narrowband energy has ceased,
+   we switch to using the fourth set of taps which was saved.
+
+   When we revert to an older set of taps, we must replace both the 16 bit and 32 bit
+   working tap sets. The saved 16 bit values are good enough to also be used as a replacement
+   for the 32 bit values. We loose the fractions, but they should soon settle down in a
+   reasonable way. */
+
+#ifdef HAVE_CONFIG_H
+#include &lt;config.h&gt;
+#endif
+
+#include &lt;stdlib.h&gt;
+#include &lt;inttypes.h&gt;
+#include &lt;string.h&gt;
+#include &lt;stdio.h&gt;
+
+#include &quot;celliax_spandsp.h&quot;
+
+//#include &quot;spandsp/telephony.h&quot;
+//#include &quot;spandsp/logging.h&quot;
+//#include &quot;spandsp/bit_operations.h&quot;
+//#include &quot;spandsp/echo.h&quot;
+
+//#include &quot;bit_operations.h&quot;
+//#include &quot;giova.h&quot;
+
+#if !defined(NULL)
+#define NULL (void *) 0
+#endif
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+#if 0
+#define MIN_TX_POWER_FOR_ADAPTION   64*64
+#define MIN_RX_POWER_FOR_ADAPTION   64*64
+
+static int narrowband_detect(echo_can_state_t * ec)
+{
+  int k;
+  int i;
+  float temp;
+  float scale;
+  float sf[128];
+  float f_acf[128];
+  int32_t acf[28];
+  int score;
+  int len = 32;
+  int alen = 9;
+
+  k = ec-&gt;curr_pos;
+  for (i = 0; i &lt; len; i++) {
+    sf[i] = ec-&gt;fir_state.history[k++];
+    if (k &gt;= 256)
+      k = 0;
+  }
+  for (k = 0; k &lt; alen; k++) {
+    temp = 0;
+    for (i = k; i &lt; len; i++)
+      temp += sf[i] * sf[i - k];
+    f_acf[k] = temp;
+  }
+  scale = 0x1FFFFFFF / f_acf[0];
+  for (k = 0; k &lt; alen; k++)
+    acf[k] = (int32_t) (f_acf[k] * scale);
+  score = 0;
+  for (i = 0; i &lt; 9; i++) {
+    if (ec-&gt;last_acf[i] &gt;= 0 &amp;&amp; acf[i] &gt;= 0) {
+      if ((ec-&gt;last_acf[i] &gt;&gt; 1) &lt; acf[i] &amp;&amp; acf[i] &lt; (ec-&gt;last_acf[i] &lt;&lt; 1))
+        score++;
+    } else if (ec-&gt;last_acf[i] &lt; 0 &amp;&amp; acf[i] &lt; 0) {
+      if ((ec-&gt;last_acf[i] &gt;&gt; 1) &gt; acf[i] &amp;&amp; acf[i] &gt; (ec-&gt;last_acf[i] &lt;&lt; 1))
+        score++;
+    }
+  }
+  memcpy(ec-&gt;last_acf, acf, alen * sizeof(ec-&gt;last_acf[0]));
+  return score;
+}
+
+static __inline__ void lms_adapt(echo_can_state_t * ec, int factor)
+{
+  int i;
+
+#if 0
+  mmx_t *mmx_taps;
+  mmx_t *mmx_coeffs;
+  mmx_t *mmx_hist;
+  mmx_t mmx;
+
+  mmx.w[0] = mmx.w[1] = mmx.w[2] = mmx.w[3] = factor;
+  mmx_hist = (mmx_t *) &amp; fir-&gt;history[fir-&gt;curr_pos];
+  mmx_taps = (mmx_t *) &amp; fir-&gt;taps;
+  mmx_coeffs = (mmx_t *) fir-&gt;coeffs;
+  i = fir-&gt;taps;
+  movq_m2r(mmx, mm0);
+  while (i &gt; 0) {
+    movq_m2r(mmx_hist[0], mm1);
+    movq_m2r(mmx_taps[0], mm0);
+    movq_m2r(mmx_taps[1], mm1);
+    movq_r2r(mm1, mm2);
+    pmulhw(mm0, mm1);
+    pmullw(mm0, mm2);
+
+    pmaddwd_r2r(mm1, mm0);
+    pmaddwd_r2r(mm3, mm2);
+    paddd_r2r(mm0, mm4);
+    paddd_r2r(mm2, mm4);
+    movq_r2m(mm0, mmx_taps[0]);
+    movq_r2m(mm1, mmx_taps[0]);
+    movq_r2m(mm2, mmx_coeffs[0]);
+    mmx_taps += 2;
+    mmx_coeffs += 1;
+    mmx_hist += 1;
+    i -= 4;
+    )
+      emms();
+#elif 0
+  /* Update the FIR taps */
+  for (i = ec-&gt;taps - 1; i &gt;= 0; i--) {
+    /* Leak to avoid the coefficients drifting beyond the ability of the
+       adaption process to bring them back under control. */
+    ec-&gt;fir_taps32[i] -= (ec-&gt;fir_taps32[i] &gt;&gt; 23);
+    ec-&gt;fir_taps32[i] += (ec-&gt;fir_state.history[i + ec-&gt;curr_pos] * factor);
+    ec-&gt;latest_correction = (ec-&gt;fir_state.history[i + ec-&gt;curr_pos] * factor);
+    ec-&gt;fir_taps16[ec-&gt;tap_set][i] = ec-&gt;fir_taps32[i] &gt;&gt; 15;
+  }
+#else
+  int offset1;
+  int offset2;
+
+  /* Update the FIR taps */
+  offset2 = ec-&gt;curr_pos;
+  offset1 = ec-&gt;taps - offset2;
+  for (i = ec-&gt;taps - 1; i &gt;= offset1; i--) {
+    ec-&gt;fir_taps32[i] += (ec-&gt;fir_state.history[i - offset1] * factor);
+    ec-&gt;fir_taps16[ec-&gt;tap_set][i] = (int16_t) (ec-&gt;fir_taps32[i] &gt;&gt; 15);
+  }
+  for (; i &gt;= 0; i--) {
+    ec-&gt;fir_taps32[i] += (ec-&gt;fir_state.history[i + offset2] * factor);
+    ec-&gt;fir_taps16[ec-&gt;tap_set][i] = (int16_t) (ec-&gt;fir_taps32[i] &gt;&gt; 15);
+  }
+#endif
+}
+
+/*- End of function --------------------------------------------------------*/
+
+#ifdef NOT_NEEDED
+echo_can_state_t *echo_can_create(int len, int adaption_mode)
+{
+  echo_can_state_t *ec;
+  int i;
+  int j;
+
+  ec = (echo_can_state_t *) malloc(sizeof(*ec));
+  if (ec == NULL)
+    return NULL;
+  memset(ec, 0, sizeof(*ec));
+  ec-&gt;taps = len;
+  ec-&gt;curr_pos = ec-&gt;taps - 1;
+  ec-&gt;tap_mask = ec-&gt;taps - 1;
+  if ((ec-&gt;fir_taps32 = (int32_t *) malloc(ec-&gt;taps * sizeof(int32_t))) == NULL) {
+    free(ec);
+    return NULL;
+  }
+  memset(ec-&gt;fir_taps32, 0, ec-&gt;taps * sizeof(int32_t));
+  for (i = 0; i &lt; 4; i++) {
+    if ((ec-&gt;fir_taps16[i] = (int16_t *) malloc(ec-&gt;taps * sizeof(int16_t))) == NULL) {
+      for (j = 0; j &lt; i; j++)
+        free(ec-&gt;fir_taps16[j]);
+      free(ec-&gt;fir_taps32);
+      free(ec);
+      return NULL;
+    }
+    memset(ec-&gt;fir_taps16[i], 0, ec-&gt;taps * sizeof(int16_t));
+  }
+  fir16_create(&amp;ec-&gt;fir_state, ec-&gt;fir_taps16[0], ec-&gt;taps);
+  ec-&gt;rx_power_threshold = 10000000;
+  ec-&gt;geigel_max = 0;
+  ec-&gt;geigel_lag = 0;
+  ec-&gt;dtd_onset = FALSE;
+  ec-&gt;tap_set = 0;
+  ec-&gt;tap_rotate_counter = 1600;
+  ec-&gt;cng_level = 1000;
+  echo_can_adaption_mode(ec, adaption_mode);
+  return ec;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+void echo_can_free(echo_can_state_t * ec)
+{
+  int i;
+
+  fir16_free(&amp;ec-&gt;fir_state);
+  free(ec-&gt;fir_taps32);
+  for (i = 0; i &lt; 4; i++)
+    free(ec-&gt;fir_taps16[i]);
+  free(ec);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+void echo_can_adaption_mode(echo_can_state_t * ec, int adaption_mode)
+{
+  ec-&gt;adaption_mode = adaption_mode;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+void echo_can_flush(echo_can_state_t * ec)
+{
+  int i;
+
+  for (i = 0; i &lt; 4; i++)
+    ec-&gt;tx_power[i] = 0;
+  for (i = 0; i &lt; 3; i++)
+    ec-&gt;rx_power[i] = 0;
+  ec-&gt;clean_rx_power = 0;
+  ec-&gt;nonupdate_dwell = 0;
+
+  fir16_flush(&amp;ec-&gt;fir_state);
+  ec-&gt;fir_state.curr_pos = ec-&gt;taps - 1;
+  memset(ec-&gt;fir_taps32, 0, ec-&gt;taps * sizeof(int32_t));
+  for (i = 0; i &lt; 4; i++)
+    memset(ec-&gt;fir_taps16[i], 0, ec-&gt;taps * sizeof(int16_t));
+
+  ec-&gt;curr_pos = ec-&gt;taps - 1;
+
+  ec-&gt;supp_test1 = 0;
+  ec-&gt;supp_test2 = 0;
+  ec-&gt;supp1 = 0;
+  ec-&gt;supp2 = 0;
+  ec-&gt;vad = 0;
+  ec-&gt;cng_level = 1000;
+  ec-&gt;cng_filter = 0;
+
+  ec-&gt;geigel_max = 0;
+  ec-&gt;geigel_lag = 0;
+  ec-&gt;dtd_onset = FALSE;
+  ec-&gt;tap_set = 0;
+  ec-&gt;tap_rotate_counter = 1600;
+
+  ec-&gt;latest_correction = 0;
+
+  memset(ec-&gt;last_acf, 0, sizeof(ec-&gt;last_acf));
+  ec-&gt;narrowband_count = 0;
+  ec-&gt;narrowband_score = 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int sample_no = 0;
+
+int16_t echo_can_update(echo_can_state_t * ec, int16_t tx, int16_t rx)
+{
+  int32_t echo_value;
+  int clean_rx;
+  int nsuppr;
+  int score;
+  int i;
+
+  sample_no++;
+  ec-&gt;latest_correction = 0;
+  /* Evaluate the echo - i.e. apply the FIR filter */
+  /* Assume the gain of the FIR does not exceed unity. Exceeding unity
+     would seem like a rather poor thing for an echo cancellor to do :)
+     This means we can compute the result with a total disregard for
+     overflows. 16bits x 16bits -&gt; 31bits, so no overflow can occur in
+     any multiply. While accumulating we may overflow and underflow the
+     32 bit scale often. However, if the gain does not exceed unity,
+     everything should work itself out, and the final result will be
+     OK, without any saturation logic. */
+  /* Overflow is very much possible here, and we do nothing about it because
+     of the compute costs */
+  /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound
+     bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems
+     best */
+  echo_value = fir16(&amp;ec-&gt;fir_state, tx);
+
+  /* And the answer is..... */
+  clean_rx = rx - echo_value;
+//printf(&quot;echo is %&quot; PRId32 &quot;\n&quot;, echo_value);
+  /* That was the easy part. Now we need to adapt! */
+  if (ec-&gt;nonupdate_dwell &gt; 0)
+    ec-&gt;nonupdate_dwell--;
+
+  /* Calculate short term power levels using very simple single pole IIRs */
+  /* TODO: Is the nasty modulus approach the fastest, or would a real
+     tx*tx power calculation actually be faster? Using the squares
+     makes the numbers grow a lot! */
+  ec-&gt;tx_power[3] += ((abs(tx) - ec-&gt;tx_power[3]) &gt;&gt; 5);
+  ec-&gt;tx_power[2] += ((tx * tx - ec-&gt;tx_power[2]) &gt;&gt; 8);
+  ec-&gt;tx_power[1] += ((tx * tx - ec-&gt;tx_power[1]) &gt;&gt; 5);
+  ec-&gt;tx_power[0] += ((tx * tx - ec-&gt;tx_power[0]) &gt;&gt; 3);
+  ec-&gt;rx_power[1] += ((rx * rx - ec-&gt;rx_power[1]) &gt;&gt; 6);
+  ec-&gt;rx_power[0] += ((rx * rx - ec-&gt;rx_power[0]) &gt;&gt; 3);
+  ec-&gt;clean_rx_power += ((clean_rx * clean_rx - ec-&gt;clean_rx_power) &gt;&gt; 6);
+
+  score = 0;
+  /* If there is very little being transmitted, any attempt to train is
+     futile. We would either be training on the far end's noise or signal,
+     the channel's own noise, or our noise. Either way, this is hardly good
+     training, so don't do it (avoid trouble). */
+  if (ec-&gt;tx_power[0] &gt; MIN_TX_POWER_FOR_ADAPTION) {
+    /* If the received power is very low, either we are sending very little or
+       we are already well adapted. There is little point in trying to improve
+       the adaption under these circumstances, so don't do it (reduce the
+       compute load). */
+    if (ec-&gt;tx_power[1] &gt; ec-&gt;rx_power[0]) {
+      /* There is no (or little) far-end speech. */
+      if (ec-&gt;nonupdate_dwell == 0) {
+        if (++ec-&gt;narrowband_count &gt;= 160) {
+          ec-&gt;narrowband_count = 0;
+          score = narrowband_detect(ec);
+//printf(&quot;Do the narrowband test %d at %d\n&quot;, score, ec-&gt;curr_pos);
+          if (score &gt; 6) {
+            if (ec-&gt;narrowband_score == 0)
+              memcpy(ec-&gt;fir_taps16[3], ec-&gt;fir_taps16[(ec-&gt;tap_set + 1) % 3],
+                     ec-&gt;taps * sizeof(int16_t));
+            ec-&gt;narrowband_score += score;
+          } else {
+            if (ec-&gt;narrowband_score &gt; 200) {
+//printf(&quot;Revert to %d at %d\n&quot;, (ec-&gt;tap_set + 1)%3, sample_no);
+              memcpy(ec-&gt;fir_taps16[ec-&gt;tap_set], ec-&gt;fir_taps16[3],
+                     ec-&gt;taps * sizeof(int16_t));
+              memcpy(ec-&gt;fir_taps16[(ec-&gt;tap_set - 1) % 3], ec-&gt;fir_taps16[3],
+                     ec-&gt;taps * sizeof(int16_t));
+              for (i = 0; i &lt; ec-&gt;taps; i++)
+                ec-&gt;fir_taps32[i] = ec-&gt;fir_taps16[3][i] &lt;&lt; 15;
+              ec-&gt;tap_rotate_counter = 1600;
+            }
+            ec-&gt;narrowband_score = 0;
+          }
+        }
+        ec-&gt;dtd_onset = FALSE;
+        if (--ec-&gt;tap_rotate_counter &lt;= 0) {
+//printf(&quot;Rotate to %d at %d\n&quot;, ec-&gt;tap_set, sample_no);
+          ec-&gt;tap_rotate_counter = 1600;
+          ec-&gt;tap_set++;
+          if (ec-&gt;tap_set &gt; 2)
+            ec-&gt;tap_set = 0;
+          ec-&gt;fir_state.coeffs = ec-&gt;fir_taps16[ec-&gt;tap_set];
+        }
+        /* ... and we are not in the dwell time from previous speech. */
+        if ((ec-&gt;adaption_mode &amp; ECHO_CAN_USE_ADAPTION) &amp;&amp; ec-&gt;narrowband_score == 0) {
+          //nsuppr = saturate((clean_rx &lt;&lt; 16)/ec-&gt;tx_power[1]);
+          //nsuppr = clean_rx/ec-&gt;tx_power[1];
+          /* If a sudden surge in signal level (e.g. the onset of a tone
+             burst) cause an abnormally high instantaneous to average
+             signal power ratio, we could kick the adaption badly in the
+             wrong direction. This is because the tx_power takes too long
+             to react and rise. We need to stop too rapid adaption to the
+             new signal. We normalise to a value derived from the
+             instantaneous signal if it exceeds the peak by too much. */
+          nsuppr = clean_rx;
+          /* Divide isn't very quick, but the &quot;where is the top bit&quot; and shift
+             instructions are single cycle. */
+          if (tx &gt; 4 * ec-&gt;tx_power[3])
+            i = top_bit(tx) - 8;
+          else
+            i = top_bit(ec-&gt;tx_power[3]) - 8;
+          if (i &gt; 0)
+            nsuppr &gt;&gt;= i;
+          lms_adapt(ec, nsuppr);
+        }
+      }
+      //printf(&quot;%10d %10d %10d %10d %10d\n&quot;, rx, clean_rx, nsuppr, ec-&gt;tx_power[1], ec-&gt;rx_power[1]);
+      //printf(&quot;%.4f\n&quot;, (float) ec-&gt;rx_power[1]/(float) ec-&gt;clean_rx_power);
+    } else {
+      if (!ec-&gt;dtd_onset) {
+//printf(&quot;Revert to %d at %d\n&quot;, (ec-&gt;tap_set + 1)%3, sample_no);
+        memcpy(ec-&gt;fir_taps16[ec-&gt;tap_set], ec-&gt;fir_taps16[(ec-&gt;tap_set + 1) % 3],
+               ec-&gt;taps * sizeof(int16_t));
+        memcpy(ec-&gt;fir_taps16[(ec-&gt;tap_set - 1) % 3],
+               ec-&gt;fir_taps16[(ec-&gt;tap_set + 1) % 3], ec-&gt;taps * sizeof(int16_t));
+        for (i = 0; i &lt; ec-&gt;taps; i++)
+          ec-&gt;fir_taps32[i] = ec-&gt;fir_taps16[(ec-&gt;tap_set + 1) % 3][i] &lt;&lt; 15;
+        ec-&gt;tap_rotate_counter = 1600;
+        ec-&gt;dtd_onset = TRUE;
+      }
+      ec-&gt;nonupdate_dwell = NONUPDATE_DWELL_TIME;
+    }
+  }
+
+  if (ec-&gt;rx_power[1])
+    ec-&gt;vad = (8000 * ec-&gt;clean_rx_power) / ec-&gt;rx_power[1];
+  else
+    ec-&gt;vad = 0;
+  if (ec-&gt;rx_power[1] &gt; 2048 * 2048 &amp;&amp; ec-&gt;clean_rx_power &gt; 4 * ec-&gt;rx_power[1]) {
+    /* The EC seems to be making things worse, instead of better. Zap it! */
+    memset(ec-&gt;fir_taps32, 0, ec-&gt;taps * sizeof(int32_t));
+    for (i = 0; i &lt; 4; i++)
+      memset(ec-&gt;fir_taps16[i], 0, ec-&gt;taps * sizeof(int16_t));
+  }
+#if defined(XYZZY)
+  if ((ec-&gt;adaption_mode &amp; ECHO_CAN_USE_SUPPRESSOR)) {
+    ec-&gt;supp_test1 +=
+      (ec-&gt;fir_state.history[ec-&gt;curr_pos] -
+       ec-&gt;fir_state.history[(ec-&gt;curr_pos - 7) &amp; ec-&gt;tap_mask]);
+    ec-&gt;supp_test2 +=
+      (ec-&gt;fir_state.history[(ec-&gt;curr_pos - 24) &amp; ec-&gt;tap_mask] -
+       ec-&gt;fir_state.history[(ec-&gt;curr_pos - 31) &amp; ec-&gt;tap_mask]);
+    if (ec-&gt;supp_test1 &gt; 42 &amp;&amp; ec-&gt;supp_test2 &gt; 42)
+      supp_change = 25;
+    else
+      supp_change = 50;
+    supp = supp_change + k1 * ec-&gt;supp1 + k2 * ec-&gt;supp2;
+    ec-&gt;supp2 = ec-&gt;supp1;
+    ec-&gt;supp1 = supp;
+    clean_rx *= (1 - supp);
+  }
+#endif
+
+  if ((ec-&gt;adaption_mode &amp; ECHO_CAN_USE_NLP)) {
+    /* Non-linear processor - a fancy way to say &quot;zap small signals, to avoid
+       residual echo due to (uLaw/ALaw) non-linearity in the channel.&quot;. */
+    if (ec-&gt;rx_power[1] &lt; 30000000) {
+      if (!ec-&gt;cng) {
+        ec-&gt;cng_level = ec-&gt;clean_rx_power;
+        ec-&gt;cng = TRUE;
+      }
+      if ((ec-&gt;adaption_mode &amp; ECHO_CAN_USE_CNG)) {
+        /* Very elementary comfort noise generation */
+        /* Just random numbers rolled off very vaguely Hoth-like */
+        ec-&gt;cng_rndnum = 1664525U * ec-&gt;cng_rndnum + 1013904223U;
+        ec-&gt;cng_filter = ((ec-&gt;cng_rndnum &amp; 0xFFFF) - 32768 + 5 * ec-&gt;cng_filter) &gt;&gt; 3;
+        clean_rx = (ec-&gt;cng_filter * ec-&gt;cng_level) &gt;&gt; 17;
+        /* TODO: A better CNG, with more accurate (tracking) spectral shaping! */
+      } else {
+        clean_rx = 0;
+      }
+//clean_rx = -16000;
+    } else {
+      ec-&gt;cng = FALSE;
+    }
+  } else {
+    ec-&gt;cng = FALSE;
+  }
+
+//printf(&quot;Narrowband score %4d %5d at %d\n&quot;, ec-&gt;narrowband_score, score, sample_no);
+  /* Roll around the rolling buffer */
+  if (ec-&gt;curr_pos &lt;= 0)
+    ec-&gt;curr_pos = ec-&gt;taps;
+  ec-&gt;curr_pos--;
+  return (int16_t) clean_rx;
+}
+
+#endif //NOT_NEEDED
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
+#endif
+
+#include &lt;inttypes.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;time.h&gt;
+#include &lt;fcntl.h&gt;
+#include &lt;math.h&gt;
+
+//#include &quot;spandsp/telephony.h&quot;
+//#include &quot;spandsp/tone_detect.h&quot;
+//#include &quot;spandsp/tone_generate.h&quot;
+//#include &quot;spandsp/super_tone_rx.h&quot;
+//#include &quot;giova.h&quot;
+
+#if !defined(M_PI)
+/* C99 systems may not define M_PI */
+#define M_PI 3.14159265358979323846264338327
+#endif
+
+//#define USE_3DNOW
+
+#define DEFAULT_DTMF_TX_LEVEL       -10
+#define DEFAULT_DTMF_TX_ON_TIME     50
+#define DEFAULT_DTMF_TX_OFF_TIME    55
+
+#define DTMF_THRESHOLD              8.0e7f
+#define DTMF_NORMAL_TWIST           6.3f    /* 8dB */
+#define DTMF_REVERSE_TWIST          2.5f    /* 4dB */
+#define DTMF_RELATIVE_PEAK_ROW      6.3f    /* 8dB */
+#define DTMF_RELATIVE_PEAK_COL      6.3f    /* 8dB */
+#define DTMF_TO_TOTAL_ENERGY        42.0f
+
+static const float dtmf_row[] = {
+  697.0f, 770.0f, 852.0f, 941.0f
+};
+static const float dtmf_col[] = {
+  1209.0f, 1336.0f, 1477.0f, 1633.0f
+};
+
+static const char dtmf_positions[] = &quot;123A&quot; &quot;456B&quot; &quot;789C&quot; &quot;*0#D&quot;;
+
+static goertzel_descriptor_t dtmf_detect_row[4];
+static goertzel_descriptor_t dtmf_detect_col[4];
+
+//
+//static int dtmf_tx_inited = 0;
+//static tone_gen_descriptor_t dtmf_digit_tones[16];
+
+#if defined(USE_3DNOW)
+static __inline__ void _dtmf_goertzel_update(goertzel_state_t * s, float x[], int samples)
+{
+  int n;
+  float v;
+  int i;
+  float vv[16];
+
+  vv[4] = s[0].v2;
+  vv[5] = s[1].v2;
+  vv[6] = s[2].v2;
+  vv[7] = s[3].v2;
+  vv[8] = s[0].v3;
+  vv[9] = s[1].v3;
+  vv[10] = s[2].v3;
+  vv[11] = s[3].v3;
+  vv[12] = s[0].fac;
+  vv[13] = s[1].fac;
+  vv[14] = s[2].fac;
+  vv[15] = s[3].fac;
+
+  //v1 = s-&gt;v2;
+  //s-&gt;v2 = s-&gt;v3;
+  //s-&gt;v3 = s-&gt;fac*s-&gt;v2 - v1 + x[0];
+
+  __asm__ __volatile__(&quot; femms;\n&quot; &quot; movq        16(%%edx),%%mm2;\n&quot;
+                       &quot; movq        24(%%edx),%%mm3;\n&quot; &quot; movq        32(%%edx),%%mm4;\n&quot;
+                       &quot; movq        40(%%edx),%%mm5;\n&quot; &quot; movq        48(%%edx),%%mm6;\n&quot;
+                       &quot; movq        56(%%edx),%%mm7;\n&quot; &quot; jmp         1f;\n&quot;
+                       &quot; .align 32;\n&quot; &quot; 1: ;\n&quot; &quot; prefetch    (%%eax);\n&quot;
+                       &quot; movq        %%mm3,%%mm1;\n&quot; &quot; movq        %%mm2,%%mm0;\n&quot;
+                       &quot; movq        %%mm5,%%mm3;\n&quot; &quot; movq        %%mm4,%%mm2;\n&quot;
+                       &quot; pfmul       %%mm7,%%mm5;\n&quot; &quot; pfmul       %%mm6,%%mm4;\n&quot;
+                       &quot; pfsub       %%mm1,%%mm5;\n&quot; &quot; pfsub       %%mm0,%%mm4;\n&quot;
+                       &quot; movq        (%%eax),%%mm0;\n&quot; &quot; movq        %%mm0,%%mm1;\n&quot;
+                       &quot; punpckldq   %%mm0,%%mm1;\n&quot; &quot; add         $4,%%eax;\n&quot;
+                       &quot; pfadd       %%mm1,%%mm5;\n&quot; &quot; pfadd       %%mm1,%%mm4;\n&quot;
+                       &quot; dec         %%ecx;\n&quot; &quot; jnz         1b;\n&quot;
+                       &quot; movq        %%mm2,16(%%edx);\n&quot; &quot; movq        %%mm3,24(%%edx);\n&quot;
+                       &quot; movq        %%mm4,32(%%edx);\n&quot; &quot; movq        %%mm5,40(%%edx);\n&quot;
+                       &quot; femms;\n&quot;::&quot;c&quot;(samples), &quot;a&quot;(x), &quot;d&quot;(vv)
+                       :&quot;memory&quot;, &quot;eax&quot;, &quot;ecx&quot;);
+
+  s[0].v2 = vv[4];
+  s[1].v2 = vv[5];
+  s[2].v2 = vv[6];
+  s[3].v2 = vv[7];
+  s[0].v3 = vv[8];
+  s[1].v3 = vv[9];
+  s[2].v3 = vv[10];
+  s[3].v3 = vv[11];
+}
+
+/*- End of function --------------------------------------------------------*/
+#endif
+
+int dtmf_rx(dtmf_rx_state_t * s, const int16_t amp[], int samples)
+{
+  float row_energy[4];
+  float col_energy[4];
+  float famp;
+  float v1;
+  int i;
+  int j;
+  int sample;
+  int best_row;
+  int best_col;
+  int limit;
+  uint8_t hit;
+
+  hit = 0;
+  for (sample = 0; sample &lt; samples; sample = limit) {
+    /* The block length is optimised to meet the DTMF specs. */
+    if ((samples - sample) &gt;= (102 - s-&gt;current_sample))
+      limit = sample + (102 - s-&gt;current_sample);
+    else
+      limit = samples;
+#if defined(USE_3DNOW)
+    _dtmf_goertzel_update(s-&gt;row_out, amp + sample, limit - sample);
+    _dtmf_goertzel_update(s-&gt;col_out, amp + sample, limit - sample);
+#else
+    /* The following unrolled loop takes only 35% (rough estimate) of the 
+       time of a rolled loop on the machine on which it was developed */
+    for (j = sample; j &lt; limit; j++) {
+      famp = amp[j];
+      if (s-&gt;filter_dialtone) {
+        /* Sharp notches applied at 350Hz and 440Hz - the two common dialtone frequencies.
+           These are rather high Q, to achieve the required narrowness, without using lots of
+           sections. */
+        v1 = 0.98356f * famp + 1.8954426f * s-&gt;z350_1 - 0.9691396f * s-&gt;z350_2;
+        famp = v1 - 1.9251480f * s-&gt;z350_1 + s-&gt;z350_2;
+        s-&gt;z350_2 = s-&gt;z350_1;
+        s-&gt;z350_1 = v1;
+
+        v1 = 0.98456f * famp + 1.8529543f * s-&gt;z440_1 - 0.9691396f * s-&gt;z440_2;
+        famp = v1 - 1.8819938f * s-&gt;z440_1 + s-&gt;z440_2;
+        s-&gt;z440_2 = s-&gt;z440_1;
+        s-&gt;z440_1 = v1;
+      }
+      s-&gt;energy += famp * famp;
+      /* With GCC 2.95, the following unrolled code seems to take about 35%
+         (rough estimate) as long as a neat little 0-3 loop */
+      v1 = s-&gt;row_out[0].v2;
+      s-&gt;row_out[0].v2 = s-&gt;row_out[0].v3;
+      s-&gt;row_out[0].v3 = s-&gt;row_out[0].fac * s-&gt;row_out[0].v2 - v1 + famp;
+
+      v1 = s-&gt;col_out[0].v2;
+      s-&gt;col_out[0].v2 = s-&gt;col_out[0].v3;
+      s-&gt;col_out[0].v3 = s-&gt;col_out[0].fac * s-&gt;col_out[0].v2 - v1 + famp;
+
+      v1 = s-&gt;row_out[1].v2;
+      s-&gt;row_out[1].v2 = s-&gt;row_out[1].v3;
+      s-&gt;row_out[1].v3 = s-&gt;row_out[1].fac * s-&gt;row_out[1].v2 - v1 + famp;
+
+      v1 = s-&gt;col_out[1].v2;
+      s-&gt;col_out[1].v2 = s-&gt;col_out[1].v3;
+      s-&gt;col_out[1].v3 = s-&gt;col_out[1].fac * s-&gt;col_out[1].v2 - v1 + famp;
+
+      v1 = s-&gt;row_out[2].v2;
+      s-&gt;row_out[2].v2 = s-&gt;row_out[2].v3;
+      s-&gt;row_out[2].v3 = s-&gt;row_out[2].fac * s-&gt;row_out[2].v2 - v1 + famp;
+
+      v1 = s-&gt;col_out[2].v2;
+      s-&gt;col_out[2].v2 = s-&gt;col_out[2].v3;
+      s-&gt;col_out[2].v3 = s-&gt;col_out[2].fac * s-&gt;col_out[2].v2 - v1 + famp;
+
+      v1 = s-&gt;row_out[3].v2;
+      s-&gt;row_out[3].v2 = s-&gt;row_out[3].v3;
+      s-&gt;row_out[3].v3 = s-&gt;row_out[3].fac * s-&gt;row_out[3].v2 - v1 + famp;
+
+      v1 = s-&gt;col_out[3].v2;
+      s-&gt;col_out[3].v2 = s-&gt;col_out[3].v3;
+      s-&gt;col_out[3].v3 = s-&gt;col_out[3].fac * s-&gt;col_out[3].v2 - v1 + famp;
+    }
+#endif
+    s-&gt;current_sample += (limit - sample);
+    if (s-&gt;current_sample &lt; 102)
+      continue;
+
+    /* We are at the end of a DTMF detection block */
+    /* Find the peak row and the peak column */
+    row_energy[0] = goertzel_result(&amp;s-&gt;row_out[0]);
+    best_row = 0;
+    col_energy[0] = goertzel_result(&amp;s-&gt;col_out[0]);
+    best_col = 0;
+
+    for (i = 1; i &lt; 4; i++) {
+      row_energy[i] = goertzel_result(&amp;s-&gt;row_out[i]);
+      if (row_energy[i] &gt; row_energy[best_row])
+        best_row = i;
+      col_energy[i] = goertzel_result(&amp;s-&gt;col_out[i]);
+      if (col_energy[i] &gt; col_energy[best_col])
+        best_col = i;
+    }
+    hit = 0;
+    /* Basic signal level test and the twist test */
+    if (row_energy[best_row] &gt;= DTMF_THRESHOLD &amp;&amp; col_energy[best_col] &gt;= DTMF_THRESHOLD
+        &amp;&amp; col_energy[best_col] &lt; row_energy[best_row] * s-&gt;reverse_twist
+        &amp;&amp; col_energy[best_col] * s-&gt;normal_twist &gt; row_energy[best_row]) {
+      /* Relative peak test ... */
+      for (i = 0; i &lt; 4; i++) {
+        if ((i != best_col
+             &amp;&amp; col_energy[i] * DTMF_RELATIVE_PEAK_COL &gt; col_energy[best_col])
+            || (i != best_row
+                &amp;&amp; row_energy[i] * DTMF_RELATIVE_PEAK_ROW &gt; row_energy[best_row])) {
+          break;
+        }
+      }
+      /* ... and fraction of total energy test */
+      if (i &gt;= 4
+          &amp;&amp; (row_energy[best_row] + col_energy[best_col]) &gt;
+          DTMF_TO_TOTAL_ENERGY * s-&gt;energy) {
+        hit = dtmf_positions[(best_row &lt;&lt; 2) + best_col];
+      }
+    }
+    /* The logic in the next test should ensure the following for different successive hit patterns:
+       -----ABB = start of digit B.
+       ----B-BB = start of digit B
+       ----A-BB = start of digit B
+       BBBBBABB = still in digit B.
+       BBBBBB-- = end of digit B
+       BBBBBBC- = end of digit B
+       BBBBACBB = B ends, then B starts again.
+       BBBBBBCC = B ends, then C starts.
+       BBBBBCDD = B ends, then D starts.
+       This can work with:
+       - Back to back differing digits. Back-to-back digits should
+       not happen. The spec. says there should be a gap between digits.
+       However, many real phones do not impose a gap, and rolling across
+       the keypad can produce little or no gap.
+       - It tolerates nasty phones that give a very wobbly start to a digit.
+       - VoIP can give sample slips. The phase jumps that produces will cause
+       the block it is in to give no detection. This logic will ride over a
+       single missed block, and not falsely declare a second digit. If the
+       hiccup happens in the wrong place on a minimum length digit, however
+       we would still fail to detect that digit. Could anything be done to
+       deal with that? Packet loss is clearly a no-go zone.
+       Note this is only relevant to VoIP using A-law, u-law or similar.
+       Low bit rate codecs scramble DTMF too much for it to be recognised,
+       and often slip in units larger than a sample. */
+    if (hit != s-&gt;in_digit) {
+      if (s-&gt;last_hit != s-&gt;in_digit) {
+        /* We have two successive indications that something has changed. */
+        /* To declare digit on, the hits must agree. Otherwise we declare tone off. */
+        hit = (hit &amp;&amp; hit == s-&gt;last_hit) ? hit : 0;
+#if 0
+        if (s-&gt;realtime_callback) {
+          /* Avoid reporting multiple no digit conditions on flaky hits */
+          if (s-&gt;in_digit || hit) {
+            i = (s-&gt;in_digit
+                 &amp;&amp; !hit) ? -99 : rint(log10f(s-&gt;energy) * 10.0f - 20.08f - 90.30F +
+                                       DBM0_MAX_POWER);
+            s-&gt;realtime_callback(s-&gt;realtime_callback_data, hit, i);
+          }
+        } else {
+#endif
+          if (hit) {
+            if (s-&gt;current_digits &lt; MAX_DTMF_DIGITS) {
+              s-&gt;digits[s-&gt;current_digits++] = (char) hit;
+              s-&gt;digits[s-&gt;current_digits] = '\0';
+              if (s-&gt;callback) {
+                s-&gt;callback(s-&gt;callback_data, s-&gt;digits, s-&gt;current_digits);
+                s-&gt;current_digits = 0;
+              }
+            } else {
+              s-&gt;lost_digits++;
+            }
+          }
+#if 0
+        }
+#endif
+        s-&gt;in_digit = hit;
+      }
+    }
+    s-&gt;last_hit = hit;
+    /* Reinitialise the detector for the next block */
+    for (i = 0; i &lt; 4; i++) {
+      goertzel_reset(&amp;s-&gt;row_out[i]);
+      goertzel_reset(&amp;s-&gt;col_out[i]);
+    }
+    s-&gt;energy = 0.0f;
+    s-&gt;current_sample = 0;
+  }
+  if (s-&gt;current_digits &amp;&amp; s-&gt;callback) {
+    s-&gt;callback(s-&gt;callback_data, s-&gt;digits, s-&gt;current_digits);
+    s-&gt;digits[0] = '\0';
+    s-&gt;current_digits = 0;
+  }
+  return 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+size_t dtmf_rx_get(dtmf_rx_state_t * s, char *buf, int max)
+{
+  if (max &gt; s-&gt;current_digits)
+    max = s-&gt;current_digits;
+  if (max &gt; 0) {
+    memcpy(buf, s-&gt;digits, max);
+    memmove(s-&gt;digits, s-&gt;digits + max, s-&gt;current_digits - max);
+    s-&gt;current_digits -= max;
+  }
+  buf[max] = '\0';
+  return max;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+#if 0
+void dtmf_rx_set_realtime_callback(dtmf_rx_state_t * s, tone_report_func_t callback,
+                                   void *user_data)
+{
+  s-&gt;realtime_callback = callback;
+  s-&gt;realtime_callback_data = user_data;
+}
+#endif
+/*- End of function --------------------------------------------------------*/
+
+void dtmf_rx_parms(dtmf_rx_state_t * s, int filter_dialtone, int twist, int reverse_twist)
+{
+  if (filter_dialtone &gt;= 0) {
+    s-&gt;z350_1 = 0.0f;
+    s-&gt;z350_2 = 0.0f;
+    s-&gt;z440_1 = 0.0f;
+    s-&gt;z440_2 = 0.0f;
+    s-&gt;filter_dialtone = filter_dialtone;
+  }
+  if (twist &gt;= 0)
+    s-&gt;normal_twist = powf(10.0f, twist / 10.0f);
+  if (reverse_twist &gt;= 0)
+    s-&gt;reverse_twist = powf(10.0f, reverse_twist / 10.0f);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+dtmf_rx_state_t *dtmf_rx_init(dtmf_rx_state_t * s, dtmf_rx_callback_t callback,
+                              void *user_data)
+{
+  int i;
+  static int initialised = 0;
+
+  s-&gt;callback = callback;
+  s-&gt;callback_data = user_data;
+  s-&gt;realtime_callback = NULL;
+  s-&gt;realtime_callback_data = NULL;
+  s-&gt;filter_dialtone = 0;
+  s-&gt;normal_twist = DTMF_NORMAL_TWIST;
+  s-&gt;reverse_twist = DTMF_REVERSE_TWIST;
+
+  s-&gt;in_digit = 0;
+  s-&gt;last_hit = 0;
+
+  if (!initialised) {
+    for (i = 0; i &lt; 4; i++) {
+      make_goertzel_descriptor(&amp;dtmf_detect_row[i], dtmf_row[i], 102);
+      make_goertzel_descriptor(&amp;dtmf_detect_col[i], dtmf_col[i], 102);
+    }
+    initialised = 1;
+  }
+  for (i = 0; i &lt; 4; i++) {
+    goertzel_init(&amp;s-&gt;row_out[i], &amp;dtmf_detect_row[i]);
+    goertzel_init(&amp;s-&gt;col_out[i], &amp;dtmf_detect_col[i]);
+  }
+  s-&gt;energy = 0.0f;
+  s-&gt;current_sample = 0;
+  s-&gt;lost_digits = 0;
+  s-&gt;current_digits = 0;
+  s-&gt;digits[0] = '\0';
+  return s;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+#if 0
+static void dtmf_tx_initialise(void)
+{
+  int row;
+  int col;
+
+  if (dtmf_tx_inited)
+    return;
+  for (row = 0; row &lt; 4; row++) {
+    for (col = 0; col &lt; 4; col++) {
+      make_tone_gen_descriptor(&amp;dtmf_digit_tones[row * 4 + col], (int) dtmf_row[row],
+                               DEFAULT_DTMF_TX_LEVEL, (int) dtmf_col[col],
+                               DEFAULT_DTMF_TX_LEVEL, DEFAULT_DTMF_TX_ON_TIME,
+                               DEFAULT_DTMF_TX_OFF_TIME, 0, 0, FALSE);
+    }
+  }
+  dtmf_tx_inited = TRUE;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int dtmf_tx(dtmf_tx_state_t * s, int16_t amp[], int max_samples)
+{
+  int len;
+  size_t dig;
+  char *cp;
+
+  len = 0;
+  if (s-&gt;tones.current_section &gt;= 0) {
+    /* Deal with the fragment left over from last time */
+    len = tone_gen(&amp;(s-&gt;tones), amp, max_samples);
+  }
+  dig = 0;
+  while (dig &lt; s-&gt;current_digits &amp;&amp; len &lt; max_samples) {
+    /* Step to the next digit */
+    if ((cp = strchr(dtmf_positions, s-&gt;digits[dig++])) == NULL)
+      continue;
+    tone_gen_init(&amp;(s-&gt;tones), &amp;(s-&gt;tone_descriptors[cp - dtmf_positions]));
+    len += tone_gen(&amp;(s-&gt;tones), amp + len, max_samples - len);
+  }
+  if (dig) {
+    /* Shift out the consumed digits */
+    s-&gt;current_digits -= dig;
+    memmove(s-&gt;digits, s-&gt;digits + dig, s-&gt;current_digits);
+  }
+  return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+size_t dtmf_tx_put(dtmf_tx_state_t * s, const char *digits)
+{
+  size_t len;
+
+  /* This returns the number of characters that would not fit in the buffer.
+     The buffer will only be loaded if the whole string of digits will fit,
+     in which case zero is returned. */
+  if ((len = strlen(digits)) &gt; 0) {
+    if (s-&gt;current_digits + len &lt;= MAX_DTMF_DIGITS) {
+      memcpy(s-&gt;digits + s-&gt;current_digits, digits, len);
+      s-&gt;current_digits += len;
+      len = 0;
+    } else {
+      len = MAX_DTMF_DIGITS - s-&gt;current_digits;
+    }
+  }
+  return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+dtmf_tx_state_t *dtmf_tx_init(dtmf_tx_state_t * s)
+{
+  if (!dtmf_tx_inited)
+    dtmf_tx_initialise();
+  s-&gt;tone_descriptors = dtmf_digit_tones;
+  tone_gen_init(&amp;(s-&gt;tones), &amp;dtmf_digit_tones[0]);
+  s-&gt;current_sample = 0;
+  s-&gt;current_digits = 0;
+  s-&gt;tones.current_section = -1;
+  return s;
+}
+#endif //NO TX
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
+
+void make_goertzel_descriptor(goertzel_descriptor_t * t, float freq, int samples)
+{
+  //t-&gt;fac = 2.0f*cosf(2.0f*M_PI*(freq/(float) SAMPLE_RATE));
+  t-&gt;fac = 2.0f * cosf(2.0f * M_PI * (freq / (float) 8000));
+  t-&gt;samples = samples;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+goertzel_state_t *goertzel_init(goertzel_state_t * s, goertzel_descriptor_t * t)
+{
+  if (s || (s = malloc(sizeof(goertzel_state_t)))) {
+    s-&gt;v2 = s-&gt;v3 = 0.0;
+    s-&gt;fac = t-&gt;fac;
+    s-&gt;samples = t-&gt;samples;
+    s-&gt;current_sample = 0;
+  }
+  return s;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+void goertzel_reset(goertzel_state_t * s)
+{
+  s-&gt;v2 = s-&gt;v3 = 0.0;
+  s-&gt;current_sample = 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int goertzel_update(goertzel_state_t * s, const int16_t amp[], int samples)
+{
+  int i;
+  float v1;
+
+  if (samples &gt; s-&gt;samples - s-&gt;current_sample)
+    samples = s-&gt;samples - s-&gt;current_sample;
+  for (i = 0; i &lt; samples; i++) {
+    v1 = s-&gt;v2;
+    s-&gt;v2 = s-&gt;v3;
+    s-&gt;v3 = s-&gt;fac * s-&gt;v2 - v1 + amp[i];
+  }
+  s-&gt;current_sample += samples;
+  return samples;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+float goertzel_result(goertzel_state_t * s)
+{
+  float v1;
+
+  /* Push a zero through the process to finish things off. */
+  v1 = s-&gt;v2;
+  s-&gt;v2 = s-&gt;v3;
+  s-&gt;v3 = s-&gt;fac * s-&gt;v2 - v1;
+  /* Now calculate the non-recursive side of the filter. */
+  /* The result here is not scaled down to allow for the magnification
+     effect of the filter (the usual DFT magnification effect). */
+  return s-&gt;v3 * s-&gt;v3 + s-&gt;v2 * s-&gt;v2 - s-&gt;v2 * s-&gt;v3 * s-&gt;fac;
+}
+
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskcelliax_spandsph"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.h (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.h                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/celliax_spandsp.h        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,1028 @@
</span><ins>+
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * bit_operations.h - Various bit level operations, such as bit reversal
+ *
+ * Written by Steve Underwood &lt;steveu@coppice.org&gt;
+ *
+ * Copyright (C) 2006 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: bit_operations.h,v 1.15 2007/02/23 13:16:13 steveu Exp $
+ */
+
+/*! \file */
+
+#ifndef _CELLIAX_SPANDSP_H
+#define _CELLIAX_SPANDSP_H
+
+#include &lt;math.h&gt;
+
+/*! \brief Find the bit position of the highest set bit in a word
+    \param bits The word to be searched
+    \return The bit number of the highest set bit, or -1 if the word is zero. */
+static __inline__ int top_bit(unsigned int bits)
+{
+  int res;
+
+#if defined(__i386__)  ||  defined(__x86_64__)
+__asm__(&quot; xorl %[res],%[res];\n&quot; &quot; decl %[res];\n&quot; &quot; bsrl %[bits],%[res]\n&quot;:[res] &quot;=&amp;r&quot;
+          (res)
+:        [bits] &quot;rm&quot;(bits));
+  return res;
+#elif defined(__ppc__)  ||   defined(__powerpc__)
+__asm__(&quot;cntlzw %[res],%[bits];\n&quot;:[res] &quot;=&amp;r&quot;(res)
+:        [bits] &quot;r&quot;(bits));
+  return 31 - res;
+#else
+  if (bits == 0)
+    return -1;
+  res = 0;
+  if (bits &amp; 0xFFFF0000) {
+    bits &amp;= 0xFFFF0000;
+    res += 16;
+  }
+  if (bits &amp; 0xFF00FF00) {
+    bits &amp;= 0xFF00FF00;
+    res += 8;
+  }
+  if (bits &amp; 0xF0F0F0F0) {
+    bits &amp;= 0xF0F0F0F0;
+    res += 4;
+  }
+  if (bits &amp; 0xCCCCCCCC) {
+    bits &amp;= 0xCCCCCCCC;
+    res += 2;
+  }
+  if (bits &amp; 0xAAAAAAAA) {
+    bits &amp;= 0xAAAAAAAA;
+    res += 1;
+  }
+  return res;
+#endif
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Find the bit position of the lowest set bit in a word
+    \param bits The word to be searched
+    \return The bit number of the lowest set bit, or -1 if the word is zero. */
+static __inline__ int bottom_bit(unsigned int bits)
+{
+  int res;
+
+#if defined(__i386__)  ||  defined(__x86_64__)
+__asm__(&quot; xorl %[res],%[res];\n&quot; &quot; decl %[res];\n&quot; &quot; bsfl %[bits],%[res]\n&quot;:[res] &quot;=&amp;r&quot;
+          (res)
+:        [bits] &quot;rm&quot;(bits));
+  return res;
+#else
+  if (bits == 0)
+    return -1;
+  res = 31;
+  if (bits &amp; 0x0000FFFF) {
+    bits &amp;= 0x0000FFFF;
+    res -= 16;
+  }
+  if (bits &amp; 0x00FF00FF) {
+    bits &amp;= 0x00FF00FF;
+    res -= 8;
+  }
+  if (bits &amp; 0x0F0F0F0F) {
+    bits &amp;= 0x0F0F0F0F;
+    res -= 4;
+  }
+  if (bits &amp; 0x33333333) {
+    bits &amp;= 0x33333333;
+    res -= 2;
+  }
+  if (bits &amp; 0x55555555) {
+    bits &amp;= 0x55555555;
+    res -= 1;
+  }
+  return res;
+#endif
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Bit reverse a byte.
+    \param data The byte to be reversed.
+    \return The bit reversed version of data. */
+static __inline__ uint8_t bit_reverse8(uint8_t x)
+{
+#if defined(__i386__)  ||  defined(__x86_64__)  ||  defined(__ppc__)  ||  defined(__powerpc__)
+  /* If multiply is fast */
+  return ((x * 0x0802U &amp; 0x22110U) | (x * 0x8020U &amp; 0x88440U)) * 0x10101U &gt;&gt; 16;
+#else
+  /* If multiply is slow, but we have a barrel shifter */
+  x = (x &gt;&gt; 4) | (x &lt;&lt; 4);
+  x = ((x &amp; 0xCC) &gt;&gt; 2) | ((x &amp; 0x33) &lt;&lt; 2);
+  return ((x &amp; 0xAA) &gt;&gt; 1) | ((x &amp; 0x55) &lt;&lt; 1);
+#endif
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Bit reverse a 16 bit word.
+    \param data The word to be reversed.
+    \return The bit reversed version of data. */
+uint16_t bit_reverse16(uint16_t data);
+
+/*! \brief Bit reverse a 32 bit word.
+    \param data The word to be reversed.
+    \return The bit reversed version of data. */
+uint32_t bit_reverse32(uint32_t data);
+
+/*! \brief Bit reverse each of the four bytes in a 32 bit word.
+    \param data The word to be reversed.
+    \return The bit reversed version of data. */
+uint32_t bit_reverse_4bytes(uint32_t data);
+
+#if defined(__x86_64__)
+/*! \brief Bit reverse each of the eight bytes in a 64 bit word.
+    \param data The word to be reversed.
+    \return The bit reversed version of data. */
+uint64_t bit_reverse_8bytes(uint64_t data);
+#endif
+
+/*! \brief Bit reverse each bytes in a buffer.
+    \param to The buffer to place the reversed data in.
+    \param from The buffer containing the data to be reversed.
+    \param The length of the data in the buffer. */
+void bit_reverse(uint8_t to[], const uint8_t from[], int len);
+
+/*! \brief Find the number of set bits in a 32 bit word.
+    \param x The word to be searched.
+    \return The number of set bits. */
+int one_bits32(uint32_t x);
+
+/*! \brief Create a mask as wide as the number in a 32 bit word.
+    \param x The word to be searched.
+    \return The mask. */
+uint32_t make_mask32(uint32_t x);
+
+/*! \brief Create a mask as wide as the number in a 16 bit word.
+    \param x The word to be searched.
+    \return The mask. */
+uint16_t make_mask16(uint16_t x);
+
+/*! \brief Find the least significant one in a word, and return a word
+           with just that bit set.
+    \param x The word to be searched.
+    \return The word with the single set bit. */
+static __inline__ uint32_t least_significant_one32(uint32_t x)
+{
+  return (x &amp; (-(int32_t) x));
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Find the most significant one in a word, and return a word
+           with just that bit set.
+    \param x The word to be searched.
+    \return The word with the single set bit. */
+static __inline__ uint32_t most_significant_one32(uint32_t x)
+{
+#if defined(__i386__)  ||  defined(__x86_64__)  ||  defined(__ppc__)  ||  defined(__powerpc__)
+  return 1 &lt;&lt; top_bit(x);
+#else
+  x = make_mask32(x);
+  return (x ^ (x &gt;&gt; 1));
+#endif
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Find the parity of a byte.
+    \param x The byte to be checked.
+    \return 1 for odd, or 0 for even. */
+static __inline__ int parity8(uint8_t x)
+{
+  x = (x ^ (x &gt;&gt; 4)) &amp; 0x0F;
+  return (0x6996 &gt;&gt; x) &amp; 1;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Find the parity of a 16 bit word.
+    \param x The word to be checked.
+    \return 1 for odd, or 0 for even. */
+static __inline__ int parity16(uint16_t x)
+{
+  x ^= (x &gt;&gt; 8);
+  x = (x ^ (x &gt;&gt; 4)) &amp; 0x0F;
+  return (0x6996 &gt;&gt; x) &amp; 1;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*! \brief Find the parity of a 32 bit word.
+    \param x The word to be checked.
+    \return 1 for odd, or 0 for even. */
+static __inline__ int parity32(uint32_t x)
+{
+  x ^= (x &gt;&gt; 16);
+  x ^= (x &gt;&gt; 8);
+  x = (x ^ (x &gt;&gt; 4)) &amp; 0x0F;
+  return (0x6996 &gt;&gt; x) &amp; 1;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*- End of file ------------------------------------------------------------*/
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * fir.h - General telephony FIR routines
+ *
+ * Written by Steve Underwood &lt;steveu@coppice.org&gt;
+ *
+ * Copyright (C) 2002 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: fir.h,v 1.8 2006/10/24 13:45:28 steveu Exp $
+ */
+
+/*! \page fir_page FIR filtering
+\section fir_page_sec_1 What does it do?
+???.
+
+\section fir_page_sec_2 How does it work?
+???.
+*/
+
+#if 0
+#if defined(USE_MMX)  ||  defined(USE_SSE2)
+#include &quot;mmx.h&quot;
+#endif
+
+/*!
+    16 bit integer FIR descriptor. This defines the working state for a single
+    instance of an FIR filter using 16 bit integer coefficients.
+*/
+typedef struct {
+  int taps;
+  int curr_pos;
+  const int16_t *coeffs;
+  int16_t *history;
+} fir16_state_t;
+
+/*!
+    32 bit integer FIR descriptor. This defines the working state for a single
+    instance of an FIR filter using 32 bit integer coefficients, and filtering
+    16 bit integer data.
+*/
+typedef struct {
+  int taps;
+  int curr_pos;
+  const int32_t *coeffs;
+  int16_t *history;
+} fir32_state_t;
+
+/*!
+    Floating point FIR descriptor. This defines the working state for a single
+    instance of an FIR filter using floating point coefficients and data.
+*/
+typedef struct {
+  int taps;
+  int curr_pos;
+  const float *coeffs;
+  float *history;
+} fir_float_state_t;
+
+static __inline__ const int16_t *fir16_create(fir16_state_t * fir, const int16_t * coeffs,
+                                              int taps)
+{
+  fir-&gt;taps = taps;
+  fir-&gt;curr_pos = taps - 1;
+  fir-&gt;coeffs = coeffs;
+#if defined(USE_MMX)  ||  defined(USE_SSE2)
+  if ((fir-&gt;history = malloc(2 * taps * sizeof(int16_t))))
+    memset(fir-&gt;history, 0, 2 * taps * sizeof(int16_t));
+#else
+  if ((fir-&gt;history = (int16_t *) malloc(taps * sizeof(int16_t))))
+    memset(fir-&gt;history, 0, taps * sizeof(int16_t));
+#endif
+  return fir-&gt;history;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void fir16_flush(fir16_state_t * fir)
+{
+#if defined(USE_MMX)  ||  defined(USE_SSE2)
+  memset(fir-&gt;history, 0, 2 * fir-&gt;taps * sizeof(int16_t));
+#else
+  memset(fir-&gt;history, 0, fir-&gt;taps * sizeof(int16_t));
+#endif
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void fir16_free(fir16_state_t * fir)
+{
+  free(fir-&gt;history);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int16_t fir16(fir16_state_t * fir, int16_t sample)
+{
+  int i;
+  int32_t y;
+#if defined(USE_MMX)
+  mmx_t *mmx_coeffs;
+  mmx_t *mmx_hist;
+
+  fir-&gt;history[fir-&gt;curr_pos] = sample;
+  fir-&gt;history[fir-&gt;curr_pos + fir-&gt;taps] = sample;
+
+  mmx_coeffs = (mmx_t *) fir-&gt;coeffs;
+  mmx_hist = (mmx_t *) &amp; fir-&gt;history[fir-&gt;curr_pos];
+  i = fir-&gt;taps;
+  pxor_r2r(mm4, mm4);
+  /* 8 samples per iteration, so the filter must be a multiple of 8 long. */
+  while (i &gt; 0) {
+    movq_m2r(mmx_coeffs[0], mm0);
+    movq_m2r(mmx_coeffs[1], mm2);
+    movq_m2r(mmx_hist[0], mm1);
+    movq_m2r(mmx_hist[1], mm3);
+    mmx_coeffs += 2;
+    mmx_hist += 2;
+    pmaddwd_r2r(mm1, mm0);
+    pmaddwd_r2r(mm3, mm2);
+    paddd_r2r(mm0, mm4);
+    paddd_r2r(mm2, mm4);
+    i -= 8;
+  }
+  movq_r2r(mm4, mm0);
+  psrlq_i2r(32, mm0);
+  paddd_r2r(mm0, mm4);
+  movd_r2m(mm4, y);
+  emms();
+#elif defined(USE_SSE2)
+  xmm_t *xmm_coeffs;
+  xmm_t *xmm_hist;
+
+  fir-&gt;history[fir-&gt;curr_pos] = sample;
+  fir-&gt;history[fir-&gt;curr_pos + fir-&gt;taps] = sample;
+
+  xmm_coeffs = (xmm_t *) fir-&gt;coeffs;
+  xmm_hist = (xmm_t *) &amp; fir-&gt;history[fir-&gt;curr_pos];
+  i = fir-&gt;taps;
+  pxor_r2r(xmm4, xmm4);
+  /* 16 samples per iteration, so the filter must be a multiple of 16 long. */
+  while (i &gt; 0) {
+    movdqu_m2r(xmm_coeffs[0], xmm0);
+    movdqu_m2r(xmm_coeffs[1], xmm2);
+    movdqu_m2r(xmm_hist[0], xmm1);
+    movdqu_m2r(xmm_hist[1], xmm3);
+    xmm_coeffs += 2;
+    xmm_hist += 2;
+    pmaddwd_r2r(xmm1, xmm0);
+    pmaddwd_r2r(xmm3, xmm2);
+    paddd_r2r(xmm0, xmm4);
+    paddd_r2r(xmm2, xmm4);
+    i -= 16;
+  }
+  movdqa_r2r(xmm4, xmm0);
+  psrldq_i2r(8, xmm0);
+  paddd_r2r(xmm0, xmm4);
+  movdqa_r2r(xmm4, xmm0);
+  psrldq_i2r(4, xmm0);
+  paddd_r2r(xmm0, xmm4);
+  movd_r2m(xmm4, y);
+#else
+  int offset1;
+  int offset2;
+
+  fir-&gt;history[fir-&gt;curr_pos] = sample;
+
+  offset2 = fir-&gt;curr_pos;
+  offset1 = fir-&gt;taps - offset2;
+  y = 0;
+  for (i = fir-&gt;taps - 1; i &gt;= offset1; i--)
+    y += fir-&gt;coeffs[i] * fir-&gt;history[i - offset1];
+  for (; i &gt;= 0; i--)
+    y += fir-&gt;coeffs[i] * fir-&gt;history[i + offset2];
+#endif
+  if (fir-&gt;curr_pos &lt;= 0)
+    fir-&gt;curr_pos = fir-&gt;taps;
+  fir-&gt;curr_pos--;
+  return (int16_t) (y &gt;&gt; 15);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ const int16_t *fir32_create(fir32_state_t * fir, const int32_t * coeffs,
+                                              int taps)
+{
+  fir-&gt;taps = taps;
+  fir-&gt;curr_pos = taps - 1;
+  fir-&gt;coeffs = coeffs;
+  fir-&gt;history = (int16_t *) malloc(taps * sizeof(int16_t));
+  if (fir-&gt;history)
+    memset(fir-&gt;history, '\0', taps * sizeof(int16_t));
+  return fir-&gt;history;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void fir32_flush(fir32_state_t * fir)
+{
+  memset(fir-&gt;history, 0, fir-&gt;taps * sizeof(int16_t));
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void fir32_free(fir32_state_t * fir)
+{
+  free(fir-&gt;history);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int16_t fir32(fir32_state_t * fir, int16_t sample)
+{
+  int i;
+  int32_t y;
+  int offset1;
+  int offset2;
+
+  fir-&gt;history[fir-&gt;curr_pos] = sample;
+  offset2 = fir-&gt;curr_pos;
+  offset1 = fir-&gt;taps - offset2;
+  y = 0;
+  for (i = fir-&gt;taps - 1; i &gt;= offset1; i--)
+    y += fir-&gt;coeffs[i] * fir-&gt;history[i - offset1];
+  for (; i &gt;= 0; i--)
+    y += fir-&gt;coeffs[i] * fir-&gt;history[i + offset2];
+  if (fir-&gt;curr_pos &lt;= 0)
+    fir-&gt;curr_pos = fir-&gt;taps;
+  fir-&gt;curr_pos--;
+  return (int16_t) (y &gt;&gt; 15);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ const float *fir_float_create(fir_float_state_t * fir,
+                                                const float *coeffs, int taps)
+{
+  fir-&gt;taps = taps;
+  fir-&gt;curr_pos = taps - 1;
+  fir-&gt;coeffs = coeffs;
+  fir-&gt;history = (float *) malloc(taps * sizeof(float));
+  if (fir-&gt;history)
+    memset(fir-&gt;history, '\0', taps * sizeof(float));
+  return fir-&gt;history;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void fir_float_free(fir_float_state_t * fir)
+{
+  free(fir-&gt;history);
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ int16_t fir_float(fir_float_state_t * fir, int16_t sample)
+{
+  int i;
+  float y;
+  int offset1;
+  int offset2;
+
+  fir-&gt;history[fir-&gt;curr_pos] = sample;
+
+  offset2 = fir-&gt;curr_pos;
+  offset1 = fir-&gt;taps - offset2;
+  y = 0;
+  for (i = fir-&gt;taps - 1; i &gt;= offset1; i--)
+    y += fir-&gt;coeffs[i] * fir-&gt;history[i - offset1];
+  for (; i &gt;= 0; i--)
+    y += fir-&gt;coeffs[i] * fir-&gt;history[i + offset2];
+  if (fir-&gt;curr_pos &lt;= 0)
+    fir-&gt;curr_pos = fir-&gt;taps;
+  fir-&gt;curr_pos--;
+  return (int16_t) y;
+}
+
+/*- End of function --------------------------------------------------------*/
+#endif
+
+/*- End of file ------------------------------------------------------------*/
+
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * echo.h - An echo cancellor, suitable for electrical and acoustic
+ *            cancellation. This code does not currently comply with
+ *            any relevant standards (e.g. G.164/5/7/8).
+ *
+ * Written by Steve Underwood &lt;steveu@coppice.org&gt;
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * Based on a bit from here, a bit from there, eye of toad,
+ * ear of bat, etc - plus, of course, my own 2 cents.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: echo.h,v 1.9 2006/10/24 13:45:28 steveu Exp $
+ */
+
+/*! \file */
+
+/*! \page echo_can_page Line echo cancellation for voice
+
+\section echo_can_page_sec_1 What does it do?
+This module aims to provide G.168-2002 compliant echo cancellation, to remove
+electrical echoes (e.g. from 2-4 wire hybrids) from voice calls.
+
+\section echo_can_page_sec_2 How does it work?
+The heart of the echo cancellor is FIR filter. This is adapted to match the echo
+impulse response of the telephone line. It must be long enough to adequately cover
+the duration of that impulse response. The signal transmitted to the telephone line
+is passed through the FIR filter. Once the FIR is properly adapted, the resulting
+output is an estimate of the echo signal received from the line. This is subtracted
+from the received signal. The result is an estimate of the signal which originated
+at the far end of the line, free from echos of our own transmitted signal. 
+
+The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and was
+introduced in 1960. It is the commonest form of filter adaption used in things
+like modem line equalisers and line echo cancellers. There it works very well.
+However, it only works well for signals of constant amplitude. It works very poorly
+for things like speech echo cancellation, where the signal level varies widely.
+This is quite easy to fix. If the signal level is normalised - similar to applying
+AGC - LMS can work as well for a signal of varying amplitude as it does for a modem
+signal. This normalised least mean squares (NLMS) algorithm is the commonest one used
+for speech echo cancellation. Many other algorithms exist - e.g. RLS (essentially
+the same as Kalman filtering), FAP, etc. Some perform significantly better than NLMS.
+However, factors such as computational complexity and patents favour the use of NLMS.
+
+A simple refinement to NLMS can improve its performance with speech. NLMS tends
+to adapt best to the strongest parts of a signal. If the signal is white noise,
+the NLMS algorithm works very well. However, speech has more low frequency than
+high frequency content. Pre-whitening (i.e. filtering the signal to flatten
+its spectrum) the echo signal improves the adapt rate for speech, and ensures the
+final residual signal is not heavily biased towards high frequencies. A very low
+complexity filter is adequate for this, so pre-whitening adds little to the
+compute requirements of the echo canceller.
+
+An FIR filter adapted using pre-whitened NLMS performs well, provided certain
+conditions are met: 
+
+    - The transmitted signal has poor self-correlation.
+    - There is no signal being generated within the environment being cancelled.
+
+The difficulty is that neither of these can be guaranteed.
+
+If the adaption is performed while transmitting noise (or something fairly noise
+like, such as voice) the adaption works very well. If the adaption is performed
+while transmitting something highly correlative (typically narrow band energy
+such as signalling tones or DTMF), the adaption can go seriously wrong. The reason
+is there is only one solution for the adaption on a near random signal - the impulse
+response of the line. For a repetitive signal, there are any number of solutions
+which converge the adaption, and nothing guides the adaption to choose the generalised
+one. Allowing an untrained canceller to converge on this kind of narrowband
+energy probably a good thing, since at least it cancels the tones. Allowing a well
+converged canceller to continue converging on such energy is just a way to ruin
+its generalised adaption. A narrowband detector is needed, so adapation can be
+suspended at appropriate times.
+
+The adaption process is based on trying to eliminate the received signal. When
+there is any signal from within the environment being cancelled it may upset the
+adaption process. Similarly, if the signal we are transmitting is small, noise
+may dominate and disturb the adaption process. If we can ensure that the
+adaption is only performed when we are transmitting a significant signal level,
+and the environment is not, things will be OK. Clearly, it is easy to tell when
+we are sending a significant signal. Telling, if the environment is generating a
+significant signal, and doing it with sufficient speed that the adaption will
+not have diverged too much more we stop it, is a little harder. 
+
+The key problem in detecting when the environment is sourcing significant energy
+is that we must do this very quickly. Given a reasonably long sample of the
+received signal, there are a number of strategies which may be used to assess
+whether that signal contains a strong far end component. However, by the time
+that assessment is complete the far end signal will have already caused major
+mis-convergence in the adaption process. An assessment algorithm is needed which
+produces a fairly accurate result from a very short burst of far end energy. 
+
+\section echo_can_page_sec_3 How do I use it?
+The echo cancellor processes both the transmit and receive streams sample by
+sample. The processing function is not declared inline. Unfortunately,
+cancellation requires many operations per sample, so the call overhead is only a
+minor burden. 
+*/
+
+#define NONUPDATE_DWELL_TIME        600 /* 600 samples, or 75ms */
+
+#if 0
+/* Mask bits for the adaption mode */
+#define ECHO_CAN_USE_NLP            0x01
+#define ECHO_CAN_USE_SUPPRESSOR     0x02
+#define ECHO_CAN_USE_CNG            0x04
+#define ECHO_CAN_USE_ADAPTION       0x08
+
+/*!
+    G.168 echo canceller descriptor. This defines the working state for a line
+    echo canceller.
+*/
+typedef struct {
+  int tx_power[4];
+  int rx_power[3];
+  int clean_rx_power;
+
+  int rx_power_threshold;
+  int nonupdate_dwell;
+
+  fir16_state_t fir_state;
+  /*! Echo FIR taps (16 bit version) */
+  int16_t *fir_taps16[4];
+  /*! Echo FIR taps (32 bit version) */
+  int32_t *fir_taps32;
+
+  int curr_pos;
+
+  int taps;
+  int tap_mask;
+  int adaption_mode;
+
+  int32_t supp_test1;
+  int32_t supp_test2;
+  int32_t supp1;
+  int32_t supp2;
+  int vad;
+  int cng;
+  /* Parameters for the Hoth noise generator */
+  int cng_level;
+  int cng_rndnum;
+  int cng_filter;
+
+  int16_t geigel_max;
+  int geigel_lag;
+  int dtd_onset;
+  int tap_set;
+  int tap_rotate_counter;
+
+  int32_t latest_correction;    /* Indication of the magnitude of the latest
+                                   adaption, or a code to indicate why adaption
+                                   was skipped, for test purposes */
+  int32_t last_acf[28];
+  int narrowband_count;
+  int narrowband_score;
+} echo_can_state_t;
+
+/*! Create a voice echo canceller context.
+    \param len The length of the canceller, in samples.
+    \return The new canceller context, or NULL if the canceller could not be created.
+*/
+echo_can_state_t *echo_can_create(int len, int adaption_mode);
+
+/*! Free a voice echo canceller context.
+    \param ec The echo canceller context.
+*/
+void echo_can_free(echo_can_state_t * ec);
+
+/*! Flush (reinitialise) a voice echo canceller context.
+    \param ec The echo canceller context.
+*/
+void echo_can_flush(echo_can_state_t * ec);
+
+/*! Set the adaption mode of a voice echo canceller context.
+    \param ec The echo canceller context.
+    \param adapt The mode.
+*/
+void echo_can_adaption_mode(echo_can_state_t * ec, int adaption_mode);
+
+/*! Process a sample through a voice echo canceller.
+    \param ec The echo canceller context.
+    \param tx The transmitted audio sample.
+    \param rx The received audio sample.
+    \return The clean (echo cancelled) received sample.
+*/
+int16_t echo_can_update(echo_can_state_t * ec, int16_t tx, int16_t rx);
+
+#endif
+/*- End of file ------------------------------------------------------------*/
+
+/*!
+    Floating point Goertzel filter descriptor.
+*/
+typedef struct {
+  float fac;
+  int samples;
+} goertzel_descriptor_t;
+
+/*!
+    Floating point Goertzel filter state descriptor.
+*/
+typedef struct {
+  float v2;
+  float v3;
+  float fac;
+  int samples;
+  int current_sample;
+} goertzel_state_t;
+
+/*! \brief Create a descriptor for use with either a Goertzel transform */
+void make_goertzel_descriptor(goertzel_descriptor_t * t, float freq, int samples);
+
+/*! \brief Initialise the state of a Goertzel transform.
+    \param s The Goertzel context. If NULL, a context is allocated with malloc.
+    \param t The Goertzel descriptor.
+    \return A pointer to the Goertzel state. */
+goertzel_state_t *goertzel_init(goertzel_state_t * s, goertzel_descriptor_t * t);
+
+/*! \brief Reset the state of a Goertzel transform.
+    \param s The Goertzel context.
+    \param t The Goertzel descriptor.
+    \return A pointer to the Goertzel state. */
+void goertzel_reset(goertzel_state_t * s);
+
+/*! \brief Update the state of a Goertzel transform.
+    \param s The Goertzel context
+    \param amp The samples to be transformed
+    \param samples The number of samples
+    \return The number of samples unprocessed */
+int goertzel_update(goertzel_state_t * s, const int16_t amp[], int samples);
+
+/*! \brief Evaluate the final result of a Goertzel transform.
+    \param s The Goertzel context
+    \return The result of the transform. */
+float goertzel_result(goertzel_state_t * s);
+
+/*! \brief Update the state of a Goertzel transform.
+    \param s The Goertzel context
+    \param amp The sample to be transformed. */
+static __inline__ void goertzel_sample(goertzel_state_t * s, int16_t amp)
+{
+  float v1;
+
+  v1 = s-&gt;v2;
+  s-&gt;v2 = s-&gt;v3;
+  s-&gt;v3 = s-&gt;fac * s-&gt;v2 - v1 + amp;
+  s-&gt;current_sample++;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * tone_detect.c - General telephony tone detection.
+ *
+ * Written by Steve Underwood &lt;steveu@coppice.org&gt;
+ *
+ * Copyright (C) 2001-2003, 2005 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: tone_detect.c,v 1.31 2007/03/03 10:40:33 steveu Exp $
+ */
+
+/*! \file tone_detect.h */
+
+#if !defined(M_PI)
+/* C99 systems may not define M_PI */
+#define M_PI 3.14159265358979323846264338327
+#endif
+/*! \page dtmf_rx_page DTMF receiver
+\section dtmf_rx_page_sec_1 What does it do?
+The DTMF receiver detects the standard DTMF digits. It is compliant with
+ITU-T Q.23, ITU-T Q.24, and the local DTMF specifications of most administrations.
+Its passes the test suites. It also scores *very* well on the standard
+talk-off tests. 
+
+The current design uses floating point extensively. It is not tolerant of DC.
+It is expected that a DC restore stage will be placed before the DTMF detector.
+Unless the dial tone filter is switched on, the detector has poor tolerance
+of dial tone. Whether this matter depends on your application. If you are using
+the detector in an IVR application you will need proper echo cancellation to
+get good performance in the presence of speech prompts, so dial tone will not
+exist. If you do need good dial tone tolerance, a dial tone filter can be
+enabled in the detector.
+
+\section dtmf_rx_page_sec_2 How does it work?
+Like most other DSP based DTMF detector's, this one uses the Goertzel algorithm
+to look for the DTMF tones. What makes each detector design different is just how
+that algorithm is used.
+
+Basic DTMF specs:
+    - Minimum tone on = 40ms
+    - Minimum tone off = 50ms
+    - Maximum digit rate = 10 per second
+    - Normal twist &lt;= 8dB accepted
+    - Reverse twist &lt;= 4dB accepted
+    - S/N &gt;= 15dB will detect OK
+    - Attenuation &lt;= 26dB will detect OK
+    - Frequency tolerance +- 1.5% will detect, +-3.5% will reject
+
+TODO:
+*/
+
+/*! \page dtmf_tx_page DTMF tone generation
+\section dtmf_tx_page_sec_1 What does it do?
+
+The DTMF tone generation module provides for the generation of the
+repertoire of 16 DTMF dual tones. 
+
+\section dtmf_tx_page_sec_2 How does it work?
+*/
+
+#define MAX_DTMF_DIGITS 128
+
+typedef void (*dtmf_rx_callback_t) (void *user_data, const char *digits, int len);
+
+/*!
+    DTMF generator state descriptor. This defines the state of a single
+    working instance of a DTMF generator.
+*/
+#if 0
+typedef struct {
+  tone_gen_descriptor_t *tone_descriptors;
+  tone_gen_state_t tones;
+  char digits[MAX_DTMF_DIGITS + 1];
+  int current_sample;
+  size_t current_digits;
+} dtmf_tx_state_t;
+
+#endif
+
+/*!
+    DTMF digit detector descriptor.
+*/
+typedef struct {
+  /*! Optional callback funcion to deliver received digits. */
+  dtmf_rx_callback_t callback;
+  /*! An opaque pointer passed to the callback function. */
+  void *callback_data;
+  /*! Optional callback funcion to deliver real time digit state changes. */
+  //tone_report_func_t realtime_callback;
+  void *realtime_callback;
+  /*! An opaque pointer passed to the real time callback function. */
+  void *realtime_callback_data;
+  /*! TRUE if dialtone should be filtered before processing */
+  int filter_dialtone;
+  /*! Maximum acceptable &quot;normal&quot; (lower bigger than higher) twist ratio */
+  float normal_twist;
+  /*! Maximum acceptable &quot;reverse&quot; (higher bigger than lower) twist ratio */
+  float reverse_twist;
+
+  /*! 350Hz filter state for the optional dialtone filter */
+  float z350_1;
+  float z350_2;
+  /*! 440Hz filter state for the optional dialtone filter */
+  float z440_1;
+  float z440_2;
+
+  /*! Tone detector working states */
+  goertzel_state_t row_out[4];
+  goertzel_state_t col_out[4];
+  /*! The accumlating total energy on the same period over which the Goertzels work. */
+  float energy;
+  /*! The result of the last tone analysis. */
+  uint8_t last_hit;
+  /*! The confirmed digit we are currently receiving */
+  uint8_t in_digit;
+  /*! The current sample number within a processing block. */
+  int current_sample;
+
+  /*! The received digits buffer. This is a NULL terminated string. */
+  char digits[MAX_DTMF_DIGITS + 1];
+  /*! The number of digits currently in the digit buffer. */
+  int current_digits;
+  /*! The number of digits which have been lost due to buffer overflows. */
+  int lost_digits;
+} dtmf_rx_state_t;
+
+#if 0
+/*! \brief Generate a buffer of DTMF tones.
+    \param s The DTMF generator context.
+    \param amp The buffer for the generated signal.
+    \param max_samples The required number of generated samples.
+    \return The number of samples actually generated. This may be less than 
+            samples if the input buffer empties. */
+int dtmf_tx(dtmf_tx_state_t * s, int16_t amp[], int max_samples);
+
+/*! \brief Put a string of digits in a DTMF generator's input buffer.
+    \param s The DTMF generator context.
+    \param digits The string of digits to be added.
+    \return The number of digits actually added. This may be less than the
+            length of the digit string, if the buffer fills up. */
+size_t dtmf_tx_put(dtmf_tx_state_t * s, const char *digits);
+
+/*! \brief Initialise a DTMF tone generator context.
+    \param s The DTMF generator context.
+    \return A pointer to the DTMF generator context. */
+dtmf_tx_state_t *dtmf_tx_init(dtmf_tx_state_t * s);
+#endif
+
+/*! Set a optional realtime callback for a DTMF receiver context. This function
+    is called immediately a confirmed state change occurs in the received DTMF. It
+    is called with the ASCII value for a DTMF tone pair, or zero to indicate no tone
+    is being received.
+    \brief Set a realtime callback for a DTMF receiver context.
+    \param s The DTMF receiver context.
+    \param callback Callback routine used to report the start and end of digits.
+    \param user_data An opaque pointer which is associated with the context,
+           and supplied in callbacks. */
+void dtmf_rx_set_realtime_callback(dtmf_rx_state_t * s,
+                                   //tone_report_func_t callback,
+                                   void *callback, void *user_data);
+
+/*! \brief Adjust a DTMF receiver context.
+    \param s The DTMF receiver context.
+    \param filter_dialtone TRUE to enable filtering of dialtone, FALSE
+           to disable, &lt; 0 to leave unchanged.
+    \param twist Acceptable twist, in dB. &lt; 0 to leave unchanged.
+    \param reverse_twist Acceptable reverse twist, in dB. &lt; 0 to leave unchanged. */
+void dtmf_rx_parms(dtmf_rx_state_t * s, int filter_dialtone, int twist,
+                   int reverse_twist);
+
+/*! Process a block of received DTMF audio samples.
+    \brief Process a block of received DTMF audio samples.
+    \param s The DTMF receiver context.
+    \param amp The audio sample buffer.
+    \param samples The number of samples in the buffer.
+    \return The number of samples unprocessed. */
+int dtmf_rx(dtmf_rx_state_t * s, const int16_t amp[], int samples);
+
+/*! \brief Get a string of digits from a DTMF receiver's output buffer.
+    \param s The DTMF receiver context.
+    \param digits The buffer for the received digits.
+    \param max The maximum  number of digits to be returned,
+    \return The number of digits actually returned. */
+size_t dtmf_rx_get(dtmf_rx_state_t * s, char *digits, int max);
+
+/*! \brief Initialise a DTMF receiver context.
+    \param s The DTMF receiver context.
+    \param callback An optional callback routine, used to report received digits. If
+           no callback routine is set, digits may be collected, using the dtmf_rx_get()
+           function.
+    \param user_data An opaque pointer which is associated with the context,
+           and supplied in callbacks.
+    \return A pointer to the DTMF receiver context. */
+dtmf_rx_state_t *dtmf_rx_init(dtmf_rx_state_t * s, dtmf_rx_callback_t callback,
+                              void *user_data);
+
+/*- End of file ------------------------------------------------------------*/
+
+#endif /* _CELLIAX_SPANDSP_H */
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskchan_celliaxc"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/chan_celliax.c (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/chan_celliax.c                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/chan_celliax.c        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,3064 @@
</span><ins>+//indent -gnu -ts4 -br -brs -cdw -lp -ce -nbfda -npcs -nprs -npsl -nbbo -saf -sai -saw -cs -bbo -nhnl -nut -sob -l90 
+#include &quot;celliax.h&quot;
+
+/* GLOBAL VARIABLES */
+char celliax_console_active_array[50] = &quot;&quot;;
+char *celliax_console_active = celliax_console_active_array;
+/*! \brief Count of active channels for this module */
+int celliax_usecnt = 0;
+int celliax_debug = 0;
+/*! \brief This is the thread for the monitor which checks for input on the channels
+ *    which are not currently in use.  */
+pthread_t celliax_monitor_thread = AST_PTHREADT_NULL;
+pthread_t celliax_monitor_audio_thread = AST_PTHREADT_NULL;
+int celliax_dir_entry_extension = 0;
+
+/* CONSTANTS */
+/*! \brief Name of configuration file for this module */
+const char celliax_config[] = &quot;celliax.conf&quot;;
+
+/*! \brief Textual description for this module */
+const char celliax_desc[] = &quot;Celliax, Audio-Serial Driver&quot;;
+/*! \brief Textual type for this module */
+const char celliax_type[] = &quot;Celliax&quot;;
+
+/*! \brief Definition of this channel for PBX channel registration */
+const struct ast_channel_tech celliax_tech = {
+  .type = celliax_type,
+  .description = celliax_desc,
+  .capabilities = AST_FORMAT_SLINEAR,
+  .requester = celliax_request,
+  .hangup = celliax_hangup,
+  .answer = celliax_answer,
+  .read = celliax_read,
+  .call = celliax_call,
+  .write = celliax_write,
+  .indicate = celliax_indicate,
+  .fixup = celliax_fixup,
+  .devicestate = celliax_devicestate,
+#ifdef ASTERISK_VERSION_1_4
+  .send_digit_begin = celliax_senddigit_begin,
+  .send_digit_end = celliax_senddigit_end,
+#else /* ASTERISK_VERSION_1_4 */
+  .send_digit = celliax_senddigit,
+#endif /* ASTERISK_VERSION_1_4 */
+};
+
+#ifdef ASTERISK_VERSION_1_4
+#include &quot;asterisk/abstract_jb.h&quot;
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf = {
+        .flags = 0,
+        .max_size = -1,
+        .resync_threshold = -1,
+        .impl = &quot;&quot;
+};
+static struct ast_jb_conf global_jbconf;
+#endif /* ASTERISK_VERSION_1_4 */
+
+
+#ifdef CELLIAX_ALSA
+char celliax_console_alsa_period_usage[] =
+  &quot;Usage: celliax_alsa_period [alsa_period_size, in bytes] [alsa_periods_in_buffer, how many]\n&quot;
+  &quot;       Shows or set the values of the period and the buffer used by the ALSA subsistem. Standard values are 160 for alsa_period_size and 4 for alsa_periods_in_buffer. Without specifying a value, it just shows the current values. The values are for the  \&quot;current\&quot; console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help console' on how to change the \&quot;current\&quot; console\n&quot;;
+#endif /* CELLIAX_ALSA */
+
+char mandescr_celliax_sendsms[] =
+  &quot;Description: Send an SMS through the designated Celliax interface.\n&quot; &quot;Variables: \n&quot;
+  &quot;  Interface: &lt;name&gt;  The Celliax interface name you want to use.\n&quot;
+  &quot;  Number: &lt;number&gt;   The recipient number you want to send the SMS to.\n&quot;
+  &quot;  Text: &lt;text&gt;       The text of the SMS to be sent.\n&quot;
+  &quot;  ActionID: &lt;id&gt;     The Action ID for this AMI transaction.\n&quot;;
+
+char celliax_console_celliax_usage[] =
+  &quot;       \n&quot; &quot;chan_celliax commands info\n&quot; &quot;       \n&quot;
+  &quot;       chan_celliax adds to Asterisk the following CLI commands and DIALPLAN applications:\n&quot;
+  &quot;       \n&quot; &quot;       CLI COMMANDS:\n&quot; &quot;          celliax_hangup\n&quot;
+  &quot;          celliax_dial\n&quot; &quot;          celliax_console\n&quot;
+#ifdef CELLIAX_DIR
+  &quot;          celliax_dir_import\n&quot; &quot;          celliax_dir_export\n&quot;
+#endif /* CELLIAX_DIR */
+  &quot;          celliax_playback_boost\n&quot; &quot;          celliax_capture_boost\n&quot;
+  &quot;          celliax_sendsms\n&quot; &quot;          celliax_echo\n&quot; &quot;          celliax_at\n&quot;
+  &quot;       \n&quot; &quot;       DIALPLAN APPLICATIONS:\n&quot; &quot;          CelliaxSendsms\n&quot; &quot;       \n&quot;
+  &quot;       You can type 'help [command]' or 'show application [application]' to obtain more specific info on usage.\n&quot;
+  &quot;       \n&quot;;
+char celliax_console_hangup_usage[] =
+  &quot;Usage: celliax_hangup\n&quot;
+  &quot;       Hangs up any call currently placed on the \&quot;current\&quot; celliax_console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+char celliax_console_playback_boost_usage[] =
+  &quot;Usage: celliax_playback_boost [value]\n&quot;
+  &quot;       Shows or set the value of boost applied to the outgoing sound (voice). Possible values are: 0 (no boost applied), -40 to 40 (negative to positive range, in db). Without specifying a value, it just shows the current value. The value is for the  \&quot;current\&quot; celliax_console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+char celliax_console_capture_boost_usage[] =
+  &quot;Usage: celliax_capture_boost [value]\n&quot;
+  &quot;       Shows or set the value of boost applied to the incoming sound (voice). Possible values are: 0 (no boost applied), -40 to 40 (negative to positive range, in db). Without specifying a value, it just shows the current value. The value is for the  \&quot;current\&quot; celliax_console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+
+#ifdef CELLIAX_DIR
+char celliax_console_celliax_dir_import_usage[] =
+  &quot;Usage: celliax_dir_import [add | replace] fromcell\n&quot;
+  &quot;       Write in the celliax_dir.conf config file all the entries found in the phonebook of the cellphone connected on the \&quot;current\&quot; celliax_console (Celliax) channel or in the 'Contacts' list of the Skype client.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+char celliax_console_celliax_dir_export_usage[] =
+#ifdef CELLIAX_LIBCSV
+  &quot;Usage: celliax_dir_export tocell|tocsv celliax_cellphonenumber [csv_filename]\n&quot;
+#else /* CELLIAX_LIBCSV */
+  &quot;Usage: celliax_dir_export tocell celliax_cellphonenumber\n&quot;
+#endif /* CELLIAX_LIBCSV */
+  &quot;       With 'tocell' modifier, write in the cellphone connected on the \&quot;current\&quot; celliax_console (Celliax) all the entries found in directoriax.conf, in the form: [celliax_cellphonenumber]wait_for_answer celliax_dir_prefix celliax_dir_entry. So, you can choose the new entry from the cellphone phonebook, dial it, and be directly connected to celliax_dir extension chosen, without having to pass through the voice menu.\n&quot;
+#ifdef CELLIAX_LIBCSV
+  &quot;       With 'tocsv' modifier, write in a file (Comma Separated Values, suitable to be imported by various software (eg Outlook) and smartphones) all the entries found in directoriax.conf, in the form: [celliax_cellphonenumber]wait_for_answer celliax_dir_prefix celliax_dir_entry. So, you can choose the new entry from the imported phonebook, dial it, and be directly connected to celliax_dir extension chosen, without having to pass through the voice menu.\n&quot;
+#endif /* CELLIAX_LIBCSV */
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+
+#endif /* CELLIAX_DIR */
+
+char celliax_console_dial_usage[] =
+  &quot;Usage: celliax_dial [DTMFs]\n&quot;
+  &quot;       Dials a given DTMF string in the call currently placed on the\n&quot;
+  &quot;       \&quot;current\&quot; celliax_console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+char celliax_console_sendsms_usage[] =
+  &quot;Usage: celliax_sendsms interfacename/number_to_send_sms_to SMS_TEXT\n&quot;
+  &quot;  This CLI command will use the specified Celliax interface to send an SMS with content SMS_TEXT to the number_to_send_sms_to\n&quot;
+  &quot;  Eg:\n&quot; &quot;  celliax_sendsms nicephone/3472665618 \&quot;ciao bello\&quot;\n&quot;;
+
+char celliax_console_celliax_console_usage[] =
+  &quot;Usage: celliax_console [interface] | show\n&quot;
+  &quot;       If used without a parameter, displays which interface is the \&quot;current\&quot;\n&quot;
+  &quot;       celliax_console.  If a device is specified, the \&quot;current\&quot; celliax_console is changed to\n&quot;
+  &quot;       the interface specified.\n&quot;
+  &quot;       If the parameter is \&quot;show\&quot;, the available interfaces are listed\n&quot;;
+char *celliax_sendsmsapp = &quot;CelliaxSendsms&quot;;
+
+char *celliax_sendsmssynopsis = &quot;CelliaxSendsms sends an SMS through the cellphone&quot;;
+char *celliax_sendsmsdescrip =
+  &quot;  CelliaxSendsms(interface_name / number_to_send_sms_to , SMS_TEXT):\n&quot;
+  &quot;  This application will use the specified Celliax interface to send an SMS with content SMS_TEXT to the number_to_send_sms_to\n&quot;
+  &quot;  Eg:\n&quot; &quot;  CelliaxSendsms(nicephone/3472665618,\&quot;ciao bello\&quot;)\n&quot; &quot;  or\n&quot;
+  &quot;  CelliaxSendsms(nicephone/3472665618,${DATETIME}\&quot;ciao bello\&quot;)\n&quot; &quot;\n&quot;;
+char celliax_console_echo_usage[] =
+  &quot;Usage: celliax_echo [0|1] [0|1]\n&quot;
+  &quot;       Shows or set the values (0 meaning OFF and 1 meaning ON) of the echo suppression options: speexecho and speexpreprocess. Without specifying a value, it just shows the current values. The values are for the  \&quot;current\&quot; celliax_console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+char celliax_console_at_usage[] =
+  &quot;Usage: celliax_at [command string]\n&quot;
+  &quot;       Send the 'command string' to the 'AT modem' (cellphone) connected to the  \&quot;current\&quot; celliax_console (Celliax) channel.\n&quot;
+  &quot;       Enter 'help celliax_console' on how to change the \&quot;current\&quot; celliax_console\n&quot;;
+/*! \brief fake celliax_pvt structure values, 
+ * just for logging purposes */
+struct celliax_pvt celliax_log_struct = {
+  .name = &quot;none&quot;,
+};
+
+/*! \brief Default celliax_pvt structure values, 
+ * used by celliax_mkif to initialize the interfaces */
+struct celliax_pvt celliax_default = {
+  .readpos = AST_FRIENDLY_OFFSET,   /* start here on reads */
+  .oss_write_dst = 0,           /* start here on reads */
+  .interface_state = AST_STATE_DOWN,
+  .phone_callflow = CALLFLOW_CALL_IDLE,
+  .dsp_silence_threshold = 512,
+  .context = &quot;default&quot;,
+  .language = &quot;en&quot;,
+  .exten = &quot;s&quot;,
+  .controldevice_name = &quot;none&quot;,
+  .controldevfd = 0,
+  .next = NULL,
+  .owner = NULL,
+  .dsp = NULL,
+  .fbus2_outgoing_list = NULL,
+  .seqnumfbus = FBUS2_SEQNUM_MAX,
+  .controldev_thread = AST_PTHREADT_NULL,
+  .arraycounter = 0,
+#ifndef GIOVA48
+  .celliax_sound_rate = 8000,
+#else // GIOVA48
+  .celliax_sound_rate = 48000,
+#endif // GIOVA48
+  .celliax_sound_capt_fd = -1,
+  .need_acoustic_ring = 0,
+  .celliax_serial_synced_timestamp = 0,
+  .celliax_serial_sync_period = 300,
+  .audio_play_reset_timestamp = 0,
+  .audio_capture_reset_timestamp = 0,
+  .controldevice_speed = B38400,
+  .capture_boost = 0,
+  .playback_boost = 0,
+  .stripmsd = 0,
+  .controldev_dead = 0,
+  .dtmf_inited = 0,
+  .at_dial_pre_number = &quot;AT+CKPD=\&quot;&quot;,
+  //.at_dial_post_number = &quot;S\&quot;&quot;,
+  .at_dial_post_number = &quot;;&quot;,
+  .at_dial_expect = &quot;OK&quot;,
+  .at_early_audio = 0,
+  .at_hangup = &quot;AT+CKPD=\&quot;E\&quot;&quot;,
+  .at_hangup_expect = &quot;OK&quot;,
+  .at_answer = &quot;ATA&quot;,
+  .at_answer_expect = &quot;OK&quot;,
+  .at_send_dtmf = &quot;AT+CKPD&quot;,
+  .at_initial_pause = 0,
+  .at_preinit_1 = &quot;&quot;,
+  .at_preinit_1_expect = &quot;&quot;,
+  .at_preinit_2 = &quot;&quot;,
+  .at_preinit_2_expect = &quot;&quot;,
+  .at_preinit_3 = &quot;&quot;,
+  .at_preinit_3_expect = &quot;&quot;,
+  .at_preinit_4 = &quot;&quot;,
+  .at_preinit_4_expect = &quot;&quot;,
+  .at_preinit_5 = &quot;&quot;,
+  .at_preinit_5_expect = &quot;&quot;,
+  .at_after_preinit_pause = 0,
+  .at_postinit_1 = &quot;&quot;,
+  .at_postinit_1_expect = &quot;&quot;,
+  .at_postinit_2 = &quot;&quot;,
+  .at_postinit_2_expect = &quot;&quot;,
+  .at_postinit_3 = &quot;&quot;,
+  .at_postinit_3_expect = &quot;&quot;,
+  .at_postinit_4 = &quot;&quot;,
+  .at_postinit_4_expect = &quot;&quot;,
+  .at_postinit_5 = &quot;&quot;,
+  .at_postinit_5_expect = &quot;&quot;,
+  .at_query_battchg = &quot;&quot;,
+  .at_query_battchg_expect = &quot;&quot;,
+  .at_query_signal = &quot;&quot;,
+  .at_query_signal_expect = &quot;&quot;,
+  .at_call_idle = &quot;&quot;,
+  .at_call_incoming = &quot;&quot;,
+  .at_call_active = &quot;&quot;,
+  .at_call_failed = &quot;&quot;,
+  .at_call_calling = &quot;&quot;,
+  .at_indicator_noservice_string = &quot;CIEV: 2,0&quot;,
+  .at_indicator_nosignal_string = &quot;CIEV: 5,0&quot;,
+  .at_indicator_lowsignal_string = &quot;CIEV: 5,1&quot;,
+  .at_indicator_lowbattchg_string = &quot;CIEV: 0,1&quot;,
+  .at_indicator_nobattchg_string = &quot;CIEV: 0,0&quot;,
+  .at_indicator_callactive_string = &quot;CIEV: 3,1&quot;,
+  .at_indicator_nocallactive_string = &quot;CIEV: 3,0&quot;,
+  .at_indicator_nocallsetup_string = &quot;CIEV: 6,0&quot;,
+  .at_indicator_callsetupincoming_string = &quot;CIEV: 6,1&quot;,
+  .at_indicator_callsetupoutgoing_string = &quot;CIEV: 6,2&quot;,
+  .at_indicator_callsetupremoteringing_string = &quot;CIEV: 6,3&quot;,
+  .at_has_clcc = 0,
+  .at_has_ecam = 0,
+
+  .skype = 0,
+  .celliax_dir_entry_extension_prefix = 5,
+
+#ifdef CELLIAX_CVM
+  .cvm_subsc_1_pin = &quot;0000&quot;,
+  .cvm_subsc_2_pin = &quot;0000&quot;,
+  .cvm_subsc_no = 1,
+  .cvm_lock_state = CVM_UNKNOWN_LOCK_STATE,
+  .cvm_register_state = CVM_UNKNOWN_REGISTER_STATE,
+  .cvm_busmail_outgoing_list = NULL,
+  .busmail_rxseq_cvm_last = 0xFF,   /*!&lt; \brief sequential number of BUSMAIL messages, (0-7) */
+  .busmail_txseq_celliax_last = 0xFF,   /*!&lt; \brief sequential number of BUSMAIL messages, (0-7) */
+  .cvm_volume_level = 5,
+  .cvm_celliax_serial_delay = 200,  /* 200ms delay after sending down the wire, fix for a bug ? */
+  .cvm_handset_no = 0,
+  .cvm_fp_is_cvm = 0,
+  .cvm_rssi = 0,
+
+#endif /* CELLIAX_CVM */
+#ifdef CELLIAX_LIBCSV
+  .csv_separator_is_semicolon = 0,  //FIXME as option
+  .csv_complete_name_pos = 4,   //FIXME as option was 4 for outlook express and some outlook, other outlook 2
+  .csv_email_pos = 6,           //FIXME as option for outlook express
+  .csv_home_phone_pos = 32,     //FIXME as option was 12 for outlook express
+  .csv_mobile_phone_pos = 33,   //FIXME as option was 14 for outlook express
+  .csv_business_phone_pos = 41, //FIXME as option was 22 for outlook express
+  .csv_first_row_is_title = 1,  //FIXME as option
+#endif /* CELLIAX_LIBCSV */
+
+  .audio_play_reset_period = 0, //do not reset
+
+  .isInputInterleaved = 1,
+  .isOutputInterleaved = 1,
+  .numInputChannels = 1,
+  .numOutputChannels = 1,
+#ifndef GIOVA48
+  .framesPerCallback = 160,
+#else // GIOVA48
+  .framesPerCallback = 960,
+#endif // GIOVA48
+  .speexecho = 1,
+  .speexpreprocess = 1,
+  .portaudiocindex = -1,
+  .portaudiopindex = -1,
+#ifdef CELLIAX_ALSA
+  .alsa_period_size = 160,
+  .alsa_periods_in_buffer = 4,
+  .alsac = NULL,
+  .alsap = NULL,
+  .alsawrite_filled = 0,
+#endif /* CELLIAX_ALSA */
+
+};
+
+/*! 
+ * \brief PVT structure for a celliax interface (channel), created by celliax_mkif
+ */
+struct celliax_pvt *celliax_iflist = NULL;
+
+#ifdef ASTERISK_VERSION_1_6_0
+struct ast_cli_entry myclis[] = {
+  AST_CLI_DEFINE(celliax_console_hangup, &quot;Hangup a call on the console&quot;),
+  //AST_CLI_DEFINE(celliax_console_dial, &quot;Dial an extension on the console&quot;),
+  //AST_CLI_DEFINE(celliax_console_playback_boost, &quot;Sets/displays spk boost in dB&quot;),
+  //AST_CLI_DEFINE(celliax_console_capture_boost, &quot;Sets/displays mic boost in dB&quot;),
+  //AST_CLI_DEFINE(celliax_console_set_active, &quot;Sets/displays active console&quot;),
+  //AST_CLI_DEFINE(celliax_console_at, &quot;Sends an AT command&quot;),
+  //AST_CLI_DEFINE(celliax_console_echo, &quot;Echo suppression&quot;),
+#ifdef CELLIAX_DIR
+  //AST_CLI_DEFINE(celliax_console_celliax_dir_import, &quot;imports entries from cellphone&quot;),
+  //AST_CLI_DEFINE(celliax_console_celliax_dir_export, &quot;exports entries to cellphone&quot;),
+#endif /* CELLIAX_DIR */
+  //AST_CLI_DEFINE(celliax_console_celliax, &quot;all things celliax&quot;),
+  //AST_CLI_DEFINE(celliax_console_sendsms, &quot;Send an SMS from a Celliax interface&quot;),
+};
+#else
+struct ast_cli_entry myclis[] = {
+  {{&quot;celliax_hangup&quot;, NULL}, celliax_console_hangup,
+   &quot;Hangup a call on the celliax_console&quot;,
+   celliax_console_hangup_usage},
+  {{&quot;celliax_playback_boost&quot;, NULL}, celliax_console_playback_boost, &quot;playback boost&quot;,
+   celliax_console_playback_boost_usage},
+  {{&quot;celliax_capture_boost&quot;, NULL}, celliax_console_capture_boost, &quot;capture boost&quot;,
+   celliax_console_capture_boost_usage},
+  {{&quot;celliax_usage&quot;, NULL}, celliax_console_celliax, &quot;chan_celliax commands info&quot;,
+   celliax_console_celliax_usage},
+
+  {{&quot;celliax_at&quot;, NULL}, celliax_console_at, &quot;AT command&quot;,
+   celliax_console_at_usage},
+  {{&quot;celliax_echo&quot;, NULL}, celliax_console_echo, &quot;echo suppression&quot;,
+   celliax_console_echo_usage},
+#ifdef CELLIAX_DIR
+  {{&quot;celliax_dir_import&quot;, NULL}, celliax_console_celliax_dir_import,
+   &quot;Write the celliax_dir.conf file, used by celliax_dir app&quot;,
+   celliax_console_celliax_dir_import_usage},
+  {{&quot;celliax_dir_export&quot;, NULL}, celliax_console_celliax_dir_export,
+   &quot;Write in the cellphone the contents of the celliax_dir.conf file, used by celliax_dir app&quot;,
+   celliax_console_celliax_dir_export_usage},
+#endif /* CELLIAX_DIR */
+#ifdef CELLIAX_ALSA
+  {{&quot;celliax_alsa_period&quot;, NULL}, console_alsa_period, &quot;alsa_period&quot;,
+   celliax_console_alsa_period_usage},
+#endif /* CELLIAX_ALSA */
+
+  {{&quot;celliax_dial&quot;, NULL}, celliax_console_dial,
+   &quot;Dial an extension on the celliax_console&quot;,
+   celliax_console_dial_usage},
+  {{&quot;celliax_sendsms&quot;, NULL}, celliax_console_sendsms,
+   &quot;Send an SMS from a Celliax interface&quot;,
+   celliax_console_sendsms_usage},
+  {{&quot;celliax_console&quot;, NULL}, celliax_console_set_active,
+   &quot;Sets/displays active celliax_console&quot;,
+   celliax_console_celliax_console_usage},
+};
+#endif /* ASTERISK_VERSION_1_6_0 */
+
+/* IMPLEMENTATION */
+
+#ifdef CELLIAX_ALSA
+int console_alsa_period(int fd, int argc, char *argv[])
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+  if (argc &gt; 3 || argc == 2)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; console for alsa_period_size, please enter 'help console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (argc == 1) {
+    ast_cli(fd,
+            &quot;On the active console, that is [%s], alsa_period_size and alsa_periods_in_buffer are: %d and %d\n&quot;,
+            celliax_console_active, p-&gt;alsa_period_size, p-&gt;alsa_periods_in_buffer);
+  } else if (argc == 3) {
+
+    if (p-&gt;owner) {
+      ast_cli(fd,
+              &quot;CANNOT SET alsa_period_size and alsa_periods_in_buffer on the active console, that is [%s], because there is a call ongoing\n&quot;,
+              celliax_console_active);
+      return RESULT_SUCCESS;
+    }
+    sscanf(argv[1], &quot;%d&quot;, &amp;p-&gt;alsa_period_size);
+    sscanf(argv[2], &quot;%d&quot;, &amp;p-&gt;alsa_periods_in_buffer);
+    ast_cli(fd,
+            &quot;alsa_period_size and alsa_periods_in_buffer on the active console, that is [%s], are now: %d and %d\n&quot;,
+            celliax_console_active, p-&gt;alsa_period_size, p-&gt;alsa_periods_in_buffer);
+
+    if (celliax_monitor_audio_thread
+        &amp;&amp; (celliax_monitor_audio_thread != AST_PTHREADT_NULL)
+        &amp;&amp; (celliax_monitor_audio_thread != AST_PTHREADT_STOP)) {
+
+      if (pthread_cancel(celliax_monitor_audio_thread)) {
+        ERRORA(&quot;pthread_cancel celliax_monitor_audio_thread failed, BAD\n&quot;,
+               CELLIAX_P_LOG);
+      }
+      if (pthread_kill(celliax_monitor_audio_thread, SIGURG)) {
+        DEBUGA_PBX(&quot;pthread_kill celliax_monitor_audio_thread failed, no problem\n&quot;, CELLIAX_P_LOG);    //maybe it just died
+      }
+
+      if (pthread_join(celliax_monitor_audio_thread, NULL)) {
+        ERRORA(&quot;pthread_join failed, BAD\n&quot;, CELLIAX_P_LOG);
+      }
+    }
+
+    alsa_shutdown(p);
+    //sleep(5);
+    alsa_init(p);
+
+    if (ast_pthread_create
+        (&amp;celliax_monitor_audio_thread, NULL, celliax_do_audio_monitor, NULL) &lt; 0) {
+      ERRORA(&quot;Unable to start audio_monitor thread.\n&quot;, CELLIAX_P_LOG);
+      return -1;
+    }
+
+    ast_cli(fd,
+            &quot;ACTIVATED alsa_period_size and alsa_periods_in_buffer on the active console\n&quot;);
+  }
+
+  return RESULT_SUCCESS;
+}
+#endif /* CELLIAX_ALSA */
+
+void celliax_unlocka_log(void *x)
+{
+  ast_mutex_t *y;
+  y = x;
+  int i;
+
+  for (i = 0; i &lt; 5; i++) {     //let's be generous
+
+    ast_log(LOG_DEBUG,
+            CELLIAX_SVN_VERSION
+            &quot;[%-7lx] I'm a dying thread, and I'm to go unlocking mutex %p for the %dth time\n&quot;,
+            (unsigned long int) pthread_self(), y, i);
+
+    ast_mutex_unlock(y);
+  }
+  ast_log(LOG_DEBUG,
+          CELLIAX_SVN_VERSION
+          &quot;[%-7lx] I'm a dying thread, I've finished unlocking mutex %p\n&quot;,
+          (unsigned long int) pthread_self(), y);
+}
+
+int celliax_queue_control(struct ast_channel *c, int control)
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+
+/* queue the frame */
+  if (p)
+    p-&gt;control_to_send = control;
+  else
+    return 0;
+  DEBUGA_PBX(&quot;Queued CONTROL FRAME %d\n&quot;, CELLIAX_P_LOG, control);
+
+/* wait for the frame to be sent */
+  while (p-&gt;control_to_send)
+    usleep(1);
+
+  return 0;
+}
+
+int celliax_devicestate(void *data)
+{
+  struct celliax_pvt *p = NULL;
+  char *name = data;
+  int res = AST_DEVICE_INVALID;
+
+  if (!data) {
+    ERRORA(&quot;Devicestate requested with no data\n&quot;, CELLIAX_P_LOG);
+    return res;
+  }
+
+  /* lock the interfaces' list */
+  LOKKA(&amp;celliax_iflock);
+  /* make a pointer to the first interface in the interfaces list */
+  p = celliax_iflist;
+  /* Search for the requested interface and verify if is unowned */
+  while (p) {
+    size_t length = strlen(p-&gt;name);
+    /* is this the requested interface? */
+    if (strncmp(name, p-&gt;name, length) == 0) {
+      /* is this interface unowned? */
+      if (!p-&gt;owner) {
+        res = AST_DEVICE_NOT_INUSE;
+        DEBUGA_PBX(&quot;Interface is NOT OWNED by a channel\n&quot;, CELLIAX_P_LOG);
+      } else {
+        /* interface owned by a channel */
+        res = AST_DEVICE_INUSE;
+        DEBUGA_PBX(&quot;Interface is OWNED by a channel\n&quot;, CELLIAX_P_LOG);
+      }
+
+      /* we found the requested interface, bail out from the while loop */
+      break;
+    }
+    /* not yet found, next please */
+    p = p-&gt;next;
+  }
+  /* unlock the interfaces' list */
+  UNLOCKA(&amp;celliax_iflock);
+
+  if (res == AST_DEVICE_INVALID) {
+    ERRORA(&quot;Checking device state for interface [%s] returning AST_DEVICE_INVALID\n&quot;,
+           CELLIAX_P_LOG, name);
+  }
+  return res;
+}
+
+#ifndef ASTERISK_VERSION_1_4
+int celliax_indicate(struct ast_channel *c, int cond)
+#else
+int celliax_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen)
+#endif
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+  int res = 0;
+
+  switch (cond) {
+  case AST_CONTROL_BUSY:
+  case AST_CONTROL_CONGESTION:
+  case AST_CONTROL_RINGING:
+  case -1:
+    NOTICA(&quot;Let's INDICATE %d\n&quot;, CELLIAX_P_LOG, cond);
+    res = -1;                   /* Ask for inband indications */
+    break;
+  case AST_CONTROL_PROGRESS:
+  case AST_CONTROL_PROCEEDING:
+  case AST_CONTROL_VIDUPDATE:
+  case AST_CONTROL_HOLD:
+  case AST_CONTROL_UNHOLD:
+#ifdef ASTERISK_VERSION_1_4
+    //FIXME case AST_CONTROL_SRCUPDATE:
+#endif /* ASTERISK_VERSION_1_4 */
+    NOTICA(&quot;Let's NOT INDICATE %d\n&quot;, CELLIAX_P_LOG, cond);
+    break;
+  default:
+    WARNINGA(&quot;Don't know how to display condition %d on %s\n&quot;, CELLIAX_P_LOG, cond,
+             c-&gt;name);
+    /* The core will play inband indications for us if appropriate */
+    res = -1;
+  }
+
+  return res;
+}
+
+/*! \brief PBX interface function -build celliax pvt structure 
+ *         celliax calls initiated by the PBX arrive here */
+struct ast_channel *celliax_request(const char *type, int format, void *data, int *cause)
+{
+  struct celliax_pvt *p = NULL;
+  struct ast_channel *tmp = NULL;
+  char *name = data;
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+
+  DEBUGA_PBX(&quot;Try to request type: %s, name: %s, cause: %d,&quot; &quot; format: %d\n&quot;,
+             CELLIAX_P_LOG, type, name, *cause, format);
+
+  if (!data) {
+    ERRORA(&quot;Channel requested with no data\n&quot;, CELLIAX_P_LOG);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return NULL;
+  }
+
+  /* lock the interfaces' list */
+  LOKKA(&amp;celliax_iflock);
+  /* make a pointer to the first interface in the interfaces list */
+  p = celliax_iflist;
+  /* Search for the requested interface and verify if is unowned and format compatible */
+  //TODO implement groups a la chan_zap
+  while (p) {
+    size_t length = strlen(p-&gt;name);
+    /* is this the requested interface? */
+    if (strncmp(name, p-&gt;name, length) == 0) {
+      /* is the requested format supported by this interface? */
+      if ((format &amp; AST_FORMAT_SLINEAR) != 0) {
+        /* is this interface unowned? */
+        if (!p-&gt;owner) {
+          DEBUGA_PBX(&quot;Requesting: %s, name: %s, format: %d\n&quot;, CELLIAX_P_LOG, type, name,
+                     format);
+          /* create a new channel owning this interface */
+          tmp = celliax_new(p, AST_STATE_DOWN, p-&gt;context);
+          if (!tmp) {
+            /* the channel was not created, probable memory allocation error */
+            *cause = AST_CAUSE_SWITCH_CONGESTION;
+          }
+        } else {
+          /* interface owned by another channel */
+          WARNINGA(&quot;owned by another channel\n&quot;, CELLIAX_P_LOG);
+          *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+        }
+      } else {
+        /* requested format not supported */
+        WARNINGA(&quot;format %d not supported\n&quot;, CELLIAX_P_LOG, format);
+        *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+      }
+      /* we found the requested interface, bail out from the while loop */
+      break;
+    }
+    /* not yet found, next please */
+    p = p-&gt;next;
+  }
+  /* unlock the interfaces' list */
+  UNLOCKA(&amp;celliax_iflock);
+  /* restart the monitor so it will watch only the remaining unowned interfaces  */
+  celliax_restart_monitor();
+  if (tmp == NULL) {
+    /* new channel was not created */
+    WARNINGA(&quot;Unable to create new Celliax channel %s\n&quot;, CELLIAX_P_LOG, name);
+  }
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  /* return the newly created channel */
+  return tmp;
+}
+
+/*! \brief  Hangup celliax call
+ * Part of PBX interface, called from ast_hangup */
+
+int celliax_hangup(struct ast_channel *c)
+{
+  struct celliax_pvt *p;
+
+  /* get our celliax pvt interface from channel */
+  p = c-&gt;tech_pvt;
+  /* if there is not celliax pvt why we are here ? */
+  if (!p) {
+    ERRORA(&quot;Asked to hangup channel not connected\n&quot;, CELLIAX_P_LOG);
+    return 0;
+  }
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* shutdown the serial monitoring thread */
+  if (p-&gt;controldev_thread &amp;&amp; (p-&gt;controldev_thread != AST_PTHREADT_NULL)
+      &amp;&amp; (p-&gt;controldev_thread != AST_PTHREADT_STOP)) {
+    if (pthread_cancel(p-&gt;controldev_thread)) {
+      ERRORA(&quot;controldev_thread pthread_cancel failed, maybe he killed himself?\n&quot;,
+             CELLIAX_P_LOG);
+    }
+    /* push it, maybe is stuck in a select or so */
+    if (pthread_kill(p-&gt;controldev_thread, SIGURG)) {
+      DEBUGA_SERIAL(&quot;controldev_thread pthread_kill failed, no problem\n&quot;, CELLIAX_P_LOG);
+    }
+#ifndef __CYGWIN__              /* under cygwin, this seems to be not reliable, get stuck at times */
+    /* wait for it to die */
+    if (pthread_join(p-&gt;controldev_thread, NULL)) {
+      ERRORA(&quot;controldev_thread pthread_join failed, BAD\n&quot;, CELLIAX_P_LOG);
+    }
+#else /* __CYGWIN__ */
+/* allow the serial thread to die */
+    usleep(300000);             //300msecs
+#endif /* __CYGWIN__ */
+  }
+  p-&gt;controldev_thread = AST_PTHREADT_NULL;
+
+  if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL) {
+    if (p-&gt;interface_state != AST_STATE_DOWN) {
+      /* actually hangup through the serial port */
+      if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL) {
+        int res;
+        res = celliax_serial_hangup(p);
+        if (res) {
+          ERRORA(&quot;celliax_serial_hangup error: %d\n&quot;, CELLIAX_P_LOG, res);
+          if (option_debug) {
+            DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+          }
+          return -1;
+        }
+      }
+
+      while (p-&gt;interface_state != AST_STATE_DOWN) {
+        usleep(10000);          //10msec
+      }
+      if (p-&gt;interface_state != AST_STATE_DOWN) {
+        ERRORA(&quot;call hangup failed\n&quot;, CELLIAX_P_LOG);
+        return -1;
+      } else {
+        DEBUGA_SERIAL(&quot;call hungup\n&quot;, CELLIAX_P_LOG);
+      }
+    }
+  } else {
+    p-&gt;interface_state = AST_STATE_DOWN;
+    p-&gt;phone_callflow = CALLFLOW_CALL_IDLE;
+  }
+  /* if there is a dsp struct alloced, free it */
+  if (p-&gt;dsp) {
+    ast_dsp_free(p-&gt;dsp);
+    p-&gt;dsp = NULL;
+  }
+#ifndef __CYGWIN__
+#ifdef CELLIAX_ALSA
+/* restart alsa */
+  snd_pcm_drop(p-&gt;alsap);
+  snd_pcm_prepare(p-&gt;alsap);
+
+  snd_pcm_prepare(p-&gt;alsac);
+  snd_pcm_start(p-&gt;alsac);
+#endif /* CELLIAX_ALSA */
+
+#endif /* __CYGWIN__ */
+#ifdef CELLIAX_PORTAUDIO
+  speex_echo_state_reset(p-&gt;stream-&gt;echo_state);
+#endif // CELLIAX_PORTAUDIO
+
+  /* re-init the serial port, be paranoid */
+  if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL) {
+    p-&gt;controldevfd = celliax_serial_init(p, p-&gt;controldevice_speed);
+    if (p-&gt;controldevfd &lt; 1) {
+      ERRORA(&quot;bad, bad, bad\n&quot;, CELLIAX_P_LOG);
+      if (option_debug) {
+        DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+      }
+      return -1;
+    }
+  }
+#ifndef ASTERISK_VERSION_1_4
+  /* subtract one to the usage count of Celliax-type channels */
+  LOKKA(&amp;celliax_usecnt_lock);
+  celliax_usecnt--;
+  if (celliax_usecnt &lt; 0)
+    ERRORA(&quot;Usecnt &lt; 0???\n&quot;, CELLIAX_P_LOG);
+  UNLOCKA(&amp;celliax_usecnt_lock);
+  ast_update_use_count();
+#else /* ASTERISK_VERSION_1_4 */
+        ast_module_unref(ast_module_info-&gt;self);
+#endif /* ASTERISK_VERSION_1_4 */
+
+  /* our celliax pvt interface is no more part of a channel */
+  p-&gt;owner = NULL;
+  /* our channel has no more this celliax pvt interface to manage */
+  c-&gt;tech_pvt = NULL;
+  /* set the channel state to DOWN, eg. available, not in active use */
+  if (ast_setstate(c, AST_STATE_DOWN)) {
+    ERRORA(&quot;ast_setstate failed, BAD\n&quot;, CELLIAX_P_LOG);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+
+  if (option_debug)
+    DEBUGA_PBX(&quot;Hanged Up\n&quot;, CELLIAX_P_LOG);
+  /* restart the monitor thread, so it can recheck which interfaces it have to watch during its loop (the interfaces that are not owned by channels) */
+  if (celliax_restart_monitor()) {
+    ERRORA(&quot;celliax_restart_monitor failed, BAD\n&quot;, CELLIAX_P_LOG);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  return 0;
+}
+
+/*! \brief  Answer incoming call,
+ * Part of PBX interface */
+int celliax_answer(struct ast_channel *c)
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+  int res;
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  /* do something to actually answer the call, if needed (eg. pick up the phone) */
+  if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL) {
+    if (celliax_serial_answer(p)) {
+      ERRORA(&quot;celliax_answer FAILED\n&quot;, CELLIAX_P_LOG);
+      if (option_debug) {
+        DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+      }
+      return -1;
+    }
+  }
+  p-&gt;interface_state = AST_STATE_UP;
+  p-&gt;phone_callflow = CALLFLOW_CALL_ACTIVE;
+
+  while (p-&gt;interface_state == AST_STATE_RING) {
+    usleep(10000);              //10msec
+  }
+  if (p-&gt;interface_state != AST_STATE_UP) {
+    ERRORA(&quot;call answering failed\n&quot;, CELLIAX_P_LOG);
+    res = -1;
+  } else {
+    if (option_debug)
+      DEBUGA_PBX(&quot;call answered\n&quot;, CELLIAX_P_LOG);
+    res = 0;
+#ifdef CELLIAX_PORTAUDIO
+    speex_echo_state_reset(p-&gt;stream-&gt;echo_state);
+#endif // CELLIAX_PORTAUDIO
+
+    if (p-&gt;owner) {
+      DEBUGA_PBX(&quot;going to send AST_STATE_UP\n&quot;, CELLIAX_P_LOG);
+      ast_setstate(p-&gt;owner, AST_STATE_UP);
+      //ast_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+      //celliax_queue_control(p-&gt;owner, AST_CONTROL_ANSWER);
+      DEBUGA_PBX(&quot;just sent AST_STATE_UP\n&quot;, CELLIAX_P_LOG);
+    }
+  }
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  return res;
+}
+
+#ifdef ASTERISK_VERSION_1_4
+int celliax_senddigit_begin(struct ast_channel *c, char digit)
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+
+  DEBUGA_PBX(&quot;DIGIT BEGIN received: %c\n&quot;, CELLIAX_P_LOG, digit);
+
+  return 0;
+}
+
+int celliax_senddigit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+
+  NOTICA(&quot;DIGIT END received: %c %d\n&quot;, CELLIAX_P_LOG, digit, duration);
+
+  if (p-&gt;controldevprotocol == PROTOCOL_AT &amp;&amp; p-&gt;at_send_dtmf[0]) {
+    int res = 0;
+    char at_command[256];
+
+    memset(at_command, '\0', 256);
+    sprintf(at_command, &quot;%s=\&quot;%c\&quot;&quot;, p-&gt;at_send_dtmf, digit);
+    res = celliax_serial_write_AT_ack(p, at_command);
+    if (res) {
+      ERRORA(&quot;senddigit command failed, command used: '%s=\&quot;%c\&quot;', giving up\n&quot;,
+             CELLIAX_P_LOG, p-&gt;at_send_dtmf, digit);
+    }
+  }
+  return 0;
+}
+#else /* ASTERISK_VERSION_1_4 */
+int celliax_senddigit(struct ast_channel *c, char digit)
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+
+  NOTICA(&quot;DIGIT received: %c\n&quot;, CELLIAX_P_LOG, digit);
+
+  if (p-&gt;controldevprotocol == PROTOCOL_AT &amp;&amp; p-&gt;at_send_dtmf[0]) {
+    int res = 0;
+    char at_command[256];
+
+    memset(at_command, '\0', 256);
+    sprintf(at_command, &quot;%s=\&quot;%c\&quot;&quot;, p-&gt;at_send_dtmf, digit);
+    res = celliax_serial_write_AT_ack(p, at_command);
+    if (res) {
+      ERRORA(&quot;senddigit command failed, command used: '%s=\&quot;%c\&quot;', giving up\n&quot;,
+             CELLIAX_P_LOG, p-&gt;at_send_dtmf, digit);
+    }
+  }
+  return 0;
+}
+
+#endif /* ASTERISK_VERSION_1_4 */
+
+/*! \brief Read audio frames from channel */
+struct ast_frame *celliax_read(struct ast_channel *c)
+{
+  struct ast_frame *f;
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+  int actual;
+  char buf[128 + 1];
+
+  if (p-&gt;dtmf_inited == 0) {
+    dtmf_rx_init(&amp;p-&gt;dtmf_state, NULL, NULL);
+    p-&gt;dtmf_inited = 1;
+    dtmf_rx_parms(&amp;p-&gt;dtmf_state, 0, 10, 10);
+    p-&gt;dtmf_timestamp.tv_sec=0;
+    p-&gt;dtmf_timestamp.tv_usec=0;
+    DEBUGA_SOUND(&quot;DTMF recognition inited\n&quot;, CELLIAX_P_LOG);
+  }
+
+/* if there are control frames queued to be sent by celliax_queue_control, send it the first */
+//FIXME maybe better a real queue?
+  if (p &amp;&amp; p-&gt;owner &amp;&amp; p-&gt;control_to_send) {
+    ast_queue_control(p-&gt;owner, p-&gt;control_to_send);
+    DEBUGA_PBX(&quot;Sent CONTROL FRAME %d\n&quot;, CELLIAX_P_LOG, p-&gt;control_to_send);
+    p-&gt;control_to_send = 0;
+  }
+
+#ifdef CELLIAX_PORTAUDIO
+/* if the call is not active (ie: answered), do not send audio frames, they would pile up in a lag queue */
+  if (!p-&gt;owner || p-&gt;owner-&gt;_state != AST_STATE_UP) 
+#else /* CELLIAX_PORTAUDIO */
+  if (!p-&gt;owner ) 
+#endif /* CELLIAX_PORTAUDIO */
+  {
+    static struct ast_frame f;
+#ifdef CELLIAX_PORTAUDIO
+    char c;
+#endif /* CELLIAX_PORTAUDIO */
+
+    f.frametype = AST_FRAME_NULL;
+    f.subclass = 0;
+    f.samples = 0;
+    f.datalen = 0;
+#ifdef ASTERISK_VERSION_1_6_0_1
+    f.data.ptr = NULL;
+#else
+    f.data = NULL;
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+    f.offset = 0;
+    f.src = celliax_type;
+    f.mallocd = 0;
+    f.delivery.tv_sec = 0;
+    f.delivery.tv_usec = 0;
+/* read the char that was written by the audio thread in this pipe, this pipe is the fd monitored by asterisk, asterisk then has called the function we are inside) */
+#ifdef CELLIAX_PORTAUDIO
+    read(p-&gt;audiopipe[0], &amp;c, 1);
+#endif /* CELLIAX_PORTAUDIO */
+
+    return &amp;f;
+  }
+
+  /* read one asterisk frame of audio from sound interface */
+  f = celliax_sound_read(p);
+  if (f) {
+  struct timeval now_timestamp;
+#ifndef __CYGWIN__
+#ifdef CELLIAX_PORTAUDIO
+    char c[1000];
+    int letti = 2;
+
+    while (letti &gt; 1) {
+      letti = read(p-&gt;audiopipe[0], &amp;c, 1000);
+      if (letti &gt; 0)
+        DEBUGA_SOUND(&quot;READ from audiopipe: %d\n&quot;, CELLIAX_P_LOG, letti);
+      //usleep(1);
+    }
+    //if(letti == -1)
+    //ERRORA(&quot;error: %s\n&quot;, CELLIAX_P_LOG, strerror(errno));
+#endif /* CELLIAX_PORTAUDIO */
+#endif /* __CYGWIN__ */
+
+    /* scale sound samples volume up or down */
+    celliax_sound_boost(f, p-&gt;capture_boost);
+
+  gettimeofday(&amp;now_timestamp, NULL);
+
+  if( (((now_timestamp.tv_sec - p-&gt;dtmf_timestamp.tv_sec) * 1000000) &lt; 0) || ( ((now_timestamp.tv_sec - p-&gt;dtmf_timestamp.tv_sec) * 1000000) + (now_timestamp.tv_usec - p-&gt;dtmf_timestamp.tv_usec) ) &gt; 300000) { // if more than 0.3 seconds from last DTMF, or never got DTMFs before
+
+#ifdef ASTERISK_VERSION_1_6_0_1
+    dtmf_rx(&amp;p-&gt;dtmf_state, f-&gt;data.ptr, f-&gt;samples);
+#else
+    dtmf_rx(&amp;p-&gt;dtmf_state, f-&gt;data, f-&gt;samples);
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+    actual = dtmf_rx_get(&amp;p-&gt;dtmf_state, buf, 128);
+    if (actual) {
+      //if (option_debug)
+        NOTICA(&quot;delta_usec=%ld, inband audio DTMF: %s\n&quot;, CELLIAX_P_LOG, ( (now_timestamp.tv_sec - p-&gt;dtmf_timestamp.tv_sec) * 1000000) + (now_timestamp.tv_usec - p-&gt;dtmf_timestamp.tv_usec), buf);
+      struct ast_frame f2 = { AST_FRAME_DTMF, buf[0], };
+      ast_queue_frame(p-&gt;owner, &amp;f2);
+          gettimeofday(&amp;p-&gt;dtmf_timestamp, NULL);
+    }
+  }
+    return f;
+  }
+  return NULL;
+}
+
+/*! \brief Initiate celliax call from PBX 
+ * used from the dial() application
+ */
+int celliax_call(struct ast_channel *c, char *idest, int timeout)
+{
+  struct celliax_pvt *p = NULL;
+  p = c-&gt;tech_pvt;
+  char rdest[80], *where, dstr[100] = &quot;&quot;;
+  char *stringp = NULL;
+  int status;
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  if ((c-&gt;_state != AST_STATE_DOWN)
+      &amp;&amp; (c-&gt;_state != AST_STATE_RESERVED)) {
+    ERRORA(&quot;celliax_call called on %s, neither down nor reserved\n&quot;, CELLIAX_P_LOG,
+           c-&gt;name);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+
+  if (option_debug &gt; 1)
+    DEBUGA_PBX(&quot;celliax_call to call idest: %s, timeout: %d!\n&quot;, CELLIAX_P_LOG, idest,
+               timeout);
+
+  strncpy(rdest, idest, sizeof(rdest) - 1);
+  // try '/' as separator
+  stringp = rdest;
+  strsep(&amp;stringp, &quot;/&quot;);
+  where = strsep(&amp;stringp, &quot;/&quot;);
+
+  if (!where) {
+    ERRORA
+      (&quot;Destination %s is not recognized. Chan_celliax requires a standard destination with slashes (Celliax/device/destination, eg: 'Celliax/nicephone/3472665618')\n&quot;,
+       CELLIAX_P_LOG, idest);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+
+  strncpy(dstr, where + p-&gt;stripmsd, sizeof(dstr) - 1);
+  if (option_debug &gt; 1)
+    DEBUGA_PBX(&quot;celliax_call dialing idest: %s, timeout: %d, dstr: %s!\n&quot;, CELLIAX_P_LOG,
+               idest, timeout, dstr);
+
+  if (p-&gt;controldev_dead) {
+    WARNINGA(&quot;celliax_call: device is dead, cannot call!\n&quot;, CELLIAX_P_LOG);
+    status = -1;
+  } else {
+    ast_setstate(c, AST_STATE_DIALING);
+    status = celliax_serial_call(p, dstr);
+  }
+
+  if (status) {
+    WARNINGA(&quot;celliax_call dialing failed: %d!\n&quot;, CELLIAX_P_LOG, status);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  } else {
+    if (option_debug)
+      DEBUGA_PBX(&quot;call ongoing\n&quot;, CELLIAX_P_LOG);
+    ast_queue_control(p-&gt;owner, AST_CONTROL_RINGING);
+  }
+
+  if (option_debug &gt; 1)
+    DEBUGA_PBX(&quot;celliax_call dialed idest: %s, timeout: %d, dstr: %s!\n&quot;, CELLIAX_P_LOG,
+               idest, timeout, dstr);
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+#ifdef CELLIAX_PORTAUDIO
+  speex_echo_state_reset(p-&gt;stream-&gt;echo_state);
+#endif // CELLIAX_PORTAUDIO
+  return 0;
+}
+
+/*! \brief Send audio frame to channel */
+int celliax_write(struct ast_channel *c, struct ast_frame *f)
+{
+  struct celliax_pvt *p = c-&gt;tech_pvt;
+  if (p-&gt;owner &amp;&amp; p-&gt;owner-&gt;_state != AST_STATE_UP) {
+    return 0;
+  }
+
+  celliax_sound_boost(f, p-&gt;playback_boost);
+
+  return celliax_sound_write(p, f);
+}
+
+/*! \brief  Fix up a channel:  If a channel is consumed, this is called.
+ * Basically update any -&gt;owner links */
+int celliax_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+  struct celliax_pvt *p = newchan-&gt;tech_pvt;
+
+  if (!p) {
+    ERRORA(&quot;No pvt after masquerade. Strange things may happen\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+
+  if (p-&gt;owner != oldchan) {
+    ERRORA(&quot;old channel wasn't %p but was %p\n&quot;, CELLIAX_P_LOG, oldchan, p-&gt;owner);
+    return -1;
+  }
+
+  p-&gt;owner = newchan;
+  return 0;
+}
+
+int celliax_sound_boost(struct ast_frame *f, double boost)
+{
+/* LUIGI RIZZO's magic */
+  if (boost != 0) {             /* scale and clip values */
+    int i, x;
+
+#ifdef ASTERISK_VERSION_1_6_0_1
+    int16_t *ptr = (int16_t *) f-&gt;data.ptr;
+#else
+    int16_t *ptr = (int16_t *) f-&gt;data;
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+    for (i = 0; i &lt; f-&gt;samples; i++) {
+      x = (ptr[i] * boost) / BOOST_SCALE;
+      if (x &gt; 32767) {
+        x = 32767;
+      } else if (x &lt; -32768) {
+        x = -32768;
+      }
+      ptr[i] = x;
+    }
+  }
+  return 0;
+}
+
+struct ast_channel *celliax_new(struct celliax_pvt *p, int state, char *context)
+{
+  struct ast_channel *tmp;
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  /* alloc a generic channel struct */
+#ifndef ASTERISK_VERSION_1_4
+  tmp = ast_channel_alloc(1);
+#else
+  //tmp = ast_channel_alloc(1, state, 0, 0, &quot;&quot;, p-&gt;exten, p-&gt;context, 0, &quot;&quot;);
+  tmp =
+    ast_channel_alloc(1, state, 0, 0, &quot;&quot;, p-&gt;exten, p-&gt;context, 0, &quot;Celliax/%s&quot;, p-&gt;name);
+
+#endif /* ASTERISK_VERSION_1_4 */
+  if (tmp) {
+
+    /* give a name to the newly created channel */
+#ifndef ASTERISK_VERSION_1_4
+    snprintf(tmp-&gt;name, sizeof(tmp-&gt;name), &quot;Celliax/%s&quot;, p-&gt;name);
+    tmp-&gt;type = celliax_type;
+#else /* ASTERISK_VERSION_1_4 */
+    ast_string_field_build(tmp, name, &quot;Celliax/%s&quot;, p-&gt;name);
+#endif /* ASTERISK_VERSION_1_4 */
+
+    DEBUGA_PBX(&quot;new channel: name=%s requested_state=%d\n&quot;, CELLIAX_P_LOG, tmp-&gt;name,
+               state);
+
+    /* fd for the channel to poll for incoming audio */
+    tmp-&gt;fds[0] = p-&gt;celliax_sound_capt_fd;
+
+    /* audio formats managed */
+    tmp-&gt;nativeformats = AST_FORMAT_SLINEAR;
+    tmp-&gt;readformat = AST_FORMAT_SLINEAR;
+    tmp-&gt;writeformat = AST_FORMAT_SLINEAR;
+    /* the technology description (eg. the interface type) of the newly created channel is the Celliax's one */
+    tmp-&gt;tech = &amp;celliax_tech;
+    /* the technology pvt (eg. the interface) of the newly created channel is this interface pvt */
+    tmp-&gt;tech_pvt = p;
+
+    /* copy this interface default context, extension, language to the newly created channel */
+    if (strlen(p-&gt;context))
+      strncpy(tmp-&gt;context, p-&gt;context, sizeof(tmp-&gt;context) - 1);
+    if (strlen(p-&gt;exten))
+      strncpy(tmp-&gt;exten, p-&gt;exten, sizeof(tmp-&gt;exten) - 1);
+#ifndef ASTERISK_VERSION_1_4
+    if (strlen(p-&gt;language))
+      strncpy(tmp-&gt;language, p-&gt;language, sizeof(tmp-&gt;language) - 1);
+#else
+    if (strlen(p-&gt;language))
+      ast_string_field_set(tmp, language, p-&gt;language);
+#endif /* ASTERISK_VERSION_1_4 */
+    /* copy the requested context (not necessarily the interface default) to the newly created channel */
+    if (strlen(context))
+      strncpy(tmp-&gt;context, context, sizeof(tmp-&gt;context) - 1);
+
+    /* copy this interface default callerid in the newly created channel */
+    ast_set_callerid(tmp, !ast_strlen_zero(p-&gt;callid_number) ? p-&gt;callid_number : NULL,
+                     !ast_strlen_zero(p-&gt;callid_name) ? p-&gt;callid_name : NULL,
+                     !ast_strlen_zero(p-&gt;callid_number) ? p-&gt;callid_number : NULL);
+
+    /* the owner of this interface pvt is the newly created channel */
+    p-&gt;owner = tmp;
+    /* if this interface pvt has an initialized dsp struct, free it */
+    if (p-&gt;dsp) {
+      DEBUGA_SOUND(&quot;freeing dsp\n&quot;, CELLIAX_P_LOG);
+      ast_dsp_free(p-&gt;dsp);
+      p-&gt;dsp = NULL;
+    }
+#ifndef ASTERISK_VERSION_1_4
+    /* set the newly created channel state to the requested state */
+    if (ast_setstate(tmp, state)) {
+      ERRORA(&quot;ast_setstate failed, BAD\n&quot;, CELLIAX_P_LOG);
+      ast_dsp_free(p-&gt;dsp);
+      ast_channel_free(tmp);
+      if (option_debug) {
+        DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+      }
+      return NULL;
+    }
+#endif /* ASTERISK_VERSION_1_4 */
+
+#ifdef AST_VERION_1_4
+        ast_module_ref(ast_module_info-&gt;self);
+        ast_jb_configure(tmp, &amp;global_jbconf);
+#endif /* AST_VERION_1_4 */
+
+    /* if the requested state is different from DOWN, let the pbx manage this interface (now part of the newly created channel) */
+    if (state != AST_STATE_DOWN) {
+      DEBUGA_PBX(&quot;Try to start PBX on %s, state=%d\n&quot;, CELLIAX_P_LOG, tmp-&gt;name, state);
+      if (ast_pbx_start(tmp)) {
+        ERRORA(&quot;Unable to start PBX on %s\n&quot;, CELLIAX_P_LOG, tmp-&gt;name);
+        ast_dsp_free(p-&gt;dsp);
+        ast_channel_free(tmp);
+        if (option_debug) {
+          DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+        }
+        return NULL;
+      }
+    }
+    /* let's start the serial monitoring thread too, so we can have serial signaling */
+    if (ast_pthread_create(&amp;p-&gt;controldev_thread, NULL, celliax_do_controldev_thread, p) &lt;
+        0) {
+      ERRORA(&quot;Unable to start controldev thread.\n&quot;, CELLIAX_P_LOG);
+      ast_dsp_free(p-&gt;dsp);
+      ast_channel_free(tmp);
+      tmp = NULL;
+    }
+    DEBUGA_SERIAL(&quot;STARTED controldev_thread=%lu STOP=%lu NULL=%lu\n&quot;, CELLIAX_P_LOG,
+                  (unsigned long) p-&gt;controldev_thread, (unsigned long) AST_PTHREADT_STOP,
+                  (unsigned long) AST_PTHREADT_NULL);
+
+#ifndef ASTERISK_VERSION_1_4
+    /* add one to the usage count of Celliax-type channels */
+    LOKKA(&amp;celliax_usecnt_lock);
+    celliax_usecnt++;
+    UNLOCKA(&amp;celliax_usecnt_lock);
+    ast_update_use_count();
+#endif /* ASTERISK_VERSION_1_4 */
+
+    /* return the newly created channel */
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return tmp;
+  }
+  ERRORA(&quot;failed memory allocation for Celliax channel\n&quot;, CELLIAX_P_LOG);
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  return NULL;
+}
+
+/*!
+ * \brief Load the module into Asterisk and start its threads
+ *
+ * This function register the module into Asterisk,
+ * create the interfaces for the channels, 
+ * start the auxiliary threads for the interfaces,
+ * then start a monitor thread. The monitor thread
+ * will signal Asterisk when an interface receive a call.
+ *
+ *
+ * \return zero on success, -1 on error.
+ */
+int load_module(void)
+{
+  int i;
+  struct ast_config *cfg;
+  struct celliax_pvt *tmp;
+  struct celliax_pvt *p = NULL;
+#ifdef ASTERISK_VERSION_1_6_0
+  struct ast_flags config_flags = { 0 };
+#endif /* ASTERISK_VERSION_1_6_0 */
+
+
+
+#ifdef ASTERISK_VERSION_1_4
+        /* Copy the default jb config over global_jbconf */
+        memcpy(&amp;global_jbconf, &amp;default_jbconf, sizeof(struct ast_jb_conf));
+#endif /* ASTERISK_VERSION_1_4 */
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  ast_register_application(celliax_sendsmsapp, celliax_sendsms, celliax_sendsmssynopsis,
+                           celliax_sendsmsdescrip);
+
+  ast_manager_register2(&quot;CELLIAXsendsms&quot;, EVENT_FLAG_SYSTEM, celliax_manager_sendsms,
+                        &quot;Send an SMS&quot;, mandescr_celliax_sendsms);
+  /* make sure we can register our channel type with Asterisk */
+  i = ast_channel_register(&amp;celliax_tech);
+  if (i &lt; 0) {
+    ERRORA(&quot;Unable to register channel type '%s'\n&quot;, CELLIAX_P_LOG, celliax_type);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+  /* load celliax.conf config file */
+#ifdef ASTERISK_VERSION_1_6_0
+  cfg = ast_config_load(celliax_config, config_flags);
+#else
+  cfg = ast_config_load(celliax_config);
+#endif /* ASTERISK_VERSION_1_6_0 */
+  if (cfg != NULL) {
+    char *ctg = NULL;
+    int is_first_category = 1;
+    while ((ctg = ast_category_browse(cfg, ctg)) != NULL) {
+      /* create one interface for each category in celliax.conf config file, first one set the defaults */
+      tmp = celliax_mkif(cfg, ctg, is_first_category);
+      if (tmp) {
+        NOTICA(&quot;Created channel Celliax: celliax.conf category '[%s]', channel name '%s'&quot;
+               &quot; control_device_name '%s'\n&quot;, CELLIAX_P_LOG, ctg, tmp-&gt;name,
+               tmp-&gt;controldevice_name);
+        /* add interface to celliax_iflist */
+        tmp-&gt;next = celliax_iflist;
+        celliax_iflist = tmp;
+        /* next one will not be the first ;) */
+        if (is_first_category == 1) {
+          is_first_category = 0;
+          celliax_console_active = tmp-&gt;name;
+        }
+      } else {
+        ERRORA(&quot;Unable to create channel Celliax from celliax.conf category '[%s]'\n&quot;,
+               CELLIAX_P_LOG, ctg);
+        /* if error, unload config from memory and return */
+        ast_config_destroy(cfg);
+        ast_channel_unregister(&amp;celliax_tech);
+        if (option_debug) {
+          DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+        }
+        return -1;
+      }
+      /* do it for each category described in config */
+    }
+
+    /* we finished, unload config from memory */
+    ast_config_destroy(cfg);
+  } else {
+    ERRORA(&quot;Unable to load celliax_config celliax.conf\n&quot;, CELLIAX_P_LOG);
+    ast_channel_unregister(&amp;celliax_tech);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+#ifndef ASTERISK_VERSION_1_6_0
+  ast_cli_register_multiple(myclis, sizeof(myclis) / sizeof(struct ast_cli_entry));
+#endif /* ASTERISK_VERSION_1_6_0 */
+  /* start to monitor the interfaces (celliax_iflist) for the first time */
+  if (celliax_restart_monitor()) {
+    ERRORA(&quot;celliax_restart_monitor failed, BAD\n&quot;, CELLIAX_P_LOG);
+    if (option_debug) {
+      DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+    }
+    return -1;
+  }
+#ifdef CELLIAX_DIR
+  //celliax_dir_create_extensions();
+#endif /* CELLIAX_DIR */
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  return 0;
+}
+
+/*!
+ * \brief Unload the module from Asterisk and shutdown its threads
+ *
+ * This function unregister the module from Asterisk,
+ * destroy the interfaces for the channels, 
+ * shutdown the auxiliary threads for the interfaces,
+ * then shutdown its monitor thread.
+ *
+ * \return zero on success, -1 on error.
+ */
+int unload_module(void)
+{
+  struct celliax_pvt *p = NULL, *p2 = NULL;
+  int res;
+
+  if (option_debug) {
+    DEBUGA_PBX(&quot;ENTERING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+
+  /* unregister our channel type with Asterisk */
+  ast_channel_unregister(&amp;celliax_tech);
+  ast_cli_unregister_multiple(myclis, sizeof(myclis) / sizeof(struct ast_cli_entry));
+
+  ast_unregister_application(celliax_sendsmsapp);
+
+  /* lock the celliax_monlock, kill the monitor thread, unlock the celliax_monlock */
+  LOKKA(&amp;celliax_monlock);
+  if (celliax_monitor_thread &amp;&amp; (celliax_monitor_thread != AST_PTHREADT_NULL)
+      &amp;&amp; (celliax_monitor_thread != AST_PTHREADT_STOP)) {
+    if (pthread_cancel(celliax_monitor_thread)) {
+      ERRORA(&quot;pthread_cancel failed, BAD\n&quot;, CELLIAX_P_LOG);
+      if (option_debug) {
+        DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+      }
+      return -1;
+    }
+    if (pthread_kill(celliax_monitor_thread, SIGURG)) {
+      DEBUGA_PBX(&quot;pthread_kill failed\n&quot;, CELLIAX_P_LOG);   //maybe it just died
+    }
+#ifndef __CYGWIN__              /* under cygwin, this seems to be not reliable, get stuck at times */
+    if (pthread_join(celliax_monitor_thread, NULL)) {
+      ERRORA(&quot;pthread_join failed, BAD\n&quot;, CELLIAX_P_LOG);
+      if (option_debug) {
+        DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+      }
+      return -1;
+    }
+#endif /* __CYGWIN__ */
+  }
+  celliax_monitor_thread = AST_PTHREADT_STOP;
+  UNLOCKA(&amp;celliax_monlock);
+
+  if (celliax_monitor_audio_thread &amp;&amp; (celliax_monitor_audio_thread != AST_PTHREADT_NULL)
+      &amp;&amp; (celliax_monitor_audio_thread != AST_PTHREADT_STOP)) {
+
+    if (pthread_cancel(celliax_monitor_audio_thread)) {
+      ERRORA(&quot;pthread_cancel celliax_monitor_audio_thread failed, BAD\n&quot;, CELLIAX_P_LOG);
+    }
+    if (pthread_kill(celliax_monitor_audio_thread, SIGURG)) {
+      DEBUGA_PBX(&quot;pthread_kill celliax_monitor_audio_thread failed, no problem\n&quot;, CELLIAX_P_LOG);  //maybe it just died
+    }
+
+    if (pthread_join(celliax_monitor_audio_thread, NULL)) {
+      ERRORA(&quot;pthread_join failed, BAD\n&quot;, CELLIAX_P_LOG);
+    }
+  }
+  /* lock the celliax_iflock, and go through the interfaces list (celliax_iflist) */
+  LOKKA(&amp;celliax_iflock);
+  p = celliax_iflist;
+  while (p) {
+    /* for each interface in list */
+    p2 = p-&gt;next;
+    /* shutdown the sound system, close sound fds, and if exist shutdown the sound managing threads */
+    DEBUGA_SOUND(&quot;shutting down sound\n&quot;, CELLIAX_P_LOG);
+    res = celliax_sound_shutdown(p);
+    if (res == -1) {
+      ERRORA(&quot;Failed to shutdown sound\n&quot;, CELLIAX_P_LOG);
+    }
+
+    /* if a serial port has been opened, close it */
+    if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL)
+      if (p-&gt;controldevfd)
+        close(p-&gt;controldevfd);
+
+    /* if a dsp struct has been allocated, free it */
+    if (p-&gt;dsp) {
+      ast_dsp_free(p-&gt;dsp);
+      p-&gt;dsp = NULL;
+    }
+    DEBUGA_PBX(&quot;freeing PVT\n&quot;, CELLIAX_P_LOG);
+    /* free the pvt allocated memory */
+    free(p);
+    /* next one, please */
+    p = p2;
+  }
+  /* finished with the interfaces list, unlock the celliax_iflock */
+  UNLOCKA(&amp;celliax_iflock);
+
+#ifdef __CYGWIN__
+  NOTICA(&quot;Sleping 5 secs, please wait...\n&quot;, CELLIAX_P_LOG);
+  sleep(5);                     /* without this pause, for some unknown (to me) reason it crashes on cygwin */
+#endif /* __CYGWIN__ */
+  NOTICA(&quot;Unloaded Celliax Module\n&quot;, CELLIAX_P_LOG);
+  if (option_debug) {
+    DEBUGA_PBX(&quot;EXITING FUNC\n&quot;, CELLIAX_P_LOG);
+  }
+  return 0;
+}
+
+/*!
+ * \brief Return the count of active channels for this module
+ *
+ * \return the count of active channels for this module
+ */
+int usecount()
+{
+  int res;
+  static struct celliax_pvt *p = &amp;celliax_log_struct;
+/* lock the celliax_usecnt lock */
+  LOKKA(&amp;celliax_usecnt_lock);
+  /* retrieve the celliax_usecnt */
+  res = celliax_usecnt;
+/* unlock the celliax_usecnt lock */
+  UNLOCKA(&amp;celliax_usecnt_lock);
+  /* return the celliax_usecnt */
+  return res;
+}
+
+/*!
+ * \brief Return the textual description of the module
+ *
+ * \return the textual description of the module
+ */
+char *description()
+{
+  return (char *) celliax_desc;
+}
+
+/*!
+ * \brief Return the ASTERISK_GPL_KEY
+ *
+ * \return the ASTERISK_GPL_KEY
+ */
+char *key()
+{
+  struct celliax_pvt *p = NULL;
+
+  if (option_debug)
+    NOTICA(&quot;Returning Key\n&quot;, CELLIAX_P_LOG);
+
+  return ASTERISK_GPL_KEY;
+}
+
+/*!
+ * \brief Create and initialize one interface for the module
+ * \param cfg pointer to configuration data from celliax.conf
+ * \param ctg pointer to a category name to be found in cfg
+ * \param is_first_category is this the first category in cfg
+ *
+ * This function create and initialize one interface for the module
+ *
+ * \return a pointer to the PVT structure of interface on success, NULL on error.
+ */
+struct celliax_pvt *celliax_mkif(struct ast_config *cfg, char *ctg, int is_first_category)
+{
+  struct celliax_pvt *tmp;
+  struct ast_variable *v;
+  int res;
+
+  int debug_all = 0;
+  int debug_at = 0;
+  int debug_fbus2 = 0;
+  int debug_serial = 0;
+  int debug_sound = 0;
+  int debug_pbx = 0;
+  int debug_skype = 0;
+  int debug_call = 0;
+  int debug_locks = 0;
+  int debug_monitorlocks = 0;
+#ifdef CELLIAX_CVM
+  int debug_cvm = 0;
+#endif /* CELLIAX_CVM */
+
+  /* alloc memory for PVT */
+  tmp = malloc(sizeof(struct celliax_pvt));
+  if (tmp == NULL)              /* fail */
+    return NULL;
+  /* clear memory for PVT */
+  memset(tmp, 0, sizeof(struct celliax_pvt));
+
+  //NOTICA(&quot;malloced %d bytes\n&quot;, CELLIAX_TMP_LOG, sizeof(struct celliax_pvt));
+
+  /* if we are reading the &quot;first&quot; category of the config file, take SELECTED values as defaults, overriding the values in celliax_default */
+  if (is_first_category == 1) {
+    /* for each variable in category, copy it in the celliax_default struct */
+    for (v = ast_variable_browse(cfg, ctg); v; v = v-&gt;next) {
+      M_START(v-&gt;name, v-&gt;value);
+
+      M_STR(&quot;control_device_protocol&quot;, celliax_default.controldevprotocolname)
+        M_STR(&quot;context&quot;, celliax_default.context)
+        M_STR(&quot;language&quot;, celliax_default.language)
+        M_STR(&quot;extension&quot;, celliax_default.exten)
+        M_UINT(&quot;dsp_silence_threshold&quot;, celliax_default.dsp_silence_threshold)
+        M_UINT(&quot;audio_play_reset_period&quot;, celliax_default.audio_play_reset_period)
+#ifdef CELLIAX_ALSA
+        M_UINT(&quot;alsa_period_size&quot;, celliax_default.alsa_period_size)
+        M_UINT(&quot;alsa_periods_in_buffer&quot;, celliax_default.alsa_periods_in_buffer)
+#endif /* CELLIAX_ALSA */
+        M_F(&quot;playback_boost&quot;,
+            celliax_store_boost(v-&gt;value, &amp;celliax_default.playback_boost))
+        M_F(&quot;capture_boost&quot;,
+            celliax_store_boost(v-&gt;value, &amp;celliax_default.capture_boost))
+        M_UINT(&quot;celliax_dir_entry_extension_prefix&quot;,
+               celliax_default.celliax_dir_entry_extension_prefix)
+        M_UINT(&quot;celliax_dir_prefix&quot;, celliax_default.celliax_dir_prefix)
+        M_STR(&quot;sms_receiving_program&quot;, tmp-&gt;sms_receiving_program)
+        M_END(;
+        );
+    }
+  }
+
+  /* initialize the newly created PVT from the celliax_default values */
+  *tmp = celliax_default;
+
+  /* initialize the mutexes */
+  ast_mutex_init(&amp;tmp-&gt;controldev_lock);
+  ast_mutex_init(&amp;tmp-&gt;fbus2_outgoing_list_lock);
+#ifdef CELLIAX_CVM
+  ast_mutex_init(&amp;tmp-&gt;cvm_busmail_outgoing_list_lock);
+#endif /* CELLIAX_CVM */
+
+  /* the category name becomes the interface name */
+  tmp-&gt;name = strdup(ctg);
+
+  /* for each category in config file, &quot;first&quot; included, read in ALL the values */
+  for (v = ast_variable_browse(cfg, ctg); v; v = v-&gt;next) {
+    M_START(v-&gt;name, v-&gt;value);
+
+    M_BOOL(&quot;debug_all&quot;, debug_all)
+      M_BOOL(&quot;debug_at&quot;, debug_at)
+      M_BOOL(&quot;debug_fbus2&quot;, debug_fbus2)
+      M_BOOL(&quot;debug_serial&quot;, debug_serial)
+      M_BOOL(&quot;debug_sound&quot;, debug_sound)
+      M_BOOL(&quot;debug_pbx&quot;, debug_pbx)
+      M_BOOL(&quot;debug_skype&quot;, debug_skype)
+      M_BOOL(&quot;debug_call&quot;, debug_call)
+      M_BOOL(&quot;debug_locks&quot;, debug_locks)
+      M_BOOL(&quot;debug_monitorlocks&quot;, debug_monitorlocks)
+#ifdef CELLIAX_CVM
+      M_BOOL(&quot;debug_cvm&quot;, debug_cvm)
+      M_STR(&quot;cvm_subscription_1_pin&quot;, tmp-&gt;cvm_subsc_1_pin)
+      M_STR(&quot;cvm_subscription_2_pin&quot;, tmp-&gt;cvm_subsc_2_pin)
+      M_UINT(&quot;cvm_subscription_no&quot;, tmp-&gt;cvm_subsc_no)
+      M_UINT(&quot;cvm_volume_level&quot;, tmp-&gt;cvm_volume_level)
+      M_UINT(&quot;cvm_celliax_serial_delay&quot;, tmp-&gt;cvm_celliax_serial_delay)
+#endif /* CELLIAX_CVM */
+      M_BOOL(&quot;skype&quot;, tmp-&gt;skype)
+      M_BOOL(&quot;need_acoustic_ring&quot;, tmp-&gt;need_acoustic_ring)
+      M_STR(&quot;control_device_name&quot;, tmp-&gt;controldevice_name)
+      M_UINT(&quot;control_device_speed&quot;, tmp-&gt;controldevice_speed)
+      M_STR(&quot;control_device_protocol&quot;, tmp-&gt;controldevprotocolname)
+      M_STR(&quot;context&quot;, tmp-&gt;context)
+      M_STR(&quot;language&quot;, tmp-&gt;language)
+      M_STR(&quot;extension&quot;, tmp-&gt;exten)
+      M_UINT(&quot;dsp_silence_threshold&quot;, tmp-&gt;dsp_silence_threshold)
+      M_UINT(&quot;audio_play_reset_period&quot;, tmp-&gt;audio_play_reset_period)
+      M_UINT(&quot;portaudio_capture_device_id&quot;, tmp-&gt;portaudiocindex)
+      M_UINT(&quot;portaudio_playback_device_id&quot;, tmp-&gt;portaudiopindex)
+      M_F(&quot;playback_boost&quot;, celliax_store_boost(v-&gt;value, &amp;tmp-&gt;playback_boost))
+      M_F(&quot;capture_boost&quot;, celliax_store_boost(v-&gt;value, &amp;tmp-&gt;capture_boost))
+#ifdef CELLIAX_ALSA
+      M_STR(&quot;alsa_capture_device_name&quot;, tmp-&gt;alsacname)
+      M_STR(&quot;alsa_playback_device_name&quot;, tmp-&gt;alsapname)
+      M_UINT(&quot;alsa_period_size&quot;, tmp-&gt;alsa_period_size)
+      M_UINT(&quot;alsa_periods_in_buffer&quot;, tmp-&gt;alsa_periods_in_buffer)
+#endif /* CELLIAX_WINMM */
+      M_STR(&quot;at_dial_pre_number&quot;, tmp-&gt;at_dial_pre_number)
+      M_STR(&quot;at_dial_post_number&quot;, tmp-&gt;at_dial_post_number)
+
+      M_STR(&quot;at_dial_expect&quot;, tmp-&gt;at_dial_expect)
+      M_UINT(&quot;at_early_audio&quot;, tmp-&gt;at_early_audio)
+      M_STR(&quot;at_hangup&quot;, tmp-&gt;at_hangup)
+      M_STR(&quot;at_hangup_expect&quot;, tmp-&gt;at_hangup_expect)
+      M_STR(&quot;at_answer&quot;, tmp-&gt;at_answer)
+      M_STR(&quot;at_answer_expect&quot;, tmp-&gt;at_answer_expect)
+      M_STR(&quot;at_send_dtmf&quot;, tmp-&gt;at_send_dtmf)
+
+      M_UINT(&quot;at_initial_pause&quot;, tmp-&gt;at_initial_pause)
+      M_STR(&quot;at_preinit_1&quot;, tmp-&gt;at_preinit_1)
+      M_STR(&quot;at_preinit_1_expect&quot;, tmp-&gt;at_preinit_1_expect)
+      M_STR(&quot;at_preinit_2&quot;, tmp-&gt;at_preinit_2)
+      M_STR(&quot;at_preinit_2_expect&quot;, tmp-&gt;at_preinit_2_expect)
+      M_STR(&quot;at_preinit_3&quot;, tmp-&gt;at_preinit_3)
+      M_STR(&quot;at_preinit_3_expect&quot;, tmp-&gt;at_preinit_3_expect)
+      M_STR(&quot;at_preinit_4&quot;, tmp-&gt;at_preinit_4)
+      M_STR(&quot;at_preinit_4_expect&quot;, tmp-&gt;at_preinit_4_expect)
+      M_STR(&quot;at_preinit_5&quot;, tmp-&gt;at_preinit_5)
+      M_STR(&quot;at_preinit_5_expect&quot;, tmp-&gt;at_preinit_5_expect)
+      M_UINT(&quot;at_after_preinit_pause&quot;, tmp-&gt;at_after_preinit_pause)
+
+      M_STR(&quot;at_postinit_1&quot;, tmp-&gt;at_postinit_1)
+      M_STR(&quot;at_postinit_1_expect&quot;, tmp-&gt;at_postinit_1_expect)
+      M_STR(&quot;at_postinit_2&quot;, tmp-&gt;at_postinit_2)
+      M_STR(&quot;at_postinit_2_expect&quot;, tmp-&gt;at_postinit_2_expect)
+      M_STR(&quot;at_postinit_3&quot;, tmp-&gt;at_postinit_3)
+      M_STR(&quot;at_postinit_3_expect&quot;, tmp-&gt;at_postinit_3_expect)
+      M_STR(&quot;at_postinit_4&quot;, tmp-&gt;at_postinit_4)
+      M_STR(&quot;at_postinit_4_expect&quot;, tmp-&gt;at_postinit_4_expect)
+      M_STR(&quot;at_postinit_5&quot;, tmp-&gt;at_postinit_5)
+      M_STR(&quot;at_postinit_5_expect&quot;, tmp-&gt;at_postinit_5_expect)
+
+      M_STR(&quot;at_query_battchg&quot;, tmp-&gt;at_query_battchg)
+      M_STR(&quot;at_query_battchg_expect&quot;, tmp-&gt;at_query_battchg_expect)
+      M_STR(&quot;at_query_signal&quot;, tmp-&gt;at_query_signal)
+      M_STR(&quot;at_query_signal_expect&quot;, tmp-&gt;at_query_signal_expect)
+      M_STR(&quot;at_call_idle&quot;, tmp-&gt;at_call_idle)
+      M_STR(&quot;at_call_incoming&quot;, tmp-&gt;at_call_incoming)
+      M_STR(&quot;at_call_active&quot;, tmp-&gt;at_call_active)
+      M_STR(&quot;at_call_failed&quot;, tmp-&gt;at_call_failed)
+      M_STR(&quot;at_call_calling&quot;, tmp-&gt;at_call_calling)
+      M_STR(&quot;at_indicator_noservice_string&quot;, tmp-&gt;at_indicator_noservice_string)
+      M_STR(&quot;at_indicator_nosignal_string&quot;, tmp-&gt;at_indicator_nosignal_string)
+      M_STR(&quot;at_indicator_lowsignal_string&quot;, tmp-&gt;at_indicator_lowsignal_string)
+      M_STR(&quot;at_indicator_lowbattchg_string&quot;, tmp-&gt;at_indicator_lowbattchg_string)
+      M_STR(&quot;at_indicator_nobattchg_string&quot;, tmp-&gt;at_indicator_nobattchg_string)
+      M_STR(&quot;at_indicator_callactive_string&quot;, tmp-&gt;at_indicator_callactive_string)
+      M_STR(&quot;at_indicator_nocallactive_string&quot;, tmp-&gt;at_indicator_nocallactive_string)
+      M_STR(&quot;at_indicator_nocallsetup_string&quot;, tmp-&gt;at_indicator_nocallsetup_string)
+      M_STR(&quot;at_indicator_callsetupincoming_string&quot;,
+            tmp-&gt;at_indicator_callsetupincoming_string)
+      M_STR(&quot;at_indicator_callsetupoutgoing_string&quot;,
+            tmp-&gt;at_indicator_callsetupoutgoing_string)
+      M_STR(&quot;at_indicator_callsetupremoteringing_string&quot;,
+            tmp-&gt;at_indicator_callsetupremoteringing_string)
+      M_UINT(&quot;celliax_dir_entry_extension_prefix&quot;,
+             tmp-&gt;celliax_dir_entry_extension_prefix)
+      M_UINT(&quot;celliax_dir_prefix&quot;, tmp-&gt;celliax_dir_prefix)
+#ifdef CELLIAX_LIBCSV
+      M_UINT(&quot;csv_separator_is_semicolon&quot;, tmp-&gt;csv_separator_is_semicolon)
+      M_UINT(&quot;csv_complete_name_pos&quot;, tmp-&gt;csv_complete_name_pos)
+      M_UINT(&quot;csv_email_pos&quot;, tmp-&gt;csv_email_pos)
+      M_UINT(&quot;csv_home_phone_pos&quot;, tmp-&gt;csv_home_phone_pos)
+      M_UINT(&quot;csv_mobile_phone_pos&quot;, tmp-&gt;csv_mobile_phone_pos)
+      M_UINT(&quot;csv_business_phone_pos&quot;, tmp-&gt;csv_business_phone_pos)
+      M_UINT(&quot;csv_first_row_is_title&quot;, tmp-&gt;csv_first_row_is_title)
+#endif /* CELLIAX_LIBCSV */
+      M_STR(&quot;sms_receiving_program&quot;, tmp-&gt;sms_receiving_program)
+      M_BOOL(&quot;speexecho&quot;, tmp-&gt;speexecho)
+      M_BOOL(&quot;speexpreprocess&quot;, tmp-&gt;speexpreprocess)
+      M_END(;
+      );
+  }
+
+  if (debug_all) {
+    celliax_debug = celliax_debug | DEBUG_ALL;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_ALL activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_ALL debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_ALL activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+  if (debug_at) {
+    celliax_debug = celliax_debug | DEBUG_AT;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_AT activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_AT debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_AT activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_fbus2) {
+    celliax_debug = celliax_debug | DEBUG_FBUS2;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_FBUS2 activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_FBUS2 debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_FBUS2 activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_serial) {
+    celliax_debug = celliax_debug | DEBUG_SERIAL;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_SERIAL activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_SERIAL debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_SERIAL activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_sound) {
+    celliax_debug = celliax_debug | DEBUG_SOUND;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_SOUND activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_SOUND debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_SOUND activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_pbx) {
+    celliax_debug = celliax_debug | DEBUG_PBX;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_PBX activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_PBX debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_PBX activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_call) {
+    celliax_debug = celliax_debug | DEBUG_CALL;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_CALL activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_CALL debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_CALL activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_locks) {
+    celliax_debug = celliax_debug | DEBUG_LOCKS;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_LOCKS activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_LOCKS debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_LOCKS activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+
+  if (debug_monitorlocks) {
+    celliax_debug = celliax_debug | DEBUG_MONITORLOCKS;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_MONITORLOCKS activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_MONITORLOCKS debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_MONITORLOCKS activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+#ifdef CELLIAX_CVM
+  if (debug_cvm) {
+    celliax_debug = celliax_debug | DEBUG_CVM;
+    if (!option_debug) {
+      WARNINGA
+        (&quot;DEBUG_CVM activated, but option_debug is 0. You have to set debug level higher than zero to see some debugging output. Please use the command \&quot;set debug 10\&quot; or start Asterisk with \&quot;-dddddddddd\&quot; option for full DEBUG_CVM debugging output.\n&quot;,
+         CELLIAX_TMP_LOG);
+    } else {
+      NOTICA(&quot;DEBUG_CVM activated. \n&quot;, CELLIAX_TMP_LOG);
+    }
+  }
+#endif /* CELLIAX_CVM */
+
+  if (option_debug &gt; 1) {
+    DEBUGA_SOUND(&quot;playback_boost is %f\n&quot;, CELLIAX_TMP_LOG, tmp-&gt;playback_boost);
+    DEBUGA_SOUND(&quot;capture_boost is %f\n&quot;, CELLIAX_TMP_LOG, tmp-&gt;capture_boost);
+  }
+
+  /* serial protocols are named in config with a string, but as int in this software */
+  if (strcasecmp(tmp-&gt;controldevprotocolname, &quot;fbus2&quot;) == 0)
+    tmp-&gt;controldevprotocol = PROTOCOL_FBUS2;
+  else if (strcasecmp(tmp-&gt;controldevprotocolname, &quot;at&quot;) == 0)
+    tmp-&gt;controldevprotocol = PROTOCOL_AT;
+  else if (strcasecmp(tmp-&gt;controldevprotocolname, &quot;no_serial&quot;) == 0)
+    tmp-&gt;controldevprotocol = PROTOCOL_NO_SERIAL;
+  else if (strcasecmp(tmp-&gt;controldevprotocolname, &quot;alsa_voicemodem&quot;) == 0)
+    tmp-&gt;controldevprotocol = PROTOCOL_ALSA_VOICEMODEM;
+#ifdef CELLIAX_CVM
+  else if (strcasecmp(tmp-&gt;controldevprotocolname, &quot;cvm_busmail&quot;) == 0)
+    tmp-&gt;controldevprotocol = PROTOCOL_CVM_BUSMAIL;
+#endif /* CELLIAX_CVM */
+  else {
+#ifndef CELLIAX_CVM
+    ERRORA
+      (&quot;control_device_protocol in celliax.conf MUST be = fbus2|at|no_serial|alsa_voicemodem, but is = '%s'\n&quot;,
+       CELLIAX_TMP_LOG,
+       tmp-&gt;controldevprotocolname ? tmp-&gt;controldevprotocolname : &quot;NULL&quot;);
+#else
+    ERRORA
+      (&quot;control_device_protocol in celliax.conf MUST be = fbus2|at|no_serial|alsa_voicemodem|cvm_busmail, but is = '%s'\n&quot;,
+       CELLIAX_TMP_LOG,
+       tmp-&gt;controldevprotocolname ? tmp-&gt;controldevprotocolname : &quot;NULL&quot;);
+#endif /* CELLIAX_CVM */
+
+    /* we failed, free the PVT */
+    free(tmp);
+    return NULL;
+  }
+
+  if (tmp-&gt;controldevice_speed != celliax_default.controldevice_speed) {
+    /* serial speeds are numbers in config file, but we needs definitions in this software */
+    if (tmp-&gt;controldevice_speed == 9600)
+      tmp-&gt;controldevice_speed = B9600;
+    else if (tmp-&gt;controldevice_speed == 19200)
+      tmp-&gt;controldevice_speed = B19200;
+    else if (tmp-&gt;controldevice_speed == 38400)
+      tmp-&gt;controldevice_speed = B38400;
+    else if (tmp-&gt;controldevice_speed == 57600)
+      tmp-&gt;controldevice_speed = B57600;
+    else if (tmp-&gt;controldevice_speed == 115200)
+      tmp-&gt;controldevice_speed = B115200;
+    else {
+      ERRORA
+        (&quot;controldevice_speed has to be given one of the following values: 9600|19200|38400|57600|115200. In the config file, was given: %d\n&quot;,
+         CELLIAX_TMP_LOG, tmp-&gt;controldevice_speed);
+      free(tmp);
+      return NULL;
+    }
+  }
+
+/* CVM PP DECT modules supports registration to two DECT FPs (bases), but CVM can be only connected to one DECT FP at the time, so we need two PINs (for registration) and info to which DECT FP connect*/
+#ifdef CELLIAX_CVM
+  if (tmp-&gt;cvm_subsc_no != celliax_default.cvm_subsc_no) {
+    if ((tmp-&gt;cvm_subsc_no != 1) &amp;&amp; (tmp-&gt;cvm_subsc_no != 2)) {
+      ERRORA
+        (&quot;cvm_subscription_no has to be given one of the following values: 1|2. In the config file, was given: %d\n&quot;,
+         CELLIAX_TMP_LOG, tmp-&gt;cvm_subsc_no);
+      free(tmp);
+      return NULL;
+    }
+  }
+
+  if (tmp-&gt;cvm_subsc_1_pin != celliax_default.cvm_subsc_1_pin) {
+    if (4 != strlen(tmp-&gt;cvm_subsc_1_pin)) {
+      ERRORA
+        (&quot;cvm_subscription_1_pin has to be 4 digits long. In the config file, was given: %s\n&quot;,
+         CELLIAX_TMP_LOG, tmp-&gt;cvm_subsc_1_pin);
+      free(tmp);
+      return NULL;
+    }
+  }
+
+  if (tmp-&gt;cvm_subsc_2_pin != celliax_default.cvm_subsc_2_pin) {
+    if (4 != strlen(tmp-&gt;cvm_subsc_2_pin)) {
+      ERRORA
+        (&quot;cvm_subscription_2_pin has to be 4 digits long. In the config file, was given: %s\n&quot;,
+         CELLIAX_TMP_LOG, tmp-&gt;cvm_subsc_2_pin);
+      free(tmp);
+      return NULL;
+    }
+  }
+
+  if (tmp-&gt;cvm_volume_level != celliax_default.cvm_volume_level) {
+    if ((0 &gt; tmp-&gt;cvm_volume_level) &amp;&amp; (9 &lt; tmp-&gt;cvm_volume_level)) {
+      ERRORA(&quot;cvm_volume_level has to be 0-9. In the config file, was given: %d\n&quot;,
+             CELLIAX_TMP_LOG, tmp-&gt;cvm_volume_level);
+      free(tmp);
+      return NULL;
+    }
+  }
+
+  if (tmp-&gt;cvm_celliax_serial_delay != celliax_default.cvm_celliax_serial_delay) {
+    if ((0 &gt; tmp-&gt;cvm_celliax_serial_delay) &amp;&amp; (65535 &lt; tmp-&gt;cvm_celliax_serial_delay)) {
+      ERRORA
+        (&quot;cvm_celliax_serial_dealy has to be 0-65535. In the config file, was given: %d\n&quot;,
+         CELLIAX_TMP_LOG, tmp-&gt;cvm_celliax_serial_delay);
+      free(tmp);
+      return NULL;
+    }
+  }
+#endif /* CELLIAX_CVM */
+
+/* initialize the soundcard channels (input and output) used by this interface (a multichannel soundcard can be used by multiple interfaces), optionally starting the sound managing threads */
+  res = celliax_sound_init(tmp);
+  if (res == -1) {
+    ERRORA(&quot;Failed initializing sound device\n&quot;, CELLIAX_TMP_LOG);
+    /* we failed, free the PVT */
+    if (tmp)
+      free(tmp);
+    return NULL;
+  }
+  if (tmp-&gt;need_acoustic_ring) {
+    /* alloc and initialize a new dsp struct for this interface pvt, WITH silence suppression */
+    if (celliax_sound_dsp_set(tmp, tmp-&gt;dsp_silence_threshold, 1)) {
+      ERRORA(&quot;celliax_sound_dsp_set failed\n&quot;, CELLIAX_TMP_LOG);
+      celliax_sound_shutdown(tmp);
+      if (tmp)
+        free(tmp);
+      return NULL;
+    }
+  }
+
+  /* init the serial port */
+  if (tmp-&gt;controldevprotocol != PROTOCOL_NO_SERIAL) {
+    tmp-&gt;controldevfd = celliax_serial_init(tmp, tmp-&gt;controldevice_speed);
+    if (tmp-&gt;controldevfd &lt; 1) {
+      ERRORA(&quot;celliax_serial_init failed\n&quot;, CELLIAX_TMP_LOG);
+      celliax_sound_shutdown(tmp);
+      if (tmp)
+        free(tmp);
+      return NULL;
+    }
+  }
+
+  /* config the phone/modem on the serial port */
+  if (tmp-&gt;controldevprotocol != PROTOCOL_NO_SERIAL) {
+    //int res;
+    res = celliax_serial_config(tmp);
+    if (res) {
+      ERRORA(&quot;celliax_serial_config failed\n&quot;, CELLIAX_TMP_LOG);
+      celliax_sound_shutdown(tmp);
+      if (tmp)
+        free(tmp);
+      return NULL;
+    }
+  }
+
+  /* return the newly created celliax_pvt */
+  return tmp;
+}
+
+/*! \brief (Re)Start the module main monitor thread, watching for incoming calls on the interfaces */
+int celliax_restart_monitor(void)
+{
+  static struct celliax_pvt *p = &amp;celliax_log_struct;
+  /* If we're supposed to be stopped -- stay stopped */
+  if (celliax_monitor_thread == AST_PTHREADT_STOP)
+    return 0;
+  LOKKA(&amp;celliax_monlock);
+  /* Do not seems possible to me that this function can be called by the very same monitor thread, but let's be paranoid */
+  if (celliax_monitor_thread == pthread_self()) {
+    UNLOCKA(&amp;celliax_monlock);
+    ERRORA(&quot;Cannot kill myself\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  /* if the monitor thread exists */
+  if (celliax_monitor_thread != AST_PTHREADT_NULL) {
+    /* Wake up the thread, it can be stuck waiting in a select or so */
+    pthread_kill(celliax_monitor_thread, SIGURG);
+    pthread_kill(celliax_monitor_audio_thread, SIGURG);
+  } else {
+    /* the monitor thread does not exists, start a new monitor */
+    if (ast_pthread_create(&amp;celliax_monitor_thread, NULL, celliax_do_monitor, NULL) &lt; 0) {
+      UNLOCKA(&amp;celliax_monlock);
+      ERRORA(&quot;Unable to start monitor thread.\n&quot;, CELLIAX_P_LOG);
+      return -1;
+    }
+
+    if (ast_pthread_create
+        (&amp;celliax_monitor_audio_thread, NULL, celliax_do_audio_monitor, NULL) &lt; 0) {
+      ERRORA(&quot;Unable to start audio_monitor thread.\n&quot;, CELLIAX_P_LOG);
+      return -1;
+    }
+
+  }
+  UNLOCKA(&amp;celliax_monlock);
+  return 0;
+}
+
+/*! \brief The celliax monitoring thread 
+ * \note   This thread monitors all the celliax interfaces that are not in a call
+ *         (and thus do not have a separate thread) indefinitely 
+ *         */
+void *celliax_do_monitor(void *data)
+{
+  fd_set rfds;
+  int res;
+  struct celliax_pvt *p = NULL;
+  int max = -1;
+  struct timeval to;
+  time_t now_timestamp;
+
+  if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)) {
+    ERRORA(&quot;Unable to set cancel type to deferred\n&quot;, CELLIAX_P_LOG);
+    return NULL;
+  }
+
+  for (;;) {
+    pthread_testcancel();
+    /* Don't let anybody kill us right away.  Nobody should lock the interface list
+       and wait for the monitor list, but the other way around is okay. */
+    PUSHA_UNLOCKA(&amp;celliax_monlock);
+    MONITORLOKKA(&amp;celliax_monlock);
+    /* Lock the interface list */
+    PUSHA_UNLOCKA(&amp;celliax_iflock);
+    MONITORLOKKA(&amp;celliax_iflock);
+    /* Build the stuff we're going to select on, that is the celliax_serial_fd of every
+       celliax_pvt that does not have an associated owner channel. In the case of FBUS2 3310
+       and in the case of PROTOCOL_NO_SERIAL we add the audio_fd as well, because there is not serial signaling of incoming calls */
+    FD_ZERO(&amp;rfds);
+
+    time(&amp;now_timestamp);
+    p = celliax_iflist;
+    while (p) {
+      if (!p-&gt;owner) {
+        /* This interface needs to be watched, as it lacks an owner */
+
+        if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL &amp;&amp; !p-&gt;controldev_dead) {
+          /* This interface needs its serial connection to be watched, nokia 3310 and compatibles needs sounds as well */
+          if (FD_ISSET(p-&gt;controldevfd, &amp;rfds)) {
+            ERRORA(&quot;Bizarre! Descriptor %d (controldevfd) appears twice ?\n&quot;,
+                   CELLIAX_P_LOG, p-&gt;controldevfd);
+          }
+          if (p-&gt;controldevfd &gt; 0) {
+
+            //time(&amp;now_timestamp);
+            if ((now_timestamp - p-&gt;celliax_serial_synced_timestamp) &gt; p-&gt;celliax_serial_sync_period) { //TODO find a sensible period. 5min? in config?
+              int rt;
+              if (option_debug &gt; 1)
+                DEBUGA_SERIAL(&quot;Syncing Serial\n&quot;, CELLIAX_P_LOG);
+              rt = celliax_serial_sync(p);
+              if (rt) {
+                p-&gt;controldev_dead = 1;
+                close(p-&gt;controldevfd);
+                ERRORA(&quot;serial sync failed, declaring %s dead\n&quot;, CELLIAX_P_LOG,
+                       p-&gt;controldevice_name);
+              }
+              rt = celliax_serial_getstatus(p);
+              if (rt) {
+                p-&gt;controldev_dead = 1;
+                close(p-&gt;controldevfd);
+                ERRORA(&quot;serial getstatus failed, declaring %s dead\n&quot;, CELLIAX_P_LOG,
+                       p-&gt;controldevice_name);
+              }
+
+            }
+
+            if (!p-&gt;controldev_dead) {
+              /* add this file descriptor to the set watched by the select */
+              FD_SET(p-&gt;controldevfd, &amp;rfds);
+              if (p-&gt;controldevfd &gt; max) {
+                /* adjust the maximum file descriptor value the select has to watch for */
+                max = p-&gt;controldevfd;
+              }
+            }
+          }
+        }
+
+      }
+      /* next interface, please */
+      p = p-&gt;next;
+    }
+    /* Okay, now that we know what to do, release the interface lock */
+    MONITORUNLOCKA(&amp;celliax_iflock);
+    POPPA_UNLOCKA(&amp;celliax_iflock);
+    /* And from now on, we're okay to be killed, so release the monitor lock as well */
+    MONITORUNLOCKA(&amp;celliax_monlock);
+    POPPA_UNLOCKA(&amp;celliax_monlock);
+
+    /* you want me to die? */
+    pthread_testcancel();
+
+    /* Wait for something to happen */
+    to.tv_sec = 0;
+    to.tv_usec = 500000;        /* we select with this timeout because under cygwin we avoid the signal usage, so there is no way to end the thread if it is stuck waiting for select */
+    res = ast_select(max + 1, &amp;rfds, NULL, NULL, &amp;to);
+
+    /* you want me to die? */
+    pthread_testcancel();
+
+    /* Okay, select has finished.  Let's see what happened.  */
+
+    /* If there are errors...  */
+    if (res &lt; 0) {
+      if (errno == EINTR)       /* EINTR is just the select 
+                                   being interrupted by a SIGURG, or so */
+        continue;
+      else {
+        ERRORA(&quot;select returned %d: %s\n&quot;, CELLIAX_P_LOG, res, strerror(errno));
+//FIXME what to do here? is the interface that failed signaled? which interface we have to disable?
+        return NULL;
+      }
+    }
+
+    /* must not be killed while celliax_iflist is locked */
+    PUSHA_UNLOCKA(&amp;celliax_monlock);
+    MONITORLOKKA(&amp;celliax_monlock);
+    /* Alright, lock the interface list again, and let's look and see what has
+       happened */
+    PUSHA_UNLOCKA(&amp;celliax_iflock);
+    MONITORLOKKA(&amp;celliax_iflock);
+
+    p = celliax_iflist;
+    for (; p; p = p-&gt;next) {
+
+      if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL &amp;&amp; !p-&gt;controldev_dead) {
+        if (!p-&gt;owner) {        //give all the serial channels that have no owner a read, so we can have the timers clicking
+
+          if (!p-&gt;celliax_serial_monitoring) {
+            p-&gt;celliax_serial_monitoring = 1;
+            res = celliax_serial_monitor(p);
+            if (res == -1) {    //manage the graceful interface shutdown
+              p-&gt;controldev_dead = 1;
+              close(p-&gt;controldevfd);
+              ERRORA(&quot;celliax_serial_monitor failed, declaring %s dead\n&quot;, CELLIAX_P_LOG,
+                     p-&gt;controldevice_name);
+            } else if (!p-&gt;need_acoustic_ring
+                       &amp;&amp; p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL
+                       &amp;&amp; p-&gt;interface_state == AST_STATE_RING) {
+              if (option_debug)
+                DEBUGA_PBX(&quot;INCOMING RING\n&quot;, CELLIAX_P_LOG);
+              if (!celliax_new(p, AST_STATE_RING, p-&gt;context)) {
+                //FIXME what to do here?
+                ERRORA(&quot;celliax_new failed! BAD BAD BAD\n&quot;, CELLIAX_P_LOG);
+              }
+            }
+            p-&gt;celliax_serial_monitoring = 0;
+          }
+        }
+      }
+
+      if (p-&gt;controldevprotocol != PROTOCOL_NO_SERIAL &amp;&amp; p-&gt;controldev_dead) {
+
+        /* init the serial port */
+        p-&gt;controldevfd = celliax_serial_init(p, p-&gt;controldevice_speed);
+        if (p-&gt;controldevfd &lt; 1) {
+          DEBUGA_SERIAL(&quot;celliax_serial_init failed\n&quot;, CELLIAX_P_LOG);
+        } else {
+
+          /* config the phone/modem on the serial port */
+          res = celliax_serial_config(p);
+          if (res) {
+            DEBUGA_SERIAL(&quot;celliax_serial_config failed\n&quot;, CELLIAX_P_LOG);
+            close(p-&gt;controldevfd);
+          } else {
+
+            NOTICA(&quot;Wow, the serial port has come back! Let's see if it will work\n&quot;,
+                   CELLIAX_P_LOG);
+            p-&gt;controldev_dead = 0;
+          }
+
+        }
+
+      }
+
+    }
+    MONITORUNLOCKA(&amp;celliax_iflock);
+    POPPA_UNLOCKA(&amp;celliax_iflock);
+    MONITORUNLOCKA(&amp;celliax_monlock);
+    POPPA_UNLOCKA(&amp;celliax_monlock);
+    pthread_testcancel();
+  }
+/* Never reached */
+  return NULL;
+
+}
+
+void *celliax_do_audio_monitor(void *data)
+{
+  fd_set rfds;
+  int res;
+  struct celliax_pvt *p = NULL;
+  int max = -1;
+  struct timeval to;
+
+  if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)) {
+    ERRORA(&quot;Unable to set cancel type to deferred\n&quot;, CELLIAX_P_LOG);
+    return NULL;
+  }
+
+  for (;;) {
+    pthread_testcancel();
+    /* Lock the interface list */
+    PUSHA_UNLOCKA(&amp;celliax_iflock);
+    MONITORLOKKA(&amp;celliax_iflock);
+
+    FD_ZERO(&amp;rfds);
+
+    p = celliax_iflist;
+    while (p) {
+      if (!p-&gt;owner) {
+        /* This interface needs to be watched, as it lacks an owner */
+
+        if (p-&gt;controldevprotocol == PROTOCOL_NO_SERIAL || p-&gt;need_acoustic_ring) {
+          /* This interface needs its incoming sound to be watched, because it cannot signal incoming ring from serial (eg nokia 3310 and compatibles) */
+          if (p-&gt;celliax_sound_capt_fd &gt; 0) {
+            /* if fd exist */
+            if (FD_ISSET(p-&gt;celliax_sound_capt_fd, &amp;rfds)) {
+              ERRORA(&quot;Bizarre! Descriptor %d (celliax_sound_capt_fd) appears twice ?\n&quot;,
+                     CELLIAX_P_LOG, p-&gt;celliax_sound_capt_fd);
+            }
+            /* add this file descriptor to the set watched by the select */
+            FD_SET(p-&gt;celliax_sound_capt_fd, &amp;rfds);
+            if (p-&gt;celliax_sound_capt_fd &gt; max) {
+              /* adjust the maximum file descriptor value the select has to watch for */
+              max = p-&gt;celliax_sound_capt_fd;
+            }
+          }
+        }
+
+      }
+      /* next interface, please */
+      p = p-&gt;next;
+    }
+    /* Okay, now that we know what to do, release the interface lock */
+
+    MONITORUNLOCKA(&amp;celliax_iflock);
+    POPPA_UNLOCKA(&amp;celliax_iflock);
+    /* you want me to die? */
+    pthread_testcancel();
+
+    /* Wait for something to happen */
+    to.tv_sec = 0;
+    to.tv_usec = 500000;        /* we select with this timeout because under cygwin we avoid the signal usage, so there is no way to end the thread if it is stuck waiting for select */
+    res = ast_select(max + 1, &amp;rfds, NULL, NULL, &amp;to);
+
+    /* you want me to die? */
+    pthread_testcancel();
+
+    /* Okay, select has finished.  Let's see what happened.  */
+
+    /* If there are errors...  */
+    if (res &lt; 0) {
+      if (errno == EINTR) {     /* EINTR is just the select 
+                                   being interrupted by a SIGURG, or so */
+        usleep(100);
+        continue;
+      } else {
+        ERRORA(&quot;select returned %d: %s\n&quot;, CELLIAX_P_LOG, res, strerror(errno));
+//FIXME what to do here? is the interface that failed signaled? which interface we have to disable?
+        return NULL;
+      }
+    }
+    /* If there are no file descriptors changed, just continue  */
+
+    if (res == 0) {
+      usleep(100);              //let's breath
+      continue;
+    }
+//usleep(10); //let's breath
+
+    /* Lock the interface list */
+    PUSHA_UNLOCKA(&amp;celliax_iflock);
+    MONITORLOKKA(&amp;celliax_iflock);
+
+    p = celliax_iflist;
+    for (; p; p = p-&gt;next) {
+
+      if (FD_ISSET(p-&gt;celliax_sound_capt_fd, &amp;rfds)) {
+        res = celliax_sound_monitor(p);
+        if (res &lt; 0) {
+          ERRORA(&quot;celliax_sound_monitor ERROR %d\n&quot;, CELLIAX_P_LOG, res);
+        } else if (res == CALLFLOW_INCOMING_RING) {
+          p-&gt;phone_callflow = CALLFLOW_INCOMING_RING;
+          p-&gt;interface_state = AST_STATE_RING;
+          if (option_debug)
+            DEBUGA_PBX(&quot;INCOMING RING\n&quot;, CELLIAX_P_LOG);
+          if (!celliax_new(p, AST_STATE_RING, p-&gt;context))
+            ERRORA(&quot;celliax_new failed! BAD BAD BAD\n&quot;, CELLIAX_P_LOG);
+        } else {
+        }
+
+      }
+
+    }
+/* Okay, now that we know what to do, release the interface lock */
+
+    MONITORUNLOCKA(&amp;celliax_iflock);
+    POPPA_UNLOCKA(&amp;celliax_iflock);
+
+    pthread_testcancel();
+  }
+/* Never reached */
+  return NULL;
+}
+
+/*!
+ * \brief Initialize the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces) 
+ * \param p the celliax_pvt of the interface
+ *
+ * This function initialize the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces). It simply pass its parameters to the right function for the sound system for which has been compiled, eg. alsa_init for ALSA, oss_init for OSS, winmm_init for Windows Multimedia, etc and return the result 
+ *
+ * \return zero on success, -1 on error.
+ */
+
+int celliax_sound_init(struct celliax_pvt *p)
+{
+#ifdef CELLIAX_ALSA
+  return alsa_init(p);
+#endif /* CELLIAX_ALSA */
+#ifdef CELLIAX_PORTAUDIO
+  return celliax_portaudio_init(p);
+#endif /* CELLIAX_PORTAUDIO */
+
+  return -1;
+}
+
+/*!
+ * \brief Shutdown the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces) 
+ * \param p the celliax_pvt of the interface
+ *
+ * This function shutdown the soundcard channels (input and output) used by one interface (a multichannel soundcard can be used by multiple interfaces). It simply pass its parameters to the right function for the sound system for which has been compiled, eg. alsa_shutdown for ALSA, oss_shutdown for OSS, winmm_shutdown for Windows Multimedia, etc and return the result
+ *
+ * \return zero on success, -1 on error.
+ */
+
+int celliax_sound_shutdown(struct celliax_pvt *p)
+{
+
+#ifdef CELLIAX_ALSA
+  return alsa_shutdown(p);
+#endif /* CELLIAX_ALSA */
+#ifdef CELLIAX_PORTAUDIO
+  return celliax_portaudio_shutdown(p);
+#endif /* CELLIAX_PORTAUDIO */
+
+  return -1;
+}
+
+/*! \brief returns an asterisk frame categorized by dsp algorithms */
+struct ast_frame *celliax_sound_dsp_analize(struct celliax_pvt *p, struct ast_frame *f,
+                                            int dsp_silence_threshold)
+{
+  if (!p-&gt;dsp) {
+    DEBUGA_SOUND(&quot;no dsp, initializing it \n&quot;, CELLIAX_P_LOG);
+    if (celliax_sound_dsp_set(p, dsp_silence_threshold, 1)) {
+      ERRORA(&quot;celliax_sound_dsp_set failed\n&quot;, CELLIAX_P_LOG);
+      return NULL;
+    }
+  }
+
+  /* process with dsp */
+  if (p-&gt;dsp) {
+    if (f-&gt;frametype == AST_FRAME_VOICE) {
+      f = ast_dsp_process(p-&gt;owner, p-&gt;dsp, f);
+    } else {
+      //WARNINGA(&quot;not a VOICE frame ! \n&quot;, CELLIAX_P_LOG);
+    }
+  }
+  return f;
+}
+
+/*! \brief initialize the dsp algorithms and structures */
+int celliax_sound_dsp_set(struct celliax_pvt *p, int dsp_silence_threshold,
+                          int silence_suppression)
+{
+
+/*  let asterisk dsp algorithms detect dtmf */
+  if (p-&gt;dsp) {
+    return 0;
+  }
+  if (option_debug &gt; 1)
+    DEBUGA_SOUND(&quot;alloc dsp \n&quot;, CELLIAX_P_LOG);
+  p-&gt;dsp = ast_dsp_new();
+  if (p-&gt;dsp) {
+    if (silence_suppression) {
+      ast_dsp_set_threshold(p-&gt;dsp, dsp_silence_threshold);
+      DEBUGA_SOUND(&quot;set dsp_silence_threshold=%d\n&quot;, CELLIAX_P_LOG,
+                   dsp_silence_threshold);
+      if (option_debug &gt; 1)
+        DEBUGA_SOUND(&quot;Detecting silence, I mean, voice\n&quot;, CELLIAX_P_LOG);
+      ast_dsp_set_features(p-&gt;dsp, 0 | DSP_FEATURE_SILENCE_SUPPRESS);
+    } else {
+      if (option_debug &gt; 1)
+        DEBUGA_SOUND(&quot;WITHOUT SILENCE_SUPPRESS, Detecting inband dtmf with sw DSP\n&quot;,
+                     CELLIAX_P_LOG);
+
+#ifdef ASTERISK_VERSION_1_6_0_1
+      ast_dsp_set_features(p-&gt;dsp, 0 | DSP_FEATURE_DIGIT_DETECT);
+#else
+      ast_dsp_set_features(p-&gt;dsp, 0 | DSP_FEATURE_DTMF_DETECT);
+#endif /* ASTERISK_VERSION_1_6_0_1 */
+    }
+
+    /*
+       if (ast_dsp_digitmode(p-&gt;dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF)) {
+       ERRORA(&quot;ast_dsp_digitmode failed\n&quot;, CELLIAX_P_LOG);
+       return -1;
+       }
+     */
+  } else {
+    ERRORA(&quot;ast_dsp_new failed\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  return 0;
+}
+
+/*! \brief Read audio frames from interface */
+struct ast_frame *celliax_sound_read(struct celliax_pvt *p)
+{
+  struct ast_frame *f = NULL;
+#ifdef CELLIAX_ALSA
+  f = alsa_read(p);
+#endif /* CELLIAX_ALSA */
+#ifdef CELLIAX_PORTAUDIO
+  f = celliax_portaudio_read(p);
+#endif /* CELLIAX_PORTAUDIO */
+
+  return f;
+}
+
+/*! \brief Send audio frame to interface */
+int celliax_sound_write(struct celliax_pvt *p, struct ast_frame *f)
+{
+  int ret = -1;
+
+#ifdef CELLIAX_ALSA
+  ret = alsa_write(p, f);
+#endif /* CELLIAX_ALSA */
+#ifdef CELLIAX_PORTAUDIO
+  ret = celliax_portaudio_write(p, f);
+#endif /* CELLIAX_PORTAUDIO */
+
+  return ret;
+}
+
+/*! \brief read an audio frame and tell if is &quot;voice&quot; (interpreted as incoming RING) */
+int celliax_sound_monitor(struct celliax_pvt *p)
+{
+  struct ast_frame *f;
+  f = celliax_sound_read(p);
+  if (f) {
+    f = celliax_sound_dsp_analize(p, f, p-&gt;dsp_silence_threshold);
+    if (f) {
+      if (f-&gt;frametype == AST_FRAME_VOICE) {
+        DEBUGA_SOUND(&quot;VOICE\n&quot;, CELLIAX_P_LOG);
+        return CALLFLOW_INCOMING_RING;
+      } else {
+        return AST_STATE_DOWN;
+      }
+    }
+  }
+  return -1;
+}
+
+/*!
+ * \brief This thread runs during a call, and monitor the interface serial port for signaling, like hangup, caller id, etc
+ *
+ */
+void *celliax_do_controldev_thread(void *data)
+{
+  struct celliax_pvt *p = data;
+  int res;
+
+  DEBUGA_SERIAL(&quot;In celliax_do_controldev_thread: started, p=%p\n&quot;, CELLIAX_P_LOG, p);
+
+  if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)) {
+    ERRORA(&quot;Unable to set cancel type to deferred\n&quot;, CELLIAX_P_LOG);
+    return NULL;
+  }
+
+  while (1) {
+    int rt;
+    time_t now_timestamp;
+
+    if (p-&gt;controldevprotocol == PROTOCOL_NO_SERIAL) {
+      while (1) {
+        usleep(10000);
+        pthread_testcancel();
+      }
+    }
+#ifdef CELLIAX_CVM
+    if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL) {
+      usleep(p-&gt;cvm_celliax_serial_delay * 1000);   //to get msecs
+    } else {
+      usleep(1000);
+    }
+
+#else
+    usleep(1000);
+#endif /* CELLIAX_CVM */
+
+    pthread_testcancel();
+    /* do not read from a dead controldev */
+    if (p-&gt;controldev_dead) {
+      DEBUGA_SERIAL(&quot;celliax_do_controldev_thread: device %s is dead\n&quot;, CELLIAX_P_LOG,
+                    p-&gt;controldevice_name);
+      if (p-&gt;owner)
+        celliax_queue_control(p-&gt;owner, AST_CONTROL_HANGUP);
+      return NULL;
+    } else {
+      pthread_testcancel();
+      res = celliax_serial_read(p);
+      pthread_testcancel();
+      if (res == -1) {
+        p-&gt;controldev_dead = 1;
+        close(p-&gt;controldevfd);
+        ERRORA(&quot;serial read failed, declaring %s dead\n&quot;, CELLIAX_P_LOG,
+               p-&gt;controldevice_name);
+      }
+    }
+
+    pthread_testcancel();
+    time(&amp;now_timestamp);
+    if ((now_timestamp - p-&gt;celliax_serial_synced_timestamp) &gt; p-&gt;celliax_serial_synced_timestamp &amp;&amp; !p-&gt;controldev_dead) { //TODO find a sensible period. 5min? in config?
+      DEBUGA_SERIAL(&quot;Syncing Serial\n&quot;, CELLIAX_P_LOG);
+      pthread_testcancel();
+      rt = celliax_serial_sync(p);
+      pthread_testcancel();
+      if (rt) {
+        p-&gt;controldev_dead = 1;
+        close(p-&gt;controldevfd);
+        ERRORA(&quot;serial sync failed, declaring %s dead\n&quot;, CELLIAX_P_LOG,
+               p-&gt;controldevice_name);
+      }
+      pthread_testcancel();
+      rt = celliax_serial_getstatus(p);
+      pthread_testcancel();
+      if (rt) {
+        p-&gt;controldev_dead = 1;
+        close(p-&gt;controldevfd);
+        ERRORA(&quot;serial getstatus failed, declaring %s dead\n&quot;, CELLIAX_P_LOG,
+               p-&gt;controldevice_name);
+      }
+
+    }
+    pthread_testcancel();
+  }
+  return NULL;
+
+}
+
+int celliax_serial_init(struct celliax_pvt *p, 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 = p-&gt;controldevfd;
+  if (fd) {
+    close(fd);
+  }
+/*  open the serial port */
+#ifdef __CYGWIN__
+  fd = open(p-&gt;controldevice_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
+  sleep(1);
+  close(fd);
+#endif /* __CYGWIN__ */
+  fd = open(p-&gt;controldevice_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
+  if (fd == -1) {
+    DEBUGA_SERIAL(&quot;serial error: %s\n&quot;, CELLIAX_P_LOG, strerror(errno));
+    p-&gt;controldevfd = fd;
+    return -1;
+  }
+/*  flush it */
+  rt = tcflush(fd, TCIFLUSH);
+  if (rt == -1) {
+    ERRORA(&quot;serial error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+  }
+/*  attributes */
+  tp.c_cflag = B0 | CS8 | CLOCAL | CREAD | HUPCL;
+  tp.c_iflag = IGNPAR;
+  tp.c_cflag &amp;= ~CRTSCTS;
+  tp.c_oflag = 0;
+  tp.c_lflag = 0;
+  tp.c_cc[VMIN] = 1;
+  tp.c_cc[VTIME] = 0;
+/*  set controldevice_speed */
+  rt = cfsetispeed(&amp;tp, p-&gt;controldevice_speed);
+  if (rt == -1) {
+    ERRORA(&quot;serial error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+  }
+  rt = cfsetospeed(&amp;tp, p-&gt;controldevice_speed);
+  if (rt == -1) {
+    ERRORA(&quot;serial error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+  }
+/*  set port attributes */
+  if (tcsetattr(fd, TCSADRAIN, &amp;tp) == -1) {
+    ERRORA(&quot;serial error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+  }
+  rt = tcsetattr(fd, TCSANOW, &amp;tp);
+  if (rt == -1) {
+    ERRORA(&quot;serial error: %s&quot;, CELLIAX_P_LOG, strerror(errno));
+  }
+  unsigned int status = 0;
+#ifndef __CYGWIN__
+  ioctl(fd, TIOCMGET, &amp;status);
+  status |= TIOCM_DTR;          /*  Set DTR high */
+  status &amp;= ~TIOCM_RTS;         /*  Set RTS low */
+  ioctl(fd, TIOCMSET, &amp;status);
+  ioctl(fd, TIOCMGET, &amp;status);
+  unsigned int flags = TIOCM_DTR;
+  ioctl(fd, TIOCMBIS, &amp;flags);
+  flags = TIOCM_RTS;
+  ioctl(fd, TIOCMBIC, &amp;flags);
+  ioctl(fd, TIOCMGET, &amp;status);
+#else /* __CYGWIN__ */
+  ioctl(fd, TIOCMGET, &amp;status);
+  status |= TIOCM_DTR;          /*  Set DTR high */
+  status &amp;= ~TIOCM_RTS;         /*  Set RTS low */
+  ioctl(fd, TIOCMSET, &amp;status);
+#endif /* __CYGWIN__ */
+  p-&gt;controldevfd = fd;
+  DEBUGA_SERIAL(&quot;Syncing Serial\n&quot;, CELLIAX_P_LOG);
+  rt = celliax_serial_sync(p);
+  if (rt == -1) {
+    ERRORA(&quot;Serial init error\n&quot;, CELLIAX_P_LOG);
+    return -1;
+  }
+  return (fd);
+}
+
+int celliax_serial_sync(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_sync_AT(p);
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_sync_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_sync_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+
+  return -1;
+}
+
+int celliax_serial_getstatus(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_getstatus_AT(p);
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_getstatus_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_getstatus_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+int celliax_serial_read(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_read_AT(p, 0, 100000, 0, NULL, 1);    // a 10th of a second timeout
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_read_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_read_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+int celliax_serial_hangup(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_hangup_AT(p);
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_hangup_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_hangup_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+int celliax_serial_answer(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_answer_AT(p);
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_answer_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_answer_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+int celliax_serial_config(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_config_AT(p);
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_config_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_config_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+int celliax_serial_monitor(struct celliax_pvt *p)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_read_AT(p, 0, 100000, 0, NULL, 1);    // a 10th of a second timeout
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_read_FBUS2(p);
+#endif /* CELLIAX_FBUS2 */
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_read_CVM_BUSMAIL(p);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+/************************************************/
+
+/* LUIGI RIZZO's magic */
+/*
+ * store the boost factor
+ */
+#ifdef ASTERISK_VERSION_1_6_0
+void celliax_store_boost(const char *s, double *boost)
+#else
+void celliax_store_boost(char *s, double *boost)
+#endif                          /* ASTERISK_VERSION_1_6_0 */
+{
+  struct celliax_pvt *p = NULL;
+
+  if (sscanf(s, &quot;%lf&quot;, boost) != 1) {
+    ERRORA(&quot;invalid boost &lt;%s&gt;\n&quot;, CELLIAX_P_LOG, s);
+    return;
+  }
+  if (*boost &lt; -BOOST_MAX) {
+    WARNINGA(&quot;boost %s too small, using %d\n&quot;, CELLIAX_P_LOG, s, -BOOST_MAX);
+    *boost = -BOOST_MAX;
+  } else if (*boost &gt; BOOST_MAX) {
+    WARNINGA(&quot;boost %s too large, using %d\n&quot;, CELLIAX_P_LOG, s, BOOST_MAX);
+    *boost = BOOST_MAX;
+  }
+  *boost = exp(log(10) * *boost / 20) * BOOST_SCALE;
+  if (option_debug &gt; 1)
+    DEBUGA_SOUND(&quot;setting boost %s to %f\n&quot;, CELLIAX_P_LOG, s, *boost);
+}
+
+int celliax_serial_call(struct celliax_pvt *p, char *dstr)
+{
+  if (p-&gt;controldevprotocol == PROTOCOL_AT)
+    return celliax_serial_call_AT(p, dstr);
+#ifdef CELLIAX_FBUS2
+  if (p-&gt;controldevprotocol == PROTOCOL_FBUS2)
+    return celliax_serial_call_FBUS2(p, dstr);
+#endif /* CELLIAX_FBUS2 */
+  if (p-&gt;controldevprotocol == PROTOCOL_NO_SERIAL)
+    return 0;
+#ifdef CELLIAX_CVM
+  if (p-&gt;controldevprotocol == PROTOCOL_CVM_BUSMAIL)
+    return celliax_serial_call_CVM_BUSMAIL(p, dstr);
+#endif /* CELLIAX_CVM */
+  return -1;
+}
+
+/*
+ * returns a pointer to the descriptor with the given name
+ */
+struct celliax_pvt *celliax_console_find_desc(char *dev)
+{
+  struct celliax_pvt *p;
+
+  for (p = celliax_iflist; p &amp;&amp; strcmp(p-&gt;name, dev) != 0; p = p-&gt;next);
+  if (p == NULL)
+    WARNINGA(&quot;could not find &lt;%s&gt;\n&quot;, CELLIAX_P_LOG, dev);
+
+  return p;
+}
+
+int celliax_console_playback_boost(int fd, int argc, char *argv[])
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+  if (argc &gt; 2)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; celliax_console for playback_boost, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (argc == 1) {
+    ast_cli(fd, &quot;playback_boost on the active celliax_console, that is [%s], is: %5.1f\n&quot;,
+            celliax_console_active,
+            20 * log10(((double) p-&gt;playback_boost / (double) BOOST_SCALE)));
+  } else if (argc == 2) {
+    celliax_store_boost(argv[1], &amp;p-&gt;playback_boost);
+
+    ast_cli(fd,
+            &quot;playback_boost on the active celliax_console, that is [%s], is now: %5.1f\n&quot;,
+            celliax_console_active,
+            20 * log10(((double) p-&gt;playback_boost / (double) BOOST_SCALE)));
+  }
+
+  return RESULT_SUCCESS;
+}
+
+int celliax_console_capture_boost(int fd, int argc, char *argv[])
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+  if (argc &gt; 2)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; celliax_console for capture_boost, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (argc == 1) {
+    ast_cli(fd, &quot;capture_boost on the active celliax_console, that is [%s], is: %5.1f\n&quot;,
+            celliax_console_active,
+            20 * log10(((double) p-&gt;capture_boost / (double) BOOST_SCALE)));
+  } else if (argc == 2) {
+    celliax_store_boost(argv[1], &amp;p-&gt;capture_boost);
+
+    ast_cli(fd,
+            &quot;capture_boost on the active celliax_console, that is [%s], is now: %5.1f\n&quot;,
+            celliax_console_active,
+            20 * log10(((double) p-&gt;capture_boost / (double) BOOST_SCALE)));
+  }
+
+  return RESULT_SUCCESS;
+}
+
+int celliax_console_echo(int fd, int argc, char *argv[])
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+  if (argc != 3 &amp;&amp; argc != 1)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; celliax_console for celliax_echo, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (argc == 1) {
+    ast_cli(fd,
+            &quot;On the active celliax_console, that is [%s], speexecho and speexpreprocess are: %d and %d\n&quot;,
+            celliax_console_active, p-&gt;speexecho, p-&gt;speexpreprocess);
+  } else if (argc == 3) {
+    sscanf(argv[1], &quot;%d&quot;, &amp;p-&gt;speexecho);
+    sscanf(argv[2], &quot;%d&quot;, &amp;p-&gt;speexpreprocess);
+    ast_cli(fd,
+            &quot;On the active celliax_console, that is [%s], speexecho and speexpreprocess are NOW: %d and %d\n&quot;,
+            celliax_console_active, p-&gt;speexecho, p-&gt;speexpreprocess);
+  }
+
+  return RESULT_SUCCESS;
+}
+
+#ifndef ASTERISK_VERSION_1_6_0
+int celliax_console_hangup(int fd, int argc, char *argv[])
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+  if (argc != 1)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; celliax_console for hanging up, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+  if (!p-&gt;owner) {
+    ast_cli(fd, &quot;No call to hangup on the active celliax_console, that is [%s]\n&quot;,
+            celliax_console_active);
+    return RESULT_FAILURE;
+  }
+  if (p-&gt;owner)
+    ast_queue_hangup(p-&gt;owner);
+  return RESULT_SUCCESS;
+}
+#else /* ASTERISK_VERSION_1_6_0 */
+char *celliax_console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+
+        switch (cmd) {
+        case CLI_INIT:
+                e-&gt;command = &quot;celliax hangup&quot;;
+                e-&gt;usage =
+                        &quot;Usage: celliax hangup\n&quot;
+                        &quot;       Hangup a call on the celliax channel.\n&quot;;
+
+                return NULL;
+        case CLI_GENERATE:
+                return NULL; 
+        }
+
+  if (a-&gt;argc != 2)
+    return CLI_SHOWUSAGE;
+  if (!p) {
+    ast_cli(a-&gt;fd,
+            &quot;No \&quot;current\&quot; celliax_console for hanging up, please enter 'help celliax_console'\n&quot;);
+    return CLI_SUCCESS;
+  }
+  if (!p-&gt;owner) {
+    ast_cli(a-&gt;fd, &quot;No call to hangup on the active celliax_console, that is [%s]\n&quot;,
+            celliax_console_active);
+    return CLI_FAILURE;
+  }
+  if (p-&gt;owner)
+    ast_queue_hangup(p-&gt;owner);
+  return CLI_SUCCESS;
+}
+
+#endif /* ASTERISK_VERSION_1_6_0 */
+int celliax_console_sendsms(int fd, int argc, char *argv[])
+{
+  char *s = NULL;
+  char *s1 = NULL;
+  char command[512];
+
+  if (argc != 3)
+    return RESULT_SHOWUSAGE;
+
+  s = argv[1];
+  s1 = argv[2];
+
+  memset(command, 0, sizeof(command));
+
+  sprintf(command, &quot;%s|%s|&quot;, s, s1);
+
+  celliax_sendsms(NULL, (void *) &amp;command);
+
+  return RESULT_SUCCESS;
+}
+
+int celliax_console_dial(int fd, int argc, char *argv[])
+{
+  char *s = NULL;
+  struct celliax_pvt *p = celliax_console_find_desc(celliax_console_active);
+
+  if (argc != 2)
+    return RESULT_SHOWUSAGE;
+  if (!p) {
+    ast_cli(fd,
+            &quot;No \&quot;current\&quot; celliax_console for dialing, please enter 'help celliax_console'\n&quot;);
+    return RESULT_SUCCESS;
+  }
+
+  if (p-&gt;owner) {               /* already in a call */
+    int i;
+    struct ast_frame f = { AST_FRAME_DTMF, 0 };
+
+    s = argv[1];
+    /* send the string one char at a time */
+    for (i = 0; i &lt; strlen(s); i++) {
+      f.subclass = s[i];
+      ast_queue_frame(p-&gt;owner, &amp;f);
+    }
+    return RESULT_SUCCESS;
+  } else
+    ast_cli(fd,
+            &quot;No call in which to dial on the \&quot;current\&quot; celliax_console, that is [%s]\n&quot;,
+            celliax_console_active);
+  if (s)
+    free(s);
+  return RESULT_SUCCESS;
+}
+
+int celliax_console_set_active(int fd, int argc, char *argv[])
+{
+  if (argc == 1)
+    ast_cli(fd,
+            &quot;\&quot;current\&quot; celliax_console is [%s]\n    Enter 'celliax_console show' to see the available interfaces.\n    Enter 'celliax_console interfacename' to change the \&quot;current\&quot; celliax_console.\n&quot;,
+            celliax_console_active);
+  else if (argc != 2)
+    return RESULT_SHOWUSAGE;
+  else {
+    struct celliax_pvt *p;
+    if (strcmp(argv[1], &quot;show&quot;) == 0) {
+      ast_cli(fd, &quot;Available interfaces:\n&quot;);
+      for (p = celliax_iflist; p; p = p-&gt;next)
+        ast_cli(fd, &quot;     [%s]\n&quot;, p-&gt;name);
+      return RESULT_SUCCESS;
+    }
+    p = celliax_console_find_desc(argv[1]);
+    if (p == NULL)
+      ast_cli(fd, &quot;Interface [%s] do not exists!\n&quot;, argv[1]);
+    else {
+      celliax_console_active = p-&gt;name;
+      ast_cli(fd, &quot;\&quot;current\&quot; celliax_console is now: [%s]\n&quot;, argv[1]);
+    }
+  }
+  return RESULT_SUCCESS;
+}
+
+int celliax_console_celliax(int fd, int argc, char *argv[])
+{
+  return RESULT_SHOWUSAGE;
+}
+
+#ifdef ASTERISK_VERSION_1_4
+#ifndef AST_MODULE
+#define AST_MODULE &quot;chan_celliax&quot;
+#endif
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, &quot;Celliax, Audio-Serial Driver&quot;);
+#endif /* ASTERISK_VERSION_1_4 */
</ins></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxasteriskciapalo"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/asterisk/ciapalo (0 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/asterisk/ciapalo                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/asterisk/ciapalo        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+#!/usr/bin/perl
+open(FILEOUT, &quot;&gt;&gt; /tmp/smses_received&quot;);
+while(&lt;&gt;){
+       print FILEOUT  $_ ;
+       printf FILEOUT  &quot;\n&quot; ;
+}
+close(FILEOUT);
+
</ins><span class="cx">Property changes on: freeswitch/branches/gmaruzz/mod_celliax/asterisk/ciapalo
</span><span class="cx">___________________________________________________________________
</span><span class="cx">Name: svn:executable
</span><span class="cx">   + *
</span></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxmod_celliaxc"></a>
<div class="modfile"><h4>Modified: freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c (14802 => 14803)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c        2009-09-10 05:49:25 UTC (rev 14802)
+++ freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c        2009-09-10 08:20:20 UTC (rev 14803)
</span><span class="lines">@@ -84,12 +84,14 @@
</span><span class="cx"> SWITCH_MODULE_LOAD_FUNCTION(mod_celliax_load);
</span><span class="cx"> SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_celliax_shutdown);
</span><span class="cx"> SWITCH_MODULE_DEFINITION(mod_celliax, mod_celliax_load, mod_celliax_shutdown, NULL);
</span><ins>+#if 0
</ins><span class="cx"> SWITCH_STANDARD_API(sk_function);
</span><span class="cx"> /* BEGIN: Changes here */
</span><span class="cx"> #define SK_SYNTAX &quot;list [full] || console || celliax_API_msg || remove &lt; celliaxusername | #interface_name | #interface_id &gt; || reload&quot;
</span><span class="cx"> /* END: Changes heres */
</span><span class="cx"> SWITCH_STANDARD_API(celliax_function);
</span><span class="cx"> #define CELLIAX_SYNTAX &quot;interface_name celliax_API_msg&quot;
</span><ins>+#endif //0
</ins><span class="cx"> 
</span><span class="cx"> SWITCH_STANDARD_API(sendsms_function);
</span><span class="cx"> #define SENDSMS_SYNTAX &quot;sendsms interface_name destination_number SMS_text&quot;
</span><span class="lines">@@ -179,7 +181,9 @@
</span><span class="cx"> 
</span><span class="cx"> /* BEGIN: Changes here */
</span><span class="cx"> static switch_status_t interface_exists(char *the_interface);
</span><ins>+#if 0
</ins><span class="cx"> static switch_status_t remove_interface(char *the_interface);
</span><ins>+#endif //0
</ins><span class="cx"> /* END: Changes here */
</span><span class="cx"> 
</span><span class="cx"> static switch_status_t channel_on_init(switch_core_session_t *session);
</span><span class="lines">@@ -323,6 +327,7 @@
</span><span class="cx">         return SWITCH_STATUS_FALSE;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if 0
</ins><span class="cx"> static switch_status_t remove_interface(char *the_interface)
</span><span class="cx"> {
</span><span class="cx">         int x = 10;
</span><span class="lines">@@ -431,6 +436,7 @@
</span><span class="cx">         //running = 1;
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="cx"> }
</span><ins>+#endif //0
</ins><span class="cx"> 
</span><span class="cx"> /* END: Changes here */
</span><span class="cx"> 
</span><span class="lines">@@ -1479,9 +1485,11 @@
</span><span class="cx"> 
</span><span class="cx">         if (running) {
</span><span class="cx"> 
</span><ins>+#if 0
</ins><span class="cx">                 SWITCH_ADD_API(commands_api_interface, &quot;sk&quot;, &quot;Celliax console commands&quot;, sk_function, SK_SYNTAX);
</span><span class="cx">                 SWITCH_ADD_API(commands_api_interface, &quot;celliax&quot;, &quot;Celliax interface commands&quot;, celliax_function, CELLIAX_SYNTAX);
</span><del>-                SWITCH_ADD_API(commands_api_interface, &quot;sendsms&quot;, &quot;sendsms interface destination_number SMS_text&quot;, sendsms_function, CELLIAX_SYNTAX);
</del><ins>+#endif //0
+                SWITCH_ADD_API(commands_api_interface, &quot;sendsms&quot;, &quot;sendsms interface destination_number SMS_text&quot;, sendsms_function, SENDSMS_SYNTAX);
</ins><span class="cx"> 
</span><span class="cx">                 /* indicate that the module should continue to be loaded */
</span><span class="cx">                 return SWITCH_STATUS_SUCCESS;
</span><span class="lines">@@ -1783,6 +1791,7 @@
</span><span class="cx">         return NULL;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if 0
</ins><span class="cx"> SWITCH_STANDARD_API(sk_function)
</span><span class="cx"> {
</span><span class="cx">         char *mycmd = NULL, *argv[10] = { 0 };
</span><span class="lines">@@ -1938,6 +1947,7 @@
</span><span class="cx"> 
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="cx"> }
</span><ins>+#endif //0
</ins><span class="cx"> 
</span><span class="cx"> SWITCH_STANDARD_API(sendsms_function)
</span><span class="cx"> {
</span></span></pre>
</div>
</div>
<div id="footer">See you at ClueCon</div>

</body>
</html>