/* * QEMU System Emulator * * Copyright (c) 2003-2008 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" #include "chardev/char-win.h" static void win_chr_read(Chardev *chr, DWORD len) { WinChardev *s = WIN_CHARDEV(chr); int max_size = qemu_chr_be_can_write(chr); int ret, err; uint8_t buf[CHR_READ_BUF_LEN]; DWORD size; if (len > max_size) { len = max_size; } if (len == 0) { return; } ZeroMemory(&s->orecv, sizeof(s->orecv)); s->orecv.hEvent = s->hrecv; ret = ReadFile(s->file, buf, len, &size, &s->orecv); if (!ret) { err = GetLastError(); if (err == ERROR_IO_PENDING) { ret = GetOverlappedResult(s->file, &s->orecv, &size, TRUE); } } if (size > 0) { qemu_chr_be_write(chr, buf, size); } } static int win_chr_serial_poll(void *opaque) { Chardev *chr = CHARDEV(opaque); WinChardev *s = WIN_CHARDEV(opaque); COMSTAT status; DWORD comerr; ClearCommError(s->file, &comerr, &status); if (status.cbInQue > 0) { win_chr_read(chr, status.cbInQue); return 1; } return 0; } int win_chr_serial_init(Chardev *chr, const char *filename, Error **errp) { WinChardev *s = WIN_CHARDEV(chr); COMMCONFIG comcfg; COMMTIMEOUTS cto = { 0, 0, 0, 0, 0}; COMSTAT comstat; DWORD size; DWORD err; s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); if (!s->hsend) { error_setg(errp, "Failed CreateEvent"); goto fail; } s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); if (!s->hrecv) { error_setg(errp, "Failed CreateEvent"); goto fail; } s->file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (s->file == INVALID_HANDLE_VALUE) { error_setg_win32(errp, GetLastError(), "Failed CreateFile"); s->file = NULL; goto fail; } if (!SetupComm(s->file, NRECVBUF, NSENDBUF)) { error_setg(errp, "Failed SetupComm"); goto fail; } ZeroMemory(&comcfg, sizeof(COMMCONFIG)); size = sizeof(COMMCONFIG); GetDefaultCommConfig(filename, &comcfg, &size); comcfg.dcb.DCBlength = sizeof(DCB); CommConfigDialog(filename, NULL, &comcfg); if (!SetCommState(s->file, &comcfg.dcb)) { error_setg(errp, "Failed SetCommState"); goto fail; } if (!SetCommMask(s->file, EV_ERR)) { error_setg(errp, "Failed SetCommMask"); goto fail; } cto.ReadIntervalTimeout = MAXDWORD; if (!SetCommTimeouts(s->file, &cto)) { error_setg(errp, "Failed SetCommTimeouts"); goto fail; } if (!ClearCommError(s->file, &err, &comstat)) { error_setg(errp, "Failed ClearCommError"); goto fail; } qemu_add_polling_cb(win_chr_serial_poll, chr); return 0; fail: return -1; } int win_chr_pipe_poll(void *opaque) { Chardev *chr = CHARDEV(opaque); WinChardev *s = WIN_CHARDEV(opaque); DWORD size; PeekNamedPipe(s->file, NULL, 0, NULL, &size, NULL); if (size > 0) { win_chr_read(chr, size); return 1; } return 0; } /* Called with chr_write_lock held. */ static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1) { WinChardev *s = WIN_CHARDEV(chr); DWORD len, ret, size, err; len = len1; ZeroMemory(&s->osend, sizeof(s->osend)); s->osend.hEvent = s->hsend; while (len > 0) { if (s->hsend) { ret = WriteFile(s->file, buf, len, &size, &s->osend); } else { ret = WriteFile(s->file, buf, len, &size, NULL); } if (!ret) { err = GetLastError(); if (err == ERROR_IO_PENDING) { ret = GetOverlappedResult(s->file, &s->osend, &size, TRUE); if (ret) { buf += size; len -= size; } else { break; } } else { break; } } else { buf += size; len -= size; } } return len1 - len; } static void char_win_finalize(Object *obj) { Chardev *chr = CHARDEV(obj); WinChardev *s = WIN_CHARDEV(chr); if (s->hsend) { CloseHandle(s->hsend); } if (s->hrecv) { CloseHandle(s->hrecv); } if (!s->keep_open && s->file) { CloseHandle(s->file); } if (s->fpipe) { qemu_del_polling_cb(win_chr_pipe_poll, chr); } else { qemu_del_polling_cb(win_chr_serial_poll, chr); } qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } void win_chr_set_file(Chardev *chr, HANDLE file, bool keep_open) { WinChardev *s = WIN_CHARDEV(chr); s->keep_open = keep_open; s->file = file; } static void char_win_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); cc->chr_write = win_chr_write; } static const TypeInfo char_win_type_info = { .name = TYPE_CHARDEV_WIN, .parent = TYPE_CHARDEV, .instance_size = sizeof(WinChardev), .instance_finalize = char_win_finalize, .class_init = char_win_class_init, .abstract = true, }; static void register_types(void) { type_register_static(&char_win_type_info); } type_init(register_types);