isdnfax/G3/fsm.c

613 lines
15 KiB
C

/* $Id$
******************************************************************************
Fax program for ISDN.
Main state-machine for G3-fax handeling.
Copyright (C) 1999 Morten Rolland [Morten.Rolland@asker.mail.telia.com]
Copyright (C) 1999 Thomas Reinemannn [tom.reinemann@gmx.net]
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************
*/
/* This file contains all the states and their related logic needed to
* implement a G3-fax encoder/decoder. It is important to notice that
* the states of this state-machine may not always map directly to
* the G3-fax specifications. The reason for this is the fact that
* everything is handeled in software in this implementation, and that
* extra states and logic may be needed to handle problems usually
* handled by hardware directely.
*/
#include <ifax/types.h>
#include <ifax/G3/fax.h>
#include <ifax/G3/kernel.h>
#include <ifax/G3/g3-timers.h>
#include <ifax/G3/commandframes.h>
#include <ifax/misc/timers.h>
#include <ifax/misc/softsignals.h>
#include <ifax/misc/statemachine.h>
#include <ifax/modules/hdlc-framing.h>
/* All timing is done in units of 1/8000 seconds (one ISDN-sample) */
// How about tolerances?
#define SEVENTYFIVEMILLISECONDS 600
#define ZEROPOINTTWOSECONDS 1600
#define ZEROPOINTFIFESECONDS 4000
#define ONESECOND 8000
#define THREESECONDS 24000
#define THREEPOINTEIGHTSECONDS 30400
#define SIXSECONDS 48000
#define TENSECONDS 80000
#define THIRTYFIVESECONDS 280000
#define SIXTYSECONDS 480000
#define T1_TIME THIRTYFIVESECONDS
#define T2_TIME SIXSECONDS
#define T3_TIME TENSECONDS
#define T5_TIME SIXTYSECONDS
/* Global states that may get called from other files as well as from
* this one.
*/
DEFGLOBALSTATE(fsm_wait_softsignal)
DEFGLOBALSTATE(do_hard_exit)
/* List states here that is called in a forward fashion (most of them) */
DEFSTATE(start_answer_incomming)
DEFSTATE(do_CED)
DEFSTATE(done_CED)
DEFSTATE(start_DIS)
DEFSTATE(do_DIS)
DEFSTATE(done_DIS)
DEFSTATE(hunt_for_DCS_or_DTC)
/* Jump to 'start_answer_incomming' when an incomming call is
* accepted (answered) and we identify ourselves as a fax-machine.
* The initialize_fsm_incomming() function is used to initialize the fsm
* and signal-chain to handle an incomming call and jump to this state.
*/
void fax_initialize_fsm_incomming()
{
fax_setup_outgoing_DIS(); /* Prepare data-frames in 'fax' structure */
fax_setup_outgoing_NSF();
fax_setup_outgoing_CSI();
initialize_statemachines(); /* Reset all state-machines for a fresh start */
init_fsm(FSM_FAX_MAIN,start_answer_incomming); /* Get the main one going */
ifax_connect(fax->silence,fax->rateconv7k2to8k0); /* Start off silent */
}
STATE(start_answer_incomming)
{
/* Stay silent for 0.2 seconds before outputing the CED */
FSMWAITJUMP(TIMER_AUX,ZEROPOINTTWOSECONDS,do_CED);
}
STATE(do_CED)
{
/* Output the CED sinus signal for 3.8 sec */
ifax_connect(fax->sinusCED,fax->rateconv7k2to8k0);
FSMWAITJUMP(TIMER_AUX,THREEPOINTEIGHTSECONDS,done_CED);
}
STATE(done_CED)
{
/* After the CED, wait 75ms and do the DIS */
ifax_connect(fax->silence,fax->rateconv7k2to8k0);
FSMWAITJUMP(TIMER_AUX,SEVENTYFIVEMILLISECONDS,start_DIS);
}
STATE(start_DIS)
{
/* When we hook up the HDLC+V.21 they go online and send FLAGs */
ifax_connect(fax->modulatorV21,fax->rateconv7k2to8k0);
ifax_connect(fax->encoderHDLC,fax->modulatorV21);
/* Keep sending FLAGs for one second before proceeding with real frames */
FSMWAITJUMP(TIMER_AUX,ONESECOND,do_DIS);
}
STATE(do_DIS)
{
/* After one second of FLAGs, we transmit the DIS (and optional frames) */
if ( fax->NSFsize ) {
/* There is a NSF */
ifax_command(fax->encoderHDLC,CMD_HDLC_FRAMING_TXFRAME,
fax->NSF,fax->NSFsize,255);
}
if ( fax->CSIsize ) {
/* There is a CSI */
ifax_command(fax->encoderHDLC,CMD_HDLC_FRAMING_TXFRAME,
fax->CSI,fax->CSIsize,255);
}
/* Assume there is a DIS (it better...) */
ifax_command(fax->encoderHDLC,CMD_HDLC_FRAMING_TXFRAME,
fax->DIS,fax->DISsize,255);
FSMJUMP(done_DIS);
}
STATE(done_DIS)
{
/* Wait until the HDLC-frames has been transmitted before receiving */
if ( ifax_command(fax->encoderHDLC,CMD_HDLC_FRAMING_IDLE) > 2 ) {
ifax_connect(fax->silence,fax->rateconv7k2to8k0);
FSMJUMP(hunt_for_DCS_or_DTC);
}
}
STATE(hunt_for_DCS_or_DTC)
{
/* Stay online a little to flush buffers, then exit */
FSMWAITJUMP(TIMER_AUX,ONESECOND,do_hard_exit);
}
/**********************************************************************
*
* Utility functions/states/subroutines.
*/
/* The 'fsm_wait_softsignal' is a subroutine called by the 'FSMWAIT' macro
* to perform the actual waiting... May be used to wait for other signals
* as well.
*/
GLOBALSTATE(fsm_wait_softsignal)
{
if ( softsignaled_clr(fsmself->arg) ) {
FSMRETURN;
}
}
GLOBALSTATE(do_hard_exit)
{
exit(17);
}
#if 0
/* The following code can't be used yet, because so many other modules
* lack the softsignaling functionality when certain events occour.
* Some other modules required has not been written yet.
*/
/**********************************************************************
* SENDING A FAX
* Jump to 'start_sending_fax' when an user wishes to send a fax
* The initialize_fsm_sending() function is used to initialize the fsm
* and signal-chain to handle sending a fax.
*/
DEFSTATE(start_sending_fax)
DEFSTATE(con_established)
DEFSTATE(A_CNG_sound)
DEFSTATE(A_CNG_silence)
DEFSTATE(entry_PhaseB)
void initialize_fsm_sending(struct G3fax *fax)
{
fax->fsm = start_sending_fax;
fax_run_internals(fax);
}
STATE (start_sending_fax)
{
dial_isdn (number);
fax->fsm = con_established;
one_shot_timer(TIMER_DIAL, THIRTYFIFESECONDS);
one_shot_timer(TIMER_AUX, THREESECONDS);
}
STATE (con_established)
{
if (conected) {
fax->fsm = A_CNG_sound;
one_shot_timer(TIMER_AUX, ZEROPOINTFIFESECONDS);
return;
}
if () {
return_from_subroutine (fax);
return;
}
}
STATE (A_CNG_silence)
{
if ( softsignaled(TIMER_AUX) ) {
ifax_connect(fax->sinusCNG,fax->rateconv7k2to8k0);
one_shot_timer(TIMER_AUX, ZEROPOINTFIFESECONDS);
fax->fsm = A_CNG_sound;
return;
}
if (softsignaled (CED)) {
fax->fsm = entry_PhaseB;
return;
}
if (softsignaled(TIMER_DIAL)) {
softsignal(ACTION_HANGUP);
return_from_subroutine (fax);
return;
}
}
STATE (A_CNG_sound)
{
if ( softsignaled(TIMER_AUX) ) {
ifax_connect(fax->silence,fax->rateconv7k2to8k0);
one_shot_timer(TIMER_AUX, THREESECONDS);
fax->fsm = A_CNG_silence;
return;
}
if (softsignaled (CED)) {
fax->fsm = entry_PhaseB;
return;
}
if (softsignaled(TIMER_DIAL)) {
softsignal(ACTION_HANGUP);
return_from_subroutine (fax);
return;
}
}
/****************************************************************************
*
* Phase B, FIGURE 5-2a/T.30
*
* State-machine for phase B of transmitting terminal
*/
DEFSTATE(B_CNG_sound)
DEFSTATE(B_CNG_silence)
STATE (entry_PhaseB) {
one_shot_timer(TIMER_T1, T1_TIME);
}
STATE (B_CNG_silence)
{
if ( softsignaled(TIMER_AUX) ) {
ifax_connect(fax->sinusCNG,fax->rateconv7k2to8k0);
one_shot_timer(TIMER_AUX, ZEROPOINTFIFESECONDS);
fax->fsm = B_CNG_sound;
return;
}
if (softsignaled_clr(DIS_RECEIVED) || softsignaled_clr (DTC_RECEIVED)) {
fax->fsm = entry_PhaseB;
return;
}
if (softsignaled(T1)) {
softsignal(ACTION_HANGUP);
return_from_subroutine (fax);
return;
}
}
STATE (B_CNG_sound)
{
if ( softsignaled(TIMER_AUX) ) {
ifax_connect(fax->silence,fax->rateconv7k2to8k0);
one_shot_timer(TIMER_AUX, THREESECONDS);
fax->fsm = B_CNG_silence;
return;
}
if (softsignaled_clr(DIS_RECEIVED) || softsignaled_clr (DTC_RECEIVED)) {
fax->fsm = entry_PhaseB;
return;
}
if (softsignaled(T1)) {
softsignal(ACTION_HANGUP);
return_from_subroutine (fax);
return;
}
}
/****************************************************************************
*
* Phase E; Call Release, FIGURE 5-2d/T.30
*
* This is the call release part. It contains only the entry point to
* disconnect the line.
*
*/
DEFSTATE (Entry_B)
STATE(Entry_B)
{
softsignal(ACTION_HANGUP);
return_from_subroutine(fax);
}
/****************************************************************************
*
* Response Received, FIGURE 5-2s/T.30
*
* This state-machine subroutine checks if a valid response has
* arrived within the time-limits. If an illegal situation
* occours, like the remote transmitting more than it is allowed to,
* a hangup is performed.
*
* By doing 'call_subroutine' on the state 'response_received',
* the answer will be available in RESPONSERECEIVED_RESULT .
*/
DEFSTATE(response_received_flag)
DEFSTATE(response_received_raf)
DEFSTATE(response_received_sg1)
DEFSTATE(response_received_sg2)
DEFSTATE(response_received_sg2_helper)
DEFSTATE(response_received_tdcn)
DEFSTATE(response_received_disconnect)
STATE(response_received)
{
one_shot_timer(TIMER_T4,THREESECONDS);
fax->fsm = response_received_flag;
}
STATE(response_received_flag)
{
if ( softsignaled_clr(HDLCFLAGDETECTED) ) {
one_shot_timer(TIMER_SHUTUP,THREESECONDS);
fax->fsm = response_received_RAF;
}
if ( softsignaled(TIMER_T4) ) {
softsignal_set(RESPONSERECEIVED_RESULT,0);
return_from_subroutine(fax);
}
}
STATE(response_received_raf)
{
if ( softsignaled_clr(HDLCFRAMERECEIVED) ) {
fax_decode_controlmsg(fax);
if ( fax->FCSerror || fax->ctrlmsg == MSG_CRP ) {
fax->fsm = response_received_sg2;
return;
}
if ( fax->ctrlmsg == MSG_DCN ) {
fax->fsm = response_received_disconnect;
return;
}
if ( process_optional_response(fax) ) {
fax->fsm = response_received_flag;
return;
}
softsignal(RESPONSERECEIVED_RESULT);
return_from_subroutine(fax);
return;
}
/* We don't have a frame with data, Check for 200 ms silence (end of
* transmission) or a runaway remote fax that keeps sending.
*/
if ( softsignaled(V21CARRIEROK) ) {
if ( softsignaled(TIMER_SHUTUP) ) {
fax->fsm = response_received_tdcn;
}
}
one_shot_timer(TIMER_AUX,ZEROPOINTTWOSECONDS);
fax->fsm = response_received_sg1;
}
STATE(response_received_sg1)
{
if ( softsignaled(V21CARRIEROK ) ) {
fax->fsm = response_received_raf;
return;
}
if ( softsignaled(TIMER_AUX) ) {
softsignal_clr(RESPONSERECEIVED_RESULT);
return_from_subroutine(fax);
}
}
STATE(response_received_sg2)
{
if ( softfignaled(V21CARRIEROK) ) {
if ( softsignaled(TIMER_SHUTUP) )
fax->fsm = response_received_tdcn;
return;
}
one_shot_timer(TIMER_AUX,ZEROPOINTTWOSECONDS);
fax->fsm = response_received_sg2_helper;
}
STATE(response_received_sg2_helper)
{
if ( softsignaled(V21CARRIEROK) ) {
fax->fsm = response_received_sg2;
return;
}
if ( softsignaled(TIMER_AUX) ) {
softsignal_clr(RESPONSERECEIVED_RESULT);
return_from_subroutine(fax);
}
}
STATE(response_received_tdcn)
{
/* Transmit a disconnect line command */
fax->fsm = response_received_disconnect;
}
STATE(response_received_disconnect)
{
softsignal(ACTION_HANGUP);
return_from_subroutine(fax);
}
/****************************************************************************
*
* Command Received, FIGURE 5-2t/T.30
*
* This state-machine subroutine checks if a valid command has
* arrived within the time-limits. If an illegal situation
* occours, like the remote transmitting more than it is allowed to,
* a hangup is performed.
*
* By doing 'call_subroutine' on the state 'command_received',
* the answer will be available in COMMANDRECEIVED_RESULT .
*/
DEFSTATE(command_received_flag)
DEFSTATE(command_received_raf)
DEFSTATE(command_received_sg1)
DEFSTATE(command_received_sg2)
DEFSTATE(command_received_sg2_helper)
DEFSTATE(command_received_disconnect)
STATE (command_received){
fax->fsm = command_received_flag;
}
STATE (command_received_flag)
{
if ( softsignaled_clr(HDLCFLAGDETECTED) ) {
one_shot_timer(TIMER_T2,SIXSECONDS); ?????
fax->fsm = command_received_RAF;
}
else {
softsignal_clr(COMMANDRECEIVED_RESULT);
return_from_subroutine(fax);
}
}
STATE (command_received_raf) {
if ( softsignaled_clr(HDLCFRAMERECEIVED) ) {
fax_decode_controlmsg(fax);
if ( fax->FCSerror) {
one_shot_timer(TIMER_SHUTUP,THREESECONDS);
one_shot_timer(TIMER_AUX,ZEROPOINTTWOSECONDS);
fax->fsm = command_received_sg2;
return;
}
if ( fax->ctrlmsg == MSG_DCN ) {
fax->fsm = command_received_disconnect;
return;
}
if ( process_optional_command(fax) ) {
fax->fsm = command_received_flag;
return;
}
softsignal(COMMANDRECEIVED_RESULT);
return_from_subroutine(fax);
return;
}
else {
one_shot_timer(TIMER_SHUTUP,THREESECONDS);
one_shot_timer(TIMER_AUX,ZEROPOINTTWOSECONDS);
fax->fsm = command_received_sg1;
}
}
/* We don't have a frame with data, Check for 200 ms silence (end of
* transmission) or a runaway remote fax that keeps sending.
*/
STATE(command_received_sg1) {
if ( softsignaled(V21CARRIEROK) ) {
if ( softsignaled(TIMER_AUX) ) { // 0.2 seconds
softsignal_clr(COMMANDRECEIVED_RESULT);
return_from_subroutine(fax);
}
}
else
if ( softsignaled (TIMER_SHUTUP)) {
softsignal_clr(COMMANDRECEIVED_RESULT);
return_from_subroutine(fax);
}
else {
fax->fsm = command_received_raf;
}
}
}
STATE(command_received_sg2) {
if ( softsignaled(V21CARRIEROK) ) {
if ( softsignaled(TIMER_AUX) ) { // 0.2 seconds
if (CRP option)
Response CRP;
fax->fsm = command_received_raf;
}
}
else
if ( softsignaled (TIMER_SHUTUP)) {
fax->fsm = command_received_disconnect;
}
}
}
STATE(command_received_disconnect)
{
softsignal(ACTION_HANGUP);
return_from_subroutine(fax);
}
#endif