<!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][14551] </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=14551">14551</a></dd>
<dt>Author</dt> <dd>gmaruzz</dd>
<dt>Date</dt> <dd>2009-08-18 04:37:23 -0500 (Tue, 18 Aug 2009)</dd>
</dl>

<h3>Log Message</h3>
<pre>celliax: added chan_celliax.c and celliax_additional.c from asterisk distr, beginning to modifying mod_celliax.c</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxcelliax_protocolc">freeswitch/branches/gmaruzz/mod_celliax/celliax_protocol.c</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><a href="#freeswitchbranchesgmaruzzmod_celliaxcelliax_additionalc">freeswitch/branches/gmaruzz/mod_celliax/celliax_additional.c</a></li>
<li><a href="#freeswitchbranchesgmaruzzmod_celliaxchan_celliaxc">freeswitch/branches/gmaruzz/mod_celliax/chan_celliax.c</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="freeswitchbranchesgmaruzzmod_celliaxcelliax_additionalc"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/celliax_additional.c (0 => 14551)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/celliax_additional.c                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/celliax_additional.c        2009-08-18 09:37:23 UTC (rev 14551)
</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_celliaxcelliax_protocolc"></a>
<div class="modfile"><h4>Modified: freeswitch/branches/gmaruzz/mod_celliax/celliax_protocol.c (14550 => 14551)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/celliax_protocol.c        2009-08-18 05:33:42 UTC (rev 14550)
+++ freeswitch/branches/gmaruzz/mod_celliax/celliax_protocol.c        2009-08-18 09:37:23 UTC (rev 14551)
</span><span class="lines">@@ -1406,6 +1406,7 @@
</span><span class="cx"> {
</span><span class="cx"> 
</span><span class="cx">         private_t *tech_pvt = obj;
</span><ins>+#if 0
</ins><span class="cx">         struct SkypiaxHandles *SkypiaxHandles;
</span><span class="cx">         char buf[512];
</span><span class="cx">         Display *disp = NULL;
</span><span class="lines">@@ -1441,7 +1442,10 @@
</span><span class="cx"> 
</span><span class="cx">         SkypiaxHandles-&gt;disp = disp;
</span><span class="cx"> 
</span><del>-        if (celliax_present(SkypiaxHandles)) {
</del><ins>+#endif// 0
+        //if (celliax_present(SkypiaxHandles)) 
+        if (1) {
+#if 0
</ins><span class="cx">                 root = DefaultRootWindow(disp);
</span><span class="cx">                 win = XCreateSimpleWindow(disp, root, 0, 0, 1, 1, 0, BlackPixel(disp, DefaultScreen(disp)), BlackPixel(disp, DefaultScreen(disp)));
</span><span class="cx"> 
</span><span class="lines">@@ -1474,7 +1478,11 @@
</span><span class="cx"> 
</span><span class="cx">                 b = buffer;
</span><span class="cx"> 
</span><ins>+#endif // 0
</ins><span class="cx">                 while (1) {
</span><ins>+                        celliax_sleep(1000000); //1 sec
+                        DEBUGA_SKYPE(&quot;ciao!\n&quot;, SKYPIAX_P_LOG);
+#if 0
</ins><span class="cx">                         XNextEvent(disp, &amp;an_event);
</span><span class="cx">                         if (!(running &amp;&amp; tech_pvt-&gt;running))
</span><span class="cx">                                 break;
</span><span class="lines">@@ -1535,6 +1543,7 @@
</span><span class="cx">                         default:
</span><span class="cx">                                 break;
</span><span class="cx">                         }
</span><ins>+#endif //0
</ins><span class="cx">                 }
</span><span class="cx">         } else {
</span><span class="cx">                 ERRORA(&quot;Skype is not running, maybe crashed. Please run/restart Skype and relaunch Skypiax\n&quot;, SKYPIAX_P_LOG);
</span></span></pre></div>
<a id="freeswitchbranchesgmaruzzmod_celliaxchan_celliaxc"></a>
<div class="addfile"><h4>Added: freeswitch/branches/gmaruzz/mod_celliax/chan_celliax.c (0 => 14551)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/chan_celliax.c                                (rev 0)
+++ freeswitch/branches/gmaruzz/mod_celliax/chan_celliax.c        2009-08-18 09:37:23 UTC (rev 14551)
</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_celliaxmod_celliaxc"></a>
<div class="modfile"><h4>Modified: freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c (14550 => 14551)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c        2009-08-18 05:33:42 UTC (rev 14550)
+++ freeswitch/branches/gmaruzz/mod_celliax/mod_celliax.c        2009-08-18 09:37:23 UTC (rev 14551)
</span><span class="lines">@@ -1269,16 +1269,13 @@
</span><span class="cx"> 
</span><span class="cx">                                 switch_threadattr_create(&amp;celliax_api_thread_attr, celliax_module_pool);
</span><span class="cx">                                 switch_threadattr_stacksize_set(celliax_api_thread_attr, SWITCH_THREAD_STACKSIZE);
</span><del>-                                switch_thread_create(&amp;globals.SKYPIAX_INTERFACES[interface_id].celliax_api_thread,
-                                                                         celliax_api_thread_attr, celliax_do_skypeapi_thread, &amp;globals.SKYPIAX_INTERFACES[interface_id], celliax_module_pool);
</del><ins>+                                switch_thread_create(&amp;globals.SKYPIAX_INTERFACES[interface_id].celliax_api_thread, celliax_api_thread_attr, celliax_do_skypeapi_thread, &amp;globals.SKYPIAX_INTERFACES[interface_id], celliax_module_pool);
</ins><span class="cx"> 
</span><span class="cx">                                 switch_sleep(100000);
</span><span class="cx"> 
</span><span class="cx">                                 switch_threadattr_create(&amp;celliax_signaling_thread_attr, celliax_module_pool);
</span><span class="cx">                                 switch_threadattr_stacksize_set(celliax_signaling_thread_attr, SWITCH_THREAD_STACKSIZE);
</span><del>-                                switch_thread_create(&amp;globals.SKYPIAX_INTERFACES[interface_id].
-                                                                         celliax_signaling_thread, celliax_signaling_thread_attr,
-                                                                         celliax_signaling_thread_func, &amp;globals.SKYPIAX_INTERFACES[interface_id], celliax_module_pool);
</del><ins>+                                switch_thread_create(&amp;globals.SKYPIAX_INTERFACES[interface_id].celliax_signaling_thread, celliax_signaling_thread_attr, celliax_signaling_thread_func, &amp;globals.SKYPIAX_INTERFACES[interface_id], celliax_module_pool);
</ins><span class="cx"> 
</span><span class="cx">                                 switch_sleep(100000);
</span><span class="cx"> 
</span></span></pre>
</div>
</div>
<div id="footer">See you at ClueCon</div>

</body>
</html>