Merged revisions 328247 via svnmerge from
[asterisk/asterisk.git] / res / res_fax_spandsp.c
index 630cccb..296dd07 100644 (file)
@@ -5,6 +5,22 @@
  *
  * Matthew Nicholson <mnicholson@digium.com>
  *
+ * Initial T.38-gateway code
+ * 2008, Daniel Ferenci <daniel.ferenci@nethemba.com>
+ * Created by Nethemba s.r.o. http://www.nethemba.com
+ * Sponsored by IPEX a.s. http://www.ipex.cz
+ *
+ * T.38-gateway integration into asterisk app_fax and rework
+ * 2008, Gregory Hinton Nietsky <gregory@dnstelecom.co.za>
+ * dns Telecom http://www.dnstelecom.co.za
+ *
+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
+ * 2010, Anton Verevkin <mymail@verevkin.it>
+ * ViaNetTV http://www.vianettv.com
+ *
+ * Modified to make T.38-gateway work
+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
+ *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
  * any of the maintainers of this project for assistance;
  * \brief Spandsp T.38 and G.711 FAX Resource
  *
  * \author Matthew Nicholson <mnicholson@digium.com>
+ * \author Gregory H. Nietsky <gregory@distrotech.co.za>
  *
  * This module registers the Spandsp FAX technology with the res_fax module.
  */
 
 /*** MODULEINFO
-               <depend>spandsp</depend>
+       <depend>spandsp</depend>
+       <depend>res_fax</depend>
+       <support_level>extended</support_level>
 ***/
 
 #include "asterisk.h"
@@ -45,9 +64,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/timing.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/res_fax.h"
+#include "asterisk/channel.h"
 
 #define SPANDSP_FAX_SAMPLES 160
 #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES      /* 50 ticks per second, 20ms, 160 samples per second */
+#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
 
 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
 static void spandsp_fax_destroy(struct ast_fax_session *s);
@@ -56,10 +77,14 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
 static int spandsp_fax_start(struct ast_fax_session *s);
 static int spandsp_fax_cancel(struct ast_fax_session *s);
 static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
+static int spandsp_fax_gateway_start(struct ast_fax_session *s);
+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
+static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s);
 
 static char *spandsp_fax_cli_show_capabilities(int fd);
 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
 static char *spandsp_fax_cli_show_stats(int fd);
+static char *spandsp_fax_cli_show_settings(int fd);
 
 static struct ast_fax_tech spandsp_fax_tech = {
        .type = "Spandsp",
@@ -73,7 +98,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
         */
        .version = "pre-20090220",
 #endif
-       .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,
+       .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY,
        .new_session = spandsp_fax_new,
        .destroy_session = spandsp_fax_destroy,
        .read = spandsp_fax_read,
@@ -84,6 +109,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
        .cli_show_capabilities = spandsp_fax_cli_show_capabilities,
        .cli_show_session = spandsp_fax_cli_show_session,
        .cli_show_stats = spandsp_fax_cli_show_stats,
+       .cli_show_settings = spandsp_fax_cli_show_settings,
 };
 
 struct spandsp_fax_stats {
@@ -111,6 +137,7 @@ static struct {
 struct spandsp_pvt {
        unsigned int ist38:1;
        unsigned int isdone:1;
+       enum ast_t38_state ast_t38_state;
        fax_state_t fax_state;
        t38_terminal_state_t t38_state;
        t30_state_t *t30_state;
@@ -118,6 +145,9 @@ struct spandsp_pvt {
 
        struct spandsp_fax_stats *stats;
 
+       struct spandsp_fax_gw_stats *t38stats;
+       t38_gateway_state_t t38_gw_state;
+
        struct ast_timer *timer;
        AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
 };
@@ -155,7 +185,9 @@ static void session_destroy(struct spandsp_pvt *p)
  */
 static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
 {
-       struct spandsp_pvt *p = data;
+       int res = -1;
+       struct ast_fax_session *s = data;
+       struct spandsp_pvt *p = s->tech_pvt;
        struct ast_frame fax_frame = {
                .frametype = AST_FRAME_MODEM,
                .subclass.integer = AST_MODEM_T38,
@@ -171,13 +203,23 @@ static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, c
        AST_FRAME_SET_BUFFER(f, buf, 0, len);
 
        if (!(f = ast_frisolate(f))) {
-               return -1;
+               return res;
        }
 
-       /* no need to lock, this all runs in the same thread */
-       AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               ast_set_flag(f, AST_FAX_FRFLAG_GATEWAY);
+               if (p->ast_t38_state == T38_STATE_NEGOTIATED) {
+                       res = ast_write(s->chan, f);
+               } else {
+                       res = ast_queue_frame(s->chan, f);
+               }
+               ast_frfree(f);
+       } else {
+               /* no need to lock, this all runs in the same thread */
+               AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
+       }
 
-       return 0;
+       return res;
 }
 
 static int update_stats(struct spandsp_pvt *p, int completion_code)
@@ -363,7 +405,7 @@ static void spandsp_log(int level, const char *msg)
        } else if (level == SPAN_LOG_WARNING) {
                ast_log(LOG_WARNING, "%s", msg);
        } else {
-               ast_log(LOG_DEBUG, "%s", msg);
+               ast_fax_log(LOG_DEBUG, msg);
        }
 }
 
@@ -404,7 +446,7 @@ static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *det
 
 static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details)
 {
-       t30_set_ecm_capability(t30_state, (details->option.ecm == AST_FAX_OPTFLAG_DEFAULT) ? 1 : details->option.ecm );
+       t30_set_ecm_capability(t30_state, details->option.ecm);
        t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
 }
 
@@ -419,6 +461,11 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
                goto e_return;
        }
 
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               s->state = AST_FAX_STATE_INITIALIZED;
+               return p;
+       }
+
        AST_LIST_HEAD_INIT(&p->read_frames);
 
        if (s->details->caps & AST_FAX_TECH_RECEIVE) {
@@ -447,7 +494,7 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
                }
 
                /* init t38 stuff */
-               t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p);
+               t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, s);
                set_logging(&p->t38_state.logging, s->details);
        }
 
@@ -472,7 +519,12 @@ static void spandsp_fax_destroy(struct ast_fax_session *s)
 {
        struct spandsp_pvt *p = s->tech_pvt;
 
-       session_destroy(p);
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               spandsp_fax_gateway_cleanup(s);
+       } else {
+               session_destroy(p);
+       }
+
        ast_free(p);
        s->tech_pvt = NULL;
        s->fd = -1;
@@ -489,11 +541,10 @@ static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s)
 
        struct ast_frame fax_frame = {
                .frametype = AST_FRAME_VOICE,
-               .subclass.codec = AST_FORMAT_SLINEAR,
                .src = "res_fax_spandsp_g711",
        };
-
        struct ast_frame *f = &fax_frame;
+       ast_format_set(&fax_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
 
        ast_timer_ack(p->timer, 1);
 
@@ -534,6 +585,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
 {
        struct spandsp_pvt *p = s->tech_pvt;
 
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               return spandsp_fax_gateway_process(s, f);
+       }
+
        /* XXX do we need to lock here? */
        if (s->state == AST_FAX_STATE_COMPLETE) {
                ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
@@ -547,6 +602,182 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
        }
 }
 
+/*! \brief generate T.30 packets sent to the T.30 leg of gateway
+ * \param chan T.30 channel
+ * \param data fax session structure
+ * \param len not used
+ * \param samples no of samples generated
+ * \return -1 on failure or 0 on sucess*/
+static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len, int samples)
+{
+       int res = -1;
+       struct ast_fax_session *s = data;
+       struct spandsp_pvt *p = s->tech_pvt;
+       uint8_t buffer[AST_FRIENDLY_OFFSET + samples * sizeof(uint16_t)];
+       struct ast_frame *f;
+       struct ast_frame t30_frame = {
+               .frametype = AST_FRAME_VOICE,
+               .src = "res_fax_spandsp_g711",
+               .samples = samples,
+               .flags = AST_FAX_FRFLAG_GATEWAY,
+       };
+
+       AST_FRAME_SET_BUFFER(&t30_frame, buffer, AST_FRIENDLY_OFFSET, t30_frame.samples * sizeof(int16_t));
+
+       ast_format_set(&t30_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
+       if (!(f = ast_frisolate(&t30_frame))) {
+               return p->isdone ? -1 : res;
+       }
+
+       /* generate a T.30 packet */
+       if ((f->samples = t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples))) {
+               f->datalen = f->samples * sizeof(int16_t);
+               res = ast_write(chan, f);
+       }
+       ast_frfree(f);
+       return p->isdone ? -1 : res;
+}
+
+/*! \brief simple routine to allocate data to generator
+ * \param chan channel
+ * \param params generator data
+ * \return data to use in generator call*/
+static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params) {
+       ao2_ref(params, +1);
+       return params;
+}
+
+static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data) {
+       ao2_ref(data, -1);
+}
+
+/*! \brief activate a spandsp gateway based on the information in the given fax session
+ * \param s fax session
+ * \return -1 on error 0 on sucess*/
+static int spandsp_fax_gateway_start(struct ast_fax_session *s) {
+       struct spandsp_pvt *p = s->tech_pvt;
+       struct ast_fax_t38_parameters *t38_param;
+       int i, modems = 0;
+       struct ast_channel *peer;
+       static struct ast_generator t30_gen = {
+               alloc: spandsp_fax_gw_gen_alloc,
+               release: spandsp_fax_gw_gen_release,
+               generate: spandsp_fax_gw_t30_gen,
+       };
+
+#if SPANDSP_RELEASE_DATE >= 20081012
+       /* for spandsp shaphots 0.0.6 and higher */
+       p->t38_core_state=&p->t38_gw_state.t38x.t38;
+#else
+       /* for spandsp release 0.0.5 */
+       p->t38_core_state=&p->t38_gw_state.t38;
+#endif
+
+       if (!t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, s)) {
+               return -1;
+       }
+
+       p->ist38 = 1;
+       p->ast_t38_state = ast_channel_get_t38_state(s->chan);
+       if (!(peer = ast_bridged_channel(s->chan))) {
+               ast_channel_unlock(s->chan);
+               return -1;
+       }
+       ast_activate_generator(p->ast_t38_state == T38_STATE_NEGOTIATED ? peer : s->chan, &t30_gen , s);
+
+       set_logging(&p->t38_gw_state.logging, s->details);
+       set_logging(&p->t38_core_state->logging, s->details);
+
+       t38_param = (p->ast_t38_state == T38_STATE_NEGOTIATED) ? &s->details->our_t38_parameters : &s->details->their_t38_parameters;
+       t38_set_t38_version(p->t38_core_state, t38_param->version);
+       t38_gateway_set_ecm_capability(&p->t38_gw_state, s->details->option.ecm);
+       t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp);
+       t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal);
+       t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr);
+       t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig);
+       t38_set_data_rate_management_method(p->t38_core_state, 
+                       (t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2);
+
+       t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE);
+       t38_set_sequence_number_handling(p->t38_core_state, TRUE);
+
+       if (AST_FAX_MODEM_V17 & s->details->modems) {
+               modems |= T30_SUPPORT_V17;
+       }
+       if (AST_FAX_MODEM_V27 & s->details->modems) {
+               modems |= T30_SUPPORT_V27TER;
+       }
+       if (AST_FAX_MODEM_V29 & s->details->modems) {
+               modems |= T30_SUPPORT_V29;
+       }
+       if (AST_FAX_MODEM_V34 & s->details->modems) {
+#if defined(T30_SUPPORT_V34)
+               modems |= T30_SUPPORT_V34;
+#elif defined(T30_SUPPORT_V34HDX)
+               modems |= T30_SUPPORT_V34HDX;
+#else
+               ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n");
+#endif
+       }
+
+       t38_gateway_set_supported_modems(&p->t38_gw_state, modems);
+
+       /* engage udptl nat on other side of T38 line 
+        * (Asterisk changes media ports thus we send a few packets to reinitialize
+        * pinholes in NATs and FWs
+        */
+       for (i=0; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) {
+#if SPANDSP_RELEASE_DATE >= 20091228
+               t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL);
+#elif SPANDSP_RELEASE_DATE >= 20081012
+               t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38x.t38.indicator_tx_count);
+#else
+               t38_core_send_indicator(&p->t38_gw_state.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38.indicator_tx_count);
+#endif
+       }
+
+       s->state = AST_FAX_STATE_ACTIVE;
+
+       return 0;
+}
+
+/*! \brief process a frame from the bridge
+ * \param s fax session
+ * \param f frame to process
+ * \return 1 on sucess 0 on incorect packet*/
+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f)
+{
+       struct spandsp_pvt *p = s->tech_pvt;
+
+       /*invalid frame*/
+       if (!f->data.ptr || !f->datalen) {
+               return -1;
+       }
+
+       /* Process a IFP packet */
+       if ((f->frametype == AST_FRAME_MODEM) && (f->subclass.integer == AST_MODEM_T38)) {
+               return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
+       } else if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
+               return t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples);
+       }
+
+       return -1;
+}
+
+/*! \brief gather data and clean up after gateway ends
+ * \param s fax session*/
+static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s)
+{
+       struct spandsp_pvt *p = s->tech_pvt;
+       t38_stats_t t38_stats;
+
+       t38_gateway_get_transfer_statistics(&p->t38_gw_state, &t38_stats);
+
+       s->details->option.ecm = t38_stats.error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE;
+       s->details->pages_transferred = t38_stats.pages_transferred;
+       ast_string_field_build(s->details, transfer_rate, "%d", t38_stats.bit_rate);
+}
+
 /*! \brief */
 static int spandsp_fax_start(struct ast_fax_session *s)
 {
@@ -554,6 +785,10 @@ static int spandsp_fax_start(struct ast_fax_session *s)
 
        s->state = AST_FAX_STATE_OPEN;
 
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               return spandsp_fax_gateway_start(s);
+       }
+
        if (p->ist38) {
 #if SPANDSP_RELEASE_DATE >= 20080725
                /* for spandsp shaphots 0.0.6 and higher */
@@ -623,6 +858,12 @@ static int spandsp_fax_start(struct ast_fax_session *s)
 static int spandsp_fax_cancel(struct ast_fax_session *s)
 {
        struct spandsp_pvt *p = s->tech_pvt;
+
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               p->isdone = 1;
+               return 0;
+       }
+
        t30_terminate(p->t30_state);
        p->isdone = 1;
        return 0;
@@ -651,7 +892,7 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
 /*! \brief */
 static char *spandsp_fax_cli_show_capabilities(int fd)
 {
-       ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n");
+       ast_cli(fd, "SEND RECEIVE T.38 G.711 GATEWAY\n\n");
        return  CLI_SUCCESS;
 }
 
@@ -659,35 +900,48 @@ static char *spandsp_fax_cli_show_capabilities(int fd)
 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
 {
        struct spandsp_pvt *p = s->tech_pvt;
-       t30_stats_t stats;
 
        ao2_lock(s);
-       ast_cli(fd, "%-22s : %d\n", "session", s->id);
-       ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
-       ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
-       if (s->state != AST_FAX_STATE_UNINITIALIZED) {
-               t30_get_transfer_statistics(p->t30_state, &stats);
-               ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
-               ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
-               ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
-               ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
+       if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+               ast_cli(fd, "%-22s : %d\n", "session", s->id);
+               ast_cli(fd, "%-22s : %s\n", "operation", "Gateway");
+               ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
+               if (s->state != AST_FAX_STATE_UNINITIALIZED) {
+                       t38_stats_t stats;
+                       t38_gateway_get_transfer_statistics(&p->t38_gw_state, &stats);
+                       ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
+                       ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
+                       ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
+               }
+       } else {
+               ast_cli(fd, "%-22s : %d\n", "session", s->id);
+               ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
+               ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
+               if (s->state != AST_FAX_STATE_UNINITIALIZED) {
+                       t30_stats_t stats;
+                       t30_get_transfer_statistics(p->t30_state, &stats);
+                       ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
+                       ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
+                       ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
+                       ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
 #if SPANDSP_RELEASE_DATE >= 20090220
-               ast_cli(fd, "%-22s : %d\n", "Page Number", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx);
+                       ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
 #else
-               ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred);
+                       ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
 #endif
-               ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
+                       ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
 
-               ast_cli(fd, "\nData Statistics:\n");
+                       ast_cli(fd, "\nData Statistics:\n");
 #if SPANDSP_RELEASE_DATE >= 20090220
-               ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
-               ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
+                       ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
+                       ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
 #else
-               ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
-               ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
+                       ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
+                       ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
 #endif
-               ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
-               ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
+                       ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
+                       ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
+               }
        }
        ao2_unlock(s);
        ast_cli(fd, "\n\n");
@@ -703,7 +957,7 @@ static char *spandsp_fax_cli_show_stats(int fd)
        ast_cli(fd, "%-20.20s : %d\n", "Switched to T.38", spandsp_global_stats.g711.switched);
        ast_cli(fd, "%-20.20s : %d\n", "Call Dropped", spandsp_global_stats.g711.call_dropped);
        ast_cli(fd, "%-20.20s : %d\n", "No FAX", spandsp_global_stats.g711.nofax);
-       ast_cli(fd, "%-20.20s : %d\n", "Negotation Failed", spandsp_global_stats.g711.neg_failed);
+       ast_cli(fd, "%-20.20s : %d\n", "Negotiation Failed", spandsp_global_stats.g711.neg_failed);
        ast_cli(fd, "%-20.20s : %d\n", "Train Failure", spandsp_global_stats.g711.failed_to_train);
        ast_cli(fd, "%-20.20s : %d\n", "Retries Exceeded", spandsp_global_stats.g711.retries_exceeded);
        ast_cli(fd, "%-20.20s : %d\n", "Protocol Error", spandsp_global_stats.g711.protocol_error);
@@ -717,7 +971,7 @@ static char *spandsp_fax_cli_show_stats(int fd)
        ast_cli(fd, "%-20.20s : %d\n", "Success", spandsp_global_stats.t38.success);
        ast_cli(fd, "%-20.20s : %d\n", "Call Dropped", spandsp_global_stats.t38.call_dropped);
        ast_cli(fd, "%-20.20s : %d\n", "No FAX", spandsp_global_stats.t38.nofax);
-       ast_cli(fd, "%-20.20s : %d\n", "Negotation Failed", spandsp_global_stats.t38.neg_failed);
+       ast_cli(fd, "%-20.20s : %d\n", "Negotiation Failed", spandsp_global_stats.t38.neg_failed);
        ast_cli(fd, "%-20.20s : %d\n", "Train Failure", spandsp_global_stats.t38.failed_to_train);
        ast_cli(fd, "%-20.20s : %d\n", "Retries Exceeded", spandsp_global_stats.t38.retries_exceeded);
        ast_cli(fd, "%-20.20s : %d\n", "Protocol Error", spandsp_global_stats.t38.protocol_error);
@@ -731,6 +985,13 @@ static char *spandsp_fax_cli_show_stats(int fd)
        return CLI_SUCCESS;
 }
 
+/*! \brief Show res_fax_spandsp settings */
+static char *spandsp_fax_cli_show_settings(int fd)
+{
+       /* no settings at the moment */
+       return CLI_SUCCESS;
+}
+
 /*! \brief unload res_fax_spandsp */
 static int unload_module(void)
 {