xref: /openbmc/qemu/chardev/char-win-stdio.c (revision 8e6fe6b8)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "qemu/module.h"
28 #include "chardev/char-win.h"
29 #include "chardev/char-win-stdio.h"
30 
31 typedef struct {
32     Chardev parent;
33     HANDLE  hStdIn;
34     HANDLE  hInputReadyEvent;
35     HANDLE  hInputDoneEvent;
36     HANDLE  hInputThread;
37     uint8_t win_stdio_buf;
38 } WinStdioChardev;
39 
40 #define WIN_STDIO_CHARDEV(obj)                                          \
41     OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
42 
43 static void win_stdio_wait_func(void *opaque)
44 {
45     Chardev *chr = CHARDEV(opaque);
46     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
47     INPUT_RECORD       buf[4];
48     int                ret;
49     DWORD              dwSize;
50     int                i;
51 
52     ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
53 
54     if (!ret) {
55         /* Avoid error storm */
56         qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
57         return;
58     }
59 
60     for (i = 0; i < dwSize; i++) {
61         KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
62 
63         if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
64             int j;
65             if (kev->uChar.AsciiChar != 0) {
66                 for (j = 0; j < kev->wRepeatCount; j++) {
67                     if (qemu_chr_be_can_write(chr)) {
68                         uint8_t c = kev->uChar.AsciiChar;
69                         qemu_chr_be_write(chr, &c, 1);
70                     }
71                 }
72             }
73         }
74     }
75 }
76 
77 static DWORD WINAPI win_stdio_thread(LPVOID param)
78 {
79     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
80     int                ret;
81     DWORD              dwSize;
82 
83     while (1) {
84 
85         /* Wait for one byte */
86         ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
87 
88         /* Exit in case of error, continue if nothing read */
89         if (!ret) {
90             break;
91         }
92         if (!dwSize) {
93             continue;
94         }
95 
96         /* Some terminal emulator returns \r\n for Enter, just pass \n */
97         if (stdio->win_stdio_buf == '\r') {
98             continue;
99         }
100 
101         /* Signal the main thread and wait until the byte was eaten */
102         if (!SetEvent(stdio->hInputReadyEvent)) {
103             break;
104         }
105         if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
106             != WAIT_OBJECT_0) {
107             break;
108         }
109     }
110 
111     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
112     return 0;
113 }
114 
115 static void win_stdio_thread_wait_func(void *opaque)
116 {
117     Chardev *chr = CHARDEV(opaque);
118     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
119 
120     if (qemu_chr_be_can_write(chr)) {
121         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
122     }
123 
124     SetEvent(stdio->hInputDoneEvent);
125 }
126 
127 static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
128 {
129     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
130     DWORD              dwMode = 0;
131 
132     GetConsoleMode(stdio->hStdIn, &dwMode);
133 
134     if (echo) {
135         SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
136     } else {
137         SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
138     }
139 }
140 
141 static void qemu_chr_open_stdio(Chardev *chr,
142                                 ChardevBackend *backend,
143                                 bool *be_opened,
144                                 Error **errp)
145 {
146     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
147     DWORD              dwMode;
148     int                is_console = 0;
149 
150     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
151     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
152         error_setg(errp, "cannot open stdio: invalid handle");
153         return;
154     }
155 
156     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
157 
158     if (is_console) {
159         if (qemu_add_wait_object(stdio->hStdIn,
160                                  win_stdio_wait_func, chr)) {
161             error_setg(errp, "qemu_add_wait_object: failed");
162             goto err1;
163         }
164     } else {
165         DWORD   dwId;
166 
167         stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
168         stdio->hInputDoneEvent  = CreateEvent(NULL, FALSE, FALSE, NULL);
169         if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
170             || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
171             error_setg(errp, "cannot create event");
172             goto err2;
173         }
174         if (qemu_add_wait_object(stdio->hInputReadyEvent,
175                                  win_stdio_thread_wait_func, chr)) {
176             error_setg(errp, "qemu_add_wait_object: failed");
177             goto err2;
178         }
179         stdio->hInputThread     = CreateThread(NULL, 0, win_stdio_thread,
180                                                chr, 0, &dwId);
181 
182         if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
183             error_setg(errp, "cannot create stdio thread");
184             goto err3;
185         }
186     }
187 
188     dwMode |= ENABLE_LINE_INPUT;
189 
190     if (is_console) {
191         /* set the terminal in raw mode */
192         /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
193         dwMode |= ENABLE_PROCESSED_INPUT;
194     }
195 
196     SetConsoleMode(stdio->hStdIn, dwMode);
197 
198     qemu_chr_set_echo_win_stdio(chr, false);
199 
200     return;
201 
202 err3:
203     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
204 err2:
205     CloseHandle(stdio->hInputReadyEvent);
206     CloseHandle(stdio->hInputDoneEvent);
207 err1:
208     qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
209 }
210 
211 static void char_win_stdio_finalize(Object *obj)
212 {
213     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
214 
215     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
216         CloseHandle(stdio->hInputReadyEvent);
217     }
218     if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
219         CloseHandle(stdio->hInputDoneEvent);
220     }
221     if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
222         TerminateThread(stdio->hInputThread, 0);
223     }
224 }
225 
226 static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
227 {
228     HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
229     DWORD   dwSize;
230     int     len1;
231 
232     len1 = len;
233 
234     while (len1 > 0) {
235         if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
236             break;
237         }
238         buf  += dwSize;
239         len1 -= dwSize;
240     }
241 
242     return len - len1;
243 }
244 
245 static void char_win_stdio_class_init(ObjectClass *oc, void *data)
246 {
247     ChardevClass *cc = CHARDEV_CLASS(oc);
248 
249     cc->open = qemu_chr_open_stdio;
250     cc->chr_write = win_stdio_write;
251     cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
252 }
253 
254 static const TypeInfo char_win_stdio_type_info = {
255     .name = TYPE_CHARDEV_WIN_STDIO,
256     .parent = TYPE_CHARDEV,
257     .instance_size = sizeof(WinStdioChardev),
258     .instance_finalize = char_win_stdio_finalize,
259     .class_init = char_win_stdio_class_init,
260     .abstract = true,
261 };
262 
263 static void register_types(void)
264 {
265     type_register_static(&char_win_stdio_type_info);
266 }
267 
268 type_init(register_types);
269