From 02b946cdc5873417b2d34418e295850d4afe0d90 Mon Sep 17 00:00:00 2001 From: irrelevantdotcom Date: Thu, 30 Aug 2018 19:11:21 +0100 Subject: [PATCH 1/2] Add parity options Support for e.g. 7e1 data formats on analogue line side. --- app_softmodem.c | 206 +++++++++++++++++++++++++++++------------------- 1 file changed, 125 insertions(+), 81 deletions(-) diff --git a/app_softmodem.c b/app_softmodem.c index c783009..2bced51 100644 --- a/app_softmodem.c +++ b/app_softmodem.c @@ -2,10 +2,12 @@ * Softmodem for Asterisk * * 2010, Christian Groeger - * + * * Based on app_fax.c by Dmitry Andrianov * and Steve Underwood * + * Parity options added 2018 Rob O'Donnell + * * This program is free software, distributed under the terms of * the GNU General Public License * @@ -15,7 +17,7 @@ spandsp extended ***/ - + #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") @@ -77,8 +79,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") " l or m: least or most significant bit first (default: m)\n" " d(...): amount of data bits (5-8, default: 8)\n" " s(...): amount of stop bits (1-2, default: 1)\n" + " e or o: add even or odd parity bit " u: Send ULM header to Telnet server (Btx)\n" - " n: Send NULL-Byte to modem after carrier detection (Btx)\n" **/ + " n: Send NULL-Byte to modem after carrier detection (Btx)\n" + + + **/ static const char app[] = "Softmodem"; @@ -92,6 +98,8 @@ enum { OPT_STOPBITS = (1 << 6), OPT_ULM_HEADER = (1 << 7), OPT_NULL = (1 << 8), + OPT_EVEN_PARITY = (1 << 9), + OPT_ODD_PARITY = (1 << 10) }; enum { @@ -113,6 +121,8 @@ AST_APP_OPTIONS(additional_options, BEGIN_OPTIONS AST_APP_OPTION('m', OPT_MSB_FIRST), AST_APP_OPTION_ARG('d', OPT_DATABITS, OPT_ARG_DATABITS), AST_APP_OPTION_ARG('s', OPT_STOPBITS, OPT_ARG_STOPBITS), + AST_APP_OPTION('e', OPT_EVEN_PARITY), + AST_APP_OPTION('o', OPT_ODD_PARITY), AST_APP_OPTION('u', OPT_ULM_HEADER), AST_APP_OPTION('n', OPT_NULL), END_OPTIONS ); @@ -141,6 +151,7 @@ typedef struct { int ulmheader; int sendnull; volatile int finished; + int paritytype; } modem_session; #define MODEM_BITBUFFER_SIZE 16 @@ -162,12 +173,16 @@ typedef struct { // this is called by spandsp whenever it filtered a new bit from the line static void modem_put_bit(void *user_data, int bit) { int stop, stop2, i; - + modem_data *rx = (modem_data*) user_data; - + int databits = rx->session->databits; int stopbits = rx->session->stopbits; - + int paritybits = 0; + + if (rx->session->paritytype) + paritybits = 1; + // modem recognised us and starts responding through sending it's pilot signal if (rx->state->answertone<=0) { if (bit==SIG_STATUS_CARRIER_UP) { @@ -180,31 +195,33 @@ static void modem_put_bit(void *user_data, int bit) { else { // ignore other spandsp-stuff if (bit==1 || bit==0) { - + // insert bit into our bitbuffer rx->bitbuffer[rx->writepos]=bit; rx->writepos++; if (rx->writepos>=MODEM_BITBUFFER_SIZE) rx->writepos=0; - + if (rx->fillfill++; - } else { + } else { // our bitbuffer is full, this probably won't happen // printf("full buffer\n"); rx->readpos++; if (rx->readpos>=MODEM_BITBUFFER_SIZE) rx->readpos=0; } - - // full byte = 1 startbit + databits + stopbits - while (rx->fill>=(1+databits+stopbits)) { + + // full byte = 1 startbit + databits + paritybits + stopbits + while (rx->fill>=(1+databits+paritybits+stopbits)) { if (rx->bitbuffer[rx->readpos]==0) { // check for startbit - stop=(rx->readpos+1+databits)%MODEM_BITBUFFER_SIZE; - stop2=(rx->readpos+2+databits)%MODEM_BITBUFFER_SIZE; + stop=(rx->readpos+1+paritybits+databits)%MODEM_BITBUFFER_SIZE; + stop2=(rx->readpos+2+paritybits+databits)%MODEM_BITBUFFER_SIZE; if ( (rx->bitbuffer[stop]==1) && (stopbits==1 || (stopbits==2 && rx->bitbuffer[stop2]==1)) ) { // check for stopbit -> valid framing + + char byte=0; - + for(i=0; isession->lsb) { //lsb first if (rx->bitbuffer[(rx->readpos+1+i)%MODEM_BITBUFFER_SIZE]) @@ -214,12 +231,16 @@ static void modem_put_bit(void *user_data, int bit) { byte |= (1<sock, &byte, 1, 0); - + // TODO - why does this increment by 10? rx->readpos=(rx->readpos+10)%MODEM_BITBUFFER_SIZE; rx->fill-=10; - + } else { // no valid framing (no stopbit), remove first bit and maybe try again rx->fill--; rx->readpos++; @@ -233,9 +254,9 @@ static void modem_put_bit(void *user_data, int bit) { } } } - - - + + + return; } @@ -244,11 +265,17 @@ static int modem_get_bit(void *user_data) { modem_data *tx = (modem_data*) user_data; char byte=0; int i, rc; - + int databits=tx->session->databits; int stopbits=tx->session->stopbits; - - // no new data in send (bit)buffer, + int paritybits = 0; + + if (tx->session->paritytype) + paritybits = 1; + + + + // no new data in send (bit)buffer, // either we just picked up the line, the terminal started to respond, // than we check for new data on the socket // or there's no new data, so we send 1s (mark) @@ -257,8 +284,11 @@ static int modem_get_bit(void *user_data) { rc=recv(tx->sock,&byte, 1, 0); if (rc>0) { // new data on socket, we put that byte into our bitbuffer - for (i=0; i<(databits+stopbits); i++) { - if (i>=databits) tx->bitbuffer[tx->writepos]=1; // stopbits + for (i=0; i<(databits+paritybits+stopbits); i++) { + if (paritybits && (i == databits) ) { + tx->bitbuffer[tx->writepos] = /*(tx->session->paritytype == 1) -*/ __builtin_parity( byte); + } else if ( i >= databits ) + tx->bitbuffer[tx->writepos]=1; // stopbits else { // databits if (tx->session->lsb) { if (byte & (1<bitbuffer[tx->writepos]=1; @@ -286,17 +316,19 @@ static int modem_get_bit(void *user_data) { } if ( tx->state->answertone>0 ) { // ast_log(LOG_WARNING,"Got TE's tone, will send null-byte.\n"); - + if (tx->session->sendnull) { // send null byte - for (i=0; i<(databits+stopbits); i++) { - if (i>=databits) tx->bitbuffer[tx->writepos]=1; //stopbits + for (i=0; i<(databits+paritybits+stopbits); i++) { + if (paritybits && (i == databits) ) + tx->bitbuffer[tx->writepos] = /*(tx->session->paritytype == 1) -*/ __builtin_parity( 0 ); // yes I know! + else if (i>=databits) tx->bitbuffer[tx->writepos]=1; //stopbits else tx->bitbuffer[tx->writepos]=0; //databits tx->writepos++; if (tx->writepos>=MODEM_BITBUFFER_SIZE) tx->writepos=0; } } tx->state->nulsent=1; - + if (tx->session->ulmheader) { // send ULM relay protocol header, include connection speed float tx_baud,rx_baud; @@ -319,24 +351,24 @@ static int modem_get_bit(void *user_data) { tx_baud=0; rx_baud=0; } - + char header[60]; - int headerlength=sprintf(header, + int headerlength=sprintf(header, "Version: 1\r\nTXspeed: %.2f\r\nRXspeed: %.2f\r\n\r\n", tx_baud/(1+databits+stopbits), rx_baud/(1+databits+stopbits)); send(tx->sock, header, headerlength, 0); } - + if (tx->session->sendnull) return 0; else return 1; } } - - // no new data on socket, NULL-byte already sent, send mark-frequency + + // no new data on socket, NULL-byte already sent, send mark-frequency return 1; - + } else { // there still is data in the bitbuffer, so we just send that out i=tx->bitbuffer[tx->readpos]; @@ -357,7 +389,7 @@ static int fsk_generator_generate(struct ast_channel *chan, void *data, int len, fsk_tx_state_t *tx = (fsk_tx_state_t*) data; uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)]; int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET); - + struct ast_frame outf = { .frametype = AST_FRAME_VOICE, .subclass.format = ast_format_slin, @@ -369,7 +401,7 @@ static int fsk_generator_generate(struct ast_channel *chan, void *data, int len, samples = MAX_SAMPLES; } - + if ((len = fsk_tx(tx, buf, samples)) > 0) { outf.samples = len; AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t)); @@ -389,7 +421,7 @@ static int v22_generator_generate(struct ast_channel *chan, void *data, int len, v22bis_state_t *tx = (v22bis_state_t*) data; uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)]; int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET); - + struct ast_frame outf = { .frametype = AST_FRAME_VOICE, .subclass.format = ast_format_slin, @@ -401,7 +433,7 @@ static int v22_generator_generate(struct ast_channel *chan, void *data, int len, samples = MAX_SAMPLES; } - + if ((len = v22bis_tx(tx, buf, samples)) > 0) { outf.samples = len; AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t)); @@ -428,17 +460,17 @@ static int softmodem_communicate(modem_session *s) { int res = -1; struct ast_format *original_read_fmt; struct ast_format *original_write_fmt; - + modem_data rxdata, txdata; - + struct ast_frame *inf = NULL; - + fsk_tx_state_t *modem_tx; fsk_rx_state_t *modem_rx; - + v22bis_state_t *v22_modem; - - + + original_read_fmt = ast_channel_readformat(s->chan); if (original_read_fmt != ast_format_slin) { res=ast_set_read_format(s->chan, ast_format_slin); @@ -456,50 +488,50 @@ static int softmodem_communicate(modem_session *s) { return res; } } - + int sock; struct sockaddr_in server; struct hostent *hp; struct ast_hostent ahp; - + sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) { ast_log(LOG_WARNING, "Could not create socket.\n"); return res; } - + server.sin_family=AF_INET; hp=ast_gethostbyname(s->host, &ahp); memcpy( (char *)&server.sin_addr, hp->h_addr, hp->h_length); //bcopy ( hp->h_addr, &(server.sin_addr.s_addr), hp->h_length); server.sin_port=htons(s->port); - + if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) { ast_log(LOG_WARNING, "Cannot connect to remote host.\n"); return res; } - + fcntl(sock, F_SETFL, O_NONBLOCK); - - + + connection_state state; state.answertone=-1; //no carrier yet state.nulsent=0; - + rxdata.sock=sock; rxdata.writepos=0; rxdata.readpos=0; rxdata.fill=0; rxdata.state= &state; rxdata.session=s; - + txdata.sock=sock; txdata.writepos=0; txdata.readpos=0; txdata.fill=0; txdata.state= &state; txdata.session=s; - + // initialise spandsp-stuff, give it our callback-functions if (s->version==VERSION_V21) { modem_tx = fsk_tx_init(NULL, &preset_fsk_specs[FSK_V21CH2], modem_get_bit, &txdata); @@ -523,7 +555,7 @@ static int softmodem_communicate(modem_session *s) { ast_log(LOG_ERROR,"Unsupported modem type. Sorry.\n"); return res; } - + if (s->version==VERSION_V21 || s->version==VERSION_V23 || s->version==VERSION_BELL103) { fsk_tx_power (modem_tx, s->txpower); fsk_rx_signal_cutoff(modem_rx, s->rxcutoff); @@ -532,13 +564,13 @@ static int softmodem_communicate(modem_session *s) { v22bis_tx_power(v22_modem, s->txpower); v22bis_rx_signal_cutoff(v22_modem, s->rxcutoff); } - + //printf("comm: baud %i\n",btx_tx->baud_rate); if (s->version==VERSION_V21 || s->version==VERSION_V23 || s->version==VERSION_BELL103) ast_activate_generator(s->chan, &fsk_generator, modem_tx); else if (s->version==VERSION_V22 || s->version==VERSION_V22BIS) ast_activate_generator(s->chan, &v22_generator, v22_modem); - + while (!s->finished) { res = ast_waitfor(s->chan, 20); if (res < 0) @@ -552,7 +584,7 @@ static int softmodem_communicate(modem_session *s) { res = -1; break; } - + /* Check the frame type. Format also must be checked because there is a chance that a frame in old format was already queued before we set chanel format to slinear so it will still be received by ast_read */ @@ -573,18 +605,18 @@ static int softmodem_communicate(modem_session *s) { } } } - + ast_frfree(inf); inf = NULL; } - + close(sock); - + if (s->version==VERSION_V22 || s->version==VERSION_V22BIS) { v22bis_release(v22_modem); v22bis_free(v22_modem); } - + if (original_write_fmt != ast_format_slin) { if (ast_set_write_format(s->chan, original_write_fmt) < 0) ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", ast_channel_name(s->chan)); @@ -616,7 +648,7 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { ast_log(LOG_ERROR, "Channel is NULL. Giving up.\n"); return -1; } - + /* answer channel if not already answered */ if (ast_channel_state(chan) != AST_STATE_UP) { res = ast_answer(chan); @@ -625,7 +657,7 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { return res; } } - + session.chan=chan; session.finished=0; session.rxcutoff=-35.0f; @@ -634,17 +666,18 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { session.lsb=0; session.databits=8; session.stopbits=1; + session.paritytype=0; session.ulmheader=0; session.sendnull=0; - + parse=ast_strdupa(data); AST_STANDARD_APP_ARGS(args,parse); - + if (args.host) session.host=args.host; else session.host="localhost"; - + if (args.port) { session.port=atoi(args.port); if ((session.port<0) || (session.port>65535)) { @@ -653,21 +686,21 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { } } else session.port=23; - - + + if (args.options) { ast_app_parse_options(additional_options, &options, option_args, args.options); - + if (ast_test_flag(&options, OPT_RX_CUTOFF)) { if (!ast_strlen_zero(option_args[OPT_ARG_RX_CUTOFF])) session.rxcutoff=atof(option_args[OPT_ARG_RX_CUTOFF]); } - + if (ast_test_flag(&options, OPT_TX_POWER)) { if (!ast_strlen_zero(option_args[OPT_ARG_TX_POWER])) session.txpower=atof(option_args[OPT_ARG_TX_POWER]); } - + if (ast_test_flag(&options, OPT_MODEM_VERSION)) { if (!ast_strlen_zero(option_args[OPT_ARG_MODEM_VERSION])) { if (strcmp(option_args[OPT_ARG_MODEM_VERSION],"V21")==0) @@ -682,7 +715,7 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { session.version=VERSION_V22BIS; } } - + if (ast_test_flag(&options, OPT_LSB_FIRST)) { if (ast_test_flag(&options, OPT_MSB_FIRST)) { ast_log(LOG_ERROR, "Please only set l or m flag, not both.\n"); @@ -690,18 +723,18 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { } session.lsb=1; } - + if (ast_test_flag(&options, OPT_DATABITS)) { if (!ast_strlen_zero(option_args[OPT_ARG_DATABITS])) { session.databits = atoi(option_args[OPT_ARG_DATABITS]); - + if ((session.databits<5) || (session.databits>8)) { ast_log(LOG_ERROR, "Only 5-8 data bits are supported.\n"); return -1; } } } - + if (ast_test_flag(&options, OPT_STOPBITS)) { if (!ast_strlen_zero(option_args[OPT_ARG_STOPBITS])) { session.stopbits = atoi(option_args[OPT_ARG_STOPBITS]); @@ -711,7 +744,18 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { } } } - + + if (ast_test_flag(&options, OPT_EVEN_PARITY)) { + if (ast_test_flag(&options, OPT_ODD_PARITY)) { + ast_log(LOG_ERROR, "Please only set e or o (parity) flag, not both.\n"); + return -1; + } + session.paritytype = 1; + } + if (ast_test_flag(&options, OPT_ODD_PARITY)) { + session.paritytype = 2; + } + if (ast_test_flag(&options, OPT_ULM_HEADER)) { session.ulmheader = 1; } @@ -719,9 +763,9 @@ static int softmodem_exec(struct ast_channel *chan, const char *data) { session.sendnull = 1; } } - + res=softmodem_communicate(&session); - + return res; } From 596f2b6111bd5e79d10da128e89aa42b7b1285dc Mon Sep 17 00:00:00 2001 From: irrelevantdotcom Date: Fri, 31 Aug 2018 22:00:06 +0100 Subject: [PATCH 2/2] update docn Add description of parity options --- README | 43 +++++++++++++++++++++++++------------------ app_softmodem.c | 22 +++++++++++----------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/README b/README index 37a3ccf..69c675a 100644 --- a/README +++ b/README @@ -14,12 +14,12 @@ app_softmodem.c depends on the spandsp library (http://www.soft-switch.org/). - + It is tested and working with Asterisk 13t. - + To compile put app_softmodem.c in the apps/ dir of your Asterisk source. Compile with 'make apps'. - + To use do a 'make install' on your asterisk source or copy apps/app_softmodem.so into your asterisk module directory (/lib/asterisk/mosules). @@ -31,10 +31,10 @@ exten => btx,1,Answer() exten => btx,n,Softmodem(host, port, options) exten => btx,n,Hangup() - + Without any arguments the application acts as a V.23 modem and connects to a Telnet server (port 23) on localhost. - + Options are: r(...): rx cutoff (dBi, float, default: -35) t(...): tx power (dBi, float, default: -28) @@ -47,13 +47,20 @@ l or m: least or most significant bit first (default: m) d(...): amount of data bits (5-8, default: 8) s(...): amount of stop bits (1-2, default: 1) + e or o: add even or odd parity bit u: Send Ulm Relay Protocol header to Telnet server n: Send NULL-Byte to modem after carrier detection (Btx specific) - + To act as a "Btx Vermittlungsstelle" (German videotex system) using V.23, 8 data bits, 1 stop bit and sending a NULL byte upon carrier detection and connecting to a "twistedUlm" server on port 8289 (thus sending the ULM header): - + exten => 190,1,Softmodem(localhost, 8289, v(V23)ld(8)s(1)un) - + + To accept calls fom UK Prestel terminals, using V.23, 7 data bits, Even parity, 1 stop bit - + + exten => 618,n,Softmodem(localhost, 6502, v(V23),ld(7)es(1)t(-8)) + + + The modem seems to work fine with VOIP as long as you use a codec like G.711 (alaw/ulaw). Please deactivate any echo cancellation you might use. @@ -62,32 +69,32 @@ Btx, the Ulm Relay Protocol, general description of operation ------------------------------------------------------------------------------- - After the softmodem picks up the line a TCP connection to the specified host + After the softmodem picks up the line a TCP connection to the specified host and port is established, the call is dropped when this wasn't successfull. - + When the "n" option is specified a NULL byte is send to the caller upon the detection of a carrier signal from the caller. The Btx system in Germany did this to request the caller's identification. - + With the "u" option specified a header is send to the TCP server at this point. The header consists of some "name: value" pairs, separated by a CR+LF newline, terminated by a double newline. - + At the moment this header looks like (for V.23): Version: 1 TXspeed: 120 RXspeed: 7.5 Speeds are in bytes-per-second and may be used by the server to estimate the length of the transmission. - + After the header all data received from the terminal is put onto the socket and all data received from the socket is put on the line. - + The line is hung up when the server drops the connection, just as the connection to the server is dropped when the line is hung up. - + You find my "Ulm Relay Protocol" server here: http://github.com/proquar/twistedUlm It sends CEPT T/CD 6-1 encoded data, which it retrieves from a webserver, to the terminal and provides the characteristic BTX look-and-feel. - + You may also be interested in: http://runningserver.com/?page=runningserver.content.thelab.bildschirmtrix And you find a description of BTX MarkupLanguage here: @@ -98,7 +105,7 @@ ------------------------------------------------------------------------------- Christian Groeger - + Based on code by: Dmitry Andrianov Steve Underwood @@ -783,4 +790,4 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. \ No newline at end of file diff --git a/app_softmodem.c b/app_softmodem.c index 2bced51..b8db535 100644 --- a/app_softmodem.c +++ b/app_softmodem.c @@ -20,7 +20,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +//ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include #include @@ -231,16 +231,16 @@ static void modem_put_bit(void *user_data, int bit) { byte |= (1<sock, &byte, 1, 0); - // TODO - why does this increment by 10? + + if ( !paritybits || ( paritybits && + ( rx->bitbuffer[(rx->readpos + databits + 1)%MODEM_BITBUFFER_SIZE] == + ( (rx->session->paritytype == 2) ^ __builtin_parity(byte) ) ) ) ) { + + send(rx->sock, &byte, 1, 0); + } // else invalid parity, ignore byte + // TODO - why does this increment by 10? rx->readpos=(rx->readpos+10)%MODEM_BITBUFFER_SIZE; rx->fill-=10; - } else { // no valid framing (no stopbit), remove first bit and maybe try again rx->fill--; rx->readpos++; @@ -286,7 +286,7 @@ static int modem_get_bit(void *user_data) { // new data on socket, we put that byte into our bitbuffer for (i=0; i<(databits+paritybits+stopbits); i++) { if (paritybits && (i == databits) ) { - tx->bitbuffer[tx->writepos] = /*(tx->session->paritytype == 1) -*/ __builtin_parity( byte); + tx->bitbuffer[tx->writepos] = (tx->session->paritytype == 2) ^ __builtin_parity( byte); } else if ( i >= databits ) tx->bitbuffer[tx->writepos]=1; // stopbits else { // databits @@ -320,7 +320,7 @@ static int modem_get_bit(void *user_data) { if (tx->session->sendnull) { // send null byte for (i=0; i<(databits+paritybits+stopbits); i++) { if (paritybits && (i == databits) ) - tx->bitbuffer[tx->writepos] = /*(tx->session->paritytype == 1) -*/ __builtin_parity( 0 ); // yes I know! + tx->bitbuffer[tx->writepos] = (tx->session->paritytype == 2) ^ __builtin_parity( 0 ); // yes I know! else if (i>=databits) tx->bitbuffer[tx->writepos]=1; //stopbits else tx->bitbuffer[tx->writepos]=0; //databits tx->writepos++;