1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* -*- linux-c -*- ------------------------------------------------------- * |
3 | * |
4 | * Copyright (C) 1991, 1992 Linus Torvalds |
5 | * Copyright 2007 rPath, Inc. - All Rights Reserved |
6 | * Copyright 2009 Intel Corporation; author H. Peter Anvin |
7 | * |
8 | * ----------------------------------------------------------------------- */ |
9 | |
10 | /* |
11 | * Very simple screen and serial I/O |
12 | */ |
13 | |
14 | #include "boot.h" |
15 | |
16 | int early_serial_base; |
17 | |
18 | #define XMTRDY 0x20 |
19 | |
20 | #define TXR 0 /* Transmit register (WRITE) */ |
21 | #define LSR 5 /* Line Status */ |
22 | |
23 | /* |
24 | * These functions are in .inittext so they can be used to signal |
25 | * error during initialization. |
26 | */ |
27 | |
28 | static void __section(".inittext" ) serial_putchar(int ch) |
29 | { |
30 | unsigned timeout = 0xffff; |
31 | |
32 | while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) |
33 | cpu_relax(); |
34 | |
35 | outb(ch, early_serial_base + TXR); |
36 | } |
37 | |
38 | static void __section(".inittext" ) bios_putchar(int ch) |
39 | { |
40 | struct biosregs ireg; |
41 | |
42 | initregs(regs: &ireg); |
43 | ireg.bx = 0x0007; |
44 | ireg.cx = 0x0001; |
45 | ireg.ah = 0x0e; |
46 | ireg.al = ch; |
47 | intcall(int_no: 0x10, ireg: &ireg, NULL); |
48 | } |
49 | |
50 | void __section(".inittext" ) putchar(int ch) |
51 | { |
52 | if (ch == '\n') |
53 | putchar(ch: '\r'); /* \n -> \r\n */ |
54 | |
55 | bios_putchar(ch); |
56 | |
57 | if (early_serial_base != 0) |
58 | serial_putchar(ch); |
59 | } |
60 | |
61 | void __section(".inittext" ) puts(const char *str) |
62 | { |
63 | while (*str) |
64 | putchar(ch: *str++); |
65 | } |
66 | |
67 | /* |
68 | * Read the CMOS clock through the BIOS, and return the |
69 | * seconds in BCD. |
70 | */ |
71 | |
72 | static u8 gettime(void) |
73 | { |
74 | struct biosregs ireg, oreg; |
75 | |
76 | initregs(regs: &ireg); |
77 | ireg.ah = 0x02; |
78 | intcall(int_no: 0x1a, ireg: &ireg, oreg: &oreg); |
79 | |
80 | return oreg.dh; |
81 | } |
82 | |
83 | /* |
84 | * Read from the keyboard |
85 | */ |
86 | int getchar(void) |
87 | { |
88 | struct biosregs ireg, oreg; |
89 | |
90 | initregs(regs: &ireg); |
91 | /* ireg.ah = 0x00; */ |
92 | intcall(int_no: 0x16, ireg: &ireg, oreg: &oreg); |
93 | |
94 | return oreg.al; |
95 | } |
96 | |
97 | static int kbd_pending(void) |
98 | { |
99 | struct biosregs ireg, oreg; |
100 | |
101 | initregs(regs: &ireg); |
102 | ireg.ah = 0x01; |
103 | intcall(int_no: 0x16, ireg: &ireg, oreg: &oreg); |
104 | |
105 | return !(oreg.eflags & X86_EFLAGS_ZF); |
106 | } |
107 | |
108 | void kbd_flush(void) |
109 | { |
110 | for (;;) { |
111 | if (!kbd_pending()) |
112 | break; |
113 | getchar(); |
114 | } |
115 | } |
116 | |
117 | int getchar_timeout(void) |
118 | { |
119 | int cnt = 30; |
120 | int t0, t1; |
121 | |
122 | t0 = gettime(); |
123 | |
124 | while (cnt) { |
125 | if (kbd_pending()) |
126 | return getchar(); |
127 | |
128 | t1 = gettime(); |
129 | if (t0 != t1) { |
130 | cnt--; |
131 | t0 = t1; |
132 | } |
133 | } |
134 | |
135 | return 0; /* Timeout! */ |
136 | } |
137 | |
138 | |