| /* Serial interface for local (hardwired) serial ports on Windows systems | 
 |  | 
 |    Copyright (C) 2006-2023 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "serial.h" | 
 | #include "ser-base.h" | 
 | #include "ser-tcp.h" | 
 |  | 
 | #include <windows.h> | 
 | #include <conio.h> | 
 |  | 
 | #include <fcntl.h> | 
 | #include <unistd.h> | 
 | #include <sys/types.h> | 
 |  | 
 | #include "command.h" | 
 | #include "gdbsupport/buildargv.h" | 
 |  | 
 | struct ser_windows_state | 
 | { | 
 |   int in_progress; | 
 |   OVERLAPPED ov; | 
 |   DWORD lastCommMask; | 
 |   HANDLE except_event; | 
 | }; | 
 |  | 
 | /* CancelIo is not available for Windows 95 OS, so we need to use | 
 |    LoadLibrary/GetProcAddress to avoid a startup failure.  */ | 
 | #define CancelIo dyn_CancelIo | 
 | typedef BOOL WINAPI (CancelIo_ftype) (HANDLE); | 
 | static CancelIo_ftype *CancelIo; | 
 |  | 
 | /* 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; | 
 |  | 
 |   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 ((intptr_t) 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 = XCNEW (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.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; | 
 |  | 
 |   if (SetCommState (h, &state) == 0) | 
 |     warning (_("SetCommState failed")); | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | /* Implement the "setparity" serial_ops callback.  */ | 
 |  | 
 | static int | 
 | ser_windows_setparity (struct serial *scb, int parity) | 
 | { | 
 |   HANDLE h = (HANDLE) _get_osfhandle (scb->fd); | 
 |   DCB state; | 
 |  | 
 |   if (GetCommState (h, &state) == 0) | 
 |     return -1; | 
 |  | 
 |   switch (parity) | 
 |     { | 
 |     case GDBPARITY_NONE: | 
 |       state.Parity = NOPARITY; | 
 |       state.fParity = FALSE; | 
 |       break; | 
 |     case GDBPARITY_ODD: | 
 |       state.Parity = ODDPARITY; | 
 |       state.fParity = TRUE; | 
 |       break; | 
 |     case GDBPARITY_EVEN: | 
 |       state.Parity = EVENPARITY; | 
 |       state.fParity = TRUE; | 
 |       break; | 
 |     default: | 
 |       internal_warning ("Incorrect parity value: %d", parity); | 
 |       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.  On Windows 95 OS, CancelIo function does | 
 |      not exist.  In that case, it can be replaced by a call to CloseHandle, | 
 |      but this is not necessary here as we do close the Windows handle | 
 |      by calling close (scb->fd) below.  */ | 
 |   if (CancelIo) | 
 |     CancelIo ((HANDLE) _get_osfhandle (scb->fd)); | 
 |   state = (struct ser_windows_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 = (struct ser_windows_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; | 
 |   HANDLE h; | 
 |  | 
 |   state = (struct ser_windows_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) | 
 | { | 
 |   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; | 
 | } | 
 |  | 
 | /* On Windows, gdb_select is implemented using WaitForMulpleObjects. | 
 |    A "select thread" is created for each file descriptor.  These | 
 |    threads looks for activity on the corresponding descriptor, using | 
 |    whatever techniques are appropriate for the descriptor type.  When | 
 |    that activity occurs, the thread signals an appropriate event, | 
 |    which wakes up WaitForMultipleObjects. | 
 |  | 
 |    Each select thread is in one of two states: stopped or started. | 
 |    Select threads begin in the stopped state.  When gdb_select is | 
 |    called, threads corresponding to the descriptors of interest are | 
 |    started by calling a wait_handle function.  Each thread that | 
 |    notices activity signals the appropriate event and then reenters | 
 |    the stopped state.  Before gdb_select returns it calls the | 
 |    wait_handle_done functions, which return the threads to the stopped | 
 |    state.  */ | 
 |  | 
 | enum select_thread_state { | 
 |   STS_STARTED, | 
 |   STS_STOPPED | 
 | }; | 
 |  | 
 | struct ser_console_state | 
 | { | 
 |   /* Signaled by the select thread to indicate that data is available | 
 |      on the file descriptor.  */ | 
 |   HANDLE read_event; | 
 |   /* Signaled by the select thread to indicate that an exception has | 
 |      occurred on the file descriptor.  */ | 
 |   HANDLE except_event; | 
 |   /* Signaled by the select thread to indicate that it has entered the | 
 |      started state.  HAVE_STARTED and HAVE_STOPPED are never signaled | 
 |      simultaneously.  */ | 
 |   HANDLE have_started; | 
 |   /* Signaled by the select thread to indicate that it has stopped, | 
 |      either because data is available (and READ_EVENT is signaled), | 
 |      because an exception has occurred (and EXCEPT_EVENT is signaled), | 
 |      or because STOP_SELECT was signaled.  */ | 
 |   HANDLE have_stopped; | 
 |  | 
 |   /* Signaled by the main program to tell the select thread to enter | 
 |      the started state.  */ | 
 |   HANDLE start_select; | 
 |   /* Signaled by the main program to tell the select thread to enter | 
 |      the stopped state.  */ | 
 |   HANDLE stop_select; | 
 |   /* Signaled by the main program to tell the select thread to | 
 |      exit.  */ | 
 |   HANDLE exit_select; | 
 |  | 
 |   /* The handle for the select thread.  */ | 
 |   HANDLE thread; | 
 |   /* The state of the select thread.  This field is only accessed in | 
 |      the main program, never by the select thread itself.  */ | 
 |   enum select_thread_state thread_state; | 
 | }; | 
 |  | 
 | /* Called by a select thread to enter the stopped state.  This | 
 |    function does not return until the thread has re-entered the | 
 |    started state.  */ | 
 | static void | 
 | select_thread_wait (struct ser_console_state *state) | 
 | { | 
 |   HANDLE wait_events[2]; | 
 |  | 
 |   /* There are two things that can wake us up: a request that we enter | 
 |      the started state, or that we exit this thread.  */ | 
 |   wait_events[0] = state->start_select; | 
 |   wait_events[1] = state->exit_select; | 
 |   if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE)  | 
 |       != WAIT_OBJECT_0) | 
 |     /* Either the EXIT_SELECT event was signaled (requesting that the | 
 |        thread exit) or an error has occurred.  In either case, we exit | 
 |        the thread.  */ | 
 |     ExitThread (0); | 
 |    | 
 |   /* We are now in the started state.  */ | 
 |   SetEvent (state->have_started); | 
 | } | 
 |  | 
 | typedef DWORD WINAPI (*thread_fn_type)(void *); | 
 |  | 
 | /* Create a new select thread for SCB executing THREAD_FN.  The STATE | 
 |    will be filled in by this function before return.  */ | 
 | static void | 
 | create_select_thread (thread_fn_type thread_fn, | 
 | 		      struct serial *scb, | 
 | 		      struct ser_console_state *state) | 
 | { | 
 |   DWORD threadId; | 
 |  | 
 |   /* Create all of the events.  These are all auto-reset events.  */ | 
 |   state->read_event = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |   state->except_event = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |   state->have_started = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |   state->have_stopped = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |   state->start_select = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |   state->stop_select = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |   state->exit_select = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |  | 
 |   state->thread = CreateThread (NULL, 0, thread_fn, scb, 0, &threadId); | 
 |   /* The thread begins in the stopped state.  */ | 
 |   state->thread_state = STS_STOPPED; | 
 | } | 
 |  | 
 | /* Destroy the select thread indicated by STATE.  */ | 
 | static void | 
 | destroy_select_thread (struct ser_console_state *state) | 
 | { | 
 |   /* Ask the thread to exit.  */ | 
 |   SetEvent (state->exit_select); | 
 |   /* Wait until it does.  */ | 
 |   WaitForSingleObject (state->thread, INFINITE); | 
 |  | 
 |   /* Destroy the events.  */ | 
 |   CloseHandle (state->read_event); | 
 |   CloseHandle (state->except_event); | 
 |   CloseHandle (state->have_started); | 
 |   CloseHandle (state->have_stopped); | 
 |   CloseHandle (state->start_select); | 
 |   CloseHandle (state->stop_select); | 
 |   CloseHandle (state->exit_select); | 
 | } | 
 |  | 
 | /* Called by gdb_select to start the select thread indicated by STATE. | 
 |    This function does not return until the thread has started.  */ | 
 | static void | 
 | start_select_thread (struct ser_console_state *state) | 
 | { | 
 |   /* Ask the thread to start.  */ | 
 |   SetEvent (state->start_select); | 
 |   /* Wait until it does.  */ | 
 |   WaitForSingleObject (state->have_started, INFINITE); | 
 |   /* The thread is now started.  */ | 
 |   state->thread_state = STS_STARTED; | 
 | } | 
 |  | 
 | /* Called by gdb_select to stop the select thread indicated by STATE. | 
 |    This function does not return until the thread has stopped.  */ | 
 | static void | 
 | stop_select_thread (struct ser_console_state *state) | 
 | { | 
 |   /* If the thread is already in the stopped state, we have nothing to | 
 |      do.  Some of the wait_handle functions avoid calling | 
 |      start_select_thread if they notice activity on the relevant file | 
 |      descriptors.  The wait_handle_done functions still call | 
 |      stop_select_thread -- but it is already stopped.  */ | 
 |   if (state->thread_state != STS_STARTED) | 
 |     return; | 
 |   /* Ask the thread to stop.  */ | 
 |   SetEvent (state->stop_select); | 
 |   /* Wait until it does.  */ | 
 |   WaitForSingleObject (state->have_stopped, INFINITE); | 
 |   /* The thread is now stopped.  */ | 
 |   state->thread_state = STS_STOPPED; | 
 | } | 
 |  | 
 | static DWORD WINAPI | 
 | console_select_thread (void *arg) | 
 | { | 
 |   struct serial *scb = (struct serial *) arg; | 
 |   struct ser_console_state *state; | 
 |   int event_index; | 
 |   HANDLE h; | 
 |  | 
 |   state = (struct ser_console_state *) scb->state; | 
 |   h = (HANDLE) _get_osfhandle (scb->fd); | 
 |  | 
 |   while (1) | 
 |     { | 
 |       HANDLE wait_events[2]; | 
 |       INPUT_RECORD record; | 
 |       DWORD n_records; | 
 |  | 
 |       select_thread_wait (state); | 
 |  | 
 |       while (1) | 
 | 	{ | 
 | 	  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) | 
 | 	    break; | 
 |  | 
 | 	  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); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  /* 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); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) | 
 | 	    { | 
 | 	      WORD keycode = record.Event.KeyEvent.wVirtualKeyCode; | 
 |  | 
 | 	      /* Ignore events containing only control keys.  We must | 
 | 		 recognize "enhanced" keys which we are interested in | 
 | 		 reading via getch, if they do not map to ASCII.  But we | 
 | 		 do not want to report input available for e.g. the | 
 | 		 control key alone.  */ | 
 |  | 
 | 	      if (record.Event.KeyEvent.uChar.AsciiChar != 0 | 
 | 		  || keycode == VK_PRIOR | 
 | 		  || keycode == VK_NEXT | 
 | 		  || keycode == VK_END | 
 | 		  || keycode == VK_HOME | 
 | 		  || keycode == VK_LEFT | 
 | 		  || keycode == VK_UP | 
 | 		  || keycode == VK_RIGHT | 
 | 		  || keycode == VK_DOWN | 
 | 		  || keycode == VK_INSERT | 
 | 		  || keycode == VK_DELETE) | 
 | 		{ | 
 | 		  /* This is really a keypress.  */ | 
 | 		  SetEvent (state->read_event); | 
 | 		  break; | 
 | 		} | 
 | 	    } | 
 | 	  else if (record.EventType == MOUSE_EVENT) | 
 | 	    { | 
 | 	      SetEvent (state->read_event); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  /* Otherwise discard it and wait again.  */ | 
 | 	  ReadConsoleInput (h, &record, 1, &n_records); | 
 | 	} | 
 |  | 
 |       SetEvent(state->have_stopped); | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | fd_is_pipe (int fd) | 
 | { | 
 |   if (PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, NULL, NULL)) | 
 |     return 1; | 
 |   else | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | fd_is_file (int fd) | 
 | { | 
 |   if (GetFileType ((HANDLE) _get_osfhandle (fd)) == FILE_TYPE_DISK) | 
 |     return 1; | 
 |   else | 
 |     return 0; | 
 | } | 
 |  | 
 | static DWORD WINAPI | 
 | pipe_select_thread (void *arg) | 
 | { | 
 |   struct serial *scb = (struct serial *) arg; | 
 |   struct ser_console_state *state; | 
 |   HANDLE h; | 
 |  | 
 |   state = (struct ser_console_state *) scb->state; | 
 |   h = (HANDLE) _get_osfhandle (scb->fd); | 
 |  | 
 |   while (1) | 
 |     { | 
 |       DWORD n_avail; | 
 |  | 
 |       select_thread_wait (state); | 
 |  | 
 |       /* Wait for something to happen on the pipe.  */ | 
 |       while (1) | 
 | 	{ | 
 | 	  if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) | 
 | 	    { | 
 | 	      SetEvent (state->except_event); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  if (n_avail > 0) | 
 | 	    { | 
 | 	      SetEvent (state->read_event); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  /* Delay 10ms before checking again, but allow the stop | 
 | 	     event to wake us.  */ | 
 | 	  if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0) | 
 | 	    break; | 
 | 	} | 
 |  | 
 |       SetEvent (state->have_stopped); | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 | static DWORD WINAPI | 
 | file_select_thread (void *arg) | 
 | { | 
 |   struct serial *scb = (struct serial *) arg; | 
 |   struct ser_console_state *state; | 
 |   HANDLE h; | 
 |  | 
 |   state = (struct ser_console_state *) scb->state; | 
 |   h = (HANDLE) _get_osfhandle (scb->fd); | 
 |  | 
 |   while (1) | 
 |     { | 
 |       select_thread_wait (state); | 
 |  | 
 |       if (SetFilePointer (h, 0, NULL, FILE_CURRENT) | 
 | 	  == INVALID_SET_FILE_POINTER) | 
 | 	SetEvent (state->except_event); | 
 |       else | 
 | 	SetEvent (state->read_event); | 
 |  | 
 |       SetEvent (state->have_stopped); | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 | static void | 
 | ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) | 
 | { | 
 |   struct ser_console_state *state = (struct ser_console_state *) scb->state; | 
 |  | 
 |   if (state == NULL) | 
 |     { | 
 |       thread_fn_type thread_fn; | 
 |       int is_tty; | 
 |  | 
 |       is_tty = isatty (scb->fd); | 
 |       if (!is_tty && !fd_is_file (scb->fd) && !fd_is_pipe (scb->fd)) | 
 | 	{ | 
 | 	  *read = NULL; | 
 | 	  *except = NULL; | 
 | 	  return; | 
 | 	} | 
 |  | 
 |       state = XCNEW (struct ser_console_state); | 
 |       scb->state = state; | 
 |  | 
 |       if (is_tty) | 
 | 	thread_fn = console_select_thread; | 
 |       else if (fd_is_pipe (scb->fd)) | 
 | 	thread_fn = pipe_select_thread; | 
 |       else | 
 | 	thread_fn = file_select_thread; | 
 |  | 
 |       create_select_thread (thread_fn, scb, state); | 
 |     } | 
 |  | 
 |   *read = state->read_event; | 
 |   *except = state->except_event; | 
 |  | 
 |   /* Start from a blank state.  */ | 
 |   ResetEvent (state->read_event); | 
 |   ResetEvent (state->except_event); | 
 |   ResetEvent (state->stop_select); | 
 |  | 
 |   /* First check for a key already in the buffer.  If there is one, | 
 |      we don't need a thread.  This also catches the second key of | 
 |      multi-character returns from getch, for instance for arrow | 
 |      keys.  The second half is in a C library internal buffer, | 
 |      and PeekConsoleInput will not find it.  */ | 
 |   if (_kbhit ()) | 
 |     { | 
 |       SetEvent (state->read_event); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* Otherwise, start the select thread.  */ | 
 |   start_select_thread (state); | 
 | } | 
 |  | 
 | static void | 
 | ser_console_done_wait_handle (struct serial *scb) | 
 | { | 
 |   struct ser_console_state *state = (struct ser_console_state *) scb->state; | 
 |  | 
 |   if (state == NULL) | 
 |     return; | 
 |  | 
 |   stop_select_thread (state); | 
 | } | 
 |  | 
 | static void | 
 | ser_console_close (struct serial *scb) | 
 | { | 
 |   struct ser_console_state *state = (struct ser_console_state *) scb->state; | 
 |  | 
 |   if (scb->state) | 
 |     { | 
 |       destroy_select_thread (state); | 
 |       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 = XNEW (struct ser_console_ttystate); | 
 |       state->is_a_tty = 1; | 
 |       return state; | 
 |     } | 
 |   else | 
 |     return NULL; | 
 | } | 
 |  | 
 | struct pipe_state | 
 | { | 
 |   /* Since we use the pipe_select_thread for our select emulation, | 
 |      we need to place the state structure it requires at the front | 
 |      of our state.  */ | 
 |   struct ser_console_state wait; | 
 |  | 
 |   /* The pex obj for our (one-stage) pipeline.  */ | 
 |   struct pex_obj *pex; | 
 |  | 
 |   /* Streams for the pipeline's input and output.  */ | 
 |   FILE *input, *output; | 
 | }; | 
 |  | 
 | static struct pipe_state * | 
 | make_pipe_state (void) | 
 | { | 
 |   struct pipe_state *ps = XCNEW (struct pipe_state); | 
 |  | 
 |   ps->wait.read_event = INVALID_HANDLE_VALUE; | 
 |   ps->wait.except_event = INVALID_HANDLE_VALUE; | 
 |   ps->wait.start_select = INVALID_HANDLE_VALUE; | 
 |   ps->wait.stop_select = INVALID_HANDLE_VALUE; | 
 |  | 
 |   return ps; | 
 | } | 
 |  | 
 | static void | 
 | free_pipe_state (struct pipe_state *ps) | 
 | { | 
 |   int saved_errno = errno; | 
 |  | 
 |   if (ps->wait.read_event != INVALID_HANDLE_VALUE) | 
 |     destroy_select_thread (&ps->wait); | 
 |  | 
 |   /* Close the pipe to the child.  We must close the pipe before | 
 |      calling pex_free because pex_free will wait for the child to exit | 
 |      and the child will not exit until the pipe is closed.  */ | 
 |   if (ps->input) | 
 |     fclose (ps->input); | 
 |   if (ps->pex) | 
 |     { | 
 |       pex_free (ps->pex); | 
 |       /* pex_free closes ps->output.  */ | 
 |     } | 
 |   else if (ps->output) | 
 |     fclose (ps->output); | 
 |  | 
 |   xfree (ps); | 
 |  | 
 |   errno = saved_errno; | 
 | } | 
 |  | 
 | struct pipe_state_destroyer | 
 | { | 
 |   void operator() (pipe_state *ps) const | 
 |   { | 
 |     free_pipe_state (ps); | 
 |   } | 
 | }; | 
 |  | 
 | typedef std::unique_ptr<pipe_state, pipe_state_destroyer> pipe_state_up; | 
 |  | 
 | static int | 
 | pipe_windows_open (struct serial *scb, const char *name) | 
 | { | 
 |   FILE *pex_stderr; | 
 |  | 
 |   if (name == NULL) | 
 |     error_no_arg (_("child command")); | 
 |  | 
 |   if (*name == '|') | 
 |     { | 
 |       name++; | 
 |       name = skip_spaces (name); | 
 |     } | 
 |  | 
 |   gdb_argv argv (name); | 
 |  | 
 |   if (! argv[0] || argv[0][0] == '\0') | 
 |     error (_("missing child command")); | 
 |  | 
 |   pipe_state_up ps (make_pipe_state ()); | 
 |  | 
 |   ps->pex = pex_init (PEX_USE_PIPES, "target remote pipe", NULL); | 
 |   if (! ps->pex) | 
 |     return -1; | 
 |   ps->input = pex_input_pipe (ps->pex, 1); | 
 |   if (! ps->input) | 
 |     return -1; | 
 |  | 
 |   { | 
 |     int err; | 
 |     const char *err_msg | 
 |       = pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT | 
 | 		 | PEX_STDERR_TO_PIPE, | 
 | 		 argv[0], argv.get (), NULL, NULL, | 
 | 		 &err); | 
 |  | 
 |     if (err_msg) | 
 |       { | 
 | 	/* Our caller expects us to return -1, but all they'll do with | 
 | 	   it generally is print the message based on errno.  We have | 
 | 	   all the same information here, plus err_msg provided by | 
 | 	   pex_run, so we just raise the error here.  */ | 
 | 	if (err) | 
 | 	  error (_("error starting child process '%s': %s: %s"), | 
 | 		 name, err_msg, safe_strerror (err)); | 
 | 	else | 
 | 	  error (_("error starting child process '%s': %s"), | 
 | 		 name, err_msg); | 
 |       } | 
 |   } | 
 |  | 
 |   ps->output = pex_read_output (ps->pex, 1); | 
 |   if (! ps->output) | 
 |     return -1; | 
 |   scb->fd = fileno (ps->output); | 
 |  | 
 |   pex_stderr = pex_read_err (ps->pex, 1); | 
 |   if (! pex_stderr) | 
 |     return -1; | 
 |   scb->error_fd = fileno (pex_stderr); | 
 |  | 
 |   scb->state = ps.release (); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | pipe_windows_fdopen (struct serial *scb, int fd) | 
 | { | 
 |   struct pipe_state *ps; | 
 |  | 
 |   ps = make_pipe_state (); | 
 |  | 
 |   ps->input = fdopen (fd, "r+"); | 
 |   if (! ps->input) | 
 |     goto fail; | 
 |  | 
 |   ps->output = fdopen (fd, "r+"); | 
 |   if (! ps->output) | 
 |     goto fail; | 
 |  | 
 |   scb->fd = fd; | 
 |   scb->state = (void *) ps; | 
 |  | 
 |   return 0; | 
 |  | 
 |  fail: | 
 |   free_pipe_state (ps); | 
 |   return -1; | 
 | } | 
 |  | 
 | static void | 
 | pipe_windows_close (struct serial *scb) | 
 | { | 
 |   struct pipe_state *ps = (struct pipe_state *) scb->state; | 
 |  | 
 |   /* In theory, we should try to kill the subprocess here, but the pex | 
 |      interface doesn't give us enough information to do that.  Usually | 
 |      closing the input pipe will get the message across.  */ | 
 |  | 
 |   free_pipe_state (ps); | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | pipe_windows_read (struct serial *scb, size_t count) | 
 | { | 
 |   HANDLE pipeline_out = (HANDLE) _get_osfhandle (scb->fd); | 
 |   DWORD available; | 
 |   DWORD bytes_read; | 
 |  | 
 |   if (pipeline_out == INVALID_HANDLE_VALUE) | 
 |     return -1; | 
 |  | 
 |   if (! PeekNamedPipe (pipeline_out, NULL, 0, NULL, &available, NULL)) | 
 |     return -1; | 
 |  | 
 |   if (count > available) | 
 |     count = available; | 
 |  | 
 |   if (! ReadFile (pipeline_out, scb->buf, count, &bytes_read, NULL)) | 
 |     return -1; | 
 |  | 
 |   return bytes_read; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | pipe_windows_write (struct serial *scb, const void *buf, size_t count) | 
 | { | 
 |   struct pipe_state *ps = (struct pipe_state *) scb->state; | 
 |   HANDLE pipeline_in; | 
 |   DWORD written; | 
 |  | 
 |   int pipeline_in_fd = fileno (ps->input); | 
 |   if (pipeline_in_fd < 0) | 
 |     return -1; | 
 |  | 
 |   pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd); | 
 |   if (pipeline_in == INVALID_HANDLE_VALUE) | 
 |     return -1; | 
 |  | 
 |   if (! WriteFile (pipeline_in, buf, count, &written, NULL)) | 
 |     return -1; | 
 |  | 
 |   return written; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) | 
 | { | 
 |   struct pipe_state *ps = (struct pipe_state *) scb->state; | 
 |  | 
 |   /* Have we allocated our events yet?  */ | 
 |   if (ps->wait.read_event == INVALID_HANDLE_VALUE) | 
 |     /* Start the thread.  */ | 
 |     create_select_thread (pipe_select_thread, scb, &ps->wait); | 
 |  | 
 |   *read = ps->wait.read_event; | 
 |   *except = ps->wait.except_event; | 
 |  | 
 |   /* Start from a blank state.  */ | 
 |   ResetEvent (ps->wait.read_event); | 
 |   ResetEvent (ps->wait.except_event); | 
 |   ResetEvent (ps->wait.stop_select); | 
 |  | 
 |   start_select_thread (&ps->wait); | 
 | } | 
 |  | 
 | static void | 
 | pipe_done_wait_handle (struct serial *scb) | 
 | { | 
 |   struct pipe_state *ps = (struct pipe_state *) scb->state; | 
 |  | 
 |   /* Have we allocated our events yet?  */ | 
 |   if (ps->wait.read_event == INVALID_HANDLE_VALUE) | 
 |     return; | 
 |  | 
 |   stop_select_thread (&ps->wait); | 
 | } | 
 |  | 
 | static int | 
 | pipe_avail (struct serial *scb, int fd) | 
 | { | 
 |   HANDLE h = (HANDLE) _get_osfhandle (fd); | 
 |   DWORD numBytes; | 
 |   BOOL r = PeekNamedPipe (h, NULL, 0, NULL, &numBytes, NULL); | 
 |  | 
 |   if (r == FALSE) | 
 |     numBytes = 0; | 
 |   return numBytes; | 
 | } | 
 |  | 
 | int | 
 | gdb_pipe (int pdes[2]) | 
 | { | 
 |   if (_pipe (pdes, 512, _O_BINARY | _O_NOINHERIT) == -1) | 
 |     return -1; | 
 |   return 0; | 
 | } | 
 |  | 
 | struct net_windows_state | 
 | { | 
 |   struct ser_console_state base; | 
 |    | 
 |   HANDLE sock_event; | 
 | }; | 
 |  | 
 | /* Check whether the socket has any pending data to be read.  If so, | 
 |    set the select thread's read event.  On error, set the select | 
 |    thread's except event.  If any event was set, return true, | 
 |    otherwise return false.  */ | 
 |  | 
 | static int | 
 | net_windows_socket_check_pending (struct serial *scb) | 
 | { | 
 |   struct net_windows_state *state = (struct net_windows_state *) scb->state; | 
 |   unsigned long available; | 
 |  | 
 |   if (ioctlsocket (scb->fd, FIONREAD, &available) != 0) | 
 |     { | 
 |       /* The socket closed, or some other error.  */ | 
 |       SetEvent (state->base.except_event); | 
 |       return 1; | 
 |     } | 
 |   else if (available > 0) | 
 |     { | 
 |       SetEvent (state->base.read_event); | 
 |       return 1; | 
 |     } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static DWORD WINAPI | 
 | net_windows_select_thread (void *arg) | 
 | { | 
 |   struct serial *scb = (struct serial *) arg; | 
 |   struct net_windows_state *state; | 
 |   int event_index; | 
 |  | 
 |   state = (struct net_windows_state *) scb->state; | 
 |  | 
 |   while (1) | 
 |     { | 
 |       HANDLE wait_events[2]; | 
 |       WSANETWORKEVENTS events; | 
 |  | 
 |       select_thread_wait (&state->base); | 
 |  | 
 |       wait_events[0] = state->base.stop_select; | 
 |       wait_events[1] = state->sock_event; | 
 |  | 
 |       /* Wait for something to happen on the socket.  */ | 
 |       while (1) | 
 | 	{ | 
 | 	  event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); | 
 |  | 
 | 	  if (event_index == WAIT_OBJECT_0 | 
 | 	      || WaitForSingleObject (state->base.stop_select, 0) == WAIT_OBJECT_0) | 
 | 	    { | 
 | 	      /* We have been requested to stop.  */ | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  if (event_index != WAIT_OBJECT_0 + 1) | 
 | 	    { | 
 | 	      /* Some error has occured.  Assume that this is an error | 
 | 		 condition.  */ | 
 | 	      SetEvent (state->base.except_event); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  /* Enumerate the internal network events, and reset the | 
 | 	     object that signalled us to catch the next event.  */ | 
 | 	  if (WSAEnumNetworkEvents (scb->fd, state->sock_event, &events) != 0) | 
 | 	    { | 
 | 	      /* Something went wrong.  Maybe the socket is gone.  */ | 
 | 	      SetEvent (state->base.except_event); | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  if (events.lNetworkEvents & FD_READ) | 
 | 	    { | 
 | 	      if (net_windows_socket_check_pending (scb)) | 
 | 		break; | 
 |  | 
 | 	      /* Spurious wakeup.  That is, the socket's event was | 
 | 		 signalled before we last called recv.  */ | 
 | 	    } | 
 |  | 
 | 	  if (events.lNetworkEvents & FD_CLOSE) | 
 | 	    { | 
 | 	      SetEvent (state->base.except_event); | 
 | 	      break; | 
 | 	    } | 
 | 	} | 
 |  | 
 |       SetEvent (state->base.have_stopped); | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 | static void | 
 | net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) | 
 | { | 
 |   struct net_windows_state *state = (struct net_windows_state *) scb->state; | 
 |  | 
 |   /* Start from a clean slate.  */ | 
 |   ResetEvent (state->base.read_event); | 
 |   ResetEvent (state->base.except_event); | 
 |   ResetEvent (state->base.stop_select); | 
 |  | 
 |   *read = state->base.read_event; | 
 |   *except = state->base.except_event; | 
 |  | 
 |   /* Check any pending events.  Otherwise, start the select | 
 |      thread.  */ | 
 |   if (!net_windows_socket_check_pending (scb)) | 
 |     start_select_thread (&state->base); | 
 | } | 
 |  | 
 | static void | 
 | net_windows_done_wait_handle (struct serial *scb) | 
 | { | 
 |   struct net_windows_state *state = (struct net_windows_state *) scb->state; | 
 |  | 
 |   stop_select_thread (&state->base); | 
 | } | 
 |  | 
 | static int | 
 | net_windows_open (struct serial *scb, const char *name) | 
 | { | 
 |   struct net_windows_state *state; | 
 |   int ret; | 
 |  | 
 |   ret = net_open (scb, name); | 
 |   if (ret != 0) | 
 |     return ret; | 
 |  | 
 |   state = XCNEW (struct net_windows_state); | 
 |   scb->state = state; | 
 |  | 
 |   /* Associate an event with the socket.  */ | 
 |   state->sock_event = CreateEvent (0, TRUE, FALSE, 0); | 
 |   WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE); | 
 |  | 
 |   /* Start the thread.  */ | 
 |   create_select_thread (net_windows_select_thread, scb, &state->base); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | net_windows_close (struct serial *scb) | 
 | { | 
 |   struct net_windows_state *state = (struct net_windows_state *) scb->state; | 
 |  | 
 |   destroy_select_thread (&state->base); | 
 |   CloseHandle (state->sock_event); | 
 |  | 
 |   xfree (scb->state); | 
 |  | 
 |   net_close (scb); | 
 | } | 
 |  | 
 | /* The serial port driver.  */ | 
 |  | 
 | static const struct serial_ops hardwire_ops = | 
 | { | 
 |   "hardwire", | 
 |   ser_windows_open, | 
 |   ser_windows_close, | 
 |   NULL, | 
 |   ser_base_readchar, | 
 |   ser_base_write, | 
 |   ser_windows_flush_output, | 
 |   ser_windows_flush_input, | 
 |   ser_windows_send_break, | 
 |   ser_windows_raw, | 
 |   /* These are only used for stdin; we do not need them for serial | 
 |      ports, so supply the standard dummies.  */ | 
 |   ser_base_get_tty_state, | 
 |   ser_base_copy_tty_state, | 
 |   ser_base_set_tty_state, | 
 |   ser_base_print_tty_state, | 
 |   ser_windows_setbaudrate, | 
 |   ser_windows_setstopbits, | 
 |   ser_windows_setparity, | 
 |   ser_windows_drain_output, | 
 |   ser_base_async, | 
 |   ser_windows_read_prim, | 
 |   ser_windows_write_prim, | 
 |   NULL, | 
 |   ser_windows_wait_handle | 
 | }; | 
 |  | 
 | /* The dummy serial driver used for terminals.  We only provide the | 
 |    TTY-related methods.  */ | 
 |  | 
 | static const struct serial_ops tty_ops = | 
 | { | 
 |   "terminal", | 
 |   NULL, | 
 |   ser_console_close, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   ser_console_get_tty_state, | 
 |   ser_base_copy_tty_state, | 
 |   ser_base_set_tty_state, | 
 |   ser_base_print_tty_state, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   ser_base_drain_output, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   NULL, | 
 |   ser_console_wait_handle, | 
 |   ser_console_done_wait_handle | 
 | }; | 
 |  | 
 | /* The pipe interface.  */ | 
 |  | 
 | static const struct serial_ops pipe_ops = | 
 | { | 
 |   "pipe", | 
 |   pipe_windows_open, | 
 |   pipe_windows_close, | 
 |   pipe_windows_fdopen, | 
 |   ser_base_readchar, | 
 |   ser_base_write, | 
 |   ser_base_flush_output, | 
 |   ser_base_flush_input, | 
 |   ser_base_send_break, | 
 |   ser_base_raw, | 
 |   ser_base_get_tty_state, | 
 |   ser_base_copy_tty_state, | 
 |   ser_base_set_tty_state, | 
 |   ser_base_print_tty_state, | 
 |   ser_base_setbaudrate, | 
 |   ser_base_setstopbits, | 
 |   ser_base_setparity, | 
 |   ser_base_drain_output, | 
 |   ser_base_async, | 
 |   pipe_windows_read, | 
 |   pipe_windows_write, | 
 |   pipe_avail, | 
 |   pipe_wait_handle, | 
 |   pipe_done_wait_handle | 
 | }; | 
 |  | 
 | /* The TCP/UDP socket driver.  */ | 
 |  | 
 | static const struct serial_ops tcp_ops = | 
 | { | 
 |   "tcp", | 
 |   net_windows_open, | 
 |   net_windows_close, | 
 |   NULL, | 
 |   ser_base_readchar, | 
 |   ser_base_write, | 
 |   ser_base_flush_output, | 
 |   ser_base_flush_input, | 
 |   ser_tcp_send_break, | 
 |   ser_base_raw, | 
 |   ser_base_get_tty_state, | 
 |   ser_base_copy_tty_state, | 
 |   ser_base_set_tty_state, | 
 |   ser_base_print_tty_state, | 
 |   ser_base_setbaudrate, | 
 |   ser_base_setstopbits, | 
 |   ser_base_setparity, | 
 |   ser_base_drain_output, | 
 |   ser_base_async, | 
 |   net_read_prim, | 
 |   net_write_prim, | 
 |   NULL, | 
 |   net_windows_wait_handle, | 
 |   net_windows_done_wait_handle | 
 | }; | 
 |  | 
 | void _initialize_ser_windows (); | 
 | void | 
 | _initialize_ser_windows () | 
 | { | 
 |   WSADATA wsa_data; | 
 |  | 
 |   HMODULE hm = NULL; | 
 |  | 
 |   /* First find out if kernel32 exports CancelIo function.  */ | 
 |   hm = LoadLibrary ("kernel32.dll"); | 
 |   if (hm) | 
 |     { | 
 |       CancelIo = (CancelIo_ftype *) GetProcAddress (hm, "CancelIo"); | 
 |       FreeLibrary (hm); | 
 |     } | 
 |   else | 
 |     CancelIo = NULL; | 
 |  | 
 |   serial_add_interface (&hardwire_ops); | 
 |   serial_add_interface (&tty_ops); | 
 |   serial_add_interface (&pipe_ops); | 
 |  | 
 |   /* If WinSock works, register the TCP/UDP socket driver.  */ | 
 |  | 
 |   if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) | 
 |     /* WinSock is unavailable.  */ | 
 |     return; | 
 |  | 
 |   serial_add_interface (&tcp_ops); | 
 | } |