www.pudn.com > slow.zip > fax.C


/********************************************************* 
********************************************************** 
	fax.c 
********************************************************** 
**********************************************************/ 
/* Modem for MIPS   AJF	  January 1995 
   Fax routines (T.30) */ 
 
#include  
 
#include  
 
#include "modem.h" 
#include "fcf.h" 
 
#define MAXFRAME	252 
#define MAXMESSAGE	(MAXFRAME+4)	/* frame plus address, control, checksum */ 
#define HDLC_TIMEOUT	(-3) 
 
#define NO_STATE	0		/* state not set yet	 */ 
#define RX_CTL_STATE	1		/* receive control info	 */ 
#define TX_CTL_STATE	2		/* transmit control info */ 
#define RX_DOC_STATE	3		/* receive document	 */ 
#define TX_DOC_STATE	4		/* transmit document	 */ 
 
#define MYNAME	"+44 1904 432738"       /* sent in TSI/CSI frame */ 
 
static uchar shuffle[256] = 
  { 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 
    8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 
    4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 
    12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 
    2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 
    10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 
    6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 
    14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 
    1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 
    9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 
    5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 
    13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 
    3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 
    11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 
    7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 
    15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255, 
  }; 
 
static uchar dcstranstab[16] = 
  { /* decodes DIS bits 21-23 into DCS equivalent */ 
    0, 1, 2, 2, 4, 1, 0, 7,	/* low density	*/ 
    0, 1, 2, 4, 4, 0, 2, 7,	/* high density */ 
  }; 
 
static short scanbitstab[8] = 
  { /* decodes DCS scan time bits into num. of bits at 7200 bit/s */ 
    144, 288, 72, -1, 36, -1, -1, 0, 
  }; 
 
static uchar mpsframe[] = { MPS }; 
static uchar eopframe[] = { EOP }; 
static uchar dcnframe[] = { DCN }; 
static uchar fttframe[] = { FTT }; 
static uchar cfrframe[] = { CFR }; 
static uchar mcfframe[] = { MCF }; 
 
static uchar disframe[] = { DIS, 0x00, 0xe2, 0x0e };	/* V.29 only; no 2d coding; set "fine" bit; no scan-line padding */ 
 
static int pagecount, state, rxval, framelen, msgendtime, scanbits; 
static bool isfinal; 
static uchar frame[MAXFRAME]; 
static uchar identframe[21], dcsframe[4]; 
 
static void senddoc(), makedcs(uchar*), sendtraining(); 
static void receivedoc(), getdcs(bool), checkdcs(uchar*); 
static bool gettraining(); 
static void makeident(uchar); 
static void getmessage(), printident(), getmsg(), nextval(); 
static void msgerror(char*, word = 0, word = 0, word = 0); 
static void sendframe(uchar*, int, bool), printframe(char*, uchar*, int); 
static char *frametype(uchar); 
static bool csumok(uchar*, int); 
static ushort computecsum(uchar*, int); 
static void setstate(int); 
 
 
global void becomefax() 
  { pagecount = 0; state = NO_STATE; 
    if (options & opt_org) senddoc(); else receivedoc(); 
  } 
 
static void senddoc() 
  { makeident(TSI); 
    int ntries = 0; 
    do 
      { getmessage();	/* try for DIS */ 
	ntries++; 
      } 
    until ((frame[0] == DIS && framelen >= 4) || ntries >= 6); 
    unless (frame[0] == DIS && framelen >= 4) giveup("Failed to get a valid DIS frame"); 
    makedcs(frame);				    /* construct DCS from DIS */ 
    sendtraining(); 
    while (pagecount < numpages) 
      { setstate(TX_DOC_STATE); 
	sendpage(pagecount+1, scanbits); 
	ntries = 0; 
	do 
	  { sendframe((pagecount+1 < numpages) ? mpsframe : eopframe, 1, true);	    /* send MPS or EOP */ 
	    getmessage();	/* shd be MCF, RTP, RTN */ 
	    ntries++; 
	  } 
	until (frame[0] == MCF || frame[0] == RTP || frame[0] == RTN || ntries >= 3); 
	unless (frame[0] == MCF || frame[0] == RTP || frame[0] == RTN) break; 
	if (frame[0] == MCF || frame[0] == RTP) pagecount++; 
	if (frame[0] == RTP || frame[0] == RTN) sendtraining(); 
      } 
    sendframe(dcnframe, 1, true);   /* send DCN */ 
    if (pagecount < numpages) giveup("Document transmission failed"); 
    infomsg("Success - delivered %d pages", pagecount); 
  } 
 
static void makedcs(uchar *disfr) 
  { /* make DCS frame from received DIS; establish capabilities */ 
    dcsframe[0] = DCS; 
    dcsframe[1] = 0x00; 
    unless (disfr[2] & 0x40) giveup("Remote fax can't do Group 3 (T.4)"); 
    uchar spd = (disfr[2] >> 2) & 0xf; 
    dcsframe[2] = 0x70;			/* set to V.29 7200 bit/s; no 2d coding; clear "fine" bit */ 
    uchar n = (disfr[3] >> 1) & 7;	/* DIS bits 21-23 */ 
    if (options & opt_H) 
      { unless (disfr[2] & 0x02) giveup("Remote fax can't do 200 dpi; try -l"); 
	dcsframe[2] |= 0x02;		/* ask for high density */ 
	n += 8; 
      } 
    n = dcstranstab[n];			/* translate to DCS equivalent */ 
    dcsframe[3] = n << 1;		/* set A4 1728 dots per scan line; set scantime bits; clear extend bit */ 
    scanbits = scanbitstab[n];		/* min. scan time, in bits */ 
    infomsg("resolution=%c; speeds=%02x; scanbits=%d", (options & opt_H) ? 'H' : 'L', spd, scanbits); 
    unless (spd & 0x8) giveup("Remote fax can't do V.29"); 
  } 
 
static void sendtraining() 
  { int ntries = 0; 
    do 
      { sendframe(identframe, 21, false);	    /* send TSI */ 
	sendframe(dcsframe, 4, true);		    /* send DCS */ 
	if (options & opt_v) fprintf(stderr, ">>> TCF\n"); 
	setstate(TX_DOC_STATE); 
	for (int j = 0; j < 10800; j++) putbit(0);  /* TCF, 1.5 sec @ 7200 bps */ 
	getmessage(); 
	ntries++; 
      } 
    until (frame[0] == CFR || ntries >= 3); 
    unless (frame[0] == CFR) giveup("Remote fax training failed"); 
  } 
 
static void receivedoc() 
  { makeident(CSI); 
    getdcs(true); 
    bool ok = gettraining(); 
    int ntries = 0; 
    until (ok || ntries >= 3) 
      { getdcs(false); 
	ok = gettraining(); 
	ntries++; 
      } 
    unless (ok) giveup("Local fax training failed"); 
    sendframe(cfrframe, 1, true); 
    bool more; 
    do 
      { setstate(RX_DOC_STATE); 
	receivepage(pagecount+1); 
	ntries = 0; 
	do 
	  { getmessage(); 
	    ntries++; 
	  } 
	until (frame[0] == EOP || frame[0] == MPS || ntries >= 3); 
	unless (frame[0] == EOP || frame[0] == MPS) giveup("Remote fax has given up"); 
	more = (frame[0] == MPS); 
	sendframe(mcfframe, 1, true); 
	pagecount++; 
      } 
    while (more); 
    getmessage();	/* should be DCN */	// ??? 
    infomsg("Success - received %d pages", pagecount); 
  } 
 
static void getdcs(bool first) 
  { int ntries = 0; 
    do 
      { if (first) 
	  { sendframe(identframe, 21, false);	/* send CSI */ 
	    sendframe(disframe, 4, true);	/* send DIS */ 
	  } 
	else sendframe(fttframe, 1, true);	/* send FTT */ 
	getmessage();	/* try for DCS */ 
	ntries++; 
      } 
    until ((frame[0] == DCS && framelen >= 4) || frame[0] == DCN || ntries >= 6); 
    unless (frame[0] == DCS && framelen >= 4) giveup("Failed to get a valid DCS frame"); 
    checkdcs(frame); 
  } 
 
static void checkdcs(uchar *dcsfr) 
  { /* check DCS frame from remote sender */ 
    uchar spd = (dcsfr[2] >> 2) & 0xf; 
    if (dcsfr[2] & 0x02) options |= opt_H; 
    infomsg("resolution=%c; speeds=%02x; scanbits=%d", (options & opt_H) ? 'H' : 'L', spd, scanbits); 
    unless (dcsfr[2] & 0x40) giveup("Remote fax can't do Group 3 (T.4)"); 
    unless (spd & 0x8) giveup("Remote fax can't do V.29"); 
  } 
 
static bool gettraining() 
  { setstate(RX_DOC_STATE); 
    int totnz = 0, tot = 0; 
    for (int j = 0; j < 15; j++) 
      { int nz = 0; 
	for (int k = 0; k < 720; k++) if (!getbit()) nz++; 
	if (options & opt_v) fprintf(stderr, "%d ", nz); 
	/* V.29 training is short, so ignore first 720 symbols. 
	   Spec says TCF is +-10%, so ignore last 1440 symbols. */ 
	unless (j == 0 || j == 14 || j == 15) { totnz += nz; tot += 720; } 
      } 
    bool ok = (tot-totnz < 10);		/* tolerate 0.1% error */ 
    if (options & opt_v) 
      { fprintf(stderr, "= %d out of %d\n", totnz, tot); 
	fprintf(stderr, "<<< TCF %s\n", ok ? "ok" : "fail"); 
      } 
    return ok; 
  } 
 
static void makeident(uchar cmd) 
  { int len = strlen(MYNAME); 
    int p = 0; 
    identframe[p++] = cmd;  /* TSI or CSI */ 
    while (len > 0) identframe[p++] = shuffle[MYNAME[--len]]; 
    while (p < 21) identframe[p++] = 0x04; /* pad with spaces */ 
  } 
 
static void getmessage() 
  { setstate(RX_CTL_STATE); 
    isfinal = true; 
    msgendtime = samplecount + 4*SAMPLERATE;	/* reset timeout counter (4 secs in future) */ 
    nextval();					/* read 1st byte of preamble */ 
    do 
      { getmsg(); 
	if (frame[0] == CSI || frame[0] == TSI) printident(); 
      } 
    until (isfinal || frame[0] == TMO); 
  } 
 
static void printident() 
  { char buf[21]; 
    int p = framelen, k = 0; 
    while (p > 1 && frame[p-1] == 0x04) p--; /* delete spaces */ 
    while (p > 1 && k < 20) buf[k++] = shuffle[frame[--p]]; 
    buf[k++] = '\0'; 
    infomsg("Remote fax identifies as: %s", buf); 
  } 
 
static void getmsg() 
  { uchar message[MAXMESSAGE]; 
    int msglen; 
    bool ok = false; 
    until (ok || rxval == HDLC_TIMEOUT) 
      { until (rxval == HDLC_FLAG || rxval == HDLC_TIMEOUT) nextval(); 
	int nflags = 0; 
	while (rxval == HDLC_FLAG) { nflags++; nextval(); } 
	unless (isfinal && nflags < 10) /* preamble too short? */ 
	  { msglen = 0; 
	    until (rxval == HDLC_FLAG || rxval == HDLC_TIMEOUT || msglen >= MAXMESSAGE) 
	      { unless (rxval == HDLC_ABORT) message[msglen++] = rxval; 
		nextval(); 
	      } 
	    unless (rxval == HDLC_TIMEOUT) 
	      { if (rxval != HDLC_FLAG) 
		  msgerror("Message too long: %02x %02x %02x...", message[0], message[1], message[2]); 
		else if (msglen < 5) /* ensure frame length .ge. 1 */ 
		  msgerror("Message too short"); 
		else unless (csumok(message, msglen)) 
		  msgerror("Checksum error"); 
		else unless (message[0] == 0xff) 
		  msgerror("Got %02x after frame, exp ff", message[0]); 
		else unless (message[1] == 0xc0 || message[1] == 0xc8) 
		  msgerror("Got %02x after addr, exp c0 or c8", message[1]); 
		else ok = true; 
	      } 
	  } 
      } 
    if (ok) 
      { framelen = msglen-4;	/* length excludes addr, control, checksum */ 
	memcpy(frame, &message[2], framelen); 
	isfinal = message[1] & 8;   /* final frame? */ 
      } 
    else 
      { frame[0] = TMO;		/* fake "timeout" frame */ 
	framelen = 1; 
	isfinal = true; 
      } 
    printframe("<<<", frame, framelen); 
  } 
 
static void nextval() 
  { rxval = (after(samplecount, msgendtime)) ? HDLC_TIMEOUT : getsync(); 
  } 
 
static void msgerror(char *msg, word p1, word p2, word p3) 
  { if (options & opt_v) 
      { fprintf(stderr, "*** "); fprintf(stderr, msg, p1, p2, p3); putc('\n', stderr); 
      } 
  } 
 
static void sendframe(uchar *fr, int frlen, bool final) 
  { setstate(TX_CTL_STATE); 
    printframe(">>>", fr, frlen); 
    uchar message[MAXMESSAGE]; 
    message[0] = 0xff; message[1] = final ? 0xc8 : 0xc0; 
    memcpy(&message[2], fr, frlen); 
    ushort csum = computecsum(message, frlen+2); 
    message[frlen+2] = (csum >> 8) ^ 0xff; message[frlen+3] = (csum & 0xff) ^ 0xff; 
    unless (csumok(message, frlen+4)) giveup("Bug! Tx checksum"); 
    putsync(HDLC_FLAG); putsync(HDLC_FLAG); 
    for (int i=0; i < frlen+4; i++) putsync(message[i]); 
    putsync(HDLC_FLAG); 
  } 
 
static void printframe(char *io, uchar *fr, int frlen) 
  { if (options & opt_v) 
      { fprintf(stderr, "%s %s:", io, frametype(fr[0])); 
	for (int i=0; i < frlen; i++) fprintf(stderr, " %02x", fr[i]); 
	putc('\n', stderr); 
      } 
  } 
 
static char *frametype(uchar x) 
  { switch (x) 
      { default:    return "???"; 
	case DIS:   return "DIS"; 
	case CSI:   return "CSI"; 
	case NSF:   return "NSF"; 
	case CFR:   return "CFR"; 
	case FTT:   return "FTT"; 
	case MCF:   return "MCF"; 
	case RTN:   return "RTN"; 
	case RTP:   return "RTP"; 
	case DCS:   return "DCS"; 
	case TSI:   return "TSI"; 
	case DCN:   return "DCN"; 
	case XCN:   return "XCN"; 
	case MPS:   return "MPS"; 
	case EOP:   return "EOP"; 
	case TMO:   return "TMO"; 
      } 
  } 
 
static bool csumok(uchar *msg, int len) 
  { ushort csum = computecsum(msg, len); 
    return (csum == 0x1d0f); 
  } 
 
static ushort computecsum(uchar *msg, int len) 
  { /* CCITT V.41 crc (sort of) */ 
    ushort reg = 0xffff; 
    while (len-- > 0) 
      { uchar x = *(msg++); 
	for (int j=0; j < 8; j++) 
	  { uchar bit = (reg >> 15) ^ (x >> 7); 
	    reg <<= 1; x <<= 1; 
	    if (bit) reg ^= 0x1021; 
	  } 
      } 
    return reg; 
  } 
 
static void setstate(int st) 
  { unless (st == state) 
      { switch (st) 
	  { case RX_CTL_STATE: 
		flushoutput(); discardinput();			/* flush Tx before switching to Rx */ 
		initrx_fsk(V21o); 
		break; 
 
	    case TX_CTL_STATE: 
		if (state == TX_DOC_STATE) sendpause(0.075);	/* pause before switching from TX_DOC to TX_CTL */ 
		inittx_fsk(V21a); 
		for (int i=0; i < 40; i++) putsync(HDLC_FLAG);	/* send HDLC preamble */ 
		break; 
 
	    case RX_DOC_STATE: 
		/* old state is tx_ctl or rx_ctl */ 
		if (state == TX_CTL_STATE) { flushoutput(); discardinput(); } 
		initrx_v29(); 
		break; 
 
	    case TX_DOC_STATE: 
		sendpause(0.075);	/* pause before switching from anything to TX_DOC */ 
		inittx_v29(); 
		break; 
	  } 
	state = st; 
      } 
  }