COMM.CPP
资源名称:commutil.zip [点击查看]
上传用户:xr_qian
上传日期:2007-01-05
资源大小:443k
文件大小:31k
源码类别:
通讯/手机编程
开发平台:
DOS
- // ******************************************************************** //
- // //
- // COMM.CPP //
- // Copyright (c) 1993, Michael Holmes and Bob Flanders //
- // C++ Communication Utilities //
- // //
- // Chapter 7: Receiving a FAX //
- // Last changed in chapter 7 //
- // //
- // This file contains the functions to implement the basic //
- // communications class and facilities. Each instance of the //
- // Comm class will control a single communications port. //
- // //
- // ******************************************************************** //
- class Comm
- {
- public:
- Comm(int b, // define a comm instance
- int i, // base addr, interrupt
- int d, // baud rate divisor
- int l, // line control setting
- int fc = 0, // flow control flag
- UINT si = 3200, // input queue size
- UINT so = 1500); // and output queue size
- UINT Read(char *c, // read a character from queue
- char *m, // ..and get modem status reg
- char *l), // and line status register
- Set8n(void), // set 8 data bits, no parity
- ICount(void), // get depth of input queue
- OCount(void), // ..or output queue
- IFree(void), // free space in input queue
- OFree(void); // ..or output queue
- int Modem(void), // rtn modem status register
- ModemChanged(void), // rtn TRUE if msr changed
- IFlow(void), // rtn input flow ctrl status
- IEmpty(void), // rtn TRUE if input queue
- OEmpty(void); // ..or output queue empty
- long GetSpeed(void); // rtn current speed in bps
- void SetSpeed(int d), // set up port's divisor
- SetBPS(long s), // set up port's speed
- SetLine(int l), // ..and line control register
- Write(int c), // write a character
- Write(char *s), // ..or a string of characters
- Write(char *s, int l), // ..or a block of characters
- IClear(void), // clear input queue
- OClear(void), // ..and output queue
- DTR(long t = 1500L), // lower DTR temporarily
- RTS(int), // RTS signal control
- IntRoutine(void); // interrupt service routine
- ~Comm(); // destructor
- private:
- UINT base, // base port address
- irq, // interrupt number
- divisor, // baud rate divisor
- line, // initial line control
- i_size, // input buffer size
- o_size, // output buffer size
- i_start, // flow ctl restart limit
- i_stop, // ..and upper stop limit
- i_count, // characters in input queue
- o_count, // ..and output queue
- Deque(void); // deque an output queue char
- char *i_buf, // input buffer
- *i_get, // ..and nxt user get location
- *i_put, // ..and nxt comm put location
- *i_limit, // ..and last location
- *i_last, // ..and last i_put location
- i_of, // ..and input overflow flag
- *o_buf, // output buffer
- *o_get, // ..and nxt comm get location
- *o_put, // ..and nxt user put location
- *o_limit, // ..and last location
- msr_changed, // msr changed flag
- int_msr, // last interrupt msr
- int_lsr, // ..and last interrupt lsr
- fifo, // 16550 flag
- flow, // flow control enable flag
- o_flow, // output flow controlled flag
- i_flow, // input flow controlled flag
- empty_trans; // empty transmitter flag
- void InstallInt(void), // install interrupt svc rtn
- DeInstallInt(void), // de-install interrupt rtn
- SetLimits(void), // set up flow ctl limits
- CheckFifo(void), // set up fifo flags
- Queue(int c, // queue char to input queue
- int m, // saving modem status reg
- int l), // and line status register
- interrupt (*old_comm)(...); // old comm interrupt pointer
- };
- extern
- Comm *comm; // current Comm instance
- void interrupt far comm_int(...); // comm interrupt routine
- /* ******************************************************************** *
- *
- * Comm -- define communications port instance
- *
- * ******************************************************************** */
- Comm::Comm(int b, // comm port base address
- int i, // interrupt number
- int d, // baud rate divisor
- int l, // line control setting
- int fc, // flow control flag
- UINT si, // input queue size
- UINT so) // output queue size
- {
- UINT max_size = 64535U / 3; // max number of input chars
- base = b; // set up instance base addr
- irq = i; // ..interrupt request
- o_size = so; // ..output buffer size
- flow = fc; // ..flow control flag
- CheckFifo(); // ..fifo flag if available
- SetSpeed(d); // ..line speed
- SetLine(l); // ..line format
- if (si < 512) // q. less than .5k of buffer?
- si = 512; // a. yes .. set to minimum
- if (si > max_size) // q. greater than maximum?
- si = max_size; // a. yes .. set to max size
- if (so < 256) // q. really small output buf?
- so = 256; // a. yes .. set to minimum
- i_size = si; // save input buffer size
- empty_trans = 1; // ..set empty transmitter flag
- msr_changed = 1; // ..set msr changed flag
- o_flow = i_flow = 0; // ..clear active flags
- o_count = i_count = 0; // ..and current queue counts
- i_buf = i_get = i_put = // allocate input buffer
- new char[i_size * 3]; // ..for data, lsr and msr
- i_limit = i_buf + (i_size - 1) * 3; // ..set limit address
- memset(i_buf, 0, i_size * 3); // ..and clear area to nulls
- o_buf = o_get = o_put = new char[o_size]; // allocate output buffer
- o_limit = o_buf + o_size - 1; // ..set limit address
- memset(o_buf, 0, o_size); // ..and clear area to nulls
- SetLimits(); // set up flow ctl limits
- InstallInt(); // ..and interrupt routine
- int_msr = IN(MSR); // set up initial MSR
- }
- /* ******************************************************************** *
- *
- * SetLimits -- set up receive buffer flow control limits
- *
- * This routine sets up the flow control limits for receive
- * operations. Flow control will be asserted when the buffer
- * reaches a point when there is not enough room to receive
- * a second worth of data in the input buffer. Flow control will
- * be de-asserted when 50% of the upper limit is read by the
- * application.
- *
- * ******************************************************************** */
- void Comm::SetLimits(void)
- {
- i_stop = (UINT) (115200L / 10) / divisor; // get 1 second in characters
- if (i_stop > i_size) // q. limit too high?
- i_stop = i_size / 2; // a. yes .. set at half
- else
- i_stop = i_size - i_stop; // else .. set limit point
- i_start = i_stop / 2; // set restart point at 50%
- }
- /* ******************************************************************** *
- *
- * CheckFifo -- if UART is a 16550, set FIFO variable to true
- *
- * ******************************************************************** */
- void Comm::CheckFifo(void)
- {
- fifo = 0; // assume standard uart
- OUT(FCR, 0xcf); // try to enable FIFOs
- if ((IN(IIR) & 0xc0) != 0xc0) // q. FIFO bits found?
- return; // a. no .. just return
- OUT(FCR, 0); // turn off FIFOs
- fifo = 1; // set fifo available flag
- }
- /* ******************************************************************** *
- *
- * SetSpeed -- set up port baud rate divisor
- *
- * ******************************************************************** */
- void Comm::SetSpeed(int d) // baud rate divisor
- {
- divisor = d; // save divisor for later
- OUT(IER, 0); // clear interrupts enable
- if (fifo) // q. is UART a 16550 chip?
- OUT(FCR, FCR_16550); // a. yes .. enable fifo
- IN(LSR); // read/reset line status reg
- IN(MSR); // ..and modem status register
- IN(IIR); // ..and interrupt id register
- IN(RBR); // ..and receive buffer reg
- __asm cli // stop interrupts
- OUT(LCR, IN(LCR) | LCR_DLAB); // set divisor latch bit
- OUT(DLM, d >> 8); // out msb portion of divisor
- OUT(DLL, d & 0xff); // ..then the lsb portion
- OUT(LCR, IN(LCR) & ~LCR_DLAB); // clear divisor latch bit
- OUT(MCR, MCR_DO); // enable DTR, OUT2 and RTS
- __asm sti // ..and interrupts
- OUT(IER, IER_RBF | IER_TBE | IER_MSI); // then enable UART interrupts
- }
- /* ******************************************************************** *
- *
- * SetBPS -- set port's speed
- *
- * ******************************************************************** */
- void Comm::SetBPS(long s) // speed in BPS
- {
- SetSpeed((int)(115200L / s)); // calc and set divisor
- }
- /* ******************************************************************** *
- *
- * GetSpeed -- retrieve current port speed
- *
- * ******************************************************************** */
- long Comm::GetSpeed(void)
- {
- return((long) (115200L / (long) divisor)); // return current speed
- }
- /* ******************************************************************** *
- *
- * SetLine -- set up port's line control register
- *
- * ******************************************************************** */
- void Comm::SetLine(int l) // new line control setting
- {
- line = l & ~LCR_DLAB; // save new value in instance
- OUT(LCR, line); // ..and write new LCR
- }
- /* ******************************************************************** *
- *
- * Set8n -- set 8 data bits, no parity
- *
- * ******************************************************************** */
- UINT Comm::Set8n(void)
- {
- UINT oldline; // old LCR value
- oldline = IN(LCR); // get the current LCR value
- line = ((oldline & LCR_STOP) | LCR_WLEN); // setup 8 databits, no parity
- OUT(LCR, line); // ..and write new LCR
- return(oldline); // return old LCR value
- }
- /* ******************************************************************** *
- *
- * InstallInt -- install interrupt service routine
- *
- * ******************************************************************** */
- void Comm::InstallInt(void)
- {
- int mask; // interrupt mask
- old_comm = getvect(irq + 8); // save old comm interrupt rtn
- comm = this; // ..save instance address
- setvect(irq + 8, comm_int); // ..establish new routine
- mask = inportb(I8259M); // get current interrupt mask
- mask &= ~(1 << irq); // determine new mask
- outportb(I8259M, mask); // ..and put in place
- }
- /* ******************************************************************** *
- *
- * DeInstallInt -- de-install interrupt service routine
- *
- * ******************************************************************** */
- void Comm::DeInstallInt(void)
- {
- int mask; // interrupt mask
- OUT(IER, 0); // disable UART interrupt
- OUT(FCR, 0); // ..and fifo, if available
- setvect(irq + 8, old_comm); // re-establish old comm rtn
- mask = inportb(I8259M); // get current interrupt mask
- mask |= (1 << irq); // determine new mask
- outportb(I8259M, mask); // ..and turn off comm int
- }
- /* ******************************************************************** *
- *
- * ModemChanged -- returns TRUE if the modem status register changed
- *
- * ******************************************************************** */
- int Comm::ModemChanged(void)
- {
- int rtn; // return value
- rtn = msr_changed; // get current status
- msr_changed = 0; // ..and clear flag
- return(rtn); // ..and return current status
- }
- /* ******************************************************************** *
- *
- * Modem -- returns the modem status register
- *
- * ******************************************************************** */
- int Comm::Modem(void)
- {
- return(int_msr); // return last modem status
- }
- /* ******************************************************************** *
- *
- * IFlow -- return status of input flow control
- *
- * ******************************************************************** */
- int Comm::IFlow(void)
- {
- return(i_flow); // rtn TRUE if input flow
- // ..has been restricted
- }
- /* ******************************************************************** **
- *
- * Read -- read a character, the msr and lsr from the port's input queue
- *
- * Returns: -1 = input queue was empty
- * 0 = character returned
- * n = number of characters overflowed buffer
- *
- * ******************************************************************** */
- UINT Comm::Read(char *c, // queued character
- char *m, // modem status register
- char *l) // line status register
- {
- if (i_count == 0) // q. input queue empty?
- return(-1); // a. yes .. return w/err code
- *c = *i_get++; // get char from input queue
- *m = *i_get++; // ..then get modem status
- *l = *i_get++; // ..then get line status
- i_count--; // ..finally decrement count
- if (i_get > i_limit) // q. reached end of buffer?
- i_get = i_buf; // a. yes .. set to beginning
- if (i_flow && (ICount() <= i_start)) // q. port need restarting?
- RTS(1); // a. yes .. raise RTS line
- return(((UCHAR) *l == 0xff) ? // return with character or
- ((*m << 8) + *c) : 0); // ..with overflow count
- // ..based on lsr flag
- }
- /* ******************************************************************** **
- *
- * IEmpty -- return TRUE if input queue is empty
- *
- * ******************************************************************** */
- int Comm::IEmpty(void)
- {
- return((i_count == 0) && // TRUE if queue empty
- ((UCHAR) i_put[2] != 0xff)); // ..and not in overflow
- }
- /* ******************************************************************** **
- *
- * OEmpty -- return TRUE if output queue empty
- *
- * ******************************************************************** */
- int Comm::OEmpty(void)
- {
- return(o_count == 0); // TRUE if queue empty
- }
- /* ******************************************************************** **
- *
- * ICount -- get depth of the input queue
- *
- * ******************************************************************** */
- UINT Comm::ICount(void)
- {
- return(i_count); // return nbr in queue
- }
- /* ******************************************************************** **
- *
- * OCount -- get depth of the output queue
- *
- * ******************************************************************** */
- UINT Comm::OCount(void)
- {
- return(o_count); // return nbr in queue
- }
- /* ******************************************************************** **
- *
- * IFree -- get free space in input queue
- *
- * ******************************************************************** */
- UINT Comm::IFree(void)
- {
- return(i_size - i_count); // buffer size less in-use
- }
- /* ******************************************************************** **
- *
- * OFree -- get free space in output queue
- *
- * ******************************************************************** */
- UINT Comm::OFree(void)
- {
- return(o_size - o_count); // buffer size less in-use
- }
- /* ******************************************************************** **
- *
- * Write -- write a character to the output queue
- *
- * ******************************************************************** */
- void Comm::Write(int c) // character to queue up
- {
- while (NOT OFree()) // q. room in output queue?
- ; // a. no .. wait a bit
- *o_put++ = c; // put char into output queue
- if (empty_trans) // q. pump need priming?
- { // a. yes .. try to do output
- empty_trans = 0; // clear empty tranmitter flag
- o_count++; // ..and count character
- if ((NOT flow || IN(MSR) & MSR_CTS) // q. output flow satisfied?
- && (c = Deque()) != -1) // ..and queue not empty?
- OUT(THR, c); // a. yes .. send char to port
- }
- else
- o_count++; // else .. count characters
- if (o_put > o_limit) // q. reach end of buffer?
- o_put = o_buf; // a. yes .. set to beginning
- }
- /* ******************************************************************** *
- *
- * Write -- write a string of characters to output queue
- *
- * ******************************************************************** */
- void Comm::Write(char *s) // string of chars to output
- {
- for (; *s;) // for each char in the string
- Write(*s++); // write to the output queue
- }
- /* ******************************************************************** *
- *
- * Write -- write a block of characters to output queue
- *
- * ******************************************************************** */
- void Comm::Write(char *s, // block of chars to output
- int l) // length of block
- {
- for (; l--;) // for each char in the block
- Write(*s++); // write to the output queue
- }
- /* ******************************************************************** **
- *
- * IClear -- clear input queue of unread characters
- *
- * ******************************************************************** */
- void Comm::IClear(void)
- {
- __asm cli // stop interrupts
- i_get = i_put = i_buf; // reset buffer pointers
- i_count = 0; // ..and queue count
- __asm sti // ..and re-enable interrupts
- }
- /* ******************************************************************** **
- *
- * OClear -- clear output queue of unsent characters
- *
- * ******************************************************************** */
- void Comm::OClear(void)
- {
- __asm cli // stop interrupts
- o_get = o_put = o_buf; // reset buffer pointers
- o_count = 0; // ..and queue count
- __asm sti // ..and re-enable interrupts
- }
- /* ******************************************************************** *
- *
- * Queue -- put a character into the input queue
- *
- * ******************************************************************** */
- void Comm::Queue(int c, // character to store
- int m, // modem status register
- int l) // line status register
- {
- if (flow && (ICount() >= i_stop) && // q. flow control needed?
- NOT i_flow) // ..and not already on
- RTS(0); // a. yes .. clear RTS line
- switch (IFree()) // based on avail queue space
- {
- case 0: // full input queue
- if (i_of) // q. in overflow?
- { // a. yes .. count lost chars
- if (*(UINT *) i_last < 65534U) // q. within lost count range?
- (*(UINT *) i_last)++; // a. yes .. tally another one
- }
- else
- {
- i_of = 1; // set overflow flag
- *(UINT *) i_last = 2; // init counter to 2
- i_last[2] = 0xff; // ..and set error flag in lsr
- }
- break; // ..and return to caller
- case 1: // almost full
- i_last = i_put; // save last saved addr
- i_of = 0; // clear overflow flag
- default: // from empty to almost full
- i_count++; // count characters in queue
- *i_put++ = c; // save character in queue
- *i_put++ = m; // ..and modem status register
- *i_put++ = l; // ..and line status register
- if (i_put > i_limit) // q. reach end of buffer?
- i_put = i_buf; // a. yes .. set to beginning
- }
- }
- /* ******************************************************************** *
- *
- * Deque -- get a character from the output queue
- *
- * ******************************************************************** */
- UINT Comm::Deque(void)
- {
- char c; // work character
- if (o_count == 0) // q. output queue empty?
- return(-1); // a. yes .. rtn empty handed
- c = *o_get++; // get char from output queue
- o_count--; // show character being removed
- if (o_get > o_limit) // q. reached end of buffer?
- o_get = o_buf; // a. yes .. set to beginning
- return(c & 0xff); // ..and rtn with char to send
- }
- /* ******************************************************************** **
- *
- * ~Comm -- destructor
- *
- * ******************************************************************** */
- Comm::~Comm(void)
- {
- OUT(MCR, 0); // take down DTR and RTS
- DeInstallInt(); // remove interrupt service
- delete i_buf; // free input
- delete o_buf; // ..and output queues
- }
- /* ******************************************************************** *
- *
- * DTR -- cycle DTR modem signal
- *
- * ******************************************************************** */
- void Comm::DTR(long t) // time to hold DTR low
- {
- OUT(MCR, MCR_DO & ~MCR_DTR); // set off DTR control line
- wait_ms(t); // wait a little bit
- OUT(MCR, MCR_DO); // ..and restore DTR
- }
- /* ******************************************************************** *
- *
- * RTS -- control RTS modem signal
- *
- * ******************************************************************** */
- void Comm::RTS(int f) // RTS enable/disable flag
- {
- OUT(MCR, MCR_DO - (f ? 0 : MCR_RTS)); // set mcr register
- i_flow = NOT f; // ..and set flow ctl'd flag
- }
- /* ******************************************************************** **
- *
- * IntRoutine -- communications port interrupt service routine
- *
- * ******************************************************************** */
- void Comm::IntRoutine(void)
- {
- int c, // output character
- cnt, // loop counter
- first_cycle; // first cycle flag
- char iir, // interrupt id register
- lsr; // working line status reg
- while (((iir = IN(IIR)) & IIR_PEND) == 0) // while there is work to do
- {
- switch (iir & IIR_II) // handle each interrupt
- {
- case IIR_MSI: // modem status interrupt
- msr_changed = 1; // set msr changed flag
- if ((int_msr = IN(MSR)) & // q. modem status register
- MSR_CTS) // ..ready for transmits?
- {
- if (o_flow || // q. flow controlled?
- IN(LSR) & LSR_THRE) // ..or transmitter empty?
- {
- o_flow = 0; // a. yes .. clear flag
- if ((c = Deque()) == -1)// q. output queue empty?
- empty_trans = 1; // a. yes .. set flag
- else
- OUT(THR, c); // else .. put out a character
- }
- }
- continue; // ..and check next interrupt
- case IIR_LSI: // line status interrupt
- int_lsr = IN(LSR); // read line status register
- continue; // ..and check again
- case IIR_TBE: // transmitter buffer empty
- cnt = fifo ? 15 : 1; // set up output fifo size
- first_cycle = 1; // ..and first cycle flag
- for (; cnt--;) // loop outputing characters
- {
- if (flow && // q. flow control enabled
- NOT (IN(MSR) & MSR_CTS))// ..and receiver not ready?
- {
- o_flow = first_cycle; // a. yes .. show flow ctl'd
- // ..if nothing went out
- break; // ..and check next interrupt
- }
- if ((c = Deque()) == -1) // q. output queue empty?
- {
- empty_trans = 1; // a. yes .. set flag
- break; // ..and exit this loop
- }
- OUT(THR, c); // put out another character
- first_cycle = 0; // ..and clear flag
- }
- continue; // ..and check again
- case IIR_RBF: // receiver buffer full
- while ((lsr = IN(LSR)) & LSR_DR)// while data is available
- Queue(IN(RBR), int_msr, // ..get and store
- lsr); // ..in the input queue
- continue; // check for next interrupt
- }
- }
- __asm mov al, EOI // al = end of interrupt cmd
- __asm out I8259, al // send EOI to int controller
- }
- /* ******************************************************************** *
- *
- * comm_int() -- communications port interrupt service routine header
- *
- * ******************************************************************** */
- #pragma option -O2-b-e // no global reg allocation
- // ..or dead code elimination
- void interrupt far comm_int(...)
- {
- __asm sti // re-enable interrupts
- comm->IntRoutine(); // use object's interrupt rtn
- }