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
|