]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add mod_fsk
authorAnthony Minessale <anthm@freeswitch.org>
Mon, 10 Jan 2011 23:26:14 +0000 (17:26 -0600)
committerAnthony Minessale <anthm@freeswitch.org>
Mon, 10 Jan 2011 23:26:32 +0000 (17:26 -0600)
src/mod/applications/mod_fsk/Makefile [new file with mode: 0644]
src/mod/applications/mod_fsk/fsk.c [new file with mode: 0644]
src/mod/applications/mod_fsk/fsk.h [new file with mode: 0644]
src/mod/applications/mod_fsk/fsk_callerid.c [new file with mode: 0644]
src/mod/applications/mod_fsk/fsk_callerid.h [new file with mode: 0644]
src/mod/applications/mod_fsk/mod_fsk.c [new file with mode: 0644]
src/mod/applications/mod_fsk/uart.c [new file with mode: 0644]
src/mod/applications/mod_fsk/uart.h [new file with mode: 0644]

diff --git a/src/mod/applications/mod_fsk/Makefile b/src/mod/applications/mod_fsk/Makefile
new file mode 100644 (file)
index 0000000..620f3f7
--- /dev/null
@@ -0,0 +1,6 @@
+BASE=../../../..
+
+LOCAL_SOURCES=fsk.c uart.c fsk_callerid.c
+LOCAL_OBJS=fsk.o uart.o fsk_callerid.o
+include $(BASE)/build/modmake.rules
+
diff --git a/src/mod/applications/mod_fsk/fsk.c b/src/mod/applications/mod_fsk/fsk.c
new file mode 100644 (file)
index 0000000..773b0de
--- /dev/null
@@ -0,0 +1,351 @@
+
+/*
+ *     bell202.c
+ *
+ *     Copyright (c) 2005 Robert Krten.  All Rights Reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or without
+ *     modification, are permitted provided that the following conditions
+ *     are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *     2. Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *
+ *     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ *     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *     ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ *     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ *     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ *     SUCH DAMAGE.
+ *
+ *     This module contains a Bell-202 1200-baud FSK decoder, suitable for
+ *     use in a library.  The general style of the library calls is modeled
+ *     after the POSIX pthread_*() functions.
+ *
+ *     2005 03 20      R. Krten                created
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#include "fsk.h"
+#include "uart.h"
+
+#ifndef M_PI
+#define M_PI        3.14159265358979323846
+#endif
+
+fsk_modem_definition_t fsk_modem_definitions[] =
+{
+    { /* FSK_V23_FORWARD_MODE1 */      1700,   1300,   600             },
+    { /* FSK_V23_FORWARD_MODE2 */      2100,   1300,   1200    },
+    { /* FSK_V23_BACKWARD              */      450,    390,    75              },
+    { /* FSK_BELL202                   */      2200,   1200,   1200    },
+};
+
+/*
+ *     dsp_fsk_attr_init
+ *
+ *     Initializes the attributes structure; this must be done before the
+ *     attributes structure is used.
+*/
+
+void dsp_fsk_attr_init (dsp_fsk_attr_t *attr)
+{
+       memset(attr, 0, sizeof(*attr));
+}
+
+/*
+ *     dsp_fsk_attr_get_bithandler
+ *     dsp_fsk_attr_set_bithandler
+ *     dsp_fsk_attr_get_bytehandler
+ *     dsp_fsk_attr_set_bytehandler
+ *     dsp_fsk_attr_getsamplerate
+ *     dsp_fsk_attr_setsamplerate
+ *
+ *     These functions get and set their respective elements from the
+ *     attributes structure.  If an error code is returned, it is just
+ *     zero == ok, -1 == fail.
+*/
+
+bithandler_func_t dsp_fsk_attr_get_bithandler(dsp_fsk_attr_t *attr, void **bithandler_arg)
+{
+       *bithandler_arg = attr->bithandler_arg;
+       return attr->bithandler;
+}
+
+void dsp_fsk_attr_set_bithandler(dsp_fsk_attr_t *attr, bithandler_func_t bithandler, void *bithandler_arg)
+{
+       attr->bithandler = bithandler;
+       attr->bithandler_arg = bithandler_arg;
+}
+
+bytehandler_func_t dsp_fsk_attr_get_bytehandler(dsp_fsk_attr_t *attr, void **bytehandler_arg)
+{
+       *bytehandler_arg = attr->bytehandler_arg;
+       return attr->bytehandler;
+}
+
+void dsp_fsk_attr_set_bytehandler(dsp_fsk_attr_t *attr, bytehandler_func_t bytehandler, void *bytehandler_arg)
+{
+       attr->bytehandler = bytehandler;
+       attr->bytehandler_arg = bytehandler_arg;
+}
+
+int dsp_fsk_attr_get_samplerate (dsp_fsk_attr_t *attr)
+{
+       return attr->sample_rate;
+}
+
+int dsp_fsk_attr_set_samplerate (dsp_fsk_attr_t *attr, int samplerate)
+{
+       if (samplerate <= 0) {
+               return -1;
+       }
+       attr->sample_rate = samplerate;
+       return 0;
+}
+
+/*
+ *     dsp_fsk_create
+ *
+ *     Creates a handle for subsequent use.  The handle is created to contain
+ *     a context data structure for use by the sample handler function.  The
+ *     function expects an initialized attributes structure, and returns the
+ *     handle or a NULL if there were errors.
+ *
+ *     Once created, the handle can be used until it is destroyed.
+*/
+
+dsp_fsk_handle_t *dsp_fsk_create(dsp_fsk_attr_t *attr)
+{
+       int                                             i;
+       double                                  phi_mark, phi_space;
+       dsp_fsk_handle_t        *handle;
+
+       handle = malloc(sizeof(*handle));
+       if (!handle) {
+               return NULL;
+       }
+
+       memset(handle, 0, sizeof(*handle));
+
+       /* fill the attributes member */
+       memcpy(&handle->attr, attr, sizeof(*attr));
+
+       /* see if we can do downsampling.  We only really need 6 samples to "match" */
+       if (attr->sample_rate / fsk_modem_definitions[FSK_BELL202].freq_mark > 6) {
+               handle->downsampling_count = attr->sample_rate / fsk_modem_definitions[FSK_BELL202].freq_mark / 6;
+       } else {
+               handle->downsampling_count = 1;
+       }
+       handle->current_downsample = 1;
+
+       /* calculate the correlate size (number of samples required for slowest wave) */
+       handle->corrsize = attr->sample_rate / handle->downsampling_count / fsk_modem_definitions[FSK_BELL202].freq_mark;
+
+       /* allocate the correlation sin/cos arrays and initialize */
+       for (i = 0; i < 4; i++) {
+               handle->correlates[i] = malloc(sizeof(double) * handle->corrsize);
+               if (handle->correlates[i] == NULL) {
+                       /* some failed, back out memory allocations */
+                       dsp_fsk_destroy(&handle);
+                       return NULL;
+               }
+       }
+
+       /* now initialize them */
+       phi_mark = 2. * M_PI / ((double) attr->sample_rate / (double) handle->downsampling_count / (double) fsk_modem_definitions[FSK_BELL202].freq_mark);
+       phi_space = 2. * M_PI / ((double) attr->sample_rate / (double) handle->downsampling_count / (double) fsk_modem_definitions[FSK_BELL202].freq_space);
+
+       for (i = 0; i < handle->corrsize; i++) {
+               handle->correlates[0][i] = sin(phi_mark * (double) i);
+               handle->correlates[1][i] = cos(phi_mark * (double) i);
+               handle->correlates[2][i] = sin(phi_space * (double) i);
+               handle->correlates[3][i] = cos(phi_space * (double) i);
+       }
+
+       /* initialize the ring buffer */
+       handle->buffer = malloc(sizeof(double) * handle->corrsize);
+       if (!handle->buffer) {                          /* failed; back out memory allocations */
+               dsp_fsk_destroy(&handle);
+               return NULL;
+       }
+       memset(handle->buffer, 0, sizeof(double) * handle->corrsize);
+       handle->ringstart = 0;
+
+       /* initalize intra-cell position */
+       handle->cellpos = 0;
+       handle->celladj = fsk_modem_definitions[FSK_BELL202].baud_rate / (double) attr->sample_rate * (double) handle->downsampling_count;
+
+       /* if they have provided a byte handler, add a UART to the processing chain */
+       if (handle->attr.bytehandler) {
+               dsp_uart_attr_t         uart_attr;
+               dsp_uart_handle_t       *uart_handle;
+
+               dsp_uart_attr_init(&uart_attr);
+               dsp_uart_attr_set_bytehandler(&uart_attr, handle->attr.bytehandler, handle->attr.bytehandler_arg);
+               uart_handle = dsp_uart_create(&uart_attr);
+               if (uart_handle == NULL) {
+                       dsp_fsk_destroy(&handle);
+                       return NULL;
+               }
+               handle->attr.bithandler = dsp_uart_bit_handler;
+               handle->attr.bithandler_arg = uart_handle;
+       }
+
+       return handle;
+}
+
+/*
+ *     dsp_fsk_destroy
+ *
+ *     Destroys a handle, releasing any associated memory.  Sets handle pointer to NULL 
+ *     so A destroyed handle can not be used for anything after the destroy.
+*/
+
+void dsp_fsk_destroy(dsp_fsk_handle_t **handle)
+{
+       int             i;
+
+       /* if empty handle, just return */
+       if (*handle == NULL) {
+               return;
+       }
+
+       for (i = 0; i < 4; i++) {
+               if ((*handle)->correlates[i] != NULL) {
+                       free((*handle)->correlates[i]);
+                       (*handle)->correlates[i] = NULL;
+               }
+       }
+
+       if ((*handle)->buffer != NULL) {
+               free((*handle)->buffer);
+               (*handle)->buffer = NULL;
+       }
+
+       if ((*handle)->attr.bytehandler) {
+               dsp_uart_handle_t** dhandle = (void *)(&(*handle)->attr.bithandler_arg);
+               dsp_uart_destroy(dhandle);
+       }
+
+       free(*handle);
+       *handle = NULL;
+}
+
+/*
+ *     dsp_fsk_sample
+ *
+ *     This is the main processing entry point.  The function accepts a normalized
+ *     sample (i.e., one whose range is between -1 and +1).  The function performs
+ *     the Bell-202 FSK modem decode processing, and, if it detects a valid bit,
+ *     will call the bithandler associated with the attributes structure.
+ *
+ *     For the Bell-202 standard, a logical zero (space) is 2200 Hz, and a logical
+ *     one (mark) is 1200 Hz.
+*/
+
+void
+dsp_fsk_sample (dsp_fsk_handle_t *handle, double normalized_sample)
+{
+       double  val;
+       double  factors[4];
+       int             i, j;
+
+       /* if we can avoid processing samples, do so */
+       if (handle->downsampling_count != 1) {
+               if (handle->current_downsample < handle->downsampling_count) {
+                       handle->current_downsample++;
+                       return;                                                                                         /* throw this sample out */
+               }
+               handle->current_downsample = 1;
+       }
+
+       /* store sample in buffer */
+       handle->buffer[handle->ringstart++] = normalized_sample;
+       if (handle->ringstart >= handle->corrsize) {
+               handle->ringstart = 0;
+       }
+
+       /* do the correlation calculation */
+       factors[0] = factors[1] = factors[2] = factors[3] = 0;  /* clear out intermediate sums */
+       j = handle->ringstart;
+       for (i = 0; i < handle->corrsize; i++) {
+               if (j >= handle->corrsize) {
+                       j = 0;
+               }
+               val = handle->buffer[j];
+               factors[0] += handle->correlates[0][i] * val;
+               factors[1] += handle->correlates[1][i] * val;
+               factors[2] += handle->correlates[2][i] * val;
+               factors[3] += handle->correlates[3][i] * val;
+               j++;
+       }
+
+       /* store the bit (bit value is comparison of the two sets of correlate factors) */
+       handle->previous_bit = handle->current_bit;
+       handle->current_bit = (factors[0] * factors[0] + factors[1] * factors[1] > factors[2] * factors[2] + factors[3] * factors[3]);
+
+       /* if there's a transition, we can synchronize the cell position */
+       if (handle->previous_bit != handle->current_bit) {
+               handle->cellpos = 0.5;                                                          /* adjust cell position to be in the middle of the cell */
+       }
+       handle->cellpos += handle->celladj;                                             /* walk the cell along */
+
+       if (handle->cellpos > 1.0) {
+               handle->cellpos -= 1.0;
+               
+               switch (handle->state) {
+               case FSK_STATE_DATA:
+                       {               
+                               
+                               (*handle->attr.bithandler) (handle->attr.bithandler_arg, handle->current_bit);
+                       }
+                       break;
+               case FSK_STATE_CHANSEIZE:
+                       {
+
+                               if (handle->last_bit != handle->current_bit) {
+                                       handle->conscutive_state_bits++;
+                               } else {
+                                       handle->conscutive_state_bits = 0;
+                               }
+
+                               if (handle->conscutive_state_bits > 15) {
+                                       handle->state = FSK_STATE_CARRIERSIG;
+                                       handle->conscutive_state_bits = 0;
+                               }
+                       }
+                       break;
+               case FSK_STATE_CARRIERSIG:
+                       {
+                               if (handle->current_bit) {
+                                       handle->conscutive_state_bits++;
+                               } else {
+                                       handle->conscutive_state_bits = 0;
+                               }
+
+                               if (handle->conscutive_state_bits > 15) {
+                                       handle->state = FSK_STATE_DATA;
+                                       handle->conscutive_state_bits = 0;
+                               }
+                       }
+                       break;
+               }
+
+               handle->last_bit = handle->current_bit;
+       }
+}
+
diff --git a/src/mod/applications/mod_fsk/fsk.h b/src/mod/applications/mod_fsk/fsk.h
new file mode 100644 (file)
index 0000000..b098444
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *     bell202.h
+ *
+ *     Copyright (c) 2005 Robert Krten.  All Rights Reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or without
+ *     modification, are permitted provided that the following conditions
+ *     are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *     2. Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *
+ *     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ *     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *     ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ *     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ *     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ *     SUCH DAMAGE.
+ *
+ *     This module contains the manifest constants and declarations for
+ *     the Bell-202 1200 baud FSK modem.
+ *
+ *     2005 03 20      R. Krten                created
+*/
+
+#ifndef        __FSK_H__
+#define        __FSK_H__
+#include "uart.h"
+
+typedef struct {
+    int freq_space;            /* Frequency of the 0 bit                               */
+    int freq_mark;             /* Frequency of the 1 bit                               */
+    int baud_rate;             /* baud rate for the modem                              */
+} fsk_modem_definition_t;
+
+/* Must be kept in sync with fsk_modem_definitions array in fsk.c      */
+/* V.23 definitions: http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-V.23 */
+typedef enum {
+    FSK_V23_FORWARD_MODE1 = 0, /* Maximum 600 bps for long haul        */
+    FSK_V23_FORWARD_MODE2,             /* Standard 1200 bps V.23                       */
+    FSK_V23_BACKWARD,                  /* 75 bps return path for V.23          */
+    FSK_BELL202                                        /* Bell 202 half-duplex 1200 bps        */
+} fsk_modem_types_t;
+
+typedef enum {
+       FSK_STATE_CHANSEIZE = 0,
+       FSK_STATE_CARRIERSIG,
+       FSK_STATE_DATA
+} fsk_state_t;
+
+typedef struct dsp_fsk_attr_s
+{
+       int                                     sample_rate;                                    /* sample rate in HZ */
+       bithandler_func_t       bithandler;                                             /* bit handler */
+       void                            *bithandler_arg;                                /* arbitrary ID passed to bithandler as first argument */
+       bytehandler_func_t      bytehandler;                                    /* byte handler */
+       void                            *bytehandler_arg;                               /* arbitrary ID passed to bytehandler as first argument */
+}      dsp_fsk_attr_t;
+
+typedef struct
+{
+       fsk_state_t                     state;
+       dsp_fsk_attr_t          attr;                                                   /* attributes structure */
+       double                          *correlates[4];                                 /* one for each of sin/cos for mark/space */
+       int                                     corrsize;                                               /* correlate size (also number of samples in ring buffer) */
+       double                          *buffer;                                                /* sample ring buffer */
+       int                                     ringstart;                                              /* ring buffer start offset */
+       double                          cellpos;                                                /* bit cell position */
+       double                          celladj;                                                /* bit cell adjustment for each sample */
+       int                                     previous_bit;                                   /* previous bit (for detecting a transition to sync-up cell position) */
+       int                                     current_bit;                                    /* current bit */
+       int                                     last_bit;
+       int                                     downsampling_count;                             /* number of samples to skip */
+       int                                     current_downsample;                             /* current skip count */
+       int                                     conscutive_state_bits;                  /* number of bits in a row that matches the pattern for the current state */
+}      dsp_fsk_handle_t;
+
+/*
+ *     Function prototypes
+ *
+ *     General calling order is:
+ *             a) create the attributes structure (dsp_fsk_attr_init)
+ *             b) initialize fields in the attributes structure (dsp_fsk_attr_set_*)
+ *             c) create a Bell-202 handle (dsp_fsk_create)
+ *             d) feed samples through the handler (dsp_fsk_sample)
+*/
+
+void                                   dsp_fsk_attr_init(dsp_fsk_attr_t *attributes);
+
+bithandler_func_t              dsp_fsk_attr_get_bithandler(dsp_fsk_attr_t *attributes, void **bithandler_arg);
+void                                   dsp_fsk_attr_set_bithandler(dsp_fsk_attr_t *attributes, bithandler_func_t bithandler, void *bithandler_arg);
+bytehandler_func_t             dsp_fsk_attr_get_bytehandler(dsp_fsk_attr_t *attributes, void **bytehandler_arg);
+void                                   dsp_fsk_attr_set_bytehandler(dsp_fsk_attr_t *attributes, bytehandler_func_t bytehandler, void *bytehandler_arg);
+int                                            dsp_fsk_attr_get_samplerate(dsp_fsk_attr_t *attributes);
+int                                            dsp_fsk_attr_set_samplerate(dsp_fsk_attr_t *attributes, int samplerate);
+
+dsp_fsk_handle_t *     dsp_fsk_create(dsp_fsk_attr_t *attributes);
+void                                   dsp_fsk_destroy(dsp_fsk_handle_t **handle);
+
+void                                   dsp_fsk_sample(dsp_fsk_handle_t *handle, double normalized_sample);
+
+extern fsk_modem_definition_t fsk_modem_definitions[];
+
+#endif 
+
diff --git a/src/mod/applications/mod_fsk/fsk_callerid.c b/src/mod/applications/mod_fsk/fsk_callerid.c
new file mode 100644 (file)
index 0000000..2146f66
--- /dev/null
@@ -0,0 +1,398 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * mod_fsk -- FSK data transfer
+ *
+ */
+
+#include "switch.h"
+#include "fsk_callerid.h"
+
+void bitstream_init(bitstream_t *bsp, uint8_t *data, uint32_t datalen, endian_t endian, uint8_t ss)
+{
+       memset(bsp, 0, sizeof(*bsp));
+       bsp->data = data;
+       bsp->datalen = datalen;
+       bsp->endian = endian;
+       bsp->ss = ss;
+       
+       if (endian < 0) {
+               bsp->top = bsp->bit_index = 7;
+               bsp->bot = 0;
+       } else {
+               bsp->top = bsp->bit_index = 0;
+               bsp->bot = 7;
+       }
+
+}
+
+int8_t bitstream_get_bit(bitstream_t *bsp)
+{
+       int8_t bit = -1;
+       
+
+       if (bsp->byte_index >= bsp->datalen) {
+               goto done;
+       }
+
+       if (bsp->ss) {
+               if (!bsp->ssv) {
+                       bsp->ssv = 1;
+                       return 0;
+               } else if (bsp->ssv == 2) {
+                       bsp->byte_index++;
+                       bsp->ssv = 0;
+                       return 1;
+               }
+       }
+
+       bit = (bsp->data[bsp->byte_index] >> (bsp->bit_index)) & 1;
+       
+       if (bsp->bit_index == bsp->bot) {
+               bsp->bit_index = bsp->top;
+               if (bsp->ss) {
+                       bsp->ssv = 2;
+                       goto done;
+               } 
+
+               if (++bsp->byte_index > bsp->datalen) {
+                       bit = -1;
+                       goto done;
+               }
+               
+       } else {
+               bsp->bit_index = bsp->bit_index + bsp->endian;
+       }
+
+
+ done:
+       return bit;
+}
+
+
+
+static void fsk_byte_handler (void *x, int data)
+{
+       fsk_data_state_t *state = (fsk_data_state_t *) x;
+       uint8_t byte = (uint8_t)data;
+
+ top:
+
+       if (state->init == 3) {
+               return;
+       }
+
+       if (state->dlen) {
+               goto add_byte;
+       }
+       
+       if (state->bpos == 1) {
+               state->blen = byte;
+
+               if ((uint32_t)(state->dlen = state->bpos + byte + 2) > state->bufsize) {
+                       state->dlen = state->bufsize;
+               }
+               goto top;
+       }
+
+ add_byte:
+
+       if (state->bpos <= state->dlen) {
+               state->buf[state->bpos++] = byte;
+       } else {
+               state->init = 3;
+       }
+}
+
+switch_status_t fsk_data_init(fsk_data_state_t *state, uint8_t *data, uint32_t datalen)
+{
+       memset(state, 0, sizeof(*state));
+       state->buf = data;
+       state->bufsize = datalen;
+       state->bpos = 2;
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t fsk_data_add_sdmf(fsk_data_state_t *state, const char *date, char *number)
+{
+       size_t dlen = strlen(date);
+       size_t nlen = strlen(number);
+
+       state->buf[0] = CID_TYPE_SDMF;
+       memcpy(&state->buf[state->bpos], date, dlen);
+       state->bpos += dlen;
+       memcpy(&state->buf[state->bpos], number, nlen);
+       state->bpos += nlen;
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t fsk_data_add_mdmf(fsk_data_state_t *state, mdmf_type_t type, const uint8_t *data, uint32_t datalen)
+{
+       state->buf[0] = CID_TYPE_MDMF;
+       state->buf[state->bpos++] = type;
+       state->buf[state->bpos++] = (uint8_t)datalen;
+       memcpy(&state->buf[state->bpos], data, datalen);
+       state->bpos += datalen;
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+switch_status_t fsk_data_add_checksum(fsk_data_state_t *state)
+{
+       uint32_t i;
+       uint8_t check = 0;
+
+       state->buf[1] = (uint8_t)(state->bpos - 2);
+
+       for (i = 0; i < state->bpos; i++) {
+               check = check + state->buf[i];
+       }
+
+       state->checksum = state->buf[state->bpos] = (uint8_t)(256 - check);
+       state->bpos++;
+
+       state->dlen = state->bpos;
+       state->blen = state->buf[1];
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+switch_status_t fsk_data_parse(fsk_data_state_t *state, size_t *type, char **data, size_t *len)
+{
+
+       size_t i;
+       int sum = 0;
+       
+ top:
+
+       if (state->checksum != 0 || state->ppos >= state->dlen - 1) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (!state->ppos) {
+               for(i = 0; i < state->bpos; i++) {
+                       sum += state->buf[i];
+               }
+               state->checksum = sum % 256;
+               state->ppos = 2;                
+
+               if (state->buf[0] != CID_TYPE_MDMF && state->buf[0] != CID_TYPE_SDMF) {
+                       state->checksum = -1;
+               }
+               goto top;
+       }
+
+       if (state->buf[0] == CID_TYPE_SDMF) {
+               /* convert sdmf to mdmf so we don't need 2 parsers */
+               if (state->ppos == 2) {
+                       *type = MDMF_DATETIME;
+                       *len = 8;
+               } else {
+                       if (state->buf[state->ppos] == 'P' || state->buf[state->ppos] == 'O') {
+                               *type = MDMF_NO_NUM;
+                               *len = 1;
+                       } else {
+                               *type = MDMF_PHONE_NUM;
+                               *len = state->blen - 8;
+                       }
+               }
+               *data = (char *)&state->buf[state->ppos];
+               state->ppos += *len;            
+               return SWITCH_STATUS_SUCCESS;
+       } else if (state->buf[0] == CID_TYPE_MDMF) {
+               *type = state->buf[state->ppos++];
+               *len = state->buf[state->ppos++];
+               *data = (char *)&state->buf[state->ppos];
+               state->ppos += *len;
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       return SWITCH_STATUS_FALSE;
+}
+
+switch_status_t fsk_demod_feed(fsk_data_state_t *state, int16_t *data, size_t samples)
+{
+       uint32_t x;
+       int16_t *sp = data;
+
+       if (state->init == 3) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       for (x = 0; x < samples; x++) {
+               dsp_fsk_sample (state->fsk1200_handle, (double) *sp++ / 32767.0);
+               if (state->dlen && state->bpos >= state->dlen) {
+                       state->init = 3;
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t fsk_demod_destroy(fsk_data_state_t *state)
+{
+       dsp_fsk_destroy(&state->fsk1200_handle);
+       memset(state, 0, sizeof(*state));
+       return SWITCH_STATUS_SUCCESS;
+}
+
+int fsk_demod_init(fsk_data_state_t *state, int rate, uint8_t *buf, size_t bufsize)
+{
+
+       dsp_fsk_attr_t fsk1200_attr;
+
+       if (state->fsk1200_handle) {
+               dsp_fsk_destroy(&state->fsk1200_handle);
+       }
+
+       memset(state, 0, sizeof(*state));
+       memset(buf, 0, bufsize);
+       state->buf = buf;
+       state->bufsize = bufsize;
+       
+       dsp_fsk_attr_init (&fsk1200_attr);
+       dsp_fsk_attr_set_samplerate (&fsk1200_attr, rate);
+       dsp_fsk_attr_set_bytehandler (&fsk1200_attr, fsk_byte_handler, state);
+       state->fsk1200_handle = dsp_fsk_create (&fsk1200_attr);
+
+       if (state->fsk1200_handle == NULL) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+size_t fsk_modulator_generate_bit(fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, size_t buflen)
+{
+       size_t i;
+               
+       for(i = 0 ; i < buflen; i++) {
+               fsk_trans->bit_accum += fsk_trans->bit_factor;
+               if (fsk_trans->bit_accum >= FSK_MOD_FACTOR) {
+                       fsk_trans->bit_accum -= (FSK_MOD_FACTOR + fsk_trans->bit_factor);
+                       break;
+               }
+
+               buf[i] = teletone_dds_state_modulate_sample(&fsk_trans->dds, bit);
+       }
+
+       return i;
+}
+
+
+int32_t fsk_modulator_generate_carrier_bits(fsk_modulator_t *fsk_trans, uint32_t bits)
+{
+       uint32_t i = 0;
+       size_t r = 0;
+       int8_t bit = 1;
+
+       for (i = 0; i < bits; i++) {
+               if ((r = fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) {
+                       if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != SWITCH_STATUS_SUCCESS) {
+                               break;
+                       }
+               } else {
+                       break;
+               }
+       }
+
+       return i;
+}
+
+
+void fsk_modulator_generate_chan_sieze(fsk_modulator_t *fsk_trans)
+{
+       uint32_t i = 0;
+       size_t r = 0;
+       int8_t bit = 0;
+       
+       for (i = 0; i < fsk_trans->chan_sieze_bits; i++) {
+               if ((r = fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) {
+                       if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != SWITCH_STATUS_SUCCESS) {
+                               break;
+                       }
+               } else {
+                       break;
+               }
+               bit = !bit;
+       }
+       
+
+}
+
+
+void fsk_modulator_send_data(fsk_modulator_t *fsk_trans)
+{
+       size_t r = 0;
+       int8_t bit = 0;
+
+       while((bit = bitstream_get_bit(&fsk_trans->bs)) > -1) {
+               if ((r = fsk_modulator_generate_bit(fsk_trans, bit, fsk_trans->sample_buffer, sizeof(fsk_trans->sample_buffer) / 2))) {
+                       if (fsk_trans->write_sample_callback(fsk_trans->sample_buffer, r, fsk_trans->user_data) != SWITCH_STATUS_SUCCESS) {
+                               break;
+                       }
+               } else {
+                       break;
+               }
+       }
+}
+
+
+switch_status_t fsk_modulator_init(fsk_modulator_t *fsk_trans,
+                                                                       fsk_modem_types_t modem_type,
+                                                                       uint32_t sample_rate,
+                                                                       fsk_data_state_t *fsk_data,
+                                                                       float db_level,
+                                                                       uint32_t carrier_bits_start,
+                                                                       uint32_t carrier_bits_stop,
+                                                                       uint32_t chan_sieze_bits,
+                                                                       fsk_write_sample_t write_sample_callback,
+                                                                       void *user_data)
+{
+       memset(fsk_trans, 0, sizeof(*fsk_trans));
+       fsk_trans->modem_type = modem_type;
+       teletone_dds_state_set_tone(&fsk_trans->dds, fsk_modem_definitions[fsk_trans->modem_type].freq_space, sample_rate, 0);
+       teletone_dds_state_set_tone(&fsk_trans->dds, fsk_modem_definitions[fsk_trans->modem_type].freq_mark, sample_rate, 1);
+       fsk_trans->bit_factor = (uint32_t)((fsk_modem_definitions[fsk_trans->modem_type].baud_rate * FSK_MOD_FACTOR) / (float)sample_rate);
+       fsk_trans->samples_per_bit = (uint32_t) (sample_rate / fsk_modem_definitions[fsk_trans->modem_type].baud_rate);
+       fsk_trans->est_bytes = (int32_t)(((fsk_data->dlen * 10) + carrier_bits_start + carrier_bits_stop + chan_sieze_bits) * ((fsk_trans->samples_per_bit + 1) * 2));
+       fsk_trans->bit_accum = 0;
+       fsk_trans->fsk_data = fsk_data;
+       teletone_dds_state_set_tx_level(&fsk_trans->dds, db_level);
+       bitstream_init(&fsk_trans->bs, fsk_trans->fsk_data->buf, (uint32_t)fsk_trans->fsk_data->dlen, ENDIAN_BIG, 1);
+       fsk_trans->carrier_bits_start = carrier_bits_start;
+       fsk_trans->carrier_bits_stop = carrier_bits_stop;
+       fsk_trans->chan_sieze_bits = chan_sieze_bits;
+       fsk_trans->write_sample_callback = write_sample_callback;
+       fsk_trans->user_data = user_data;
+       return SWITCH_STATUS_SUCCESS;
+}
+
diff --git a/src/mod/applications/mod_fsk/fsk_callerid.h b/src/mod/applications/mod_fsk/fsk_callerid.h
new file mode 100644 (file)
index 0000000..9018201
--- /dev/null
@@ -0,0 +1,144 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * mod_fsk -- FSK data transfer
+ *
+ */
+#ifndef __FSK_CALLER_ID_H
+#define __FSK_CALLER_ID_H
+SWITCH_BEGIN_EXTERN_C
+#include "fsk.h"
+#include "uart.h"
+
+#define FSK_MOD_FACTOR 0x10000
+
+typedef enum {
+       ENDIAN_BIG = 1,
+       ENDIAN_LITTLE = -1
+} endian_t;
+
+typedef enum {
+       CID_TYPE_SDMF = 0x04,
+       CID_TYPE_MDMF = 0x80
+} cid_type_t;
+
+typedef enum {
+       MDMF_DATETIME = 1,
+       MDMF_PHONE_NUM = 2,
+       MDMF_DDN = 3,
+       MDMF_NO_NUM = 4,
+       MDMF_PHONE_NAME = 7,
+       MDMF_NO_NAME = 8,
+       MDMF_ALT_ROUTE = 9,
+       MDMF_NAME_VALUE = 10,
+       MDMF_INVALID = 11
+} mdmf_type_t;
+
+struct bitstream {
+       uint8_t *data;
+       uint32_t datalen;
+       uint32_t byte_index;
+       uint8_t bit_index;
+       int8_t endian;
+       uint8_t top;
+       uint8_t bot;
+       uint8_t ss;
+       uint8_t ssv;
+};
+
+struct fsk_data_state {
+       dsp_fsk_handle_t *fsk1200_handle;
+       uint8_t init;
+       uint8_t *buf;
+       size_t bufsize;
+       size_t blen;
+       size_t bpos;
+       size_t dlen;
+       size_t ppos;
+       int checksum;
+};
+
+typedef struct bitstream bitstream_t;
+typedef struct fsk_data_state fsk_data_state_t;
+typedef switch_status_t (*fsk_write_sample_t)(int16_t *buf, size_t buflen, void *user_data);
+
+struct fsk_modulator {
+       teletone_dds_state_t dds;
+       bitstream_t bs;
+       uint32_t carrier_bits_start;
+       uint32_t carrier_bits_stop;
+       uint32_t chan_sieze_bits;
+       uint32_t bit_factor;
+       uint32_t bit_accum;
+       uint32_t sample_counter;
+       int32_t samples_per_bit;
+       int32_t est_bytes;
+       fsk_modem_types_t modem_type;
+       fsk_data_state_t *fsk_data;
+       fsk_write_sample_t write_sample_callback;
+       void *user_data;
+       int16_t sample_buffer[64];
+};
+
+
+typedef int (*fsk_data_decoder_t)(fsk_data_state_t *state);
+
+typedef void (*logger_t)(const char *file, const char *func, int line, int level, const char *fmt, ...);
+typedef struct fsk_modulator fsk_modulator_t;
+
+switch_status_t fsk_data_init(fsk_data_state_t *state, uint8_t *data, uint32_t datalen);
+void bitstream_init(bitstream_t *bsp, uint8_t *data, uint32_t datalen, endian_t endian, uint8_t ss);
+int8_t bitstream_get_bit(bitstream_t *bsp);
+switch_status_t fsk_data_add_mdmf(fsk_data_state_t *state, mdmf_type_t type, const uint8_t *data, uint32_t datalen);
+switch_status_t fsk_data_add_checksum(fsk_data_state_t *state);
+switch_status_t fsk_data_parse(fsk_data_state_t *state, size_t *type, char **data, size_t *len);
+switch_status_t fsk_demod_feed(fsk_data_state_t *state, int16_t *data, size_t samples);
+switch_status_t fsk_demod_destroy(fsk_data_state_t *state);
+int fsk_demod_init(fsk_data_state_t *state, int rate, uint8_t *buf, size_t bufsize);
+size_t fsk_modulator_generate_bit(fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, size_t buflen);
+int32_t fsk_modulator_generate_carrier_bits(fsk_modulator_t *fsk_trans, uint32_t bits);
+void fsk_modulator_generate_chan_sieze(fsk_modulator_t *fsk_trans);
+void fsk_modulator_send_data(fsk_modulator_t *fsk_trans);
+switch_status_t fsk_modulator_init(fsk_modulator_t *fsk_trans,
+                                                                  fsk_modem_types_t modem_type,
+                                                                  uint32_t sample_rate,
+                                                                  fsk_data_state_t *fsk_data,
+                                                                  float db_level,
+                                                                  uint32_t carrier_bits_start,
+                                                                  uint32_t carrier_bits_stop,
+                                                                  uint32_t chan_sieze_bits,
+                                                                  fsk_write_sample_t write_sample_callback,
+                                                                  void *user_data);
+
+
+#define fsk_modulator_send_all(_it) fsk_modulator_generate_chan_sieze(_it); \
+       fsk_modulator_generate_carrier_bits(_it, _it->carrier_bits_start); \
+       fsk_modulator_send_data(_it); \
+       fsk_modulator_generate_carrier_bits(_it, _it->carrier_bits_stop)
+
+SWITCH_END_EXTERN_C
+#endif
diff --git a/src/mod/applications/mod_fsk/mod_fsk.c b/src/mod/applications/mod_fsk/mod_fsk.c
new file mode 100644 (file)
index 0000000..9802353
--- /dev/null
@@ -0,0 +1,486 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * mod_fsk.c -- FSK data transfer
+ *
+ */
+#include <switch.h>
+#include "fsk_callerid.h"
+
+/* Prototypes */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_fsk_shutdown);
+SWITCH_MODULE_RUNTIME_FUNCTION(mod_fsk_runtime);
+SWITCH_MODULE_LOAD_FUNCTION(mod_fsk_load);
+
+/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) 
+ * Defines a switch_loadable_module_function_table_t and a static const char[] modname
+ */
+SWITCH_MODULE_DEFINITION(mod_fsk, mod_fsk_load, mod_fsk_shutdown, NULL);
+
+switch_status_t my_write_sample(int16_t *buf, size_t buflen, void *user_data)
+{
+       switch_buffer_t *buffer = (switch_buffer_t *) user_data;
+
+       switch_buffer_write(buffer, buf, buflen * 2);
+       
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t write_fsk_data(uint32_t rate, int32_t db, switch_buffer_t *buffer, switch_event_t *event, const char *prefix)
+{
+       fsk_modulator_t fsk_trans;
+       fsk_data_state_t fsk_data = {0};
+       uint8_t databuf[1024] = "";
+       char time_str[9];
+       struct tm tm;
+       time_t now;
+       switch_event_header_t *hp;
+       switch_size_t plen = 0;
+
+       memset(&fsk_trans, 0, sizeof(fsk_trans));
+       
+       time(&now);
+       localtime_r(&now, &tm);
+       strftime(time_str, sizeof(time_str), "%m%d%H%M", &tm);
+
+       fsk_data_init(&fsk_data, databuf, sizeof(databuf));
+       fsk_data_add_mdmf(&fsk_data, MDMF_DATETIME, (uint8_t *)time_str, strlen(time_str));
+
+       if (prefix) {
+               plen = strlen(prefix);
+       }
+
+
+       if (event) {
+               for (hp = event->headers; hp; hp = hp->next) {
+                       char *packed;
+                       char *name = hp->name;
+
+                       if (plen && strncasecmp(name, prefix, plen)) {
+                               continue;
+                       }
+
+                       name += plen;
+
+                       if (zstr(name)) {
+                               continue;
+                       }
+
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoding [%s][%s]\n", hp->name, hp->value);
+                       
+                       if (!strcasecmp(name, "phone_num")) {
+                               fsk_data_add_mdmf(&fsk_data, MDMF_PHONE_NUM, (uint8_t *)hp->value, strlen(hp->value));
+                       } else if (!strcasecmp(name, "phone_name")) {
+                               fsk_data_add_mdmf(&fsk_data, MDMF_PHONE_NAME, (uint8_t *)hp->value, strlen(hp->value));
+                       } else {
+                               packed = switch_mprintf("%q:%q", name, hp->value);
+                               fsk_data_add_mdmf(&fsk_data, MDMF_NAME_VALUE, (uint8_t *)packed, strlen(packed));
+                               free(packed);
+                       }
+               }
+       }
+
+       fsk_data_add_checksum(&fsk_data);
+
+       fsk_modulator_init(&fsk_trans, FSK_BELL202, rate, &fsk_data, db, 180, 5, 300, my_write_sample, buffer);
+       fsk_modulator_send_all((&fsk_trans));
+
+       fsk_demod_destroy(&fsk_data);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_STANDARD_APP(fsk_send_function) {
+       switch_event_t *event = NULL;
+       switch_buffer_t *buffer;
+       switch_slin_data_t sdata = { 0 };
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_frame_t *read_frame;
+       switch_status_t status;
+       
+
+       if (data) {
+               switch_ivr_sleep(session, 1000, SWITCH_TRUE, NULL);
+               switch_core_session_send_dtmf_string(session, (const char *) data);
+               switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
+       }
+       
+       if (switch_core_session_set_codec_slin(session, &sdata) != SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session),
+                                                 SWITCH_LOG_ERROR, "FAILURE\n");
+               return;
+       }
+
+       switch_buffer_create_dynamic(&buffer, 1024, 2048, 0);
+
+       switch_channel_get_variables(channel, &event);
+       
+       write_fsk_data(sdata.codec.implementation->actual_samples_per_second, -14, buffer, event, "fsk_");
+
+       while(switch_channel_ready(channel)) {
+               status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+               if (!SWITCH_READ_ACCEPTABLE(status)) {
+                       break;
+               }
+
+               if ((sdata.write_frame.datalen = switch_buffer_read(buffer, sdata.write_frame.data, 
+                                                                                                                        sdata.codec.implementation->decoded_bytes_per_packet)) <= 0) {
+                       break;
+               }
+
+               
+               if (sdata.write_frame.datalen < sdata.codec.implementation->decoded_bytes_per_packet) {
+                       memset((char *)sdata.write_frame.data + sdata.write_frame.datalen, 255, 
+                                  sdata.codec.implementation->decoded_bytes_per_packet - sdata.write_frame.datalen);
+                       sdata.write_frame.datalen = sdata.codec.implementation->decoded_bytes_per_packet;
+               }
+               sdata.write_frame.samples = sdata.write_frame.datalen / 2;
+               switch_core_session_write_frame(sdata.session, &sdata.write_frame, SWITCH_IO_FLAG_NONE, 0);
+       }
+
+       switch_buffer_destroy(&buffer);
+       switch_core_codec_destroy(&sdata.codec);
+       switch_core_session_set_read_codec(session, NULL);
+       
+}
+
+typedef struct {
+       switch_core_session_t *session;
+       fsk_data_state_t fsk_data;
+       uint8_t fbuf[512];
+       int skip;
+} switch_fsk_detect_t;
+
+
+
+
+static switch_bool_t fsk_detect_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+       switch_fsk_detect_t *pvt = (switch_fsk_detect_t *) user_data;
+       //switch_frame_t *frame = NULL;
+       switch_channel_t *channel = switch_core_session_get_channel(pvt->session);
+
+       switch (type) {
+       case SWITCH_ABC_TYPE_INIT: {
+               switch_codec_implementation_t read_impl = { 0 };
+               switch_core_session_get_read_impl(pvt->session, &read_impl);
+               
+               if (fsk_demod_init(&pvt->fsk_data, read_impl.actual_samples_per_second, pvt->fbuf, sizeof(pvt->fbuf))) {
+                       return SWITCH_FALSE;
+               }
+               
+               break;
+       }
+       case SWITCH_ABC_TYPE_CLOSE:
+               {
+                       fsk_demod_destroy(&pvt->fsk_data);
+               }
+               break;
+
+       case SWITCH_ABC_TYPE_WRITE_REPLACE:
+       case SWITCH_ABC_TYPE_READ_REPLACE:
+               {
+                       switch_frame_t *rframe;
+
+                       if (type == SWITCH_ABC_TYPE_READ_REPLACE) {
+                               rframe = switch_core_media_bug_get_read_replace_frame(bug);
+                       } else {
+                               rframe = switch_core_media_bug_get_write_replace_frame(bug);
+                       }
+
+                       if (!pvt->skip && fsk_demod_feed(&pvt->fsk_data, rframe->data, rframe->datalen / 2) != SWITCH_STATUS_SUCCESS) {
+                               char str[1024] = "";
+                               size_t type, mlen;
+                               char *sp;
+                               switch_event_t *event;
+                               const char *app_var;
+
+                               switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA);
+                               
+                               while(fsk_data_parse(&pvt->fsk_data, &type, &sp, &mlen) == SWITCH_STATUS_SUCCESS) {
+                                       char *varname = NULL, *val, *p;
+                                       
+                                       switch_copy_string(str, sp, mlen+1);
+                                       *(str+mlen) = '\0';
+                                       switch_clean_string(str);
+                                       //printf("TYPE %u LEN %u VAL [%s]\n", (unsigned)type, (unsigned)mlen, str);
+
+                                       val = str;
+
+                                       switch(type) {
+                                       case MDMF_DATETIME:
+                                               varname = "fsk_datetime";
+                                               break;
+                                       case MDMF_PHONE_NAME:
+                                               varname = "fsk_phone_name";
+                                               break;
+                                       case MDMF_PHONE_NUM:
+                                               varname = "fsk_phone_num";
+                                               break;
+                                       case MDMF_NAME_VALUE:
+                                               varname = switch_core_session_sprintf(pvt->session, "fsk_%s", val);
+                                               if ((p = strchr(varname, ':'))) {
+                                                       *p++ = '\0';
+                                                       val = p;
+                                               }
+                                               break;
+                                       default:
+                                               break;
+                                       }
+
+                                       if (varname && val) {
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "%s setting FSK var [%s][%s]\n", 
+                                                                                 switch_channel_get_name(channel), varname, val);
+                                               switch_channel_set_variable(channel, varname, val);
+                                               if (event) {
+                                                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, varname, val);
+                                               }
+                                       }
+                               }
+
+                               if (event) {
+                                       if (switch_core_session_queue_event(pvt->session, &event) != SWITCH_STATUS_SUCCESS) {
+                                               switch_event_destroy(&event);
+                                       }
+                               }
+                               
+                               if ((app_var = switch_channel_get_variable(channel, "execute_on_fsk"))) {
+                                       char *app_arg;
+
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "%s processing execute_on_fsk [%s]\n", 
+                                                                         switch_channel_get_name(channel), app_var);
+                                       if ((app_arg = strchr(app_var, ' '))) {
+                                               *app_arg++ = '\0';
+                                       }
+                                       switch_core_session_execute_application(pvt->session, app_var, app_arg);
+                               }
+                               
+                               pvt->skip = 10;
+                       }
+                       
+                       memset(rframe->data, 255, rframe->datalen);
+
+                       if (type == SWITCH_ABC_TYPE_READ_REPLACE) {
+                               switch_core_media_bug_set_read_replace_frame(bug, rframe);
+                       } else {
+                               switch_core_media_bug_set_write_replace_frame(bug, rframe);
+                       }
+
+                       if (pvt->skip && !--pvt->skip) {
+                               return SWITCH_FALSE;
+                       }
+
+               }
+               break;
+       case SWITCH_ABC_TYPE_WRITE:
+       default:
+               break;
+       }
+
+       return SWITCH_TRUE;
+}
+
+switch_status_t stop_fsk_detect_session(switch_core_session_t *session)
+{
+       switch_media_bug_t *bug;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+
+       if ((bug = switch_channel_get_private(channel, "fsk"))) {
+               switch_channel_set_private(channel, "fsk", NULL);
+               switch_core_media_bug_remove(session, &bug);
+               return SWITCH_STATUS_SUCCESS;
+       }
+       return SWITCH_STATUS_FALSE;
+}
+
+switch_status_t fsk_detect_session(switch_core_session_t *session, const char *flags)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_media_bug_t *bug;
+       switch_status_t status;
+       switch_fsk_detect_t *pvt = { 0 };
+       switch_codec_implementation_t read_impl = { 0 };
+       int bflags = SMBF_READ_REPLACE;
+
+       if (strchr(flags, 'w')) {
+               bflags = SMBF_WRITE_REPLACE;
+       }
+
+       switch_core_session_get_read_impl(session, &read_impl);
+
+       if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) {
+               return SWITCH_STATUS_MEMERR;
+       }
+
+       pvt->session = session;
+
+
+       if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if ((status = switch_core_media_bug_add(session, "fsk_detect", NULL,
+                                            fsk_detect_callback, pvt, 0, bflags | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
+               return status;
+       }
+
+       switch_channel_set_private(channel, "fsk", bug);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_STANDARD_APP(fsk_recv_function) 
+{
+       fsk_detect_session(session, data);
+}
+
+SWITCH_STANDARD_APP(fsk_display_function) 
+{
+       /* expected to be called via 'execute_on_fsk' -- passes display update over FSK */
+
+       const char *cid_name, *cid_num;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_core_session_message_t *msg;
+       switch_core_session_t *psession, *usession = NULL;
+       char *flags = (char *) data;
+
+       cid_name = switch_channel_get_variable(channel, "fsk_phone_name");
+       cid_num = switch_channel_get_variable(channel, "fsk_phone_num");
+
+       if (zstr(cid_name)) {
+               cid_name = cid_num;
+       }
+       
+       if (zstr(cid_num)) {
+               return;
+       }
+
+       if (strchr(flags, 'b')) {
+               if (switch_core_session_get_partner(session, &psession) == SWITCH_STATUS_SUCCESS) {
+                       usession = psession;
+               }
+       }
+
+       if (!usession) {
+               usession = session;
+       }
+
+       msg = switch_core_session_alloc(usession, sizeof(*msg));
+       MESSAGE_STAMP_FFL(msg);
+       msg->message_id = SWITCH_MESSAGE_INDICATE_DISPLAY;
+       msg->string_array_arg[0] = switch_core_session_strdup(usession, cid_name);
+       msg->string_array_arg[1] = switch_core_session_strdup(usession, cid_num);
+       msg->from = __FILE__;
+       switch_core_session_queue_message(usession, msg);
+
+       if (psession) {
+               switch_core_session_rwunlock(psession);
+               psession = NULL;
+       }
+}
+
+SWITCH_STANDARD_APP(fsk_simplify_function) 
+{
+       /* expected to be called via 'execute_on_fsk' -- redirects call to point-to-point and eliminates legs in the middle */
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       const char *sip_uri, *fsk_simplify_profile, *fsk_simplify_context;
+       char *bridgeto;
+       
+       if (!(sip_uri = switch_channel_get_variable(channel, "fsk_uri"))) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s Missing URI field!\n", switch_channel_get_name(channel));
+       }
+
+       if (!(fsk_simplify_profile = switch_channel_get_variable(channel, "fsk_simplify_profile"))) {
+               fsk_simplify_profile = "internal";
+       }
+
+       fsk_simplify_context = switch_channel_get_variable(channel, "fsk_simplify_context");
+
+       if (!zstr(sip_uri)) {
+               switch_core_session_t *psession;
+               switch_channel_t *pchannel;
+
+               bridgeto = switch_core_session_sprintf(session, "bridge:sofia/%s/sip:%s", fsk_simplify_profile, sip_uri);
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s transfering to [%s]\n", 
+                                                 switch_channel_get_name(channel), bridgeto);
+               
+
+               if (switch_core_session_get_partner(session, &psession) == SWITCH_STATUS_SUCCESS) {
+                       pchannel = switch_core_session_get_channel(psession);
+                       switch_channel_set_flag(pchannel, CF_REDIRECT);
+                       switch_channel_set_flag(pchannel, CF_TRANSFER);
+               }
+               
+               switch_ivr_session_transfer(session, bridgeto, "inline", fsk_simplify_context);
+
+               if (psession) {
+                       switch_ivr_session_transfer(psession, "sleep:5000", "inline", NULL);
+                       switch_core_session_rwunlock(psession);                 
+               }
+       }
+}
+
+/* Macro expands to: switch_status_t mod_fsk_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
+SWITCH_MODULE_LOAD_FUNCTION(mod_fsk_load)
+{
+       switch_application_interface_t *app_interface;
+
+       /* connect my internal structure to the blank pointer passed to me */
+       *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+       SWITCH_ADD_APP(app_interface, "fsk_send", "fsk_send", NULL, fsk_send_function, NULL, SAF_NONE);
+       SWITCH_ADD_APP(app_interface, "fsk_recv", "fsk_recv", NULL, fsk_recv_function, NULL, SAF_NONE);
+       SWITCH_ADD_APP(app_interface, "fsk_simplify", "fsk_simplify", NULL, fsk_simplify_function, NULL, SAF_NONE);
+       SWITCH_ADD_APP(app_interface, "fsk_display", "fsk_display", NULL, fsk_display_function, NULL, SAF_NONE);
+
+       /* indicate that the module should continue to be loaded */
+       return SWITCH_STATUS_SUCCESS;
+}
+
+/*
+  Called when the system shuts down
+  Macro expands to: switch_status_t mod_fsk_shutdown() */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_fsk_shutdown)
+{
+       /* Cleanup dynamically allocated config settings */
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
diff --git a/src/mod/applications/mod_fsk/uart.c b/src/mod/applications/mod_fsk/uart.c
new file mode 100644 (file)
index 0000000..5e0dd85
--- /dev/null
@@ -0,0 +1,124 @@
+
+/*
+ *     uart.c
+ *
+ *     Copyright (c) 2005 Robert Krten.  All Rights Reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or without
+ *     modification, are permitted provided that the following conditions
+ *     are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *     2. Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *
+ *     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ *     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *     ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ *     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ *     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ *     SUCH DAMAGE.
+ *
+ *     This module contains a simple 8-bit UART, which performs a callback
+ *     with the decoded byte value.
+ *
+ *     2005 06 11      R. Krten                created
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "uart.h"
+
+/*
+ *     dsp_uart_attr_init
+ *
+ *     Initializes the attributes structure; this must be done before the
+ *     attributes structure is used.
+*/
+
+void dsp_uart_attr_init (dsp_uart_attr_t *attr)
+{
+       memset (attr, 0, sizeof (*attr));
+}
+
+/*
+ *     dsp_uart_attr_get_bytehandler
+ *     dsp_uart_attr_set_bytehandler
+ *
+ *     These functions get and set their respective elements from the
+ *     attributes structure.  If an error code is returned, it is just
+ *     zero == ok, -1 == fail.
+*/
+
+bytehandler_func_t dsp_uart_attr_get_bytehandler(dsp_uart_attr_t *attr, void **bytehandler_arg)
+{
+       *bytehandler_arg = attr->bytehandler_arg;
+       return attr->bytehandler;
+}
+
+void dsp_uart_attr_set_bytehandler(dsp_uart_attr_t *attr, bytehandler_func_t bytehandler, void *bytehandler_arg)
+{
+       attr->bytehandler = bytehandler;
+       attr->bytehandler_arg = bytehandler_arg;
+}
+
+dsp_uart_handle_t *dsp_uart_create(dsp_uart_attr_t *attr)
+{
+       dsp_uart_handle_t *handle;
+
+       handle = malloc(sizeof (*handle));
+       if (handle) {
+               memset(handle, 0, sizeof (*handle));
+
+               /* fill the attributes member */
+               memcpy(&handle->attr, attr, sizeof (*attr));
+       }
+       return handle;
+}
+
+void dsp_uart_destroy(dsp_uart_handle_t **handle)
+{
+       if (*handle) {
+               free(*handle);
+               *handle = NULL;
+       }
+}
+
+
+void dsp_uart_bit_handler(void *x, int bit)
+{
+       dsp_uart_handle_t *handle = (dsp_uart_handle_t *) x;
+
+       if (!handle->have_start) {
+               if (bit) {
+                       return;         /* waiting for start bit (0) */
+               }
+               handle->have_start = 1;
+               handle->data = 0;
+               handle->nbits = 0;
+               return;
+       }
+
+       handle->data >>= 1;
+       handle->data |= 0x80 * !!bit;
+       handle->nbits++;
+
+       if (handle->nbits == 8) {
+               handle->attr.bytehandler(handle->attr.bytehandler_arg, handle->data);
+               handle->nbits = 0;
+               handle->data = 0;
+               handle->have_start = 0;
+       }
+/* might consider handling errors in the future... */
+}
+
diff --git a/src/mod/applications/mod_fsk/uart.h b/src/mod/applications/mod_fsk/uart.h
new file mode 100644 (file)
index 0000000..89c3a13
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *     uart.h
+ *
+ *     Copyright (c) 2005 Robert Krten.  All Rights Reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or without
+ *     modification, are permitted provided that the following conditions
+ *     are met:
+ *
+ *     1. Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *     2. Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *
+ *     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ *     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *     ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ *     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ *     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ *     SUCH DAMAGE.
+ *
+ *     This module contains the manifest constants and declarations for
+ *     the UART module.
+ *
+ *     2005 06 19      R. Krten                created
+*/
+
+#ifndef        __UART_H__
+#define        __UART_H__
+
+typedef void (*bytehandler_func_t) (void *, int);
+typedef void (*bithandler_func_t) (void *, int);
+
+
+typedef struct dsp_uart_attr_s
+{
+       bytehandler_func_t      bytehandler;                                    /* byte handler */
+       void                            *bytehandler_arg;                               /* arbitrary ID passed to bytehandler as first argument */
+}      dsp_uart_attr_t;
+
+typedef struct
+{
+       dsp_uart_attr_t         attr;
+       int                                     have_start;                                             /* wait for start bit to show up */
+       int                                     data;                                                   /* data buffer */
+       int                                     nbits;                                                  /* number of bits accumulated so far */
+}      dsp_uart_handle_t;
+
+/*
+ *     Function prototypes
+ *
+ *     General calling order is:
+ *             a) create the attributes structure (dsp_uart_attr_init)
+ *             b) initialize fields in the attributes structure (dsp_uart_attr_set_*)
+ *             c) create a Bell-202 handle (dsp_uart_create)
+ *             d) feed bits through dsp_uart_bit_handler
+*/
+
+void                                   dsp_uart_attr_init(dsp_uart_attr_t *attributes);
+
+bytehandler_func_t             dsp_uart_attr_get_bytehandler(dsp_uart_attr_t *attributes, void **bytehandler_arg);
+void                                   dsp_uart_attr_set_bytehandler(dsp_uart_attr_t *attributes, bytehandler_func_t bytehandler, void *bytehandler_arg);
+
+dsp_uart_handle_t *            dsp_uart_create(dsp_uart_attr_t *attributes);
+void                                   dsp_uart_destroy(dsp_uart_handle_t **handle);
+
+void                                   dsp_uart_bit_handler(void *handle, int bit);
+
+#endif
+