Source code for /engineering/SERIAL-TEST/SERIAL.CPPOriginal file SERIAL.CPP
   1 // --------------------------------------------------------------------------
   2 // --
   3 // -- Serial - Methods and functions.
   4 // --
   5 // -- Ver. 1 - Hard-code support for COM1 and COM2, only.
   6 // --
   7 // --------------------------------------------------------------------------
   8 
   9 // --------------------------------------------------------------------------
  10 // -- Includes
  11 // --------------------------------------------------------------------------
  12 
  13 #include <stdlib.h>
  14 #include <string.h>
  15 #include <dos.h>
  16 #include "defines.h"
  17 #include "fbuffer.h"
  18 #include "serial-i.h"
  19 #include "serial.h"
  20 
  21 // --------------------------------------------------------------------------
  22 // -- Defines
  23 // --------------------------------------------------------------------------
  24 
  25 // --------------------------------------------------------------------------
  26 // -- Data
  27 // --------------------------------------------------------------------------
  28 
  29 // -- Basic vars ------------------------
  30 int            portbaseCOM1    = 0;
  31 int  		   portbaseCOM2	   = 0;
  32 
  33 // -- Interrupt vectors to save ---------
  34 void           interrupt(*origVectors[2])(...);
  35 
  36 // -- Buffers ---------------------------
  37 FBuffer 	   *rxCOM1Buffer;
  38 FBuffer        *txCOM1Buffer;
  39 FBuffer        *rxCOM2Buffer;
  40 FBuffer		   *txCOM2Buffer;
  41 
  42 
  43 // --------------------------------------------------------------------------
  44 // -- Functions
  45 // --------------------------------------------------------------------------
  46 
  47 // -- Speed is REALLY important in these.   so...  we will trade size
  48 // -- for speed.  There is a handler for each port for hardware flow
  49 // -- control and no hardware flow control.
  50 
  51 // -- COM1 interrupt handler w/Hardware Flow Control --------------------
  52 void interrupt com_int1HFC(...)
  53 {
  54 	unsigned char  IIRb;
  55 	unsigned char  MSRb;
  56 	unsigned char  characterb;
  57 
  58 	Boolean		   EOISent;
  59 
  60 	// Re-enable interrupts.  The serial chip wont interrupt us until
  61 	// we've cleaned out it's pending.
  62 	enable();
  63 
  64 	// OK, find which kind of interrupt caused this and switch it
  65 	// Do this as long as there are no interrupts pending
  66 	while( !( (IIRb = inportb(portbaseCOM1 + IIR)) & IIR_PENDING ) ) {
  67 
  68 	   IIRb &= IIR_MASK;
  69 	   switch(IIRb) {
  70 
  71 		  // --- Modem Status register changed
  72 		  case IIR_MSR:
  73 			   MSRb = inportb(portbaseCOM1 + MSR);
  74 
  75 			   // See if we just got a Clear to Send
  76 			   // If so, try and send a character if there is one queued
  77 			   // AND the transmitter hold register is clear
  78 			   if (MSRb & CTS_ASSERTED) {
  79 				  if (txCOM1Buffer->Out(characterb) == FB_NO_ERROR) {
  80 					 if (inportb(portbaseCOM1 + LSR) & XMTRDY)
  81 						outportb(portbaseCOM1 + TXR, characterb);
  82 				  } else
  83 					 outportb(portbaseCOM1 + MCR, FLOW_RNR); // Drop DTR/RTS
  84 			   }
  85 
  86 			   break;
  87 
  88 		  // --- The Transmitter went empty.  See if we can send more.
  89 		  case IIR_TX:
  90 			   // Send if character is availible AND CTS is raised
  91 			   if (txCOM1Buffer->Out(characterb) == FB_NO_ERROR) {
  92 				   if (inportb(portbaseCOM1 + MSR) & CTS)
  93 					  outportb(portbaseCOM1 + TXR, characterb);
  94 			   } else
  95 				  outportb(portbaseCOM1 + MCR, FLOW_RNR);
  96 			   break;
  97 
  98 		  // --- An incoming byte.
  99 		  case IIR_RX:
 100 			   // Cool, a byte.  Do error processing later...
 101 			   rxCOM1Buffer->In(characterb);
 102 			   break;
 103 
 104 		  // --- Line error.  Probably a framing error.
 105 		  case IIR_ERROR:
 106 			   break;
 107 		  // --- A world of hurt!
 108 
 109 		  default:
 110 			   break;
 111 
 112 	   } // end case
 113 
 114 	   // Send the non-specific EOI the first go, only
 115 	   if (!EOISent) {
 116 		  outportb(ICR, EOI);
 117 		  EOISent = TRUE;
 118 	   };
 119 
 120 	} // end while
 121 
 122 }
 123 
 124 // -- COM1 interrupt handler --------------------
 125 void interrupt com_int1(...)
 126 {
 127 	unsigned char  IIRb;
 128 	unsigned char  MSRb;
 129 	unsigned char  characterb;
 130 
 131 	Boolean		   EOISent;
 132 
 133 	// Re-enable interrupts.  The serial chip wont interrupt us until
 134 	// we've cleaned out it's pending.
 135 	enable();
 136 
 137 	// OK, find which kind of interrupt caused this and switch it
 138 	// Do this as long as there are no interrupts pending
 139 	while( !( (IIRb = inportb(portbaseCOM1 + IIR)) & IIR_PENDING ) ) {
 140 
 141 	   IIRb &= IIR_MASK;
 142 	   switch(IIRb) {
 143 
 144 		  // --- Modem Status register changed
 145 		  case IIR_MSR:
 146 			   break;
 147 
 148 		  // --- The Transmitter went empty.  See if we can send more.
 149 		  case IIR_TX:
 150 			   // Send if character is availible AND CTS is raised
 151 			   if (txCOM1Buffer->Out(characterb) == FB_NO_ERROR)
 152 				  outportb(portbaseCOM1 + TXR, characterb);
 153 			   else
 154 				  outportb(portbaseCOM1 + MCR, FLOW_RNR);
 155 			   break;
 156 
 157 		  // --- An incoming byte.
 158 		  case IIR_RX:
 159 			   // Cool, a byte.  Do error processing later...
 160 			   rxCOM1Buffer->In(characterb);
 161 			   break;
 162 
 163 		  // --- Line error.  Probably a framing error.
 164 		  case IIR_ERROR:
 165 			   break;
 166 
 167 		  // --- A world of hurt!
 168 		  default:
 169 			   break;
 170 
 171 	   } // end case
 172 
 173 	   // Send the non-specific EOI the first go, only
 174 	   if (!EOISent) {
 175 		  outportb(ICR, EOI);
 176 		  EOISent = TRUE;
 177 	   };
 178 
 179 	} // end while
 180 
 181 }
 182 
 183 // -- COM2 interrupt handler w/Hardware Flow Control --------------------
 184 void interrupt com_int2HFC(...)
 185 {
 186 	unsigned char  IIRb;
 187 	unsigned char  MSRb;
 188 	unsigned char  characterb;
 189 
 190 	Boolean		   EOISent;
 191 
 192 	// Re-enable interrupts.  The serial chip wont interrupt us until
 193 	// we've cleaned out it's pending.
 194 	enable();
 195 
 196 	// OK, find which kind of interrupt caused this and switch it
 197 	// Do this as long as there are no interrupts pending
 198 	while( !( (IIRb = inportb(portbaseCOM2 + IIR)) & IIR_PENDING ) ) {
 199 
 200 	   IIRb &= IIR_MASK;
 201 	   switch(IIRb) {
 202 
 203 		  // --- Modem Status register changed
 204 		  case IIR_MSR:
 205 			   MSRb = inportb(portbaseCOM2 + MSR);
 206 
 207 			   // See if we just got a Clear to Send
 208 			   // If so, try and send a character if there is one queued
 209 			   // AND the transmitter hold register is clear
 210 			   if (MSRb & CTS_ASSERTED) {
 211 				  if (txCOM2Buffer->Out(characterb) == FB_NO_ERROR) {
 212 					 if (inportb(portbaseCOM2 + LSR) & XMTRDY)
 213 						outportb(portbaseCOM2 + TXR, characterb);
 214 				  } else
 215 					 outportb(portbaseCOM2 + MCR, FLOW_RNR); // Drop RTS
 216 			   }
 217 
 218 			   break;
 219 
 220 		  // --- The Transmitter went empty.  See if we can send more.
 221 		  case IIR_TX:
 222 			   // Send if character is availible AND CTS is raised
 223 			   if (txCOM2Buffer->Out(characterb) == FB_NO_ERROR) {
 224 				   if (inportb(portbaseCOM2 + MSR) & CTS)
 225 					  outportb(portbaseCOM2 + TXR, characterb);
 226 			   } else
 227 				  outportb(portbaseCOM2 + MCR, FLOW_RNR);
 228 			   break;
 229 
 230 		  // --- An incoming byte.
 231 		  case IIR_RX:
 232 			   // Cool, a byte.  Do error processing later...
 233 			   rxCOM2Buffer->In(characterb);
 234 			   break;
 235 
 236 		  // --- Line error.  Probibly a framing error.
 237 		  case IIR_ERROR:
 238 			   break;
 239 
 240 		  // --- A world of hurt!
 241 		  default:
 242 			   break;
 243 
 244 	   } // end case
 245 
 246 	   // Send the non-specific EOI the first go, only
 247 	   if (!EOISent) {
 248 		  outportb(ICR, EOI);
 249 		  EOISent = TRUE;
 250 	   };
 251 
 252 	} // end while
 253 
 254 }
 255 
 256 // -- COM2 interrupt handler --------------------
 257 void interrupt com_int2(...)
 258 {
 259 	unsigned char  IIRb;
 260 	unsigned char  MSRb;
 261 	unsigned char  characterb;
 262 
 263 	Boolean		   EOISent;
 264 
 265 	// Re-enable interrupts.  The serial chip wont interrupt us until
 266 	// we've cleaned out it's pending.
 267 	enable();
 268 
 269 	// OK, find which kind of interrupt caused this and switch it
 270 	// Do this as long as there are no interrupts pending
 271 	while( !( (IIRb = inportb(portbaseCOM2 + IIR)) & IIR_PENDING ) ) {
 272 
 273 	   IIRb &= IIR_MASK;
 274 	   switch(IIRb) {
 275 
 276 		  // --- Modem Status register changed
 277 		  case IIR_MSR:
 278 			   break;
 279 
 280 		  // --- The Transmitter went empty.  See if we can send more.
 281 		  case IIR_TX:
 282 			   // Send if character is availible AND CTS is raised
 283 			   if (txCOM2Buffer->Out(characterb) == FB_NO_ERROR)
 284 				  outportb(portbaseCOM2 + TXR, characterb);
 285 			   else
 286 				  outportb(portbaseCOM2 + MCR, FLOW_RNR);
 287 			   break;
 288 
 289 		  // --- An incoming byte.
 290 		  case IIR_RX:
 291 			   // Cool, a byte.  Do error processing later...
 292 			   rxCOM2Buffer->In(characterb);
 293 			   break;
 294 
 295 		  // --- Line error.  Probibly a framing error.
 296 		  case IIR_ERROR:
 297 			   break;
 298 
 299 		  // --- A world of hurt!
 300 		  default:
 301 			   break;
 302 
 303 	   } // end case
 304 
 305 	   // Send the non-specific EOI the first go, only
 306 	   if (!EOISent) {
 307 		  outportb(ICR, EOI);
 308 		  EOISent = TRUE;
 309 	   };
 310 
 311 	} // end while
 312 
 313 }
 314 
 315 
 316 // --------------------------------------------------------------------------
 317 // -- Private Methods
 318 // --------------------------------------------------------------------------
 319 
 320 // --- Set the port -----------------
 321 SS_ERROR_T Serial::setPort(SS_PORT_T port)
 322 {
 323 	int      offset;
 324 	int far *addr;
 325 
 326 	switch (port)
 327 	{
 328 	  case SS_PORT_1 : // Set the offsets and attach the FIFOs
 329 				  offset = 0x0000;
 330 				  txCOM1Buffer = &txFIFO;
 331 				  rxCOM1Buffer = &rxFIFO;
 332 				  break;
 333 
 334 	  case SS_PORT_2:
 335 				  offset = 0x0002;
 336 				  txCOM1Buffer = &txFIFO;
 337 				  rxCOM1Buffer = &rxFIFO;
 338 				  break;
 339 
 340 	  default   : return (SS_ERROR);
 341 	}
 342 
 343 	addr = (int far *)MK_FP(0x0040, offset);
 344 	if (*addr == NULL) return (SS_ERROR);
 345 	portbase = *addr;
 346 
 347 	return (SS_NO_ERROR);
 348 }
 349 
 350 // --- Set the line speed
 351 SS_ERROR_T Serial::setSpeed(SS_BPS_T speed)
 352 {
 353 	char			item;
 354 	unsigned int	divisor;
 355 	unsigned int    numeric;
 356 
 357 	switch( speed ) {
 358 	   case SS_BPS_300 :	 numeric = 300;
 359 							 break;
 360 	   case SS_BPS_2400:	 numeric = 2400;
 361 							 break;
 362 	   case SS_BPS_9600:	 numeric = 9600;
 363 							 break;
 364 	   case SS_BPS_19200:	 numeric = 19200;
 365 							 break;
 366 	   case SS_BPS_38400:	 numeric = 38400;
 367 							 break;
 368 	   default:				 return SS_ERROR;
 369 
 370 	}
 371 
 372 	divisor = (int) (115200L/numeric);
 373 
 374 	disable();
 375 	item = inportb(portbase + LCR);
 376 	outportb(portbase + LCR, (item | 0x80));
 377 
 378 	outportb(portbase + DLL, (divisor & 0x00FF));
 379 	outportb(portbase + DLH, ((divisor >> 8) & 0x00FF));
 380 
 381 	outportb(portbase + LCR, item);
 382 	enable();
 383 
 384 	return (SS_NO_ERROR);
 385 }
 386 
 387 /* Set other communications parameters */
 388 SS_ERROR_T Serial::setFraming(SS_PARITY_T parity, SS_BITS_T bits,
 389 							  SS_SBITS_T stopBit                  )
 390 {
 391     int                setting;
 392 
 393 	switch (bits) {
 394 	   case SS_BITS_5:		setting = 0x00;
 395 							break;
 396 	   case SS_BITS_7:		setting = 0x02;
 397 							break;
 398 	   case SS_BITS_8:		setting = 0x03;
 399 							break;
 400 	   default       :      return(SS_ERROR);
 401 
 402 	}
 403 
 404 	switch (stopBit) {
 405 	   case SS_SBITS_1  :  break;
 406 	   case SS_SBITS_2  :  setting |= 0x04;
 407 						   break;
 408 	   default:			   return(SS_ERROR);
 409 	}
 410 
 411 	switch (parity) {
 412 	   case SS_PARITY_NONE:  break;
 413 	   case SS_PARITY_ODD :  setting |= 0x08;
 414 							 break;
 415 	   case SS_PARITY_EVEN:  setting |= 0x18;
 416 							 break;
 417 	   default:			     return(SS_ERROR);
 418 	}
 419 
 420 	disable();
 421 	outportb(portbase + LCR, setting);
 422 	enable();
 423 
 424 	return (SS_NO_ERROR);
 425 }
 426 
 427 // --------------------------------------------------------------------------
 428 // -- Public Methods
 429 // --------------------------------------------------------------------------
 430 
 431 // --- Port Parameters ------------------------------------------------------
 432 
 433 // --- constructor
 434 PortParameters::PortParameters(void) {
 435 
 436    bps 		= SS_BPS_DEFAULT;
 437    bits     = SS_BITS_DEFAULT;
 438    sBits    = SS_SBITS_DEFAULT;
 439    parity   = SS_PARITY_DEFAULT;
 440 
 441 }
 442 
 443 // -- incrementors
 444 void PortParameters::bpsNext(   void) {
 445 
 446    if (bps == SS_BPS_MAX)
 447 	  bps = SS_BPS_MIN;
 448    else
 449 	  bps++;
 450 
 451 }
 452 
 453 void PortParameters::bitsNext(  void) {
 454 
 455    if (bits == SS_BITS_MAX)
 456 	  bits = SS_BITS_MIN;
 457    else
 458 	  bits++;
 459 }
 460 
 461 void PortParameters::sBitsNext( void) {
 462 
 463    if (sBits == SS_SBITS_MAX)
 464 	  sBits = SS_SBITS_MIN;
 465    else
 466 	  sBits++;
 467 
 468 }
 469 
 470 void PortParameters::parityNext(void) {
 471 
 472    if (parity == SS_PARITY_MAX)
 473 	  parity = SS_PARITY_MIN;
 474    else
 475 	  parity++;
 476 }
 477 
 478 
 479 // --- Serial ---------------------------------------------------------------
 480 
 481 // --- Get a character out to the port
 482 Serial& Serial::operator<<( char octet )
 483 {
 484 
 485 	// Stop the interrupts so the handlers dont monkey with the FIFOs
 486 	disable();
 487 
 488 	// Output pending?
 489 	if( txFIFO.HasData() ) {
 490 
 491 	   // We are already going.  So, just dump it in the FIFO and move on
 492 	   txFIFO.In(octet);
 493 	   enable();
 494 	   return *this;
 495 
 496 	}
 497 
 498 	// No data!
 499 	// If we are doing flow control, see if we need to wait for CTS
 500 	if (flowControl) {
 501 	   if(!(inportb(portbase + MSR) & CTS)) {
 502 
 503 		   // Assert RTS/DTR and wait for interrupt (return, that is)
 504 		   outportb(portbase + MCR, FLOW_RR);
 505 		   enable();
 506 		   return *this;
 507 
 508 	   }
 509 	} // end if flow control
 510 
 511 
 512 	// Ok, finally, if the serial chip is not busy sending something,
 513 	// Dump this char directly on it.  If we cant, put it in the FIFO
 514 	if (inportb(LSR) & XMTRDY) 
 515 	   outportb(portbase + TXR, octet);
 516 	else
 517 	   txFIFO.In(octet);
 518 
 519 	enable();
 520 	return *this;
 521 
 522 }
 523 
 524 // --- Send a whole string.  NULL terminated
 525 Serial& Serial::operator<<( char *string )
 526 {
 527 	while (*string)
 528 	{
 529 	   (*this) << *string;
 530 	   string++;
 531 	}
 532 	return *this;
 533 }
 534 
 535 // --- Get a char from the buffer.  Dump into an int.
 536 Serial &Serial::operator>>( unsigned char &octet )
 537 {
 538 	disable();  // disable ints to prevent FIFO trouble
 539 	if (rxFIFO.Out(octet) != FB_NO_ERROR) octet = SS_NO_CHARACTER;
 540 	enable();
 541 	return *this;
 542 }
 543 
 544 Serial::~Serial()
 545 {
 546    if (up) lineDown();
 547 }
 548 
 549 
 550 
 551 
 552 // --- Constructor  ----------------------------------------------
 553 Serial::Serial(SS_PORT_T port, PortParameters  portP,
 554 			   Boolean  hwFlowControl) :
 555 			   txFIFO(SS_BUFFER_SIZE),
 556 			   rxFIFO(SS_BUFFER_SIZE)
 557 
 558 {                                        
 559 	// Setup the port we want to use
 560 	enabled = FALSE;
 561 	up      = FALSE;
 562 
 563 	flowControl = hwFlowControl;
 564 	thisPort	= port;
 565 
 566 	if (setPort(port)   == SS_ERROR) return;
 567 	if (setSpeed(portP.bps) == SS_ERROR) return;
 568 	if (setFraming(portP.parity, portP.bits,
 569 				   portP.sBits               ) == SS_ERROR ) return;
 570 
 571 	enabled = TRUE;
 572 
 573 }
 574 
 575 
 576 // --- Bring the line up -------------------
 577 SS_ERROR_T Serial::lineUp(void) {
 578 
 579    int	IRQ;
 580    int  item;
 581 
 582    // Ok, determine port and grab the vector
 583    switch( thisPort ) {
 584 
 585 	  case SS_PORT_1: IRQ = IRQ4;
 586 					  origVectors[0] = getvect(0x0C);
 587 					  if(flowControl)
 588 						 setvect(0x0C,com_int1HFC);
 589 					  else
 590 						 setvect(0x0C,com_int1);
 591 					  break;
 592 
 593 	  case SS_PORT_2: IRQ = IRQ3;
 594 					  origVectors[1] = getvect(0x0B);
 595 					  if(flowControl)
 596 						 setvect(0x0B,com_int2HFC);
 597 					  else
 598 						 setvect(0x0B,com_int2);
 599 					  break;
 600 
 601 	  default:		 return(SS_BAD_PORT);
 602    }
 603 
 604    // Enable interrupts
 605    disable();
 606 
 607    // Let the modem control and interrupt control know what we want
 608    item = inportb(portbase + MCR) | MC_INT;
 609    outportb(portbase + MCR, item);
 610    outportb(portbase + IER, ALL_INT);
 611 
 612    // Let the PIC know which to kick
 613    item = inportb(IMR) & IRQ;
 614    outportb( IMR, item );
 615 
 616    enable();
 617 
 618    up = TRUE;
 619 
 620    return SS_NO_ERROR;
 621 
 622 }
 623 
 624 // --- Bring the line Down ---------------------
 625 SS_ERROR_T Serial::lineDown(void) {
 626 
 627    int	IRQ;
 628    int  item;
 629 
 630    // Ok, determine port and replace the vector
 631    switch( thisPort ) {
 632 
 633 	  case SS_PORT_1: IRQ = IRQ4;
 634 					  setvect(0x0C, origVectors[0]);
 635 					  break;
 636 
 637 	  case SS_PORT_2: IRQ = IRQ3;
 638 					  setvect(0x0B, origVectors[1]);
 639 					  break;
 640 
 641 	  default:		 return(SS_BAD_PORT);
 642    }
 643 
 644    // Enable interrupts
 645    disable();
 646 
 647    // Let the PIC know which to stop handling
 648    item = inportb(IMR) | ~IRQ;
 649    outportb( IMR, item );
 650 
 651    // Let the modem control and interrupt control know what we want
 652    outportb(portbase + IER, 0);
 653    item = inportb(portbase + MCR) & ~MC_INT;
 654    outportb(portbase + MCR, item);
 655 
 656    enable();
 657 
 658    up = FALSE;
 659 
 660    return SS_NO_ERROR;
 661 
 662 
 663 }
 664 
 665 // --------------------------------------------------------------------------
 666 // -- Functions
 667 // --------------------------------------------------------------------------
 668