FAX.CPP
资源名称:commutil.zip [点击查看]
上传用户:xr_qian
上传日期:2007-01-05
资源大小:443k
文件大小:79k
源码类别:
通讯/手机编程
开发平台:
DOS
- // ******************************************************************** //
- // //
- // 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=1r" // enter FAX class 1 mode
- #define FAX_TX_HDLC "AT +FTH=3r" // enter HDLC transmit mode
- #define FAX_RX_HDLC "AT +FRH=3r" // enter HDLC recieve mode
- #define FAX_TX_DATA "AT +FTM=%dr" // enter FAX transmit mode
- #define FAX_RX_DATA "AT +FRM=%dr" // 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=8r" // 80 ms of silence
- #define FAX_SILENT1 "AT +FRS=20r" // 200 ms of silence
- #define FAX_CLASS1 "AT +FCLASS=1r" // enter FAX class 1 mode
- #define FAX_MODEM "AT +FCLASS=0r" // return to non-FAX mode
- #define FAX_ANSWER "AT Ar" // answer an incoming call
- #define FAX_HANGUP "AT Hr" // disconnect from line
- #define FAX_DIAL "AT DT" // dial command
- #define FAX_OK "rnOKrn" // OK response
- #define FAX_ERR "rnERRORrn" // ERROR response
- #define FAX_NO_CARR "rnNO CARRIERrn" // NO CARRIER response
- #define FAX_CONN "rnCONNECTrn" // CONNECT response
- #define FAX_RING "rnRINGrn" // RING message
- #define FAX_SETMDM "AT Q0 V1 E0r" // set modem parameters
- #define FAX_RSTMDM "AT Zr" // 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 <CR> 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 <CR> 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 %srn", 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
- }
- }