summaryrefslogtreecommitdiffstats
path: root/purgatory/arch/i386/console-x86.c
blob: 9773573623de2da353d1612d59132d18ced09a63 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdint.h>
#include <arch/io.h>
#include <purgatory.h>

/*
 * VGA
 * =============================================================================
 */

/* Simple VGA output */

#define VGABASE		((void *)0xb8000)

#define MAX_YPOS	25
#define MAX_XPOS	80

static int current_ypos = 1, current_xpos = 0; 
uint8_t console_vga = 0;

static void putchar_vga(int ch)
{
	int  i, k, j;
	if (!console_vga)
		return;

	if (current_ypos >= MAX_YPOS) {
		/* scroll 1 line up */
		for (k = 1, j = 0; k < MAX_YPOS; k++, j++) {
			for (i = 0; i < MAX_XPOS; i++) {
				writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
					VGABASE + 2*(MAX_XPOS*j + i));
			}
		}
		for (i = 0; i < MAX_XPOS; i++)
			writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
		current_ypos = MAX_YPOS-1;
	}
	if (ch == '\n') {
		current_xpos = 0;
		current_ypos++;
	} else if (ch != '\r')  {
		writew(((0x7 << 8) | (unsigned short) ch),
			VGABASE + 2*(MAX_XPOS*current_ypos +
				current_xpos++));
		if (current_xpos >= MAX_XPOS) {
			current_xpos = 0;
			current_ypos++;
		}
	}
}

/*
 * Serial
 * =============================================================================
 */

/* Base Address */
uint8_t console_serial = 0;
uint16_t serial_base = 0x3f8; /* TTYS0 */
uint32_t serial_baud = 0;

#define XMTRDY          0x20

#define DLAB		0x80

#define TXR             0       /*  Transmit register (WRITE) */
#define TBR             0       /*  Transmit register (WRITE) */
#define RXR             0       /*  Receive register  (READ)  */
#define IER             1       /*  Interrupt Enable          */
#define IIR             2       /*  Interrupt ID              */
#define FCR             2       /*  FIFO control              */
#define LCR             3       /*  Line control              */
#define MCR             4       /*  Modem control             */
#define LSR             5       /*  Line Status               */
#define MSR             6       /*  Modem Status              */
#define DLL             0       /*  Divisor Latch Low         */
#define DLH             1       /*  Divisor latch High        */

static void serial_init(void)
{
	static int initialized = 0;
	if (!initialized) {
		unsigned lcr;
		outb(0x3, serial_base + LCR);	/* 8n1 */
		outb(0,   serial_base + IER);	/* no interrupt */
		outb(0,   serial_base + FCR);	/* no fifo */
		outb(0x3, serial_base + MCR);	/* DTR + RTS */
		
		lcr = inb(serial_base + LCR); 
		outb(lcr | DLAB, serial_base + LCR); 
		/* By default don't change the serial port baud rate */
		if (serial_baud) {
			unsigned divisor = 115200 / serial_baud; 
			outb(divisor & 0xff, serial_base + DLL); 
			outb((divisor >> 8) & 0xff, serial_base + DLH); 
		}
		outb(lcr & ~DLAB, serial_base + LCR);
		initialized = 1;
	}
}

static void serial_tx_byte(unsigned byte)
{
	/* Ensure the serial port is initialized */
	serial_init();

	/* Wait until I can send a byte */
	while((inb(serial_base + LSR) & 0x20) == 0)
		;
	outb(byte, serial_base + TBR);
	/* Wait until the byte is transmitted */
	while(!(inb(serial_base + LSR) & 0x40))
		;
}

static void putchar_serial(int ch)
{
	if (!console_serial) {
		return;
	}
	if (ch == '\n') {
		serial_tx_byte('\r');
	}
	serial_tx_byte(ch);
}

/* Generic wrapper function */

void putchar(int ch)
{
	putchar_vga(ch);
	putchar_serial(ch);
}