mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 18:05:37 +00:00
win32 serial port support (initial patch by kazu
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1807 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
1236cab73d
commit
f331110f35
440
vl.c
440
vl.c
|
@ -1713,11 +1713,381 @@ CharDriverState *qemu_chr_open_pty(void)
|
|||
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef struct {
|
||||
IOCanRWHandler *fd_can_read;
|
||||
IOReadHandler *fd_read;
|
||||
void *win_opaque;
|
||||
int max_size;
|
||||
HANDLE hcom, hrecv, hsend;
|
||||
OVERLAPPED orecv, osend;
|
||||
BOOL fpipe;
|
||||
DWORD len;
|
||||
} WinCharState;
|
||||
|
||||
#define NSENDBUF 2048
|
||||
#define NRECVBUF 2048
|
||||
#define MAXCONNECT 1
|
||||
#define NTIMEOUT 5000
|
||||
|
||||
static int win_chr_poll(void *opaque);
|
||||
static int win_chr_pipe_poll(void *opaque);
|
||||
|
||||
static void win_chr_close2(WinCharState *s)
|
||||
{
|
||||
if (s->hsend) {
|
||||
CloseHandle(s->hsend);
|
||||
s->hsend = NULL;
|
||||
}
|
||||
if (s->hrecv) {
|
||||
CloseHandle(s->hrecv);
|
||||
s->hrecv = NULL;
|
||||
}
|
||||
if (s->hcom) {
|
||||
CloseHandle(s->hcom);
|
||||
s->hcom = NULL;
|
||||
}
|
||||
if (s->fpipe)
|
||||
qemu_del_polling_cb(win_chr_pipe_poll, s);
|
||||
else
|
||||
qemu_del_polling_cb(win_chr_poll, s);
|
||||
}
|
||||
|
||||
static void win_chr_close(CharDriverState *chr)
|
||||
{
|
||||
WinCharState *s = chr->opaque;
|
||||
win_chr_close2(s);
|
||||
}
|
||||
|
||||
static int win_chr_init(WinCharState *s, const char *filename)
|
||||
{
|
||||
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) {
|
||||
fprintf(stderr, "Failed CreateEvent\n");
|
||||
goto fail;
|
||||
}
|
||||
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hrecv) {
|
||||
fprintf(stderr, "Failed CreateEvent\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
|
||||
s->hcom = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
|
||||
fprintf(stderr, "Failed SetupComm\n");
|
||||
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->hcom, &comcfg.dcb)) {
|
||||
fprintf(stderr, "Failed SetCommState\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(s->hcom, EV_ERR)) {
|
||||
fprintf(stderr, "Failed SetCommMask\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cto.ReadIntervalTimeout = MAXDWORD;
|
||||
if (!SetCommTimeouts(s->hcom, &cto)) {
|
||||
fprintf(stderr, "Failed SetCommTimeouts\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!ClearCommError(s->hcom, &err, &comstat)) {
|
||||
fprintf(stderr, "Failed ClearCommError\n");
|
||||
goto fail;
|
||||
}
|
||||
qemu_add_polling_cb(win_chr_poll, s);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
win_chr_close2(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
|
||||
{
|
||||
WinCharState *s = chr->opaque;
|
||||
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->hcom, buf, len, &size, &s->osend);
|
||||
else
|
||||
ret = WriteFile(s->hcom, buf, len, &size, NULL);
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
|
||||
if (ret) {
|
||||
buf += size;
|
||||
len -= size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
buf += size;
|
||||
len -= size;
|
||||
}
|
||||
}
|
||||
return len1 - len;
|
||||
}
|
||||
|
||||
static int win_chr_read_poll(WinCharState *s)
|
||||
{
|
||||
s->max_size = s->fd_can_read(s->win_opaque);
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
static void win_chr_readfile(WinCharState *s)
|
||||
{
|
||||
int ret, err;
|
||||
uint8_t buf[1024];
|
||||
DWORD size;
|
||||
|
||||
ZeroMemory(&s->orecv, sizeof(s->orecv));
|
||||
s->orecv.hEvent = s->hrecv;
|
||||
ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
|
||||
if (!ret) {
|
||||
err = GetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
s->fd_read(s->win_opaque, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void win_chr_read(WinCharState *s)
|
||||
{
|
||||
if (s->len > s->max_size)
|
||||
s->len = s->max_size;
|
||||
if (s->len == 0)
|
||||
return;
|
||||
|
||||
win_chr_readfile(s);
|
||||
}
|
||||
|
||||
static int win_chr_poll(void *opaque)
|
||||
{
|
||||
WinCharState *s = opaque;
|
||||
COMSTAT status;
|
||||
DWORD comerr;
|
||||
|
||||
ClearCommError(s->hcom, &comerr, &status);
|
||||
if (status.cbInQue > 0) {
|
||||
s->len = status.cbInQue;
|
||||
win_chr_read_poll(s);
|
||||
win_chr_read(s);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void win_chr_add_read_handler(CharDriverState *chr,
|
||||
IOCanRWHandler *fd_can_read,
|
||||
IOReadHandler *fd_read, void *opaque)
|
||||
{
|
||||
WinCharState *s = chr->opaque;
|
||||
|
||||
s->fd_can_read = fd_can_read;
|
||||
s->fd_read = fd_read;
|
||||
s->win_opaque = opaque;
|
||||
}
|
||||
|
||||
CharDriverState *qemu_chr_open_win(const char *filename)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
WinCharState *s;
|
||||
|
||||
chr = qemu_mallocz(sizeof(CharDriverState));
|
||||
if (!chr)
|
||||
return NULL;
|
||||
s = qemu_mallocz(sizeof(WinCharState));
|
||||
if (!s) {
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
chr->opaque = s;
|
||||
chr->chr_write = win_chr_write;
|
||||
chr->chr_add_read_handler = win_chr_add_read_handler;
|
||||
chr->chr_close = win_chr_close;
|
||||
|
||||
if (win_chr_init(s, filename) < 0) {
|
||||
free(s);
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
static int win_chr_pipe_poll(void *opaque)
|
||||
{
|
||||
WinCharState *s = opaque;
|
||||
DWORD size;
|
||||
|
||||
PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
|
||||
if (size > 0) {
|
||||
s->len = size;
|
||||
win_chr_read_poll(s);
|
||||
win_chr_read(s);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int win_chr_pipe_init(WinCharState *s, const char *filename)
|
||||
{
|
||||
OVERLAPPED ov;
|
||||
int ret;
|
||||
DWORD size;
|
||||
char openname[256];
|
||||
|
||||
s->fpipe = TRUE;
|
||||
|
||||
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hsend) {
|
||||
fprintf(stderr, "Failed CreateEvent\n");
|
||||
goto fail;
|
||||
}
|
||||
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!s->hrecv) {
|
||||
fprintf(stderr, "Failed CreateEvent\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
|
||||
s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
|
||||
PIPE_WAIT,
|
||||
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
|
||||
if (s->hcom == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
|
||||
s->hcom = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ZeroMemory(&ov, sizeof(ov));
|
||||
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
ret = ConnectNamedPipe(s->hcom, &ov);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Failed ConnectNamedPipe\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
|
||||
if (!ret) {
|
||||
fprintf(stderr, "Failed GetOverlappedResult\n");
|
||||
if (ov.hEvent) {
|
||||
CloseHandle(ov.hEvent);
|
||||
ov.hEvent = NULL;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ov.hEvent) {
|
||||
CloseHandle(ov.hEvent);
|
||||
ov.hEvent = NULL;
|
||||
}
|
||||
qemu_add_polling_cb(win_chr_pipe_poll, s);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
win_chr_close2(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
CharDriverState *qemu_chr_open_win_pipe(const char *filename)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
WinCharState *s;
|
||||
|
||||
chr = qemu_mallocz(sizeof(CharDriverState));
|
||||
if (!chr)
|
||||
return NULL;
|
||||
s = qemu_mallocz(sizeof(WinCharState));
|
||||
if (!s) {
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
chr->opaque = s;
|
||||
chr->chr_write = win_chr_write;
|
||||
chr->chr_add_read_handler = win_chr_add_read_handler;
|
||||
chr->chr_close = win_chr_close;
|
||||
|
||||
if (win_chr_pipe_init(s, filename) < 0) {
|
||||
free(s);
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
|
||||
{
|
||||
CharDriverState *chr;
|
||||
WinCharState *s;
|
||||
|
||||
chr = qemu_mallocz(sizeof(CharDriverState));
|
||||
if (!chr)
|
||||
return NULL;
|
||||
s = qemu_mallocz(sizeof(WinCharState));
|
||||
if (!s) {
|
||||
free(chr);
|
||||
return NULL;
|
||||
}
|
||||
s->hcom = fd_out;
|
||||
chr->opaque = s;
|
||||
chr->chr_write = win_chr_write;
|
||||
chr->chr_add_read_handler = win_chr_add_read_handler;
|
||||
return chr;
|
||||
}
|
||||
|
||||
CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
|
||||
{
|
||||
HANDLE fd_out;
|
||||
|
||||
fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (fd_out == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
return qemu_chr_open_win_file(fd_out);
|
||||
}
|
||||
#endif
|
||||
|
||||
CharDriverState *qemu_chr_open(const char *filename)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
const char *p;
|
||||
#endif
|
||||
|
||||
if (!strcmp(filename, "vc")) {
|
||||
return text_console_init(&display_state);
|
||||
|
@ -1742,12 +2112,29 @@ CharDriverState *qemu_chr_open(const char *filename)
|
|||
if (strstart(filename, "/dev/", NULL)) {
|
||||
return qemu_chr_open_tty(filename);
|
||||
} else
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
if (strstart(filename, "COM", NULL)) {
|
||||
return qemu_chr_open_win(filename);
|
||||
} else
|
||||
if (strstart(filename, "pipe:", &p)) {
|
||||
return qemu_chr_open_win_pipe(p);
|
||||
} else
|
||||
if (strstart(filename, "file:", &p)) {
|
||||
return qemu_chr_open_win_file_out(p);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_chr_close(CharDriverState *chr)
|
||||
{
|
||||
if (chr->chr_close)
|
||||
chr->chr_close(chr);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* network device redirectors */
|
||||
|
||||
|
@ -3089,6 +3476,43 @@ int qemu_set_fd_handler(int fd,
|
|||
return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* Polling handling */
|
||||
|
||||
typedef struct PollingEntry {
|
||||
PollingFunc *func;
|
||||
void *opaque;
|
||||
struct PollingEntry *next;
|
||||
} PollingEntry;
|
||||
|
||||
static PollingEntry *first_polling_entry;
|
||||
|
||||
int qemu_add_polling_cb(PollingFunc *func, void *opaque)
|
||||
{
|
||||
PollingEntry **ppe, *pe;
|
||||
pe = qemu_mallocz(sizeof(PollingEntry));
|
||||
if (!pe)
|
||||
return -1;
|
||||
pe->func = func;
|
||||
pe->opaque = opaque;
|
||||
for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next);
|
||||
*ppe = pe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qemu_del_polling_cb(PollingFunc *func, void *opaque)
|
||||
{
|
||||
PollingEntry **ppe, *pe;
|
||||
for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) {
|
||||
pe = *ppe;
|
||||
if (pe->func == func && pe->opaque == opaque) {
|
||||
*ppe = pe->next;
|
||||
qemu_free(pe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* savevm/loadvm support */
|
||||
|
||||
|
@ -3955,12 +4379,18 @@ void main_loop_wait(int timeout)
|
|||
fd_set rfds, wfds;
|
||||
int ret, nfds;
|
||||
struct timeval tv;
|
||||
PollingEntry *pe;
|
||||
|
||||
|
||||
/* XXX: need to suppress polling by better using win32 events */
|
||||
ret = 0;
|
||||
for(pe = first_polling_entry; pe != NULL; pe = pe->next) {
|
||||
ret |= pe->func(pe->opaque);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
/* XXX: see how to merge it with the select. The constraint is
|
||||
that the select must be interrupted by the timer */
|
||||
if (timeout > 0)
|
||||
if (ret == 0 && timeout > 0) {
|
||||
Sleep(timeout);
|
||||
}
|
||||
#endif
|
||||
/* poll any events */
|
||||
/* XXX: separate device handlers from system ones */
|
||||
|
|
9
vl.h
9
vl.h
|
@ -204,6 +204,14 @@ int qemu_set_fd_handler(int fd,
|
|||
IOHandler *fd_write,
|
||||
void *opaque);
|
||||
|
||||
/* Polling handling */
|
||||
|
||||
/* return TRUE if no sleep should be done afterwards */
|
||||
typedef int PollingFunc(void *opaque);
|
||||
|
||||
int qemu_add_polling_cb(PollingFunc *func, void *opaque);
|
||||
void qemu_del_polling_cb(PollingFunc *func, void *opaque);
|
||||
|
||||
/* character device */
|
||||
|
||||
#define CHR_EVENT_BREAK 0 /* serial break char */
|
||||
|
@ -237,6 +245,7 @@ typedef struct CharDriverState {
|
|||
int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
|
||||
IOEventHandler *chr_event;
|
||||
void (*chr_send_event)(struct CharDriverState *chr, int event);
|
||||
void (*chr_close)(struct CharDriverState *chr);
|
||||
void *opaque;
|
||||
} CharDriverState;
|
||||
|
||||
|
|
Loading…
Reference in a new issue