mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-28 04:25:10 +08:00
048094accc
remote.c is the last user of immediate_quit. It's relied on to immediately break the initial remote connection sync up, if the user does Ctrl-C, assuming that was because the target isn't responding. At that stage, since the connection isn't synced yet, disconnecting is the only safe thing to do. This commit reworks that, to not rely on throwing from the SIGINT signal handler. So, this commit: - Introduces the concept of a "quit handler". This is used to override what does the QUIT macro do when the quit flag is set. - Makes the "struct serial" reachar / write code call QUIT in the partial read/write loops, so the current quit handler is invoked whenever a serial->read_prim / serial->write_prim returns EINTR. - Makes the "struct serial" reachar / write code call interruptible_select instead of gdb_select, so that QUITs are detected in a race-free manner. - Stops remote.c from setting immediate_quit during the initial connection. - Instead, we install a custom quit handler whenever we're calling into the serial code. This custom quit handler knows to immediately throw a quit when we're in the initial connection setup, and otherwise defer handling the quit/Ctrl-C request to later, when we're safely out of a packet command/response sequence. This also is what is now responsible for handling "double Ctrl-C because target connection is stuck/wedged." - remote.c no longer installs a specialized SIGINT handlers, and instead re-uses the quit flag. Since we want to rely on the QUIT macro, the SIGINT handler must also set the quit. And the easiest is just to not install custom SIGINT handler in remote.c. Let the standard SIGINT handler do its job of setting the quit flag. Centralizing SIGINT handlers seems like a good thing to me, anyway. gdb/ChangeLog: 2016-04-12 Pedro Alves <palves@redhat.com> * defs.h (quit_handler_ftype, quit_handler) (make_cleanup_override_quit_handler, default_quit_handler): New. (QUIT): Adjust comments. * event-top.c (default_quit_handler): New function. (quit_handler): New global. (struct quit_handler_cleanup_data): New. (restore_quit_handler, restore_quit_handler_dtor) (make_cleanup_override_quit_handler): New. (async_request_quit): Call QUIT. * remote.c (struct remote_state) <got_ctrlc_during_io>: New field. (async_sigint_remote_twice_token, async_sigint_remote_token): Delete. (remote_close): Update comments. (remote_start_remote): Don't set immediate_quit. Set starting_up earlier. (remote_serial_quit_handler, remote_unpush_and_throw): New functions. (remote_open_1): Clear got_ctrlc_during_io. Set remote_async_terminal_ours_p unconditionally. (async_initialize_sigint_signal_handler) (async_handle_remote_sigint, async_handle_remote_sigint_twice) (remote_check_pending_interrupt, async_remote_interrupt) (async_remote_interrupt_twice) (async_cleanup_sigint_signal_handler, ofunc) (sync_remote_interrupt, sync_remote_interrupt_twice): Delete. (remote_terminal_inferior, remote_terminal_ours): Remove async checks. (remote_wait_as): Don't install a SIGINT handler in sync mode. (readchar, remote_serial_write): Override the quit handler with remote_serial_quit_handler. (getpkt_or_notif_sane_1): Don't call QUIT. (initialize_remote_ops): Don't install remote_check_pending_interrupt. (_initialize_remote): Don't create async_sigint_remote_token and async_sigint_remote_twice_token. * ser-base.c (ser_base_wait_for): Call QUIT and use interruptible_select. (ser_base_write): Call QUIT. * ser-go32.c (dos_readchar, dos_write): Call QUIT. * ser-unix.c (wait_for): Don't use VTIME. Always take the gdb_select path, but call QUIT and interruptible_select. * utils.c (maybe_quit): Call the current quit handler. Don't call target_check_pending_interrupt. (defaulted_query, prompt_for_continue): Override the quit handler with the default quit handler.
939 lines
21 KiB
C
939 lines
21 KiB
C
/* Serial interface for local (hardwired) serial ports on Un*x like systems
|
||
|
||
Copyright (C) 1992-2016 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-unix.h"
|
||
|
||
#include <fcntl.h>
|
||
#include <sys/types.h>
|
||
#include "terminal.h"
|
||
#include <sys/socket.h>
|
||
#include "gdb_sys_time.h"
|
||
|
||
#include "gdb_select.h"
|
||
#include "gdbcmd.h"
|
||
#include "filestuff.h"
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
|
||
struct hardwire_ttystate
|
||
{
|
||
struct termios termios;
|
||
};
|
||
|
||
#ifdef CRTSCTS
|
||
/* Boolean to explicitly enable or disable h/w flow control. */
|
||
static int serial_hwflow;
|
||
static void
|
||
show_serial_hwflow (struct ui_file *file, int from_tty,
|
||
struct cmd_list_element *c, const char *value)
|
||
{
|
||
fprintf_filtered (file, _("Hardware flow control is %s.\n"), value);
|
||
}
|
||
#endif
|
||
|
||
#endif /* termios */
|
||
|
||
#ifdef HAVE_TERMIO
|
||
|
||
/* It is believed that all systems which have added job control to SVR3
|
||
(e.g. sco) have also added termios. Even if not, trying to figure out
|
||
all the variations (TIOCGPGRP vs. TCGETPGRP, etc.) would be pretty
|
||
bewildering. So we don't attempt it. */
|
||
|
||
struct hardwire_ttystate
|
||
{
|
||
struct termio termio;
|
||
};
|
||
#endif /* termio */
|
||
|
||
#ifdef HAVE_SGTTY
|
||
struct hardwire_ttystate
|
||
{
|
||
struct sgttyb sgttyb;
|
||
struct tchars tc;
|
||
struct ltchars ltc;
|
||
/* Line discipline flags. */
|
||
int lmode;
|
||
};
|
||
#endif /* sgtty */
|
||
|
||
static int hardwire_open (struct serial *scb, const char *name);
|
||
static void hardwire_raw (struct serial *scb);
|
||
static int wait_for (struct serial *scb, int timeout);
|
||
static int hardwire_readchar (struct serial *scb, int timeout);
|
||
static int do_hardwire_readchar (struct serial *scb, int timeout);
|
||
static int rate_to_code (int rate);
|
||
static int hardwire_setbaudrate (struct serial *scb, int rate);
|
||
static int hardwire_setparity (struct serial *scb, int parity);
|
||
static void hardwire_close (struct serial *scb);
|
||
static int get_tty_state (struct serial *scb,
|
||
struct hardwire_ttystate * state);
|
||
static int set_tty_state (struct serial *scb,
|
||
struct hardwire_ttystate * state);
|
||
static serial_ttystate hardwire_get_tty_state (struct serial *scb);
|
||
static int hardwire_set_tty_state (struct serial *scb, serial_ttystate state);
|
||
static int hardwire_noflush_set_tty_state (struct serial *, serial_ttystate,
|
||
serial_ttystate);
|
||
static void hardwire_print_tty_state (struct serial *, serial_ttystate,
|
||
struct ui_file *);
|
||
static int hardwire_drain_output (struct serial *);
|
||
static int hardwire_flush_output (struct serial *);
|
||
static int hardwire_flush_input (struct serial *);
|
||
static int hardwire_send_break (struct serial *);
|
||
static int hardwire_setstopbits (struct serial *, int);
|
||
|
||
void _initialize_ser_hardwire (void);
|
||
|
||
/* Open up a real live device for serial I/O. */
|
||
|
||
static int
|
||
hardwire_open (struct serial *scb, const char *name)
|
||
{
|
||
scb->fd = gdb_open_cloexec (name, O_RDWR, 0);
|
||
if (scb->fd < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
get_tty_state (struct serial *scb, struct hardwire_ttystate *state)
|
||
{
|
||
#ifdef HAVE_TERMIOS
|
||
if (tcgetattr (scb->fd, &state->termios) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
if (ioctl (scb->fd, TCGETA, &state->termio) < 0)
|
||
return -1;
|
||
return 0;
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
if (ioctl (scb->fd, TIOCGETP, &state->sgttyb) < 0)
|
||
return -1;
|
||
if (ioctl (scb->fd, TIOCGETC, &state->tc) < 0)
|
||
return -1;
|
||
if (ioctl (scb->fd, TIOCGLTC, &state->ltc) < 0)
|
||
return -1;
|
||
if (ioctl (scb->fd, TIOCLGET, &state->lmode) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
set_tty_state (struct serial *scb, struct hardwire_ttystate *state)
|
||
{
|
||
#ifdef HAVE_TERMIOS
|
||
if (tcsetattr (scb->fd, TCSANOW, &state->termios) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
if (ioctl (scb->fd, TCSETA, &state->termio) < 0)
|
||
return -1;
|
||
return 0;
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
if (ioctl (scb->fd, TIOCSETN, &state->sgttyb) < 0)
|
||
return -1;
|
||
if (ioctl (scb->fd, TIOCSETC, &state->tc) < 0)
|
||
return -1;
|
||
if (ioctl (scb->fd, TIOCSLTC, &state->ltc) < 0)
|
||
return -1;
|
||
if (ioctl (scb->fd, TIOCLSET, &state->lmode) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static serial_ttystate
|
||
hardwire_get_tty_state (struct serial *scb)
|
||
{
|
||
struct hardwire_ttystate *state = XNEW (struct hardwire_ttystate);
|
||
|
||
if (get_tty_state (scb, state))
|
||
{
|
||
xfree (state);
|
||
return NULL;
|
||
}
|
||
|
||
return (serial_ttystate) state;
|
||
}
|
||
|
||
static serial_ttystate
|
||
hardwire_copy_tty_state (struct serial *scb, serial_ttystate ttystate)
|
||
{
|
||
struct hardwire_ttystate *state = XNEW (struct hardwire_ttystate);
|
||
|
||
*state = *(struct hardwire_ttystate *) ttystate;
|
||
|
||
return (serial_ttystate) state;
|
||
}
|
||
|
||
static int
|
||
hardwire_set_tty_state (struct serial *scb, serial_ttystate ttystate)
|
||
{
|
||
struct hardwire_ttystate *state;
|
||
|
||
state = (struct hardwire_ttystate *) ttystate;
|
||
|
||
return set_tty_state (scb, state);
|
||
}
|
||
|
||
static int
|
||
hardwire_noflush_set_tty_state (struct serial *scb,
|
||
serial_ttystate new_ttystate,
|
||
serial_ttystate old_ttystate)
|
||
{
|
||
struct hardwire_ttystate new_state;
|
||
#ifdef HAVE_SGTTY
|
||
struct hardwire_ttystate *state = (struct hardwire_ttystate *) old_ttystate;
|
||
#endif
|
||
|
||
new_state = *(struct hardwire_ttystate *) new_ttystate;
|
||
|
||
/* Don't change in or out of raw mode; we don't want to flush input.
|
||
termio and termios have no such restriction; for them flushing input
|
||
is separate from setting the attributes. */
|
||
|
||
#ifdef HAVE_SGTTY
|
||
if (state->sgttyb.sg_flags & RAW)
|
||
new_state.sgttyb.sg_flags |= RAW;
|
||
else
|
||
new_state.sgttyb.sg_flags &= ~RAW;
|
||
|
||
/* I'm not sure whether this is necessary; the manpage just mentions
|
||
RAW not CBREAK. */
|
||
if (state->sgttyb.sg_flags & CBREAK)
|
||
new_state.sgttyb.sg_flags |= CBREAK;
|
||
else
|
||
new_state.sgttyb.sg_flags &= ~CBREAK;
|
||
#endif
|
||
|
||
return set_tty_state (scb, &new_state);
|
||
}
|
||
|
||
static void
|
||
hardwire_print_tty_state (struct serial *scb,
|
||
serial_ttystate ttystate,
|
||
struct ui_file *stream)
|
||
{
|
||
struct hardwire_ttystate *state = (struct hardwire_ttystate *) ttystate;
|
||
int i;
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
fprintf_filtered (stream, "c_iflag = 0x%x, c_oflag = 0x%x,\n",
|
||
(int) state->termios.c_iflag,
|
||
(int) state->termios.c_oflag);
|
||
fprintf_filtered (stream, "c_cflag = 0x%x, c_lflag = 0x%x\n",
|
||
(int) state->termios.c_cflag,
|
||
(int) state->termios.c_lflag);
|
||
#if 0
|
||
/* This not in POSIX, and is not really documented by those systems
|
||
which have it (at least not Sun). */
|
||
fprintf_filtered (stream, "c_line = 0x%x.\n", state->termios.c_line);
|
||
#endif
|
||
fprintf_filtered (stream, "c_cc: ");
|
||
for (i = 0; i < NCCS; i += 1)
|
||
fprintf_filtered (stream, "0x%x ", state->termios.c_cc[i]);
|
||
fprintf_filtered (stream, "\n");
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
fprintf_filtered (stream, "c_iflag = 0x%x, c_oflag = 0x%x,\n",
|
||
state->termio.c_iflag, state->termio.c_oflag);
|
||
fprintf_filtered (stream, "c_cflag = 0x%x, c_lflag = 0x%x, c_line = 0x%x.\n",
|
||
state->termio.c_cflag, state->termio.c_lflag,
|
||
state->termio.c_line);
|
||
fprintf_filtered (stream, "c_cc: ");
|
||
for (i = 0; i < NCC; i += 1)
|
||
fprintf_filtered (stream, "0x%x ", state->termio.c_cc[i]);
|
||
fprintf_filtered (stream, "\n");
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
fprintf_filtered (stream, "sgttyb.sg_flags = 0x%x.\n",
|
||
state->sgttyb.sg_flags);
|
||
|
||
fprintf_filtered (stream, "tchars: ");
|
||
for (i = 0; i < (int) sizeof (struct tchars); i++)
|
||
fprintf_filtered (stream, "0x%x ", ((unsigned char *) &state->tc)[i]);
|
||
fprintf_filtered (stream, "\n");
|
||
|
||
fprintf_filtered (stream, "ltchars: ");
|
||
for (i = 0; i < (int) sizeof (struct ltchars); i++)
|
||
fprintf_filtered (stream, "0x%x ", ((unsigned char *) &state->ltc)[i]);
|
||
fprintf_filtered (stream, "\n");
|
||
|
||
fprintf_filtered (stream, "lmode: 0x%x\n", state->lmode);
|
||
#endif
|
||
}
|
||
|
||
/* Wait for the output to drain away, as opposed to flushing
|
||
(discarding) it. */
|
||
|
||
static int
|
||
hardwire_drain_output (struct serial *scb)
|
||
{
|
||
#ifdef HAVE_TERMIOS
|
||
return tcdrain (scb->fd);
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
return ioctl (scb->fd, TCSBRK, 1);
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
/* Get the current state and then restore it using TIOCSETP,
|
||
which should cause the output to drain and pending input
|
||
to be discarded. */
|
||
{
|
||
struct hardwire_ttystate state;
|
||
|
||
if (get_tty_state (scb, &state))
|
||
{
|
||
return (-1);
|
||
}
|
||
else
|
||
{
|
||
return (ioctl (scb->fd, TIOCSETP, &state.sgttyb));
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
hardwire_flush_output (struct serial *scb)
|
||
{
|
||
#ifdef HAVE_TERMIOS
|
||
return tcflush (scb->fd, TCOFLUSH);
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
return ioctl (scb->fd, TCFLSH, 1);
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
/* This flushes both input and output, but we can't do better. */
|
||
return ioctl (scb->fd, TIOCFLUSH, 0);
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
hardwire_flush_input (struct serial *scb)
|
||
{
|
||
ser_base_flush_input (scb);
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
return tcflush (scb->fd, TCIFLUSH);
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
return ioctl (scb->fd, TCFLSH, 0);
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
/* This flushes both input and output, but we can't do better. */
|
||
return ioctl (scb->fd, TIOCFLUSH, 0);
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
hardwire_send_break (struct serial *scb)
|
||
{
|
||
#ifdef HAVE_TERMIOS
|
||
return tcsendbreak (scb->fd, 0);
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
return ioctl (scb->fd, TCSBRK, 0);
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
{
|
||
int status;
|
||
|
||
status = ioctl (scb->fd, TIOCSBRK, 0);
|
||
|
||
/* Can't use usleep; it doesn't exist in BSD 4.2. */
|
||
/* Note that if this gdb_select() is interrupted by a signal it will not
|
||
wait the full length of time. I think that is OK. */
|
||
gdb_usleep (250000);
|
||
status = ioctl (scb->fd, TIOCCBRK, 0);
|
||
return status;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
hardwire_raw (struct serial *scb)
|
||
{
|
||
struct hardwire_ttystate state;
|
||
|
||
if (get_tty_state (scb, &state))
|
||
fprintf_unfiltered (gdb_stderr, "get_tty_state failed: %s\n",
|
||
safe_strerror (errno));
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
state.termios.c_iflag = 0;
|
||
state.termios.c_oflag = 0;
|
||
state.termios.c_lflag = 0;
|
||
state.termios.c_cflag &= ~CSIZE;
|
||
state.termios.c_cflag |= CLOCAL | CS8;
|
||
#ifdef CRTSCTS
|
||
/* h/w flow control. */
|
||
if (serial_hwflow)
|
||
state.termios.c_cflag |= CRTSCTS;
|
||
else
|
||
state.termios.c_cflag &= ~CRTSCTS;
|
||
#ifdef CRTS_IFLOW
|
||
if (serial_hwflow)
|
||
state.termios.c_cflag |= CRTS_IFLOW;
|
||
else
|
||
state.termios.c_cflag &= ~CRTS_IFLOW;
|
||
#endif
|
||
#endif
|
||
state.termios.c_cc[VMIN] = 0;
|
||
state.termios.c_cc[VTIME] = 0;
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
state.termio.c_iflag = 0;
|
||
state.termio.c_oflag = 0;
|
||
state.termio.c_lflag = 0;
|
||
state.termio.c_cflag &= ~CSIZE;
|
||
state.termio.c_cflag |= CLOCAL | CS8;
|
||
state.termio.c_cc[VMIN] = 0;
|
||
state.termio.c_cc[VTIME] = 0;
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
state.sgttyb.sg_flags |= RAW | ANYP;
|
||
state.sgttyb.sg_flags &= ~(CBREAK | ECHO);
|
||
#endif
|
||
|
||
scb->current_timeout = 0;
|
||
|
||
if (set_tty_state (scb, &state))
|
||
fprintf_unfiltered (gdb_stderr, "set_tty_state failed: %s\n",
|
||
safe_strerror (errno));
|
||
}
|
||
|
||
/* Wait for input on scb, with timeout seconds. Returns 0 on success,
|
||
otherwise SERIAL_TIMEOUT or SERIAL_ERROR. */
|
||
|
||
/* FIXME: cagney/1999-09-16: Don't replace this with the equivalent
|
||
ser_base*() until the old TERMIOS/SGTTY/... timer code has been
|
||
flushed. . */
|
||
|
||
/* NOTE: cagney/1999-09-30: Much of the code below is dead. The only
|
||
possible values of the TIMEOUT parameter are ONE and ZERO.
|
||
Consequently all the code that tries to handle the possability of
|
||
an overflowed timer is unnecessary. */
|
||
|
||
static int
|
||
wait_for (struct serial *scb, int timeout)
|
||
{
|
||
while (1)
|
||
{
|
||
struct timeval tv;
|
||
fd_set readfds;
|
||
int numfds;
|
||
|
||
/* 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_SET (scb->fd, &readfds);
|
||
|
||
QUIT;
|
||
|
||
if (timeout >= 0)
|
||
numfds = interruptible_select (scb->fd + 1, &readfds, 0, 0, &tv);
|
||
else
|
||
numfds = interruptible_select (scb->fd + 1, &readfds, 0, 0, 0);
|
||
|
||
if (numfds == -1 && errno == EINTR)
|
||
continue;
|
||
else if (numfds == -1)
|
||
return SERIAL_ERROR;
|
||
else if (numfds == 0)
|
||
return SERIAL_TIMEOUT;
|
||
|
||
return 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, EOF if line dropped dead, or SERIAL_ERROR for any
|
||
other error (see errno in that case). */
|
||
|
||
/* FIXME: cagney/1999-09-16: Don't replace this with the equivalent
|
||
ser_base*() until the old TERMIOS/SGTTY/... timer code has been
|
||
flushed. */
|
||
|
||
/* NOTE: cagney/1999-09-16: This function is not identical to
|
||
ser_base_readchar() as part of replacing it with ser_base*()
|
||
merging will be required - this code handles the case where read()
|
||
times out due to no data while ser_base_readchar() doesn't expect
|
||
that. */
|
||
|
||
static int
|
||
do_hardwire_readchar (struct serial *scb, int timeout)
|
||
{
|
||
int status, delta;
|
||
int detach = 0;
|
||
|
||
if (timeout > 0)
|
||
timeout++;
|
||
|
||
/* 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)
|
||
detach = deprecated_ui_loop_hook (0);
|
||
|
||
if (detach)
|
||
return SERIAL_TIMEOUT;
|
||
|
||
scb->timeout_remaining = (timeout < 0 ? timeout : timeout - delta);
|
||
status = wait_for (scb, delta);
|
||
|
||
if (status < 0)
|
||
return status;
|
||
|
||
status = read (scb->fd, scb->buf, BUFSIZ);
|
||
|
||
if (status <= 0)
|
||
{
|
||
if (status == 0)
|
||
{
|
||
/* Zero characters means timeout (it could also be EOF, but
|
||
we don't (yet at least) distinguish). */
|
||
if (scb->timeout_remaining > 0)
|
||
{
|
||
timeout = scb->timeout_remaining;
|
||
continue;
|
||
}
|
||
else if (scb->timeout_remaining < 0)
|
||
continue;
|
||
else
|
||
return SERIAL_TIMEOUT;
|
||
}
|
||
else if (errno == EINTR)
|
||
continue;
|
||
else
|
||
return SERIAL_ERROR; /* Got an error from read. */
|
||
}
|
||
|
||
scb->bufcnt = status;
|
||
scb->bufcnt--;
|
||
scb->bufp = scb->buf;
|
||
return *scb->bufp++;
|
||
}
|
||
}
|
||
|
||
static int
|
||
hardwire_readchar (struct serial *scb, int timeout)
|
||
{
|
||
return generic_readchar (scb, timeout, do_hardwire_readchar);
|
||
}
|
||
|
||
|
||
#ifndef B19200
|
||
#define B19200 EXTA
|
||
#endif
|
||
|
||
#ifndef B38400
|
||
#define B38400 EXTB
|
||
#endif
|
||
|
||
/* Translate baud rates from integers to damn B_codes. Unix should
|
||
have outgrown this crap years ago, but even POSIX wouldn't buck it. */
|
||
|
||
static struct
|
||
{
|
||
int rate;
|
||
int code;
|
||
}
|
||
baudtab[] =
|
||
{
|
||
{
|
||
50, B50
|
||
}
|
||
,
|
||
{
|
||
75, B75
|
||
}
|
||
,
|
||
{
|
||
110, B110
|
||
}
|
||
,
|
||
{
|
||
134, B134
|
||
}
|
||
,
|
||
{
|
||
150, B150
|
||
}
|
||
,
|
||
{
|
||
200, B200
|
||
}
|
||
,
|
||
{
|
||
300, B300
|
||
}
|
||
,
|
||
{
|
||
600, B600
|
||
}
|
||
,
|
||
{
|
||
1200, B1200
|
||
}
|
||
,
|
||
{
|
||
1800, B1800
|
||
}
|
||
,
|
||
{
|
||
2400, B2400
|
||
}
|
||
,
|
||
{
|
||
4800, B4800
|
||
}
|
||
,
|
||
{
|
||
9600, B9600
|
||
}
|
||
,
|
||
{
|
||
19200, B19200
|
||
}
|
||
,
|
||
{
|
||
38400, B38400
|
||
}
|
||
,
|
||
#ifdef B57600
|
||
{
|
||
57600, B57600
|
||
}
|
||
,
|
||
#endif
|
||
#ifdef B115200
|
||
{
|
||
115200, B115200
|
||
}
|
||
,
|
||
#endif
|
||
#ifdef B230400
|
||
{
|
||
230400, B230400
|
||
}
|
||
,
|
||
#endif
|
||
#ifdef B460800
|
||
{
|
||
460800, B460800
|
||
}
|
||
,
|
||
#endif
|
||
{
|
||
-1, -1
|
||
}
|
||
,
|
||
};
|
||
|
||
static int
|
||
rate_to_code (int rate)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; baudtab[i].rate != -1; i++)
|
||
{
|
||
/* test for perfect macth. */
|
||
if (rate == baudtab[i].rate)
|
||
return baudtab[i].code;
|
||
else
|
||
{
|
||
/* check if it is in between valid values. */
|
||
if (rate < baudtab[i].rate)
|
||
{
|
||
if (i)
|
||
{
|
||
warning (_("Invalid baud rate %d. "
|
||
"Closest values are %d and %d."),
|
||
rate, baudtab[i - 1].rate, baudtab[i].rate);
|
||
}
|
||
else
|
||
{
|
||
warning (_("Invalid baud rate %d. Minimum value is %d."),
|
||
rate, baudtab[0].rate);
|
||
}
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* The requested speed was too large. */
|
||
warning (_("Invalid baud rate %d. Maximum value is %d."),
|
||
rate, baudtab[i - 1].rate);
|
||
return -1;
|
||
}
|
||
|
||
static int
|
||
hardwire_setbaudrate (struct serial *scb, int rate)
|
||
{
|
||
struct hardwire_ttystate state;
|
||
int baud_code = rate_to_code (rate);
|
||
|
||
if (baud_code < 0)
|
||
{
|
||
/* The baud rate was not valid.
|
||
A warning has already been issued. */
|
||
errno = EINVAL;
|
||
return -1;
|
||
}
|
||
|
||
if (get_tty_state (scb, &state))
|
||
return -1;
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
cfsetospeed (&state.termios, baud_code);
|
||
cfsetispeed (&state.termios, baud_code);
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
#ifndef CIBAUD
|
||
#define CIBAUD CBAUD
|
||
#endif
|
||
|
||
state.termio.c_cflag &= ~(CBAUD | CIBAUD);
|
||
state.termio.c_cflag |= baud_code;
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
state.sgttyb.sg_ispeed = baud_code;
|
||
state.sgttyb.sg_ospeed = baud_code;
|
||
#endif
|
||
|
||
return set_tty_state (scb, &state);
|
||
}
|
||
|
||
static int
|
||
hardwire_setstopbits (struct serial *scb, int num)
|
||
{
|
||
struct hardwire_ttystate state;
|
||
int newbit;
|
||
|
||
if (get_tty_state (scb, &state))
|
||
return -1;
|
||
|
||
switch (num)
|
||
{
|
||
case SERIAL_1_STOPBITS:
|
||
newbit = 0;
|
||
break;
|
||
case SERIAL_1_AND_A_HALF_STOPBITS:
|
||
case SERIAL_2_STOPBITS:
|
||
newbit = 1;
|
||
break;
|
||
default:
|
||
return 1;
|
||
}
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
if (!newbit)
|
||
state.termios.c_cflag &= ~CSTOPB;
|
||
else
|
||
state.termios.c_cflag |= CSTOPB; /* two bits */
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
if (!newbit)
|
||
state.termio.c_cflag &= ~CSTOPB;
|
||
else
|
||
state.termio.c_cflag |= CSTOPB; /* two bits */
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
return 0; /* sgtty doesn't support this */
|
||
#endif
|
||
|
||
return set_tty_state (scb, &state);
|
||
}
|
||
|
||
/* Implement the "setparity" serial_ops callback. */
|
||
|
||
static int
|
||
hardwire_setparity (struct serial *scb, int parity)
|
||
{
|
||
struct hardwire_ttystate state;
|
||
int newparity = 0;
|
||
|
||
if (get_tty_state (scb, &state))
|
||
return -1;
|
||
|
||
switch (parity)
|
||
{
|
||
case GDBPARITY_NONE:
|
||
newparity = 0;
|
||
break;
|
||
case GDBPARITY_ODD:
|
||
newparity = PARENB | PARODD;
|
||
break;
|
||
case GDBPARITY_EVEN:
|
||
newparity = PARENB;
|
||
break;
|
||
default:
|
||
internal_warning (__FILE__, __LINE__,
|
||
"Incorrect parity value: %d", parity);
|
||
return -1;
|
||
}
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
state.termios.c_cflag &= ~(PARENB | PARODD);
|
||
state.termios.c_cflag |= newparity;
|
||
#endif
|
||
|
||
#ifdef HAVE_TERMIO
|
||
state.termio.c_cflag &= ~(PARENB | PARODD);
|
||
state.termio.c_cflag |= newparity;
|
||
#endif
|
||
|
||
#ifdef HAVE_SGTTY
|
||
return 0; /* sgtty doesn't support this */
|
||
#endif
|
||
return set_tty_state (scb, &state);
|
||
}
|
||
|
||
|
||
static void
|
||
hardwire_close (struct serial *scb)
|
||
{
|
||
if (scb->fd < 0)
|
||
return;
|
||
|
||
close (scb->fd);
|
||
scb->fd = -1;
|
||
}
|
||
|
||
|
||
|
||
/* The hardwire ops. */
|
||
|
||
static const struct serial_ops hardwire_ops =
|
||
{
|
||
"hardwire",
|
||
hardwire_open,
|
||
hardwire_close,
|
||
NULL,
|
||
/* FIXME: Don't replace this with the equivalent ser_base*() until
|
||
the old TERMIOS/SGTTY/... timer code has been flushed. cagney
|
||
1999-09-16. */
|
||
hardwire_readchar,
|
||
ser_base_write,
|
||
hardwire_flush_output,
|
||
hardwire_flush_input,
|
||
hardwire_send_break,
|
||
hardwire_raw,
|
||
hardwire_get_tty_state,
|
||
hardwire_copy_tty_state,
|
||
hardwire_set_tty_state,
|
||
hardwire_print_tty_state,
|
||
hardwire_noflush_set_tty_state,
|
||
hardwire_setbaudrate,
|
||
hardwire_setstopbits,
|
||
hardwire_setparity,
|
||
hardwire_drain_output,
|
||
ser_base_async,
|
||
ser_unix_read_prim,
|
||
ser_unix_write_prim
|
||
};
|
||
|
||
void
|
||
_initialize_ser_hardwire (void)
|
||
{
|
||
serial_add_interface (&hardwire_ops);
|
||
|
||
#ifdef HAVE_TERMIOS
|
||
#ifdef CRTSCTS
|
||
add_setshow_boolean_cmd ("remoteflow", no_class,
|
||
&serial_hwflow, _("\
|
||
Set use of hardware flow control for remote serial I/O."), _("\
|
||
Show use of hardware flow control for remote serial I/O."), _("\
|
||
Enable or disable hardware flow control (RTS/CTS) on the serial port\n\
|
||
when debugging using remote targets."),
|
||
NULL,
|
||
show_serial_hwflow,
|
||
&setlist, &showlist);
|
||
#endif
|
||
#endif
|
||
}
|
||
|
||
int
|
||
ser_unix_read_prim (struct serial *scb, size_t count)
|
||
{
|
||
return read (scb->fd, scb->buf, count);
|
||
}
|
||
|
||
int
|
||
ser_unix_write_prim (struct serial *scb, const void *buf, size_t len)
|
||
{
|
||
return write (scb->fd, buf, len);
|
||
}
|