]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/8250/test.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / 8250 / test.c
1 /* test.c
2  *
3  * 8250/16450/16550/16750 serial port UART test program.
4  * (C) 2009-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  *
10  * Compiles for intended target environments:
11  *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box] */
12
13 /* TODO: - Can you transform the IRQ buffering code into some kind of C object that the program can allocate as needed (including what size buffer) so that
14  *         such IRQ enabled code is easily usable in other programming projects?
15  *       - See how well this works on that Saber laptop where the trackball is a serial device on COM2.
16  *       - Add code to show you the state of the line and modem status registers, and twiddle them too.
17  *       - Does this program actually work on... say... the IBM PC/XT you have sitting in the corner?
18  *       - How about that ancient 286 laptop you have? The 386 one? The Compaq elite? */
19
20 /* Warnings regarding this code: The order of operations involved in enabling interrupts is
21  * very important! The 8250/16450/16550A/etc UARTs in PC hardware are just so goddamn finicky
22  * about getting interrupts to fire like they're supposed to that if you change the order of
23  * I/O port operations (even if to a more intuitive order) you will probably break UART
24  * interrupt support on many configurations.
25  *
26  * I must explain why the code enables interrupts the way it does in the specific order:
27  *   1. At the very start, the IRQ is unmasked ahead of time (prior to main loop)
28  *   2. The user hits '6' to enable interrupts
29  *   3. The code sends a specific EOI to the programmable interrupt controller. If we don't do this,
30  *      the Programmable Interrupt Controller may fail to send interrupts in the event the UART
31  *      was already holding the IRQ line high and interrupts will not happen
32  *   4. We set all lower 4 bits of the Interrupt Enable register of the UART so that we're notified
33  *      when (1) data is available (2) the trasmitter buffer is empty (and waiting for more)
34  *      (3 & 4) any change in the line or modem status registers
35  *   5. Experience tells me that if for any reason the UART had events pending, but was interrupted
36  *      for any reason, it might not fire any futher interrupts until someone reads the IIR and
37  *      services each register appropriately. So the UART is never serviced because the IRQ is never
38  *      fired, and the IRQ is never fired because the UART is never serviced. Forcibly read and discard
39  *      all "events" from the UART to clear out that queue and get the UART firing off interrupts once
40  *      again
41  *
42  *   Again: if you change the order of operations there, you will produce code that for one reason or
43  *   another fail to enable UART interrupts on some computers, and will happen to work on other
44  *   computers. Follow the above order strictly, and interrupts will fire like they are supposed to. */
45
46 /* Test scenarios.
47  *
48  *     DB-9 port = Device is a UART with an externally visible 9-pin D-shell connector
49  *     DB-25 port= Device is a UART with an externally visible 25-pin D-shell connector
50  *     Int modem = Device is an internal modem emulating a UART
51  *     IRDA      = Device is an infared port pretending to be a UART
52  *     Emulated  = If platform involves an emulator, the UART is emulated by the software
53  *                 If the platform is actual hardware, then the BIOS or host OS traps I/O to emulate a UART
54  *
55  * Scenario                          I/O works       Bi-dir    Up to    UART loop     Type      Notes
56  *                                 Poll  Interrupt  ectional   baud   Poll Interrupt
57  * --------------------------------------------------------------------------------------------------
58  * Microsoft Virtual PC 2007
59  *     - real & protected mode       Y     Y           Y      115200   N      N       Emulated  Everything works, except loopback is improperly
60  *                                                                                              emulated. What is actually received is the last byte
61  *                                                                                              received, not the one we sent out.
62  *            * Actual transfer speed is limited by the mechanism used to emulate. If using NT pipes like \\.\pipe\dostest the fake UART
63  *              will stall until that pipe can be written by Virtual PC.
64  *            * Works with both traditional and ISA Plug & Play versions of the test program.
65  *            * Prior versions of these comments complained about the Real-mode copy running erratically and crashing.
66  *              It was discovered the CPU detection code was calling a far routine as if near and the incorrect stack
67  *              return caused the erratic behavior (as well as solid hangs on other configurations). Since the fix was
68  *              applied VirtualBox now runs our code flawlessly.
69  *
70  * Oracle VirtualBox 4.0.4
71  *     - real & protected ver        Y     Y           Y      115200   Y      Y       Emulated  Everything works fine. VirtualBox's UART emulation
72  *                                                                                              is very tolerant of errors, perhaps even deceptively
73  *                                                                                              tolerant compared to actual hardware.
74  *            * Works with traditional program. VirtualBox does not emulate ISA Plug & Play.
75  *
76  * Toshiba Satellite Pro 465CDX laptop
77  *     - With COM1 (0x3F8)           Y     Y           Y      115200   Y      ?      DB-9 port  Works fine.
78  *     - With COM2 (0x2F8)           Y     Y           ?      ?        Y      ?      Int modem  Communication works at any baud, Hayes compatible
79  *     - With COM3 (0x3E8)           Y     Y           N      115200   Y      ?      IRDA       Toshiba's BIOS puts this on IRQ 9. The port and IRQ
80  *                                                                                              will only be detected properly by the ISA PnP version.
81  *                                                                                              The traditional version will mis-detect the port IRQ
82  *                                                                                              as 4.
83  *
84  *                                                                                              Bidirectional communications are impossible over IRDA.
85  *                                                                                              If both ends transmit one byte simultaneously the
86  *                                                                                              bits collide and each end receives gibberish.
87  *                                                                                              Perfect transmission is only possible if each end
88  *                                                                                              takes turns transmitting and receiving.
89  *             * DOS extender problem (dos32a): When we enable and hook the UART IRQ the DOS extender
90  *               crashes back to DOS with "Invalid TSS" exception (INT 0x0A). This makes the 32-bit
91  *               protected mode version unusable on Toshiba hardware unless polling is involved.
92  *               For interrupt-enabled testing, you must use the real-mode version.
93  *
94  * Toshiba Libretto laptop
95  *     - With COM1 (0x3F8)           Y     Y           Y      115200   Y      ?      DB-9 port  Actual communication not verified. The port is only
96  *                                                                                              available through the docking station, which I do not
97  *                                                                                              have.
98  *     - With COM2 (0x2F8)           Y     Y           N      115200   Y      ?      IRDA       Bidirectional communications are not possible. Each end
99  *                                                                                              must take turns transmitting and receiving, or else
100  *                                                                                              data becomes corrupt in transmission.
101  *             * DOS extender problem (dos32a): When we enable and hook the UART IRQ the DOS extender
102  *               crashes back to DOS with "Invalid TSS" exception (INT 0x0A). This makes the 32-bit
103  *               protected mode version unusable on Toshiba hardware unless polling is involved.
104  *               For interrupt-enabled testing, you must use the real-mode version.
105  *
106  * Compaq Elite laptop
107  *     - With COM1 (0x3F8)           Y     Y           Y      115200   Y      ?      DB-9 port
108  */
109
110 #include <stdio.h>
111 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
112 #include <stdlib.h>
113 #include <string.h>
114 #include <unistd.h>
115 #include <assert.h>
116 #include <fcntl.h>
117 #include <dos.h>
118
119 #include <hw/cpu/cpu.h>
120 #include <hw/dos/dos.h>
121 #include <hw/8254/8254.h>
122 #include <hw/8259/8259.h>
123 #include <hw/8250/8250.h>
124 #include <hw/dos/doswin.h>
125
126 #if defined(ISAPNP)
127 #include <hw/8250/8250pnp.h>
128 #include <hw/isapnp/isapnp.h>
129 #endif
130
131 /* global variable: the uart object */
132 static struct info_8250 *uart = NULL;
133
134 /* IRQ transfer buffers. Used by interrupt handler to store incoming data. Non-interrupt code should disable interrupts before accessing */
135 #define IRQ_BUFFER_SIZE 512
136
137 static unsigned char irq_buffer[IRQ_BUFFER_SIZE];
138 static unsigned int irq_buffer_i=0,irq_buffer_o=0;
139
140 static unsigned char irq_bufferout[IRQ_BUFFER_SIZE];
141 static unsigned int irq_bufferout_i=0,irq_bufferout_o=0;
142
143 static void irq_buffer_reset() {
144         _cli();
145         irq_buffer_i = irq_buffer_o = 0;
146         irq_bufferout_i = irq_bufferout_o = 0;
147         _sti();
148 }
149
150 /* take any IRQ buffered output and send it.
151  * must be called with interrupts disabled.
152  * may be called from the IRQ handler. */
153 static void irq_uart_xmit_bufferout(struct info_8250 *uart) {
154         unsigned char c;
155
156         if (irq_bufferout_o != irq_bufferout_i) {
157                 /* then send it */
158                 while (uart_8250_can_write(uart) && irq_bufferout_o != irq_bufferout_i) {
159                         c = irq_bufferout[irq_bufferout_o++];
160                         if (irq_bufferout_o >= IRQ_BUFFER_SIZE) irq_bufferout_o = 0;
161                         outp(uart->port+PORT_8250_IO,c);
162                 }
163         }
164 }
165
166 static void irq_uart_recv_buffer(struct info_8250 *uart) {
167         unsigned char c;
168
169         while (uart_8250_can_read(uart)) {
170                 c = inp(uart->port+PORT_8250_IO);
171                 if ((irq_buffer_i+1)%IRQ_BUFFER_SIZE != irq_buffer_o) {
172                         irq_buffer[irq_buffer_i++] = c;
173                         if (irq_buffer_i >= IRQ_BUFFER_SIZE) irq_buffer_i = 0;
174                 }
175         }
176 }
177
178 static int irq_bufferout_count() {
179         int x = irq_bufferout_i - irq_bufferout_o;
180         if (x < 0) x += IRQ_BUFFER_SIZE;
181         return x;
182 }
183
184 /* NOTE: You're supposed to call this function with interrupts disabled,
185  *       or from within an interrupt handler in response to the UART's IRQ signal. */
186 static void irq_uart_handle_iir(struct info_8250 *uart) {
187         unsigned char reason,c,patience = 8;
188
189 #if TARGET_MSDOS == 32
190         (*((unsigned short*)0xB8010))++;
191 #else
192         (*((unsigned short far*)MK_FP(0xB800,0x0010)))++;
193 #endif
194
195         /* why the interrupt? */
196         /* NOTE: we loop a maximum of 8 times in case the UART we're talking to happens
197          *       to be some cheap knockoff chipset that never clears the IIR register
198          *       when all events are read */
199         /* if there was actually an interrupt, then handle it. loop until all interrupt conditions are depleted */
200         while (((reason=inp(uart->port+PORT_8250_IIR)&7)&1) == 0) {
201                 reason >>= 1;
202 #if TARGET_MSDOS == 32
203                 (*((unsigned short*)0xB8000))++;
204 #else
205                 (*((unsigned short far*)MK_FP(0xB800,0x0000)))++;
206 #endif
207
208                 if (reason == 3) { /* line status */
209 #if TARGET_MSDOS == 32
210                         (*((unsigned short*)0xB8008))++;
211 #else
212                         (*((unsigned short far*)MK_FP(0xB800,0x0008)))++;
213 #endif
214
215                         c = inp(uart->port+PORT_8250_LSR);
216                         /* do what you will with this info */
217                 }
218                 else if (reason == 0) { /* modem status */
219 #if TARGET_MSDOS == 32
220                         (*((unsigned short*)0xB8006))++;
221 #else
222                         (*((unsigned short far*)MK_FP(0xB800,0x0006)))++;
223 #endif
224
225                         c = inp(uart->port+PORT_8250_MSR);
226                         /* do what you will with this info */
227                 }
228                 else if (reason == 2) { /* data avail */
229 #if TARGET_MSDOS == 32
230                         (*((unsigned short*)0xB8002))++;
231 #else
232                         (*((unsigned short far*)MK_FP(0xB800,0x0002)))++;
233 #endif
234
235                         irq_uart_recv_buffer(uart);
236                 }
237                 else if (reason == 1) { /* transmit empty */
238 #if TARGET_MSDOS == 32
239                         (*((unsigned short*)0xB8004))++;
240 #else
241                         (*((unsigned short far*)MK_FP(0xB800,0x0004)))++;
242 #endif
243
244                         irq_uart_xmit_bufferout(uart);
245                 }
246
247                 if (--patience == 0) {
248 #if TARGET_MSDOS == 32
249                         (*((unsigned short*)0xB800A))++;
250 #else
251                         (*((unsigned short far*)MK_FP(0xB800,0x000A)))++;
252 #endif
253                         break;
254                 }
255         }
256 }
257
258 static void (interrupt *old_irq)() = NULL;
259 static void interrupt uart_irq() {
260         /* clear interrupts, just in case. NTS: the nature of interrupt handlers
261          * on the x86 platform (IF in EFLAGS) ensures interrupts will be reenabled on exit */
262         _cli();
263         irq_uart_handle_iir(uart);
264
265         /* ack PIC */
266         if (uart->irq >= 8) p8259_OCW2(8,P8259_OCW2_NON_SPECIFIC_EOI);
267         p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
268 }
269
270 static void change_config(struct info_8250 *uart) {
271         int done=0,c;
272         uint8_t by,t2;
273
274         while (!done) {
275                 {
276                         unsigned long baud=0;
277                         unsigned char bits=0,stop_bits=0,parity=0;
278                         uart_8250_get_config(uart,&baud,&bits,&stop_bits,&parity);
279                         printf("State: %lu baud %u-bit %u stop bits %s\n",baud,bits,stop_bits,type_8250_parity(parity));
280                         printf("1/!. Baud rate inc/dec  2/@. Bits inc/dec  3. Stop bits  4. Parity\n");
281                         printf("? "); fflush(stdout);
282                 }
283
284                 c = getch();
285                 printf("\n");
286                 if (c == 27) {
287                         done=1;
288                         break;
289                 }
290                 else if (c == '1' || c == '!') {
291                         unsigned long rate = 9600;
292                         printf("new rate? "); fflush(stdout);
293                         scanf("%lu",&rate);
294                         uart_8250_set_baudrate(uart,uart_8250_baud_to_divisor(uart,rate));
295                 }
296                 else if (c == '2' || c == '@') {
297                         by = inp(uart->port+PORT_8250_LCR);
298                         if (c == '2')   by = (by & (~3)) | (((by & 3) + 3) & 3);
299                         else            by = (by & (~3)) | (((by & 3) + 1) & 3);
300                         outp(uart->port+PORT_8250_LCR,by);
301                 }
302                 else if (c == '3' || c == '#') {
303                         by = inp(uart->port+PORT_8250_LCR);
304                         by ^= 4;
305                         outp(uart->port+PORT_8250_LCR,by);
306                 }
307                 else if (c == '4' || c == '$') {
308                         by = inp(uart->port+PORT_8250_LCR);
309                         t2 = (by >> 3) & 7;
310                         if (t2 == 0)            t2 = 1;
311                         else if (t2 == 1)       t2 = 3;
312                         else if (t2 >= 3)       t2 += 2;
313                         if (t2 >= 8) t2 = 0;
314                         by &= ~(7 << 3);
315                         by |= t2 << 3;
316                         outp(uart->port+PORT_8250_LCR,by);
317                 }
318         }
319 }
320
321 static void config_fifo(struct info_8250 *uart) {
322         uint8_t fcr=0;
323         int done=0,c;
324
325         if (uart->type <= TYPE_8250_IS_16550) {
326                 printf("Your UART (as far as I know) does not have a FIFO\n");
327                 return;
328         }
329
330         while (!done) {
331                 printf("FCR: Enable=%u mode=%u 64byte=%u recv_trigger_level=%u\n",
332                         (fcr & 1) ? 1 : 0,
333                         (fcr & 8) ? 1 : 0,
334                         (fcr & 32) ? 1 : 0,
335                         fcr >> 6);
336                 printf("1. Flush & enable/disable     2. Mode       3. 64byte    4. Level\n");
337                 printf("? "); fflush(stdout);
338
339                 c = getch();
340                 printf("\n");
341                 if (c == 27) {
342                         done=1;
343                         break;
344                 }
345                 else if (c == '1') {
346                         fcr ^= 1;
347                         uart_8250_set_FIFO(uart,fcr);
348                 }
349                 else if (c == '2') {
350                         fcr ^= 1 << 3;
351                         uart_8250_set_FIFO(uart,fcr);
352                 }
353                 else if (c == '3') {
354                         fcr ^= 1 << 5;
355                         uart_8250_set_FIFO(uart,fcr);
356                 }
357                 else if (c == '4') {
358                         fcr += 1 << 6;
359                         uart_8250_set_FIFO(uart,fcr);
360                 }
361         }
362 }
363
364 void irq_bufferout_write(unsigned char c) {
365         _cli();
366         while (((irq_bufferout_i+1)%IRQ_BUFFER_SIZE) == irq_bufferout_o) {
367                 /* try to force transmit interrupt that gets the character out the door */
368                 uart_toggle_xmit_ien(uart);
369                 _sti();
370                 /* IODELAY during interrupt enable. Give the UART's interrupt a chance to interrupt the CPU */
371                 inp(uart->port+PORT_8250_MCR); /* iodelay */
372                 inp(uart->port+PORT_8250_MCR); /* iodelay */
373                 inp(uart->port+PORT_8250_MCR); /* iodelay */
374                 inp(uart->port+PORT_8250_MCR); /* iodelay */
375                 _cli();
376         }
377
378         /* put it in the buffer */
379         irq_bufferout[irq_bufferout_i++] = c;
380         if (irq_bufferout_i >= IRQ_BUFFER_SIZE) irq_bufferout_i = 0;
381         if (irq_bufferout_count() == 1) uart_toggle_xmit_ien(uart);
382         _sti();
383 }
384
385 static void show_console(struct info_8250 *uart) {
386         static const char *msg = "Testing testing 1... 2... 3... 01234567890123456789.\r\n"
387                 "The big dog jumped the shark or something like that.\r\n";
388         unsigned char pc=0,seqmatch=0,xmitseq=0,xmitbyte=0;
389         const size_t msg_len = strlen(msg);
390         unsigned int patience;
391         char stuck_xmit=0;
392         int c;
393
394         printf("Incoming data will be printed out, and data you type will xmit (port=%X)\n",uart->port);
395         printf("Tap ESC twice to exit. '~' = type out a predefined message.\n");
396         printf("SHIFT + ~ to rapidly transmit the message.\n");
397         printf("Type CTRL+A to initiate sequential byte test.\n");
398
399         if (use_8250_int) irq_buffer_reset();
400
401         while (1) {
402                 if (kbhit()) {
403                         c = getch();
404                         if (c == 27) {
405                                 c = getch();
406                                 if (c == 27) break;
407                         }
408                         if (c == '`') {
409                                 stuck_xmit = !stuck_xmit;
410                         }
411                         else if (c == 1) { /* CTRL+S */
412                                 xmitseq = !xmitseq;
413                         }
414                         else if (c == '~') {
415                                 const char *p = msg;
416                                 while (*p != 0) {
417                                         c = *p++;
418                                         if (use_8250_int) {
419                                                 irq_bufferout_write(c);
420                                         }
421                                         else {
422                                                 while (!uart_8250_can_write(uart));
423                                                 uart_8250_write(uart,(uint8_t)c);
424                                         }
425                                 }
426                         }
427                         else if (c == 13) {
428                                 if (use_8250_int) {
429                                         irq_bufferout_write(13);
430                                         irq_bufferout_write(10);
431                                 }
432                                 else {
433                                         while (!uart_8250_can_write(uart));
434                                         uart_8250_write(uart,13);
435                                         while (!uart_8250_can_write(uart));
436                                         uart_8250_write(uart,10);
437                                 }
438                                 printf("\n");
439                         }
440                         else if (c == 10) {
441                                 /* ignore */
442                         }
443                         else {
444                                 if (use_8250_int) {
445                                         irq_bufferout_write(c);
446                                 }
447                                 else {
448                                         while (!uart_8250_can_write(uart));
449                                         uart_8250_write(uart,(uint8_t)c);
450                                 }
451                                 fwrite(&c,1,1,stdout); fflush(stdout);
452                         }
453                 }
454
455                 if (use_8250_int) {
456                         int sm=0;
457                         /* the interrupt handler takes care of reading the data in (in bursts, if necessary).
458                          * our job is to follow the buffer. Note we have a "patience" parameter to break out
459                          * of the loop in cases where fast continious transmission prevents us from ever
460                          * emptying the buffer entirely. */
461                         _cli();
462                         patience = msg_len;
463                         while (patience-- != 0 && irq_buffer_o != irq_buffer_i) {
464                                 c = irq_buffer[irq_buffer_o++];
465                                 if (irq_buffer_o >= IRQ_BUFFER_SIZE) irq_buffer_o = 0;
466                                 _sti();
467
468                                 if (seqmatch >= 16) {
469                                         if (((pc+1)&0xFF) != c) {
470                                                 if (sm++ == 0)
471                                                         printf("Sequential byte error %u -> %u\n",pc,c);
472                                         }
473                                         else if (c == 0) {
474                                                 printf(":)"); fflush(stdout);
475                                         }
476                                 }
477                                 else {
478                                         if (c == 13) {
479                                                 printf("\n");
480                                         }
481                                         else {
482                                                 fwrite(&c,1,1,stdout); fflush(stdout);
483                                         }
484                                 }
485
486                                 if (((pc+1)&0xFF) == c) {
487                                         if (seqmatch < 255) seqmatch++;
488                                 }
489                                 else if (seqmatch > 0) {
490                                         seqmatch--;
491                                 }
492                                 pc = c;
493                         }
494                         _sti();
495                 }
496                 else {
497                         while (uart_8250_can_read(uart)) {
498                                 c = uart_8250_read(uart);
499                                 if (seqmatch >= 16) {
500                                         if (((pc+1)&0xFF) != c) {
501                                                 printf("Sequential byte error %u -> %u\n",pc,c);
502                                         }
503                                         else if (c == 0) {
504                                                 printf(":)"); fflush(stdout);
505                                         }
506                                 }
507                                 else {
508                                         if (c == 13) {
509                                                 printf("\n");
510                                         }
511                                         else {
512                                                 fwrite(&c,1,1,stdout); fflush(stdout);
513                                         }
514
515                                 }
516
517                                 if (((pc+1)&0xFF) == c) {
518                                         if (seqmatch < 255) seqmatch++;
519                                 }
520                                 else if (seqmatch > 0) {
521                                         seqmatch--;
522                                 }
523                                 pc = c;
524                         }
525                 }
526
527                 if (xmitseq) {
528                         unsigned int c = 0;
529                         for (c=0;c < 0x40;c++) {
530                                 if (use_8250_int) {
531                                         irq_bufferout_write(xmitbyte++);
532                                 }
533                                 else {
534                                         if (!uart_8250_can_write(uart)) break;
535                                         uart_8250_write(uart,xmitbyte++);
536                                 }
537                         } 
538                 }
539                 else if (stuck_xmit) {
540                         const char *p = msg;
541                         while (*p != 0) {
542                                 c = *p++;
543                                 if (use_8250_int) {
544                                         irq_bufferout_write(c);
545                                 }
546                                 else {
547                                         while (!uart_8250_can_write(uart));
548                                         uart_8250_write(uart,(uint8_t)c);
549                                 }
550                         }
551                 }
552         }
553
554         printf("\nDONE\n");
555 }
556
557 #ifdef ISAPNP
558 static unsigned char devnode_raw[4096];
559
560 void pnp_serial_scan() {
561         /* most of the time the serial ports are BIOS controlled and on the motherboard.
562          * they usually don't even show up in a PnP isolation scan. so we have to use
563          * the "get device nodes" functions of the PnP BIOS. */
564         {
565                 struct isa_pnp_device_node far *devn;
566                 unsigned int ret_ax,nodesize=0xFFFF;
567                 unsigned char numnodes=0xFF;
568                 struct isapnp_tag tag;
569                 unsigned char node;
570
571                 printf("Enumerating PnP system device nodes...\n");
572
573                 ret_ax = isa_pnp_bios_number_of_sysdev_nodes(&numnodes,&nodesize);
574                 if (ret_ax == 0 && numnodes != 0xFF && nodesize < sizeof(devnode_raw)) {
575                         /* NTS: How nodes are enumerated in the PnP BIOS: set node = 0, pass address of node
576                          *      to BIOS. BIOS, if it returns node information, will also overwrite node with
577                          *      the node number of the next node, or with 0xFF if this is the last one.
578                          *      On the last one, stop enumerating. */
579                         for (node=0;node != 0xFF;) {
580                                 unsigned char far *rsc;
581                                 int port = -1;
582                                 int irq = -1;
583
584                                 /* apparently, start with 0. call updates node to
585                                  * next node number, or 0xFF to signify end */
586                                 ret_ax = isa_pnp_bios_get_sysdev_node(&node,devnode_raw,
587                                                 ISA_PNP_BIOS_GET_SYSDEV_NODE_CTRL_NOW);
588
589                                 if (ret_ax != 0)
590                                         break;
591
592                                 devn = (struct isa_pnp_device_node far*)devnode_raw;
593                                 if (!is_rs232_or_compat_pnp_device(devn))
594                                         continue;
595
596                                 /* there are three config blocks, one after the other.
597                                  *  [allocated]
598                                  *  [possible]
599                                  *  [??]
600                                  * since we're not a configuration utility, we only care about the first one */
601                                 rsc = devnode_raw + sizeof(*devn);
602                                 if (isapnp_read_tag(&rsc,devnode_raw + devn->size,&tag)) {
603                                         do {
604                                                 if (tag.tag == ISAPNP_TAG_END) /* end tag */
605                                                         break;
606
607                                                 switch (tag.tag) {
608 /*---------------------------------------------------------------------------------*/
609 case ISAPNP_TAG_IRQ_FORMAT: {
610         struct isapnp_tag_irq_format far *x = (struct isapnp_tag_irq_format far*)tag.data;
611         unsigned int i;
612         for (i=0;i < 16 && irq < 0;i++) {
613                 if (x->irq_mask & (1U << (unsigned int)i))
614                         irq = i;
615         }
616 } break;
617 case ISAPNP_TAG_IO_PORT: {
618         struct isapnp_tag_io_port far *x = (struct isapnp_tag_io_port far*)tag.data;
619         if (x->length >= 8 && port < 0) port = x->min_range;
620 } break;
621 case ISAPNP_TAG_FIXED_IO_PORT: {
622         struct isapnp_tag_fixed_io_port far *x = (struct isapnp_tag_fixed_io_port far*)tag.data;
623         if (x->length >= 8 && port < 0) port = x->base;
624 } break;
625 /*---------------------------------------------------------------------------------*/
626                                                 };
627                                         } while (isapnp_read_tag(&rsc,devnode_raw + devn->size,&tag));
628                                 }
629
630                                 if (port < 0)
631                                         continue;
632
633                                 if (add_pnp_8250(port,irq))
634                                         printf("Found PnP port @ 0x%03x IRQ %d\n",port,irq);
635                         }
636                 }
637         }
638 }
639 #endif
640
641 int main() {
642         unsigned char msr_redraw = 1;
643         unsigned char redraw = 1;
644         unsigned char p_msr = 0;
645         int i,die,choice;
646
647         printf("8250/16450/16550 test program\n");
648 #ifdef ISAPNP
649         printf("ISA Plug & Play version\n");
650 #endif
651
652         cpu_probe();            /* ..for the DOS probe routine */
653         probe_dos();            /* ..for the Windows detection code */
654         detect_windows();       /* Windows virtualizes the COM ports, and we don't want probing to occur to avoid any disruption */
655
656         if (!probe_8254()) {
657                 printf("8254 not found (I need this for time-sensitive portions of the driver)\n");
658                 return 1;
659         }
660
661         if (!probe_8259()) {
662                 printf("8259 not found (I need this for portions of the test involving serial interrupts)\n");
663                 return 1;
664         }
665
666         if (!init_8250()) {
667                 printf("Cannot init 8250 library\n");
668                 return 1;
669         }
670
671 #if defined(ISAPNP)
672         if (!init_isa_pnp_bios()) {
673                 printf("Cannot init ISA PnP\n");
674                 return 1;
675         }
676         if (find_isa_pnp_bios()) {
677                 pnp_serial_scan();
678         }
679         else {
680                 printf("Warning, ISA PnP BIOS not found\n");
681         }
682 #else
683         printf("%u BIOS I/O ports listed\nThey are: ",(unsigned int)bios_8250_ports);
684         for (i=0;i < (int)bios_8250_ports;i++) printf("0x%04x ",get_8250_bios_port(i));
685         printf("\n");
686
687         printf("Now probing ports: "); fflush(stdout);
688         for (i=0;!base_8250_full() && i < (int)bios_8250_ports;i++) {
689                 const uint16_t port = get_8250_bios_port(i);
690                 if (port == 0) continue;
691                 printf("0x%03X ",port); fflush(stdout);
692                 if (probe_8250(port)) printf("[OK] ");
693         }
694         if (windows_mode == WINDOWS_NONE || windows_mode == WINDOWS_REAL) {
695                 /* if we're running under Windows it's likely the kernel is virtualizing the ports. play it safe and
696                  * stick to the "BIOS" ports that Windows and it's virtualization likely added to the data area */
697                 for (i=0;!base_8250_full() && i < (int)(sizeof(standard_8250_ports)/sizeof(standard_8250_ports[0]));i++) {
698                         const uint16_t port = standard_8250_ports[i];
699                         if (port == 0) continue;
700                         printf("0x%03X ",port); fflush(stdout);
701                         if (probe_8250(port)) printf("[OK] ");
702                 }
703         }
704         printf("\n");
705 #endif
706
707         for (i=0;i < base_8250_ports;i++) {
708                 struct info_8250 *inf = &info_8250_port[i];
709                 printf("[%u] @ %03X (type %s IRQ %d)\n",i+1,inf->port,type_8250_to_str(inf->type),inf->irq);
710         }
711         printf("Choice? "); fflush(stdout);
712         choice = -1;
713         scanf("%d",&choice);
714         choice--;
715         if (choice < 0 || choice >= base_8250_ports) return 0;
716         uart = &info_8250_port[choice];
717
718         if (uart->irq != -1) {
719                 old_irq = _dos_getvect(irq2int(uart->irq));
720                 _dos_setvect(irq2int(uart->irq),uart_irq);
721                 if (uart->irq >= 0) {
722                         p8259_unmask(uart->irq);
723                         if (uart->irq >= 8) p8259_OCW2(8,P8259_OCW2_SPECIFIC_EOI | (uart->irq&7));
724                         p8259_OCW2(0,P8259_OCW2_SPECIFIC_EOI | (uart->irq&7));
725                 }
726         }
727
728         uart_8250_enable_interrupt(uart,0);     /* disable interrupts (set IER=0) */
729         uart_8250_disable_FIFO(uart);
730         uart_8250_set_MCR(uart,3);              /* turn on RTS and DTS */
731         uart_8250_set_line_control(uart,UART_8250_LCR_8BIT | UART_8250_LCR_PARITY);     /* 8 bit 1 stop bit odd parity */
732         uart_8250_set_baudrate(uart,uart_8250_baud_to_divisor(uart,9600));
733
734         for (die=0;die == 0;) {
735                 unsigned char msr = uart_8250_read_MSR(uart);
736
737                 if (redraw) {
738                         unsigned long baud=0;
739                         unsigned char bits=0,stop_bits=0,parity=0;
740                         unsigned char mcr = uart_8250_read_MCR(uart);
741
742                         redraw = 0;
743                         msr_redraw = 1;
744
745                         uart_8250_get_config(uart,&baud,&bits,&stop_bits,&parity);
746                         printf("\x0D");
747                         printf("State: %lu baud %u-bit %u stop bits %s DTR=%u RTS=%u LOOP=%u\n",baud,bits,stop_bits,type_8250_parity(parity),
748                                 (mcr & 1) ? 1 : 0/*DTR*/,
749                                 (mcr & 2) ? 1 : 0/*RTS*/,
750                                 (mcr & 16) ? 1 : 0/*LOOP*/);
751                         printf("1. Change config   2. Toggle DTR  3. Toggle RTS   4. Show on console\n");
752                         printf("5. Config FIFO     ");
753                         if (use_8250_int)       printf("6. To poll IO  ");
754                         else                    printf("6. To int IO   ");
755                         printf("7. Toggle LOOP ");
756                         printf("\n");
757                 }
758
759                 if ((msr ^ p_msr) & 0xF0) msr_redraw = 1;
760                 p_msr = msr;
761
762                 if (msr_redraw) {
763                         msr_redraw = 0;
764                         printf( "\x0D"
765                                 "CTS=%u DSR=%u RI=%u CD=%u ? ",
766                                 (msr & 16) ? 1 : 0,
767                                 (msr & 32) ? 1 : 0,
768                                 (msr & 64) ? 1 : 0,
769                                 (msr & 128) ? 1 : 0);
770                         fflush(stdout);
771                 }
772
773                 if (kbhit()) {
774                         choice = getch();
775                         if (choice == 27) {
776                                 die++;
777                                 break;
778                         }
779                         else if (choice == '1') {
780                                 change_config(uart);
781                                 redraw = 1;
782                         }
783                         else if (choice == '2') {
784                                 uart_8250_set_MCR(uart,uart_8250_read_MCR(uart) ^ 1);
785                                 redraw = 1;
786                         }
787                         else if (choice == '3') {
788                                 uart_8250_set_MCR(uart,uart_8250_read_MCR(uart) ^ 2);
789                                 redraw = 1;
790                         }
791                         else if (choice == '7') {
792                                 uart_8250_set_MCR(uart,uart_8250_read_MCR(uart) ^ 16);
793                                 redraw = 1;
794                         }
795                         else if (choice == '4') {
796                                 show_console(uart);
797                                 redraw = 1;
798                         }
799                         else if (choice == '5') {
800                                 config_fifo(uart);
801                                 redraw = 1;
802                         }
803                         else if (choice == '6') {
804                                 /* NTS: Apparently a lot of hardware has problems with just turning on interrupts.
805                                  *      So do everything we can to fucking whip the UART into shape. Drain & reset
806                                  *      it's FIFO. Clear all interrupt events. Unmask the IRQ. Enable all interrupt
807                                  *      events. What ever it fucking takes */
808                                 _cli();
809
810                                 /* ACK the IRQ to ensure the PIC will send more. Doing this resolves a bug on
811                                  * a Toshiba 465CDX and IBM NetVista where enabling interrupts would seem to
812                                  * "hang" the machine (when in fact it was just the PIC not firing interrupts
813                                  * and therefore preventing the keyboard and timer from having their interrupts
814                                  * serviced) */
815                                 if (uart->irq >= 8) p8259_OCW2(8,P8259_OCW2_SPECIFIC_EOI | (uart->irq&7));
816                                 p8259_OCW2(0,P8259_OCW2_SPECIFIC_EOI | (uart->irq&7));
817                                 redraw = 1;
818
819                                 use_8250_int = !use_8250_int;
820                                 if (use_8250_int) uart_8250_enable_interrupt(uart,0xF);
821                                 else uart_8250_enable_interrupt(uart,0x0);
822                                 for (i=0;i < 256 && (inp(uart->port+PORT_8250_IIR) & 1) == 0;i++) {
823                                         inp(uart->port);
824                                         inp(uart->port+PORT_8250_MSR);
825                                         inp(uart->port+PORT_8250_MCR);
826                                         inp(uart->port+PORT_8250_LSR);
827                                         inp(uart->port+PORT_8250_IIR);
828                                         inp(uart->port+PORT_8250_IER);
829                                 }
830                                 if (i == 256) printf("Warning: Unable to clear UART interrupt conditions\n");
831                                 _sti();
832                         }
833                 }
834         }
835
836         uart_8250_enable_interrupt(uart,0);     /* disable interrupts (set IER=0) */
837         uart_8250_set_MCR(uart,0);              /* RTS/DTR and aux lines off */
838         uart_8250_disable_FIFO(uart);
839         uart_8250_set_line_control(uart,UART_8250_LCR_8BIT | UART_8250_LCR_PARITY);     /* 8 bit 1 stop bit odd parity */
840         uart_8250_set_baudrate(uart,uart_8250_baud_to_divisor(uart,9600));
841
842         if (uart->irq != -1) {
843                 _dos_setvect(irq2int(uart->irq),old_irq);
844                 if (uart->irq >= 0) p8259_mask(uart->irq);
845         }
846
847         return 0;
848 }
849