xref: /openbmc/qemu/chardev/char-win.c (revision 99d423e5)
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 #include "qemu/osdep.h"
25 #include "qemu-common.h"
26 #include "qapi/error.h"
27 #include "chardev/char-win.h"
28 
29 static void win_chr_read(Chardev *chr, DWORD len)
30 {
31     WinChardev *s = WIN_CHARDEV(chr);
32     int max_size = qemu_chr_be_can_write(chr);
33     int ret, err;
34     uint8_t buf[CHR_READ_BUF_LEN];
35     DWORD size;
36 
37     if (len > max_size) {
38         len = max_size;
39     }
40     if (len == 0) {
41         return;
42     }
43 
44     ZeroMemory(&s->orecv, sizeof(s->orecv));
45     s->orecv.hEvent = s->hrecv;
46     ret = ReadFile(s->file, buf, len, &size, &s->orecv);
47     if (!ret) {
48         err = GetLastError();
49         if (err == ERROR_IO_PENDING) {
50             ret = GetOverlappedResult(s->file, &s->orecv, &size, TRUE);
51         }
52     }
53 
54     if (size > 0) {
55         qemu_chr_be_write(chr, buf, size);
56     }
57 }
58 
59 static int win_chr_serial_poll(void *opaque)
60 {
61     Chardev *chr = CHARDEV(opaque);
62     WinChardev *s = WIN_CHARDEV(opaque);
63     COMSTAT status;
64     DWORD comerr;
65 
66     ClearCommError(s->file, &comerr, &status);
67     if (status.cbInQue > 0) {
68         win_chr_read(chr, status.cbInQue);
69         return 1;
70     }
71     return 0;
72 }
73 
74 int win_chr_serial_init(Chardev *chr, const char *filename, Error **errp)
75 {
76     WinChardev *s = WIN_CHARDEV(chr);
77     COMMCONFIG comcfg;
78     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
79     COMSTAT comstat;
80     DWORD size;
81     DWORD err;
82 
83     s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
84     if (!s->hsend) {
85         error_setg(errp, "Failed CreateEvent");
86         goto fail;
87     }
88     s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
89     if (!s->hrecv) {
90         error_setg(errp, "Failed CreateEvent");
91         goto fail;
92     }
93 
94     s->file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
95                       OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
96     if (s->file == INVALID_HANDLE_VALUE) {
97         error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
98         s->file = NULL;
99         goto fail;
100     }
101 
102     if (!SetupComm(s->file, NRECVBUF, NSENDBUF)) {
103         error_setg(errp, "Failed SetupComm");
104         goto fail;
105     }
106 
107     ZeroMemory(&comcfg, sizeof(COMMCONFIG));
108     size = sizeof(COMMCONFIG);
109     GetDefaultCommConfig(filename, &comcfg, &size);
110     comcfg.dcb.DCBlength = sizeof(DCB);
111     CommConfigDialog(filename, NULL, &comcfg);
112 
113     if (!SetCommState(s->file, &comcfg.dcb)) {
114         error_setg(errp, "Failed SetCommState");
115         goto fail;
116     }
117 
118     if (!SetCommMask(s->file, EV_ERR)) {
119         error_setg(errp, "Failed SetCommMask");
120         goto fail;
121     }
122 
123     cto.ReadIntervalTimeout = MAXDWORD;
124     if (!SetCommTimeouts(s->file, &cto)) {
125         error_setg(errp, "Failed SetCommTimeouts");
126         goto fail;
127     }
128 
129     if (!ClearCommError(s->file, &err, &comstat)) {
130         error_setg(errp, "Failed ClearCommError");
131         goto fail;
132     }
133     qemu_add_polling_cb(win_chr_serial_poll, chr);
134     return 0;
135 
136  fail:
137     return -1;
138 }
139 
140 int win_chr_pipe_poll(void *opaque)
141 {
142     Chardev *chr = CHARDEV(opaque);
143     WinChardev *s = WIN_CHARDEV(opaque);
144     DWORD size;
145 
146     PeekNamedPipe(s->file, NULL, 0, NULL, &size, NULL);
147     if (size > 0) {
148         win_chr_read(chr, size);
149         return 1;
150     }
151     return 0;
152 }
153 
154 /* Called with chr_write_lock held.  */
155 static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
156 {
157     WinChardev *s = WIN_CHARDEV(chr);
158     DWORD len, ret, size, err;
159 
160     len = len1;
161     ZeroMemory(&s->osend, sizeof(s->osend));
162     s->osend.hEvent = s->hsend;
163     while (len > 0) {
164         if (s->hsend) {
165             ret = WriteFile(s->file, buf, len, &size, &s->osend);
166         } else {
167             ret = WriteFile(s->file, buf, len, &size, NULL);
168         }
169         if (!ret) {
170             err = GetLastError();
171             if (err == ERROR_IO_PENDING) {
172                 ret = GetOverlappedResult(s->file, &s->osend, &size, TRUE);
173                 if (ret) {
174                     buf += size;
175                     len -= size;
176                 } else {
177                     break;
178                 }
179             } else {
180                 break;
181             }
182         } else {
183             buf += size;
184             len -= size;
185         }
186     }
187     return len1 - len;
188 }
189 
190 static void char_win_finalize(Object *obj)
191 {
192     Chardev *chr = CHARDEV(obj);
193     WinChardev *s = WIN_CHARDEV(chr);
194 
195     if (s->hsend) {
196         CloseHandle(s->hsend);
197     }
198     if (s->hrecv) {
199         CloseHandle(s->hrecv);
200     }
201     if (!s->keep_open && s->file) {
202         CloseHandle(s->file);
203     }
204     if (s->fpipe) {
205         qemu_del_polling_cb(win_chr_pipe_poll, chr);
206     } else {
207         qemu_del_polling_cb(win_chr_serial_poll, chr);
208     }
209 
210     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
211 }
212 
213 void win_chr_set_file(Chardev *chr, HANDLE file, bool keep_open)
214 {
215     WinChardev *s = WIN_CHARDEV(chr);
216 
217     s->keep_open = keep_open;
218     s->file = file;
219 }
220 
221 static void char_win_class_init(ObjectClass *oc, void *data)
222 {
223     ChardevClass *cc = CHARDEV_CLASS(oc);
224 
225     cc->chr_write = win_chr_write;
226 }
227 
228 static const TypeInfo char_win_type_info = {
229     .name = TYPE_CHARDEV_WIN,
230     .parent = TYPE_CHARDEV,
231     .instance_size = sizeof(WinChardev),
232     .instance_finalize = char_win_finalize,
233     .class_init = char_win_class_init,
234     .abstract = true,
235 };
236 
237 static void register_types(void)
238 {
239     type_register_static(&char_win_type_info);
240 }
241 
242 type_init(register_types);
243