add mod_fsk
This commit is contained in:
parent
62941f69d8
commit
fcc912a991
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
||||
*/
|
|
@ -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... */
|
||||
}
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue