forked from kstenerud/DOS-Serial-Library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
serial.c
1647 lines (1352 loc) · 51.1 KB
/
serial.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <conio.h>
#include <dos.h>
#include "serial.h"
#if defined( __DJGPP__ )
#include <sys/farptr.h>
#include <go32.h>
#include <dpmi.h>
#define Interrupt
#define Far
#define Farpeekw(s, o) _farpeekw((s),(o))
#define _Outp(a,b) (outp(a,b),(b))
#define _Outpw(a,w) (outpw(a,w),(w))
#define CPU_DISABLE_INTERRUPTS() asm("CLI")
#define CPU_ENABLE_INTERRUPTS() asm("STI")
#elif defined( _MSC_VER ) || defined( __WATCOMC__ )
#define Interrupt __interrupt
#define Far __far
#define Farpeekw(s, o) (*((unsigned short Far*)(MK_FP((s), (o)))))
#define _Outp(a,b) outp(a,b)
#define _Outpw(a,w) outpw(a,w)
#define CPU_DISABLE_INTERRUPTS() __asm CLI
#define CPU_ENABLE_INTERRUPTS() __asm STI
#else /* __BORLANDC__ */
#define Interrupt interrupt
#define Far far
#define Farpeekw(s, o) (*((unsigned short Far*)(MK_FP((s), (o)))))
#define _Outp(a,b) outp(a,b)
#define _Outpw(a,w) outpw(a,w)
#define CPU_DISABLE_INTERRUPTS() asm CLI
#define CPU_ENABLE_INTERRUPTS() asm STI
#endif /* _MSC_VER */
#if defined( __386__ ) || defined( __DJGPP__ )
#define PROTECTED_MODE
#endif
/* ======================================================================== */
/* =========================== DEFINES & MACROS =========================== */
/* ======================================================================== */
/* Tweak Values */
#define SER_RX_BUFFER_SIZE_BITS 11 /* 2048 */
#define SER_RX_BUFFER_HIGH_PERCENT 80
#define SER_RX_BUFFER_LOW_PERCENT 20
#define SER_TX_BUFFER_SIZE_BITS 11 /* 2048 */
#define SER_TX_BUFFER_HIGH_PERCENT 80
#define SER_TX_BUFFER_LOW_PERCENT 20
#define UART_FIFO_DEFAULT_THRESHOLD 14 /* 14, 8, 4, 1, or 0 (off) */
#define COM1_DEFAULT_IRQ 4
#define COM2_DEFAULT_IRQ 3
#define COM3_DEFAULT_IRQ 4
#define COM4_DEFAULT_IRQ 3
#define COM_MIN COM_1
#define COM_MAX COM_4
#define IRQ_MIN 3
#define IRQ_MAX 15
#define IRQ_NONE 0xff
/* Receive and transmit buffers
*
* Both head and tail work in pre-increment mode:
*
* H->W->W->W
* * * *
* 0 1 2 3 4 5 ...
* * * *
* T->R->R->R
*
* Buffer empty:
*
* H
* ... 4 5 6 7 ...
* T
*
* Buffer full:
*
* H
* ... 4 5 6 7 ...
* T
*/
#define SER_RX_BUFFER_SIZE (1L<<SER_RX_BUFFER_SIZE_BITS)
#define SER_RX_BUFFER_SIZE_MASK (~((-1)<<SER_RX_BUFFER_SIZE_BITS))
#define SER_RX_BUFFER_HIGH (SER_RX_BUFFER_SIZE*SER_RX_BUFFER_HIGH_PERCENT/100UL)
#define SER_RX_BUFFER_LOW (SER_RX_BUFFER_SIZE*SER_RX_BUFFER_LOW_PERCENT/100UL)
#define SER_RX_BUFFER_READ(C) (C)->rx_buff[(C)->rx_tail = ((C)->rx_tail+1) & SER_RX_BUFFER_SIZE_MASK]
#define SER_RX_BUFFER_WRITE(C, D) (C)->rx_buff[(C)->rx_head = ((C)->rx_head+1) & SER_RX_BUFFER_SIZE_MASK] = D
#define SER_RX_BUFFER_INIT(C) (C)->rx_head = (C)->rx_tail = 0
#define SER_RX_BUFFER_EMPTY(C) ((C)->rx_head == (C)->rx_tail)
#define SER_RX_BUFFER_FULL(C) ((((C)->rx_head+1) & SER_RX_BUFFER_SIZE_MASK) == (C)->rx_tail)
#define SER_RX_BUFFER_CURRENT(C) (((C)->rx_head - (C)->rx_tail) & SER_RX_BUFFER_SIZE_MASK)
#define SER_RX_BUFFER_LOWATER(C) (SER_RX_BUFFER_CURRENT(C) < SER_RX_BUFFER_LOW)
#define SER_RX_BUFFER_HIWATER(C) (SER_RX_BUFFER_CURRENT(C) > SER_RX_BUFFER_HIGH)
#define SER_TX_BUFFER_SIZE (1L<<SER_TX_BUFFER_SIZE_BITS)
#define SER_TX_BUFFER_SIZE_MASK (~((-1)<<SER_TX_BUFFER_SIZE_BITS))
#define SER_TX_BUFFER_HIGH (SER_TX_BUFFER_SIZE*SER_TX_BUFFER_HIGH_PERCENT/100UL)
#define SER_TX_BUFFER_LOW (SER_TX_BUFFER_SIZE*SER_TX_BUFFER_LOW_PERCENT/100UL)
#define SER_TX_BUFFER_READ(C) (C)->tx_buff[(C)->tx_tail = ((C)->tx_tail+1) & SER_TX_BUFFER_SIZE_MASK]
#define SER_TX_BUFFER_WRITE(C, D) (C)->tx_buff[(C)->tx_head = ((C)->tx_head+1) & SER_TX_BUFFER_SIZE_MASK] = D
#define SER_TX_BUFFER_INIT(C) (C)->tx_head = (C)->tx_tail = 0
#define SER_TX_BUFFER_EMPTY(C) ((C)->tx_head == (C)->tx_tail)
#define SER_TX_BUFFER_FULL(C) ((((C)->tx_head+1) & SER_TX_BUFFER_SIZE_MASK) == (C)->tx_tail)
#define SER_TX_BUFFER_CURRENT(C) (((C)->tx_head - (C)->tx_tail) & SER_TX_BUFFER_SIZE_MASK)
#define SER_TX_BUFFER_LOWATER(C) (SER_TX_BUFFER_CURRENT(C) < SER_TX_BUFFER_LOW)
#define SER_TX_BUFFER_HIWATER(C) (SER_TX_BUFFER_CURRENT(C) > SER_TX_BUFFER_HIGH)
/* XON/XOFF Flow Control Commands */
#define SER_XON 0x11
#define SER_XOFF 0x13
/* PIC Registers & interrupts */
#define INTERRUPT_VECTOR_OFFSET 8
#define PIC_MASTER 0x20
#define PIC_SLAVE 0xa0
#define PIC_EOI 0x20
#define PIC_RR 0x02
#define PIC_WRITE_IMR(P, D) outp((P)|1, D) /* interrupt masks */
#define PIC_WRITE_OCW2(P, D) outp(P, D) /* R, SL, EOI, 0, 0, L2, L1, L0 */
#define PIC_WRITE_OCW3(P, D) outp(P, (D)|8) /* 0, ESMM, SMM, 0, 1, P, RR, RIS */
#define PIC_READ_IMR(P) inp((P)|1) /* interrupt masks */
#define PIC_END_INTERRUPT(P) PIC_WRITE_OCW2(P, PIC_EOI)
static unsigned char PIC_READ_IRR(unsigned int port){PIC_WRITE_OCW3(port, PIC_RR); return inp(port);}
#define PIC_ENABLE_IRQ(I) {if(I <= 7) PIC_WRITE_IMR(PIC_MASTER, PIC_READ_IMR(PIC_MASTER) & ~(1 << I)); \
else PIC_WRITE_IMR(PIC_SLAVE, PIC_READ_IMR(PIC_SLAVE) & ~(1 << ((I)-7)));}
#define PIC_DISABLE_IRQ(I) {if(I <= 7) PIC_WRITE_IMR(PIC_MASTER, PIC_READ_IMR(PIC_MASTER) | (1 << I)); \
else PIC_WRITE_IMR(PIC_SLAVE, PIC_READ_IMR(PIC_SLAVE) | (1 << ((I)-7)));}
/* UART Registers */
#define UART_RX_BUFFER 0
#define UART_TX_BUFFER 0
#define UART_INTERRUPT_ENABLE 1
#define UART_DIVISOR_LATCH_WORD 0
#define UART_DIVISOR_LATCH_LSB 0
#define UART_DIVISOR_LATCH_MSB 1
#define UART_INTERRUPT_IDENTIFY 2
#define UART_FIFO_CONTROL 2
#define UART_LINE_CONTROL 3
#define UART_MODEM_CONTROL 4
#define UART_LINE_STATUS 5
#define UART_MODEM_STATUS 6
/* UART Read commands (B = UART Base Address <int>) */
#define UART_READ_DATA(C) inp((C)->base+UART_RX_BUFFER)
#define UART_READ_INTERRUPT_ENABLE(C) inp((C)->base+UART_INTERRUPT_ENABLE)
#define UART_READ_INTERRUPT_IDENTIFY(C) inp((C)->base+UART_INTERRUPT_IDENTIFY)
#define UART_READ_LINE_CONTROL(C) inp((C)->base+UART_LINE_CONTROL)
#define UART_READ_MODEM_CONTROL(C) inp((C)->base+UART_MODEM_CONTROL)
#define UART_READ_LINE_STATUS(C) ((C)->lsr = inp((C)->base+UART_LINE_STATUS))
#define UART_READ_MODEM_STATUS(C) ((C)->msr = inp((C)->base+UART_MODEM_STATUS))
#define UART_READ_BPS(C) ((_Outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) | UART_LCR_DIVISOR_LATCH) & 0) | \
inpw((C)->base+UART_DIVISOR_LATCH_WORD) | \
(_Outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) & ~UART_LCR_DIVISOR_LATCH) & 0))
/* UART Write Commands (B = UART Base Address <int>, D = Data <char>) */
#define UART_WRITE_DATA(C, D) outp((C)->base+UART_TX_BUFFER, D)
#define UART_WRITE_INTERRUPT_ENABLE(C, D) ((C)->ier = _Outp((C)->base+UART_INTERRUPT_ENABLE, D))
#define UART_WRITE_FIFO_CONTROL(C, D) ((C)->fcr = _Outp((C)->base+UART_FIFO_CONTROL, D))
#define UART_WRITE_LINE_CONTROL(C, D) ((C)->lcr = _Outp((C)->base+UART_LINE_CONTROL, D))
#define UART_WRITE_MODEM_CONTROL(C, D) ((C)->mcr = _Outp((C)->base+UART_MODEM_CONTROL, D))
#define UART_WRITE_BPS(C, D) {outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) | UART_LCR_DIVISOR_LATCH); \
(C)->dlatch = _Outpw((C)->base+UART_DIVISOR_LATCH_WORD, D); \
outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) & ~UART_LCR_DIVISOR_LATCH);}
/* Interrupt Enable Register Components */
#define UART_IER_MODEM_STATUS 0x08
#define UART_IER_ERRORS 0x04
#define UART_IER_TX_HOLD_EMPTY 0x02
#define UART_IER_DATA_READY 0x01
/* Interrupt Identify Register Components */
#define UART_IIR_FIFO_ENABLE_1 0x80
#define UART_IIR_FIFO_ENABLE_0 0x40
#define UART_IIR_IDENTIFY_2 0x08
#define UART_IIR_IDENTIFY_1 0x04
#define UART_IIR_IDENTIFY_0 0x02
#define UART_IIR_NO_INTERRUPT_PENDING 0x01
#define UART_IIR_MASK (UART_IIR_NO_INTERRUPT_PENDING | UART_IIR_IDENTIFY_0 | UART_IIR_IDENTIFY_1)
#define UART_IIR_LINE_STATUS (UART_IIR_IDENTIFY_0 | UART_IIR_IDENTIFY_1)
#define UART_IIR_DATA_READY UART_IIR_IDENTIFY_1
#define UART_IIR_TX_HOLD_EMPTY UART_IIR_IDENTIFY_0
#define UART_IIR_MODEM_STATUS 0x00
#define UART_IIR_NO_INTERRUPT UART_IIR_NO_INTERRUPT_PENDING
/* Fifo Control Register Components */
#define UART_FCR_TRIGGER_1 0x80
#define UART_FCR_TRIGGER_0 0x40
#define UART_FCR_DMA_SELECT 0x08
#define UART_FCR_TX_FIFO_RESET 0x04
#define UART_FCR_RX_FIFO_RESET 0x02
#define UART_FCR_FIFO_ENABLE 0x01
#define UART_FCR_TRIGGER_AT_1 0x00
#define UART_FCR_TRIGGER_AT_4 UART_FCR_TRIGGER_0
#define UART_FCR_TRIGGER_AT_8 UART_FCR_TRIGGER_1
#define UART_FCR_TRIGGER_AT_14 (UART_FCR_TRIGGER_0 | UART_FCR_TRIGGER_1)
/* Line Control Register Components */
#define UART_LCR_DIVISOR_LATCH 0x80
#define UART_LCR_SET_BREAK 0x40
#define UART_LCR_STICK_PARITY 0x20
#define UART_LCR_EVEN_PARITY 0x10
#define UART_LCR_PARITY_ENABLE 0x08
#define UART_LCR_STOPBITS 0x04
#define UART_LCR_WORD_LENGTH_1 0x02
#define UART_LCR_WORD_LENGTH_0 0x01
#define UART_DATA_5 0x00
#define UART_DATA_6 UART_LCR_WORD_LENGTH_0
#define UART_DATA_7 UART_LCR_WORD_LENGTH_1
#define UART_DATA_8 (UART_LCR_WORD_LENGTH_0 | UART_LCR_WORD_LENGTH_1)
#define UART_DATA_MASK (UART_LCR_WORD_LENGTH_0 | UART_LCR_WORD_LENGTH_1)
#define UART_PARITY_NONE 0x00
#define UART_PARITY_ODD UART_LCR_PARITY_ENABLE
#define UART_PARITY_EVEN (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY)
#define UART_PARITY_MARK (UART_LCR_PARITY_ENABLE | UART_LCR_STICK_PARITY)
#define UART_PARITY_SPACE (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY | UART_LCR_STICK_PARITY)
#define UART_PARITY_MASK (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY | UART_LCR_STICK_PARITY)
#define UART_STOP_1 0x00
#define UART_STOP_2 UART_LCR_STOPBITS
#define UART_STOP_MASK UART_LCR_STOPBITS
/* Modem Control Register Components */
#define UART_MCR_LOOPBACK 0x10
#define UART_MCR_OUT2 0x08
#define UART_MCR_OUT1 0x04
#define UART_MCR_RTS 0x02
#define UART_MCR_DTR 0x01
#define UART_MCR_MASK ( UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT1 | UART_MCR_OUT2 | UART_MCR_LOOPBACK )
/* Line Status Register Components */
#define UART_LSR_FIFO_ERROR 0x80
#define UART_LSR_TX_EMPTY 0x40
#define UART_LSR_TX_HOLD_EMPTY 0x20
#define UART_LSR_BREAK_INDICATOR 0x10
#define UART_LSR_FRAME_ERROR 0x08
#define UART_LSR_PARITY_ERROR 0x04
#define UART_LSR_OVERRUN_ERROR 0x02
#define UART_LSR_DATA_READY 0x01
/* Modem Status Register Components */
#define UART_MSR_DCD 0x80
#define UART_MSR_RI 0x40
#define UART_MSR_DSR 0x20
#define UART_MSR_CTS 0x10
#define UART_MSR_DLT_DCD 0x08
#define UART_MSR_NEW_RING 0x04
#define UART_MSR_DLT_DSR 0x02
#define UART_MSR_DLT_CTS 0x01
/* BPS Rates (based on 1.8432 MHz crystal) */
#define UART_BPS_DIVISOR_50 2304
#define UART_BPS_DIVISOR_75 1536
#define UART_BPS_DIVISOR_110 1047 /* 0.026% error */
#define UART_BPS_DIVISOR_134_5 857 /* 0.058% error */
#define UART_BPS_DIVISOR_150 768
#define UART_BPS_DIVISOR_300 384
#define UART_BPS_DIVISOR_600 192
#define UART_BPS_DIVISOR_1200 96
#define UART_BPS_DIVISOR_1800 64
#define UART_BPS_DIVISOR_2400 48
#define UART_BPS_DIVISOR_3800 32
#define UART_BPS_DIVISOR_4800 24
#define UART_BPS_DIVISOR_7200 16
#define UART_BPS_DIVISOR_9600 12
#define UART_BPS_DIVISOR_19200 6
#define UART_BPS_DIVISOR_38400 3
#define UART_BPS_DIVISOR_57600 2
#define UART_BPS_DIVISOR_115200 1
/* FIFO size */
#define UART_FIFO_SIZE_IN_BYTES 16
/* ======================================================================== */
/* ==================== PROTOTYPES, TYPEDEFS & GLOBALS ==================== */
/* ======================================================================== */
typedef struct
{
unsigned char port;
unsigned char default_irq;
unsigned char irq;
unsigned char open;
unsigned char ier;
unsigned char fcr;
unsigned char lcr;
unsigned char mcr;
unsigned char lsr;
unsigned char msr;
unsigned char flow_mode;
unsigned char rx_flow_on;
unsigned char tx_flow_on;
unsigned char rx_buff[(unsigned short)SER_RX_BUFFER_SIZE];
unsigned char tx_buff[(unsigned short)SER_TX_BUFFER_SIZE];
unsigned int base;
unsigned int dlatch;
unsigned int rx_head;
unsigned int tx_head;
unsigned int rx_tail;
unsigned int tx_tail;
} serial_struct;
#if defined( __DJGPP__ )
typedef _go32_dpmi_seginfo int_handler_ptr;
static _go32_dpmi_seginfo g_old_isrs[16];
#else
typedef void Interrupt Far (*int_handler_ptr)(void);
static int_handler_ptr g_old_isrs[16];
#endif
static unsigned char g_isrs_taken[16] = {0};
#ifdef PROTECTED_MODE
static int_handler_ptr g_isr_addr;
static unsigned int g_isrs_count = 0;
#endif
/* serial port data */
static serial_struct g_comports[COM_MAX+1] =
{
#if defined( __GNUC__ )
{port: 0, default_irq: COM1_DEFAULT_IRQ, irq: IRQ_NONE},
{port: 1, default_irq: COM2_DEFAULT_IRQ, irq: IRQ_NONE},
{port: 2, default_irq: COM3_DEFAULT_IRQ, irq: IRQ_NONE},
{port: 3, default_irq: COM4_DEFAULT_IRQ, irq: IRQ_NONE}
#else
{0, COM1_DEFAULT_IRQ, IRQ_NONE},
{1, COM2_DEFAULT_IRQ, IRQ_NONE},
{2, COM3_DEFAULT_IRQ, IRQ_NONE},
{3, COM4_DEFAULT_IRQ, IRQ_NONE}
#endif
};
/* ======================================================================== */
/* ====================== INTERRUPT SERVICE ROUTINE ======================= */
/* ======================================================================== */
static void Interrupt com_general_isr(void)
{
serial_struct* com_min = (serial_struct*)(g_comports);
serial_struct* com_max = com_min + COM_MAX;
unsigned char int_id;
unsigned char data;
unsigned char slave_interrupted = 0;
serial_struct* com;
/* Disable IRQs for all COM ports, then re-enable interrupts.
* This allows other faster devices to be serviced.
*/
for(com=com_min;com<=com_max;com++)
{
if(com->open)
{
if(com->irq > 7)
slave_interrupted = 1;
PIC_DISABLE_IRQ(com->irq);
}
}
CPU_ENABLE_INTERRUPTS();
/* Process all pending interrupts */
for(com=com_min;com<=com_max;com++)
{
if(com->open)
{
while((int_id=UART_READ_INTERRUPT_IDENTIFY(com) & UART_IIR_MASK) != UART_IIR_NO_INTERRUPT)
{
switch(int_id)
{
case UART_IIR_DATA_READY:
/* Read all data from the UART */
while(UART_READ_LINE_STATUS(com) & UART_LSR_DATA_READY)
{
data = UART_READ_DATA(com);
/* Handle XON/XOFF flow control (TX) */
if(com->flow_mode == SER_HANDSHAKING_XONXOFF && (data == SER_XOFF || data == SER_XON))
{
com->tx_flow_on = data == SER_XON;
if(!SER_TX_BUFFER_EMPTY(com) && com->tx_flow_on)
UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY);
}
/* Store it if there's room, or throw it out */
else if(!SER_RX_BUFFER_FULL(com))
{
SER_RX_BUFFER_WRITE(com, data);
/* Flow control (RX) - Turn off if buffer almost full */
if(com->rx_flow_on && SER_RX_BUFFER_HIWATER(com))
{
com->rx_flow_on = 0;
switch(com->flow_mode)
{
case SER_HANDSHAKING_RTSCTS:
UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) & ~UART_MCR_RTS);
break;
case SER_HANDSHAKING_NONE:
break;
case SER_HANDSHAKING_XONXOFF:
UART_WRITE_DATA(com, SER_XOFF);
break;
case SER_HANDSHAKING_DTRDSR:
UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) & ~UART_MCR_DTR);
break;
}
}
}
}
break;
/* Change in line status */
case UART_IIR_LINE_STATUS:
UART_READ_LINE_STATUS(com);
break;
/* Change in modem status */
case UART_IIR_MODEM_STATUS:
UART_READ_MODEM_STATUS(com);
/* Handle RTS/CTS or DSR/DTR flow control (TX) */
if(com->flow_mode == SER_HANDSHAKING_RTSCTS)
com->tx_flow_on = (com->msr & UART_MSR_CTS) != 0;
else if(com->flow_mode == SER_HANDSHAKING_DTRDSR)
com->tx_flow_on = (com->msr & UART_MSR_DSR) != 0;
if(!SER_TX_BUFFER_EMPTY(com) && com->tx_flow_on)
UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY);
break;
/* UART is empty */
case UART_IIR_TX_HOLD_EMPTY:
{
int cnt;
for (cnt=0; cnt<UART_FIFO_SIZE_IN_BYTES && com->tx_flow_on && !SER_TX_BUFFER_EMPTY(com); cnt++)
UART_WRITE_DATA(com, SER_TX_BUFFER_READ(com));
if(SER_TX_BUFFER_EMPTY(com) || !com->tx_flow_on)
UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) & ~UART_IER_TX_HOLD_EMPTY);
break;
}
}
}
}
}
CPU_DISABLE_INTERRUPTS();
/* End the interrupt on the PIC */
if(slave_interrupted)
PIC_END_INTERRUPT(PIC_SLAVE);
PIC_END_INTERRUPT(PIC_MASTER);
/* Re-enable all interrupts */
for(com=com_min;com<=com_max;com++)
if(com->open)
PIC_ENABLE_IRQ(com->irq);
/* We must explicitely call 'sti' before 'iret' because 'iret'
won't always restore interrupts in a virtual environment */
CPU_ENABLE_INTERRUPTS();
}
/* ======================================================================== */
/* =========================== UTILITY ROUTINES =========================== */
/* ======================================================================== */
#ifdef PROTECTED_MODE
/* we do not know the exact size of com_general_isr() function
but for sure it's not longer then 2048 bytes */
#define ISR_SIZE 2048
#if defined( __DJGPP__ )
static void serial_dpmi_get_pvect(int vector, _go32_dpmi_seginfo *info)
{
_go32_dpmi_get_protected_mode_interrupt_vector(vector, info);
}
static void serial_dpmi_set_pvect(int vector, _go32_dpmi_seginfo *info)
{
_go32_dpmi_set_protected_mode_interrupt_vector(vector, info);
}
static int serial_dpmi_lock_memory(void)
{
unsigned long dataaddr, codeaddr;
__dpmi_meminfo dataregion, coderegion;
if(__dpmi_get_segment_base_address(_my_cs(), &codeaddr) == 0 &&
__dpmi_get_segment_base_address(_my_ds(), &dataaddr) == 0)
{
coderegion.handle = 0;
coderegion.size = ISR_SIZE;
coderegion.address = codeaddr + (unsigned long)com_general_isr;
dataregion.handle = 0;
dataregion.size = sizeof(g_comports);
dataregion.address = codeaddr + (unsigned long)g_comports;
if(__dpmi_lock_linear_region(&coderegion) == 0)
{
if(__dpmi_lock_linear_region(&dataregion) == 0)
{
g_isr_addr.pm_offset = (unsigned long)com_general_isr;
g_isr_addr.pm_selector = _go32_my_cs();
if(_go32_dpmi_allocate_iret_wrapper(&g_isr_addr) == 0)
return 1;
__dpmi_unlock_linear_region(&dataregion);
}
__dpmi_unlock_linear_region(&coderegion);
}
}
return 0;
}
static void serial_dpmi_unlock_memory(void)
{
unsigned long baseaddr;
__dpmi_meminfo region;
if(__dpmi_get_segment_base_address(_my_ds(), &baseaddr) == 0)
{
region.handle = 0;
region.size = sizeof(g_comports);
region.address = baseaddr + (unsigned long)g_comports;
__dpmi_unlock_linear_region(®ion);
}
if(__dpmi_get_segment_base_address(_my_cs(), &baseaddr) == 0)
{
region.handle = 0;
region.size = ISR_SIZE;
region.address = baseaddr + (unsigned long)com_general_isr;
__dpmi_unlock_linear_region(®ion);
}
_go32_dpmi_free_iret_wrapper(&g_isr_addr);
}
#else /* ! __DJGPP__ */
static void serial_dpmi_get_pvect(int vect, int_handler_ptr *handler)
{
union REGS r;
unsigned short sel;
unsigned long off;
/* DPMI get protected mode interrupt vector: Int 31H, Fn 0204H */
r.x.eax = 0x0204;
r.x.ebx = vect;
int386(0x31, &r, &r);
sel = (unsigned short) r.x.ecx;
off = r.x.edx;
*handler=(int_handler_ptr) MK_FP(sel, off);
}
static void serial_dpmi_set_pvect(int vect, int_handler_ptr *handler)
{
union REGS r;
void Far *ptr;
/* DPMI set protected mode interrupt vector: Int 31H, Fn 0205H */
ptr = (void Far *)*handler;
r.x.eax = 0x0205;
r.x.ebx = vect;
r.x.ecx = FP_SEG(ptr);
r.x.edx = FP_OFF(ptr);
int386(0x31, &r, &r);
}
static int serial_dpmi_lock_linear_memory(void Far *ptr, unsigned long size)
{
union REGS r;
/* DPMI get segment base address: Int 31H, Fn 0006H */
r.x.eax = 0x0006;
r.x.ebx = FP_SEG(ptr);
int386(0x31, &r, &r);
if(r.w.cflag == 0)
{
unsigned long addr = FP_OFF( ptr ) + ((r.w.cx << 16) | r.w.dx);
/* DPMI lock linear region: Int 31H, Fn 0600H */
r.x.eax = 0x0600;
r.x.ebx = addr >> 16;
r.x.ecx = addr & 0xFFFF;
r.x.esi = size >> 16;
r.x.edi = size & 0xFFFF;
int386(0x31, &r, &r);
if(r.w.cflag == 0)
{
g_isr_addr = com_general_isr;
return 1;
}
}
return 0;
}
static void serial_dpmi_unlock_linear_memory(void Far *ptr, unsigned long size)
{
union REGS r;
/* DPMI get segment base address: Int 31H, Fn 0006H */
r.x.eax = 0x0006;
r.x.ebx = FP_SEG(ptr);
int386(0x31, &r, &r);
if(r.w.cflag == 0)
{
unsigned long addr = FP_OFF( ptr ) + ((r.w.cx << 16) | r.w.dx);
/* DPMI unlock linear region: Int 31H, Fn 0601H */
r.x.eax = 0x0601;
r.x.ebx = addr >> 16;
r.x.ecx = addr & 0xFFFF;
r.x.esi = size >> 16;
r.x.edi = size & 0xFFFF;
int386(0x31, &r, &r);
}
int386 (0x31, &r, &r);
}
static int serial_dpmi_lock_memory(void)
{
if(serial_dpmi_lock_linear_memory(com_general_isr, ISR_SIZE))
{
if(serial_dpmi_lock_linear_memory(g_comports, sizeof(g_comports)))
return 1;
serial_dpmi_unlock_linear_memory(com_general_isr, ISR_SIZE);
}
return 0;
}
static void serial_dpmi_unlock_memory(void)
{
serial_dpmi_unlock_linear_memory(com_general_isr, ISR_SIZE);
serial_dpmi_unlock_linear_memory(g_comports, sizeof(g_comports));
}
#endif /* ! __DJGPP__ */
static int serial_install_irqhandler(int irq)
{
/* If we haven't taken this IRQ's ISR already, take it */
if(!g_isrs_taken[irq])
{
if( g_isrs_count++ == 0 )
{
/* lock memory used by interrupt handler in DPMI mode */
if(!serial_dpmi_lock_memory())
{
--g_isrs_count;
return SER_ERR_LOCK_MEM;
}
}
serial_dpmi_get_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_old_isrs[irq]);
serial_dpmi_set_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_isr_addr);
g_isrs_taken[irq] = 1;
}
return SER_SUCCESS;
}
static void serial_remove_irqhandler(int irq)
{
if(g_isrs_taken[irq])
{
serial_dpmi_set_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_old_isrs[irq]);
g_isrs_taken[irq] = 0;
if( --g_isrs_count == 0 )
/* unlock memory used by interrupt handler in DPMI mode */
serial_dpmi_unlock_memory();
}
}
#else /* ! PROTECTED_MODE */
static int serial_install_irqhandler(int irq)
{
/* If we haven't taken this IRQ's ISR already, take it */
if(!g_isrs_taken[irq])
{
g_old_isrs[irq] = _dos_getvect(irq+INTERRUPT_VECTOR_OFFSET);
_dos_setvect(irq+INTERRUPT_VECTOR_OFFSET, com_general_isr);
g_isrs_taken[irq] = 1;
}
return SER_SUCCESS;
}
static void serial_remove_irqhandler(int irq)
{
_dos_setvect(irq+INTERRUPT_VECTOR_OFFSET, g_old_isrs[irq]);
g_isrs_taken[irq] = 0;
}
#endif /* ! PROTECTED_MODE */
static int serial_find_irq(int comport)
{
serial_struct* com = (serial_struct*)(g_comports + comport);
unsigned char imr_m = PIC_READ_IMR(PIC_MASTER); /* Interrupt Mask Registers */
unsigned char imr_s = PIC_READ_IMR(PIC_SLAVE);
unsigned char irr_m; /* Interrupt Request Registers */
unsigned char irr_s;
/* Set up the UART */
UART_WRITE_MODEM_CONTROL(com, UART_MCR_OUT2);
UART_WRITE_FIFO_CONTROL(com, 0);
/* Wait until tx hold register is empty */
while((UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY) == 0);
CPU_DISABLE_INTERRUPTS();
/* Allow any interrupt on PIC */
PIC_WRITE_IMR(PIC_MASTER, 0);
PIC_WRITE_IMR(PIC_SLAVE, 0);
/* Do some initial polls to let things settle (win95 needs this) */
UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY);
PIC_READ_IRR(PIC_MASTER);
PIC_READ_IRR(PIC_SLAVE);
UART_WRITE_INTERRUPT_ENABLE(com, 0);
PIC_READ_IRR(PIC_MASTER);
PIC_READ_IRR(PIC_SLAVE);
/* Generate an interrupt and record all active IRQs */
UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY);
irr_m = PIC_READ_IRR(PIC_MASTER);
irr_s = PIC_READ_IRR(PIC_SLAVE);
/* Remove the interrupt and mask out all IRQs still active */
UART_WRITE_INTERRUPT_ENABLE(com, 0);
irr_m &= ~PIC_READ_IRR(PIC_MASTER);
irr_s &= ~PIC_READ_IRR(PIC_SLAVE);
/* Interrupt again to make sure */
UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY);
irr_m &= PIC_READ_IRR(PIC_MASTER);
irr_s &= PIC_READ_IRR(PIC_SLAVE);
/* Return everything to normal */
PIC_WRITE_IMR(PIC_MASTER, imr_m);
PIC_WRITE_IMR(PIC_SLAVE, imr_s);
UART_WRITE_INTERRUPT_ENABLE(com, 0);
CPU_ENABLE_INTERRUPTS();
switch(irr_m)
{
case 0x01: return 0;
case 0x02: return 1;
case 0x04:
switch(irr_s)
{
case 0x01: return 8;
case 0x02: return 9;
case 0x04: return 10;
case 0x08: return 11;
case 0x10: return 12;
case 0x20: return 13;
case 0x40: return 14;
case 0x80: return 15;
default: return 2;
}
case 0x08: return 3;
case 0x10: return 4;
case 0x20: return 5;
case 0x40: return 6;
case 0x80: return 7;
}
return SER_ERR_IRQ_NOT_FOUND;
}
static void serial_free_irq(int comport)
{
serial_struct* com = (serial_struct*)(g_comports + comport);
serial_struct* com_min = (serial_struct*)(g_comports);
serial_struct* com_max = com_min + COM_MAX;
unsigned int irq = com->irq;
serial_struct* ptr;
if(irq == IRQ_NONE)
return;
CPU_DISABLE_INTERRUPTS();
/* Disable interrupts from the UART */
UART_WRITE_INTERRUPT_ENABLE(com, 0);
UART_WRITE_MODEM_CONTROL(com, UART_MCR_OUT2);
/* Clear the FIFO and rx registers */
UART_WRITE_FIFO_CONTROL(com, 0);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_DATA(com);
UART_READ_INTERRUPT_IDENTIFY(com);
UART_READ_LINE_STATUS(com);
UART_READ_DATA(com);
/* Clear any other possible interrupt causes */
UART_READ_INTERRUPT_IDENTIFY(com);
UART_READ_MODEM_STATUS(com);
UART_READ_INTERRUPT_IDENTIFY(com);
UART_READ_LINE_STATUS(com);
UART_READ_INTERRUPT_IDENTIFY(com);
com->irq = IRQ_NONE;
CPU_ENABLE_INTERRUPTS();
for(ptr=com_min;ptr<=com_max;ptr++)
if(ptr != com && ptr->irq == irq)
return;
/* Disable interrupts from the PIC and restore the old vector */
PIC_DISABLE_IRQ(irq);
serial_remove_irqhandler(irq);
}
/* ======================================================================== */
/* ======================== BASIC SERIAL ROUTINES ========================= */
/* ======================================================================== */
int serial_open(int comport, long bps, int data_bits, char parity, int stop_bits, int handshaking)
{
serial_struct* com = (serial_struct*)(g_comports + comport);
int rc = SER_SUCCESS;
if(comport < COM_MIN || comport > COM_MAX)
return SER_ERR_INVALID_COMPORT;
if(com->open)
return SER_ERR_ALREADY_OPEN;
com->open = 1;
/* Initialize buffers */
com->rx_flow_on = 1;
com->tx_flow_on = 1;
SER_RX_BUFFER_INIT(com);
SER_TX_BUFFER_INIT(com);
/* look in bios tables (0040:0000 - 0040:0006) for com base addresses */
if(serial_set_base(comport, Farpeekw(0x0040, comport<<1)) != SER_SUCCESS)
return SER_ERR_NO_UART;
/* Turn off interrupts from UART */
UART_WRITE_INTERRUPT_ENABLE(com, 0);
/* Auto-detect IRQ if we can */
if((rc=serial_find_irq(comport)) < 0)
rc = com->default_irq;
if((rc=serial_set_irq(comport, rc)) != SER_SUCCESS)
return rc;
/* Turn off interrupts from PIC */
PIC_DISABLE_IRQ(com->irq);
/* Set the comport */
if((rc=serial_set(comport, bps, data_bits, parity, stop_bits, handshaking)) == SER_SUCCESS)
{
UART_WRITE_MODEM_CONTROL(com, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT1 | UART_MCR_OUT2);
rc = serial_set_fifo_threshold(comport, UART_FIFO_DEFAULT_THRESHOLD);
/* Get some info */
UART_READ_LINE_STATUS(com);
UART_READ_MODEM_STATUS(com);
}
/* Re-enable interrupts */
UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_DATA_READY | UART_IER_MODEM_STATUS | UART_IER_ERRORS);
PIC_ENABLE_IRQ(com->irq);
if(rc != SER_SUCCESS) /* We failed to set the comport */
serial_close(comport);
return rc;
}
int serial_close(int comport)
{
serial_struct* com = (serial_struct*)(g_comports + comport);
if(comport < COM_MIN || comport > COM_MAX)
return SER_ERR_INVALID_COMPORT;
if(!com->open)
return SER_ERR_NOT_OPEN;
/* Restore old interrupts */
serial_free_irq(comport);
/* Turn everything off */
UART_WRITE_INTERRUPT_ENABLE(com, 0);
UART_WRITE_MODEM_CONTROL(com, 0);
serial_set_fifo_threshold(comport, 0);
com->open = 0;
return SER_SUCCESS;
}
int serial_read(int comport, char* data, int len)
{
serial_struct* com = (serial_struct*)(g_comports + comport);
int i;
if(comport < COM_MIN || comport > COM_MAX)
return SER_ERR_INVALID_COMPORT;
if(!com->open)
return SER_ERR_NOT_OPEN;
if(data == 0)
return SER_ERR_NULL_PTR;
CPU_DISABLE_INTERRUPTS();
/* Fill in the array given to us */
for(i=0;!SER_RX_BUFFER_EMPTY(com) && i < len;i++)
data[i] = SER_RX_BUFFER_READ(com);
/* Flow control (RX) - Turn on if buffer almost empty */
if(!com->rx_flow_on && SER_RX_BUFFER_LOWATER(com))
{
com->rx_flow_on = 1;
if(com->flow_mode == SER_HANDSHAKING_XONXOFF)
UART_WRITE_DATA(com, SER_XON);
else if(com->flow_mode == SER_HANDSHAKING_RTSCTS)
UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) | UART_MCR_RTS);
else if(com->flow_mode == SER_HANDSHAKING_DTRDSR)
UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) | UART_MCR_DTR);
}
CPU_ENABLE_INTERRUPTS();
return i;
}
int serial_write(int comport, const char* data, int len)
{
serial_struct* com = (serial_struct*)(g_comports + comport);
int i;
if(comport < COM_MIN || comport > COM_MAX)
return SER_ERR_INVALID_COMPORT;
if(!com->open)
return SER_ERR_NOT_OPEN;
if(data == 0)
return SER_ERR_NULL_PTR;
for(i=0;i < len;i++)
{
/* Wait until we can write */
while(!(UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY))
{}
/* Write 1 char */
if(com->tx_flow_on)
UART_WRITE_DATA(com, data[i]);
else
break;
}
return i;
}