|  | /* Generic serial interface functions. | 
|  |  | 
|  | Copyright (C) 1992-2024 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 "event-top.h" | 
|  | #include "serial.h" | 
|  | #include "ser-base.h" | 
|  | #include "gdbsupport/event-loop.h" | 
|  |  | 
|  | #include "gdbsupport/gdb_select.h" | 
|  | #include "gdbsupport/gdb_sys_time.h" | 
|  | #ifdef USE_WIN32API | 
|  | #include <winsock2.h> | 
|  | #endif | 
|  |  | 
|  |  | 
|  | static timer_handler_func push_event; | 
|  | static handler_func fd_event; | 
|  |  | 
|  | /* Event handling for ASYNC serial code. | 
|  |  | 
|  | At any time the SERIAL device either: has an empty FIFO and is | 
|  | waiting on a FD event; or has a non-empty FIFO/error condition and | 
|  | is constantly scheduling timer events. | 
|  |  | 
|  | ASYNC only stops pestering its client when it is de-async'ed or it | 
|  | is told to go away.  */ | 
|  |  | 
|  | /* Value of scb->async_state: */ | 
|  | enum { | 
|  | /* When >= 0, this contains the ID of the currently scheduled timer event. | 
|  | This state is rarely encountered.  Timer events are one-off so as soon as | 
|  | the event is delivered the state is changed to NOTHING_SCHEDULED.  */ | 
|  |  | 
|  | /* The fd_event() handler is scheduled.  It is called when ever the | 
|  | file descriptor becomes ready.  */ | 
|  | FD_SCHEDULED = -1, | 
|  |  | 
|  | /* Either no task is scheduled (just going into ASYNC mode) or a | 
|  | timer event has just gone off and the current state has been | 
|  | forced into nothing scheduled.  */ | 
|  | NOTHING_SCHEDULED = -2 | 
|  | }; | 
|  |  | 
|  | /* Identify and schedule the next ASYNC task based on scb->async_state | 
|  | and scb->buf* (the input FIFO).  A state machine is used to avoid | 
|  | the need to make redundant calls into the event-loop - the next | 
|  | scheduled task is only changed when needed.  */ | 
|  |  | 
|  | static void | 
|  | reschedule (struct serial *scb) | 
|  | { | 
|  | if (serial_is_async_p (scb)) | 
|  | { | 
|  | int next_state; | 
|  |  | 
|  | switch (scb->async_state) | 
|  | { | 
|  | case FD_SCHEDULED: | 
|  | if (scb->bufcnt == 0) | 
|  | next_state = FD_SCHEDULED; | 
|  | else | 
|  | { | 
|  | delete_file_handler (scb->fd); | 
|  | next_state = create_timer (0, push_event, scb); | 
|  | } | 
|  | break; | 
|  | case NOTHING_SCHEDULED: | 
|  | if (scb->bufcnt == 0) | 
|  | { | 
|  | add_file_handler (scb->fd, fd_event, scb, "serial"); | 
|  | next_state = FD_SCHEDULED; | 
|  | } | 
|  | else | 
|  | { | 
|  | next_state = create_timer (0, push_event, scb); | 
|  | } | 
|  | break; | 
|  | default: /* TIMER SCHEDULED */ | 
|  | if (scb->bufcnt == 0) | 
|  | { | 
|  | delete_timer (scb->async_state); | 
|  | add_file_handler (scb->fd, fd_event, scb, "serial"); | 
|  | next_state = FD_SCHEDULED; | 
|  | } | 
|  | else | 
|  | next_state = scb->async_state; | 
|  | break; | 
|  | } | 
|  | if (serial_debug_p (scb)) | 
|  | { | 
|  | switch (next_state) | 
|  | { | 
|  | case FD_SCHEDULED: | 
|  | if (scb->async_state != FD_SCHEDULED) | 
|  | gdb_printf (gdb_stdlog, "[fd%d->fd-scheduled]\n", | 
|  | scb->fd); | 
|  | break; | 
|  | default: /* TIMER SCHEDULED */ | 
|  | if (scb->async_state == FD_SCHEDULED) | 
|  | gdb_printf (gdb_stdlog, "[fd%d->timer-scheduled]\n", | 
|  | scb->fd); | 
|  | break; | 
|  | } | 
|  | } | 
|  | scb->async_state = next_state; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Run the SCB's async handle, and reschedule, if the handler doesn't | 
|  | close SCB.  */ | 
|  |  | 
|  | static void | 
|  | run_async_handler_and_reschedule (struct serial *scb) | 
|  | { | 
|  | int is_open; | 
|  |  | 
|  | /* Take a reference, so a serial_close call within the handler | 
|  | doesn't make SCB a dangling pointer.  */ | 
|  | serial_ref (scb); | 
|  |  | 
|  | /* Run the handler.  */ | 
|  | scb->async_handler (scb, scb->async_context); | 
|  |  | 
|  | is_open = serial_is_open (scb); | 
|  | serial_unref (scb); | 
|  |  | 
|  | /* Get ready for more, if not already closed.  */ | 
|  | if (is_open) | 
|  | reschedule (scb); | 
|  | } | 
|  |  | 
|  | /* FD_EVENT: This is scheduled when the input FIFO is empty (and there | 
|  | is no pending error).  As soon as data arrives, it is read into the | 
|  | input FIFO and the client notified.  The client should then drain | 
|  | the FIFO using readchar().  If the FIFO isn't immediately emptied, | 
|  | push_event() is used to nag the client until it is.  */ | 
|  |  | 
|  | static void | 
|  | fd_event (int error, void *context) | 
|  | { | 
|  | struct serial *scb = (struct serial *) context; | 
|  | if (error != 0) | 
|  | { | 
|  | scb->bufcnt = SERIAL_ERROR; | 
|  | } | 
|  | else if (scb->bufcnt == 0) | 
|  | { | 
|  | /* Prime the input FIFO.  The readchar() function is used to | 
|  | pull characters out of the buffer.  See also | 
|  | generic_readchar().  */ | 
|  | int nr; | 
|  |  | 
|  | do | 
|  | { | 
|  | nr = scb->ops->read_prim (scb, BUFSIZ); | 
|  | } | 
|  | while (nr < 0 && errno == EINTR); | 
|  |  | 
|  | if (nr == 0) | 
|  | { | 
|  | scb->bufcnt = SERIAL_EOF; | 
|  | } | 
|  | else if (nr > 0) | 
|  | { | 
|  | scb->bufcnt = nr; | 
|  | scb->bufp = scb->buf; | 
|  | } | 
|  | else | 
|  | { | 
|  | scb->bufcnt = SERIAL_ERROR; | 
|  | } | 
|  | } | 
|  | run_async_handler_and_reschedule (scb); | 
|  | } | 
|  |  | 
|  | /* PUSH_EVENT: The input FIFO is non-empty (or there is a pending | 
|  | error).  Nag the client until all the data has been read.  In the | 
|  | case of errors, the client will need to close or de-async the | 
|  | device before nagging stops.  */ | 
|  |  | 
|  | static void | 
|  | push_event (void *context) | 
|  | { | 
|  | struct serial *scb = (struct serial *) context; | 
|  |  | 
|  | scb->async_state = NOTHING_SCHEDULED; /* Timers are one-off */ | 
|  | run_async_handler_and_reschedule (scb); | 
|  | } | 
|  |  | 
|  | /* Wait for input on scb, with timeout seconds.  Returns 0 on success, | 
|  | otherwise SERIAL_TIMEOUT or SERIAL_ERROR.  */ | 
|  |  | 
|  | /* NOTE: Some of the code below is dead.  The only possible values of | 
|  | the TIMEOUT parameter are ONE and ZERO.  OTOH, we should probably | 
|  | get rid of the deprecated_ui_loop_hook call in do_ser_base_readchar | 
|  | instead and support infinite time outs here.  */ | 
|  |  | 
|  | static int | 
|  | ser_base_wait_for (struct serial *scb, int timeout) | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | int numfds; | 
|  | struct timeval tv; | 
|  | fd_set readfds, exceptfds; | 
|  | int nfds; | 
|  |  | 
|  | /* NOTE: Some OS's can scramble the READFDS when the select() | 
|  | call fails (ex the kernel with Red Hat 5.2).  Initialize all | 
|  | arguments before each call.  */ | 
|  |  | 
|  | tv.tv_sec = timeout; | 
|  | tv.tv_usec = 0; | 
|  |  | 
|  | FD_ZERO (&readfds); | 
|  | FD_ZERO (&exceptfds); | 
|  | FD_SET (scb->fd, &readfds); | 
|  | FD_SET (scb->fd, &exceptfds); | 
|  |  | 
|  | QUIT; | 
|  |  | 
|  | nfds = scb->fd + 1; | 
|  | if (timeout >= 0) | 
|  | numfds = interruptible_select (nfds, &readfds, 0, &exceptfds, &tv); | 
|  | else | 
|  | numfds = interruptible_select (nfds, &readfds, 0, &exceptfds, 0); | 
|  |  | 
|  | if (numfds <= 0) | 
|  | { | 
|  | if (numfds == 0) | 
|  | return SERIAL_TIMEOUT; | 
|  | else if (errno == EINTR) | 
|  | continue; | 
|  | else | 
|  | return SERIAL_ERROR;	/* Got an error from select or | 
|  | poll.  */ | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Read any error output we might have.  */ | 
|  |  | 
|  | static void | 
|  | ser_base_read_error_fd (struct serial *scb, int close_fd) | 
|  | { | 
|  | if (scb->error_fd != -1) | 
|  | { | 
|  | ssize_t s; | 
|  | char buf[GDB_MI_MSG_WIDTH + 1]; | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | char *current; | 
|  | char *newline; | 
|  | int to_read = GDB_MI_MSG_WIDTH; | 
|  | int num_bytes = -1; | 
|  |  | 
|  | if (scb->ops->avail) | 
|  | num_bytes = (scb->ops->avail)(scb, scb->error_fd); | 
|  |  | 
|  | if (num_bytes != -1) | 
|  | to_read = (num_bytes < to_read) ? num_bytes : to_read; | 
|  |  | 
|  | if (to_read == 0) | 
|  | break; | 
|  |  | 
|  | s = read (scb->error_fd, &buf, to_read); | 
|  | if ((s == -1) || (s == 0 && !close_fd)) | 
|  | break; | 
|  |  | 
|  | if (s == 0 && close_fd) | 
|  | { | 
|  | /* End of file.  */ | 
|  | if (serial_is_async_p (scb)) | 
|  | delete_file_handler (scb->error_fd); | 
|  | close (scb->error_fd); | 
|  | scb->error_fd = -1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* In theory, embedded newlines are not a problem. | 
|  | But for MI, we want each output line to have just | 
|  | one newline for legibility.  So output things | 
|  | in newline chunks.  */ | 
|  | gdb_assert (s > 0 && s <= GDB_MI_MSG_WIDTH); | 
|  | buf[s] = '\0'; | 
|  | current = buf; | 
|  | while ((newline = strstr (current, "\n")) != NULL) | 
|  | { | 
|  | *newline = '\0'; | 
|  | gdb_puts (current, gdb_stderr); | 
|  | gdb_puts ("\n", gdb_stderr); | 
|  | current = newline + 1; | 
|  | } | 
|  |  | 
|  | gdb_puts (current, gdb_stderr); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Event-loop callback for a serial's error_fd.  Flushes any error | 
|  | output we might have.  */ | 
|  |  | 
|  | static void | 
|  | handle_error_fd (int error, gdb_client_data client_data) | 
|  | { | 
|  | serial *scb = (serial *) client_data; | 
|  |  | 
|  | ser_base_read_error_fd (scb, 0); | 
|  | } | 
|  |  | 
|  | /* Read a character with user-specified timeout.  TIMEOUT is number of | 
|  | seconds to wait, or -1 to wait forever.  Use timeout of 0 to effect | 
|  | a poll.  Returns char if successful.  Returns SERIAL_TIMEOUT if | 
|  | timeout expired, SERIAL_EOF if line dropped dead, or SERIAL_ERROR | 
|  | for any other error (see errno in that case).  */ | 
|  |  | 
|  | static int | 
|  | do_ser_base_readchar (struct serial *scb, int timeout) | 
|  | { | 
|  | int status; | 
|  | int delta; | 
|  |  | 
|  | /* We have to be able to keep the GUI alive here, so we break the | 
|  | original timeout into steps of 1 second, running the "keep the | 
|  | GUI alive" hook each time through the loop. | 
|  |  | 
|  | Also, timeout = 0 means to poll, so we just set the delta to 0, | 
|  | so we will only go through the loop once.  */ | 
|  |  | 
|  | delta = (timeout == 0 ? 0 : 1); | 
|  | while (1) | 
|  | { | 
|  | /* N.B. The UI may destroy our world (for instance by calling | 
|  | remote_stop,) in which case we want to get out of here as | 
|  | quickly as possible.  It is not safe to touch scb, since | 
|  | someone else might have freed it.  The | 
|  | deprecated_ui_loop_hook signals that we should exit by | 
|  | returning 1.  */ | 
|  |  | 
|  | if (deprecated_ui_loop_hook) | 
|  | { | 
|  | if (deprecated_ui_loop_hook (0)) | 
|  | return SERIAL_TIMEOUT; | 
|  | } | 
|  |  | 
|  | status = ser_base_wait_for (scb, delta); | 
|  | if (timeout > 0) | 
|  | timeout -= delta; | 
|  |  | 
|  | /* If we got a character or an error back from wait_for, then we can | 
|  | break from the loop before the timeout is completed.  */ | 
|  | if (status != SERIAL_TIMEOUT) | 
|  | break; | 
|  |  | 
|  | /* If we have exhausted the original timeout, then generate | 
|  | a SERIAL_TIMEOUT, and pass it out of the loop.  */ | 
|  | else if (timeout == 0) | 
|  | { | 
|  | status = SERIAL_TIMEOUT; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* We also need to check and consume the stderr because it could | 
|  | come before the stdout for some stubs.  If we just sit and wait | 
|  | for stdout, we would hit a deadlock for that case.  */ | 
|  | ser_base_read_error_fd (scb, 0); | 
|  | } | 
|  |  | 
|  | if (status < 0) | 
|  | return status; | 
|  |  | 
|  | do | 
|  | { | 
|  | status = scb->ops->read_prim (scb, BUFSIZ); | 
|  | } | 
|  | while (status < 0 && errno == EINTR); | 
|  |  | 
|  | if (status <= 0) | 
|  | { | 
|  | if (status == 0) | 
|  | return SERIAL_EOF; | 
|  | else | 
|  | /* Got an error from read.  */ | 
|  | return SERIAL_ERROR; | 
|  | } | 
|  |  | 
|  | scb->bufcnt = status; | 
|  | scb->bufcnt--; | 
|  | scb->bufp = scb->buf; | 
|  | return *scb->bufp++; | 
|  | } | 
|  |  | 
|  | /* Perform operations common to both old and new readchar.  */ | 
|  |  | 
|  | /* Return the next character from the input FIFO.  If the FIFO is | 
|  | empty, call the SERIAL specific routine to try and read in more | 
|  | characters. | 
|  |  | 
|  | Initially data from the input FIFO is returned (fd_event() | 
|  | pre-reads the input into that FIFO.  Once that has been emptied, | 
|  | further data is obtained by polling the input FD using the device | 
|  | specific readchar() function.  Note: reschedule() is called after | 
|  | every read.  This is because there is no guarantee that the lower | 
|  | level fd_event() poll_event() code (which also calls reschedule()) | 
|  | will be called.  */ | 
|  |  | 
|  | int | 
|  | generic_readchar (struct serial *scb, int timeout, | 
|  | int (do_readchar) (struct serial *scb, int timeout)) | 
|  | { | 
|  | int ch; | 
|  | if (scb->bufcnt > 0) | 
|  | { | 
|  | ch = *scb->bufp; | 
|  | scb->bufcnt--; | 
|  | scb->bufp++; | 
|  | } | 
|  | else if (scb->bufcnt < 0) | 
|  | { | 
|  | /* Some errors/eof are are sticky.  */ | 
|  | ch = scb->bufcnt; | 
|  | } | 
|  | else | 
|  | { | 
|  | ch = do_readchar (scb, timeout); | 
|  | if (ch < 0) | 
|  | { | 
|  | switch ((enum serial_rc) ch) | 
|  | { | 
|  | case SERIAL_EOF: | 
|  | case SERIAL_ERROR: | 
|  | /* Make the error/eof stick.  */ | 
|  | scb->bufcnt = ch; | 
|  | break; | 
|  | case SERIAL_TIMEOUT: | 
|  | scb->bufcnt = 0; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Read any error output we might have.  */ | 
|  | ser_base_read_error_fd (scb, 1); | 
|  |  | 
|  | reschedule (scb); | 
|  | return ch; | 
|  | } | 
|  |  | 
|  | int | 
|  | ser_base_readchar (struct serial *scb, int timeout) | 
|  | { | 
|  | return generic_readchar (scb, timeout, do_ser_base_readchar); | 
|  | } | 
|  |  | 
|  | void | 
|  | ser_base_write (struct serial *scb, const void *buf, size_t count) | 
|  | { | 
|  | const char *str = (const char *) buf; | 
|  | int cc; | 
|  |  | 
|  | while (count > 0) | 
|  | { | 
|  | QUIT; | 
|  |  | 
|  | cc = scb->ops->write_prim (scb, str, count); | 
|  |  | 
|  | if (cc < 0) | 
|  | { | 
|  | if (errno == EINTR) | 
|  | continue; | 
|  | perror_with_name ("error while writing"); | 
|  | } | 
|  | count -= cc; | 
|  | str += cc; | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | ser_base_flush_output (struct serial *scb) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | ser_base_flush_input (struct serial *scb) | 
|  | { | 
|  | if (scb->bufcnt >= 0) | 
|  | { | 
|  | scb->bufcnt = 0; | 
|  | scb->bufp = scb->buf; | 
|  | return 0; | 
|  | } | 
|  | else | 
|  | return SERIAL_ERROR; | 
|  | } | 
|  |  | 
|  | void | 
|  | ser_base_send_break (struct serial *scb) | 
|  | { | 
|  | } | 
|  |  | 
|  | int | 
|  | ser_base_drain_output (struct serial *scb) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | ser_base_raw (struct serial *scb) | 
|  | { | 
|  | return;			/* Always in raw mode.  */ | 
|  | } | 
|  |  | 
|  | serial_ttystate | 
|  | ser_base_get_tty_state (struct serial *scb) | 
|  | { | 
|  | /* Allocate a dummy.  */ | 
|  | return (serial_ttystate) XNEW (int); | 
|  | } | 
|  |  | 
|  | serial_ttystate | 
|  | ser_base_copy_tty_state (struct serial *scb, serial_ttystate ttystate) | 
|  | { | 
|  | /* Allocate another dummy.  */ | 
|  | return (serial_ttystate) XNEW (int); | 
|  | } | 
|  |  | 
|  | int | 
|  | ser_base_set_tty_state (struct serial *scb, serial_ttystate ttystate) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | ser_base_print_tty_state (struct serial *scb, | 
|  | serial_ttystate ttystate, | 
|  | struct ui_file *stream) | 
|  | { | 
|  | /* Nothing to print.  */ | 
|  | return; | 
|  | } | 
|  |  | 
|  | void | 
|  | ser_base_setbaudrate (struct serial *scb, int rate) | 
|  | { | 
|  | /* Never fails!  */ | 
|  | } | 
|  |  | 
|  | int | 
|  | ser_base_setstopbits (struct serial *scb, int num) | 
|  | { | 
|  | return 0;			/* Never fails!  */ | 
|  | } | 
|  |  | 
|  | /* Implement the "setparity" serial_ops callback.  */ | 
|  |  | 
|  | int | 
|  | ser_base_setparity (struct serial *scb, int parity) | 
|  | { | 
|  | return 0;			/* Never fails!  */ | 
|  | } | 
|  |  | 
|  | /* Put the SERIAL device into/out-of ASYNC mode.  */ | 
|  |  | 
|  | void | 
|  | ser_base_async (struct serial *scb, | 
|  | int async_p) | 
|  | { | 
|  | if (async_p) | 
|  | { | 
|  | /* Force a re-schedule.  */ | 
|  | scb->async_state = NOTHING_SCHEDULED; | 
|  | if (serial_debug_p (scb)) | 
|  | gdb_printf (gdb_stdlog, "[fd%d->asynchronous]\n", | 
|  | scb->fd); | 
|  | reschedule (scb); | 
|  |  | 
|  | if (scb->error_fd != -1) | 
|  | add_file_handler (scb->error_fd, handle_error_fd, scb, "serial-error"); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (serial_debug_p (scb)) | 
|  | gdb_printf (gdb_stdlog, "[fd%d->synchronous]\n", | 
|  | scb->fd); | 
|  | /* De-schedule whatever tasks are currently scheduled.  */ | 
|  | switch (scb->async_state) | 
|  | { | 
|  | case FD_SCHEDULED: | 
|  | delete_file_handler (scb->fd); | 
|  | break; | 
|  | case NOTHING_SCHEDULED: | 
|  | break; | 
|  | default: /* TIMER SCHEDULED */ | 
|  | delete_timer (scb->async_state); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (scb->error_fd != -1) | 
|  | delete_file_handler (scb->error_fd); | 
|  | } | 
|  | } |