2006-04-11 Jim Blandy <jimb@codesourcery.com>

Add support for 'target remote |' on MinGW.
	* ser-mingw.c (struct pipe_state): New structure.
	(make_pipe_state, free_pipe_state, cleanup_pipe_state)
	(pipe_windows_open, pipe_windows_close, pipe_windows_read)
	(pipe_windows_write, pipe_wait_handle): New functions.
	(_initialize_ser_windows): Register a "pipe" interface based on
	them.
This commit is contained in:
Jim Blandy 2006-04-25 18:02:13 +00:00
parent b25e3d8745
commit 5f1fb6dcb3
2 changed files with 249 additions and 0 deletions

View File

@ -1,3 +1,13 @@
2006-04-25 Jim Blandy <jimb@codesourcery.com>
Add support for 'target remote |' on MinGW.
* ser-mingw.c (struct pipe_state): New structure.
(make_pipe_state, free_pipe_state, cleanup_pipe_state)
(pipe_windows_open, pipe_windows_close, pipe_windows_read)
(pipe_windows_write, pipe_wait_handle): New functions.
(_initialize_ser_windows): Register a "pipe" interface based on
them.
2006-04-24 Daniel Jacobowitz <dan@codesourcery.com>
* ser-mingw.c: Include <conio.h>.

View File

@ -624,6 +624,217 @@ ser_console_get_tty_state (struct serial *scb)
return NULL;
}
struct pipe_state
{
/* Since we use the pipe_select_thread for our select emulation,
we need to place the state structure it requires at the front
of our state. */
struct ser_console_state wait;
/* The pex obj for our (one-stage) pipeline. */
struct pex_obj *pex;
/* Streams for the pipeline's input and output. */
FILE *input, *output;
};
static struct pipe_state *
make_pipe_state (void)
{
struct pipe_state *ps = XMALLOC (struct pipe_state);
memset (ps, 0, sizeof (*ps));
ps->wait.read_event = INVALID_HANDLE_VALUE;
ps->wait.except_event = INVALID_HANDLE_VALUE;
ps->wait.start_select = INVALID_HANDLE_VALUE;
ps->wait.stop_select = INVALID_HANDLE_VALUE;
return ps;
}
static void
free_pipe_state (struct pipe_state *ps)
{
int saved_errno = errno;
if (ps->wait.read_event != INVALID_HANDLE_VALUE)
CloseHandle (ps->wait.read_event);
if (ps->wait.except_event != INVALID_HANDLE_VALUE)
CloseHandle (ps->wait.except_event);
if (ps->wait.start_select != INVALID_HANDLE_VALUE)
CloseHandle (ps->wait.start_select);
/* If we have a select thread running, let the select thread free
the stop event. */
if (ps->wait.stop_select != INVALID_HANDLE_VALUE)
SetEvent (ps->wait.stop_select);
/* Close the pipe to the child. We must close the pipe before
calling pex_free because pex_free will wait for the child to exit
and the child will not exit until the pipe is closed. */
if (ps->input)
fclose (ps->input);
if (ps->pex)
pex_free (ps->pex);
/* pex_free closes ps->output. */
xfree (ps);
errno = saved_errno;
}
static void
cleanup_pipe_state (void *untyped)
{
struct pipe_state *ps = untyped;
free_pipe_state (ps);
}
static int
pipe_windows_open (struct serial *scb, const char *name)
{
char **argv = buildargv (name);
struct cleanup *back_to = make_cleanup_freeargv (argv);
if (! argv[0] || argv[0][0] == '\0')
error ("missing child command");
struct pipe_state *ps = make_pipe_state ();
make_cleanup (cleanup_pipe_state, ps);
ps->pex = pex_init (PEX_USE_PIPES, "target remote pipe", NULL);
if (! ps->pex)
goto fail;
ps->input = pex_input_pipe (ps->pex, 1);
if (! ps->input)
goto fail;
{
int err;
const char *err_msg
= pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT,
argv[0], argv, NULL, NULL,
&err);
if (err_msg)
{
/* Our caller expects us to return -1, but all they'll do with
it generally is print the message based on errno. We have
all the same information here, plus err_msg provided by
pex_run, so we just raise the error here. */
if (err)
error ("error starting child process '%s': %s: %s",
name, err_msg, safe_strerror (err));
else
error ("error starting child process '%s': %s",
name, err_msg);
}
}
ps->output = pex_read_output (ps->pex, 1);
if (! ps->output)
goto fail;
scb->fd = fileno (ps->output);
scb->state = (void *) ps;
discard_cleanups (back_to);
return 0;
fail:
do_cleanups (back_to);
return -1;
}
static void
pipe_windows_close (struct serial *scb)
{
struct pipe_state *ps = scb->state;
/* In theory, we should try to kill the subprocess here, but the pex
interface doesn't give us enough information to do that. Usually
closing the input pipe will get the message across. */
free_pipe_state (ps);
}
static int
pipe_windows_read (struct serial *scb, size_t count)
{
HANDLE pipeline_out = (HANDLE) _get_osfhandle (scb->fd);
if (pipeline_out == INVALID_HANDLE_VALUE)
return -1;
DWORD available;
if (! PeekNamedPipe (pipeline_out, NULL, 0, NULL, &available, NULL))
return -1;
if (count > available)
count = available;
DWORD bytes_read;
if (! ReadFile (pipeline_out, scb->buf, count, &bytes_read, NULL))
return -1;
return bytes_read;
}
static int
pipe_windows_write (struct serial *scb, const void *buf, size_t count)
{
struct pipe_state *ps = scb->state;
int pipeline_in_fd = fileno (ps->input);
if (pipeline_in_fd < 0)
return -1;
HANDLE pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd);
if (pipeline_in == INVALID_HANDLE_VALUE)
return -1;
DWORD written;
if (! WriteFile (pipeline_in, buf, count, &written, NULL))
return -1;
return written;
}
static void
pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
{
struct pipe_state *ps = scb->state;
/* Have we allocated our events yet? */
if (ps->wait.read_event == INVALID_HANDLE_VALUE)
{
DWORD threadId;
/* Create auto reset events to wake and terminate the select thread. */
ps->wait.start_select = CreateEvent (0, FALSE, FALSE, 0);
ps->wait.stop_select = CreateEvent (0, FALSE, FALSE, 0);
/* Create our own events to report read and exceptions separately.
The exception event is currently never used. */
ps->wait.read_event = CreateEvent (0, FALSE, FALSE, 0);
ps->wait.except_event = CreateEvent (0, FALSE, FALSE, 0);
/* Start the select thread. */
CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId);
}
ResetEvent (ps->wait.read_event);
ResetEvent (ps->wait.except_event);
SetEvent (ps->wait.start_select);
*read = ps->wait.read_event;
*except = ps->wait.except_event;
}
struct net_windows_state
{
HANDLE read_event;
@ -897,6 +1108,34 @@ _initialize_ser_windows (void)
serial_add_interface (ops);
/* The pipe interface. */
ops = XMALLOC (struct serial_ops);
memset (ops, 0, sizeof (struct serial_ops));
ops->name = "pipe";
ops->next = 0;
ops->open = pipe_windows_open;
ops->close = pipe_windows_close;
ops->readchar = ser_base_readchar;
ops->write = ser_base_write;
ops->flush_output = ser_base_flush_output;
ops->flush_input = ser_base_flush_input;
ops->send_break = ser_base_send_break;
ops->go_raw = ser_base_raw;
ops->get_tty_state = ser_base_get_tty_state;
ops->set_tty_state = ser_base_set_tty_state;
ops->print_tty_state = ser_base_print_tty_state;
ops->noflush_set_tty_state = ser_base_noflush_set_tty_state;
ops->setbaudrate = ser_base_setbaudrate;
ops->setstopbits = ser_base_setstopbits;
ops->drain_output = ser_base_drain_output;
ops->async = ser_base_async;
ops->read_prim = pipe_windows_read;
ops->write_prim = pipe_windows_write;
ops->wait_handle = pipe_wait_handle;
serial_add_interface (ops);
/* If WinSock works, register the TCP/UDP socket driver. */
if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)