/* Serial interface for a selectable event.
Copyright (C) 2016-2020 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "ser-event.h"
#include "serial.h"
#include "gdbsupport/filestuff.h"
/* On POSIX hosts, a serial_event is basically an abstraction for the
classical self-pipe trick.
On Windows, a serial_event is a wrapper around a native Windows
event object. Because we want to interface with gdb_select, which
takes file descriptors, we need to wrap that Windows event object
in a file descriptor. As _open_osfhandle can not be used with
event objects, we instead create a dummy file wrap that in a file
descriptor with _open_osfhandle, and pass that as selectable
descriptor to callers. As Windows' gdb_select converts file
descriptors back to Windows handles by calling serial->wait_handle,
nothing ever actually waits on that file descriptor. */
struct serial_event_state
{
#ifdef USE_WIN32API
/* The Windows event object, created with CreateEvent. */
HANDLE event;
#else
/* The write side of the pipe. The read side is in
serial->fd. */
int write_fd;
#endif
};
/* Open a new serial event. */
static int
serial_event_open (struct serial *scb, const char *name)
{
struct serial_event_state *state;
state = XNEW (struct serial_event_state);
scb->state = state;
#ifndef USE_WIN32API
{
int fds[2];
if (gdb_pipe_cloexec (fds) == -1)
internal_error (__FILE__, __LINE__,
"creating serial event pipe failed.");
fcntl (fds[0], F_SETFL, O_NONBLOCK);
fcntl (fds[1], F_SETFL, O_NONBLOCK);
scb->fd = fds[0];
state->write_fd = fds[1];
}
#else
{
/* A dummy file object that can be wrapped in a file descriptor.
We don't need to store this handle because closing the file
descriptor automatically closes this. */
HANDLE dummy_file;
/* A manual-reset event. */
state->event = CreateEvent (0, TRUE, FALSE, 0);
/* The dummy file handle. Created just so we have something
wrappable in a file descriptor. */
dummy_file = CreateFile ("nul", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
scb->fd = _open_osfhandle ((intptr_t) dummy_file, 0);
}
#endif
return 0;
}
static void
serial_event_close (struct serial *scb)
{
struct serial_event_state *state = (struct serial_event_state *) scb->state;
close (scb->fd);
#ifndef USE_WIN32API
close (state->write_fd);
#else
CloseHandle (state->event);
#endif
scb->fd = -1;
xfree (state);
scb->state = NULL;
}
#ifdef USE_WIN32API
/* Implementation of the wait_handle method. Returns the native
Windows event object handle. */
static void
serial_event_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
{
struct serial_event_state *state = (struct serial_event_state *) scb->state;
*read = state->event;
}
#endif
/* The serial_ops for struct serial_event objects. Note we never
register this serial type with serial_add_interface, because this
is internal implementation detail never to be used by remote
targets for protocol transport. */
static const struct serial_ops serial_event_ops =
{
"event",
serial_event_open,
serial_event_close,
NULL, /* fdopen */
NULL, /* readchar */
NULL, /* write */
NULL, /* flush_output */
NULL, /* flush_input */
NULL, /* send_break */
NULL, /* go_raw */
NULL, /* get_tty_state */
NULL, /* copy_tty_state */
NULL, /* set_tty_state */
NULL, /* print_tty_state */
NULL, /* setbaudrate */
NULL, /* setstopbits */
NULL, /* setparity */
NULL, /* drain_output */
NULL, /* async */
NULL, /* read_prim */
NULL, /* write_prim */
NULL, /* avail */
#ifdef USE_WIN32API
serial_event_wait_handle,
#endif
};
/* See ser-event.h. */
struct serial_event *
make_serial_event (void)
{
return (struct serial_event *) serial_open_ops (&serial_event_ops);
}
/* See ser-event.h. */
int
serial_event_fd (struct serial_event *event)
{
struct serial *ser = (struct serial *) event;
return ser->fd;
}
/* See ser-event.h. */
void
serial_event_set (struct serial_event *event)
{
struct serial *ser = (struct serial *) event;
struct serial_event_state *state = (struct serial_event_state *) ser->state;
#ifndef USE_WIN32API
int r;
char c = '+'; /* Anything. */
do
{
r = write (state->write_fd, &c, 1);
}
while (r < 0 && errno == EINTR);
#else
SetEvent (state->event);
#endif
}
/* See ser-event.h. */
void
serial_event_clear (struct serial_event *event)
{
struct serial *ser = (struct serial *) event;
#ifndef USE_WIN32API
int r;
do
{
char c;
r = read (ser->fd, &c, 1);
}
while (r > 0 || (r < 0 && errno == EINTR));
#else
struct serial_event_state *state = (struct serial_event_state *) ser->state;
ResetEvent (state->event);
#endif
}