www.pudn.com > commutil.zip > FAX.CPP
// ******************************************************************** //
// //
// FAX.CPP //
// Copyright (c) 1993, Michael Holmes and Bob Flanders //
// C++ Communication Utilities //
// //
// Chapter 7: Facsimile Reception and Transmission //
// Last changed in chapter 7 //
// //
// This file contains the functions to implement the Fax class. //
// This class contains all of the support for receiving and //
// transmitting facsimilies. //
// //
// ******************************************************************** //
#define FAX_CLASS1 "AT +FCLASS=1\r" // enter FAX class 1 mode
#define FAX_TX_HDLC "AT +FTH=3\r" // enter HDLC transmit mode
#define FAX_RX_HDLC "AT +FRH=3\r" // enter HDLC recieve mode
#define FAX_TX_DATA "AT +FTM=%d\r" // enter FAX transmit mode
#define FAX_RX_DATA "AT +FRM=%d\r" // enter FAX receive mode
#define FAX_TX_SPD "AT +FTM=?\r" // get FAX transmit speeds
#define FAX_RX_SPD "AT +FRM=?\r" // get FAX receive speeds
#define FAX_SILENT "AT +FRS=8\r" // 80 ms of silence
#define FAX_SILENT1 "AT +FRS=20\r" // 200 ms of silence
#define FAX_CLASS1 "AT +FCLASS=1\r" // enter FAX class 1 mode
#define FAX_MODEM "AT +FCLASS=0\r" // return to non-FAX mode
#define FAX_ANSWER "AT A\r" // answer an incoming call
#define FAX_HANGUP "AT H\r" // disconnect from line
#define FAX_DIAL "AT DT" // dial command
#define FAX_OK "\r\nOK\r\n" // OK response
#define FAX_ERR "\r\nERROR\r\n" // ERROR response
#define FAX_NO_CARR "\r\nNO CARRIER\r\n" // NO CARRIER response
#define FAX_CONN "\r\nCONNECT\r\n" // CONNECT response
#define FAX_RING "\r\nRING\r\n" // RING message
#define FAX_SETMDM "AT Q0 V1 E0\r" // set modem parameters
#define FAX_RSTMDM "AT Z\r" // reset modem
#define FAX_ADDR 0xff // value for address byte
#define FAX_CTL 0xC0 // value for control field
#define FAX_CTL_FF 0xC8 // control field + final frame
#define FAX_FCF_DIS 0x01 // digital ID signal
#define FAX_FCF_CSI 0x02 // called subscriber ID
#define FAX_FCF_NSF 0x04 // non-standard facilities
#define FAX_FCF_DCS 0xC1 // digital command signal
#define FAX_FCF_TSI 0xC2 // transmitting subscriber ID
#define FAX_FCF_CFR 0x21 // confirmation to receive
#define FAX_FCF_FTT 0x22 // failure to train
#define FAX_FCF_EOM 0xF1 // end of message
#define FAX_FCF_MPS 0xF2 // multipage signal
#define FAX_FCF_EOP 0xF4 // end of procedure
#define FAX_FCF_MCF 0x31 // message confirmation
#define FAX_FCF_DCN 0xDF // disconnect
#define FINAL 8 // final frame
#define NON_FINAL 0 // non-final frame
#define TRANSMIT 0 // connect in send mode
#define RECEIVE 1 // connect in receive mode
#define FAX_T9600 0x80 // 9600 transmit
#define FAX_T7200 0x40 // 7200 transmit
#define FAX_T4800 0x20 // 4800 transmit
#define FAX_T2400 0x10 // 2400 transmit
#define FAX_R9600 0x08 // 9600 receive
#define FAX_R7200 0x04 // 7200 receive
#define FAX_R4800 0x02 // 4800 receive
#define FAX_R2400 0x01 // 2400 receive
/* ******************************************************************** *
*
* Fax specific variables, structs, etc.
*
* ******************************************************************** */
char dis_msg[] = // DIS frame data
{ 0, // first byte:
// not G1, G2
0x70, // second byte:
// T.4 operation
// 9600/7200/4800/2400 bps
// 3.85 lines/mm
// one-dimensional coding
0x02 // third byte:
}; // 1728 pels/215mm
// A4 paper only
// 40ms receive time
char dcs_msg[] = // DCS frame data
{ 0, // first byte:
// not G1, G2
0x40, // second byte:
// T.4 operation
// speed to be set
// 3.85 lines/mm
// one-dimensional coding
0x02 // third byte:
}; // 1728 pels/215mm
// A4 paper only
// 40ms per line
struct HDLC_msg // format of HDLC message
{
UCHAR addr; // address byte (always 0xff)
UCHAR ctl_fld; // control field
UCHAR fax_ctl_fld; // FAX control field (FCF)
UCHAR data[253]; // optional data
int len; // received frame length
};
struct FxStat // Fax status structure
{
void *f_parm; // Fax init parameter
char f_msg[50]; // message string
char *f_ptr; // pointer to message
};
struct FxHdr // Fax file header structure
{
char ff_type[3]; // file type (G3)
char ff_dcs[16]; // DCS for transfer
char ff_id[21]; // original station ID
char ff_reserved[88]; // reserved space
};
/* ******************************************************************** *
*
* Fax class definition
*
* ******************************************************************** */
class Fax : Protocol
{
public:
Fax (Comm *, // Fax instance contstructor
char *, // Comm instance, station ID
int (*)(int, // status routine
struct FxStat *),
void *); // FxStat parameter
~Fax(void); // destructor
void Send(char *, char *), // send a fax
Receive(char *); // receive a fax
private:
Comm *cp; // Comm instance
int Send_CSI(void), // send optional CSI
HDLC_Mode(int), // set HDLC tx|rx mode
Data_Mode(int, int), // set data tx|rx mode
Get_Speeds(void), // get tx and rx speeds
Get_Line(char *, int), // get a CR-terminate line
Get_Char(char *, int), // get a character
Rcv_Hmsg(void), // receive an HDLC frame
Send_Our_ID(UCHAR), // send our ID to remote
Send_Hmsg(UCHAR,UCHAR,int), // send an HDLC frame
Send_TSI(void), // send TSI frame to remote
Send_DIS(void), // send DIS frame to remote
Send_DCS(void), // send DCS frame to remote
Init_Modem(void), // initialize modem
(*stat)(int, // status routine pointer
struct FxStat *),
connected; // HDLC link active
UINT oldparms, // old communications parameters
pbi; // page buffer index
long oldspeed; // old commuincations speed
void Reverse_Bytes(UCHAR *, int),// reverse bits in char array
Display_ID(char *, UCHAR *),// display remote station ID
Display_Msg(int), // display a fax_msgs message
Reset_Modem(void); // reset modem and comm parms
UCHAR speeds, // speeds supported
// 1... .... 9600 transmit
// .1.. .... 7200
// ..1. .... 4800
// ...1 .... 2400
// .... 1... 9600 receive
// .... .1.. 7200
// .... ..1. 4800
// .... ...1 2400
*pagebuf, // page buffer area
Reverse_Byte(UCHAR value); // reverse bits in a byte
char *station; // our station id
struct FxStat fs; // fax status structure
struct FxHdr fh; // fax file header structure
union { // accesss HDLC msg as chars
struct HDLC_msg hmsg; // HDLC message area
UCHAR cmsg[256]; // .. same as char array
};
};
/* ********************************************************************
*
* Fax -- Fax instance constuctor
*
* ********************************************************************/
Fax::Fax(Comm *ci, // comm instance
char *sid, // .. our station ID (telno)
int (*sr)(int, struct FxStat *), // .. status routine
void *w) // .. fax status parameter
{
cp = ci; // save comm instance pointer
stat = sr; // .. and status routine
station = sid; // .. and station ID
connected = 0; // no HDLC connection active
fs.f_parm = w; // save fax status parameter
strcpy(fh.ff_type, "G3"); // preset the file type
memset(fh.ff_reserved, ' ', // .. and clear reserved area
sizeof(fh.ff_reserved));
pagebuf = new UCHAR[1024]; // allocate page buffer
}
/* ********************************************************************
*
* ~Fax -- destructor
*
* ********************************************************************/
Fax::~Fax(void) // Fax destructor
{
delete pagebuf; // free pagebuf memory
}
/* ******************************************************************** *
*
* Init_Modem -- initialize the fax modem
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful; OK response from modem
* 1 = ERROR response from modem
*
* ******************************************************************** */
int Fax::Init_Modem(void)
{
int rc, // return code
i; // work counter for loops
char buf[80], // work buffer for speeds
*c, // work pointer
*t; // token work pointer
cp->Write("\r"); // send a to modem
Purge(cp, 2); // .. kill any receive messages
oldspeed = cp->GetSpeed(); // get the old link speed
oldparms = cp->Set8n(); // .. and old comm parameters
// .. while setting 8,n,1
cp->SetBPS(19200L); // set new comm speed
cp->SetBPS(9600L); // set new comm speed
for (rc = i = 0; (rc == 0) && i++ < 3;) // try three times
{
cp->Write(FAX_SETMDM); // set the modem parameters
rc = wait_for(FAX_OK, FAX_ERR, 2); // wait for OK response
}
if (rc < 0) // q. user cancellation?
return(rc - 1); // a. yes .. return error
speeds = 0; // reset speeds supported
cp->Write(FAX_CLASS1); // place modem in CLASS 1 mode
rc = wait_for(FAX_OK, FAX_ERR, 5) - 1; // wait for modem response
if (rc) // q. modem respond ok?
return(rc); // a. no .. tell the caller
cp->Write(FAX_TX_SPD); // retrieve transmit speeds
rc = Get_Line(buf, 5); // kill the first CR
rc = Get_Line(buf, 5); // get the line from the mode
if (rc) // q. any error?
return(rc); // a. yes.. return w/error
Purge(cp, 1); // kill additional characters
c = (*buf == '\n') ? buf+1 : buf; // select start point
while ((t = strtok(c, ",")) != 0) // while there are tokens
{
c = NULL; // continue searching buf
i = atoi(t); // get the token's value
switch(i) // for various values..
{
case 24: // 2400 found
speeds |= FAX_T2400; // .. set set the speed flag
break;
case 48: // 4800 found
speeds |= FAX_T4800; // .. set set the speed flag
break;
case 72: // 7200 found
speeds |= FAX_T7200; // .. set set the speed flag
break;
case 96: // 9600 found
speeds |= FAX_T9600; // .. set set the speed flag
break;
}
}
cp->Write(FAX_RX_SPD); // retrieve receive speeds
rc = Get_Line(buf, 5); // kill the first CR
rc = Get_Line(buf, 5); // get the line from the modem
if (rc) // q. any error?
return(rc); // a. yes.. return w/error
Purge(cp, 1); // kill additional characters
c = (*buf == '\n') ? buf+1 : buf; // select start point
while ((t = strtok(c, ",")) != 0) // while there are tokens
{
c = NULL; // continue searching buf
i = atoi(t); // get the token's value
switch(i) // for various values..
{
case 24: // 2400 found
speeds |= FAX_R2400; // .. set set the speed flag
break;
case 48: // 4800 found
speeds |= FAX_R4800; // .. set set the speed flag
break;
case 72: // 7200 found
speeds |= FAX_R7200; // .. set set the speed flag
break;
case 96: // 9600 found
speeds |= FAX_R9600; // .. set set the speed flag
break;
}
}
return(0); // return ok
}
/* ******************************************************************** *
*
* Reset_Modem -- reset modem and communications parameters
*
* ******************************************************************** */
void Fax::Reset_Modem(void)
{
int rc; // return code
cp->Write("\r"); // send a to modem
cp->DTR(); // lower DTR
Purge(cp, 1); // .. kill any receive messages
cp->SetBPS(oldspeed); // reset the comm speed
cp->SetLine(oldparms); // .. and the comm parms
cp->Write(FAX_RSTMDM); // try to reset the modem
switch (rc = wait_for(FAX_OK, FAX_ERR, 2)) // based on response
{
case -1: // user pressed escape
return; // .. return without message
case 0: // time out
case 1: // received OK
case 2: // received ERROR
Display_Msg(37+rc); // display appropriate message
}
}
/* ******************************************************************** *
*
* Send_Our_ID -- send our ID to the other station
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful; CONNECT response from modem
* 1 = NO CARRIER response from modem
*
* ******************************************************************** */
int Fax::Send_Our_ID(UCHAR ctl_byte) // FAX control field
{
int i, j; // work variables
UCHAR *ch; // work pointer
ch = hmsg.data; // get the data address
for (i = 0; i < 20; ch[i++] = 0x04); // set to 'reversed' blanks
if (station != NULL) // q. station ID set?
{ // a. yes ..
i = strlen(station); // get station ID length
i = i > 20 ? 20 : i; // max ID length = 20
for (j = 0; i--;) // for each char in station ID
{
if (strchr("0123456789+ ", // q. valid ID character?
station[i]) != 0)
ch[j++] = // a. yes.. reverse & copy it
Reverse_Byte(station[i]);
}
}
return(Send_Hmsg(NON_FINAL, ctl_byte, 20)); // send the HDLC message
}
/* ******************************************************************** *
*
* Display_ID -- display remote station ID
*
* ******************************************************************** */
void Fax::Display_ID(char *pf, // prefix
UCHAR *id) // id string
{
char cw[21]; // work area
int i, j; // work variables
for (i = 20; i--;) // backscan the ID
if (id[i] != ' ') // q. blank?
break; // a. no .. get out now
if (i++ == -1) // q. ID found?
return; // a. no .. leave now
for (j = 0; i--; cw[j++] = id[i]); // copy the ID in reverse
cw[j] = 0; // .. and end the string
sprintf(fs.f_msg, "%s %s\r\n", pf, cw); // put message in buffer
(stat)(1, &fs); // .. and display it
}
/* ******************************************************************** *
*
* Rcv_Hmsg -- receive an HDLC frame
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successfully received
* 1 = NO CARRIER or error
*
* ******************************************************************** */
int Fax::Rcv_Hmsg(void)
{
int rc, // return code
loop = 1, // loop until finished
dleflag = FALSE; // dle not seen yet
char *nxtchar, // next receive address
wc; // work for char reads
long timer; // workspace for timer
hmsg.len = 0; // initialize length
nxtchar = (char *) cmsg; // .. and next receive pointer
if ((rc = HDLC_Mode(RECEIVE)) != 0) // q. connect ok?
return(rc); // a. no .. leave w/error
timer = SECS(5); // start 5 second timer
while(loop) // enter a loop ..
switch(cp->Read(nxtchar, &wc, &wc)) // attempting to read bytes
{
case -1: // no character available
if (TimeOut(&timer)) // q. timeout?
return(-1); // a. yes.. tell the caller
if ((stat)(0, &fs)) // q. user press escape?
return(-2); // a. yes.. tell the caller
continue; // else .. continue loop
case 0: // character received
hmsg.len++; // increment received count
if (dleflag) // q. previous char DLE?
{ // a. yes ..
dleflag = FALSE; // .. reset the flag
if (*nxtchar == ETX) // q. DLE ETX sequence?
loop = 0; // a. yes .. exit the loop
}
else if (*nxtchar == DLE) // q. char a DLE?
dleflag = TRUE; // a. yes .. show true
nxtchar++; // point at next character
break; // end this case
default: // lost characters
return(1); // .. show receive unsuccessful
}
rc = wait_for(FAX_OK, FAX_ERR, 5) - 1; // OK should follow message
Reverse_Bytes(cmsg, hmsg.len - 2); // reverse the bits
return(rc); // return to caller
}
/* ******************************************************************** *
*
* Send_Hmsg -- send an HDLC frame
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful; CONNECT response from modem
* 1 = NO CARRIER response from modem
*
* ******************************************************************** */
int Fax::Send_Hmsg(UCHAR finalflg, // final flag
UCHAR ctl_byte, // fax control field
int len) // length of data bytes
{
int rc; // return code
hmsg.addr = FAX_ADDR; // set the address field
hmsg.ctl_fld = FAX_CTL | finalflg; // .. and the control field
hmsg.fax_ctl_fld = ctl_byte; // .. and fax control field
hmsg.data[len] = DLE; // .. add DLE
hmsg.data[len+1] = ETX; // .. and ETX
Reverse_Bytes(cmsg, len+3); // reverse the bits
if (NOT connected) // q. connected already?
{ // a. no .. connect now
rc = HDLC_Mode(TRANSMIT); // attempt the connection
if (rc) // q. connect ok?
return(rc); // a. no .. leave w/error
connected = TRUE; // else .. show connected.
}
cp->Write((char *) cmsg, len+5); // send to remote
if (finalflg) // q. final flag on?
{ // a. yes ..
rc = wait_for(FAX_OK, FAX_ERR, 5) - 1; // .. wait for OK
connected = FALSE; // .. and we're not connected
}
else
rc = wait_for(FAX_CONN, // else .. wait for CONNECT
FAX_NO_CARR, 60) - 1; // .. or NO CARRIER
return(rc); // return to caller
}
/* ******************************************************************** *
*
* Send_CSI -- Send the called subscriber ID signal to caller
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful; CONNECT response from modem
* 1 = NO CARRIER response from modem
*
* ******************************************************************** */
int Fax::Send_CSI(void)
{
return(Send_Our_ID(FAX_FCF_CSI)); // send CSI frame to caller
}
/* ******************************************************************** *
*
* Send_TSI -- Send the transmitting subscriber ID signal
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful
* 1 = unsuccessful
*
* ******************************************************************** */
int Fax::Send_TSI(void)
{
return(Send_Our_ID(FAX_FCF_TSI)); // send TSI frame
}
/* ******************************************************************** *
*
* Send_DIS -- Send the digital ID signal to caller
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful
* 1 = unsuccesful
*
* ******************************************************************** */
int Fax::Send_DIS(void)
{
int i; // work variable
for (i = 3; i--; hmsg.data[i] = dis_msg[i]);// copy the DIS to hmsg.data
return(Send_Hmsg(FINAL, FAX_FCF_DIS, 3)); // send the DIS
}
/* ******************************************************************** *
*
* Send_DCS -- Send the digital control signal to called station
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful
* 1 = unsuccesful
*
* ******************************************************************** */
int Fax::Send_DCS(void)
{
int i; // work variable
for (i = 3; i--; hmsg.data[i] = dcs_msg[i]);// copy the DCS to hmsg.data
return(Send_Hmsg(FINAL, FAX_FCF_DCS, 3)); // send the DCS
}
/* ******************************************************************** *
*
* HDLC_Mode -- Enter HDLC mode (transmit or receive)
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful; CONNECT response from modem
* 1 = ERROR response from modem
*
* ******************************************************************** */
int Fax::HDLC_Mode(int dir) // setup for HDLC tx or rx
// 0 = transmit, 1 = receive
{
int rc; // return code
if ((dir == TRANSMIT) && NOT connected) // q. we transmitting?
{ // a. yes ..
cp->Write(FAX_SILENT); // .. request 80ms silence
rc = wait_for(FAX_OK, // wait for response
FAX_ERR, 5) - 1;
if (rc) // q. any problems?
return(rc); // a. yes .. return the error
}
else if (connected) // q. connected already?
{ // a. yes ..
connected = FALSE; // .. reset connect status
return(0); // .. and return ok
}
cp->Write(dir ? FAX_RX_HDLC : FAX_TX_HDLC); // start requested HDLC mode
rc = wait_for(FAX_CONN, FAX_NO_CARR, 60)-1; // see if we connect
return(rc); // return to caller
}
/* ******************************************************************** *
*
* Data_Mode() -- Enter Fax Data mode (transmit or receive)
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful; CONNECT response from modem
* 1 = NO CARRIER response from modem
*
* ******************************************************************** */
int Fax::Data_Mode(int dir, // 0 = transmit, 1 = receive
int speed) // speed for transfer
{
int rc; // return code
char msg[20]; // work area for message
if ((dir == TRANSMIT) && NOT connected) // q. we transmitting?
{ // a. yes ..
cp->Write(FAX_SILENT); // .. request 80ms silence
rc = wait_for(FAX_OK, // wait for response
FAX_ERR, 5) - 1;
if (rc) // q. any problems?
return(rc); // a. yes .. return the error
}
sprintf(msg, // in message area ..
dir ? FAX_RX_DATA : FAX_TX_DATA, // .. select direction of xfer
speed); // .. and build message
cp->Write(msg); // write to modem
rc = wait_for(FAX_CONN, FAX_NO_CARR, 60)-1; // see if we connect
return(rc); // return to caller
}
/* ******************************************************************** *
*
* Get_Line -- retrieve a CR-terminated line of information
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful
* 1 = data overrun
*
* ******************************************************************** */
int Fax::Get_Line(char *buf, // buffer to contain info
int secs) // length of timeout
{
int loop = TRUE; // loop condition
char wc; // work for msr, lsr
long timer; // timer value
timer = SECS(secs); // initialize timer
while(loop) // for as long as necessary ..
{
switch(cp->Read(buf, &wc, &wc)) // attempting to read a byte
{
case -1: // no character available
if (TimeOut(&timer)) // q. timeout?
return(-1); // a. yes.. tell the caller
if ((stat)(0, &fs)) // q. user press escape?
return(-2); // a. yes.. tell the caller
continue; // else .. continue loop
case 0: // character received
if (*buf == '\r') // q. character a CR?
{ // a. yes ..
*buf = 0; // .. null it out
loop = FALSE; // .. and end the loop
}
else // else ..
buf++; // .. point to next char position
break; // end this case
default: // lost characters
return(1); // .. show receive unsuccessful
}
}
return(0); // show we finished ok
}
/* ******************************************************************** *
*
* Get_Char -- retrieve a single character
*
* returns: -2 = User pressed ESC
* -1 - Timeout
* 0 = successful
* 1 = data overrun
*
* ******************************************************************** */
int Fax::Get_Char(char *c, // character to retrieve
int secs) // length of timeout
{
int loop = TRUE; // loop condition
char wc; // work for msr, lsr
long timer; // timer value
timer = SECS(secs); // initialize timer
while(loop) // for as long as necessary ..
{
switch(cp->Read(c, &wc, &wc)) // attempting to read a byte
{
case -1: // no character available
if (TimeOut(&timer)) // q. timeout?
return(-1); // a. yes.. tell the caller
if ((stat)(0, &fs)) // q. user press escape?
return(-2); // a. yes.. tell the caller
continue; // else .. continue loop
case 0: // character received
loop = FALSE; // .. end the loop
break;
default: // lost characters
return(1); // .. show receive unsuccessful
}
}
return(0); // show we finished ok
}
/* ******************************************************************** *
*
* Display_Msg -- display a message without parameters
*
* ******************************************************************** */
void Fax::Display_Msg(int msgno)
{
fs.f_ptr = fax_msgs[msgno]; // set the address
(stat)(2, &fs); // display the message
}
/* ******************************************************************** *
*
* Reverse_Byte() -- Reverse the bits in a byte
*
* ******************************************************************** */
UCHAR Fax::Reverse_Byte(UCHAR value) // byte to reverse
{
__asm mov cx, 8 // cx = bits to shift
__asm mov al, value // al = starting value
top_loop: // top of reverse loop
__asm shl ah, 1 // shift ah up by one
__asm shr al, 1 // shift out a bit
__asm adc ah, 0 // .. add carry into ah
__asm loop top_loop // .. until all bits moved
__asm mov value, ah // save reversed value
return(value); // .. and return it
}
/* ******************************************************************** *
*
* Reverse_Bytes -- reverse the bits in the bytes of a string
*
* ******************************************************************** */
void Fax::Reverse_Bytes(UCHAR *str, // string to reverse
int len) // length of string
{
while(len--) // while there are bytes..
{
*str = Reverse_Byte(*str); // .. reverse the bits
str++; // .. next byte
}
}
/* ********************************************************************
*
* Receive -- Receive a Facsimile transmission
*
* ******************************************************************** */
void Fax::Receive(char *faxname)
{
int rc = 0, // return code
speed, // link speed
fileopen = FALSE, // receive file not open
dcs_received = FALSE, // DCS not yet received
i, // work variable
error = 0, // error number
dleflag = FALSE, // DLE sequence found flag
loop = TRUE; // loop until complete
FILE *faxfile; // fax file
char c; // work character
ULONG pagelen, // length of received page
reclen, // offset of page record length
et, // elapsed time
st; // work for timer
enum RcvStates // FAX receive states
{
PhaseA, // phase A - make connection
AwaitCall, // wait for a call
AnswerCall, // answer incoming call
SendCSI, // send our CSI frame, if needed
PhaseB, // negotiate session parameters
GetFrames, // get HDLC frames
SetSpeed, // set the link speed
PrepTCF, // prepare to receive TCF
FailConnTCF, // fail the TCF connect
RcvTCF, // receive/test TCF
RcvTCF1, // get rest of TCF
FailTCF, // fail the TCF
Confirm, // confirm training check frame
PhaseC, // receive the FAX data
RcvPage, // receive 1 page of data
EndPage, // page complete
PhaseD, // post message procedure
PhaseE, // call is done .. disconnect
RcvError, // error during receive
UserCan, // user cancelled transmission
ExitFax // exit FAX receive
} state;
strcpy(fh.ff_id, " "); // blank out the ID string
state = PhaseA; // start in Phase A
if ((faxfile = fopen(faxname, "wb+")) // q. receive file open ok?
== NULL)
{ // a. no ..
error = 9; // .. set the error code
state = RcvError; // .. and declare an error
}
fileopen = TRUE; // show the file is open
while(loop) // top of state machine
switch(state) // perform next state
{
case PhaseA: // phase A - make connection
Display_Msg(1); // update the status
if ((rc = Init_Modem()) != 0) // q. modem init ok?
{
error = 1; // a. no .. exit now
state = RcvError; // .. declare the error
continue;
}
state = AwaitCall; // else .. wait for a ring
Display_Msg(2); // update the status
break; // end this state
case AwaitCall: // wait for a call
rc = wait_for(FAX_RING, // wait for a RING from modem
FAX_RING, 10);
if (rc > 0) // q. RING arrive?
{ // a. yes ..
state = AnswerCall; // .. go answer the call
continue;
}
if (rc == -1) // q. user press ESC?
state = UserCan; // a. yes .. exit now
break; // else .. continue waiting
case AnswerCall: // answer incoming call
Display_Msg(3); // update the status
cp->Write(FAX_ANSWER); // send the answer command
rc = wait_for(FAX_CONN, // wait for a carrier
FAX_NO_CARR, 60);
if (--rc == 0) // q. connect?
{
state = SendCSI; // a. yes .. send our CSI
connected = TRUE; // .. show we're connected
}
else // else ..
{
state = RcvError; // .. process general error
error = 2; // .. show the error type
}
break; // .. next state
case SendCSI: // send our CSI frame
Display_Msg(4); // update the status
rc = Send_CSI(); // .. send our ID
if (rc) // q. error?
state = RcvError; // a. yes .. declare the error
else
state = PhaseB; // select next state
break;
case PhaseB: // negotiate session parameters
Display_Msg(5); //
state = ((rc = Send_DIS()) != 0) ? // q. DIS send go ok?
RcvError : // a. no .. declare error
GetFrames; // else .. get frames
break;
case GetFrames: // get HDLC frames
if ((rc = Rcv_Hmsg()) != 0) // q. any error getting a frame?
if (rc < 0) // a. yes .. timeout or ESC?
{ // a. yes ..
error = 3; // .. error receiving HDLC frame
state = RcvError; // .. leave with error.
continue; // .. continue process
}
// else // else .. NO CARRIER
// {
// cp->Write(FAX_SILENT1); // wait for 200ms silence
//
// rc = wait_for(FAX_OK, // wait for the OK response
// FAX_ERR,
// 10) - 1;
//
// state = SetSpeed; // .. and set the speed
// }
if (rc) // q. any error yet?
{ // a. yes ..
state = RcvError; // .. declare an error
continue;
}
switch(hmsg.fax_ctl_fld) // process based on message type
{
case FAX_FCF_TSI: // transmitting subscriber ID
Display_Msg(6); // update the status
Reverse_Bytes(hmsg.data, // .. reset the bytes to normal
20);
hmsg.data[20] = 0; // .. end string in zero
strcpy((char *) fh.ff_id, // .. copy it to file header
(char *) hmsg.data);
Display_ID("Called by:", // .. and display the remote ID
hmsg.data);
break;
case FAX_FCF_DCN: // disconnect
Display_Msg(16); // update the status
state = PhaseE; // .. and hang up
continue;
case FAX_FCF_DCS: // digital command signal
Display_Msg(7); // update the status
memcpy(fh.ff_dcs, hmsg.data,// .. move the DCS to the header
hmsg.len - 7);
dcs_received = 1; // show we got DCS
break;
}
if (hmsg.ctl_fld == FAX_CTL_FF) // q. final frame?
{ // a. yes ..
state = SetSpeed; // .. set link speed
fseek(faxfile, 0L, SEEK_SET); // go to start of file
fwrite(&fh, 128, 1, faxfile); // .. and write the header
}
break;
case SetSpeed: // set the link speed
if (dcs_received == 0) // q. dcs received?
{ // a. no ..
error = 4; // .. show the error
state = RcvError; // .. and go to error state
continue; // .. continue with next state
}
switch (fh.ff_dcs[1] & 0x30) // get modulation value
{
case 0x00: // q. speed 2400?
speed = 24; // a. yes . set speed
i = 44; // .. and message
break;
case 0x10: // q. speed 4800?
speed = 48; // a. yes .. set speed
i = 43; // .. and message
break;
case 0x30: // q. speed 7200?
speed = 72; // a. yes .. set speed
i = 42; // .. and message
break;
case 0x20: // q. speed 9600?
speed = 96; // a. yes .. set speed
i = 41; // .. and message
break;
}
Display_Msg(i); // display speed message
state = PrepTCF; // next, prepare to test link
break;
case PrepTCF: // prepare to receive TCF
rc = Data_Mode(RECEIVE, speed); // start the receive
if (rc == 1) // q. did connection fail?
state = FailConnTCF; // a. yes.. fail TCF connect
else if (rc != 0) // q. other failure?
state = RcvError; // a. yes .. declare an error
else
{
state = RcvTCF; // receive training check frame
Display_Msg(9); // update the status
}
break;
case FailConnTCF: // fail the TCF connect
rc = Send_Hmsg(FINAL, // send failure to train
FAX_FCF_FTT, 0);
if (rc) // q. retrain send fail?
state = RcvError; // a. yes .. declare an error
else
state = GetFrames; // get a new set of frames
break;
case RcvTCF: // receive/test TCF
rc = Get_Char(&c, 5); // wait a max of 5 secs for data
if (rc != 0) // q. any error
{ // a. yes ..
state = RcvError; // .. declare an error
continue; // .. and continue processing
}
if ((UCHAR) c == 0) // q. zero byte?
state = RcvTCF1; // a. yes .. receive the rest
break;
case RcvTCF1: // get rest of TCF
st = get_time(); // get time of day in ticks
for(;;) // get characters
{
rc = Get_Char(&c, 1); // get a character
if (rc != 0) // q. any error?
{ // a. yes ..
Purge(cp, 1); // .. purge the comm line
error = 7; // .. TCF too short
state = RcvError; // .. declare an error
break; // .. and exit loop
}
if (c != 0) // q. end of sequence?
break; // a. yes .. exit loop
}
if (rc != 0) // q. was there an error?
continue; // a. yes .. continue process
et = elapsed_time(st); // calculate elapsed time
if ((et < 24) || (et > 31)) // q. 1.5 seconds +/- 10%?
{ // a. yes ..
Purge(cp, 1); // .. purge the comm line
state = FailTCF; // .. declare train failure
continue; // .. and continue loop
}
rc = wait_for(FAX_NO_CARR, // wait for no carrier
FAX_ERR, 5) - 1; // .. or other information
if (rc != 0) // q. NO CARRIER received?
{ // a. no ..
state = RcvError; // .. declare receieve error
continue; // .. and continue loop
}
state = Confirm; // else .. confirm TCF
break; // .. and continue processing
case FailTCF: // fail the TCF
Display_Msg(19); // update the status
rc = Send_Hmsg(FINAL, // send failure to train
FAX_FCF_FTT, 0);
if (rc) // q. retrain send fail?
state = RcvError; // .. declare an error
else
state = GetFrames; // get a new set of frames
break;
case Confirm: // confirm training check frame
Display_Msg(10); // update the status
rc = Send_Hmsg(FINAL, // send the confirmation
FAX_FCF_CFR, 0);
if (rc != 0) // q. confirmation go ok?
{
state = RcvError; // a. no .. show the error
continue; // .. continue processing
}
state = PhaseC; // start receiving FAX
break;
case PhaseC: // receive the FAX data
rc = Data_Mode(RECEIVE, speed); // start receiving the FAX
if (rc != 0) // q. receive start ok?
{
state = RcvError; // a. no .. show the error
continue; // .. continue processing
}
Display_Msg(11); // update the status
state = RcvPage; // receive a page of data
dleflag = FALSE; // set no DLE seen yet
pbi = 0; // reset the page buffer index
pagelen = 0; // .. and no page data yet
fseek(faxfile, 0L, SEEK_END); // go to end of file
reclen = ftell(faxfile); // .. save the position
fwrite(&pagelen, 4, 1, faxfile); // .. write the page's length
break;
case RcvPage: // receive 1 page of data
rc = Get_Char(&c, 1); // get a character
if (rc != 0) // q. receive ok?
{ // a. no ..
state = RcvError; // .. declare an error
continue; // .. and continue processing
}
pagebuf[pbi++] = c; // save the character
pagelen++; // increment bytes in page
if (pbi == 1024) // q. buffer full?
{ // a. yes ..
fwrite(pagebuf, 1024, 1, // .. write out a page
faxfile);
pbi = 0; // reset page buffer index
}
if (dleflag) // q. previous char DLE?
{ // a. yes ..
dleflag = FALSE; // .. reset the flag
if (c == ETX) // q. DLE ETX sequence?
{ // a. yes ..
state = EndPage; // .. process end of page
Display_Msg(12); // update the status
}
}
else if (c == DLE) // q. char a DLE?
dleflag = TRUE; // a. yes .. show true
break; // continue processing
case EndPage: // Page complete
rc = wait_for(FAX_NO_CARR, // we should now have no carrier
FAX_ERR, 5) - 1;
if (pbi) // buffer contain data?
fwrite(pagebuf, pbi, 1, // a. yes.. write out a page
faxfile);
fseek(faxfile, reclen, SEEK_SET); // set postion to length record
fwrite(&pagelen, 4, 1, faxfile); // update the page's length
fseek(faxfile, 0L, SEEK_END); // .. return to end of file
if (rc != 0) // q. correct response?
{ // a. no ..
state = RcvError; // .. declare an error
continue;
}
state = PhaseD; // start post message procedure
break;
case PhaseD: // post message procedure
rc = Rcv_Hmsg(); // get the next message
if (rc != 0) // q. correct response?
{ // a. no ..
state = RcvError; // .. declare an error
continue;
}
i = hmsg.fax_ctl_fld; // save the response
rc = Send_Hmsg(FINAL, // send the final frame
FAX_FCF_MCF, 0); // .. message confirmation
if (rc != 0) // q. correct response?
{ // a. no ..
state = RcvError; // .. declare an error
continue;
}
switch(i) // based on the response..
{
case FAX_FCF_MPS: // multi-page signal
Display_Msg(13); // update the status
state = PhaseC; // .. receive next page
break;
case FAX_FCF_EOM: // end of message
Display_Msg(14); // update the status
state = PhaseB; // .. renegotiate & continue
break;
case FAX_FCF_EOP: // end of procedure
Display_Msg(15); // update the status
state = PhaseE; // .. disconnect now
break;
case FAX_FCF_DCN: // disconnect
Display_Msg(16); // update the status
state = PhaseE; // .. and hang up
continue;
default:
state = RcvError; // unknown response
break;
}
break; // continue processing
case PhaseE: // call is done .. disconnect
cp->Write(FAX_HANGUP); // hangup the modem
state = ExitFax; // .. and exit the fax receive
break;
case RcvError: // error during receive
Display_Msg(17); // update the status
if (rc == -2) // q. user cancellation?
{ // a. yes ..
state = UserCan; // .. show the reason
continue; // .. continue processing
}
else if (error) // q. error set?
{ // a. yes ..
fs.f_ptr = fax_errors[error]; // .. set the message pointer
(stat)(2, &fs); // .. and display it
}
else if (rc == -1) // q. timeout?
Display_Msg(20); // a. yes .. show the message
else if (rc == 1) // q. unexpected response?
Display_Msg(21); // a. yes .. tell the user
state = ExitFax; // .. set new state
break;
case UserCan: // user cancelled transmission
Display_Msg(18); // update the status
state = ExitFax; // .. set new state
break;
case ExitFax: // exit FAX receive
if (fileopen) // q. fax file open?
fclose(faxfile); // a. yes .. close the file
Reset_Modem(); // reset the modem
loop = FALSE; // .. end the loop
break; // .. and return to caller
}
}
/* ********************************************************************
*
* Send -- Send a Facsimile
*
* ******************************************************************** */
void Fax::Send(char *faxname, // fax file name
char *telno) // telephone number
{
int rc = 0, // return code
speed, // link speed
nextspeed, // next link speed
fileopen = FALSE, // receive file not open
dis_received = FALSE, // DIS not yet received
i, // work variable
error = 0, // error number
loop = TRUE; // loop until complete
FILE *faxfile; // fax file
char dis[16], // DIS received
csi[21], // CSI received
c, // work character
w; // work register
ULONG pagelen, // length of received page
st; // work for elapsed time
enum SndStates // FAX send states
{
PhaseA, // phase A - make connection
PhaseB, // get HDLC frames
SetSpeed, // set the link speed
SendTSI, // select speed, send ID & DCS
SendTCF, // send the TCF
GetConfTCF, // get TCF confirmation
PhaseC, // send the document
SendPage, // send a page of data
PhaseD, // post message process
AnotherPage, // another page to send
AllSent, // send complete
PhaseE, // disconnect
SndError, // error during send
UserCan, // user cancelled transmission
ExitFax // exit FAX receive
} state;
strcpy(fh.ff_id, " "); // blank out the ID string
state = PhaseA; // start in Phase A
if ((faxfile = fopen(faxname, "rb")) // q. send file open ok?
== NULL)
{ // a. no ..
error = 9; // .. set the error code
state = SndError; // .. and declare an error
}
fread(&fh, 128, 1, faxfile); // read the header
if (strcmp(fh.ff_type, "G3")) // q. G3 file?
{ // a. no ..
error = 13; // .. show invalid format
state = SndError; // .. and declare an error
}
fileopen = TRUE; // show the file is open
fread(&pagelen, 4, 1, faxfile); // read in the first page length
while(loop) // top of state machine
switch(state) // perform next state
{
case PhaseA: // phase A - make connection
Display_Msg(1); // update the status
if ((rc = Init_Modem()) != 0) // q. modem init ok?
{
error = 1; // a. no .. exit now
state = SndError; // .. declare the error
continue;
}
Display_Msg(22); // update the status
cp->Write(FAX_DIAL); // send the dial command
cp->Write(telno); // .. and the phone number
cp->Write('\r'); // .. and finish the command
rc = wait_for(FAX_CONN, // wait for a connection
FAX_NO_CARR, 120);
if (--rc) // q. any error
{ // a. yes ..
if (rc == 1) // q. connection fail?
error = 10; // a. yes .. show connect failed
state = SndError; // declare an error
continue; // .. go to next state
}
state = PhaseB; // get frames
connected = TRUE; // ... show we are connected
break;
case PhaseB: // get HDLC frames
if ((rc = Rcv_Hmsg()) != 0) // q. any error getting a frame?
if (rc < 0) // a. yes .. timeout or ESC?
{ // a. yes ..
error = 3; // .. error receiving HDLC frame
state = SndError; // .. leave with error.
continue; // .. continue process
}
else // else .. NO CARRIER
{
cp->Write(FAX_SILENT1); // wait for 200ms silence
rc = wait_for(FAX_OK, // wait for the OK response
FAX_ERR,
10) - 1;
state = SetSpeed; // .. and set the speed
}
if (rc) // q. any error yet?
{ // a. yes ..
state = SndError; // .. declare an error
continue;
}
switch(hmsg.fax_ctl_fld) // process based on message type
{
case FAX_FCF_CSI: // called subscriber ID
Display_Msg(23); // update the status
Reverse_Bytes(hmsg.data, // .. reset the bytes to normal
20);
hmsg.data[20] = 0; // .. end string in zero
strcpy(csi, // .. copy it to work area
(char *) hmsg.data);
Display_ID("Connected to:", // .. and display the remote ID
(UCHAR *) csi);
break;
case FAX_FCF_DIS: // digital information signal
Display_Msg(24); // update the status
memcpy(dis, hmsg.data, // .. move the DIS to work area
hmsg.len - 7);
dis_received = 1; // show we got DIS
break;
case FAX_FCF_NSF: // Non-standard facilities
Display_Msg(8); // update the status
break; // .. we don't process these
}
break;
case SetSpeed: // set the link speed
if (dis_received == 0) // q. dis received?
{ // a. no ..
error = 11; // .. show the error
state = SndError; // .. and go to error state
continue; // .. continue with next state
}
switch (dis[1] & 0x30) // select initial speed
{
case 0x30: // q. speed 7200?
case 0x20: // q. speed 9600?
nextspeed = 96; // a. yes .. set speed
break;
case 0x10: // q. speed 4800?
nextspeed = 48; // a. yes .. set speed
break;
case 0x00: // q. speed 2400?
nextspeed = 24; // a. yes . set speed
break;
}
state = SendTSI; // next, tell 'em about us
break;
case SendTSI: // select speed, send ID & DCS
dcs_msg[1] &= 0xcf; // set off speed bits
switch (speed = nextspeed) // based on selected speed
{
case 96: // q. speed = 9600?
dcs_msg[1] |= 0x20; // a. yes .. set speed
if ((dis_msg[1] | 0x30) // q. 7200 supported?
== 0x30)
nextspeed = 72; // a. yes .. next speed = 7200
else
nextspeed = 48; // else .. next speed = 4800
i = 41; // select speed message
break;
case 72: // q. speed = 7200?
dcs_msg[1] |= 0x30; // a. yes .. set speed
nextspeed = 48; // .. next speed = 4800
i = 42; // select speed message
break;
case 48: // q. speed = 4800?
dcs_msg[1] |= 0x10; // a. yes .. set speed
nextspeed = 24; // .. next speed = 2400
i = 43; // select speed message
break;
case 24: // q. speed = 2400?
error = 12; // a. yes.. can't connect
state = SndError; // .. declare the error
i = 44; // select speed message
continue; // .. continue state machine
}
Display_Msg(25); // update the status
rc = Send_TSI(); // .. send our ID
if (rc) // q. error?
{
state = SndError; // a. yes ..
continue; // .. declare the error
}
Display_Msg(26); // update status again
state = ((rc = Send_DCS()) != 0) ? // q. DCS send go ok?
SndError : // a. no .. declare error
SendTCF ; // else .. send the check frame
break;
case SendTCF: // send the TCF
Display_Msg(i); // show speed
Display_Msg(27); // update the status
rc = Data_Mode(TRANSMIT, speed); // go to data mode
if (rc) // q. any error?
{ // a. yes ..
state = (rc != 1) ? SndError : // .. select general error
GetConfTCF; // .. or just connection failure
continue;
}
cp->Write(0); // Send the TCF
for (st = get_time(); // .. let the modem send zeroes
elapsed_time(st) < 28;); // .. for 1.5 seconds
cp->Write(DLE); // .. ended with DLE
cp->Write(ETX); // .. ETX
rc = wait_for(FAX_NO_CARR, // wait for no carrier or OK
FAX_OK, 5); // .. for 5 seconds
if (rc-- < 1) // q. error?
state = SndError; // a. yes .. declare the error
else
state = GetConfTCF; // else .. try for confirmation
break;
case GetConfTCF: // get TCF confirmation
Display_Msg(28); // update the status
if ((rc = Rcv_Hmsg()) != 0) // q. any error getting a frame?
{ // a. yes ..
error = 3; // .. error receiving HDLC frame
state = SndError; // .. leave with error.
continue; // .. continue process
}
switch(hmsg.fax_ctl_fld) // process based on message type
{
case FAX_FCF_CFR: // confirmation
state = PhaseC; // send the document
break;
case FAX_FCF_FTT: // failure to train
Display_Msg(30); // update the status
state = SendTSI; // next speed and retry session
break;
default: // unknown message
error = 21; // unexpected response
state = SndError; // .. declare an error
break;
}
break;
case PhaseC: // send the document
Display_Msg(29); // update the status
rc = Data_Mode(TRANSMIT, speed); // .. set data mode
if (rc) // q. any error?
{ // a. yes ..
state = SndError; // .. declare an error
continue;
}
state = SendPage; // else .. send the page
break;
case SendPage: // send a page of data
i = pagelen > 128 ? // determine number of bytes
128 : (int) pagelen; // .. to read and send
pagelen -= i; // decrement page length
fread(pagebuf, i, 1, faxfile); // read data from the file
cp->Write((char *) pagebuf, i); // .. and send the data
st = SECS(10); // for max of 10 seconds
while ((cp->OCount() > 256) && // let the buffer empty
error == 0) // .. while there's no error
if (time_out(&(long) st)) // q. timeout?
error = 15; // a. yes .. set the error code
if (error) // q. error occur?
{ // a. yes ..
state = SndError; // .. declare an error
continue; // .. and continue processing
}
if (cp->Read(&c, &w, &w) == 0) // q. char available?
if (c == XOFF) // a. yes .. is it XOFF?
{ // a. yes ..
Display_Msg(35); // .. show XOFF received
while(cp->Read(&c, &w, &w) // .. wait for another char
== 0xffff);
Display_Msg(36); // .. erase XOFF message
}
if (pagelen == 0) // q. page complete?
state = PhaseD; // a. yes .. continue
break;
case PhaseD: // post message process
fread(&pagelen, 4, 1, faxfile); // read next page length
while (cp->OEmpty() != 0); // wait for transmit complete
rc = wait_for(FAX_OK, // should get OK
FAX_ERR, 5) - 1;
if (rc) // q. did send complete?
{ // a. no ..
state = SndError; // .. declare an error
continue;
}
Display_Msg(31); // update the status
if (pagelen) // q. more pages?
state = AnotherPage; // a. yes ... send another
else
state = AllSent; // else .. all sent
break;
case AnotherPage: // another page to send
Display_Msg(32); // update the status
rc = Send_Hmsg(FINAL, // send the multipage signal
FAX_FCF_MPS, 0);
if (rc != 0) // q. send ok?
{ // a. no ..
state = SndError; // .. declare an error
continue; // .. continue processing
}
if ((rc = Rcv_Hmsg()) != 0) // q. any error getting a frame?
{ // a. yes ..
error = 3; // .. error receiving HDLC frame
state = SndError; // .. leave with error.
continue; // .. continue process
}
switch(hmsg.fax_ctl_fld) // process based on message type
{
case FAX_FCF_MCF: // message confirmation
state = PhaseC; // send next page
break;
default:
error = 14; // unexpected response
state = SndError; // .. declare an error
continue;
}
break;
case AllSent: // send complete
Display_Msg(33); // update the status
rc = Send_Hmsg(FINAL, // send end of procedure
FAX_FCF_EOP, 0);
if (rc != 0) // q. send ok?
{ // a. no ..
state = SndError; // .. declare an error
continue; // .. continue processing
}
if ((rc = Rcv_Hmsg()) != 0) // q. any error getting a frame?
{ // a. yes ..
error = 3; // .. error receiving HDLC frame
state = SndError; // .. leave with error.
continue; // .. continue process
}
switch(hmsg.fax_ctl_fld) // process based on message type
{
case FAX_FCF_MCF & 0x7f: // message confirmation
state = PhaseE; // send next page
break;
default:
error = 14; // unexpected response
state = SndError; // .. declare an error
continue;
}
break;
case PhaseE: // disconnect
Display_Msg(34); // update the status
rc = Send_Hmsg(FINAL, // send end of procedure
FAX_FCF_DCN, 0);
if (rc != 0) // q. send ok?
{ // a. no ..
state = SndError; // .. declare an error
continue; // .. continue processing
}
state = ExitFax; // exit fax send procedure
break;
case SndError: // error during send
Display_Msg(17); // update the status
if (rc == -2) // q. user cancellation?
{ // a. yes ..
state = UserCan; // .. show the reason
continue; // .. continue processing
}
else if (error) // q. error set?
{ // a. yes ..
fs.f_ptr = fax_errors[error]; // .. set the message pointer
(stat)(2, &fs); // .. and display it
}
else if (rc == -1) // q. timeout?
Display_Msg(20); // a. yes .. show the message
else if (rc == 1) // q. unexpected response?
Display_Msg(21); // a. yes .. tell the user
state = ExitFax; // .. set new state
break;
case UserCan: // user cancelled transmission
Display_Msg(18); // update the status
state = ExitFax; // .. set new state
break;
case ExitFax: // exit FAX receive
if (fileopen) // q. fax file open?
fclose(faxfile); // a. yes .. close the file
Reset_Modem(); // .. reset the modem
loop = FALSE; // .. end the loop
break; // .. and return to caller
}
}