* RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports
@ 2006-02-03 22:05 Daniel Jacobowitz
2006-02-03 22:08 ` Daniel Jacobowitz
` (3 more replies)
0 siblings, 4 replies; 26+ messages in thread
From: Daniel Jacobowitz @ 2006-02-03 22:05 UTC (permalink / raw)
To: gdb-patches
This patch is a log bigger than I wanted it to be, unfortunately - it
fixes several related issues, that all touch the same code.
Fixes, all for a mingw32-hosted GDB:
- Windows serial support. This definitely deserves a NEWS entry,
included.
- Mark's Windows-aware select wrapper is substantially improved.
It's still a far cry from as thorough as Cygwin's, but it works
for reads and errors on serial ports, network sockets, pipes, and
consoles.
- Connecting to a closed TCP socket no longer times out; instead it
reports an error.
The primary ugly bit of this patch is the select wrapper. Windows has
interfaces for all these things which map to Unix file descriptors, but
while the Unix interfaces are actually compatible, the Windows interfaces
are just designed along similar principles. So you can handle a serial port
in roughly the same way you handle a console window.... but only roughly.
In fact you need to know what sort of device is behind each "file
descriptor", in order to handle it appropriately.
The one I'm least proud of is pipes - there does not appear to be a way to
sleep and have the OS wake you when data is available on a pipe. So I poll
every 10ms in a thread. Yuck! The other three all have subtly different
wait mechanisms.
I've tested it. I'm not sure what else to say about it. Is it OK? Are
there things blatantly wrong with it that I've missed? Are there particular
bits that you think are just too ugly, and if so, do you have any
suggestions on how to make them less ugly?
--
Daniel Jacobowitz
CodeSourcery
2006-02-03 Daniel Jacobowitz <dan@codesourcery.com>
* Makefile.in (gdb_select_h, ser_tcp_h): New.
(ALLDEPFILES): Add ser-windows.c.
(event-loop.o, ser-base.o, ser-tcp.o): Update.
(ser-windows.o): New rule.
* configure: Regenerated.
* configure.in: Add ser-windows.o for mingw32.
* ser-windows.c: New file.
* event-loop.c: Include "serial.h" and "gdb_select.h".
(gdb_select): Make global. Update comments. Use
serial_wait_handle. Handle generic exception conditions.
* ser-base.c: Include "gdb_select.h".
(ser_base_wait_for): Use gdb_select.
* serial.c (serial_for_fd): New function.
(serial_fdopen): Try "terminal" before "hardwire". Initialize
the allocated struct serial.
(serial_wait_handle): New function.
* serial.h (serial_for_fd, serial_wait_handle): New prototypes.
(struct serial_ops) [USE_WIN32API]: Add wait_handle.
* gdb_select.h: New file.
* ser-tcp.c: Include "ser-tcp.h". Remove unused "ser-unix.h" include.
(net_close, net_read_prim, net_write_prim): Make global.
(net_open): Pass an exception set to select also. Whitespace fix.
(_initialize_ser_tcp) [USE_WIN32API]: Do not register TCP support here.
* ser-tcp.h: New file.
* inflow.c (gdb_has_a_terminal): Don't initialize stdin_serial here.
(initialize_stdin_serial): New function.
* terminal.h (initialize_stdin_serial): New prototype.
* top.c (gdb_init): Call initialize_stdin_serial.
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/Makefile.in 2006-02-03 16:27:35.000000000 -0500
@@ -692,6 +692,7 @@ gdb_obstack_h = gdb_obstack.h $(obstack_
gdb_proc_service_h = gdb_proc_service.h $(gregset_h)
gdb_ptrace_h = gdb_ptrace.h
gdb_regex_h = gdb_regex.h $(xregex_h)
+gdb_select_h = gdb_select.h
gdb_stabs_h = gdb-stabs.h
gdb_stat_h = gdb_stat.h
gdb_string_h = gdb_string.h
@@ -764,6 +765,7 @@ scm_tags_h = scm-tags.h
sentinel_frame_h = sentinel-frame.h
serial_h = serial.h
ser_base_h = ser-base.h
+ser_tcp_h = ser-tcp.h
ser_unix_h = ser-unix.h
shnbsd_tdep_h = shnbsd-tdep.h
sh_tdep_h = sh-tdep.h
@@ -1440,7 +1442,7 @@ ALLDEPFILES = \
remote-st.c remote-utils.c dcache.c \
rs6000-nat.c rs6000-tdep.c \
s390-tdep.c s390-nat.c \
- ser-go32.c ser-pipe.c ser-tcp.c \
+ ser-go32.c ser-pipe.c ser-tcp.c ser-windows.c \
sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \
sol2-tdep.c \
solib-irix.c solib-svr4.c solib-sunos.c \
@@ -1920,7 +1922,8 @@ eval.o: eval.c $(defs_h) $(gdb_string_h)
$(f_lang_h) $(cp_abi_h) $(infcall_h) $(objc_lang_h) $(block_h) \
$(parser_defs_h) $(cp_support_h)
event-loop.o: event-loop.c $(defs_h) $(event_loop_h) $(event_top_h) \
- $(gdb_string_h) $(exceptions_h) $(gdb_assert_h)
+ $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) $(serial_h) \
+ $(gdb_select_h)
event-top.o: event-top.c $(defs_h) $(top_h) $(inferior_h) $(target_h) \
$(terminal_h) $(event_loop_h) $(event_top_h) $(interps_h) \
$(exceptions_h) $(gdbcmd_h) $(readline_h) $(readline_history_h)
@@ -2511,13 +2514,15 @@ ser-e7kpc.o: ser-e7kpc.c $(defs_h) $(ser
ser-go32.o: ser-go32.c $(defs_h) $(gdbcmd_h) $(serial_h) $(gdb_string_h)
serial.o: serial.c $(defs_h) $(serial_h) $(gdb_string_h) $(gdbcmd_h)
ser-base.o: ser-base.c $(defs_h) $(serial_h) $(ser_base_h) $(event_loop_h) \
- $(gdb_string_h)
+ $(gdb_select_h) $(gdb_string_h)
ser-pipe.o: ser-pipe.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
$(gdb_vfork_h) $(gdb_string_h)
-ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
+ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_tcp_h) \
$(gdb_string_h)
ser-unix.o: ser-unix.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \
$(terminal_h) $(gdb_string_h)
+ser-windows.o: ser-windows.c $(defs_h) $(serial_h) $(ser_base_h) \
+ $(ser_tcp_h) $(gdb_assert_h) $(gdb_string_h)
sh3-rom.o: sh3-rom.c $(defs_h) $(gdbcore_h) $(target_h) $(monitor_h) \
$(serial_h) $(srec_h) $(arch_utils_h) $(regcache_h) $(gdb_string_h) \
$(sh_tdep_h)
Index: src/gdb/configure.ac
===================================================================
--- src.orig/gdb/configure.ac 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/configure.ac 2006-02-03 15:24:16.000000000 -0500
@@ -1,5 +1,6 @@
dnl Autoconf configure script for GDB, the GNU debugger.
-dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+dnl 2005, 2006
dnl Free Software Foundation, Inc.
dnl
dnl This file is part of GDB.
@@ -1198,7 +1199,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser-
case ${host} in
*go32* ) SER_HARDWIRE=ser-go32.o ;;
*djgpp* ) SER_HARDWIRE=ser-go32.o ;;
- *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;;
+ *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-windows.o" ;;
esac
AC_SUBST(SER_HARDWIRE)
Index: src/gdb/ser-windows.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/ser-windows.c 2006-02-03 16:23:42.000000000 -0500
@@ -0,0 +1,796 @@
+/* Serial interface for local (hardwired) serial ports on Windows systems
+
+ Copyright (C) 2006
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "defs.h"
+#include "serial.h"
+#include "ser-base.h"
+#include "ser-tcp.h"
+
+#include <windows.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "gdb_assert.h"
+#include "gdb_string.h"
+
+void _initialize_ser_windows (void);
+
+struct ser_windows_state
+{
+ int in_progress;
+ OVERLAPPED ov;
+ DWORD lastCommMask;
+ HANDLE except_event;
+};
+
+/* Open up a real live device for serial I/O. */
+
+static int
+ser_windows_open (struct serial *scb, const char *name)
+{
+ HANDLE h;
+ struct ser_windows_state *state;
+ COMMTIMEOUTS timeouts;
+
+ /* Only allow COM ports. */
+ if (strncmp (name, "COM", 3) != 0)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ scb->fd = _open_osfhandle ((long) h, O_RDWR);
+ if (scb->fd < 0)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (!SetCommMask (h, EV_RXCHAR))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutConstant = 0;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ if (!SetCommTimeouts (h, &timeouts))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ state = xmalloc (sizeof (struct ser_windows_state));
+ memset (state, 0, sizeof (struct ser_windows_state));
+ scb->state = state;
+
+ /* Create a manual reset event to watch the input buffer. */
+ state->ov.hEvent = CreateEvent (0, TRUE, FALSE, 0);
+
+ /* Create a (currently unused) handle to record exceptions. */
+ state->except_event = CreateEvent (0, TRUE, FALSE, 0);
+
+ return 0;
+}
+
+/* Wait for the output to drain away, as opposed to flushing (discarding)
+ it. */
+
+static int
+ser_windows_drain_output (struct serial *scb)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+ return (FlushFileBuffers (h) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_flush_output (struct serial *scb)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+ return (PurgeComm (h, PURGE_TXCLEAR) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_flush_input (struct serial *scb)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+ return (PurgeComm (h, PURGE_RXCLEAR) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_send_break (struct serial *scb)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+ if (SetCommBreak (h) == 0)
+ return -1;
+
+ /* Delay for 250 milliseconds. */
+ Sleep (250);
+
+ if (ClearCommBreak (h))
+ return -1;
+
+ return 0;
+}
+
+static void
+ser_windows_raw (struct serial *scb)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+ DCB state;
+
+ if (GetCommState (h, &state) == 0)
+ return;
+
+ state.fParity = FALSE;
+ state.fOutxCtsFlow = FALSE;
+ state.fOutxDsrFlow = FALSE;
+ state.fDtrControl = DTR_CONTROL_ENABLE;
+ state.fDsrSensitivity = FALSE;
+ state.fOutX = FALSE;
+ state.fInX = FALSE;
+ state.fNull = FALSE;
+ state.fAbortOnError = FALSE;
+ state.ByteSize = 8;
+ state.Parity = NOPARITY;
+
+ scb->current_timeout = 0;
+
+ if (SetCommState (h, &state) == 0)
+ warning (_("SetCommState failed\n"));
+}
+
+static int
+ser_windows_setstopbits (struct serial *scb, int num)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+ DCB state;
+
+ if (GetCommState (h, &state) == 0)
+ return -1;
+
+ switch (num)
+ {
+ case SERIAL_1_STOPBITS:
+ state.StopBits = ONESTOPBIT;
+ break;
+ case SERIAL_1_AND_A_HALF_STOPBITS:
+ state.StopBits = ONE5STOPBITS;
+ break;
+ case SERIAL_2_STOPBITS:
+ state.StopBits = TWOSTOPBITS;
+ break;
+ default:
+ return 1;
+ }
+
+ return (SetCommState (h, &state) != 0) ? 0 : -1;
+}
+
+static int
+ser_windows_setbaudrate (struct serial *scb, int rate)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+ DCB state;
+
+ if (GetCommState (h, &state) == 0)
+ return -1;
+
+ state.BaudRate = rate;
+
+ return (SetCommState (h, &state) != 0) ? 0 : -1;
+}
+
+static void
+ser_windows_close (struct serial *scb)
+{
+ struct ser_windows_state *state;
+
+ /* Stop any pending selects. */
+ CancelIo ((HANDLE) _get_osfhandle (scb->fd));
+ state = scb->state;
+ CloseHandle (state->ov.hEvent);
+ CloseHandle (state->except_event);
+
+ if (scb->fd < 0)
+ return;
+
+ close (scb->fd);
+ scb->fd = -1;
+
+ xfree (scb->state);
+}
+
+static void
+ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+ struct ser_windows_state *state;
+ COMSTAT status;
+ DWORD errors;
+ HANDLE h = (HANDLE) _get_osfhandle (scb->fd);
+
+ state = scb->state;
+
+ *except = state->except_event;
+ *read = state->ov.hEvent;
+
+ if (state->in_progress)
+ return;
+
+ /* Reset the mask - we are only interested in any characters which
+ arrive after this point, not characters which might have arrived
+ and already been read. */
+
+ /* This really, really shouldn't be necessary - just the second one.
+ But otherwise an internal flag for EV_RXCHAR does not get
+ cleared, and we get a duplicated event, if the last batch
+ of characters included at least two arriving close together. */
+ if (!SetCommMask (h, 0))
+ warning (_("ser_windows_wait_handle: reseting mask failed"));
+
+ if (!SetCommMask (h, EV_RXCHAR))
+ warning (_("ser_windows_wait_handle: reseting mask failed (2)"));
+
+ /* There's a potential race condition here; we must check cbInQue
+ and not wait if that's nonzero. */
+
+ ClearCommError (h, &errors, &status);
+ if (status.cbInQue > 0)
+ {
+ SetEvent (state->ov.hEvent);
+ return;
+ }
+
+ state->in_progress = 1;
+ ResetEvent (state->ov.hEvent);
+ state->lastCommMask = -2;
+ if (WaitCommEvent (h, &state->lastCommMask, &state->ov))
+ {
+ gdb_assert (state->lastCommMask & EV_RXCHAR);
+ SetEvent (state->ov.hEvent);
+ }
+ else
+ gdb_assert (GetLastError () == ERROR_IO_PENDING);
+}
+
+static int
+ser_windows_read_prim (struct serial *scb, size_t count)
+{
+ struct ser_windows_state *state;
+ OVERLAPPED ov;
+ DWORD bytes_read, bytes_read_tmp;
+ HANDLE h;
+ gdb_byte *p;
+
+ state = scb->state;
+ if (state->in_progress)
+ {
+ WaitForSingleObject (state->ov.hEvent, INFINITE);
+ state->in_progress = 0;
+ ResetEvent (state->ov.hEvent);
+ }
+
+ memset (&ov, 0, sizeof (OVERLAPPED));
+ ov.hEvent = CreateEvent (0, FALSE, FALSE, 0);
+ h = (HANDLE) _get_osfhandle (scb->fd);
+
+ if (!ReadFile (h, scb->buf, /* count */ 1, &bytes_read, &ov))
+ {
+ if (GetLastError () != ERROR_IO_PENDING
+ || !GetOverlappedResult (h, &ov, &bytes_read, TRUE))
+ bytes_read = -1;
+ }
+
+ CloseHandle (ov.hEvent);
+ return bytes_read;
+}
+
+static int
+ser_windows_write_prim (struct serial *scb, const void *buf, size_t len)
+{
+ struct ser_windows_state *state;
+ OVERLAPPED ov;
+ DWORD bytes_written;
+ HANDLE h;
+
+ memset (&ov, 0, sizeof (OVERLAPPED));
+ ov.hEvent = CreateEvent (0, FALSE, FALSE, 0);
+ h = (HANDLE) _get_osfhandle (scb->fd);
+ if (!WriteFile (h, buf, len, &bytes_written, &ov))
+ {
+ if (GetLastError () != ERROR_IO_PENDING
+ || !GetOverlappedResult (h, &ov, &bytes_written, TRUE))
+ bytes_written = -1;
+ }
+
+ CloseHandle (ov.hEvent);
+ return bytes_written;
+}
+
+struct ser_console_state
+{
+ HANDLE read_event;
+ HANDLE except_event;
+
+ HANDLE start_select;
+ HANDLE stop_select;
+};
+
+static DWORD WINAPI
+console_select_thread (void *arg)
+{
+ struct serial *scb = arg;
+ struct ser_console_state *state, state_copy;
+ int event_index, fd;
+ HANDLE h;
+
+ /* Copy useful information out of the control block, to make sure
+ that we do not race with freeing it. */
+ state_copy = *(struct ser_console_state *) scb->state;
+ state = &state_copy;
+ fd = scb->fd;
+
+ h = (HANDLE) _get_osfhandle (fd);
+
+ while (1)
+ {
+ HANDLE wait_events[2];
+ INPUT_RECORD record;
+ DWORD n_records;
+
+ wait_events[0] = state->start_select;
+ wait_events[1] = state->stop_select;
+
+ if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+ CloseHandle (state->stop_select);
+ return 0;
+ }
+
+ retry:
+ wait_events[0] = state->stop_select;
+ wait_events[1] = h;
+
+ event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
+
+ if (event_index == WAIT_OBJECT_0
+ || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+ CloseHandle (state->stop_select);
+ return 0;
+ }
+
+ if (event_index != WAIT_OBJECT_0 + 1)
+ {
+ /* Wait must have failed; assume an error has occured, e.g.
+ the handle has been closed. */
+ SetEvent (state->except_event);
+ continue;
+ }
+
+ /* We've got a pending event on the console. See if it's
+ of interest. */
+ if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1)
+ {
+ /* Something went wrong. Maybe the console is gone. */
+ SetEvent (state->except_event);
+ continue;
+ }
+
+ if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
+ {
+ /* This is really a keypress. */
+ SetEvent (state->read_event);
+ continue;
+ }
+
+ /* Otherwise discard it and wait again. */
+ ReadConsoleInput (h, &record, 1, &n_records);
+ goto retry;
+ }
+}
+
+static int
+fd_is_pipe (int fd)
+{
+ if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL))
+ return 1;
+ else
+ return 0;
+}
+
+static DWORD WINAPI
+pipe_select_thread (void *arg)
+{
+ struct serial *scb = arg;
+ struct ser_console_state *state, state_copy;
+ int event_index, fd;
+ HANDLE h;
+
+ /* Copy useful information out of the control block, to make sure
+ that we do not race with freeing it. */
+ state_copy = *(struct ser_console_state *) scb->state;
+ state = &state_copy;
+ fd = scb->fd;
+
+ h = (HANDLE) _get_osfhandle (fd);
+
+ while (1)
+ {
+ HANDLE wait_events[2];
+ DWORD n_avail;
+
+ wait_events[0] = state->start_select;
+ wait_events[1] = state->stop_select;
+
+ if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+ CloseHandle (state->stop_select);
+ return 0;
+ }
+
+ retry:
+ if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL))
+ {
+ SetEvent (state->except_event);
+ continue;
+ }
+
+ if (n_avail > 0)
+ {
+ SetEvent (state->read_event);
+ continue;
+ }
+
+ if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+ CloseHandle (state->stop_select);
+ return 0;
+ }
+
+ Sleep (10);
+ goto retry;
+ }
+}
+
+static void
+ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+ struct ser_console_state *state = scb->state;
+
+ if (state == NULL)
+ {
+ DWORD threadId;
+ int is_tty;
+
+ is_tty = isatty (scb->fd);
+ if (!is_tty && !fd_is_pipe (scb->fd))
+ {
+ *read = NULL;
+ *except = NULL;
+ return;
+ }
+
+ state = xmalloc (sizeof (struct ser_console_state));
+ memset (state, 0, sizeof (struct ser_console_state));
+ scb->state = state;
+
+ /* Create auto reset events to wake and terminate the select thread. */
+ state->start_select = CreateEvent (0, FALSE, FALSE, 0);
+ state->stop_select = CreateEvent (0, FALSE, FALSE, 0);
+
+ /* Create our own events to report read and exceptions separately.
+ The exception event is currently never used. */
+ state->read_event = CreateEvent (0, FALSE, FALSE, 0);
+ state->except_event = CreateEvent (0, FALSE, FALSE, 0);
+
+ /* And finally start the select thread. */
+ if (is_tty)
+ CreateThread (NULL, 0, console_select_thread, scb, 0, &threadId);
+ else
+ CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId);
+ }
+
+ ResetEvent (state->read_event);
+ ResetEvent (state->except_event);
+
+ SetEvent (state->start_select);
+
+ *read = state->read_event;
+ *except = state->except_event;
+}
+
+static void
+ser_console_close (struct serial *scb)
+{
+ struct ser_console_state *state = scb->state;
+
+ if (scb->state)
+ {
+ SetEvent (state->stop_select);
+
+ CloseHandle (state->read_event);
+ CloseHandle (state->except_event);
+
+ xfree (scb->state);
+ }
+}
+
+struct ser_console_ttystate
+{
+ int is_a_tty;
+};
+
+static serial_ttystate
+ser_console_get_tty_state (struct serial *scb)
+{
+ if (isatty (scb->fd))
+ {
+ struct ser_console_ttystate *state;
+ state = (struct ser_console_ttystate *) xmalloc (sizeof *state);
+ state->is_a_tty = 1;
+ return state;
+ }
+ else
+ return NULL;
+}
+
+struct net_windows_state
+{
+ HANDLE read_event;
+ HANDLE except_event;
+
+ HANDLE start_select;
+ HANDLE stop_select;
+ HANDLE sock_event;
+};
+
+static DWORD WINAPI
+net_windows_select_thread (void *arg)
+{
+ struct serial *scb = arg;
+ struct net_windows_state *state, state_copy;
+ int event_index, fd;
+
+ /* Copy useful information out of the control block, to make sure
+ that we do not race with freeing it. */
+ state_copy = *(struct net_windows_state *) scb->state;
+ state = &state_copy;
+ fd = scb->fd;
+
+ while (1)
+ {
+ HANDLE wait_events[2];
+ WSANETWORKEVENTS events;
+
+ wait_events[0] = state->start_select;
+ wait_events[1] = state->stop_select;
+
+ if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+ {
+ CloseHandle (state->stop_select);
+ return 0;
+ }
+
+ wait_events[0] = state->stop_select;
+ wait_events[1] = state->sock_event;
+
+ event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE);
+
+ if (event_index == WAIT_OBJECT_0
+ || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0)
+ {
+ CloseHandle (state->stop_select);
+ return 0;
+ }
+
+ if (event_index != WAIT_OBJECT_0 + 1)
+ {
+ /* Some error has occured. Assume that this is an error
+ condition. */
+ SetEvent (state->except_event);
+ continue;
+ }
+
+ /* Enumerate the internal network events, and reset the object that
+ signalled us to catch the next event. */
+ WSAEnumNetworkEvents (fd, state->sock_event, &events);
+
+ if (events.lNetworkEvents & FD_READ)
+ SetEvent (state->read_event);
+
+ if (events.lNetworkEvents & FD_CLOSE)
+ SetEvent (state->except_event);
+ }
+}
+
+static void
+net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+ struct net_windows_state *state = scb->state;
+
+ ResetEvent (state->read_event);
+ ResetEvent (state->except_event);
+
+ SetEvent (state->start_select);
+
+ *read = state->read_event;
+ *except = state->except_event;
+}
+
+static int
+net_windows_open (struct serial *scb, const char *name)
+{
+ struct net_windows_state *state;
+ int ret;
+ DWORD threadId;
+
+ ret = net_open (scb, name);
+ if (ret != 0)
+ return ret;
+
+ state = xmalloc (sizeof (struct net_windows_state));
+ memset (state, 0, sizeof (struct net_windows_state));
+ scb->state = state;
+
+ /* Create auto reset events to wake and terminate the select thread. */
+ state->start_select = CreateEvent (0, FALSE, FALSE, 0);
+ state->stop_select = CreateEvent (0, FALSE, FALSE, 0);
+
+ /* Associate an event with the socket. */
+ state->sock_event = CreateEvent (0, TRUE, FALSE, 0);
+ WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE);
+
+ /* Create our own events to report read and close separately. */
+ state->read_event = CreateEvent (0, FALSE, FALSE, 0);
+ state->except_event = CreateEvent (0, FALSE, FALSE, 0);
+
+ /* And finally start the select thread. */
+ CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId);
+
+ return 0;
+}
+
+
+static void
+net_windows_close (struct serial *scb)
+{
+ struct net_windows_state *state = scb->state;
+
+ SetEvent (state->stop_select);
+
+ CloseHandle (state->read_event);
+ CloseHandle (state->except_event);
+ CloseHandle (state->start_select);
+ CloseHandle (state->sock_event);
+
+ xfree (scb->state);
+
+ net_close (scb);
+}
+
+void
+_initialize_ser_windows (void)
+{
+ WSADATA wsa_data;
+ struct serial_ops *ops;
+
+ /* First register the serial port driver. */
+
+ ops = XMALLOC (struct serial_ops);
+ memset (ops, 0, sizeof (struct serial_ops));
+ ops->name = "hardwire";
+ ops->next = 0;
+ ops->open = ser_windows_open;
+ ops->close = ser_windows_close;
+
+ ops->flush_output = ser_windows_flush_output;
+ ops->flush_input = ser_windows_flush_input;
+ ops->send_break = ser_windows_send_break;
+
+ /* These are only used for stdin; we do not need them for serial
+ ports, so supply the standard dummies. */
+ 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->go_raw = ser_windows_raw;
+ ops->setbaudrate = ser_windows_setbaudrate;
+ ops->setstopbits = ser_windows_setstopbits;
+ ops->drain_output = ser_windows_drain_output;
+ ops->readchar = ser_base_readchar;
+ ops->write = ser_base_write;
+ ops->async = ser_base_async;
+ ops->read_prim = ser_windows_read_prim;
+ ops->write_prim = ser_windows_write_prim;
+ ops->wait_handle = ser_windows_wait_handle;
+
+ serial_add_interface (ops);
+
+ /* Next create the dummy serial driver used for terminals. We only
+ provide the TTY-related methods. */
+
+ ops = XMALLOC (struct serial_ops);
+ memset (ops, 0, sizeof (struct serial_ops));
+
+ ops->name = "terminal";
+ ops->next = 0;
+
+ ops->close = ser_console_close;
+ ops->get_tty_state = ser_console_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->drain_output = ser_base_drain_output;
+ ops->wait_handle = ser_console_wait_handle;
+
+ serial_add_interface (ops);
+
+ /* If WinSock works, register the TCP/UDP socket driver. */
+
+ if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
+ /* WinSock is unavailable. */
+ return;
+
+ ops = XMALLOC (struct serial_ops);
+ memset (ops, 0, sizeof (struct serial_ops));
+ ops->name = "tcp";
+ ops->next = 0;
+ ops->open = net_windows_open;
+ ops->close = net_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 = net_read_prim;
+ ops->write_prim = net_write_prim;
+ ops->wait_handle = net_windows_wait_handle;
+ serial_add_interface (ops);
+}
Index: src/gdb/event-loop.c
===================================================================
--- src.orig/gdb/event-loop.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/event-loop.c 2006-02-03 16:25:46.000000000 -0500
@@ -1,5 +1,6 @@
/* Event loop machinery for GDB, the GNU debugger.
- Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006
+ Free Software Foundation, Inc.
Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions.
This file is part of GDB.
@@ -22,6 +23,7 @@
#include "defs.h"
#include "event-loop.h"
#include "event-top.h"
+#include "serial.h"
#ifdef HAVE_POLL
#if defined (HAVE_POLL_H)
@@ -37,6 +39,7 @@
#include <sys/time.h>
#include "exceptions.h"
#include "gdb_assert.h"
+#include "gdb_select.h"
typedef struct gdb_event gdb_event;
typedef void (event_handler_func) (int);
@@ -731,13 +734,11 @@ handle_file_event (int event_file_desc)
}
}
-/* Wrapper for select. This function is not yet exported from this
- file because it is not sufficiently general. For example,
- ser-base.c uses select to check for socket activity, and this
- function does not support sockets under Windows, so we do not want
- to use gdb_select in ser-base.c. */
+/* Wrapper for select. On Windows systems, where the select interface
+ only works for sockets, this uses the GDB serial abstraction to
+ handle sockets, consoles, pipes, and serial ports. */
-static int
+int
gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
{
@@ -748,40 +749,55 @@ gdb_select (int n, fd_set *readfds, fd_s
DWORD num_handles;
int fd;
int num_ready;
+ int indx;
+ static HANDLE never_handle;
num_ready = 0;
num_handles = 0;
for (fd = 0; fd < n; ++fd)
{
+ HANDLE read = NULL, except = NULL;
+ struct serial *scb;
+
/* There is no support yet for WRITEFDS. At present, this isn't
used by GDB -- but we do not want to silently ignore WRITEFDS
if something starts using it. */
- gdb_assert (!FD_ISSET (fd, writefds));
+ gdb_assert (!writefds || !FD_ISSET (fd, writefds));
+
if (!FD_ISSET (fd, readfds)
&& !FD_ISSET (fd, exceptfds))
continue;
h = (HANDLE) _get_osfhandle (fd);
- if (h == INVALID_HANDLE_VALUE)
+
+ scb = serial_for_fd (fd);
+ if (scb)
+ serial_wait_handle (scb, &read, &except);
+
+ if (read == NULL)
+ read = h;
+ if (except == NULL)
{
- /* If the underlying handle is INVALID_HANDLE_VALUE, then
- this descriptor is no more. */
- if (FD_ISSET (fd, exceptfds))
- ++num_ready;
- continue;
- }
- /* The only exceptional condition we recognize is a closed file
- descriptor. Since we have already checked for that
- condition, clear the exceptional bit for this descriptor. */
- FD_CLR (fd, exceptfds);
+ if (!never_handle)
+ never_handle = CreateEvent (0, FALSE, FALSE, 0);
+
+ except = never_handle;
+ }
+
if (FD_ISSET (fd, readfds))
- {
- gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
- handles[num_handles++] = h;
- }
+ {
+ gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
+ handles[num_handles++] = read;
+ }
+
+ if (FD_ISSET (fd, exceptfds))
+ {
+ gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
+ handles[num_handles++] = except;
+ }
}
/* If we don't need to wait for any handles, we are done. */
if (!num_handles)
- return num_ready;
+ return 0;
event = WaitForMultipleObjects (num_handles,
handles,
FALSE,
@@ -796,23 +812,34 @@ gdb_select (int n, fd_set *readfds, fd_s
if (event == WAIT_FAILED)
return -1;
if (event == WAIT_TIMEOUT)
- return num_ready;
+ return 0;
/* Run through the READFDS, clearing bits corresponding to descriptors
for which input is unavailable. */
- num_ready += num_handles;
h = handles[event - WAIT_OBJECT_0];
- for (fd = 0; fd < n; ++fd)
+ for (fd = 0, indx = 0; fd < n; ++fd)
{
HANDLE fd_h;
- if (!FD_ISSET (fd, readfds))
- continue;
- fd_h = (HANDLE) _get_osfhandle (fd);
- /* This handle might be ready, even though it wasn't the handle
- returned by WaitForMultipleObjects. */
- if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+
+ if (FD_ISSET (fd, readfds))
{
- FD_CLR (fd, readfds);
- --num_ready;
+ fd_h = handles[indx++];
+ /* This handle might be ready, even though it wasn't the handle
+ returned by WaitForMultipleObjects. */
+ if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+ FD_CLR (fd, readfds);
+ else
+ num_ready++;
+ }
+
+ if (FD_ISSET (fd, exceptfds))
+ {
+ fd_h = handles[indx++];
+ /* This handle might be ready, even though it wasn't the handle
+ returned by WaitForMultipleObjects. */
+ if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
+ FD_CLR (fd, exceptfds);
+ else
+ num_ready++;
}
}
Index: src/gdb/ser-base.c
===================================================================
--- src.orig/gdb/ser-base.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/ser-base.c 2006-02-03 16:26:09.000000000 -0500
@@ -1,7 +1,7 @@
/* Generic serial interface functions.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GDB.
@@ -25,6 +25,7 @@
#include "ser-base.h"
#include "event-loop.h"
+#include "gdb_select.h"
#include "gdb_string.h"
#include <sys/time.h>
#ifdef USE_WIN32API
@@ -202,9 +203,9 @@ ser_base_wait_for (struct serial *scb, i
FD_SET (scb->fd, &exceptfds);
if (timeout >= 0)
- numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
+ numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
else
- numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
+ numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
if (numfds <= 0)
{
Index: src/gdb/serial.c
===================================================================
--- src.orig/gdb/serial.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/serial.c 2006-02-03 16:26:43.000000000 -0500
@@ -1,7 +1,7 @@
/* Generic serial interface routines
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002 Free Software Foundation, Inc.
+ 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GDB.
@@ -233,6 +233,22 @@ serial_open (const char *name)
return scb;
}
+/* Return the open serial device for FD, if found, or NULL if FD
+ is not already opened. */
+
+struct serial *
+serial_for_fd (int fd)
+{
+ struct serial *scb;
+ struct serial_ops *ops;
+
+ for (scb = scb_base; scb; scb = scb->next)
+ if (scb->fd == fd)
+ return scb;
+
+ return NULL;
+}
+
struct serial *
serial_fdopen (const int fd)
{
@@ -246,12 +262,14 @@ serial_fdopen (const int fd)
return scb;
}
- ops = serial_interface_lookup ("hardwire");
+ ops = serial_interface_lookup ("terminal");
+ if (!ops)
+ ops = serial_interface_lookup ("hardwire");
if (!ops)
return NULL;
- scb = XMALLOC (struct serial);
+ scb = XCALLOC (1, struct serial);
scb->ops = ops;
@@ -524,6 +542,19 @@ serial_debug_p (struct serial *scb)
return scb->debug_p || global_serial_debug_p;
}
+#ifdef USE_WIN32API
+void
+serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+ if (scb->ops->wait_handle)
+ scb->ops->wait_handle (scb, read, except);
+ else
+ {
+ *read = (HANDLE) _get_osfhandle (scb->fd);
+ *except = NULL;
+ }
+}
+#endif
#if 0
/* The connect command is #if 0 because I hadn't thought of an elegant
Index: src/gdb/serial.h
===================================================================
--- src.orig/gdb/serial.h 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/serial.h 2006-02-03 16:27:11.000000000 -0500
@@ -1,5 +1,6 @@
/* Remote serial support interface definitions for GDB, the GNU Debugger.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+ 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of GDB.
@@ -22,6 +23,10 @@
#ifndef SERIAL_H
#define SERIAL_H
+#ifdef USE_WIN32API
+#include <windows.h>
+#endif
+
struct ui_file;
/* For most routines, if a failure is indicated, then errno should be
@@ -41,6 +46,10 @@ struct serial;
extern struct serial *serial_open (const char *name);
+/* Find an already opened serial stream using a file handle. */
+
+extern struct serial *serial_for_fd (int fd);
+
/* Open a new serial stream using a file handle. */
extern struct serial *serial_fdopen (const int fd);
@@ -238,6 +247,13 @@ struct serial_ops
/* Perform a low-level write operation, writing (at most) COUNT
bytes from BUF. */
int (*write_prim)(struct serial *scb, const void *buf, size_t count);
+
+#ifdef USE_WIN32API
+ /* Return a handle to wait on, indicating available data from SCB
+ when signaled, in *READ. Return a handle indicating errors
+ in *EXCEPT. */
+ void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except);
+#endif /* USE_WIN32API */
};
/* Add a new serial interface to the interface list */
@@ -248,4 +264,12 @@ extern void serial_add_interface (struct
extern void serial_log_command (const char *);
+#ifdef USE_WIN32API
+
+/* Windows-only: find or create handles that we can wait on for this
+ serial device. */
+extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *);
+
+#endif /* USE_WIN32API */
+
#endif /* SERIAL_H */
Index: src/gdb/gdb_select.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/gdb_select.h 2006-02-03 15:19:42.000000000 -0500
@@ -0,0 +1,37 @@
+/* Slightly more portable version of <sys/select.h>.
+
+ Copyright (C) 2006
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#if !defined(GDB_SELECT_H)
+#define GDB_SELECT_H
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef USE_WIN32API
+#include <windows.h>
+#endif
+
+extern int gdb_select (int n, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+
+#endif /* !defined(GDB_STRING_H) */
Index: src/gdb/ser-tcp.c
===================================================================
--- src.orig/gdb/ser-tcp.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/ser-tcp.c 2006-02-03 16:27:18.000000000 -0500
@@ -1,6 +1,6 @@
/* Serial interface for raw TCP connections on Un*x like systems.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005, 2006
Free Software Foundation, Inc.
This file is part of GDB.
@@ -23,7 +23,7 @@
#include "defs.h"
#include "serial.h"
#include "ser-base.h"
-#include "ser-unix.h"
+#include "ser-tcp.h"
#include <sys/types.h>
@@ -56,8 +56,6 @@
typedef int socklen_t;
#endif
-static int net_open (struct serial *scb, const char *name);
-static void net_close (struct serial *scb);
void _initialize_ser_tcp (void);
/* seconds to wait for connect */
@@ -67,7 +65,7 @@ void _initialize_ser_tcp (void);
/* Open a tcp socket */
-static int
+int
net_open (struct serial *scb, const char *name)
{
char *port_str, hostname[100];
@@ -153,7 +151,7 @@ net_open (struct serial *scb, const char
{
/* looks like we need to wait for the connect */
struct timeval t;
- fd_set rset, wset;
+ fd_set rset, wset, eset;
int polls = 0;
FD_ZERO (&rset);
@@ -174,10 +172,13 @@ net_open (struct serial *scb, const char
FD_SET (scb->fd, &rset);
wset = rset;
+ eset = rset;
t.tv_sec = 0;
t.tv_usec = 1000000 / POLL_INTERVAL;
- n = select (scb->fd + 1, &rset, &wset, NULL, &t);
+ /* Windows returns connection failure in eset rather than wset.
+ How perverse. */
+ n = select (scb->fd + 1, &rset, &wset, &eset, &t);
polls++;
}
while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL);
@@ -194,7 +195,7 @@ net_open (struct serial *scb, const char
{
int res, err;
socklen_t len;
- len = sizeof(err);
+ len = sizeof (err);
/* On Windows, the fourth parameter to getsockopt is a "char *";
on UNIX systems it is generally "void *". The cast to "void *"
is OK everywhere, since in C "void *" can be implicitly
@@ -230,7 +231,7 @@ net_open (struct serial *scb, const char
return 0;
}
-static void
+void
net_close (struct serial *scb)
{
if (scb->fd < 0)
@@ -240,13 +241,13 @@ net_close (struct serial *scb)
scb->fd = -1;
}
-static int
+int
net_read_prim (struct serial *scb, size_t count)
{
return recv (scb->fd, scb->buf, count, 0);
}
-static int
+int
net_write_prim (struct serial *scb, const void *buf, size_t count)
{
return send (scb->fd, buf, count, 0);
@@ -255,13 +256,12 @@ net_write_prim (struct serial *scb, cons
void
_initialize_ser_tcp (void)
{
- struct serial_ops *ops;
#ifdef USE_WIN32API
- WSADATA wsa_data;
- if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
- /* WinSock is unavailable. */
- return;
-#endif
+ /* Do nothing; the TCP serial operations will be initialized in
+ ser-windows.c. */
+ return;
+#else
+ struct serial_ops *ops;
ops = XMALLOC (struct serial_ops);
memset (ops, 0, sizeof (struct serial_ops));
ops->name = "tcp";
@@ -285,4 +285,5 @@ _initialize_ser_tcp (void)
ops->read_prim = net_read_prim;
ops->write_prim = net_write_prim;
serial_add_interface (ops);
+#endif /* USE_WIN32API */
}
Index: src/gdb/ser-tcp.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/ser-tcp.h 2006-02-03 16:34:52.000000000 -0500
@@ -0,0 +1,32 @@
+/* Serial interface for raw TCP connections on Un*x like systems.
+
+ Copyright (C) 2006 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef SER_TCP_H
+#define SER_TCP_H
+
+struct serial;
+
+extern int net_open (struct serial *scb, const char *name);
+extern void net_close (struct serial *scb);
+extern int net_read_prim (struct serial *scb, size_t count);
+extern int net_write_prim (struct serial *scb, const void *buf, size_t count);
+
+#endif
Index: src/gdb/inflow.c
===================================================================
--- src.orig/gdb/inflow.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/inflow.c 2006-02-03 16:28:04.000000000 -0500
@@ -1,6 +1,6 @@
/* Low level interface to ptrace, for GDB when running under Unix.
Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
- 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of GDB.
@@ -129,7 +129,6 @@ gdb_has_a_terminal (void)
#endif
gdb_has_a_terminal_flag = no;
- stdin_serial = serial_fdopen (0);
if (stdin_serial != NULL)
{
our_ttystate = serial_get_tty_state (stdin_serial);
@@ -730,6 +729,18 @@ gdb_setpgid (void)
return retval;
}
+/* Get all the current tty settings (including whether we have a
+ tty at all!). We can't do this in _initialize_inflow because
+ serial_fdopen() won't work until the serial_ops_list is
+ initialized, but we don't want to do it lazily either, so
+ that we can guarantee stdin_serial is opened if there is
+ a terminal. */
+void
+initialize_stdin_serial (void)
+{
+ stdin_serial = serial_fdopen (0);
+}
+
void
_initialize_inflow (void)
{
Index: src/gdb/terminal.h
===================================================================
--- src.orig/gdb/terminal.h 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/terminal.h 2006-02-03 16:28:30.000000000 -0500
@@ -1,5 +1,6 @@
/* Terminal interface definitions for GDB, the GNU Debugger.
- Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000
+ Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000,
+ 2006
Free Software Foundation, Inc.
This file is part of GDB.
@@ -88,4 +89,7 @@ extern int job_control;
we lack job control. */
extern int gdb_setpgid (void);
+/* Set up a serial structure describing standard input. In inflow.c. */
+extern void initialize_stdin_serial (void);
+
#endif /* !defined (TERMINAL_H) */
Index: src/gdb/top.c
===================================================================
--- src.orig/gdb/top.c 2006-02-03 15:17:30.000000000 -0500
+++ src/gdb/top.c 2006-02-03 15:19:42.000000000 -0500
@@ -1550,6 +1550,8 @@ gdb_init (char *argv0)
init_cli_cmds();
init_main (); /* But that omits this file! Do it now */
+ initialize_stdin_serial ();
+
async_init_signals ();
/* We need a default language for parsing expressions, so simple things like
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-03 22:05 RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports Daniel Jacobowitz @ 2006-02-03 22:08 ` Daniel Jacobowitz 2006-02-04 12:43 ` Eli Zaretskii 2006-02-04 6:28 ` Ian Lance Taylor ` (2 subsequent siblings) 3 siblings, 1 reply; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-03 22:08 UTC (permalink / raw) To: gdb-patches On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote: > Fixes, all for a mingw32-hosted GDB: > > - Windows serial support. This definitely deserves a NEWS entry, > included. > > - Mark's Windows-aware select wrapper is substantially improved. > It's still a far cry from as thorough as Cygwin's, but it works > for reads and errors on serial ports, network sockets, pipes, and > consoles. > > - Connecting to a closed TCP socket no longer times out; instead it > reports an error. Oh - I missed one. gdb_has_a_terminal() now works, so query() prints questions instead of assuming yes. That makes a big usability difference. Unfortunately, this is true for Windows consoles, but not for a Windows-native GDB run from a Cygwin SSH terminal - there stdin is just a pipe. I checked with Chris, and there really doesn't seem to be a useful way around this. So if you want to use a native Windows GDB, use it in a native Windows console (or via a pipe explicitly - e.g. Eclipse). -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-03 22:08 ` Daniel Jacobowitz @ 2006-02-04 12:43 ` Eli Zaretskii 2006-02-04 15:13 ` Daniel Jacobowitz 0 siblings, 1 reply; 26+ messages in thread From: Eli Zaretskii @ 2006-02-04 12:43 UTC (permalink / raw) To: gdb-patches > Date: Fri, 3 Feb 2006 17:08:11 -0500 > From: Daniel Jacobowitz <drow@false.org> > > Oh - I missed one. gdb_has_a_terminal() now works, so query() prints > questions instead of assuming yes. That makes a big usability > difference. Btw, as long as you are fixing annoyances in the MinGW port, how about this one: whenever GDB has a large amount of data to display via printf_filtered and its ilk, if I type `q' in response to its question whether to continue or quit after displaying a screenful of text, I get this bogus message: Quit (expect signal SIGINT when the program is resumed) That's a lie, of course: no SIGINT will be coming my way any time soon. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 12:43 ` Eli Zaretskii @ 2006-02-04 15:13 ` Daniel Jacobowitz 0 siblings, 0 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-04 15:13 UTC (permalink / raw) To: gdb-patches On Sat, Feb 04, 2006 at 02:43:33PM +0200, Eli Zaretskii wrote: > > Date: Fri, 3 Feb 2006 17:08:11 -0500 > > From: Daniel Jacobowitz <drow@false.org> > > > > Oh - I missed one. gdb_has_a_terminal() now works, so query() prints > > questions instead of assuming yes. That makes a big usability > > difference. > > Btw, as long as you are fixing annoyances in the MinGW port, how about > this one: whenever GDB has a large amount of data to display via > printf_filtered and its ilk, if I type `q' in response to its question > whether to continue or quit after displaying a screenful of text, I > get this bogus message: > > Quit (expect signal SIGINT when the program is resumed) > > That's a lie, of course: no SIGINT will be coming my way any time > soon. I'll take a look :-) -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-03 22:05 RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports Daniel Jacobowitz 2006-02-03 22:08 ` Daniel Jacobowitz @ 2006-02-04 6:28 ` Ian Lance Taylor 2006-02-04 10:30 ` Eli Zaretskii 2006-02-04 15:00 ` Daniel Jacobowitz 2006-02-04 12:38 ` Eli Zaretskii 2006-02-06 21:02 ` Daniel Jacobowitz 3 siblings, 2 replies; 26+ messages in thread From: Ian Lance Taylor @ 2006-02-04 6:28 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: gdb-patches Daniel Jacobowitz <drow@false.org> writes: > The one I'm least proud of is pipes - there does not appear to be a way to > sleep and have the OS wake you when data is available on a pipe. So I poll > every 10ms in a thread. Yuck! The other three all have subtly different > wait mechanisms. Why do you have to poll? You should be able to have a thread which just sleeps on reading the pipe. When the thread reads something, it can signal the main thread, passing it the character which it read. The cleanest portable event loop which I know of is Tcl. The Tcl code has solved pretty much all the event loop issues for Unix, Windows, and MacOS 9. Ian ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 6:28 ` Ian Lance Taylor @ 2006-02-04 10:30 ` Eli Zaretskii 2006-02-04 17:06 ` Ian Lance Taylor 2006-02-04 15:00 ` Daniel Jacobowitz 1 sibling, 1 reply; 26+ messages in thread From: Eli Zaretskii @ 2006-02-04 10:30 UTC (permalink / raw) To: Ian Lance Taylor; +Cc: drow, gdb-patches > Cc: gdb-patches@sourceware.org > From: Ian Lance Taylor <ian@airs.com> > Date: 03 Feb 2006 22:28:16 -0800 > > The cleanest portable event loop which I know of is Tcl. The Tcl code > has solved pretty much all the event loop issues for Unix, Windows, > and MacOS 9. Can you, or someone else, please show a URL where I can see that event loop, without having to download the whole package? ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 10:30 ` Eli Zaretskii @ 2006-02-04 17:06 ` Ian Lance Taylor 2006-02-04 17:45 ` Eli Zaretskii 0 siblings, 1 reply; 26+ messages in thread From: Ian Lance Taylor @ 2006-02-04 17:06 UTC (permalink / raw) To: Eli Zaretskii; +Cc: drow, gdb-patches Eli Zaretskii <eliz@gnu.org> writes: > > Cc: gdb-patches@sourceware.org > > From: Ian Lance Taylor <ian@airs.com> > > Date: 03 Feb 2006 22:28:16 -0800 > > > > The cleanest portable event loop which I know of is Tcl. The Tcl code > > has solved pretty much all the event loop issues for Unix, Windows, > > and MacOS 9. > > Can you, or someone else, please show a URL where I can see that event > loop, without having to download the whole package? Tcl is checked into the src repository, since it's part of expect which is part of DejaGNU. There isn't just one place to look at the event loop. The Windows pipe specific code is in src/tcl/win/tclWinPipe.c. Ian ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 17:06 ` Ian Lance Taylor @ 2006-02-04 17:45 ` Eli Zaretskii 0 siblings, 0 replies; 26+ messages in thread From: Eli Zaretskii @ 2006-02-04 17:45 UTC (permalink / raw) To: Ian Lance Taylor; +Cc: drow, gdb-patches > Cc: drow@false.org, gdb-patches@sourceware.org > From: Ian Lance Taylor <ian@airs.com> > Date: 04 Feb 2006 09:05:55 -0800 > > Tcl is checked into the src repository, since it's part of expect > which is part of DejaGNU. There isn't just one place to look at the > event loop. The Windows pipe specific code is in > src/tcl/win/tclWinPipe.c. Thanks. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 6:28 ` Ian Lance Taylor 2006-02-04 10:30 ` Eli Zaretskii @ 2006-02-04 15:00 ` Daniel Jacobowitz 2006-02-05 0:01 ` Ian Lance Taylor 1 sibling, 1 reply; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-04 15:00 UTC (permalink / raw) To: Ian Lance Taylor; +Cc: gdb-patches On Fri, Feb 03, 2006 at 10:28:16PM -0800, Ian Lance Taylor wrote: > Daniel Jacobowitz <drow@false.org> writes: > > > The one I'm least proud of is pipes - there does not appear to be a way to > > sleep and have the OS wake you when data is available on a pipe. So I poll > > every 10ms in a thread. Yuck! The other three all have subtly different > > wait mechanisms. > > Why do you have to poll? You should be able to have a thread which > just sleeps on reading the pipe. When the thread reads something, it > can signal the main thread, passing it the character which it read. I am glad you asked. Oh, so glad. I can't read from the pipe, because I can't pass the data to the main thread. There's a dissimilarity between the console and pipe support, and the serial and socket support. I used the serial.c interface for both pairs, but the serial and socket devices are accessed exclusively from the serial interface. On the other hand, that interface is only used for managing TTY state (and now for select) for the other two. The pipe I was interested in supporting was on stdin, and that gets read from in all sorts of interesting places - like inside the bowels of readline. If I read a character from the pipe, it's going to get lost. Pity there's no ungetc equivalent under the read() layer. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 15:00 ` Daniel Jacobowitz @ 2006-02-05 0:01 ` Ian Lance Taylor 2006-02-05 22:00 ` Daniel Jacobowitz 0 siblings, 1 reply; 26+ messages in thread From: Ian Lance Taylor @ 2006-02-05 0:01 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: gdb-patches Daniel Jacobowitz <drow@false.org> writes: > > Why do you have to poll? You should be able to have a thread which > > just sleeps on reading the pipe. When the thread reads something, it > > can signal the main thread, passing it the character which it read. > > I am glad you asked. Oh, so glad. I can't read from the pipe, because > I can't pass the data to the main thread. > > There's a dissimilarity between the console and pipe support, and the > serial and socket support. I used the serial.c interface for both > pairs, but the serial and socket devices are accessed exclusively from > the serial interface. On the other hand, that interface is only used > for managing TTY state (and now for select) for the other two. The > pipe I was interested in supporting was on stdin, and that gets > read from in all sorts of interesting places - like inside the > bowels of readline. If I read a character from the pipe, it's going > to get lost. > > Pity there's no ungetc equivalent under the read() layer. Well, OK, let's try this. Create a new pipe. Create yet another new thread which reads from the original pipe and writes to the new pipe. Freopen the new pipe onto stdin. When the new thread writes to the new pipe, it signals the event loop once, and then waits. When the event loop gets the signal, it indicates that it is OK to read from stdin, the new pipe. When you reenter the event loop, it signals the new thread to tell it that it is interested in more data and more signals. Ian ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-05 0:01 ` Ian Lance Taylor @ 2006-02-05 22:00 ` Daniel Jacobowitz 2006-02-06 3:27 ` Ian Lance Taylor 0 siblings, 1 reply; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-05 22:00 UTC (permalink / raw) To: Ian Lance Taylor; +Cc: gdb-patches On Sat, Feb 04, 2006 at 04:01:07PM -0800, Ian Lance Taylor wrote: > Well, OK, let's try this. Create a new pipe. Create yet another new > thread which reads from the original pipe and writes to the new pipe. > Freopen the new pipe onto stdin. When the new thread writes to the > new pipe, it signals the event loop once, and then waits. When the > event loop gets the signal, it indicates that it is OK to read from > stdin, the new pipe. When you reenter the event loop, it signals the > new thread to tell it that it is interested in more data and more > signals. I'm not convinced this would work. Remember, not only do we read from this pipe just about everywhere, a lot of the time we don't pass through the event loop. We'd have to have a thread which did blocking reads from the original pipe, wrote that data straight through to a new pipe, and signalled an event whenever it performed a write. Not only would that be tricky to get right, but we've also traded off a thread when selecting for a thread all the time. Do you think the 10ms poll is sufficiently nasty to justify pursuing this? I suppose I could be convinced to give it another try... -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-05 22:00 ` Daniel Jacobowitz @ 2006-02-06 3:27 ` Ian Lance Taylor 2006-02-06 4:03 ` Daniel Jacobowitz 0 siblings, 1 reply; 26+ messages in thread From: Ian Lance Taylor @ 2006-02-06 3:27 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: gdb-patches Daniel Jacobowitz <drow@false.org> writes: > On Sat, Feb 04, 2006 at 04:01:07PM -0800, Ian Lance Taylor wrote: > > Well, OK, let's try this. Create a new pipe. Create yet another new > > thread which reads from the original pipe and writes to the new pipe. > > Freopen the new pipe onto stdin. When the new thread writes to the > > new pipe, it signals the event loop once, and then waits. When the > > event loop gets the signal, it indicates that it is OK to read from > > stdin, the new pipe. When you reenter the event loop, it signals the > > new thread to tell it that it is interested in more data and more > > signals. > > I'm not convinced this would work. Remember, not only do we read from > this pipe just about everywhere, a lot of the time we don't pass > through the event loop. We'd have to have a thread which did blocking > reads from the original pipe, wrote that data straight through to a new > pipe, and signalled an event whenever it performed a write. Not only > would that be tricky to get right, but we've also traded off a thread > when selecting for a thread all the time. I guess I had assumed that you were going to have a thread around all the time anyhow. Is creating a thread on Windows so cheap that one can do it every time gdb calls select? I also don't know whether it would work. > Do you think the 10ms poll is sufficiently nasty to justify pursuing > this? I suppose I could be convinced to give it another try... I think the 10ms poll is pretty nasty, both because polling is nasty in general and because it introduces a delay when using gdb in a script. But, hey, it's only Windows, so there is certainly a limit to how much I care. Ian ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-06 3:27 ` Ian Lance Taylor @ 2006-02-06 4:03 ` Daniel Jacobowitz 0 siblings, 0 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-06 4:03 UTC (permalink / raw) To: Ian Lance Taylor; +Cc: gdb-patches On Sun, Feb 05, 2006 at 07:27:41PM -0800, Ian Lance Taylor wrote: > I guess I had assumed that you were going to have a thread around all > the time anyhow. Is creating a thread on Windows so cheap that one > can do it every time gdb calls select? Yes, it is that cheap. But I wasn't clear - we have the thread the whole time, but we don't rely on it; it goes to sleep and we wake it when selecting. It only polls when GDB is idle in a select call for this fd. (Actually, I think that it continues to poll for some time afterwards until the next byte of data does arrive on that pipe, or the pipe's closed. That's just because (A) it's cheap, and (B) I had no other reason to have a cleanup hook after the end of select.) -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-03 22:05 RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports Daniel Jacobowitz 2006-02-03 22:08 ` Daniel Jacobowitz 2006-02-04 6:28 ` Ian Lance Taylor @ 2006-02-04 12:38 ` Eli Zaretskii 2006-02-04 15:11 ` Daniel Jacobowitz 2006-02-06 21:02 ` Daniel Jacobowitz 3 siblings, 1 reply; 26+ messages in thread From: Eli Zaretskii @ 2006-02-04 12:38 UTC (permalink / raw) To: gdb-patches > Date: Fri, 3 Feb 2006 17:05:29 -0500 > From: Daniel Jacobowitz <drow@false.org> > > The primary ugly bit of this patch is the select wrapper. Windows has > interfaces for all these things which map to Unix file descriptors, but > while the Unix interfaces are actually compatible, the Windows interfaces > are just designed along similar principles. So you can handle a serial port > in roughly the same way you handle a console window.... but only roughly. > In fact you need to know what sort of device is behind each "file > descriptor", in order to handle it appropriately. Could you elaborate a bit? I cannot easily see where that device knowledge is present in the patch; it all looks to me like several instances of the same code with almost identical flow. In particular, I thought WaitForMultipleObjects could handle any kind of handle, be it a pipe, a socket, a console, a process, or a file. Even if the code is slightly different in that it calls different OS API functions, cannot it all be expressed as the same code that uses a function table indexed by the interface type? > The one I'm least proud of is pipes - there does not appear to be a way to > sleep and have the OS wake you when data is available on a pipe. ??? Again, doesn't WaitForMultipleObjects fit the bill? > I've tested it. I'm not sure what else to say about it. Is it OK? Are > there things blatantly wrong with it that I've missed? Are there particular > bits that you think are just too ugly, and if so, do you have any > suggestions on how to make them less ugly? I'd like an explanation about this paradigm: > + wait_events[0] = state->stop_select; > + wait_events[1] = h; > + > + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); > + > + if (event_index == WAIT_OBJECT_0 > + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) > + { > + CloseHandle (state->stop_select); > + return 0; > + } (You have similar code in gdb_select and elsewhere.) Why do you need to wait for an object again with WaitForSingleObject, after you've just waited for it in WaitForMultipleObjects? Otherwise, I'm okay with these patches, except that... > - Windows serial support. This definitely deserves a NEWS entry, > included. ...I don't think you included this entry. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 12:38 ` Eli Zaretskii @ 2006-02-04 15:11 ` Daniel Jacobowitz 2006-02-04 15:23 ` Eli Zaretskii 0 siblings, 1 reply; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-04 15:11 UTC (permalink / raw) To: gdb-patches On Sat, Feb 04, 2006 at 02:38:56PM +0200, Eli Zaretskii wrote: > > Date: Fri, 3 Feb 2006 17:05:29 -0500 > > From: Daniel Jacobowitz <drow@false.org> > > > > The primary ugly bit of this patch is the select wrapper. Windows has > > interfaces for all these things which map to Unix file descriptors, but > > while the Unix interfaces are actually compatible, the Windows interfaces > > are just designed along similar principles. So you can handle a serial port > > in roughly the same way you handle a console window.... but only roughly. > > In fact you need to know what sort of device is behind each "file > > descriptor", in order to handle it appropriately. > > Could you elaborate a bit? I cannot easily see where that device > knowledge is present in the patch; it all looks to me like several > instances of the same code with almost identical flow. > > In particular, I thought WaitForMultipleObjects could handle any kind > of handle, be it a pipe, a socket, a console, a process, or a file. > > Even if the code is slightly different in that it calls different OS > API functions, cannot it all be expressed as the same code that uses a > function table indexed by the interface type? Nope. Well, in a sense, that is what I've done - the serial_wait_handle interface feeds back to a central loop using WaitForMultipleObjects. The problem is that, yes, all these objects are HANDLEs, and WaitForMultipleObjects can wait for many kinds of HANDLEs. But there's different things that "waiting for a handle" might mean, and it doesn't happen to pick the right one. So what I'm doing is using other handles, controlled by threads or async I/O functions, to signal the conditions we're interested in. The four I implemented are: - Serial. Yes, I confess this is more different from the others than it has to be; the most elegant way was to open the file in "overlapped" mode, and then issue an overlapped (similar to non-blocking) wait for EV_RXCHAR. It could be reworked to use a thread, like the other three, but it would still need to use WaitCommEvent. I did this one first. As far as I can tell you can't wait directly on a serial handle at all, or if you can, the MSDN documentation doesn't tell you what will cause it to become signalled. - Console. You can wait on a console handle, and Mark's previous code did so. However, it turns out, this signals for any event in the input queue - including key release events! But reading from the console will block since that only fetches keypress events. So, we need to explicitly look for keypress events and discard other things. - Pipes. There's just no wait function. You can wait on a connected pipe, but it's always signalled as far as I've been able to determine (and it's undocumented). You can wait on a named pipe, but that only waits for a connection to be available, not data. - Sockets. They're pretty easy actually; you can associate the socket with an arbitrary event object. But afterwards you need to make a socket-specific call to figure out whether you got a read or an error; otherwise GDB doesn't detect hangups. > I'd like an explanation about this paradigm: > > > + wait_events[0] = state->stop_select; > > + wait_events[1] = h; > > + > > + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); > > + > > + if (event_index == WAIT_OBJECT_0 > > + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) > > + { > > + CloseHandle (state->stop_select); > > + return 0; > > + } > > (You have similar code in gdb_select and elsewhere.) Why do you need > to wait for an object again with WaitForSingleObject, after you've > just waited for it in WaitForMultipleObjects? WaitForMultipleObjects returns a single result. However, both of the input objects could have been signalled before we woke. The case this is trying to handle is WaitForMultipleObjects returning WAIT_OBJECT_0 + 1, and then stop_select being signalled; if someone has asked for the thread to exit, we should do so immediately, because the other handles we rely on will have been closed. (Yes, this isn't 100% perfect on race conditions. However, it is as close as I was able to come up with. I suppose I could kill and restart the threads for every select...) > > - Windows serial support. This definitely deserves a NEWS entry, > > included. > > ...I don't think you included this entry. Oops. I added it while I was writing that bullet point. +* Improved Windows host support + +GDB now builds as a cross debugger hosted on i686-mingw32, including +native console support, and remote communications using either +network sockets or serial ports. + -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 15:11 ` Daniel Jacobowitz @ 2006-02-04 15:23 ` Eli Zaretskii 2006-02-04 15:30 ` Daniel Jacobowitz 0 siblings, 1 reply; 26+ messages in thread From: Eli Zaretskii @ 2006-02-04 15:23 UTC (permalink / raw) To: gdb-patches > Date: Sat, 4 Feb 2006 10:11:24 -0500 > From: Daniel Jacobowitz <drow@false.org> > > The problem is that, yes, all these objects are HANDLEs, and > WaitForMultipleObjects can wait for many kinds of HANDLEs. But there's > different things that "waiting for a handle" might mean, and it doesn't > happen to pick the right one. So what I'm doing is using other > handles, controlled by threads or async I/O functions, to signal the > conditions we're interested in. Thanks for the explanations. It sounds like there's no way to do this significantly better than you did. So I'm satisfied and think that your patches can go in (modulo the on-going discussion of where to put them). Out of curiosity: does Cygwin's `select' use a similar object-specific code to handle different types of handles? > > ...I don't think you included this entry. > > Oops. I added it while I was writing that bullet point. > > +* Improved Windows host support > + > +GDB now builds as a cross debugger hosted on i686-mingw32, including > +native console support, and remote communications using either > +network sockets or serial ports. Thanks. I think this is good. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-04 15:23 ` Eli Zaretskii @ 2006-02-04 15:30 ` Daniel Jacobowitz 0 siblings, 0 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-04 15:30 UTC (permalink / raw) To: gdb-patches On Sat, Feb 04, 2006 at 05:23:38PM +0200, Eli Zaretskii wrote: > Out of curiosity: does Cygwin's `select' use a similar object-specific > code to handle different types of handles? Yes, it does. Since the POSIX emulation is considerably more thorough than the job I've done here, they have many more methods, but ever file descriptor in Cygwin is mapped to a Windows handle and an object providing various methods (including a thread to run during select and a peek method, as it happens). If anyone's wondering, I didn't borrow the code from Cygwin, and upon a little more reflection I don't think it would have been practical to do so without linking with all of Cygwin anyway. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-03 22:05 RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports Daniel Jacobowitz ` (2 preceding siblings ...) 2006-02-04 12:38 ` Eli Zaretskii @ 2006-02-06 21:02 ` Daniel Jacobowitz 2006-02-06 23:02 ` Mark Kettenis 2006-02-07 4:41 ` Eli Zaretskii 3 siblings, 2 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-06 21:02 UTC (permalink / raw) To: gdb-patches On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote: > Fixes, all for a mingw32-hosted GDB: Here is a revised version, in which the Windows select magic does not live in event-loop.c, and including the NEWS entry. I haven't changed the pipe polling code; thanks to Ian for his suggestions, but I'm not sufficiently sure they're workable. Are there any bits of this patch that strike you as too ugly or in the wrong place? -- Daniel Jacobowitz CodeSourcery 2006-02-03 Daniel Jacobowitz <dan@codesourcery.com> * NEWS: Mention native Windows support. * Makefile.in (gdb_select_h, ser_tcp_h): New. (ALLDEPFILES): Add ser-mingw.c. (event-loop.o, mingw-hdep.o, ser-base.o, ser-tcp.o): Update. (ser-mingw.o): New rule. * configure: Regenerated. * configure.in: Add ser-mingw.o for mingw32. * ser-mingw.c: New file. * event-loop.c: Include "gdb_select.h". (gdb_select): Make global. Update comments. Call mingw_select instead of inlining MinGW-specific code. * ser-base.c: Include "gdb_select.h". (ser_base_wait_for): Use gdb_select. * serial.c (serial_for_fd): New function. (serial_fdopen): Try "terminal" before "hardwire". Initialize the allocated struct serial. (serial_wait_handle): New function. * serial.h (serial_for_fd, serial_wait_handle): New prototypes. (struct serial_ops) [USE_WIN32API]: Add wait_handle. * gdb_select.h: New file. * ser-tcp.c: Include "ser-tcp.h". Remove unused "ser-unix.h" include. (net_close, net_read_prim, net_write_prim): Make global. (net_open): Likewise. Pass an exception set to select. Whitespace fix. (_initialize_ser_tcp) [USE_WIN32API]: Do not register TCP support here. * ser-tcp.h: New file. * inflow.c (gdb_has_a_terminal): Don't initialize stdin_serial here. (initialize_stdin_serial): New function. * terminal.h (initialize_stdin_serial): New prototype. * top.c (gdb_init): Call initialize_stdin_serial. * config/i386/xm-mingw.h (mingw_select): New prototype. * mingw-hdep.c (gdb_select): New function, cloned from gdb_select in event-loop.c. Add exception condition support. Use serial_for_fd and serial_wait_handle. Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/Makefile.in 2006-02-06 15:47:12.000000000 -0500 @@ -692,6 +692,7 @@ gdb_obstack_h = gdb_obstack.h $(obstack_ gdb_proc_service_h = gdb_proc_service.h $(gregset_h) gdb_ptrace_h = gdb_ptrace.h gdb_regex_h = gdb_regex.h $(xregex_h) +gdb_select_h = gdb_select.h gdb_stabs_h = gdb-stabs.h gdb_stat_h = gdb_stat.h gdb_string_h = gdb_string.h @@ -764,6 +765,7 @@ scm_tags_h = scm-tags.h sentinel_frame_h = sentinel-frame.h serial_h = serial.h ser_base_h = ser-base.h +ser_tcp_h = ser-tcp.h ser_unix_h = ser-unix.h shnbsd_tdep_h = shnbsd-tdep.h sh_tdep_h = sh-tdep.h @@ -1440,7 +1442,7 @@ ALLDEPFILES = \ remote-st.c remote-utils.c dcache.c \ rs6000-nat.c rs6000-tdep.c \ s390-tdep.c s390-nat.c \ - ser-go32.c ser-pipe.c ser-tcp.c \ + ser-go32.c ser-pipe.c ser-tcp.c ser-mingw.c \ sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \ sol2-tdep.c \ solib-irix.c solib-svr4.c solib-sunos.c \ @@ -1920,7 +1922,7 @@ eval.o: eval.c $(defs_h) $(gdb_string_h) $(f_lang_h) $(cp_abi_h) $(infcall_h) $(objc_lang_h) $(block_h) \ $(parser_defs_h) $(cp_support_h) event-loop.o: event-loop.c $(defs_h) $(event_loop_h) $(event_top_h) \ - $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) + $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) $(gdb_select_h) event-top.o: event-top.c $(defs_h) $(top_h) $(inferior_h) $(target_h) \ $(terminal_h) $(event_loop_h) $(event_top_h) $(interps_h) \ $(exceptions_h) $(gdbcmd_h) $(readline_h) $(readline_history_h) @@ -2280,7 +2282,8 @@ memattr.o: memattr.c $(defs_h) $(command $(target_h) $(value_h) $(language_h) $(gdb_string_h) mem-break.o: mem-break.c $(defs_h) $(symtab_h) $(breakpoint_h) $(inferior_h) \ $(target_h) -mingw-hdep.o: mingw-hdep.c $(defs_h) $(gdb_string_h) +mingw-hdep.o: mingw-hdep.c $(defs_h) $(serial_h) $(gdb_assert_h) \ + $(gdb_select_h) $(gdb_string_h) minsyms.o: minsyms.c $(defs_h) $(gdb_string_h) $(symtab_h) $(bfd_h) \ $(symfile_h) $(objfiles_h) $(demangle_h) $(value_h) $(cp_abi_h) mips64obsd-nat.o: mips64obsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \ @@ -2512,13 +2515,15 @@ ser-e7kpc.o: ser-e7kpc.c $(defs_h) $(ser ser-go32.o: ser-go32.c $(defs_h) $(gdbcmd_h) $(serial_h) $(gdb_string_h) serial.o: serial.c $(defs_h) $(serial_h) $(gdb_string_h) $(gdbcmd_h) ser-base.o: ser-base.c $(defs_h) $(serial_h) $(ser_base_h) $(event_loop_h) \ - $(gdb_string_h) + $(gdb_select_h) $(gdb_string_h) ser-pipe.o: ser-pipe.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ $(gdb_vfork_h) $(gdb_string_h) -ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ +ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_tcp_h) \ $(gdb_string_h) ser-unix.o: ser-unix.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ $(terminal_h) $(gdb_string_h) +ser-mingw.o: ser-mingw.c $(defs_h) $(serial_h) $(ser_base_h) \ + $(ser_tcp_h) $(gdb_assert_h) $(gdb_string_h) sh3-rom.o: sh3-rom.c $(defs_h) $(gdbcore_h) $(target_h) $(monitor_h) \ $(serial_h) $(srec_h) $(arch_utils_h) $(regcache_h) $(gdb_string_h) \ $(sh_tdep_h) Index: src/gdb/configure =================================================================== --- src.orig/gdb/configure 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/configure 2006-02-06 15:44:41.000000000 -0500 @@ -20093,7 +20093,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser- case ${host} in *go32* ) SER_HARDWIRE=ser-go32.o ;; *djgpp* ) SER_HARDWIRE=ser-go32.o ;; - *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;; + *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;; esac Index: src/gdb/configure.ac =================================================================== --- src.orig/gdb/configure.ac 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/configure.ac 2006-02-06 15:44:41.000000000 -0500 @@ -1,5 +1,6 @@ dnl Autoconf configure script for GDB, the GNU debugger. -dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +dnl 2005, 2006 dnl Free Software Foundation, Inc. dnl dnl This file is part of GDB. @@ -1198,7 +1199,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser- case ${host} in *go32* ) SER_HARDWIRE=ser-go32.o ;; *djgpp* ) SER_HARDWIRE=ser-go32.o ;; - *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;; + *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;; esac AC_SUBST(SER_HARDWIRE) Index: src/gdb/event-loop.c =================================================================== --- src.orig/gdb/event-loop.c 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/event-loop.c 2006-02-06 15:47:01.000000000 -0500 @@ -1,5 +1,6 @@ /* Event loop machinery for GDB, the GNU debugger. - Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006 + Free Software Foundation, Inc. Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions. This file is part of GDB. @@ -37,6 +38,7 @@ #include <sys/time.h> #include "exceptions.h" #include "gdb_assert.h" +#include "gdb_select.h" typedef struct gdb_event gdb_event; typedef void (event_handler_func) (int); @@ -731,92 +733,14 @@ handle_file_event (int event_file_desc) } } -/* Wrapper for select. This function is not yet exported from this - file because it is not sufficiently general. For example, - ser-base.c uses select to check for socket activity, and this - function does not support sockets under Windows, so we do not want - to use gdb_select in ser-base.c. */ +/* Wrapper for select. */ -static int +int gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { #ifdef USE_WIN32API - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - HANDLE h; - DWORD event; - DWORD num_handles; - int fd; - int num_ready; - - num_ready = 0; - num_handles = 0; - for (fd = 0; fd < n; ++fd) - { - /* There is no support yet for WRITEFDS. At present, this isn't - used by GDB -- but we do not want to silently ignore WRITEFDS - if something starts using it. */ - gdb_assert (!FD_ISSET (fd, writefds)); - if (!FD_ISSET (fd, readfds) - && !FD_ISSET (fd, exceptfds)) - continue; - h = (HANDLE) _get_osfhandle (fd); - if (h == INVALID_HANDLE_VALUE) - { - /* If the underlying handle is INVALID_HANDLE_VALUE, then - this descriptor is no more. */ - if (FD_ISSET (fd, exceptfds)) - ++num_ready; - continue; - } - /* The only exceptional condition we recognize is a closed file - descriptor. Since we have already checked for that - condition, clear the exceptional bit for this descriptor. */ - FD_CLR (fd, exceptfds); - if (FD_ISSET (fd, readfds)) - { - gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); - handles[num_handles++] = h; - } - } - /* If we don't need to wait for any handles, we are done. */ - if (!num_handles) - return num_ready; - event = WaitForMultipleObjects (num_handles, - handles, - FALSE, - timeout - ? (timeout->tv_sec * 1000 + timeout->tv_usec) - : INFINITE); - /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the - HANDLES included an abandoned mutex. Since GDB doesn't use - mutexes, that should never occur. */ - gdb_assert (!(WAIT_ABANDONED_0 <= event - && event < WAIT_ABANDONED_0 + num_handles)); - if (event == WAIT_FAILED) - return -1; - if (event == WAIT_TIMEOUT) - return num_ready; - /* Run through the READFDS, clearing bits corresponding to descriptors - for which input is unavailable. */ - num_ready += num_handles; - h = handles[event - WAIT_OBJECT_0]; - for (fd = 0; fd < n; ++fd) - { - HANDLE fd_h; - if (!FD_ISSET (fd, readfds)) - continue; - fd_h = (HANDLE) _get_osfhandle (fd); - /* This handle might be ready, even though it wasn't the handle - returned by WaitForMultipleObjects. */ - if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) - { - FD_CLR (fd, readfds); - --num_ready; - } - } - - return num_ready; + return mingw_select (n, readfds, writefds, exceptfds, timeout); #else return select (n, readfds, writefds, exceptfds, timeout); #endif Index: src/gdb/ser-base.c =================================================================== --- src.orig/gdb/ser-base.c 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/ser-base.c 2006-02-06 15:44:41.000000000 -0500 @@ -1,7 +1,7 @@ /* Generic serial interface functions. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, - 2003, 2004, 2005 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -25,6 +25,7 @@ #include "ser-base.h" #include "event-loop.h" +#include "gdb_select.h" #include "gdb_string.h" #include <sys/time.h> #ifdef USE_WIN32API @@ -202,9 +203,9 @@ ser_base_wait_for (struct serial *scb, i FD_SET (scb->fd, &exceptfds); if (timeout >= 0) - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); + numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); else - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0); + numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0); if (numfds <= 0) { Index: src/gdb/serial.c =================================================================== --- src.orig/gdb/serial.c 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/serial.c 2006-02-06 15:44:41.000000000 -0500 @@ -1,7 +1,7 @@ /* Generic serial interface routines Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002 Free Software Foundation, Inc. + 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -233,6 +233,22 @@ serial_open (const char *name) return scb; } +/* Return the open serial device for FD, if found, or NULL if FD + is not already opened. */ + +struct serial * +serial_for_fd (int fd) +{ + struct serial *scb; + struct serial_ops *ops; + + for (scb = scb_base; scb; scb = scb->next) + if (scb->fd == fd) + return scb; + + return NULL; +} + struct serial * serial_fdopen (const int fd) { @@ -246,12 +262,14 @@ serial_fdopen (const int fd) return scb; } - ops = serial_interface_lookup ("hardwire"); + ops = serial_interface_lookup ("terminal"); + if (!ops) + ops = serial_interface_lookup ("hardwire"); if (!ops) return NULL; - scb = XMALLOC (struct serial); + scb = XCALLOC (1, struct serial); scb->ops = ops; @@ -524,6 +542,19 @@ serial_debug_p (struct serial *scb) return scb->debug_p || global_serial_debug_p; } +#ifdef USE_WIN32API +void +serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + if (scb->ops->wait_handle) + scb->ops->wait_handle (scb, read, except); + else + { + *read = (HANDLE) _get_osfhandle (scb->fd); + *except = NULL; + } +} +#endif #if 0 /* The connect command is #if 0 because I hadn't thought of an elegant Index: src/gdb/serial.h =================================================================== --- src.orig/gdb/serial.h 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/serial.h 2006-02-06 15:44:41.000000000 -0500 @@ -1,5 +1,6 @@ /* Remote serial support interface definitions for GDB, the GNU Debugger. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000 + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, + 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -22,6 +23,10 @@ #ifndef SERIAL_H #define SERIAL_H +#ifdef USE_WIN32API +#include <windows.h> +#endif + struct ui_file; /* For most routines, if a failure is indicated, then errno should be @@ -41,6 +46,10 @@ struct serial; extern struct serial *serial_open (const char *name); +/* Find an already opened serial stream using a file handle. */ + +extern struct serial *serial_for_fd (int fd); + /* Open a new serial stream using a file handle. */ extern struct serial *serial_fdopen (const int fd); @@ -238,6 +247,13 @@ struct serial_ops /* Perform a low-level write operation, writing (at most) COUNT bytes from BUF. */ int (*write_prim)(struct serial *scb, const void *buf, size_t count); + +#ifdef USE_WIN32API + /* Return a handle to wait on, indicating available data from SCB + when signaled, in *READ. Return a handle indicating errors + in *EXCEPT. */ + void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except); +#endif /* USE_WIN32API */ }; /* Add a new serial interface to the interface list */ @@ -248,4 +264,12 @@ extern void serial_add_interface (struct extern void serial_log_command (const char *); +#ifdef USE_WIN32API + +/* Windows-only: find or create handles that we can wait on for this + serial device. */ +extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *); + +#endif /* USE_WIN32API */ + #endif /* SERIAL_H */ Index: src/gdb/gdb_select.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/gdb_select.h 2006-02-06 15:44:41.000000000 -0500 @@ -0,0 +1,37 @@ +/* Slightly more portable version of <sys/select.h>. + + Copyright (C) 2006 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#if !defined(GDB_SELECT_H) +#define GDB_SELECT_H + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifdef USE_WIN32API +#include <winsock2.h> +#endif + +extern int gdb_select (int n, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); + +#endif /* !defined(GDB_STRING_H) */ Index: src/gdb/ser-tcp.c =================================================================== --- src.orig/gdb/ser-tcp.c 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/ser-tcp.c 2006-02-06 15:44:41.000000000 -0500 @@ -1,6 +1,6 @@ /* Serial interface for raw TCP connections on Un*x like systems. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005 + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -23,7 +23,7 @@ #include "defs.h" #include "serial.h" #include "ser-base.h" -#include "ser-unix.h" +#include "ser-tcp.h" #include <sys/types.h> @@ -56,8 +56,6 @@ typedef int socklen_t; #endif -static int net_open (struct serial *scb, const char *name); -static void net_close (struct serial *scb); void _initialize_ser_tcp (void); /* seconds to wait for connect */ @@ -67,7 +65,7 @@ void _initialize_ser_tcp (void); /* Open a tcp socket */ -static int +int net_open (struct serial *scb, const char *name) { char *port_str, hostname[100]; @@ -153,7 +151,7 @@ net_open (struct serial *scb, const char { /* looks like we need to wait for the connect */ struct timeval t; - fd_set rset, wset; + fd_set rset, wset, eset; int polls = 0; FD_ZERO (&rset); @@ -174,10 +172,13 @@ net_open (struct serial *scb, const char FD_SET (scb->fd, &rset); wset = rset; + eset = rset; t.tv_sec = 0; t.tv_usec = 1000000 / POLL_INTERVAL; - n = select (scb->fd + 1, &rset, &wset, NULL, &t); + /* Windows returns connection failure in eset rather than wset. + How perverse. */ + n = select (scb->fd + 1, &rset, &wset, &eset, &t); polls++; } while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL); @@ -194,7 +195,7 @@ net_open (struct serial *scb, const char { int res, err; socklen_t len; - len = sizeof(err); + len = sizeof (err); /* On Windows, the fourth parameter to getsockopt is a "char *"; on UNIX systems it is generally "void *". The cast to "void *" is OK everywhere, since in C "void *" can be implicitly @@ -230,7 +231,7 @@ net_open (struct serial *scb, const char return 0; } -static void +void net_close (struct serial *scb) { if (scb->fd < 0) @@ -240,13 +241,13 @@ net_close (struct serial *scb) scb->fd = -1; } -static int +int net_read_prim (struct serial *scb, size_t count) { return recv (scb->fd, scb->buf, count, 0); } -static int +int net_write_prim (struct serial *scb, const void *buf, size_t count) { return send (scb->fd, buf, count, 0); @@ -255,13 +256,12 @@ net_write_prim (struct serial *scb, cons void _initialize_ser_tcp (void) { - struct serial_ops *ops; #ifdef USE_WIN32API - WSADATA wsa_data; - if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) - /* WinSock is unavailable. */ - return; -#endif + /* Do nothing; the TCP serial operations will be initialized in + ser-mingw.c. */ + return; +#else + struct serial_ops *ops; ops = XMALLOC (struct serial_ops); memset (ops, 0, sizeof (struct serial_ops)); ops->name = "tcp"; @@ -285,4 +285,5 @@ _initialize_ser_tcp (void) ops->read_prim = net_read_prim; ops->write_prim = net_write_prim; serial_add_interface (ops); +#endif /* USE_WIN32API */ } Index: src/gdb/ser-tcp.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/ser-tcp.h 2006-02-06 15:44:41.000000000 -0500 @@ -0,0 +1,32 @@ +/* Serial interface for raw TCP connections on Un*x like systems. + + Copyright (C) 2006 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef SER_TCP_H +#define SER_TCP_H + +struct serial; + +extern int net_open (struct serial *scb, const char *name); +extern void net_close (struct serial *scb); +extern int net_read_prim (struct serial *scb, size_t count); +extern int net_write_prim (struct serial *scb, const void *buf, size_t count); + +#endif Index: src/gdb/inflow.c =================================================================== --- src.orig/gdb/inflow.c 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/inflow.c 2006-02-06 15:44:41.000000000 -0500 @@ -1,6 +1,6 @@ /* Low level interface to ptrace, for GDB when running under Unix. Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, - 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -129,7 +129,6 @@ gdb_has_a_terminal (void) #endif gdb_has_a_terminal_flag = no; - stdin_serial = serial_fdopen (0); if (stdin_serial != NULL) { our_ttystate = serial_get_tty_state (stdin_serial); @@ -730,6 +729,18 @@ gdb_setpgid (void) return retval; } +/* Get all the current tty settings (including whether we have a + tty at all!). We can't do this in _initialize_inflow because + serial_fdopen() won't work until the serial_ops_list is + initialized, but we don't want to do it lazily either, so + that we can guarantee stdin_serial is opened if there is + a terminal. */ +void +initialize_stdin_serial (void) +{ + stdin_serial = serial_fdopen (0); +} + void _initialize_inflow (void) { Index: src/gdb/terminal.h =================================================================== --- src.orig/gdb/terminal.h 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/terminal.h 2006-02-06 15:44:41.000000000 -0500 @@ -1,5 +1,6 @@ /* Terminal interface definitions for GDB, the GNU Debugger. - Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000 + Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000, + 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -88,4 +89,7 @@ extern int job_control; we lack job control. */ extern int gdb_setpgid (void); +/* Set up a serial structure describing standard input. In inflow.c. */ +extern void initialize_stdin_serial (void); + #endif /* !defined (TERMINAL_H) */ Index: src/gdb/top.c =================================================================== --- src.orig/gdb/top.c 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/top.c 2006-02-06 15:44:41.000000000 -0500 @@ -1550,6 +1550,8 @@ gdb_init (char *argv0) init_cli_cmds(); init_main (); /* But that omits this file! Do it now */ + initialize_stdin_serial (); + async_init_signals (); /* We need a default language for parsing expressions, so simple things like Index: src/gdb/NEWS =================================================================== --- src.orig/gdb/NEWS 2006-02-06 15:21:55.000000000 -0500 +++ src/gdb/NEWS 2006-02-06 15:44:41.000000000 -0500 @@ -41,6 +41,12 @@ detach-fork <n> Delete a fork from the Morpho Technologies ms2 ms1-elf +* Improved Windows host support + +GDB now builds as a cross debugger hosted on i686-mingw32, including +native console support, and remote communications using either +network sockets or serial ports. + * REMOVED features The ARM rdi-share module. Index: src/gdb/ser-mingw.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/ser-mingw.c 2006-02-06 15:44:41.000000000 -0500 @@ -0,0 +1,796 @@ +/* Serial interface for local (hardwired) serial ports on Windows systems + + Copyright (C) 2006 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "serial.h" +#include "ser-base.h" +#include "ser-tcp.h" + +#include <windows.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> + +#include "gdb_assert.h" +#include "gdb_string.h" + +void _initialize_ser_windows (void); + +struct ser_windows_state +{ + int in_progress; + OVERLAPPED ov; + DWORD lastCommMask; + HANDLE except_event; +}; + +/* Open up a real live device for serial I/O. */ + +static int +ser_windows_open (struct serial *scb, const char *name) +{ + HANDLE h; + struct ser_windows_state *state; + COMMTIMEOUTS timeouts; + + /* Only allow COM ports. */ + if (strncmp (name, "COM", 3) != 0) + { + errno = ENOENT; + return -1; + } + + h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; + return -1; + } + + scb->fd = _open_osfhandle ((long) h, O_RDWR); + if (scb->fd < 0) + { + errno = ENOENT; + return -1; + } + + if (!SetCommMask (h, EV_RXCHAR)) + { + errno = EINVAL; + return -1; + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + if (!SetCommTimeouts (h, &timeouts)) + { + errno = EINVAL; + return -1; + } + + state = xmalloc (sizeof (struct ser_windows_state)); + memset (state, 0, sizeof (struct ser_windows_state)); + scb->state = state; + + /* Create a manual reset event to watch the input buffer. */ + state->ov.hEvent = CreateEvent (0, TRUE, FALSE, 0); + + /* Create a (currently unused) handle to record exceptions. */ + state->except_event = CreateEvent (0, TRUE, FALSE, 0); + + return 0; +} + +/* Wait for the output to drain away, as opposed to flushing (discarding) + it. */ + +static int +ser_windows_drain_output (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (FlushFileBuffers (h) != 0) ? 0 : -1; +} + +static int +ser_windows_flush_output (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (PurgeComm (h, PURGE_TXCLEAR) != 0) ? 0 : -1; +} + +static int +ser_windows_flush_input (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (PurgeComm (h, PURGE_RXCLEAR) != 0) ? 0 : -1; +} + +static int +ser_windows_send_break (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + if (SetCommBreak (h) == 0) + return -1; + + /* Delay for 250 milliseconds. */ + Sleep (250); + + if (ClearCommBreak (h)) + return -1; + + return 0; +} + +static void +ser_windows_raw (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return; + + state.fParity = FALSE; + state.fOutxCtsFlow = FALSE; + state.fOutxDsrFlow = FALSE; + state.fDtrControl = DTR_CONTROL_ENABLE; + state.fDsrSensitivity = FALSE; + state.fOutX = FALSE; + state.fInX = FALSE; + state.fNull = FALSE; + state.fAbortOnError = FALSE; + state.ByteSize = 8; + state.Parity = NOPARITY; + + scb->current_timeout = 0; + + if (SetCommState (h, &state) == 0) + warning (_("SetCommState failed\n")); +} + +static int +ser_windows_setstopbits (struct serial *scb, int num) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + switch (num) + { + case SERIAL_1_STOPBITS: + state.StopBits = ONESTOPBIT; + break; + case SERIAL_1_AND_A_HALF_STOPBITS: + state.StopBits = ONE5STOPBITS; + break; + case SERIAL_2_STOPBITS: + state.StopBits = TWOSTOPBITS; + break; + default: + return 1; + } + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + +static int +ser_windows_setbaudrate (struct serial *scb, int rate) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + state.BaudRate = rate; + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + +static void +ser_windows_close (struct serial *scb) +{ + struct ser_windows_state *state; + + /* Stop any pending selects. */ + CancelIo ((HANDLE) _get_osfhandle (scb->fd)); + state = scb->state; + CloseHandle (state->ov.hEvent); + CloseHandle (state->except_event); + + if (scb->fd < 0) + return; + + close (scb->fd); + scb->fd = -1; + + xfree (scb->state); +} + +static void +ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct ser_windows_state *state; + COMSTAT status; + DWORD errors; + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + state = scb->state; + + *except = state->except_event; + *read = state->ov.hEvent; + + if (state->in_progress) + return; + + /* Reset the mask - we are only interested in any characters which + arrive after this point, not characters which might have arrived + and already been read. */ + + /* This really, really shouldn't be necessary - just the second one. + But otherwise an internal flag for EV_RXCHAR does not get + cleared, and we get a duplicated event, if the last batch + of characters included at least two arriving close together. */ + if (!SetCommMask (h, 0)) + warning (_("ser_windows_wait_handle: reseting mask failed")); + + if (!SetCommMask (h, EV_RXCHAR)) + warning (_("ser_windows_wait_handle: reseting mask failed (2)")); + + /* There's a potential race condition here; we must check cbInQue + and not wait if that's nonzero. */ + + ClearCommError (h, &errors, &status); + if (status.cbInQue > 0) + { + SetEvent (state->ov.hEvent); + return; + } + + state->in_progress = 1; + ResetEvent (state->ov.hEvent); + state->lastCommMask = -2; + if (WaitCommEvent (h, &state->lastCommMask, &state->ov)) + { + gdb_assert (state->lastCommMask & EV_RXCHAR); + SetEvent (state->ov.hEvent); + } + else + gdb_assert (GetLastError () == ERROR_IO_PENDING); +} + +static int +ser_windows_read_prim (struct serial *scb, size_t count) +{ + struct ser_windows_state *state; + OVERLAPPED ov; + DWORD bytes_read, bytes_read_tmp; + HANDLE h; + gdb_byte *p; + + state = scb->state; + if (state->in_progress) + { + WaitForSingleObject (state->ov.hEvent, INFINITE); + state->in_progress = 0; + ResetEvent (state->ov.hEvent); + } + + memset (&ov, 0, sizeof (OVERLAPPED)); + ov.hEvent = CreateEvent (0, FALSE, FALSE, 0); + h = (HANDLE) _get_osfhandle (scb->fd); + + if (!ReadFile (h, scb->buf, /* count */ 1, &bytes_read, &ov)) + { + if (GetLastError () != ERROR_IO_PENDING + || !GetOverlappedResult (h, &ov, &bytes_read, TRUE)) + bytes_read = -1; + } + + CloseHandle (ov.hEvent); + return bytes_read; +} + +static int +ser_windows_write_prim (struct serial *scb, const void *buf, size_t len) +{ + struct ser_windows_state *state; + OVERLAPPED ov; + DWORD bytes_written; + HANDLE h; + + memset (&ov, 0, sizeof (OVERLAPPED)); + ov.hEvent = CreateEvent (0, FALSE, FALSE, 0); + h = (HANDLE) _get_osfhandle (scb->fd); + if (!WriteFile (h, buf, len, &bytes_written, &ov)) + { + if (GetLastError () != ERROR_IO_PENDING + || !GetOverlappedResult (h, &ov, &bytes_written, TRUE)) + bytes_written = -1; + } + + CloseHandle (ov.hEvent); + return bytes_written; +} + +struct ser_console_state +{ + HANDLE read_event; + HANDLE except_event; + + HANDLE start_select; + HANDLE stop_select; +}; + +static DWORD WINAPI +console_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state, state_copy; + int event_index, fd; + HANDLE h; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct ser_console_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + h = (HANDLE) _get_osfhandle (fd); + + while (1) + { + HANDLE wait_events[2]; + INPUT_RECORD record; + DWORD n_records; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + retry: + wait_events[0] = state->stop_select; + wait_events[1] = h; + + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Wait must have failed; assume an error has occured, e.g. + the handle has been closed. */ + SetEvent (state->except_event); + continue; + } + + /* We've got a pending event on the console. See if it's + of interest. */ + if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1) + { + /* Something went wrong. Maybe the console is gone. */ + SetEvent (state->except_event); + continue; + } + + if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) + { + /* This is really a keypress. */ + SetEvent (state->read_event); + continue; + } + + /* Otherwise discard it and wait again. */ + ReadConsoleInput (h, &record, 1, &n_records); + goto retry; + } +} + +static int +fd_is_pipe (int fd) +{ + if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL)) + return 1; + else + return 0; +} + +static DWORD WINAPI +pipe_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state, state_copy; + int event_index, fd; + HANDLE h; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct ser_console_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + h = (HANDLE) _get_osfhandle (fd); + + while (1) + { + HANDLE wait_events[2]; + DWORD n_avail; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + retry: + if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) + { + SetEvent (state->except_event); + continue; + } + + if (n_avail > 0) + { + SetEvent (state->read_event); + continue; + } + + if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + Sleep (10); + goto retry; + } +} + +static void +ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct ser_console_state *state = scb->state; + + if (state == NULL) + { + DWORD threadId; + int is_tty; + + is_tty = isatty (scb->fd); + if (!is_tty && !fd_is_pipe (scb->fd)) + { + *read = NULL; + *except = NULL; + return; + } + + state = xmalloc (sizeof (struct ser_console_state)); + memset (state, 0, sizeof (struct ser_console_state)); + scb->state = state; + + /* Create auto reset events to wake and terminate the select thread. */ + state->start_select = CreateEvent (0, FALSE, FALSE, 0); + state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Create our own events to report read and exceptions separately. + The exception event is currently never used. */ + state->read_event = CreateEvent (0, FALSE, FALSE, 0); + state->except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* And finally start the select thread. */ + if (is_tty) + CreateThread (NULL, 0, console_select_thread, scb, 0, &threadId); + else + CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); + } + + ResetEvent (state->read_event); + ResetEvent (state->except_event); + + SetEvent (state->start_select); + + *read = state->read_event; + *except = state->except_event; +} + +static void +ser_console_close (struct serial *scb) +{ + struct ser_console_state *state = scb->state; + + if (scb->state) + { + SetEvent (state->stop_select); + + CloseHandle (state->read_event); + CloseHandle (state->except_event); + + xfree (scb->state); + } +} + +struct ser_console_ttystate +{ + int is_a_tty; +}; + +static serial_ttystate +ser_console_get_tty_state (struct serial *scb) +{ + if (isatty (scb->fd)) + { + struct ser_console_ttystate *state; + state = (struct ser_console_ttystate *) xmalloc (sizeof *state); + state->is_a_tty = 1; + return state; + } + else + return NULL; +} + +struct net_windows_state +{ + HANDLE read_event; + HANDLE except_event; + + HANDLE start_select; + HANDLE stop_select; + HANDLE sock_event; +}; + +static DWORD WINAPI +net_windows_select_thread (void *arg) +{ + struct serial *scb = arg; + struct net_windows_state *state, state_copy; + int event_index, fd; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct net_windows_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + while (1) + { + HANDLE wait_events[2]; + WSANETWORKEVENTS events; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + wait_events[0] = state->stop_select; + wait_events[1] = state->sock_event; + + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Some error has occured. Assume that this is an error + condition. */ + SetEvent (state->except_event); + continue; + } + + /* Enumerate the internal network events, and reset the object that + signalled us to catch the next event. */ + WSAEnumNetworkEvents (fd, state->sock_event, &events); + + if (events.lNetworkEvents & FD_READ) + SetEvent (state->read_event); + + if (events.lNetworkEvents & FD_CLOSE) + SetEvent (state->except_event); + } +} + +static void +net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct net_windows_state *state = scb->state; + + ResetEvent (state->read_event); + ResetEvent (state->except_event); + + SetEvent (state->start_select); + + *read = state->read_event; + *except = state->except_event; +} + +static int +net_windows_open (struct serial *scb, const char *name) +{ + struct net_windows_state *state; + int ret; + DWORD threadId; + + ret = net_open (scb, name); + if (ret != 0) + return ret; + + state = xmalloc (sizeof (struct net_windows_state)); + memset (state, 0, sizeof (struct net_windows_state)); + scb->state = state; + + /* Create auto reset events to wake and terminate the select thread. */ + state->start_select = CreateEvent (0, FALSE, FALSE, 0); + state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Associate an event with the socket. */ + state->sock_event = CreateEvent (0, TRUE, FALSE, 0); + WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE); + + /* Create our own events to report read and close separately. */ + state->read_event = CreateEvent (0, FALSE, FALSE, 0); + state->except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* And finally start the select thread. */ + CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId); + + return 0; +} + + +static void +net_windows_close (struct serial *scb) +{ + struct net_windows_state *state = scb->state; + + SetEvent (state->stop_select); + + CloseHandle (state->read_event); + CloseHandle (state->except_event); + CloseHandle (state->start_select); + CloseHandle (state->sock_event); + + xfree (scb->state); + + net_close (scb); +} + +void +_initialize_ser_windows (void) +{ + WSADATA wsa_data; + struct serial_ops *ops; + + /* First register the serial port driver. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "hardwire"; + ops->next = 0; + ops->open = ser_windows_open; + ops->close = ser_windows_close; + + ops->flush_output = ser_windows_flush_output; + ops->flush_input = ser_windows_flush_input; + ops->send_break = ser_windows_send_break; + + /* These are only used for stdin; we do not need them for serial + ports, so supply the standard dummies. */ + 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->go_raw = ser_windows_raw; + ops->setbaudrate = ser_windows_setbaudrate; + ops->setstopbits = ser_windows_setstopbits; + ops->drain_output = ser_windows_drain_output; + ops->readchar = ser_base_readchar; + ops->write = ser_base_write; + ops->async = ser_base_async; + ops->read_prim = ser_windows_read_prim; + ops->write_prim = ser_windows_write_prim; + ops->wait_handle = ser_windows_wait_handle; + + serial_add_interface (ops); + + /* Next create the dummy serial driver used for terminals. We only + provide the TTY-related methods. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + + ops->name = "terminal"; + ops->next = 0; + + ops->close = ser_console_close; + ops->get_tty_state = ser_console_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->drain_output = ser_base_drain_output; + ops->wait_handle = ser_console_wait_handle; + + serial_add_interface (ops); + + /* If WinSock works, register the TCP/UDP socket driver. */ + + if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) + /* WinSock is unavailable. */ + return; + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "tcp"; + ops->next = 0; + ops->open = net_windows_open; + ops->close = net_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 = net_read_prim; + ops->write_prim = net_write_prim; + ops->wait_handle = net_windows_wait_handle; + serial_add_interface (ops); +} Index: src/gdb/config/i386/xm-mingw.h =================================================================== --- src.orig/gdb/config/i386/xm-mingw.h 2006-02-06 15:23:13.000000000 -0500 +++ src/gdb/config/i386/xm-mingw.h 2006-02-06 15:45:37.000000000 -0500 @@ -28,3 +28,4 @@ char *mingw_strerror (int); # define strerror mingw_strerror #endif +int mingw_select (int, void *, void *, void *, void *); Index: src/gdb/mingw-hdep.c =================================================================== --- src.orig/gdb/mingw-hdep.c 2006-02-06 15:22:52.000000000 -0500 +++ src/gdb/mingw-hdep.c 2006-02-06 15:50:58.000000000 -0500 @@ -25,7 +25,10 @@ #define IN_MINGW_HDEP #include "defs.h" +#include "serial.h" +#include "gdb_assert.h" +#include "gdb_select.h" #include "gdb_string.h" #include <windows.h> @@ -65,3 +68,120 @@ mingw_strerror (int errnum) return buffer; } + +/* Wrapper for select. On Windows systems, where the select interface + only works for sockets, this uses the GDB serial abstraction to + handle sockets, consoles, pipes, and serial ports. + + The arguments to this function are the same as the traditional + arguments to select; the void * usage is necessary to avoid + including <windows.h> in order to prototype this function. */ + +int +mingw_select (int n, void *readfds_, void *writefds_, void *exceptfds_, + void *timeout_) +{ + static HANDLE never_handle; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + HANDLE h; + DWORD event; + DWORD num_handles; + int fd; + int num_ready; + int indx; + struct timeval *timeout = timeout_; + fd_set *readfds = readfds_, *writefds = writefds_, *exceptfds = exceptfds_; + + num_ready = 0; + num_handles = 0; + for (fd = 0; fd < n; ++fd) + { + HANDLE read = NULL, except = NULL; + struct serial *scb; + + /* There is no support yet for WRITEFDS. At present, this isn't + used by GDB -- but we do not want to silently ignore WRITEFDS + if something starts using it. */ + gdb_assert (!writefds || !FD_ISSET (fd, writefds)); + + if (!FD_ISSET (fd, readfds) + && !FD_ISSET (fd, exceptfds)) + continue; + h = (HANDLE) _get_osfhandle (fd); + + scb = serial_for_fd (fd); + if (scb) + serial_wait_handle (scb, &read, &except); + + if (read == NULL) + read = h; + if (except == NULL) + { + if (!never_handle) + never_handle = CreateEvent (0, FALSE, FALSE, 0); + + except = never_handle; + } + + if (FD_ISSET (fd, readfds)) + { + gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); + handles[num_handles++] = read; + } + + if (FD_ISSET (fd, exceptfds)) + { + gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); + handles[num_handles++] = except; + } + } + /* If we don't need to wait for any handles, we are done. */ + if (!num_handles) + return 0; + event = WaitForMultipleObjects (num_handles, + handles, + FALSE, + timeout + ? (timeout->tv_sec * 1000 + timeout->tv_usec) + : INFINITE); + /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the + HANDLES included an abandoned mutex. Since GDB doesn't use + mutexes, that should never occur. */ + gdb_assert (!(WAIT_ABANDONED_0 <= event + && event < WAIT_ABANDONED_0 + num_handles)); + if (event == WAIT_FAILED) + return -1; + if (event == WAIT_TIMEOUT) + return 0; + /* Run through the READFDS, clearing bits corresponding to descriptors + for which input is unavailable. */ + h = handles[event - WAIT_OBJECT_0]; + for (fd = 0, indx = 0; fd < n; ++fd) + { + HANDLE fd_h; + + if (FD_ISSET (fd, readfds)) + { + fd_h = handles[indx++]; + /* This handle might be ready, even though it wasn't the handle + returned by WaitForMultipleObjects. */ + if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) + FD_CLR (fd, readfds); + else + num_ready++; + } + + if (FD_ISSET (fd, exceptfds)) + { + fd_h = handles[indx++]; + /* This handle might be ready, even though it wasn't the handle + returned by WaitForMultipleObjects. */ + if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) + FD_CLR (fd, exceptfds); + else + num_ready++; + } + } + + return num_ready; +} ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-06 21:02 ` Daniel Jacobowitz @ 2006-02-06 23:02 ` Mark Kettenis 2006-02-06 23:17 ` Daniel Jacobowitz ` (2 more replies) 2006-02-07 4:41 ` Eli Zaretskii 1 sibling, 3 replies; 26+ messages in thread From: Mark Kettenis @ 2006-02-06 23:02 UTC (permalink / raw) To: drow; +Cc: gdb-patches > Date: Mon, 6 Feb 2006 16:02:38 -0500 > From: Daniel Jacobowitz <drow@false.org> > > On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote: > > Fixes, all for a mingw32-hosted GDB: > > Here is a revised version, in which the Windows select magic does not > live in event-loop.c, and including the NEWS entry. I haven't changed > the pipe polling code; thanks to Ian for his suggestions, but I'm > not sufficiently sure they're workable. > > Are there any bits of this patch that strike you as too ugly or in the > wrong place? Hmm, can't mingw_select() just be called select()? That'd remove much of the ugliness. Mark ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-06 23:02 ` Mark Kettenis @ 2006-02-06 23:17 ` Daniel Jacobowitz 2006-02-06 23:21 ` Christopher Faylor 2006-02-09 22:38 ` Daniel Jacobowitz 2 siblings, 0 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-06 23:17 UTC (permalink / raw) To: Mark Kettenis; +Cc: gdb-patches On Tue, Feb 07, 2006 at 12:01:43AM +0100, Mark Kettenis wrote: > > Date: Mon, 6 Feb 2006 16:02:38 -0500 > > From: Daniel Jacobowitz <drow@false.org> > > > > On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote: > > > Fixes, all for a mingw32-hosted GDB: > > > > Here is a revised version, in which the Windows select magic does not > > live in event-loop.c, and including the NEWS entry. I haven't changed > > the pipe polling code; thanks to Ian for his suggestions, but I'm > > not sufficiently sure they're workable. > > > > Are there any bits of this patch that strike you as too ugly or in the > > wrong place? > > Hmm, can't mingw_select() just be called select()? That'd remove much > of the ugliness. No, it can't. Two reasons. One is the same reason I mentioned for strerror: with __attribute__((dllimport)) in the headers, GCC will generate a load from the import table at call sites, and overriding the function won't work. MinGW doesn't use this today, if compiling with GCC, but from the bits in the header files, it looks like they either did in the past or want to in the future. And I think they do use the equivalent when compiling with MSVC. No, I haven't tried building GDB with MSVC. Another reason is that I don't want to intercept all calls to select. First of all, because select() is a useful function on Windows (works on sockets only, but does work there) and it would be a valid way to implement mingw_select for sockets - I nearly did that. And second because we call select, the socket version, explicitly from ser-tcp.c before the connection and associated serial device are completely set up. If I could get around #1, I would look into handling #2, but it seems likely to be more confusing than helpful, to me. It wouldn't get rid of too much of the ugliness either, I'm afraid :-( For instance you'd still need gdb_select.h; if you want fd_set on Windows, you need <windows.h> or <winsock2.h>. No <sys/select.h> to be found. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-06 23:02 ` Mark Kettenis 2006-02-06 23:17 ` Daniel Jacobowitz @ 2006-02-06 23:21 ` Christopher Faylor 2006-02-09 22:38 ` Daniel Jacobowitz 2 siblings, 0 replies; 26+ messages in thread From: Christopher Faylor @ 2006-02-06 23:21 UTC (permalink / raw) To: gdb-patches On Tue, Feb 07, 2006 at 12:01:43AM +0100, Mark Kettenis wrote: >> Date: Mon, 6 Feb 2006 16:02:38 -0500 >> From: Daniel Jacobowitz >> >> On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote: >> > Fixes, all for a mingw32-hosted GDB: >> >> Here is a revised version, in which the Windows select magic does not >> live in event-loop.c, and including the NEWS entry. I haven't changed >> the pipe polling code; thanks to Ian for his suggestions, but I'm >> not sufficiently sure they're workable. >> >> Are there any bits of this patch that strike you as too ugly or in the >> wrong place? > >Hmm, can't mingw_select() just be called select()? That'd remove much >of the ugliness. Unfortunately, there's already a select, with similar semantics to UNIX, exported by Windows's winsock dll. It can only be used for sockets, though. It's not useful for anything else. cgf ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-06 23:02 ` Mark Kettenis 2006-02-06 23:17 ` Daniel Jacobowitz 2006-02-06 23:21 ` Christopher Faylor @ 2006-02-09 22:38 ` Daniel Jacobowitz 2006-02-10 7:53 ` Eli Zaretskii 2006-02-10 20:46 ` Mark Kettenis 2 siblings, 2 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-09 22:38 UTC (permalink / raw) To: Mark Kettenis; +Cc: gdb-patches Here's a version that puts gdb_select off in its own file, and uses it everywhere that it can be used (which does not include ser-tcp.c, sorry). -- Daniel Jacobowitz CodeSourcery 2006-02-03 Daniel Jacobowitz <dan@codesourcery.com> * NEWS: Mention native Windows support. * Makefile.in (gdb_select_h, ser_tcp_h): New. (ALLDEPFILES): Add ser-mingw.c. (event-loop.o, inflow.o, mingw-hdep.o, posix-hdep.o, ser-base.o) (ser-tcp.o, ser-unix.o): Update. (ser-mingw.o): New rule. * configure: Regenerated. * configure.ac: Add ser-mingw.o for mingw32. * ser-mingw.c: New file. * event-loop.c: Include "gdb_select.h". (gdb_select): Remove, moved to mingw-hdep.c and posix-hdep.c. * ser-base.c: Include "gdb_select.h". (ser_base_wait_for): Use gdb_select. * serial.c (serial_for_fd): New function. (serial_fdopen): Try "terminal" before "hardwire". Initialize the allocated struct serial. (serial_wait_handle): New function. * serial.h (serial_for_fd, serial_wait_handle): New prototypes. (struct serial_ops) [USE_WIN32API]: Add wait_handle. * gdb_select.h: New file. * ser-tcp.c: Include "ser-tcp.h". Remove unused "ser-unix.h" include. (net_close, net_read_prim, net_write_prim): Make global. (net_open): Likewise. Pass an exception set to select. Whitespace fix. Document why we can not use gdb_select. (_initialize_ser_tcp) [USE_WIN32API]: Do not register TCP support here. * ser-tcp.h: New file. * inflow.c (gdb_has_a_terminal): Don't initialize stdin_serial here. (handle_sigio): Use gdb_select. (initialize_stdin_serial): New function. * terminal.h (initialize_stdin_serial): New prototype. * top.c (gdb_init): Call initialize_stdin_serial. * mingw-hdep.c (gdb_select): New function, moved from gdb_select in event-loop.c. Add exception condition support. Use serial_for_fd and serial_wait_handle. Fix timeout handling. * posix-hdep.c: Include "gdb_select.h". (gdb_select): New function. * remote-st.c (connect_command): Use gdb_select. * ser-unix.c: Include "gdb_select.h". (hardwire_send_break, wait_for): Use gdb_select. Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2006-02-09 17:26:52.000000000 -0500 +++ src/gdb/Makefile.in 2006-02-09 17:32:27.000000000 -0500 @@ -692,6 +692,7 @@ gdb_obstack_h = gdb_obstack.h $(obstack_ gdb_proc_service_h = gdb_proc_service.h $(gregset_h) gdb_ptrace_h = gdb_ptrace.h gdb_regex_h = gdb_regex.h $(xregex_h) +gdb_select_h = gdb_select.h gdb_stabs_h = gdb-stabs.h gdb_stat_h = gdb_stat.h gdb_string_h = gdb_string.h @@ -764,6 +765,7 @@ scm_tags_h = scm-tags.h sentinel_frame_h = sentinel-frame.h serial_h = serial.h ser_base_h = ser-base.h +ser_tcp_h = ser-tcp.h ser_unix_h = ser-unix.h shnbsd_tdep_h = shnbsd-tdep.h sh_tdep_h = sh-tdep.h @@ -1442,7 +1444,7 @@ ALLDEPFILES = \ remote-st.c remote-utils.c dcache.c \ rs6000-nat.c rs6000-tdep.c \ s390-tdep.c s390-nat.c \ - ser-go32.c ser-pipe.c ser-tcp.c \ + ser-go32.c ser-pipe.c ser-tcp.c ser-mingw.c \ sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \ sol2-tdep.c \ solib-irix.c solib-svr4.c solib-sunos.c \ @@ -1922,7 +1924,7 @@ eval.o: eval.c $(defs_h) $(gdb_string_h) $(f_lang_h) $(cp_abi_h) $(infcall_h) $(objc_lang_h) $(block_h) \ $(parser_defs_h) $(cp_support_h) event-loop.o: event-loop.c $(defs_h) $(event_loop_h) $(event_top_h) \ - $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) + $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) $(gdb_select_h) event-top.o: event-top.c $(defs_h) $(top_h) $(inferior_h) $(target_h) \ $(terminal_h) $(event_loop_h) $(event_top_h) $(interps_h) \ $(exceptions_h) $(gdbcmd_h) $(readline_h) $(readline_history_h) @@ -2129,7 +2131,7 @@ inf-loop.o: inf-loop.c $(defs_h) $(infer $(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) inflow.o: inflow.c $(defs_h) $(frame_h) $(inferior_h) $(command_h) \ $(serial_h) $(terminal_h) $(target_h) $(gdbthread_h) $(gdb_string_h) \ - $(inflow_h) + $(inflow_h) $(gdb_select_h) inf-ptrace.o: inf-ptrace.c $(defs_h) $(command_h) $(inferior_h) $(inflow_h) \ $(gdbcore_h) $(regcache_h) $(gdb_assert_h) \ $(gdb_string_h) $(gdb_ptrace_h) $(gdb_wait_h) $(inf_child_h) @@ -2282,7 +2284,8 @@ memattr.o: memattr.c $(defs_h) $(command $(target_h) $(value_h) $(language_h) $(gdb_string_h) mem-break.o: mem-break.c $(defs_h) $(symtab_h) $(breakpoint_h) $(inferior_h) \ $(target_h) -mingw-hdep.o: mingw-hdep.c $(defs_h) $(gdb_string_h) +mingw-hdep.o: mingw-hdep.c $(defs_h) $(serial_h) $(gdb_assert_h) \ + $(gdb_select_h) $(gdb_string_h) minsyms.o: minsyms.c $(defs_h) $(gdb_string_h) $(symtab_h) $(bfd_h) \ $(symfile_h) $(objfiles_h) $(demangle_h) $(value_h) $(cp_abi_h) mips64obsd-nat.o: mips64obsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \ @@ -2378,7 +2381,7 @@ p-exp.o: p-exp.c $(defs_h) $(gdb_string_ p-lang.o: p-lang.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(expression_h) $(parser_defs_h) $(language_h) $(p_lang_h) \ $(valprint_h) $(value_h) -posix-hdep.o: posix-hdep.c $(defs_h) +posix-hdep.o: posix-hdep.c $(defs_h) $(gdb_select_h) ppc-bdm.o: ppc-bdm.c $(defs_h) $(gdbcore_h) $(gdb_string_h) $(frame_h) \ $(inferior_h) $(bfd_h) $(symfile_h) $(target_h) $(gdbcmd_h) \ $(objfiles_h) $(gdb_stabs_h) $(serial_h) $(ocd_h) $(ppc_tdep_h) \ @@ -2515,13 +2518,15 @@ ser-e7kpc.o: ser-e7kpc.c $(defs_h) $(ser ser-go32.o: ser-go32.c $(defs_h) $(gdbcmd_h) $(serial_h) $(gdb_string_h) serial.o: serial.c $(defs_h) $(serial_h) $(gdb_string_h) $(gdbcmd_h) ser-base.o: ser-base.c $(defs_h) $(serial_h) $(ser_base_h) $(event_loop_h) \ - $(gdb_string_h) + $(gdb_select_h) $(gdb_string_h) ser-pipe.o: ser-pipe.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ $(gdb_vfork_h) $(gdb_string_h) -ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ +ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_tcp_h) \ $(gdb_string_h) ser-unix.o: ser-unix.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ - $(terminal_h) $(gdb_string_h) + $(terminal_h) $(gdb_select_h) $(gdb_string_h) +ser-mingw.o: ser-mingw.c $(defs_h) $(serial_h) $(ser_base_h) \ + $(ser_tcp_h) $(gdb_assert_h) $(gdb_string_h) sh3-rom.o: sh3-rom.c $(defs_h) $(gdbcore_h) $(target_h) $(monitor_h) \ $(serial_h) $(srec_h) $(arch_utils_h) $(regcache_h) $(gdb_string_h) \ $(sh_tdep_h) Index: src/gdb/configure =================================================================== --- src.orig/gdb/configure 2006-02-09 17:26:52.000000000 -0500 +++ src/gdb/configure 2006-02-09 17:26:52.000000000 -0500 @@ -20097,7 +20097,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser- case ${host} in *go32* ) SER_HARDWIRE=ser-go32.o ;; *djgpp* ) SER_HARDWIRE=ser-go32.o ;; - *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;; + *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;; esac Index: src/gdb/configure.ac =================================================================== --- src.orig/gdb/configure.ac 2006-02-09 17:26:52.000000000 -0500 +++ src/gdb/configure.ac 2006-02-09 17:26:52.000000000 -0500 @@ -1,5 +1,6 @@ dnl Autoconf configure script for GDB, the GNU debugger. -dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +dnl 2005, 2006 dnl Free Software Foundation, Inc. dnl dnl This file is part of GDB. @@ -1202,7 +1203,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser- case ${host} in *go32* ) SER_HARDWIRE=ser-go32.o ;; *djgpp* ) SER_HARDWIRE=ser-go32.o ;; - *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;; + *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;; esac AC_SUBST(SER_HARDWIRE) Index: src/gdb/event-loop.c =================================================================== --- src.orig/gdb/event-loop.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/event-loop.c 2006-02-09 17:26:52.000000000 -0500 @@ -1,5 +1,6 @@ /* Event loop machinery for GDB, the GNU debugger. - Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006 + Free Software Foundation, Inc. Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions. This file is part of GDB. @@ -37,6 +38,7 @@ #include <sys/time.h> #include "exceptions.h" #include "gdb_assert.h" +#include "gdb_select.h" typedef struct gdb_event gdb_event; typedef void (event_handler_func) (int); @@ -731,97 +733,6 @@ handle_file_event (int event_file_desc) } } -/* Wrapper for select. This function is not yet exported from this - file because it is not sufficiently general. For example, - ser-base.c uses select to check for socket activity, and this - function does not support sockets under Windows, so we do not want - to use gdb_select in ser-base.c. */ - -static int -gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) -{ -#ifdef USE_WIN32API - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - HANDLE h; - DWORD event; - DWORD num_handles; - int fd; - int num_ready; - - num_ready = 0; - num_handles = 0; - for (fd = 0; fd < n; ++fd) - { - /* There is no support yet for WRITEFDS. At present, this isn't - used by GDB -- but we do not want to silently ignore WRITEFDS - if something starts using it. */ - gdb_assert (!FD_ISSET (fd, writefds)); - if (!FD_ISSET (fd, readfds) - && !FD_ISSET (fd, exceptfds)) - continue; - h = (HANDLE) _get_osfhandle (fd); - if (h == INVALID_HANDLE_VALUE) - { - /* If the underlying handle is INVALID_HANDLE_VALUE, then - this descriptor is no more. */ - if (FD_ISSET (fd, exceptfds)) - ++num_ready; - continue; - } - /* The only exceptional condition we recognize is a closed file - descriptor. Since we have already checked for that - condition, clear the exceptional bit for this descriptor. */ - FD_CLR (fd, exceptfds); - if (FD_ISSET (fd, readfds)) - { - gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); - handles[num_handles++] = h; - } - } - /* If we don't need to wait for any handles, we are done. */ - if (!num_handles) - return num_ready; - event = WaitForMultipleObjects (num_handles, - handles, - FALSE, - timeout - ? (timeout->tv_sec * 1000 + timeout->tv_usec) - : INFINITE); - /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the - HANDLES included an abandoned mutex. Since GDB doesn't use - mutexes, that should never occur. */ - gdb_assert (!(WAIT_ABANDONED_0 <= event - && event < WAIT_ABANDONED_0 + num_handles)); - if (event == WAIT_FAILED) - return -1; - if (event == WAIT_TIMEOUT) - return num_ready; - /* Run through the READFDS, clearing bits corresponding to descriptors - for which input is unavailable. */ - num_ready += num_handles; - h = handles[event - WAIT_OBJECT_0]; - for (fd = 0; fd < n; ++fd) - { - HANDLE fd_h; - if (!FD_ISSET (fd, readfds)) - continue; - fd_h = (HANDLE) _get_osfhandle (fd); - /* This handle might be ready, even though it wasn't the handle - returned by WaitForMultipleObjects. */ - if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) - { - FD_CLR (fd, readfds); - --num_ready; - } - } - - return num_ready; -#else - return select (n, readfds, writefds, exceptfds, timeout); -#endif -} - /* Called by gdb_do_one_event to wait for new events on the monitored file descriptors. Queue file events as they are detected by the poll. Index: src/gdb/ser-base.c =================================================================== --- src.orig/gdb/ser-base.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/ser-base.c 2006-02-09 17:26:52.000000000 -0500 @@ -1,7 +1,7 @@ /* Generic serial interface functions. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, - 2003, 2004, 2005 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -25,6 +25,7 @@ #include "ser-base.h" #include "event-loop.h" +#include "gdb_select.h" #include "gdb_string.h" #include <sys/time.h> #ifdef USE_WIN32API @@ -202,9 +203,9 @@ ser_base_wait_for (struct serial *scb, i FD_SET (scb->fd, &exceptfds); if (timeout >= 0) - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); + numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); else - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0); + numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0); if (numfds <= 0) { Index: src/gdb/serial.c =================================================================== --- src.orig/gdb/serial.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/serial.c 2006-02-09 17:26:52.000000000 -0500 @@ -1,7 +1,7 @@ /* Generic serial interface routines Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002 Free Software Foundation, Inc. + 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -233,6 +233,22 @@ serial_open (const char *name) return scb; } +/* Return the open serial device for FD, if found, or NULL if FD + is not already opened. */ + +struct serial * +serial_for_fd (int fd) +{ + struct serial *scb; + struct serial_ops *ops; + + for (scb = scb_base; scb; scb = scb->next) + if (scb->fd == fd) + return scb; + + return NULL; +} + struct serial * serial_fdopen (const int fd) { @@ -246,12 +262,14 @@ serial_fdopen (const int fd) return scb; } - ops = serial_interface_lookup ("hardwire"); + ops = serial_interface_lookup ("terminal"); + if (!ops) + ops = serial_interface_lookup ("hardwire"); if (!ops) return NULL; - scb = XMALLOC (struct serial); + scb = XCALLOC (1, struct serial); scb->ops = ops; @@ -524,6 +542,19 @@ serial_debug_p (struct serial *scb) return scb->debug_p || global_serial_debug_p; } +#ifdef USE_WIN32API +void +serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + if (scb->ops->wait_handle) + scb->ops->wait_handle (scb, read, except); + else + { + *read = (HANDLE) _get_osfhandle (scb->fd); + *except = NULL; + } +} +#endif #if 0 /* The connect command is #if 0 because I hadn't thought of an elegant Index: src/gdb/serial.h =================================================================== --- src.orig/gdb/serial.h 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/serial.h 2006-02-09 17:26:52.000000000 -0500 @@ -1,5 +1,6 @@ /* Remote serial support interface definitions for GDB, the GNU Debugger. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000 + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, + 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -22,6 +23,10 @@ #ifndef SERIAL_H #define SERIAL_H +#ifdef USE_WIN32API +#include <windows.h> +#endif + struct ui_file; /* For most routines, if a failure is indicated, then errno should be @@ -41,6 +46,10 @@ struct serial; extern struct serial *serial_open (const char *name); +/* Find an already opened serial stream using a file handle. */ + +extern struct serial *serial_for_fd (int fd); + /* Open a new serial stream using a file handle. */ extern struct serial *serial_fdopen (const int fd); @@ -238,6 +247,13 @@ struct serial_ops /* Perform a low-level write operation, writing (at most) COUNT bytes from BUF. */ int (*write_prim)(struct serial *scb, const void *buf, size_t count); + +#ifdef USE_WIN32API + /* Return a handle to wait on, indicating available data from SCB + when signaled, in *READ. Return a handle indicating errors + in *EXCEPT. */ + void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except); +#endif /* USE_WIN32API */ }; /* Add a new serial interface to the interface list */ @@ -248,4 +264,12 @@ extern void serial_add_interface (struct extern void serial_log_command (const char *); +#ifdef USE_WIN32API + +/* Windows-only: find or create handles that we can wait on for this + serial device. */ +extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *); + +#endif /* USE_WIN32API */ + #endif /* SERIAL_H */ Index: src/gdb/gdb_select.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/gdb_select.h 2006-02-09 17:27:11.000000000 -0500 @@ -0,0 +1,37 @@ +/* Slightly more portable version of <sys/select.h>. + + Copyright (C) 2006 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#if !defined(GDB_SELECT_H) +#define GDB_SELECT_H + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifdef USE_WIN32API +#include <winsock2.h> +#endif + +extern int gdb_select (int n, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); + +#endif /* !defined(GDB_SELECT_H) */ Index: src/gdb/ser-tcp.c =================================================================== --- src.orig/gdb/ser-tcp.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/ser-tcp.c 2006-02-09 17:26:52.000000000 -0500 @@ -1,6 +1,6 @@ /* Serial interface for raw TCP connections on Un*x like systems. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005 + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -23,7 +23,7 @@ #include "defs.h" #include "serial.h" #include "ser-base.h" -#include "ser-unix.h" +#include "ser-tcp.h" #include <sys/types.h> @@ -56,8 +56,6 @@ typedef int socklen_t; #endif -static int net_open (struct serial *scb, const char *name); -static void net_close (struct serial *scb); void _initialize_ser_tcp (void); /* seconds to wait for connect */ @@ -67,7 +65,7 @@ void _initialize_ser_tcp (void); /* Open a tcp socket */ -static int +int net_open (struct serial *scb, const char *name) { char *port_str, hostname[100]; @@ -153,7 +151,7 @@ net_open (struct serial *scb, const char { /* looks like we need to wait for the connect */ struct timeval t; - fd_set rset, wset; + fd_set rset, wset, eset; int polls = 0; FD_ZERO (&rset); @@ -174,10 +172,19 @@ net_open (struct serial *scb, const char FD_SET (scb->fd, &rset); wset = rset; + eset = rset; t.tv_sec = 0; t.tv_usec = 1000000 / POLL_INTERVAL; - n = select (scb->fd + 1, &rset, &wset, NULL, &t); + /* POSIX systems return connection success or failure by signalling + wset. Windows systems return success in wset and failure in + eset. + + We must call select here, rather than gdb_select, because + the serial structure has not yet been initialized - the + MinGW select wrapper will not know that this FD refers + to a socket. */ + n = select (scb->fd + 1, &rset, &wset, &eset, &t); polls++; } while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL); @@ -194,7 +201,7 @@ net_open (struct serial *scb, const char { int res, err; socklen_t len; - len = sizeof(err); + len = sizeof (err); /* On Windows, the fourth parameter to getsockopt is a "char *"; on UNIX systems it is generally "void *". The cast to "void *" is OK everywhere, since in C "void *" can be implicitly @@ -230,7 +237,7 @@ net_open (struct serial *scb, const char return 0; } -static void +void net_close (struct serial *scb) { if (scb->fd < 0) @@ -240,13 +247,13 @@ net_close (struct serial *scb) scb->fd = -1; } -static int +int net_read_prim (struct serial *scb, size_t count) { return recv (scb->fd, scb->buf, count, 0); } -static int +int net_write_prim (struct serial *scb, const void *buf, size_t count) { return send (scb->fd, buf, count, 0); @@ -255,13 +262,12 @@ net_write_prim (struct serial *scb, cons void _initialize_ser_tcp (void) { - struct serial_ops *ops; #ifdef USE_WIN32API - WSADATA wsa_data; - if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) - /* WinSock is unavailable. */ - return; -#endif + /* Do nothing; the TCP serial operations will be initialized in + ser-mingw.c. */ + return; +#else + struct serial_ops *ops; ops = XMALLOC (struct serial_ops); memset (ops, 0, sizeof (struct serial_ops)); ops->name = "tcp"; @@ -285,4 +291,5 @@ _initialize_ser_tcp (void) ops->read_prim = net_read_prim; ops->write_prim = net_write_prim; serial_add_interface (ops); +#endif /* USE_WIN32API */ } Index: src/gdb/ser-tcp.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/ser-tcp.h 2006-02-09 17:26:52.000000000 -0500 @@ -0,0 +1,32 @@ +/* Serial interface for raw TCP connections on Un*x like systems. + + Copyright (C) 2006 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef SER_TCP_H +#define SER_TCP_H + +struct serial; + +extern int net_open (struct serial *scb, const char *name); +extern void net_close (struct serial *scb); +extern int net_read_prim (struct serial *scb, size_t count); +extern int net_write_prim (struct serial *scb, const void *buf, size_t count); + +#endif Index: src/gdb/inflow.c =================================================================== --- src.orig/gdb/inflow.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/inflow.c 2006-02-09 17:26:52.000000000 -0500 @@ -1,6 +1,6 @@ /* Low level interface to ptrace, for GDB when running under Unix. Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, - 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -32,9 +32,7 @@ #include "gdb_string.h" #include <signal.h> #include <fcntl.h> -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif +#include "gdb_select.h" #include "inflow.h" @@ -129,7 +127,6 @@ gdb_has_a_terminal (void) #endif gdb_has_a_terminal_flag = no; - stdin_serial = serial_fdopen (0); if (stdin_serial != NULL) { our_ttystate = serial_get_tty_state (stdin_serial); @@ -643,7 +640,7 @@ handle_sigio (int signo) FD_ZERO (&readfds); FD_SET (target_activity_fd, &readfds); - numfds = select (target_activity_fd + 1, &readfds, NULL, NULL, NULL); + numfds = gdb_select (target_activity_fd + 1, &readfds, NULL, NULL, NULL); if (numfds >= 0 && FD_ISSET (target_activity_fd, &readfds)) { #ifndef _WIN32 @@ -730,6 +727,18 @@ gdb_setpgid (void) return retval; } +/* Get all the current tty settings (including whether we have a + tty at all!). We can't do this in _initialize_inflow because + serial_fdopen() won't work until the serial_ops_list is + initialized, but we don't want to do it lazily either, so + that we can guarantee stdin_serial is opened if there is + a terminal. */ +void +initialize_stdin_serial (void) +{ + stdin_serial = serial_fdopen (0); +} + void _initialize_inflow (void) { Index: src/gdb/terminal.h =================================================================== --- src.orig/gdb/terminal.h 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/terminal.h 2006-02-09 17:26:52.000000000 -0500 @@ -1,5 +1,6 @@ /* Terminal interface definitions for GDB, the GNU Debugger. - Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000 + Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000, + 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -88,4 +89,7 @@ extern int job_control; we lack job control. */ extern int gdb_setpgid (void); +/* Set up a serial structure describing standard input. In inflow.c. */ +extern void initialize_stdin_serial (void); + #endif /* !defined (TERMINAL_H) */ Index: src/gdb/top.c =================================================================== --- src.orig/gdb/top.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/top.c 2006-02-09 17:26:52.000000000 -0500 @@ -1550,6 +1550,8 @@ gdb_init (char *argv0) init_cli_cmds(); init_main (); /* But that omits this file! Do it now */ + initialize_stdin_serial (); + async_init_signals (); /* We need a default language for parsing expressions, so simple things like Index: src/gdb/NEWS =================================================================== --- src.orig/gdb/NEWS 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/NEWS 2006-02-09 17:26:52.000000000 -0500 @@ -41,6 +41,12 @@ detach-fork <n> Delete a fork from the Morpho Technologies ms2 ms1-elf +* Improved Windows host support + +GDB now builds as a cross debugger hosted on i686-mingw32, including +native console support, and remote communications using either +network sockets or serial ports. + * REMOVED features The ARM rdi-share module. Index: src/gdb/ser-mingw.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/ser-mingw.c 2006-02-09 17:26:52.000000000 -0500 @@ -0,0 +1,796 @@ +/* Serial interface for local (hardwired) serial ports on Windows systems + + Copyright (C) 2006 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "serial.h" +#include "ser-base.h" +#include "ser-tcp.h" + +#include <windows.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> + +#include "gdb_assert.h" +#include "gdb_string.h" + +void _initialize_ser_windows (void); + +struct ser_windows_state +{ + int in_progress; + OVERLAPPED ov; + DWORD lastCommMask; + HANDLE except_event; +}; + +/* Open up a real live device for serial I/O. */ + +static int +ser_windows_open (struct serial *scb, const char *name) +{ + HANDLE h; + struct ser_windows_state *state; + COMMTIMEOUTS timeouts; + + /* Only allow COM ports. */ + if (strncmp (name, "COM", 3) != 0) + { + errno = ENOENT; + return -1; + } + + h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; + return -1; + } + + scb->fd = _open_osfhandle ((long) h, O_RDWR); + if (scb->fd < 0) + { + errno = ENOENT; + return -1; + } + + if (!SetCommMask (h, EV_RXCHAR)) + { + errno = EINVAL; + return -1; + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + if (!SetCommTimeouts (h, &timeouts)) + { + errno = EINVAL; + return -1; + } + + state = xmalloc (sizeof (struct ser_windows_state)); + memset (state, 0, sizeof (struct ser_windows_state)); + scb->state = state; + + /* Create a manual reset event to watch the input buffer. */ + state->ov.hEvent = CreateEvent (0, TRUE, FALSE, 0); + + /* Create a (currently unused) handle to record exceptions. */ + state->except_event = CreateEvent (0, TRUE, FALSE, 0); + + return 0; +} + +/* Wait for the output to drain away, as opposed to flushing (discarding) + it. */ + +static int +ser_windows_drain_output (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (FlushFileBuffers (h) != 0) ? 0 : -1; +} + +static int +ser_windows_flush_output (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (PurgeComm (h, PURGE_TXCLEAR) != 0) ? 0 : -1; +} + +static int +ser_windows_flush_input (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (PurgeComm (h, PURGE_RXCLEAR) != 0) ? 0 : -1; +} + +static int +ser_windows_send_break (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + if (SetCommBreak (h) == 0) + return -1; + + /* Delay for 250 milliseconds. */ + Sleep (250); + + if (ClearCommBreak (h)) + return -1; + + return 0; +} + +static void +ser_windows_raw (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return; + + state.fParity = FALSE; + state.fOutxCtsFlow = FALSE; + state.fOutxDsrFlow = FALSE; + state.fDtrControl = DTR_CONTROL_ENABLE; + state.fDsrSensitivity = FALSE; + state.fOutX = FALSE; + state.fInX = FALSE; + state.fNull = FALSE; + state.fAbortOnError = FALSE; + state.ByteSize = 8; + state.Parity = NOPARITY; + + scb->current_timeout = 0; + + if (SetCommState (h, &state) == 0) + warning (_("SetCommState failed\n")); +} + +static int +ser_windows_setstopbits (struct serial *scb, int num) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + switch (num) + { + case SERIAL_1_STOPBITS: + state.StopBits = ONESTOPBIT; + break; + case SERIAL_1_AND_A_HALF_STOPBITS: + state.StopBits = ONE5STOPBITS; + break; + case SERIAL_2_STOPBITS: + state.StopBits = TWOSTOPBITS; + break; + default: + return 1; + } + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + +static int +ser_windows_setbaudrate (struct serial *scb, int rate) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + state.BaudRate = rate; + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + +static void +ser_windows_close (struct serial *scb) +{ + struct ser_windows_state *state; + + /* Stop any pending selects. */ + CancelIo ((HANDLE) _get_osfhandle (scb->fd)); + state = scb->state; + CloseHandle (state->ov.hEvent); + CloseHandle (state->except_event); + + if (scb->fd < 0) + return; + + close (scb->fd); + scb->fd = -1; + + xfree (scb->state); +} + +static void +ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct ser_windows_state *state; + COMSTAT status; + DWORD errors; + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + state = scb->state; + + *except = state->except_event; + *read = state->ov.hEvent; + + if (state->in_progress) + return; + + /* Reset the mask - we are only interested in any characters which + arrive after this point, not characters which might have arrived + and already been read. */ + + /* This really, really shouldn't be necessary - just the second one. + But otherwise an internal flag for EV_RXCHAR does not get + cleared, and we get a duplicated event, if the last batch + of characters included at least two arriving close together. */ + if (!SetCommMask (h, 0)) + warning (_("ser_windows_wait_handle: reseting mask failed")); + + if (!SetCommMask (h, EV_RXCHAR)) + warning (_("ser_windows_wait_handle: reseting mask failed (2)")); + + /* There's a potential race condition here; we must check cbInQue + and not wait if that's nonzero. */ + + ClearCommError (h, &errors, &status); + if (status.cbInQue > 0) + { + SetEvent (state->ov.hEvent); + return; + } + + state->in_progress = 1; + ResetEvent (state->ov.hEvent); + state->lastCommMask = -2; + if (WaitCommEvent (h, &state->lastCommMask, &state->ov)) + { + gdb_assert (state->lastCommMask & EV_RXCHAR); + SetEvent (state->ov.hEvent); + } + else + gdb_assert (GetLastError () == ERROR_IO_PENDING); +} + +static int +ser_windows_read_prim (struct serial *scb, size_t count) +{ + struct ser_windows_state *state; + OVERLAPPED ov; + DWORD bytes_read, bytes_read_tmp; + HANDLE h; + gdb_byte *p; + + state = scb->state; + if (state->in_progress) + { + WaitForSingleObject (state->ov.hEvent, INFINITE); + state->in_progress = 0; + ResetEvent (state->ov.hEvent); + } + + memset (&ov, 0, sizeof (OVERLAPPED)); + ov.hEvent = CreateEvent (0, FALSE, FALSE, 0); + h = (HANDLE) _get_osfhandle (scb->fd); + + if (!ReadFile (h, scb->buf, /* count */ 1, &bytes_read, &ov)) + { + if (GetLastError () != ERROR_IO_PENDING + || !GetOverlappedResult (h, &ov, &bytes_read, TRUE)) + bytes_read = -1; + } + + CloseHandle (ov.hEvent); + return bytes_read; +} + +static int +ser_windows_write_prim (struct serial *scb, const void *buf, size_t len) +{ + struct ser_windows_state *state; + OVERLAPPED ov; + DWORD bytes_written; + HANDLE h; + + memset (&ov, 0, sizeof (OVERLAPPED)); + ov.hEvent = CreateEvent (0, FALSE, FALSE, 0); + h = (HANDLE) _get_osfhandle (scb->fd); + if (!WriteFile (h, buf, len, &bytes_written, &ov)) + { + if (GetLastError () != ERROR_IO_PENDING + || !GetOverlappedResult (h, &ov, &bytes_written, TRUE)) + bytes_written = -1; + } + + CloseHandle (ov.hEvent); + return bytes_written; +} + +struct ser_console_state +{ + HANDLE read_event; + HANDLE except_event; + + HANDLE start_select; + HANDLE stop_select; +}; + +static DWORD WINAPI +console_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state, state_copy; + int event_index, fd; + HANDLE h; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct ser_console_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + h = (HANDLE) _get_osfhandle (fd); + + while (1) + { + HANDLE wait_events[2]; + INPUT_RECORD record; + DWORD n_records; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + retry: + wait_events[0] = state->stop_select; + wait_events[1] = h; + + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Wait must have failed; assume an error has occured, e.g. + the handle has been closed. */ + SetEvent (state->except_event); + continue; + } + + /* We've got a pending event on the console. See if it's + of interest. */ + if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1) + { + /* Something went wrong. Maybe the console is gone. */ + SetEvent (state->except_event); + continue; + } + + if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) + { + /* This is really a keypress. */ + SetEvent (state->read_event); + continue; + } + + /* Otherwise discard it and wait again. */ + ReadConsoleInput (h, &record, 1, &n_records); + goto retry; + } +} + +static int +fd_is_pipe (int fd) +{ + if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL)) + return 1; + else + return 0; +} + +static DWORD WINAPI +pipe_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state, state_copy; + int event_index, fd; + HANDLE h; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct ser_console_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + h = (HANDLE) _get_osfhandle (fd); + + while (1) + { + HANDLE wait_events[2]; + DWORD n_avail; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + retry: + if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) + { + SetEvent (state->except_event); + continue; + } + + if (n_avail > 0) + { + SetEvent (state->read_event); + continue; + } + + if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + Sleep (10); + goto retry; + } +} + +static void +ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct ser_console_state *state = scb->state; + + if (state == NULL) + { + DWORD threadId; + int is_tty; + + is_tty = isatty (scb->fd); + if (!is_tty && !fd_is_pipe (scb->fd)) + { + *read = NULL; + *except = NULL; + return; + } + + state = xmalloc (sizeof (struct ser_console_state)); + memset (state, 0, sizeof (struct ser_console_state)); + scb->state = state; + + /* Create auto reset events to wake and terminate the select thread. */ + state->start_select = CreateEvent (0, FALSE, FALSE, 0); + state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Create our own events to report read and exceptions separately. + The exception event is currently never used. */ + state->read_event = CreateEvent (0, FALSE, FALSE, 0); + state->except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* And finally start the select thread. */ + if (is_tty) + CreateThread (NULL, 0, console_select_thread, scb, 0, &threadId); + else + CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); + } + + ResetEvent (state->read_event); + ResetEvent (state->except_event); + + SetEvent (state->start_select); + + *read = state->read_event; + *except = state->except_event; +} + +static void +ser_console_close (struct serial *scb) +{ + struct ser_console_state *state = scb->state; + + if (scb->state) + { + SetEvent (state->stop_select); + + CloseHandle (state->read_event); + CloseHandle (state->except_event); + + xfree (scb->state); + } +} + +struct ser_console_ttystate +{ + int is_a_tty; +}; + +static serial_ttystate +ser_console_get_tty_state (struct serial *scb) +{ + if (isatty (scb->fd)) + { + struct ser_console_ttystate *state; + state = (struct ser_console_ttystate *) xmalloc (sizeof *state); + state->is_a_tty = 1; + return state; + } + else + return NULL; +} + +struct net_windows_state +{ + HANDLE read_event; + HANDLE except_event; + + HANDLE start_select; + HANDLE stop_select; + HANDLE sock_event; +}; + +static DWORD WINAPI +net_windows_select_thread (void *arg) +{ + struct serial *scb = arg; + struct net_windows_state *state, state_copy; + int event_index, fd; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct net_windows_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + while (1) + { + HANDLE wait_events[2]; + WSANETWORKEVENTS events; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + wait_events[0] = state->stop_select; + wait_events[1] = state->sock_event; + + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Some error has occured. Assume that this is an error + condition. */ + SetEvent (state->except_event); + continue; + } + + /* Enumerate the internal network events, and reset the object that + signalled us to catch the next event. */ + WSAEnumNetworkEvents (fd, state->sock_event, &events); + + if (events.lNetworkEvents & FD_READ) + SetEvent (state->read_event); + + if (events.lNetworkEvents & FD_CLOSE) + SetEvent (state->except_event); + } +} + +static void +net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct net_windows_state *state = scb->state; + + ResetEvent (state->read_event); + ResetEvent (state->except_event); + + SetEvent (state->start_select); + + *read = state->read_event; + *except = state->except_event; +} + +static int +net_windows_open (struct serial *scb, const char *name) +{ + struct net_windows_state *state; + int ret; + DWORD threadId; + + ret = net_open (scb, name); + if (ret != 0) + return ret; + + state = xmalloc (sizeof (struct net_windows_state)); + memset (state, 0, sizeof (struct net_windows_state)); + scb->state = state; + + /* Create auto reset events to wake and terminate the select thread. */ + state->start_select = CreateEvent (0, FALSE, FALSE, 0); + state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Associate an event with the socket. */ + state->sock_event = CreateEvent (0, TRUE, FALSE, 0); + WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE); + + /* Create our own events to report read and close separately. */ + state->read_event = CreateEvent (0, FALSE, FALSE, 0); + state->except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* And finally start the select thread. */ + CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId); + + return 0; +} + + +static void +net_windows_close (struct serial *scb) +{ + struct net_windows_state *state = scb->state; + + SetEvent (state->stop_select); + + CloseHandle (state->read_event); + CloseHandle (state->except_event); + CloseHandle (state->start_select); + CloseHandle (state->sock_event); + + xfree (scb->state); + + net_close (scb); +} + +void +_initialize_ser_windows (void) +{ + WSADATA wsa_data; + struct serial_ops *ops; + + /* First register the serial port driver. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "hardwire"; + ops->next = 0; + ops->open = ser_windows_open; + ops->close = ser_windows_close; + + ops->flush_output = ser_windows_flush_output; + ops->flush_input = ser_windows_flush_input; + ops->send_break = ser_windows_send_break; + + /* These are only used for stdin; we do not need them for serial + ports, so supply the standard dummies. */ + 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->go_raw = ser_windows_raw; + ops->setbaudrate = ser_windows_setbaudrate; + ops->setstopbits = ser_windows_setstopbits; + ops->drain_output = ser_windows_drain_output; + ops->readchar = ser_base_readchar; + ops->write = ser_base_write; + ops->async = ser_base_async; + ops->read_prim = ser_windows_read_prim; + ops->write_prim = ser_windows_write_prim; + ops->wait_handle = ser_windows_wait_handle; + + serial_add_interface (ops); + + /* Next create the dummy serial driver used for terminals. We only + provide the TTY-related methods. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + + ops->name = "terminal"; + ops->next = 0; + + ops->close = ser_console_close; + ops->get_tty_state = ser_console_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->drain_output = ser_base_drain_output; + ops->wait_handle = ser_console_wait_handle; + + serial_add_interface (ops); + + /* If WinSock works, register the TCP/UDP socket driver. */ + + if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) + /* WinSock is unavailable. */ + return; + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "tcp"; + ops->next = 0; + ops->open = net_windows_open; + ops->close = net_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 = net_read_prim; + ops->write_prim = net_write_prim; + ops->wait_handle = net_windows_wait_handle; + serial_add_interface (ops); +} Index: src/gdb/mingw-hdep.c =================================================================== --- src.orig/gdb/mingw-hdep.c 2006-02-09 17:26:52.000000000 -0500 +++ src/gdb/mingw-hdep.c 2006-02-09 17:26:52.000000000 -0500 @@ -21,7 +21,10 @@ Boston, MA 02110-1301, USA. */ #include "defs.h" +#include "serial.h" +#include "gdb_assert.h" +#include "gdb_select.h" #include "gdb_string.h" #include <windows.h> @@ -69,3 +72,124 @@ safe_strerror (int errnum) return buffer; } + +/* Wrapper for select. On Windows systems, where the select interface + only works for sockets, this uses the GDB serial abstraction to + handle sockets, consoles, pipes, and serial ports. + + The arguments to this function are the same as the traditional + arguments to select on POSIX platforms. */ + +int +gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + static HANDLE never_handle; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + HANDLE h; + DWORD event; + DWORD num_handles; + int fd; + int num_ready; + int indx; + + num_ready = 0; + num_handles = 0; + for (fd = 0; fd < n; ++fd) + { + HANDLE read = NULL, except = NULL; + struct serial *scb; + + /* There is no support yet for WRITEFDS. At present, this isn't + used by GDB -- but we do not want to silently ignore WRITEFDS + if something starts using it. */ + gdb_assert (!writefds || !FD_ISSET (fd, writefds)); + + if (!FD_ISSET (fd, readfds) + && !FD_ISSET (fd, exceptfds)) + continue; + h = (HANDLE) _get_osfhandle (fd); + + scb = serial_for_fd (fd); + if (scb) + serial_wait_handle (scb, &read, &except); + + if (read == NULL) + read = h; + if (except == NULL) + { + if (!never_handle) + never_handle = CreateEvent (0, FALSE, FALSE, 0); + + except = never_handle; + } + + if (FD_ISSET (fd, readfds)) + { + gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); + handles[num_handles++] = read; + } + + if (FD_ISSET (fd, exceptfds)) + { + gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); + handles[num_handles++] = except; + } + } + /* If we don't need to wait for any handles, we are done. */ + if (!num_handles) + { + if (timeout) + Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000); + + return 0; + } + + event = WaitForMultipleObjects (num_handles, + handles, + FALSE, + timeout + ? (timeout->tv_sec * 1000 + + timeout->tv_usec / 1000) + : INFINITE); + /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the + HANDLES included an abandoned mutex. Since GDB doesn't use + mutexes, that should never occur. */ + gdb_assert (!(WAIT_ABANDONED_0 <= event + && event < WAIT_ABANDONED_0 + num_handles)); + if (event == WAIT_FAILED) + return -1; + if (event == WAIT_TIMEOUT) + return 0; + /* Run through the READFDS, clearing bits corresponding to descriptors + for which input is unavailable. */ + h = handles[event - WAIT_OBJECT_0]; + for (fd = 0, indx = 0; fd < n; ++fd) + { + HANDLE fd_h; + + if (FD_ISSET (fd, readfds)) + { + fd_h = handles[indx++]; + /* This handle might be ready, even though it wasn't the handle + returned by WaitForMultipleObjects. */ + if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) + FD_CLR (fd, readfds); + else + num_ready++; + } + + if (FD_ISSET (fd, exceptfds)) + { + fd_h = handles[indx++]; + /* This handle might be ready, even though it wasn't the handle + returned by WaitForMultipleObjects. */ + if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) + FD_CLR (fd, exceptfds); + else + num_ready++; + } + } + + return num_ready; +} Index: src/gdb/posix-hdep.c =================================================================== --- src.orig/gdb/posix-hdep.c 2006-02-09 17:26:52.000000000 -0500 +++ src/gdb/posix-hdep.c 2006-02-09 17:26:52.000000000 -0500 @@ -22,6 +22,8 @@ #include "defs.h" +#include "gdb_select.h" + /* The strerror() function can return NULL for errno values that are out of range. Provide a "safe" version that always returns a printable string. */ @@ -41,3 +43,11 @@ safe_strerror (int errnum) return (msg); } +/* Wrapper for select. Nothing special needed on POSIX platforms. */ + +int +gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + return select (n, readfds, writefds, exceptfds, timeout); +} Index: src/gdb/remote-st.c =================================================================== --- src.orig/gdb/remote-st.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/remote-st.c 2006-02-09 17:26:52.000000000 -0500 @@ -704,7 +704,7 @@ connect_command (char *args, int fromtty { FD_SET (0, &readfds); FD_SET (deprecated_serial_fd (st2000_desc), &readfds); - numfds = select (sizeof (readfds) * 8, &readfds, 0, 0, 0); + numfds = gdb_select (sizeof (readfds) * 8, &readfds, 0, 0, 0); } while (numfds == 0); Index: src/gdb/ser-unix.c =================================================================== --- src.orig/gdb/ser-unix.c 2006-02-09 17:25:51.000000000 -0500 +++ src/gdb/ser-unix.c 2006-02-09 17:26:52.000000000 -0500 @@ -31,6 +31,7 @@ #include <sys/socket.h> #include <sys/time.h> +#include "gdb_select.h" #include "gdb_string.h" #ifdef HAVE_TERMIOS @@ -365,7 +366,7 @@ hardwire_send_break (struct serial *scb) the full length of time. I think that is OK. */ timeout.tv_sec = 0; timeout.tv_usec = 250000; - select (0, 0, 0, 0, &timeout); + gdb_select (0, 0, 0, 0, &timeout); status = ioctl (scb->fd, TIOCCBRK, 0); return status; } @@ -448,9 +449,9 @@ wait_for (struct serial *scb, int timeou FD_SET (scb->fd, &readfds); if (timeout >= 0) - numfds = select (scb->fd + 1, &readfds, 0, 0, &tv); + numfds = gdb_select (scb->fd + 1, &readfds, 0, 0, &tv); else - numfds = select (scb->fd + 1, &readfds, 0, 0, 0); + numfds = gdb_select (scb->fd + 1, &readfds, 0, 0, 0); if (numfds <= 0) if (numfds == 0) ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-09 22:38 ` Daniel Jacobowitz @ 2006-02-10 7:53 ` Eli Zaretskii 2006-02-10 20:46 ` Mark Kettenis 1 sibling, 0 replies; 26+ messages in thread From: Eli Zaretskii @ 2006-02-10 7:53 UTC (permalink / raw) To: gdb-patches > Date: Thu, 9 Feb 2006 17:38:32 -0500 > From: Daniel Jacobowitz <drow@false.org> > Cc: gdb-patches@sourceware.org > > Here's a version that puts gdb_select off in its own file, and uses it > everywhere that it can be used (which does not include ser-tcp.c, > sorry). Fine with me. Thanks! ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-09 22:38 ` Daniel Jacobowitz 2006-02-10 7:53 ` Eli Zaretskii @ 2006-02-10 20:46 ` Mark Kettenis 2006-02-10 22:02 ` Daniel Jacobowitz 1 sibling, 1 reply; 26+ messages in thread From: Mark Kettenis @ 2006-02-10 20:46 UTC (permalink / raw) To: drow; +Cc: gdb-patches > Date: Thu, 9 Feb 2006 17:38:32 -0500 > From: Daniel Jacobowitz <drow@false.org> > > Here's a version that puts gdb_select off in its own file, and uses it > everywhere that it can be used (which does not include ser-tcp.c, > sorry). It's a pity ser-tcp.c can't use it. Can you add a comment explaining why? Otherwise, this looks fine to me (and the safe_strerror patch looks fine too). Thanks for doing this. Mark ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-10 20:46 ` Mark Kettenis @ 2006-02-10 22:02 ` Daniel Jacobowitz 0 siblings, 0 replies; 26+ messages in thread From: Daniel Jacobowitz @ 2006-02-10 22:02 UTC (permalink / raw) To: gdb-patches On Fri, Feb 10, 2006 at 09:45:58PM +0100, Mark Kettenis wrote: > > Date: Thu, 9 Feb 2006 17:38:32 -0500 > > From: Daniel Jacobowitz <drow@false.org> > > > > Here's a version that puts gdb_select off in its own file, and uses it > > everywhere that it can be used (which does not include ser-tcp.c, > > sorry). > > It's a pity ser-tcp.c can't use it. Can you add a comment explaining > why? Otherwise, this looks fine to me (and the safe_strerror patch > looks fine too). Thanks for doing this. I already had :-) We must call select here, rather than gdb_select, because the serial structure has not yet been initialized - the MinGW select wrapper will not know that this FD refers to a socket. */ Here's what I checked in after testing. -- Daniel Jacobowitz CodeSourcery 2006-02-10 Daniel Jacobowitz <dan@codesourcery.com> * NEWS: Mention native Windows support. * Makefile.in (gdb_select_h, ser_tcp_h): New. (ALLDEPFILES): Add ser-mingw.c. (event-loop.o, inflow.o, mingw-hdep.o, posix-hdep.o, ser-base.o) (ser-tcp.o, ser-unix.o): Update. (ser-mingw.o): New rule. * configure: Regenerated. * configure.ac: Add ser-mingw.o for mingw32. * ser-mingw.c: New file. * event-loop.c: Include "gdb_select.h". (gdb_select): Remove, moved to mingw-hdep.c and posix-hdep.c. * ser-base.c: Include "gdb_select.h". (ser_base_wait_for): Use gdb_select. * serial.c (serial_for_fd): New function. (serial_fdopen): Try "terminal" before "hardwire". Initialize the allocated struct serial. (serial_wait_handle): New function. * serial.h (serial_for_fd, serial_wait_handle): New prototypes. (struct serial_ops) [USE_WIN32API]: Add wait_handle. * gdb_select.h: New file. * ser-tcp.c: Include "ser-tcp.h". Remove unused "ser-unix.h" include. (net_close, net_read_prim, net_write_prim): Make global. (net_open): Likewise. Pass an exception set to select. Whitespace fix. Document why we can not use gdb_select. (_initialize_ser_tcp) [USE_WIN32API]: Do not register TCP support here. * ser-tcp.h: New file. * inflow.c (gdb_has_a_terminal): Don't initialize stdin_serial here. (handle_sigio): Use gdb_select. (initialize_stdin_serial): New function. * terminal.h (initialize_stdin_serial): New prototype. * top.c (gdb_init): Call initialize_stdin_serial. * mingw-hdep.c (gdb_select): New function, moved from gdb_select in event-loop.c. Add exception condition support. Use serial_for_fd and serial_wait_handle. Fix timeout handling. * posix-hdep.c: Include "gdb_select.h". (gdb_select): New function. * remote-st.c (connect_command): Use gdb_select. * ser-unix.c: Include "gdb_select.h". (hardwire_send_break, wait_for): Use gdb_select. Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2006-02-10 12:41:37.000000000 -0500 +++ src/gdb/Makefile.in 2006-02-10 12:42:13.000000000 -0500 @@ -692,6 +692,7 @@ gdb_obstack_h = gdb_obstack.h $(obstack_ gdb_proc_service_h = gdb_proc_service.h $(gregset_h) gdb_ptrace_h = gdb_ptrace.h gdb_regex_h = gdb_regex.h $(xregex_h) +gdb_select_h = gdb_select.h gdb_stabs_h = gdb-stabs.h gdb_stat_h = gdb_stat.h gdb_string_h = gdb_string.h @@ -764,6 +765,7 @@ scm_tags_h = scm-tags.h sentinel_frame_h = sentinel-frame.h serial_h = serial.h ser_base_h = ser-base.h +ser_tcp_h = ser-tcp.h ser_unix_h = ser-unix.h shnbsd_tdep_h = shnbsd-tdep.h sh_tdep_h = sh-tdep.h @@ -1442,7 +1444,7 @@ ALLDEPFILES = \ remote-st.c remote-utils.c dcache.c \ rs6000-nat.c rs6000-tdep.c \ s390-tdep.c s390-nat.c \ - ser-go32.c ser-pipe.c ser-tcp.c \ + ser-go32.c ser-pipe.c ser-tcp.c ser-mingw.c \ sh-tdep.c sh64-tdep.c shnbsd-tdep.c shnbsd-nat.c \ sol2-tdep.c \ solib-irix.c solib-svr4.c solib-sunos.c \ @@ -1922,7 +1924,7 @@ eval.o: eval.c $(defs_h) $(gdb_string_h) $(f_lang_h) $(cp_abi_h) $(infcall_h) $(objc_lang_h) $(block_h) \ $(parser_defs_h) $(cp_support_h) event-loop.o: event-loop.c $(defs_h) $(event_loop_h) $(event_top_h) \ - $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) + $(gdb_string_h) $(exceptions_h) $(gdb_assert_h) $(gdb_select_h) event-top.o: event-top.c $(defs_h) $(top_h) $(inferior_h) $(target_h) \ $(terminal_h) $(event_loop_h) $(event_top_h) $(interps_h) \ $(exceptions_h) $(gdbcmd_h) $(readline_h) $(readline_history_h) @@ -2129,7 +2131,7 @@ inf-loop.o: inf-loop.c $(defs_h) $(infer $(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) inflow.o: inflow.c $(defs_h) $(frame_h) $(inferior_h) $(command_h) \ $(serial_h) $(terminal_h) $(target_h) $(gdbthread_h) $(gdb_string_h) \ - $(inflow_h) + $(inflow_h) $(gdb_select_h) inf-ptrace.o: inf-ptrace.c $(defs_h) $(command_h) $(inferior_h) $(inflow_h) \ $(gdbcore_h) $(regcache_h) $(gdb_assert_h) \ $(gdb_string_h) $(gdb_ptrace_h) $(gdb_wait_h) $(inf_child_h) @@ -2282,7 +2284,8 @@ memattr.o: memattr.c $(defs_h) $(command $(target_h) $(value_h) $(language_h) $(gdb_string_h) mem-break.o: mem-break.c $(defs_h) $(symtab_h) $(breakpoint_h) $(inferior_h) \ $(target_h) -mingw-hdep.o: mingw-hdep.c $(defs_h) $(gdb_string_h) +mingw-hdep.o: mingw-hdep.c $(defs_h) $(serial_h) $(gdb_assert_h) \ + $(gdb_select_h) $(gdb_string_h) minsyms.o: minsyms.c $(defs_h) $(gdb_string_h) $(symtab_h) $(bfd_h) \ $(symfile_h) $(objfiles_h) $(demangle_h) $(value_h) $(cp_abi_h) mips64obsd-nat.o: mips64obsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \ @@ -2378,7 +2381,7 @@ p-exp.o: p-exp.c $(defs_h) $(gdb_string_ p-lang.o: p-lang.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(expression_h) $(parser_defs_h) $(language_h) $(p_lang_h) \ $(valprint_h) $(value_h) -posix-hdep.o: posix-hdep.c $(defs_h) $(gdb_string_h) +posix-hdep.o: posix-hdep.c $(defs_h) $(gdb_string_h) $(gdb_select_h) ppc-bdm.o: ppc-bdm.c $(defs_h) $(gdbcore_h) $(gdb_string_h) $(frame_h) \ $(inferior_h) $(bfd_h) $(symfile_h) $(target_h) $(gdbcmd_h) \ $(objfiles_h) $(gdb_stabs_h) $(serial_h) $(ocd_h) $(ppc_tdep_h) \ @@ -2515,13 +2518,15 @@ ser-e7kpc.o: ser-e7kpc.c $(defs_h) $(ser ser-go32.o: ser-go32.c $(defs_h) $(gdbcmd_h) $(serial_h) $(gdb_string_h) serial.o: serial.c $(defs_h) $(serial_h) $(gdb_string_h) $(gdbcmd_h) ser-base.o: ser-base.c $(defs_h) $(serial_h) $(ser_base_h) $(event_loop_h) \ - $(gdb_string_h) + $(gdb_select_h) $(gdb_string_h) ser-pipe.o: ser-pipe.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ $(gdb_vfork_h) $(gdb_string_h) -ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ +ser-tcp.o: ser-tcp.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_tcp_h) \ $(gdb_string_h) ser-unix.o: ser-unix.c $(defs_h) $(serial_h) $(ser_base_h) $(ser_unix_h) \ - $(terminal_h) $(gdb_string_h) + $(terminal_h) $(gdb_select_h) $(gdb_string_h) +ser-mingw.o: ser-mingw.c $(defs_h) $(serial_h) $(ser_base_h) \ + $(ser_tcp_h) $(gdb_assert_h) $(gdb_string_h) sh3-rom.o: sh3-rom.c $(defs_h) $(gdbcore_h) $(target_h) $(monitor_h) \ $(serial_h) $(srec_h) $(arch_utils_h) $(regcache_h) $(gdb_string_h) \ $(sh_tdep_h) Index: src/gdb/configure =================================================================== --- src.orig/gdb/configure 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/configure 2006-02-10 12:42:02.000000000 -0500 @@ -20097,7 +20097,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser- case ${host} in *go32* ) SER_HARDWIRE=ser-go32.o ;; *djgpp* ) SER_HARDWIRE=ser-go32.o ;; - *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;; + *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;; esac Index: src/gdb/configure.ac =================================================================== --- src.orig/gdb/configure.ac 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/configure.ac 2006-02-10 12:42:02.000000000 -0500 @@ -1,5 +1,6 @@ dnl Autoconf configure script for GDB, the GNU debugger. -dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +dnl Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +dnl 2005, 2006 dnl Free Software Foundation, Inc. dnl dnl This file is part of GDB. @@ -1202,7 +1203,7 @@ SER_HARDWIRE="ser-base.o ser-unix.o ser- case ${host} in *go32* ) SER_HARDWIRE=ser-go32.o ;; *djgpp* ) SER_HARDWIRE=ser-go32.o ;; - *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o" ;; + *mingw32*) SER_HARDWIRE="ser-base.o ser-tcp.o ser-mingw.o" ;; esac AC_SUBST(SER_HARDWIRE) Index: src/gdb/event-loop.c =================================================================== --- src.orig/gdb/event-loop.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/event-loop.c 2006-02-10 12:42:02.000000000 -0500 @@ -1,5 +1,6 @@ /* Event loop machinery for GDB, the GNU debugger. - Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006 + Free Software Foundation, Inc. Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions. This file is part of GDB. @@ -37,6 +38,7 @@ #include <sys/time.h> #include "exceptions.h" #include "gdb_assert.h" +#include "gdb_select.h" typedef struct gdb_event gdb_event; typedef void (event_handler_func) (int); @@ -731,97 +733,6 @@ handle_file_event (int event_file_desc) } } -/* Wrapper for select. This function is not yet exported from this - file because it is not sufficiently general. For example, - ser-base.c uses select to check for socket activity, and this - function does not support sockets under Windows, so we do not want - to use gdb_select in ser-base.c. */ - -static int -gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) -{ -#ifdef USE_WIN32API - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - HANDLE h; - DWORD event; - DWORD num_handles; - int fd; - int num_ready; - - num_ready = 0; - num_handles = 0; - for (fd = 0; fd < n; ++fd) - { - /* There is no support yet for WRITEFDS. At present, this isn't - used by GDB -- but we do not want to silently ignore WRITEFDS - if something starts using it. */ - gdb_assert (!FD_ISSET (fd, writefds)); - if (!FD_ISSET (fd, readfds) - && !FD_ISSET (fd, exceptfds)) - continue; - h = (HANDLE) _get_osfhandle (fd); - if (h == INVALID_HANDLE_VALUE) - { - /* If the underlying handle is INVALID_HANDLE_VALUE, then - this descriptor is no more. */ - if (FD_ISSET (fd, exceptfds)) - ++num_ready; - continue; - } - /* The only exceptional condition we recognize is a closed file - descriptor. Since we have already checked for that - condition, clear the exceptional bit for this descriptor. */ - FD_CLR (fd, exceptfds); - if (FD_ISSET (fd, readfds)) - { - gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); - handles[num_handles++] = h; - } - } - /* If we don't need to wait for any handles, we are done. */ - if (!num_handles) - return num_ready; - event = WaitForMultipleObjects (num_handles, - handles, - FALSE, - timeout - ? (timeout->tv_sec * 1000 + timeout->tv_usec) - : INFINITE); - /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the - HANDLES included an abandoned mutex. Since GDB doesn't use - mutexes, that should never occur. */ - gdb_assert (!(WAIT_ABANDONED_0 <= event - && event < WAIT_ABANDONED_0 + num_handles)); - if (event == WAIT_FAILED) - return -1; - if (event == WAIT_TIMEOUT) - return num_ready; - /* Run through the READFDS, clearing bits corresponding to descriptors - for which input is unavailable. */ - num_ready += num_handles; - h = handles[event - WAIT_OBJECT_0]; - for (fd = 0; fd < n; ++fd) - { - HANDLE fd_h; - if (!FD_ISSET (fd, readfds)) - continue; - fd_h = (HANDLE) _get_osfhandle (fd); - /* This handle might be ready, even though it wasn't the handle - returned by WaitForMultipleObjects. */ - if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) - { - FD_CLR (fd, readfds); - --num_ready; - } - } - - return num_ready; -#else - return select (n, readfds, writefds, exceptfds, timeout); -#endif -} - /* Called by gdb_do_one_event to wait for new events on the monitored file descriptors. Queue file events as they are detected by the poll. Index: src/gdb/ser-base.c =================================================================== --- src.orig/gdb/ser-base.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/ser-base.c 2006-02-10 12:42:02.000000000 -0500 @@ -1,7 +1,7 @@ /* Generic serial interface functions. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, - 2003, 2004, 2005 Free Software Foundation, Inc. + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -25,6 +25,7 @@ #include "ser-base.h" #include "event-loop.h" +#include "gdb_select.h" #include "gdb_string.h" #include <sys/time.h> #ifdef USE_WIN32API @@ -202,9 +203,9 @@ ser_base_wait_for (struct serial *scb, i FD_SET (scb->fd, &exceptfds); if (timeout >= 0) - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); + numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); else - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0); + numfds = gdb_select (scb->fd + 1, &readfds, 0, &exceptfds, 0); if (numfds <= 0) { Index: src/gdb/serial.c =================================================================== --- src.orig/gdb/serial.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/serial.c 2006-02-10 12:42:02.000000000 -0500 @@ -1,7 +1,7 @@ /* Generic serial interface routines Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, - 2001, 2002 Free Software Foundation, Inc. + 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -233,6 +233,22 @@ serial_open (const char *name) return scb; } +/* Return the open serial device for FD, if found, or NULL if FD + is not already opened. */ + +struct serial * +serial_for_fd (int fd) +{ + struct serial *scb; + struct serial_ops *ops; + + for (scb = scb_base; scb; scb = scb->next) + if (scb->fd == fd) + return scb; + + return NULL; +} + struct serial * serial_fdopen (const int fd) { @@ -246,12 +262,14 @@ serial_fdopen (const int fd) return scb; } - ops = serial_interface_lookup ("hardwire"); + ops = serial_interface_lookup ("terminal"); + if (!ops) + ops = serial_interface_lookup ("hardwire"); if (!ops) return NULL; - scb = XMALLOC (struct serial); + scb = XCALLOC (1, struct serial); scb->ops = ops; @@ -524,6 +542,19 @@ serial_debug_p (struct serial *scb) return scb->debug_p || global_serial_debug_p; } +#ifdef USE_WIN32API +void +serial_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + if (scb->ops->wait_handle) + scb->ops->wait_handle (scb, read, except); + else + { + *read = (HANDLE) _get_osfhandle (scb->fd); + *except = NULL; + } +} +#endif #if 0 /* The connect command is #if 0 because I hadn't thought of an elegant Index: src/gdb/serial.h =================================================================== --- src.orig/gdb/serial.h 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/serial.h 2006-02-10 12:42:02.000000000 -0500 @@ -1,5 +1,6 @@ /* Remote serial support interface definitions for GDB, the GNU Debugger. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000 + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, + 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -22,6 +23,10 @@ #ifndef SERIAL_H #define SERIAL_H +#ifdef USE_WIN32API +#include <windows.h> +#endif + struct ui_file; /* For most routines, if a failure is indicated, then errno should be @@ -41,6 +46,10 @@ struct serial; extern struct serial *serial_open (const char *name); +/* Find an already opened serial stream using a file handle. */ + +extern struct serial *serial_for_fd (int fd); + /* Open a new serial stream using a file handle. */ extern struct serial *serial_fdopen (const int fd); @@ -238,6 +247,13 @@ struct serial_ops /* Perform a low-level write operation, writing (at most) COUNT bytes from BUF. */ int (*write_prim)(struct serial *scb, const void *buf, size_t count); + +#ifdef USE_WIN32API + /* Return a handle to wait on, indicating available data from SCB + when signaled, in *READ. Return a handle indicating errors + in *EXCEPT. */ + void (*wait_handle) (struct serial *scb, HANDLE *read, HANDLE *except); +#endif /* USE_WIN32API */ }; /* Add a new serial interface to the interface list */ @@ -248,4 +264,12 @@ extern void serial_add_interface (struct extern void serial_log_command (const char *); +#ifdef USE_WIN32API + +/* Windows-only: find or create handles that we can wait on for this + serial device. */ +extern void serial_wait_handle (struct serial *, HANDLE *, HANDLE *); + +#endif /* USE_WIN32API */ + #endif /* SERIAL_H */ Index: src/gdb/gdb_select.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/gdb_select.h 2006-02-10 12:42:02.000000000 -0500 @@ -0,0 +1,37 @@ +/* Slightly more portable version of <sys/select.h>. + + Copyright (C) 2006 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#if !defined(GDB_SELECT_H) +#define GDB_SELECT_H + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifdef USE_WIN32API +#include <winsock2.h> +#endif + +extern int gdb_select (int n, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); + +#endif /* !defined(GDB_SELECT_H) */ Index: src/gdb/ser-tcp.c =================================================================== --- src.orig/gdb/ser-tcp.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/ser-tcp.c 2006-02-10 12:42:02.000000000 -0500 @@ -1,6 +1,6 @@ /* Serial interface for raw TCP connections on Un*x like systems. - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005 + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2001, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -23,7 +23,7 @@ #include "defs.h" #include "serial.h" #include "ser-base.h" -#include "ser-unix.h" +#include "ser-tcp.h" #include <sys/types.h> @@ -56,8 +56,6 @@ typedef int socklen_t; #endif -static int net_open (struct serial *scb, const char *name); -static void net_close (struct serial *scb); void _initialize_ser_tcp (void); /* seconds to wait for connect */ @@ -67,7 +65,7 @@ void _initialize_ser_tcp (void); /* Open a tcp socket */ -static int +int net_open (struct serial *scb, const char *name) { char *port_str, hostname[100]; @@ -153,7 +151,7 @@ net_open (struct serial *scb, const char { /* looks like we need to wait for the connect */ struct timeval t; - fd_set rset, wset; + fd_set rset, wset, eset; int polls = 0; FD_ZERO (&rset); @@ -174,10 +172,19 @@ net_open (struct serial *scb, const char FD_SET (scb->fd, &rset); wset = rset; + eset = rset; t.tv_sec = 0; t.tv_usec = 1000000 / POLL_INTERVAL; - n = select (scb->fd + 1, &rset, &wset, NULL, &t); + /* POSIX systems return connection success or failure by signalling + wset. Windows systems return success in wset and failure in + eset. + + We must call select here, rather than gdb_select, because + the serial structure has not yet been initialized - the + MinGW select wrapper will not know that this FD refers + to a socket. */ + n = select (scb->fd + 1, &rset, &wset, &eset, &t); polls++; } while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL); @@ -194,7 +201,7 @@ net_open (struct serial *scb, const char { int res, err; socklen_t len; - len = sizeof(err); + len = sizeof (err); /* On Windows, the fourth parameter to getsockopt is a "char *"; on UNIX systems it is generally "void *". The cast to "void *" is OK everywhere, since in C "void *" can be implicitly @@ -230,7 +237,7 @@ net_open (struct serial *scb, const char return 0; } -static void +void net_close (struct serial *scb) { if (scb->fd < 0) @@ -240,13 +247,13 @@ net_close (struct serial *scb) scb->fd = -1; } -static int +int net_read_prim (struct serial *scb, size_t count) { return recv (scb->fd, scb->buf, count, 0); } -static int +int net_write_prim (struct serial *scb, const void *buf, size_t count) { return send (scb->fd, buf, count, 0); @@ -255,13 +262,12 @@ net_write_prim (struct serial *scb, cons void _initialize_ser_tcp (void) { - struct serial_ops *ops; #ifdef USE_WIN32API - WSADATA wsa_data; - if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) - /* WinSock is unavailable. */ - return; -#endif + /* Do nothing; the TCP serial operations will be initialized in + ser-mingw.c. */ + return; +#else + struct serial_ops *ops; ops = XMALLOC (struct serial_ops); memset (ops, 0, sizeof (struct serial_ops)); ops->name = "tcp"; @@ -285,4 +291,5 @@ _initialize_ser_tcp (void) ops->read_prim = net_read_prim; ops->write_prim = net_write_prim; serial_add_interface (ops); +#endif /* USE_WIN32API */ } Index: src/gdb/ser-tcp.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/ser-tcp.h 2006-02-10 12:42:02.000000000 -0500 @@ -0,0 +1,32 @@ +/* Serial interface for raw TCP connections on Un*x like systems. + + Copyright (C) 2006 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef SER_TCP_H +#define SER_TCP_H + +struct serial; + +extern int net_open (struct serial *scb, const char *name); +extern void net_close (struct serial *scb); +extern int net_read_prim (struct serial *scb, size_t count); +extern int net_write_prim (struct serial *scb, const void *buf, size_t count); + +#endif Index: src/gdb/inflow.c =================================================================== --- src.orig/gdb/inflow.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/inflow.c 2006-02-10 12:42:02.000000000 -0500 @@ -1,6 +1,6 @@ /* Low level interface to ptrace, for GDB when running under Unix. Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, - 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -32,9 +32,7 @@ #include "gdb_string.h" #include <signal.h> #include <fcntl.h> -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif +#include "gdb_select.h" #include "inflow.h" @@ -129,7 +127,6 @@ gdb_has_a_terminal (void) #endif gdb_has_a_terminal_flag = no; - stdin_serial = serial_fdopen (0); if (stdin_serial != NULL) { our_ttystate = serial_get_tty_state (stdin_serial); @@ -643,7 +640,7 @@ handle_sigio (int signo) FD_ZERO (&readfds); FD_SET (target_activity_fd, &readfds); - numfds = select (target_activity_fd + 1, &readfds, NULL, NULL, NULL); + numfds = gdb_select (target_activity_fd + 1, &readfds, NULL, NULL, NULL); if (numfds >= 0 && FD_ISSET (target_activity_fd, &readfds)) { #ifndef _WIN32 @@ -730,6 +727,18 @@ gdb_setpgid (void) return retval; } +/* Get all the current tty settings (including whether we have a + tty at all!). We can't do this in _initialize_inflow because + serial_fdopen() won't work until the serial_ops_list is + initialized, but we don't want to do it lazily either, so + that we can guarantee stdin_serial is opened if there is + a terminal. */ +void +initialize_stdin_serial (void) +{ + stdin_serial = serial_fdopen (0); +} + void _initialize_inflow (void) { Index: src/gdb/terminal.h =================================================================== --- src.orig/gdb/terminal.h 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/terminal.h 2006-02-10 12:42:02.000000000 -0500 @@ -1,5 +1,6 @@ /* Terminal interface definitions for GDB, the GNU Debugger. - Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000 + Copyright (C) 1986, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1999, 2000, + 2006 Free Software Foundation, Inc. This file is part of GDB. @@ -88,4 +89,7 @@ extern int job_control; we lack job control. */ extern int gdb_setpgid (void); +/* Set up a serial structure describing standard input. In inflow.c. */ +extern void initialize_stdin_serial (void); + #endif /* !defined (TERMINAL_H) */ Index: src/gdb/top.c =================================================================== --- src.orig/gdb/top.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/top.c 2006-02-10 12:42:02.000000000 -0500 @@ -1550,6 +1550,8 @@ gdb_init (char *argv0) init_cli_cmds(); init_main (); /* But that omits this file! Do it now */ + initialize_stdin_serial (); + async_init_signals (); /* We need a default language for parsing expressions, so simple things like Index: src/gdb/NEWS =================================================================== --- src.orig/gdb/NEWS 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/NEWS 2006-02-10 12:42:02.000000000 -0500 @@ -41,6 +41,12 @@ detach-fork <n> Delete a fork from the Morpho Technologies ms2 ms1-elf +* Improved Windows host support + +GDB now builds as a cross debugger hosted on i686-mingw32, including +native console support, and remote communications using either +network sockets or serial ports. + * REMOVED features The ARM rdi-share module. Index: src/gdb/ser-mingw.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/ser-mingw.c 2006-02-10 12:42:02.000000000 -0500 @@ -0,0 +1,796 @@ +/* Serial interface for local (hardwired) serial ports on Windows systems + + Copyright (C) 2006 + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "serial.h" +#include "ser-base.h" +#include "ser-tcp.h" + +#include <windows.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> + +#include "gdb_assert.h" +#include "gdb_string.h" + +void _initialize_ser_windows (void); + +struct ser_windows_state +{ + int in_progress; + OVERLAPPED ov; + DWORD lastCommMask; + HANDLE except_event; +}; + +/* Open up a real live device for serial I/O. */ + +static int +ser_windows_open (struct serial *scb, const char *name) +{ + HANDLE h; + struct ser_windows_state *state; + COMMTIMEOUTS timeouts; + + /* Only allow COM ports. */ + if (strncmp (name, "COM", 3) != 0) + { + errno = ENOENT; + return -1; + } + + h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; + return -1; + } + + scb->fd = _open_osfhandle ((long) h, O_RDWR); + if (scb->fd < 0) + { + errno = ENOENT; + return -1; + } + + if (!SetCommMask (h, EV_RXCHAR)) + { + errno = EINVAL; + return -1; + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + if (!SetCommTimeouts (h, &timeouts)) + { + errno = EINVAL; + return -1; + } + + state = xmalloc (sizeof (struct ser_windows_state)); + memset (state, 0, sizeof (struct ser_windows_state)); + scb->state = state; + + /* Create a manual reset event to watch the input buffer. */ + state->ov.hEvent = CreateEvent (0, TRUE, FALSE, 0); + + /* Create a (currently unused) handle to record exceptions. */ + state->except_event = CreateEvent (0, TRUE, FALSE, 0); + + return 0; +} + +/* Wait for the output to drain away, as opposed to flushing (discarding) + it. */ + +static int +ser_windows_drain_output (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (FlushFileBuffers (h) != 0) ? 0 : -1; +} + +static int +ser_windows_flush_output (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (PurgeComm (h, PURGE_TXCLEAR) != 0) ? 0 : -1; +} + +static int +ser_windows_flush_input (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + return (PurgeComm (h, PURGE_RXCLEAR) != 0) ? 0 : -1; +} + +static int +ser_windows_send_break (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + if (SetCommBreak (h) == 0) + return -1; + + /* Delay for 250 milliseconds. */ + Sleep (250); + + if (ClearCommBreak (h)) + return -1; + + return 0; +} + +static void +ser_windows_raw (struct serial *scb) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return; + + state.fParity = FALSE; + state.fOutxCtsFlow = FALSE; + state.fOutxDsrFlow = FALSE; + state.fDtrControl = DTR_CONTROL_ENABLE; + state.fDsrSensitivity = FALSE; + state.fOutX = FALSE; + state.fInX = FALSE; + state.fNull = FALSE; + state.fAbortOnError = FALSE; + state.ByteSize = 8; + state.Parity = NOPARITY; + + scb->current_timeout = 0; + + if (SetCommState (h, &state) == 0) + warning (_("SetCommState failed\n")); +} + +static int +ser_windows_setstopbits (struct serial *scb, int num) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + switch (num) + { + case SERIAL_1_STOPBITS: + state.StopBits = ONESTOPBIT; + break; + case SERIAL_1_AND_A_HALF_STOPBITS: + state.StopBits = ONE5STOPBITS; + break; + case SERIAL_2_STOPBITS: + state.StopBits = TWOSTOPBITS; + break; + default: + return 1; + } + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + +static int +ser_windows_setbaudrate (struct serial *scb, int rate) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + state.BaudRate = rate; + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + +static void +ser_windows_close (struct serial *scb) +{ + struct ser_windows_state *state; + + /* Stop any pending selects. */ + CancelIo ((HANDLE) _get_osfhandle (scb->fd)); + state = scb->state; + CloseHandle (state->ov.hEvent); + CloseHandle (state->except_event); + + if (scb->fd < 0) + return; + + close (scb->fd); + scb->fd = -1; + + xfree (scb->state); +} + +static void +ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct ser_windows_state *state; + COMSTAT status; + DWORD errors; + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + + state = scb->state; + + *except = state->except_event; + *read = state->ov.hEvent; + + if (state->in_progress) + return; + + /* Reset the mask - we are only interested in any characters which + arrive after this point, not characters which might have arrived + and already been read. */ + + /* This really, really shouldn't be necessary - just the second one. + But otherwise an internal flag for EV_RXCHAR does not get + cleared, and we get a duplicated event, if the last batch + of characters included at least two arriving close together. */ + if (!SetCommMask (h, 0)) + warning (_("ser_windows_wait_handle: reseting mask failed")); + + if (!SetCommMask (h, EV_RXCHAR)) + warning (_("ser_windows_wait_handle: reseting mask failed (2)")); + + /* There's a potential race condition here; we must check cbInQue + and not wait if that's nonzero. */ + + ClearCommError (h, &errors, &status); + if (status.cbInQue > 0) + { + SetEvent (state->ov.hEvent); + return; + } + + state->in_progress = 1; + ResetEvent (state->ov.hEvent); + state->lastCommMask = -2; + if (WaitCommEvent (h, &state->lastCommMask, &state->ov)) + { + gdb_assert (state->lastCommMask & EV_RXCHAR); + SetEvent (state->ov.hEvent); + } + else + gdb_assert (GetLastError () == ERROR_IO_PENDING); +} + +static int +ser_windows_read_prim (struct serial *scb, size_t count) +{ + struct ser_windows_state *state; + OVERLAPPED ov; + DWORD bytes_read, bytes_read_tmp; + HANDLE h; + gdb_byte *p; + + state = scb->state; + if (state->in_progress) + { + WaitForSingleObject (state->ov.hEvent, INFINITE); + state->in_progress = 0; + ResetEvent (state->ov.hEvent); + } + + memset (&ov, 0, sizeof (OVERLAPPED)); + ov.hEvent = CreateEvent (0, FALSE, FALSE, 0); + h = (HANDLE) _get_osfhandle (scb->fd); + + if (!ReadFile (h, scb->buf, /* count */ 1, &bytes_read, &ov)) + { + if (GetLastError () != ERROR_IO_PENDING + || !GetOverlappedResult (h, &ov, &bytes_read, TRUE)) + bytes_read = -1; + } + + CloseHandle (ov.hEvent); + return bytes_read; +} + +static int +ser_windows_write_prim (struct serial *scb, const void *buf, size_t len) +{ + struct ser_windows_state *state; + OVERLAPPED ov; + DWORD bytes_written; + HANDLE h; + + memset (&ov, 0, sizeof (OVERLAPPED)); + ov.hEvent = CreateEvent (0, FALSE, FALSE, 0); + h = (HANDLE) _get_osfhandle (scb->fd); + if (!WriteFile (h, buf, len, &bytes_written, &ov)) + { + if (GetLastError () != ERROR_IO_PENDING + || !GetOverlappedResult (h, &ov, &bytes_written, TRUE)) + bytes_written = -1; + } + + CloseHandle (ov.hEvent); + return bytes_written; +} + +struct ser_console_state +{ + HANDLE read_event; + HANDLE except_event; + + HANDLE start_select; + HANDLE stop_select; +}; + +static DWORD WINAPI +console_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state, state_copy; + int event_index, fd; + HANDLE h; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct ser_console_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + h = (HANDLE) _get_osfhandle (fd); + + while (1) + { + HANDLE wait_events[2]; + INPUT_RECORD record; + DWORD n_records; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + retry: + wait_events[0] = state->stop_select; + wait_events[1] = h; + + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Wait must have failed; assume an error has occured, e.g. + the handle has been closed. */ + SetEvent (state->except_event); + continue; + } + + /* We've got a pending event on the console. See if it's + of interest. */ + if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1) + { + /* Something went wrong. Maybe the console is gone. */ + SetEvent (state->except_event); + continue; + } + + if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) + { + /* This is really a keypress. */ + SetEvent (state->read_event); + continue; + } + + /* Otherwise discard it and wait again. */ + ReadConsoleInput (h, &record, 1, &n_records); + goto retry; + } +} + +static int +fd_is_pipe (int fd) +{ + if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL)) + return 1; + else + return 0; +} + +static DWORD WINAPI +pipe_select_thread (void *arg) +{ + struct serial *scb = arg; + struct ser_console_state *state, state_copy; + int event_index, fd; + HANDLE h; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct ser_console_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + h = (HANDLE) _get_osfhandle (fd); + + while (1) + { + HANDLE wait_events[2]; + DWORD n_avail; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + retry: + if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) + { + SetEvent (state->except_event); + continue; + } + + if (n_avail > 0) + { + SetEvent (state->read_event); + continue; + } + + if (WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + Sleep (10); + goto retry; + } +} + +static void +ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct ser_console_state *state = scb->state; + + if (state == NULL) + { + DWORD threadId; + int is_tty; + + is_tty = isatty (scb->fd); + if (!is_tty && !fd_is_pipe (scb->fd)) + { + *read = NULL; + *except = NULL; + return; + } + + state = xmalloc (sizeof (struct ser_console_state)); + memset (state, 0, sizeof (struct ser_console_state)); + scb->state = state; + + /* Create auto reset events to wake and terminate the select thread. */ + state->start_select = CreateEvent (0, FALSE, FALSE, 0); + state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Create our own events to report read and exceptions separately. + The exception event is currently never used. */ + state->read_event = CreateEvent (0, FALSE, FALSE, 0); + state->except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* And finally start the select thread. */ + if (is_tty) + CreateThread (NULL, 0, console_select_thread, scb, 0, &threadId); + else + CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); + } + + ResetEvent (state->read_event); + ResetEvent (state->except_event); + + SetEvent (state->start_select); + + *read = state->read_event; + *except = state->except_event; +} + +static void +ser_console_close (struct serial *scb) +{ + struct ser_console_state *state = scb->state; + + if (scb->state) + { + SetEvent (state->stop_select); + + CloseHandle (state->read_event); + CloseHandle (state->except_event); + + xfree (scb->state); + } +} + +struct ser_console_ttystate +{ + int is_a_tty; +}; + +static serial_ttystate +ser_console_get_tty_state (struct serial *scb) +{ + if (isatty (scb->fd)) + { + struct ser_console_ttystate *state; + state = (struct ser_console_ttystate *) xmalloc (sizeof *state); + state->is_a_tty = 1; + return state; + } + else + return NULL; +} + +struct net_windows_state +{ + HANDLE read_event; + HANDLE except_event; + + HANDLE start_select; + HANDLE stop_select; + HANDLE sock_event; +}; + +static DWORD WINAPI +net_windows_select_thread (void *arg) +{ + struct serial *scb = arg; + struct net_windows_state *state, state_copy; + int event_index, fd; + + /* Copy useful information out of the control block, to make sure + that we do not race with freeing it. */ + state_copy = *(struct net_windows_state *) scb->state; + state = &state_copy; + fd = scb->fd; + + while (1) + { + HANDLE wait_events[2]; + WSANETWORKEVENTS events; + + wait_events[0] = state->start_select; + wait_events[1] = state->stop_select; + + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + wait_events[0] = state->stop_select; + wait_events[1] = state->sock_event; + + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + { + CloseHandle (state->stop_select); + return 0; + } + + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Some error has occured. Assume that this is an error + condition. */ + SetEvent (state->except_event); + continue; + } + + /* Enumerate the internal network events, and reset the object that + signalled us to catch the next event. */ + WSAEnumNetworkEvents (fd, state->sock_event, &events); + + if (events.lNetworkEvents & FD_READ) + SetEvent (state->read_event); + + if (events.lNetworkEvents & FD_CLOSE) + SetEvent (state->except_event); + } +} + +static void +net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct net_windows_state *state = scb->state; + + ResetEvent (state->read_event); + ResetEvent (state->except_event); + + SetEvent (state->start_select); + + *read = state->read_event; + *except = state->except_event; +} + +static int +net_windows_open (struct serial *scb, const char *name) +{ + struct net_windows_state *state; + int ret; + DWORD threadId; + + ret = net_open (scb, name); + if (ret != 0) + return ret; + + state = xmalloc (sizeof (struct net_windows_state)); + memset (state, 0, sizeof (struct net_windows_state)); + scb->state = state; + + /* Create auto reset events to wake and terminate the select thread. */ + state->start_select = CreateEvent (0, FALSE, FALSE, 0); + state->stop_select = CreateEvent (0, FALSE, FALSE, 0); + + /* Associate an event with the socket. */ + state->sock_event = CreateEvent (0, TRUE, FALSE, 0); + WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE); + + /* Create our own events to report read and close separately. */ + state->read_event = CreateEvent (0, FALSE, FALSE, 0); + state->except_event = CreateEvent (0, FALSE, FALSE, 0); + + /* And finally start the select thread. */ + CreateThread (NULL, 0, net_windows_select_thread, scb, 0, &threadId); + + return 0; +} + + +static void +net_windows_close (struct serial *scb) +{ + struct net_windows_state *state = scb->state; + + SetEvent (state->stop_select); + + CloseHandle (state->read_event); + CloseHandle (state->except_event); + CloseHandle (state->start_select); + CloseHandle (state->sock_event); + + xfree (scb->state); + + net_close (scb); +} + +void +_initialize_ser_windows (void) +{ + WSADATA wsa_data; + struct serial_ops *ops; + + /* First register the serial port driver. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "hardwire"; + ops->next = 0; + ops->open = ser_windows_open; + ops->close = ser_windows_close; + + ops->flush_output = ser_windows_flush_output; + ops->flush_input = ser_windows_flush_input; + ops->send_break = ser_windows_send_break; + + /* These are only used for stdin; we do not need them for serial + ports, so supply the standard dummies. */ + 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->go_raw = ser_windows_raw; + ops->setbaudrate = ser_windows_setbaudrate; + ops->setstopbits = ser_windows_setstopbits; + ops->drain_output = ser_windows_drain_output; + ops->readchar = ser_base_readchar; + ops->write = ser_base_write; + ops->async = ser_base_async; + ops->read_prim = ser_windows_read_prim; + ops->write_prim = ser_windows_write_prim; + ops->wait_handle = ser_windows_wait_handle; + + serial_add_interface (ops); + + /* Next create the dummy serial driver used for terminals. We only + provide the TTY-related methods. */ + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + + ops->name = "terminal"; + ops->next = 0; + + ops->close = ser_console_close; + ops->get_tty_state = ser_console_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->drain_output = ser_base_drain_output; + ops->wait_handle = ser_console_wait_handle; + + serial_add_interface (ops); + + /* If WinSock works, register the TCP/UDP socket driver. */ + + if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) + /* WinSock is unavailable. */ + return; + + ops = XMALLOC (struct serial_ops); + memset (ops, 0, sizeof (struct serial_ops)); + ops->name = "tcp"; + ops->next = 0; + ops->open = net_windows_open; + ops->close = net_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 = net_read_prim; + ops->write_prim = net_write_prim; + ops->wait_handle = net_windows_wait_handle; + serial_add_interface (ops); +} Index: src/gdb/mingw-hdep.c =================================================================== --- src.orig/gdb/mingw-hdep.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/mingw-hdep.c 2006-02-10 12:42:02.000000000 -0500 @@ -21,7 +21,10 @@ Boston, MA 02110-1301, USA. */ #include "defs.h" +#include "serial.h" +#include "gdb_assert.h" +#include "gdb_select.h" #include "gdb_string.h" #include <windows.h> @@ -69,3 +72,124 @@ safe_strerror (int errnum) return buffer; } + +/* Wrapper for select. On Windows systems, where the select interface + only works for sockets, this uses the GDB serial abstraction to + handle sockets, consoles, pipes, and serial ports. + + The arguments to this function are the same as the traditional + arguments to select on POSIX platforms. */ + +int +gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + static HANDLE never_handle; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + HANDLE h; + DWORD event; + DWORD num_handles; + int fd; + int num_ready; + int indx; + + num_ready = 0; + num_handles = 0; + for (fd = 0; fd < n; ++fd) + { + HANDLE read = NULL, except = NULL; + struct serial *scb; + + /* There is no support yet for WRITEFDS. At present, this isn't + used by GDB -- but we do not want to silently ignore WRITEFDS + if something starts using it. */ + gdb_assert (!writefds || !FD_ISSET (fd, writefds)); + + if (!FD_ISSET (fd, readfds) + && !FD_ISSET (fd, exceptfds)) + continue; + h = (HANDLE) _get_osfhandle (fd); + + scb = serial_for_fd (fd); + if (scb) + serial_wait_handle (scb, &read, &except); + + if (read == NULL) + read = h; + if (except == NULL) + { + if (!never_handle) + never_handle = CreateEvent (0, FALSE, FALSE, 0); + + except = never_handle; + } + + if (FD_ISSET (fd, readfds)) + { + gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); + handles[num_handles++] = read; + } + + if (FD_ISSET (fd, exceptfds)) + { + gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); + handles[num_handles++] = except; + } + } + /* If we don't need to wait for any handles, we are done. */ + if (!num_handles) + { + if (timeout) + Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000); + + return 0; + } + + event = WaitForMultipleObjects (num_handles, + handles, + FALSE, + timeout + ? (timeout->tv_sec * 1000 + + timeout->tv_usec / 1000) + : INFINITE); + /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the + HANDLES included an abandoned mutex. Since GDB doesn't use + mutexes, that should never occur. */ + gdb_assert (!(WAIT_ABANDONED_0 <= event + && event < WAIT_ABANDONED_0 + num_handles)); + if (event == WAIT_FAILED) + return -1; + if (event == WAIT_TIMEOUT) + return 0; + /* Run through the READFDS, clearing bits corresponding to descriptors + for which input is unavailable. */ + h = handles[event - WAIT_OBJECT_0]; + for (fd = 0, indx = 0; fd < n; ++fd) + { + HANDLE fd_h; + + if (FD_ISSET (fd, readfds)) + { + fd_h = handles[indx++]; + /* This handle might be ready, even though it wasn't the handle + returned by WaitForMultipleObjects. */ + if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) + FD_CLR (fd, readfds); + else + num_ready++; + } + + if (FD_ISSET (fd, exceptfds)) + { + fd_h = handles[indx++]; + /* This handle might be ready, even though it wasn't the handle + returned by WaitForMultipleObjects. */ + if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) + FD_CLR (fd, exceptfds); + else + num_ready++; + } + } + + return num_ready; +} Index: src/gdb/posix-hdep.c =================================================================== --- src.orig/gdb/posix-hdep.c 2006-02-10 12:41:57.000000000 -0500 +++ src/gdb/posix-hdep.c 2006-02-10 12:42:02.000000000 -0500 @@ -24,6 +24,8 @@ #include "gdb_string.h" +#include "gdb_select.h" + /* The strerror() function can return NULL for errno values that are out of range. Provide a "safe" version that always returns a printable string. */ @@ -43,3 +45,11 @@ safe_strerror (int errnum) return (msg); } +/* Wrapper for select. Nothing special needed on POSIX platforms. */ + +int +gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + return select (n, readfds, writefds, exceptfds, timeout); +} Index: src/gdb/remote-st.c =================================================================== --- src.orig/gdb/remote-st.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/remote-st.c 2006-02-10 12:42:02.000000000 -0500 @@ -704,7 +704,7 @@ connect_command (char *args, int fromtty { FD_SET (0, &readfds); FD_SET (deprecated_serial_fd (st2000_desc), &readfds); - numfds = select (sizeof (readfds) * 8, &readfds, 0, 0, 0); + numfds = gdb_select (sizeof (readfds) * 8, &readfds, 0, 0, 0); } while (numfds == 0); Index: src/gdb/ser-unix.c =================================================================== --- src.orig/gdb/ser-unix.c 2006-02-10 12:41:28.000000000 -0500 +++ src/gdb/ser-unix.c 2006-02-10 12:42:02.000000000 -0500 @@ -31,6 +31,7 @@ #include <sys/socket.h> #include <sys/time.h> +#include "gdb_select.h" #include "gdb_string.h" #ifdef HAVE_TERMIOS @@ -365,7 +366,7 @@ hardwire_send_break (struct serial *scb) the full length of time. I think that is OK. */ timeout.tv_sec = 0; timeout.tv_usec = 250000; - select (0, 0, 0, 0, &timeout); + gdb_select (0, 0, 0, 0, &timeout); status = ioctl (scb->fd, TIOCCBRK, 0); return status; } @@ -448,9 +449,9 @@ wait_for (struct serial *scb, int timeou FD_SET (scb->fd, &readfds); if (timeout >= 0) - numfds = select (scb->fd + 1, &readfds, 0, 0, &tv); + numfds = gdb_select (scb->fd + 1, &readfds, 0, 0, &tv); else - numfds = select (scb->fd + 1, &readfds, 0, 0, 0); + numfds = gdb_select (scb->fd + 1, &readfds, 0, 0, 0); if (numfds <= 0) if (numfds == 0) ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports 2006-02-06 21:02 ` Daniel Jacobowitz 2006-02-06 23:02 ` Mark Kettenis @ 2006-02-07 4:41 ` Eli Zaretskii 1 sibling, 0 replies; 26+ messages in thread From: Eli Zaretskii @ 2006-02-07 4:41 UTC (permalink / raw) To: gdb-patches > Date: Mon, 6 Feb 2006 16:02:38 -0500 > From: Daniel Jacobowitz <drow@false.org> > > On Fri, Feb 03, 2006 at 05:05:29PM -0500, Daniel Jacobowitz wrote: > > Fixes, all for a mingw32-hosted GDB: > > Here is a revised version, in which the Windows select magic does not > live in event-loop.c, and including the NEWS entry. I haven't changed > the pipe polling code; thanks to Ian for his suggestions, but I'm > not sufficiently sure they're workable. > > Are there any bits of this patch that strike you as too ugly or in the > wrong place? I'm fine with this. ^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2006-02-10 22:02 UTC | newest] Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2006-02-03 22:05 RFA: Various Windows (mingw32) additions, mostly relating to select or serial ports Daniel Jacobowitz 2006-02-03 22:08 ` Daniel Jacobowitz 2006-02-04 12:43 ` Eli Zaretskii 2006-02-04 15:13 ` Daniel Jacobowitz 2006-02-04 6:28 ` Ian Lance Taylor 2006-02-04 10:30 ` Eli Zaretskii 2006-02-04 17:06 ` Ian Lance Taylor 2006-02-04 17:45 ` Eli Zaretskii 2006-02-04 15:00 ` Daniel Jacobowitz 2006-02-05 0:01 ` Ian Lance Taylor 2006-02-05 22:00 ` Daniel Jacobowitz 2006-02-06 3:27 ` Ian Lance Taylor 2006-02-06 4:03 ` Daniel Jacobowitz 2006-02-04 12:38 ` Eli Zaretskii 2006-02-04 15:11 ` Daniel Jacobowitz 2006-02-04 15:23 ` Eli Zaretskii 2006-02-04 15:30 ` Daniel Jacobowitz 2006-02-06 21:02 ` Daniel Jacobowitz 2006-02-06 23:02 ` Mark Kettenis 2006-02-06 23:17 ` Daniel Jacobowitz 2006-02-06 23:21 ` Christopher Faylor 2006-02-09 22:38 ` Daniel Jacobowitz 2006-02-10 7:53 ` Eli Zaretskii 2006-02-10 20:46 ` Mark Kettenis 2006-02-10 22:02 ` Daniel Jacobowitz 2006-02-07 4:41 ` Eli Zaretskii
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox