summaryrefslogtreecommitdiff
path: root/com32/lib/vdprintf.c
blob: d74f27820620d38b9c69302563e7b923a44378fb (plain)
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
/*
 * vdprintf.c
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/io.h>
#include <sys/cpu.h>

#undef DEBUG
#define DEBUG 1
#include <dprintf.h>

#define BUFFER_SIZE	4096

enum serial_port_regs {
    THR = 0,
    RBR = 0,
    DLL = 0,
    DLM = 1,
    IER = 1,
    IIR = 2,
    FCR = 2,
    LCR = 3,
    MCR = 4,
    LSR = 5,
    MSR = 6,
    SCR = 7,
};

#ifndef DEBUG_PORT
# define DEBUG_PORT 0x03f8	/* I/O base address */
#endif

static const uint16_t debug_base = DEBUG_PORT;

static void debug_putc(char c)
{
    if (c == '\n')
	debug_putc('\r');

    while ((inb(debug_base + LSR) & 0x20) == 0)
	cpu_relax();
    outb(c, debug_base + THR);
}

void vdprintf(const char *format, va_list ap)
{
    int rv;
    char buffer[BUFFER_SIZE];
    char *p;
    static bool debug_init = false;
    static bool debug_ok   = false;

    rv = vsnprintf(buffer, BUFFER_SIZE, format, ap);

    if (rv < 0)
	return;

    if (rv > BUFFER_SIZE - 1)
	rv = BUFFER_SIZE - 1;

    /*
     * This unconditionally outputs to a serial port at 0x3f8 regardless of
     * if one is enabled or not (this means we don't have to enable the real
     * serial console and therefore get conflicting output.)
     */
    if (__unlikely(!debug_init)) {
	uint8_t dll, dlm, lcr;

	debug_init = true;

	cli();

	/* Initialize the serial port to 115200 n81 with FIFOs enabled */
	outb(0x83, debug_base + LCR);
	outb(0x01, debug_base + DLL);
	outb(0x00, debug_base + DLM);
	(void)inb(debug_base + IER);	/* Synchronize */
	dll = inb(debug_base + DLL);
	dlm = inb(debug_base + DLM);
	lcr = inb(debug_base + LCR);
	
	outb(0x03, debug_base + LCR);
	(void)inb(debug_base + IER);	/* Synchronize */

	outb(0x00, debug_base + IER);
	(void)inb(debug_base + IER);	/* Synchronize */

	sti();

	if (dll != 0x01 || dlm != 0x00 || lcr != 0x83) {
	    /* No serial port present */
	    return;
	}

	outb(0x01, debug_base + FCR);
	(void)inb(debug_base + IER);	/* Synchronize */
	if (inb(debug_base + IIR) < 0xc0) {
	    outb(0x00, debug_base + FCR); /* Disable non-functional FIFOs */
	    (void)inb(debug_base + IER);	/* Synchronize */
	}

	debug_ok = true;
    }

    if (!debug_ok)
	return;

    p = buffer;
    while (rv--)
	debug_putc(*p++);
}