xref: /openbmc/qemu/chardev/char-win-stdio.c (revision 3cce8bd4d737f2ca688bbdcb92cd5cc683245bbd)
10e58f177SMarc-André Lureau /*
20e58f177SMarc-André Lureau  * QEMU System Emulator
30e58f177SMarc-André Lureau  *
40e58f177SMarc-André Lureau  * Copyright (c) 2003-2008 Fabrice Bellard
50e58f177SMarc-André Lureau  *
60e58f177SMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
70e58f177SMarc-André Lureau  * of this software and associated documentation files (the "Software"), to deal
80e58f177SMarc-André Lureau  * in the Software without restriction, including without limitation the rights
90e58f177SMarc-André Lureau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100e58f177SMarc-André Lureau  * copies of the Software, and to permit persons to whom the Software is
110e58f177SMarc-André Lureau  * furnished to do so, subject to the following conditions:
120e58f177SMarc-André Lureau  *
130e58f177SMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
140e58f177SMarc-André Lureau  * all copies or substantial portions of the Software.
150e58f177SMarc-André Lureau  *
160e58f177SMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170e58f177SMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180e58f177SMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
190e58f177SMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200e58f177SMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
210e58f177SMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
220e58f177SMarc-André Lureau  * THE SOFTWARE.
230e58f177SMarc-André Lureau  */
240b8fa32fSMarkus Armbruster 
250e58f177SMarc-André Lureau #include "qemu/osdep.h"
260e58f177SMarc-André Lureau #include "qapi/error.h"
27db725815SMarkus Armbruster #include "qemu/main-loop.h"
280b8fa32fSMarkus Armbruster #include "qemu/module.h"
298228e353SMarc-André Lureau #include "chardev/char-win.h"
308228e353SMarc-André Lureau #include "chardev/char-win-stdio.h"
31db1015e9SEduardo Habkost #include "qom/object.h"
320e58f177SMarc-André Lureau 
33db1015e9SEduardo Habkost struct WinStdioChardev {
340e58f177SMarc-André Lureau     Chardev parent;
350e58f177SMarc-André Lureau     HANDLE  hStdIn;
36*903cc9e1Ssongziming     DWORD   dwOldMode;
370e58f177SMarc-André Lureau     HANDLE  hInputReadyEvent;
380e58f177SMarc-André Lureau     HANDLE  hInputDoneEvent;
390e58f177SMarc-André Lureau     HANDLE  hInputThread;
400e58f177SMarc-André Lureau     uint8_t win_stdio_buf;
41db1015e9SEduardo Habkost };
42db1015e9SEduardo Habkost typedef struct WinStdioChardev WinStdioChardev;
430e58f177SMarc-André Lureau 
DECLARE_INSTANCE_CHECKER(WinStdioChardev,WIN_STDIO_CHARDEV,TYPE_CHARDEV_WIN_STDIO)448110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(WinStdioChardev, WIN_STDIO_CHARDEV,
458110fa1dSEduardo Habkost                          TYPE_CHARDEV_WIN_STDIO)
460e58f177SMarc-André Lureau 
470e58f177SMarc-André Lureau static void win_stdio_wait_func(void *opaque)
480e58f177SMarc-André Lureau {
490e58f177SMarc-André Lureau     Chardev *chr = CHARDEV(opaque);
500e58f177SMarc-André Lureau     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
510e58f177SMarc-André Lureau     INPUT_RECORD       buf[4];
520e58f177SMarc-André Lureau     int                ret;
530e58f177SMarc-André Lureau     DWORD              dwSize;
540e58f177SMarc-André Lureau     int                i;
550e58f177SMarc-André Lureau 
560e58f177SMarc-André Lureau     ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
570e58f177SMarc-André Lureau 
580e58f177SMarc-André Lureau     if (!ret) {
590e58f177SMarc-André Lureau         /* Avoid error storm */
600e58f177SMarc-André Lureau         qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
610e58f177SMarc-André Lureau         return;
620e58f177SMarc-André Lureau     }
630e58f177SMarc-André Lureau 
640e58f177SMarc-André Lureau     for (i = 0; i < dwSize; i++) {
650e58f177SMarc-André Lureau         KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent;
660e58f177SMarc-André Lureau 
670e58f177SMarc-André Lureau         if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) {
680e58f177SMarc-André Lureau             int j;
690e58f177SMarc-André Lureau             if (kev->uChar.AsciiChar != 0) {
700e58f177SMarc-André Lureau                 for (j = 0; j < kev->wRepeatCount; j++) {
710e58f177SMarc-André Lureau                     if (qemu_chr_be_can_write(chr)) {
720e58f177SMarc-André Lureau                         uint8_t c = kev->uChar.AsciiChar;
730e58f177SMarc-André Lureau                         qemu_chr_be_write(chr, &c, 1);
740e58f177SMarc-André Lureau                     }
750e58f177SMarc-André Lureau                 }
760e58f177SMarc-André Lureau             }
770e58f177SMarc-André Lureau         }
780e58f177SMarc-André Lureau     }
790e58f177SMarc-André Lureau }
800e58f177SMarc-André Lureau 
win_stdio_thread(LPVOID param)810e58f177SMarc-André Lureau static DWORD WINAPI win_stdio_thread(LPVOID param)
820e58f177SMarc-André Lureau {
830e58f177SMarc-André Lureau     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
840e58f177SMarc-André Lureau     int                ret;
850e58f177SMarc-André Lureau     DWORD              dwSize;
860e58f177SMarc-André Lureau 
870e58f177SMarc-André Lureau     while (1) {
880e58f177SMarc-André Lureau 
890e58f177SMarc-André Lureau         /* Wait for one byte */
900e58f177SMarc-André Lureau         ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL);
910e58f177SMarc-André Lureau 
920e58f177SMarc-André Lureau         /* Exit in case of error, continue if nothing read */
930e58f177SMarc-André Lureau         if (!ret) {
940e58f177SMarc-André Lureau             break;
950e58f177SMarc-André Lureau         }
960e58f177SMarc-André Lureau         if (!dwSize) {
970e58f177SMarc-André Lureau             continue;
980e58f177SMarc-André Lureau         }
990e58f177SMarc-André Lureau 
1000e58f177SMarc-André Lureau         /* Some terminal emulator returns \r\n for Enter, just pass \n */
1010e58f177SMarc-André Lureau         if (stdio->win_stdio_buf == '\r') {
1020e58f177SMarc-André Lureau             continue;
1030e58f177SMarc-André Lureau         }
1040e58f177SMarc-André Lureau 
1050e58f177SMarc-André Lureau         /* Signal the main thread and wait until the byte was eaten */
1060e58f177SMarc-André Lureau         if (!SetEvent(stdio->hInputReadyEvent)) {
1070e58f177SMarc-André Lureau             break;
1080e58f177SMarc-André Lureau         }
1090e58f177SMarc-André Lureau         if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE)
1100e58f177SMarc-André Lureau             != WAIT_OBJECT_0) {
1110e58f177SMarc-André Lureau             break;
1120e58f177SMarc-André Lureau         }
1130e58f177SMarc-André Lureau     }
1140e58f177SMarc-André Lureau 
1150e58f177SMarc-André Lureau     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
1160e58f177SMarc-André Lureau     return 0;
1170e58f177SMarc-André Lureau }
1180e58f177SMarc-André Lureau 
win_stdio_thread_wait_func(void * opaque)1190e58f177SMarc-André Lureau static void win_stdio_thread_wait_func(void *opaque)
1200e58f177SMarc-André Lureau {
1210e58f177SMarc-André Lureau     Chardev *chr = CHARDEV(opaque);
1220e58f177SMarc-André Lureau     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
1230e58f177SMarc-André Lureau 
1240e58f177SMarc-André Lureau     if (qemu_chr_be_can_write(chr)) {
1250e58f177SMarc-André Lureau         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
1260e58f177SMarc-André Lureau     }
1270e58f177SMarc-André Lureau 
1280e58f177SMarc-André Lureau     SetEvent(stdio->hInputDoneEvent);
1290e58f177SMarc-André Lureau }
1300e58f177SMarc-André Lureau 
qemu_chr_set_echo_win_stdio(Chardev * chr,bool echo)1310e58f177SMarc-André Lureau static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
1320e58f177SMarc-André Lureau {
1330e58f177SMarc-André Lureau     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
1340e58f177SMarc-André Lureau     DWORD              dwMode = 0;
1350e58f177SMarc-André Lureau 
1360e58f177SMarc-André Lureau     GetConsoleMode(stdio->hStdIn, &dwMode);
1370e58f177SMarc-André Lureau 
1380e58f177SMarc-André Lureau     if (echo) {
1390e58f177SMarc-André Lureau         SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT);
1400e58f177SMarc-André Lureau     } else {
1410e58f177SMarc-André Lureau         SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT);
1420e58f177SMarc-André Lureau     }
1430e58f177SMarc-André Lureau }
1440e58f177SMarc-André Lureau 
qemu_chr_open_stdio(Chardev * chr,ChardevBackend * backend,bool * be_opened,Error ** errp)1450e58f177SMarc-André Lureau static void qemu_chr_open_stdio(Chardev *chr,
1460e58f177SMarc-André Lureau                                 ChardevBackend *backend,
1470e58f177SMarc-André Lureau                                 bool *be_opened,
1480e58f177SMarc-André Lureau                                 Error **errp)
1490e58f177SMarc-André Lureau {
15006639f8fSBin Meng     ChardevStdio *opts = backend->u.stdio.data;
15106639f8fSBin Meng     bool stdio_allow_signal = !opts->has_signal || opts->signal;
1520e58f177SMarc-André Lureau     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
1530e58f177SMarc-André Lureau     DWORD              dwMode;
1540e58f177SMarc-André Lureau     int                is_console = 0;
1550e58f177SMarc-André Lureau 
1560e58f177SMarc-André Lureau     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
1570e58f177SMarc-André Lureau     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
1580e58f177SMarc-André Lureau         error_setg(errp, "cannot open stdio: invalid handle");
1590e58f177SMarc-André Lureau         return;
1600e58f177SMarc-André Lureau     }
1610e58f177SMarc-André Lureau 
1620e58f177SMarc-André Lureau     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
163*903cc9e1Ssongziming     stdio->dwOldMode = dwMode;
1640e58f177SMarc-André Lureau 
1650e58f177SMarc-André Lureau     if (is_console) {
1660e58f177SMarc-André Lureau         if (qemu_add_wait_object(stdio->hStdIn,
1670e58f177SMarc-André Lureau                                  win_stdio_wait_func, chr)) {
1680e58f177SMarc-André Lureau             error_setg(errp, "qemu_add_wait_object: failed");
1690e58f177SMarc-André Lureau             goto err1;
1700e58f177SMarc-André Lureau         }
1710e58f177SMarc-André Lureau     } else {
1720e58f177SMarc-André Lureau         DWORD   dwId;
1730e58f177SMarc-André Lureau 
1740e58f177SMarc-André Lureau         stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1750e58f177SMarc-André Lureau         stdio->hInputDoneEvent  = CreateEvent(NULL, FALSE, FALSE, NULL);
1760e58f177SMarc-André Lureau         if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
1770e58f177SMarc-André Lureau             || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
1780e58f177SMarc-André Lureau             error_setg(errp, "cannot create event");
1790e58f177SMarc-André Lureau             goto err2;
1800e58f177SMarc-André Lureau         }
1810e58f177SMarc-André Lureau         if (qemu_add_wait_object(stdio->hInputReadyEvent,
1820e58f177SMarc-André Lureau                                  win_stdio_thread_wait_func, chr)) {
1830e58f177SMarc-André Lureau             error_setg(errp, "qemu_add_wait_object: failed");
1840e58f177SMarc-André Lureau             goto err2;
1850e58f177SMarc-André Lureau         }
1860e58f177SMarc-André Lureau         stdio->hInputThread     = CreateThread(NULL, 0, win_stdio_thread,
1870e58f177SMarc-André Lureau                                                chr, 0, &dwId);
1880e58f177SMarc-André Lureau 
1890e58f177SMarc-André Lureau         if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
1900e58f177SMarc-André Lureau             error_setg(errp, "cannot create stdio thread");
1910e58f177SMarc-André Lureau             goto err3;
1920e58f177SMarc-André Lureau         }
1930e58f177SMarc-André Lureau     }
1940e58f177SMarc-André Lureau 
1951e0c5446SZhang Huasen     dwMode |= ENABLE_LINE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT;
1960e58f177SMarc-André Lureau 
1970e58f177SMarc-André Lureau     if (is_console) {
1980e58f177SMarc-André Lureau         /* set the terminal in raw mode */
1990e58f177SMarc-André Lureau         /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */
20006639f8fSBin Meng         if (stdio_allow_signal) {
2010e58f177SMarc-André Lureau             dwMode |= ENABLE_PROCESSED_INPUT;
20206639f8fSBin Meng         } else {
20306639f8fSBin Meng             dwMode &= ~ENABLE_PROCESSED_INPUT;
20406639f8fSBin Meng         }
2050e58f177SMarc-André Lureau     }
2060e58f177SMarc-André Lureau 
2070e58f177SMarc-André Lureau     SetConsoleMode(stdio->hStdIn, dwMode);
2080e58f177SMarc-André Lureau 
2090e58f177SMarc-André Lureau     qemu_chr_set_echo_win_stdio(chr, false);
2100e58f177SMarc-André Lureau 
2110e58f177SMarc-André Lureau     return;
2120e58f177SMarc-André Lureau 
2130e58f177SMarc-André Lureau err3:
2140e58f177SMarc-André Lureau     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
2150e58f177SMarc-André Lureau err2:
2160e58f177SMarc-André Lureau     CloseHandle(stdio->hInputReadyEvent);
2170e58f177SMarc-André Lureau     CloseHandle(stdio->hInputDoneEvent);
2180e58f177SMarc-André Lureau err1:
2190e58f177SMarc-André Lureau     qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
2200e58f177SMarc-André Lureau }
2210e58f177SMarc-André Lureau 
char_win_stdio_finalize(Object * obj)2220e58f177SMarc-André Lureau static void char_win_stdio_finalize(Object *obj)
2230e58f177SMarc-André Lureau {
2240e58f177SMarc-André Lureau     WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
2250e58f177SMarc-André Lureau 
226*903cc9e1Ssongziming     if (stdio->hStdIn != INVALID_HANDLE_VALUE) {
227*903cc9e1Ssongziming         SetConsoleMode(stdio->hStdIn, stdio->dwOldMode);
228*903cc9e1Ssongziming     }
2290e58f177SMarc-André Lureau     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
2300e58f177SMarc-André Lureau         CloseHandle(stdio->hInputReadyEvent);
2310e58f177SMarc-André Lureau     }
2320e58f177SMarc-André Lureau     if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) {
2330e58f177SMarc-André Lureau         CloseHandle(stdio->hInputDoneEvent);
2340e58f177SMarc-André Lureau     }
2350e58f177SMarc-André Lureau     if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
2360e58f177SMarc-André Lureau         TerminateThread(stdio->hInputThread, 0);
2370e58f177SMarc-André Lureau     }
2380e58f177SMarc-André Lureau }
2390e58f177SMarc-André Lureau 
win_stdio_write(Chardev * chr,const uint8_t * buf,int len)2400e58f177SMarc-André Lureau static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
2410e58f177SMarc-André Lureau {
2420e58f177SMarc-André Lureau     HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
2430e58f177SMarc-André Lureau     DWORD   dwSize;
2440e58f177SMarc-André Lureau     int     len1;
2450e58f177SMarc-André Lureau 
2460e58f177SMarc-André Lureau     len1 = len;
2470e58f177SMarc-André Lureau 
2480e58f177SMarc-André Lureau     while (len1 > 0) {
2490e58f177SMarc-André Lureau         if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) {
2500e58f177SMarc-André Lureau             break;
2510e58f177SMarc-André Lureau         }
2520e58f177SMarc-André Lureau         buf  += dwSize;
2530e58f177SMarc-André Lureau         len1 -= dwSize;
2540e58f177SMarc-André Lureau     }
2550e58f177SMarc-André Lureau 
2560e58f177SMarc-André Lureau     return len - len1;
2570e58f177SMarc-André Lureau }
2580e58f177SMarc-André Lureau 
char_win_stdio_class_init(ObjectClass * oc,void * data)2590e58f177SMarc-André Lureau static void char_win_stdio_class_init(ObjectClass *oc, void *data)
2600e58f177SMarc-André Lureau {
2610e58f177SMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
2620e58f177SMarc-André Lureau 
2630e58f177SMarc-André Lureau     cc->open = qemu_chr_open_stdio;
2640e58f177SMarc-André Lureau     cc->chr_write = win_stdio_write;
2650e58f177SMarc-André Lureau     cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
2660e58f177SMarc-André Lureau }
2670e58f177SMarc-André Lureau 
2680e58f177SMarc-André Lureau static const TypeInfo char_win_stdio_type_info = {
2690e58f177SMarc-André Lureau     .name = TYPE_CHARDEV_WIN_STDIO,
2700e58f177SMarc-André Lureau     .parent = TYPE_CHARDEV,
2710e58f177SMarc-André Lureau     .instance_size = sizeof(WinStdioChardev),
2720e58f177SMarc-André Lureau     .instance_finalize = char_win_stdio_finalize,
2730e58f177SMarc-André Lureau     .class_init = char_win_stdio_class_init,
2740e58f177SMarc-André Lureau     .abstract = true,
2750e58f177SMarc-André Lureau };
2760e58f177SMarc-André Lureau 
register_types(void)2770e58f177SMarc-André Lureau static void register_types(void)
2780e58f177SMarc-André Lureau {
2790e58f177SMarc-André Lureau     type_register_static(&char_win_stdio_type_info);
2800e58f177SMarc-André Lureau }
2810e58f177SMarc-André Lureau 
2820e58f177SMarc-André Lureau type_init(register_types);
283