osmocom-analog/src/datenklo/am791x.c

1267 lines
37 KiB
C

/* AM7910 / AM7911 modem chip emulation and signal processing
*
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Not implemented:
* - auto answer
* - Bell 202 5 bps back channel
* - equalizer
*/
/*
* Implementation is done according to the AM7910/AM7911 datasheet. This is
* only a (short and bad) summary, so read the datasheet!!!
*
* DTR state:
* When DTR is off, state is clamped to INIT state.
* When DTR becomes on, RX and TX state machines begin to run.
* When DTR becomes off, state machines change to INIT state immidiately.
*
* (B)RTS state:
* When (B)RTS becomes on, data transmission is enabled.
* Then (B)CTS becomes on, when the timer (B)RCON is complete.
* This means that data transmission is now allowed by upper layer.
* When (B)RTS becomes off, data transmission is disabled.
* Then (B)CTS becomes off, when the timer (B)RCOFF is complete.
*
* (B)CD state:
* When carrier is detected, timer (B)CDON is started.
* When carrier is stable and timer is complete, (B)CD becomes on and data
* reception is enabled.
* When carrier is lost, timer (B)CDOFF is started.
* When carrier timer is complete, (B)CD becomes off and data reception is
* disabled.
* While transmitting half duplex mode, (B)CD will be blocked to prevent
* carrier detection from loopback of audio signal.
*
* (B)TD data:
* When transmitting, data is requested from upper layer and forwarded into
* modulator.
* When not transmitting, this data is blocked, meaning that '1' (MARK) is
* transmitted into the modulator, regardless of the upper layer data.
* STO (soft turn off) and/or silence is sent after transmission is over.
*
* (B)RD data:
* When data reception is not blocked, data is received from the demodulator
* and forwarded towards upper layer.
* While receiving in half duplex mode, (B)RD is blocked, meaning that '1'
* (MARK) is forwarded toward upper layer, regardless fo the data from the
* demodulator.
* Squelch (mute receive audio) is used to prevent noise when turning off
* half duplex transmission.
*
* Audio level is based on milliwatts (at 600 Ohms), which is a value of 1.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "../libsample/sample.h"
#include "../libfsk/fsk.h"
#include "am791x.h"
#define db2level(db) pow(10, (double)(db) / 20.0)
#define level2db(level) (20 * log10(level))
/* levels used (related to dBm0 (1 mW)) */
#define TX_LEVEL -3 /* according to datasheet (at 600 Ohms) */
#define RX_CD_ON_7910 -40.5 /* according to datasheet (at 600 Ohms) */
#define RX_CD_ON_7911 -42.0 /* according to datasheet (at 600 Ohms) */
#define RX_CD_OFF_7910 -45.0 /* according to datasheet (at 600 Ohms) */
#define RX_CD_OFF_7911 -47.5 /* according to datasheet (at 600 Ohms) */
#define RX_QUALITY 0.1 /* FIXME: minium quality */
#define BIT_ADJUST 0.5 /* must be 0.5 to completely sync each bit */
/* frequencies used */
/* standard f0, f1 (tx) f0, f1 (rx) f0, f1 (back tx+rx) */
#define BELL_103_ORIG 1070, 1270, 2025, 2225, 0, 0,
#define BELL_103_ANS 2025, 2225, 1070, 1270, 0, 0,
#define BELL_103_ORIG_L 1070, 1270, 1070, 1270, 0, 0,
#define BELL_103_ANS_L 2025, 2225, 2025, 2225, 0, 0,
#define CCITT_V21_ORIG 1180, 980, 1850, 1650, 0, 0,
#define CCITT_V21_ANS 1850, 1650, 1180, 980, 0, 0,
#define CCITT_V21_ORG_L 1180, 980, 1180, 980, 0, 0,
#define CCITT_V21_ANS_L 1850, 1650, 1850, 1650, 0, 0,
#define CCITT_V23_M1 1700, 1300, 1700, 1300, 0, 0,
#define CCITT_V23_M1B 1700, 1300, 1700, 1300, 450, 390,
#define CCITT_V23_M2 2100, 1300, 2100, 1300, 0, 0,
#define CCITT_V23_M2B 2100, 1300, 2100, 1300, 450, 390,
#define BELL_202 2200, 1200, 2200, 1200, 0, 0,
#define BELL_202B 2200, 1200, 2200, 1200, 487, 387,
#define CCITT_V23_BACK 0, 0, 0, 0, 450, 390,
#define BELL_202_BACK 0, 0, 0, 0, 487, 387,
#define RESERVED 0, 0, 0, 0, 0, 0,
/* timings used */
/* timer/std 7910 7911 */
#define T_RCON_B103 0.2083, 0.025,
#define T_RCOFF_B103 0.0004, 0.00052,
#define T_BRCON_B103 NAN, NAN,
#define T_BRCOFF_B103 NAN, NAN,
#define T_CDON_B103 0.0291, 0.010,
#define T_CDOFF_B103 0.021, 0.007,
#define T_BCDON_B103 NAN, NAN,
#define T_BCDOFF_B103 NAN, NAN,
#define T_AT_B103 1.9, 1.9,
#define T_SIL1_B103 1.3, 2.0,
#define T_SIL2_B103 NAN, NAN,
#define T_SQ_B103 NAN, NAN,
#define T_STO_B103 NAN, NAN,
#define T_B103 \
T_RCON_B103 T_RCOFF_B103 T_BRCON_B103 T_BRCOFF_B103 \
T_CDON_B103 T_CDOFF_B103 T_BCDON_B103 T_BCDOFF_B103 \
T_AT_B103 T_SIL1_B103 T_SIL2_B103 T_SQ_B103 T_STO_B103
/* timer/std 7910 7911 */
#define T_RCON_V21 0.400, 0.025,
#define T_RCOFF_V21 0.0004, 0.00052,
#define T_BRCON_V21 NAN, NAN,
#define T_BRCOFF_V21 NAN, NAN,
#define T_CDON_V21 0.301, 0.010,
#define T_CDOFF_V21 0.021, 0.007,
#define T_BCDON_V21 NAN, NAN,
#define T_BCDOFF_V21 NAN, NAN,
#define T_AT_V21 3.0, 3.0,
#define T_SIL1_V21 1.9, 2.0,
#define T_SIL2_V21 NAN, 0.075,
#define T_SQ_V21 NAN, NAN,
#define T_STO_V21 NAN, NAN,
#define T_V21 \
T_RCON_V21 T_RCOFF_V21 T_BRCON_V21 T_BRCOFF_V21 \
T_CDON_V21 T_CDOFF_V21 T_BCDON_V21 T_BCDOFF_V21 \
T_AT_V21 T_SIL1_V21 T_SIL2_V21 T_SQ_V21 T_STO_V21
/* timer/std 7910 7911 */
#define T_RCON_V23 0.2083, 0.008,
#define T_RCOFF_V23 0.0004, 0.00052,
#define T_BRCON_V23 0.0823, 0.0823,
#define T_BRCOFF_V23 0.0004, 0.0005,
#define T_CDON_V23 0.011, 0.003,
#define T_CDOFF_V23 0.0035, 0.002,
#define T_BCDON_V23 0.017, 0.018,
#define T_BCDOFF_V23 0.021, 0.022,
#define T_AT_V23 3.0, 3.0,
#define T_SIL1_V23 1.9, 2.0,
#define T_SIL2_V23 NAN, 0.075,
#define T_SQ_V23 0.1563, 0.009,
#define T_STO_V23 NAN, 0.008,
#define T_V23 \
T_RCON_V23 T_RCOFF_V23 T_BRCON_V23 T_BRCOFF_V23 \
T_CDON_V23 T_CDOFF_V23 T_BCDON_V23 T_BCDOFF_V23 \
T_AT_V23 T_SIL1_V23 T_SIL2_V23 T_SQ_V23 T_STO_V23
/* timer/std 7910 7911 */
#define T_RCON_B202 0.1833, 0.008,
#define T_RCOFF_B202 0.0004, 0.00052,
#define T_BRCON_B202 0.0823, 0.0823,
#define T_BRCOFF_B202 0.0004, 0.0005,
#define T_CDON_B202 0.018, 0.003,
#define T_CDOFF_B202 0.012, 0.002,
#define T_BCDON_B202 0.017, 0.018,
#define T_BCDOFF_B202 0.021, 0.022,
#define T_AT_B202 1.9, 1.9,
#define T_SIL1_B202 1.3, 2.0,
#define T_SIL2_B202 NAN, NAN,
#define T_SQ_B202 0.1563, 0.009,
#define T_STO_B202 0.024, 0.008,
#define T_B202 \
T_RCON_B202 T_RCOFF_B202 T_BRCON_B202 T_BRCOFF_B202 \
T_CDON_B202 T_CDOFF_B202 T_BCDON_B202 T_BCDOFF_B202 \
T_AT_B202 T_SIL1_B202 T_SIL2_B202 T_SQ_B202 T_STO_B202
#define T_RES \
NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, \
NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, \
NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN,
/* mode definition */
static struct am791x_mode {
int sup_7910, sup_7911; /* supported */
int f0_tx, f1_tx; /* frequencies */
int f0_rx, f1_rx;
int f0_back, f1_back;
double t_rcon_7910, t_rcon_7911; /* timers */
double t_rcoff_7910, t_rcoff_7911;
double t_brcon_7910, t_brcon_7911;
double t_brcoff_7910, t_brcoff_7911;
double t_cdon_7910, t_cdon_7911;
double t_cdoff_7910, t_cdoff_7911;
double t_bcdon_7910, t_bcdon_7911;
double t_bcdoff_7910, t_bcdoff_7911;
double t_at_7910, t_at_7911;
double t_sil1_7910, t_sil1_7911;
double t_sil2_7910, t_sil2_7911;
double t_sq_7910, t_sq_7911;
double t_sto_7910, t_sto_7911;
int fullduplex; /* duplex */
int loopback_main, loopback_back; /* loopback */
int equalizer, sto; /* equalizer, soft turn off */
int bell_202; /* is BELL 202 mode */
double max_baud; /* maximum baud rate */
const char *description; /* description */
} am791x_modes[32] = {
/*sup frequencies timers duplex loop EQ,STO BELL202 maxBAUD description */
/* normal modes */
{ 1, 1, BELL_103_ORIG T_B103 1, 0, 0, 0, 0, 0, 300, "Bell 103 originate (300 bps full-duplex)" },
{ 1, 1, BELL_103_ANS T_B103 1, 0, 0, 0, 0, 1, 300, "Bell 103 answer (300 bps full-duplex)" },
{ 1, 1, BELL_202 T_B202 0, 0, 0, 0, 0, 1, 1200, "Bell 202 (1200 bps half-duplex)" },
{ 1, 1, BELL_202 T_B202 0, 0, 0, 1, 0, 1, 1200, "Bell 202 with equalizer (1200 bps half-duplex)" },
{ 1, 1, CCITT_V21_ORIG T_V21 1, 0, 0, 0, 0, 0, 300, "CCITT V.21 originate (300 bps full-duplex)" },
{ 1, 1, CCITT_V21_ANS T_V21 1, 0, 0, 0, 0, 0, 300, "CCITT V.21 answer (300 bps full-duplex)" },
{ 1, 1, CCITT_V23_M2 T_V23 0, 0, 0, 0, 0, 0, 1200, "CCITT V.23 mode 2 (1200 bps half-duplex)" },
{ 1, 1, CCITT_V23_M2 T_V23 0, 0, 0, 1, 0, 0, 1200, "CCITT V.23 mode 2 with equalizer (1200 bps half-duplex)" },
{ 1, 1, CCITT_V23_M1B T_V23 0, 0, 0, 0, 0, 0, 1200, "CCITT V.23 mode 1 (600/75 bps half-duplex)" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
{ 0, 1, BELL_202B T_B202 0, 0, 0, 0, 0, 1, 1200, "Bell 202 (1200/150 bps half-duplex)" },
{ 0, 1, BELL_202B T_B202 0, 0, 0, 1, 0, 1, 1200, "Bell 202 with equalizer (1200/150 bps half-duplex)" },
{ 0, 1, CCITT_V23_M1B T_V23 0, 0, 0, 0, 1, 0, 1200, "CCITT V.23 mode 1 with soft turn-off (600/75 bps half-duplex)" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
{ 0, 1, CCITT_V23_M2B T_V23 0, 0, 0, 0, 1, 0, 1200, "CCITT V.23 mode 2 with soft turn-off (1200/75 bps half-duplex)" },
{ 0, 1, CCITT_V23_M2B T_V23 0, 0, 0, 1, 1, 0, 1200, "CCITT V.23 mode 2 with soft turn-off and equalizer (1200/75 bps half-duplex)" },
/* loopback modes */
{ 1, 1, BELL_103_ORIG_L T_B103 0, 1, 0, 0, 0, 0, 300, "Bell 103 orig loopback (300 bps)" },
{ 1, 1, BELL_103_ANS_L T_B103 0, 1, 0, 0, 0, 0, 300, "Bell 103 answer loopback (300 bps)" },
{ 1, 1, BELL_202 T_B202 0, 1, 0, 0, 0, 1, 1200, "Bell 202 main loopback (1200 bps)" },
{ 1, 1, BELL_202 T_B202 0, 1, 0, 1, 0, 1, 1200, "Bell 202 main loopback with equalizer (1200 bps)" },
{ 1, 1, CCITT_V21_ORG_L T_V21 0, 1, 0, 0, 0, 0, 300, "CCITT V.21 originate loopback (300 bps)" },
{ 1, 1, CCITT_V21_ANS_L T_V21 0, 1, 0, 0, 0, 0, 300, "CCITT V.21 answer loopback (300 bps)" },
{ 1, 1, CCITT_V23_M2 T_V23 0, 1, 0, 0, 0, 0, 1200, "CCITT V.23 mode 2 main loopback (1200 bps)" },
{ 1, 1, CCITT_V23_M2 T_V23 0, 1, 0, 1, 0, 0, 1200, "CCITT V.23 mode 2 main loopback with equalizer (1200 bps)" },
{ 1, 1, CCITT_V23_M1 T_V23 0, 1, 0, 0, 0, 0, 1200, "CCITT V.23 mode 1 main loopback (600 bps)" },
{ 1, 1, CCITT_V23_BACK T_V23 0, 0, 1, 0, 0, 0, 150, "CCITT V.23 back loopback (75/150 bps)" },
{ 0, 1, BELL_202_BACK T_B202 0, 0, 1, 0, 0, 1, 150, "Bell 202 back loopback (150 bps)" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
{ 0, 0, RESERVED T_RES 0, 0, 0, 0, 0, 0, 0, "Reserved" },
};
const char *am791x_state_names[] = {
"INIT",
"RCON",
"CDON",
"DATA",
"RCOFF",
"CDOFF",
"STO_OFF",
"SQ_OFF",
"BRCON",
"BCDON",
"BDATA",
"BRCOFF",
"BCDOFF",
};
/* list all modes */
void am791x_list_mc(enum am791x_type type)
{
int i;
const char *description;
for (i = 0; i < 32; i++) {
if ((!type && am791x_modes[i].sup_7910) || (type && am791x_modes[i].sup_7911))
description = am791x_modes[i].description;
else
description = am791x_modes[31].description;
printf("mc %d: %s\n", i, description);
}
}
/* init STO signal */
void init_sto(am791x_t *am791x)
{
am791x->sto_phaseshift65536 = 900 / (double)am791x->samplerate * 65536.0;
}
/* transmit STO signal, use phase from FSK modulator, to avoid phase jumps */
int send_sto(am791x_t *am791x, sample_t *sample, int length)
{
fsk_mod_t *fsk = &am791x->fsk_tx;
int count = 0;
double phase, phaseshift;
phase = fsk->tx_phase65536;
/* modulate STO */
phaseshift = am791x->sto_phaseshift65536;
while (count < length && fsk->tx_bitpos < 1.0) {
sample[count++] = fsk->sin_tab[(uint16_t)phase];
phase += phaseshift;
if (phase >= 65536.0)
phase -= 65536.0;
}
fsk->tx_phase65536 = phase;
return count;
}
/* send audio from FSK modulator */
void am791x_send(am791x_t *am791x, sample_t *samples, int length)
{
if (am791x->tx_sto)
send_sto(am791x, samples, length);
else {
if (am791x->f0_tx) {
/* if filter set (even if tx_silence, we want to request bits from upper layer) */
fsk_mod_send(&am791x->fsk_tx, samples, length, 0);
}
if (!am791x->f0_tx || am791x->tx_silence) {
/* if filter not set, or silence */
memset(samples, 0, length * sizeof(*samples));
}
}
}
/* receive audio and feed into FSK demodulator */
void am791x_receive(am791x_t *am791x, sample_t *samples, int length)
{
if (!am791x->f0_rx) {
/* no mode set */
memset(samples, 0, length * sizeof(*samples));
return;
}
if (am791x->squelch) {
/* handle squelch, but then demod... */
memset(samples, 0, length * sizeof(*samples));
}
if (am791x->f0_rx) {
/* handle RX audio */
fsk_demod_receive(&am791x->fsk_rx, samples, length);
}
}
/* provide bit to FSK modulator */
static int fsk_send_bit(void *inst)
{
am791x_t *am791x = (am791x_t *)inst;
int bit, bbit;
bit = am791x->td_cb(am791x->inst);
bbit = am791x->btd_cb(am791x->inst);
/* main channel returns TD */
if (!am791x->block_td) {
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, "Modulating bit '%d' for MAIN channel\n", bit);
#endif
return bit;
}
/* back channel returns BTD */
if (!am791x->block_btd) {
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, "Modulating bit '%d' for BACK channel\n", bbit);
#endif
return bbit;
}
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, "Modulating bit '1', because TD & BTD is ignored\n");
#endif
return 1;
}
static void handle_rx_state(am791x_t *am791x);
/* get bit from FSK demodulator */
static void fsk_receive_bit(void *inst, int bit, double quality, double level)
{
am791x_t *am791x = (am791x_t *)inst;
int *block, *cd;
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, "Demodulated bit '%d' (level = %.0f dBm, quality = %%%.0f)\n", bit, level2db(level), quality * 100.0);
#endif
if (!am791x->rx_back_channel) {
block = &am791x->block_cd;
cd = &am791x->cd;
} else {
block = &am791x->block_bcd;
cd = &am791x->bcd;
}
/* detection/loss of carrier */
if (*block && *cd) {
*cd = 0;
handle_rx_state(am791x);
} else
if (!(*block) && !(*cd) && level > am791x->cd_on && quality >= RX_QUALITY) {
PDEBUG(DDSP, DEBUG_DEBUG, "Good quality (level = %.0f dBm, quality = %%%.0f)\n", level2db(level), quality * 100.0);
*cd = 1;
handle_rx_state(am791x);
} else
if (*cd && (level < am791x->cd_off || quality < RX_QUALITY)) {
PDEBUG(DDSP, DEBUG_DEBUG, "Bad quality (level = %.0f dBm, quality = %%%.0f)\n", level2db(level), quality * 100.0);
*cd = 0;
handle_rx_state(am791x);
}
/* assume 1 if no carrier */
if (!(*cd)) {
bit = 1;
quality = 0;
level = 0;
}
/* main channel forwards bit to RD */
if (!am791x->block_rd) {
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, " -> Forwarding bit '%d' to MAIN channel\n", bit);
#endif
am791x->rd_cb(am791x->inst, bit, quality * 100.0, level2db(level));
} else {
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, " -> Forwarding bit '1' to MAIN channel, because RD is set to MARK\n");
#endif
am791x->rd_cb(am791x->inst, 1, NAN, NAN);
}
/* main channel forwards bit to RD */
if (!am791x->block_brd) {
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, " -> Forwarding bit '%d' to BACK channel\n", bit);
#endif
am791x->brd_cb(am791x->inst, bit, quality * 100.0, level2db(level));
} else {
#ifdef HEAVY_DEBUG
PDEBUG(DDSP, DEBUG_DEBUG, " -> Forwarding bit '1' to BACK channel, because BRD is set to MARK\n");
#endif
am791x->brd_cb(am791x->inst, 1, NAN, NAN);
}
}
/* setup FSK */
static void set_filters(am791x_t *am791x)
{
const char *name_tx = NULL, *name_rx = NULL;
int f0_tx = -1, f1_tx = -1;
int f0_rx = -1, f1_rx = -1;
uint8_t mc = am791x->mc;
/* not supported */
if (!((am791x->type) ? am791x_modes[mc].sup_7911 : am791x_modes[mc].sup_7910)) {
f0_tx = 0;
f1_tx = 0;
f0_rx = 0;
f1_rx = 0;
} else
switch (am791x->tx_state) {
case AM791X_STATE_INIT:
/* when RTS and BRTS are not asserted */
f0_tx = 0; // TX !!
f1_tx = 0;
name_tx = "MAIN";
if (am791x->loopback_back) {
/* loopback (BACK): always listens to back channel */
f0_rx = am791x_modes[mc].f0_back; // RX !!
f1_rx = am791x_modes[mc].f1_back;
name_rx = "BACK";
am791x->rx_back_channel = 1;
} else {
/* listen to main channel's RX frequencies */
f0_rx = am791x_modes[mc].f0_rx; // RX !!
f1_rx = am791x_modes[mc].f1_rx;
name_rx = "MAIN";
am791x->rx_back_channel = 0;
}
break;
case AM791X_STATE_RCON:
/* when asserting RTS */
f0_tx = am791x_modes[mc].f0_tx;
f1_tx = am791x_modes[mc].f1_tx;
name_tx = "MAIN";
if (!am791x->loopback_main && am791x_modes[mc].f0_back && am791x_modes[mc].f1_back) {
/* switch receiver to back channel, (if not in loopback mode) */
f0_rx = am791x_modes[mc].f0_back;
f1_rx = am791x_modes[mc].f1_back;
name_rx = "BACK";
am791x->rx_back_channel = 1;
}
break;
case AM791X_STATE_BRCON:
/* when asserting BRTS */
f0_tx = am791x_modes[mc].f0_back;
f1_tx = am791x_modes[mc].f1_back;
name_tx = "BACK";
break;
default:
/* keep current frequencies in other state */
return;
}
/* transmitter not used anymore or has changed */
if (f0_tx >= 0 && am791x->f0_tx && (f0_tx != am791x->f0_tx || f1_tx != am791x->f1_tx)) {
/* disable transmitter */
fsk_mod_cleanup(&am791x->fsk_tx);
am791x->f0_tx = 0;
am791x->f1_tx = 0;
}
/* transmitter used */
if (f0_tx > 0 && am791x->f0_tx == 0) {
PDEBUG(DDSP, DEBUG_DEBUG, "Setting modulator to %s channel's frequencies (F0 = %d, F1 = %d), baudrate %.0f\n", name_tx, f0_tx, f1_tx, am791x->tx_baud);
if (fsk_mod_init(&am791x->fsk_tx, am791x, fsk_send_bit, am791x->samplerate, am791x->tx_baud, (double)f0_tx, (double)f1_tx, am791x->tx_level, 0, 1) < 0)
PDEBUG(DDSP, DEBUG_ERROR, "FSK RX init failed!\n");
else {
am791x->f0_tx = f0_tx;
am791x->f1_tx = f1_tx;
}
}
/* receiver has changed */
if (f0_rx >= 0 && am791x->f0_rx && (f0_rx != am791x->f0_rx || f1_rx != am791x->f1_rx)) {
/* disable receiver */
fsk_demod_cleanup(&am791x->fsk_rx);
am791x->f0_rx = 0;
am791x->f1_rx = 0;
}
/* receiver used */
if (f0_rx > 0 && am791x->f0_rx == 0) {
PDEBUG(DDSP, DEBUG_DEBUG, "Setting demodulator to %s channel's frequencies (F0 = %d, F1 = %d), baudrate %.0f\n", name_rx, f0_rx, f1_rx, am791x->rx_baud);
if (fsk_demod_init(&am791x->fsk_rx, am791x, fsk_receive_bit, am791x->samplerate, am791x->rx_baud, (double)f0_rx, (double)f1_rx, BIT_ADJUST) < 0)
PDEBUG(DDSP, DEBUG_ERROR, "FSK RX init failed!\n");
else {
am791x->f0_rx = f0_rx;
am791x->f1_rx = f1_rx;
}
}
}
/* new state */
static void new_tx_state(am791x_t *am791x, enum am791x_st state)
{
if (am791x->tx_state != state)
PDEBUG(DAM791X, DEBUG_DEBUG, "Change TX state %s -> %s\n", am791x_state_names[am791x->tx_state], am791x_state_names[state]);
am791x->tx_state = state;
}
static void new_rx_state(am791x_t *am791x, enum am791x_st state)
{
if (am791x->rx_state != state)
PDEBUG(DAM791X, DEBUG_DEBUG, "Change RX state %s -> %s\n", am791x_state_names[am791x->rx_state], am791x_state_names[state]);
am791x->rx_state = state;
}
/* new flags */
static void set_flag(int *flag_p, int value, const char *name)
{
if (*flag_p != value) {
PDEBUG(DAM791X, DEBUG_DEBUG, " -> %s\n", name);
*flag_p = value;
}
}
/*
* state machine according to datasheet
*/
static void go_main_channel_tx(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Enable transmitter on main channel\n");
/* only block RD, if not full duplex and not 4-wire (loopback mode) */
if (!am791x->fullduplex && !am791x->loopback_main) {
set_flag(&am791x->block_rd, 1, "RD = MARK");
set_flag(&am791x->block_cd, 1, "SET CD HIGH");
}
/* activate TD now and set CTS timer (RCON) */
set_flag(&am791x->block_td, 0, "TD RELEASED");
set_flag(&am791x->tx_silence, 0, "RESET SILENCE");
timer_start(&am791x->tx_timer, am791x->t_rcon);
new_tx_state(am791x, AM791X_STATE_RCON);
set_filters(am791x);
/* check CD to be blocked */
if (!am791x->fullduplex && !am791x->loopback_main) {
if (am791x->line_cd) {
/* send CD off */
am791x->cd_cb(am791x->inst, 0);
}
}
}
static void rcon_release_rts(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "RTS was released\n");
set_flag(&am791x->block_td, 1, "TD IGNORED");
set_flag(&am791x->tx_silence, 1, "SET SILENCE");
set_flag(&am791x->block_cd, 0, "RELEASE CD");
new_tx_state(am791x, AM791X_STATE_INIT);
set_filters(am791x);
/* check CD to be released */
if (am791x->line_cd) {
/* send CD on */
am791x->cd_cb(am791x->inst, 1);
}
}
static void rcon_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Transmission started\n");
new_tx_state(am791x, AM791X_STATE_DATA);
/* CTS on */
am791x->cts_cb(am791x->inst, 1);
}
static void tx_data_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "RTS was released\n");
new_tx_state(am791x, AM791X_STATE_RCOFF);
set_flag(&am791x->block_td, 1, "TD IGNORED");
if (am791x->sto) {
set_flag(&am791x->tx_sto, 1, "start STO");
} else {
set_flag(&am791x->tx_silence, 1, "SET SILENCE (if not STO)");
}
if (!am791x->fullduplex) {
set_flag(&am791x->squelch, 1, "SET SQUELCH (ON)");
}
timer_start(&am791x->tx_timer, am791x->t_rcoff);
}
static void rcoff_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Transmission over\n");
/* CTS off */
am791x->cts_cb(am791x->inst, 0);
if (am791x->fullduplex) {
new_tx_state(am791x, AM791X_STATE_INIT);
set_filters(am791x);
return;
}
if (!am791x->sto) {
timer_start(&am791x->tx_timer, am791x->t_sq - am791x->t_rcoff);
new_tx_state(am791x, AM791X_STATE_SQ_OFF);
return;
}
timer_start(&am791x->tx_timer, am791x->t_sto - am791x->t_rcoff);
new_tx_state(am791x, AM791X_STATE_STO_OFF);
}
static void sq_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Squelch over\n");
set_flag(&am791x->block_cd, 0, "CD RELEASED");
new_tx_state(am791x, AM791X_STATE_INIT);
set_filters(am791x);
/* SET SQUELCH OFF */
set_flag(&am791x->squelch, 0, "SET SQUELCH OFF");
if (am791x->line_cd) {
/* send CD on */
am791x->cd_cb(am791x->inst, 1);
}
}
static void sto_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "STO over\n");
set_flag(&am791x->tx_sto, 0, "stop STO");
timer_start(&am791x->tx_timer, am791x->t_sq - am791x->t_sto);
new_tx_state(am791x, AM791X_STATE_SQ_OFF);
}
static void go_back_channel_tx(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Enable transmitter on back channel\n");
if (!am791x->loopback_back) {
set_flag(&am791x->block_brd, 1, "BRD = MARK");
set_flag(&am791x->block_bcd, 1, "SET BCD HIGH");
}
/* activate BTD now and set BCTS timer (BRCON) */
set_flag(&am791x->block_btd, 0, "BTD RELEASED");
set_flag(&am791x->tx_silence, 0, "RESET SILENCE");
timer_start(&am791x->tx_timer, am791x->t_brcon);
new_tx_state(am791x, AM791X_STATE_BRCON);
set_filters(am791x);
/* check BCD to be blocked */
if (!am791x->loopback_back) {
if (am791x->line_bcd) {
/* send BCD off */
am791x->bcd_cb(am791x->inst, 0);
}
}
}
static void brcon_release_brts(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "BRTS was released\n");
set_flag(&am791x->tx_silence, 1, "SET SILENCE");
new_tx_state(am791x, AM791X_STATE_INIT);
set_filters(am791x);
}
static void brcon_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Transmission started\n");
new_tx_state(am791x, AM791X_STATE_BDATA);
/* BCTS on */
am791x->bcts_cb(am791x->inst, 1);
}
static void tx_bdata_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "BRTS was released\n");
set_flag(&am791x->block_btd, 1, "BTD IGNORED");
set_flag(&am791x->tx_silence, 1, "SET SILENCE");
timer_start(&am791x->tx_timer, am791x->t_brcoff);
}
static void brcoff_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Transmission over\n");
/* BCTS off */
am791x->bcts_cb(am791x->inst, 0);
set_flag(&am791x->block_bcd, 0, "BCD RELEASED");
new_tx_state(am791x, AM791X_STATE_INIT);
set_filters(am791x);
/* check BCD to be released */
if (am791x->line_bcd) {
/* send BCD on */
am791x->bcd_cb(am791x->inst, 1);
}
}
static void handle_tx_state(am791x_t *am791x)
{
switch (am791x->tx_state) {
/* depending on the states we change to "main TX " or to "back TX" state */
case AM791X_STATE_INIT:
/* select TX on main or back channel, according to states */
if (am791x->line_brts) {
if (am791x->loopback_back) {
go_back_channel_tx(am791x);
break;
}
if (!am791x->line_rts && !am791x->loopback_main && !am791x->fullduplex) {
go_back_channel_tx(am791x);
break;
}
}
if (!am791x->loopback_back && am791x->line_rts) {
go_main_channel_tx(am791x);
break;
}
break;
/* all main channel states ... */
case AM791X_STATE_RCON:
if (!am791x->line_rts) {
rcon_release_rts(am791x);
break;
}
if (!timer_running(&am791x->tx_timer)) {
rcon_done(am791x);
break;
}
break;
case AM791X_STATE_DATA:
if (!am791x->line_rts) {
tx_data_done(am791x);
break;
}
break;
case AM791X_STATE_RCOFF:
if (!timer_running(&am791x->tx_timer)) {
rcoff_done(am791x);
break;
}
break;
case AM791X_STATE_STO_OFF:
if (!timer_running(&am791x->tx_timer)) {
sto_done(am791x);
break;
}
break;
case AM791X_STATE_SQ_OFF:
if (!timer_running(&am791x->tx_timer)) {
sq_done(am791x);
break;
}
break;
/* all back channel states */
case AM791X_STATE_BRCON:
if (!am791x->line_brts) {
brcon_release_brts(am791x);
break;
}
if (!timer_running(&am791x->tx_timer)) {
brcon_done(am791x);
break;
}
break;
case AM791X_STATE_BDATA:
if (!am791x->line_brts) {
tx_bdata_done(am791x);
break;
}
break;
case AM791X_STATE_BRCOFF:
if (!timer_running(&am791x->tx_timer)) {
brcoff_done(am791x);
break;
}
break;
default:
PDEBUG(DAM791X, DEBUG_ERROR, "State %s not handled!\n", am791x_state_names[am791x->rx_state]);
}
}
static void go_main_channel_rx(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Enable receiver on main channel\n");
timer_start(&am791x->rx_timer, am791x->t_cdon);
new_rx_state(am791x, AM791X_STATE_CDON);
}
static void cdon_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Reception started\n");
set_flag(&am791x->block_rd, 0, "RD RELEASED");
new_rx_state(am791x, AM791X_STATE_DATA);
set_flag(&am791x->line_cd, 1, "set CD");
/* check CD not blocked */
if (!am791x->block_cd) {
/* send CD on */
am791x->cd_cb(am791x->inst, 1);
}
}
static void cdon_no_cd(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier is gone\n");
timer_stop(&am791x->rx_timer);
new_rx_state(am791x, AM791X_STATE_INIT);
}
static void rx_data_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier lost\n");
timer_start(&am791x->rx_timer, am791x->t_cdoff);
new_rx_state(am791x, AM791X_STATE_CDOFF);
}
static void cdoff_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Reception finished\n");
set_flag(&am791x->block_rd, 1, "RD = MARK");
new_rx_state(am791x, AM791X_STATE_INIT);
set_flag(&am791x->line_cd, 0, "release CD");
/* check CD not blocked */
if (!am791x->block_cd) {
/* send CD off */
am791x->cd_cb(am791x->inst, 0);
}
}
static void cdoff_cd(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier recovered\n");
timer_stop(&am791x->rx_timer);
new_rx_state(am791x, AM791X_STATE_DATA);
}
static void go_back_channel_rx(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Enable receiver on back channel\n");
timer_start(&am791x->rx_timer, am791x->t_bcdon);
new_rx_state(am791x, AM791X_STATE_BCDON);
}
static void bcdon_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier was detected\n");
set_flag(&am791x->block_brd, 0, "BRD RELEASED");
new_rx_state(am791x, AM791X_STATE_BDATA);
set_flag(&am791x->line_bcd, 1, "set BCD");
/* check BCD not blocked */
if (!am791x->block_bcd) {
/* send BCD on */
am791x->bcd_cb(am791x->inst, 1);
}
}
static void bcdon_no_cd(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier is gone\n");
timer_stop(&am791x->rx_timer);
new_rx_state(am791x, AM791X_STATE_INIT);
}
static void rx_bdata_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier lost\n");
timer_start(&am791x->rx_timer, am791x->t_bcdoff);
new_rx_state(am791x, AM791X_STATE_BCDOFF);
}
static void bcdoff_done(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Reception finished\n");
if (!am791x->bell_202)
set_flag(&am791x->block_brd, 1, "BRD = MARK");
new_rx_state(am791x, AM791X_STATE_INIT);
set_flag(&am791x->line_bcd, 0, "release BCD");
/* check BCD not blocked */
if (!am791x->block_bcd) {
/* send BCD off */
am791x->bcd_cb(am791x->inst, 0);
}
}
static void bcdoff_cd(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Carrier recovered\n");
timer_stop(&am791x->rx_timer);
new_rx_state(am791x, AM791X_STATE_BDATA);
}
static void handle_rx_state(am791x_t *am791x)
{
switch (am791x->rx_state) {
case AM791X_STATE_INIT:
/* select RX on main or back channel, according to states */
if (!am791x->loopback_back) {
if (am791x->cd) {
go_main_channel_rx(am791x);
break;
}
if (!am791x->loopback_main && !am791x->fullduplex && am791x->bcd) {
go_back_channel_rx(am791x);
break;
}
} else {
if (am791x->bcd) {
go_back_channel_rx(am791x);
break;
}
}
break;
/* all main channel states ... */
case AM791X_STATE_CDON:
if (!timer_running(&am791x->rx_timer)) {
cdon_done(am791x);
break;
}
if (!am791x->cd) {
cdon_no_cd(am791x);
break;
}
break;
case AM791X_STATE_DATA:
if (!am791x->cd) {
rx_data_done(am791x);
break;
}
break;
case AM791X_STATE_CDOFF:
if (!timer_running(&am791x->rx_timer)) {
cdoff_done(am791x);
break;
}
if (am791x->cd) {
cdoff_cd(am791x);
break;
}
break;
/* all back channel states ... */
case AM791X_STATE_BCDON:
if (!timer_running(&am791x->rx_timer)) {
bcdon_done(am791x);
break;
}
if (!am791x->bcd) {
bcdon_no_cd(am791x);
break;
}
break;
case AM791X_STATE_BDATA:
if (!am791x->bcd) {
rx_bdata_done(am791x);
break;
}
break;
case AM791X_STATE_BCDOFF:
if (!timer_running(&am791x->rx_timer)) {
bcdoff_done(am791x);
break;
}
if (am791x->bcd) {
bcdoff_cd(am791x);
break;
}
break;
default:
PDEBUG(DAM791X, DEBUG_ERROR, "State %s not handled!\n", am791x_state_names[am791x->rx_state]);
}
}
/* handle both (rx and tx) states */
static void handle_state(am791x_t *am791x)
{
/* DTR blocks all */
if (!am791x->line_dtr) {
/* do reset of all states, if not in INIT state */
if (am791x->tx_state != AM791X_STATE_INIT || am791x->rx_state != AM791X_STATE_INIT)
am791x_reset(am791x);
return;
}
/* handle states if DTR is on */
handle_tx_state(am791x);
handle_rx_state(am791x);
}
/* timeout events */
static void tx_timeout(struct timer *timer)
{
am791x_t *am791x = (am791x_t *)timer->priv;
handle_tx_state(am791x);
}
static void rx_timeout(struct timer *timer)
{
am791x_t *am791x = (am791x_t *)timer->priv;
handle_rx_state(am791x);
}
/* init routine, must be called before anything else */
int am791x_init(am791x_t *am791x, void *inst, enum am791x_type type, uint8_t mc, int samplerate, double tx_baud, double rx_baud, void (*cts)(void *inst, int cts), void (*bcts)(void *inst, int cts), void (*cd)(void *inst, int cd), void (*bcd)(void *inst, int cd), int (*td)(void *inst), int (*btd)(void *inst), void (*rd)(void *inst, int bit, double quality, double level), void (*brd)(void *inst, int bit, double quality, double level))
{
memset(am791x, 0, sizeof(*am791x));
/* init timers */
timer_init(&am791x->tx_timer, tx_timeout, am791x);
timer_init(&am791x->rx_timer, rx_timeout, am791x);
PDEBUG(DAM791X, DEBUG_DEBUG, "Initializing instance of AM791%d:\n", type);
am791x->inst = inst;
am791x->type = type;
am791x->samplerate = samplerate;
am791x->cts_cb = cts;
am791x->bcts_cb = bcts;
am791x->cd_cb = cd;
am791x->bcd_cb = bcd;
am791x->td_cb = td;
am791x->btd_cb = btd;
am791x->rd_cb = rd;
am791x->brd_cb = brd;
/* levels */
am791x->tx_level = db2level(TX_LEVEL);
am791x->cd_on = db2level((am791x->type) ? RX_CD_ON_7911 : RX_CD_ON_7910);
am791x->cd_off = db2level((am791x->type) ? RX_CD_OFF_7911 : RX_CD_OFF_7910);
init_sto(am791x);
/* set initial mode and reset */
am791x_mc(am791x, mc, samplerate, tx_baud, rx_baud);
return 0;
}
/* exit routine, must be called when exit */
void am791x_exit(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Exit instance of AM791%d:\n", am791x->type);
/* bring to reset state, be sure to clean FSK processes */
am791x_reset(am791x);
timer_exit(&am791x->tx_timer);
timer_exit(&am791x->rx_timer);
}
/* get some default baud rate for each mode, before IOCTL sets it (if it sets it) */
double am791x_max_baud(uint8_t mc)
{
if (am791x_modes[mc].max_baud)
return am791x_modes[mc].max_baud;
else
return 300; /* useless modem. just report something to the caller */
}
/* change mode on the fly, may be called any time by upper layer */
int am791x_mc(am791x_t *am791x, uint8_t mc, int samplerate, double tx_baud, double rx_baud)
{
int rc = 0;
/* prevent out of range value */
if (mc >= 32)
mc = 0;
if (!((am791x->type) ? am791x_modes[mc].sup_7911 : am791x_modes[mc].sup_7910))
rc = -EINVAL;
PDEBUG(DAM791X, DEBUG_INFO, "Setting mode %d: %s\n", mc, am791x_modes[mc].description);
PDEBUG(DAM791X, DEBUG_DEBUG, " -> Baud rate: %.1f/%.1f\n", rx_baud, tx_baud);
am791x->mc = mc;
am791x->samplerate = samplerate;
am791x->tx_baud = tx_baud;
am791x->rx_baud = rx_baud;
am791x->t_rcon = (am791x->type) ? am791x_modes[mc].t_rcon_7911 : am791x_modes[mc].t_rcon_7910;
am791x->t_rcoff = (am791x->type) ? am791x_modes[mc].t_rcoff_7911 : am791x_modes[mc].t_rcoff_7910;
am791x->t_brcon = (am791x->type) ? am791x_modes[mc].t_brcon_7911 : am791x_modes[mc].t_brcon_7910;
am791x->t_brcoff = (am791x->type) ? am791x_modes[mc].t_brcoff_7911 : am791x_modes[mc].t_brcoff_7910;
am791x->t_cdon = (am791x->type) ? am791x_modes[mc].t_cdon_7911 : am791x_modes[mc].t_cdon_7910;
am791x->t_cdoff = (am791x->type) ? am791x_modes[mc].t_cdoff_7911 : am791x_modes[mc].t_cdoff_7910;
am791x->t_bcdon = (am791x->type) ? am791x_modes[mc].t_bcdon_7911 : am791x_modes[mc].t_bcdon_7910;
am791x->t_bcdoff = (am791x->type) ? am791x_modes[mc].t_bcdoff_7911 : am791x_modes[mc].t_bcdoff_7910;
am791x->t_at = (am791x->type) ? am791x_modes[mc].t_at_7911 : am791x_modes[mc].t_at_7910;
am791x->t_sil1 = (am791x->type) ? am791x_modes[mc].t_sil1_7911 : am791x_modes[mc].t_sil1_7910;
am791x->t_sil2 = (am791x->type) ? am791x_modes[mc].t_sil2_7911 : am791x_modes[mc].t_sil2_7910;
am791x->t_sq = (am791x->type) ? am791x_modes[mc].t_sq_7911 : am791x_modes[mc].t_sq_7910;
am791x->t_sto = (am791x->type) ? am791x_modes[mc].t_sto_7911 : am791x_modes[mc].t_sto_7910;
am791x->fullduplex = am791x_modes[mc].fullduplex;
am791x->loopback_main = am791x_modes[mc].loopback_main;
am791x->loopback_back = am791x_modes[mc].loopback_back;
am791x->equalizer = am791x_modes[mc].equalizer;
am791x->sto = am791x_modes[mc].sto;
am791x->bell_202 = am791x_modes[mc].bell_202;
/* changing mode causes a reset */
am791x_reset(am791x);
/* Return error on invalid mode. The emualtion still works, but no TX/RX possible. */
return rc;
}
/* reset at any time, may be called any time by upper layer */
void am791x_reset(am791x_t *am791x)
{
PDEBUG(DAM791X, DEBUG_INFO, "Reset!\n");
timer_stop(&am791x->tx_timer);
timer_stop(&am791x->rx_timer);
if (am791x->f0_tx) {
fsk_mod_cleanup(&am791x->fsk_tx);
am791x->f0_tx = 0;
am791x->f1_tx = 0;
}
if (am791x->f0_rx) {
fsk_demod_cleanup(&am791x->fsk_rx);
am791x->f0_rx = 0;
am791x->f1_rx = 0;
}
/* initial states */
am791x->tx_state = AM791X_STATE_INIT;
am791x->rx_state = AM791X_STATE_INIT;
am791x->tx_silence = 1; /* state machine implies that silence is sent */
am791x->tx_sto = 0; /* state machine implies that STO is not sent */
am791x->block_td = 1; /* state machine implies that TD is MARK (1) */
am791x->block_rd = 1; /* state machine implies that RD is ignored */
am791x->line_cd = 0;
am791x->block_cd = 0; /* state machine implies that CD is released */
am791x->block_btd = 1; /* state machine implies that BTD is MARK (1) */
am791x->block_brd = 1; /* state machine implies that BTD is ignored */
am791x->line_bcd = 0;
am791x->block_bcd = 0; /* state machine implies that BCD is released */
am791x->squelch = 0; /* state machine implies that squelch is off */
/* set filters, if DTR is on */
if (am791x->line_dtr)
set_filters(am791x);
handle_state(am791x);
}
/* change input lines */
void am791x_dtr(am791x_t *am791x, int dtr)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Terminal is%s ready!\n", (dtr) ? "" : " not");
/* set filters, if DTR becomes on */
if (!am791x->line_dtr && dtr) {
am791x->line_dtr = dtr;
set_filters(am791x);
} else
am791x->line_dtr = dtr;
handle_state(am791x);
}
void am791x_rts(am791x_t *am791x, int rts)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Terminal %s RTS.\n", (rts) ? "sets" : "clears");
am791x->line_rts = rts;
handle_state(am791x);
}
void am791x_brts(am791x_t *am791x, int rts)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Terminal %s BRTS.\n", (rts) ? "sets" : "clears");
am791x->line_brts = rts;
handle_state(am791x);
}
void am791x_ring(am791x_t *am791x, int ring)
{
PDEBUG(DAM791X, DEBUG_DEBUG, "Terminal %s RING.\n", (ring) ? "sets" : "clears");
am791x->line_ring = ring;
handle_state(am791x);
}