* eCos tx3904sio sim - devo part 1/2
authorFrank Ch. Eigler <fche@redhat.com>
Tue, 25 Aug 1998 13:58:35 +0000 (13:58 +0000)
committerFrank Ch. Eigler <fche@redhat.com>
Tue, 25 Aug 1998 13:58:35 +0000 (13:58 +0000)
Tue Aug 25 12:45:27 1998  Frank Ch. Eigler  <fche@cygnus.com>
* dv-sockser.c (sockser_addr): Make variable non-static.

sim/common/dv-sockser.c [new file with mode: 0644]

diff --git a/sim/common/dv-sockser.c b/sim/common/dv-sockser.c
new file mode 100644 (file)
index 0000000..d90c817
--- /dev/null
@@ -0,0 +1,377 @@
+/* Serial port emulation using sockets.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   Contributed by Cygnus Solutions.
+
+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, 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.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* FIXME: will obviously need to evolve.
+   - connectionless sockets might be more appropriate.  */
+
+#include "sim-main.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#include <signal.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#ifndef __CYGWIN32__
+#include <netinet/tcp.h>
+#endif
+
+#include "sim-assert.h"
+#include "sim-options.h"
+
+#include "dv-sockser.h"
+\f
+/* Get definitions for both O_NONBLOCK and O_NDELAY.  */
+
+#ifndef O_NDELAY
+#ifdef FNDELAY
+#define O_NDELAY FNDELAY
+#else /* ! defined (FNDELAY) */
+#define O_NDELAY 0
+#endif /* ! defined (FNDELAY) */
+#endif /* ! defined (O_NDELAY) */
+
+#ifndef O_NONBLOCK
+#ifdef FNBLOCK
+#define O_NONBLOCK FNBLOCK
+#else /* ! defined (FNBLOCK) */
+#define O_NONBLOCK 0
+#endif /* ! defined (FNBLOCK) */
+#endif /* ! defined (O_NONBLOCK) */
+\f
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* Compromise between eating cpu and properly busy-waiting.
+   One could have an option to set this but for now that seems
+   like featuritis.  */
+#define DEFAULT_TIMEOUT 100 /* microseconds */
+
+/* FIXME: These should allocated at run time and kept with other simulator
+   state (duh...).  Later.  */
+const char * sockser_addr = NULL;
+/* Timeout in microseconds during status flag computation.
+   Setting this to zero achieves proper busy wait semantics but eats cpu.  */
+static unsigned int sockser_timeout = DEFAULT_TIMEOUT;
+static int sockser_listen_fd = -1;
+static int sockser_fd = -1;
+\f
+/* FIXME: use tree properties when they're ready.  */
+
+typedef enum {
+  OPTION_ADDR = OPTION_START
+} SOCKSER_OPTIONS;
+
+static DECLARE_OPTION_HANDLER (sockser_option_handler);
+
+static const OPTION sockser_options[] =
+{
+  { { "sockser-addr", required_argument, NULL, OPTION_ADDR },
+      '\0', "SOCKET ADDRESS", "Set serial emulation socket address",
+      sockser_option_handler },
+  { { NULL, no_argument, NULL, 0 }, '\0', NULL, NULL, NULL }
+};
+
+static SIM_RC
+sockser_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt,
+                       char *arg, int is_command)
+{
+  switch (opt)
+    {
+    case OPTION_ADDR :
+      sockser_addr = arg;
+      break;
+    }
+
+  return SIM_RC_OK;
+}
+
+static SIM_RC
+dv_sockser_init (SIM_DESC sd)
+{
+  struct hostent *hostent;
+  struct sockaddr_in sockaddr;
+  char hostname[100];
+  const char *port_str;
+  int tmp,port;
+
+  if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT
+      || sockser_addr == NULL)
+    return SIM_RC_OK;
+
+  if (*sockser_addr == '/')
+    {
+      /* support for these can come later */
+      sim_io_eprintf (sd, "sockser init: unix domain sockets not supported: `%s'\n",
+                     sockser_addr);
+      return SIM_RC_FAIL;
+    }
+
+  port_str = strchr (sockser_addr, ':');
+  if (!port_str)
+    {
+      sim_io_eprintf (sd, "sockser init: missing port number: `%s'\n",
+                     sockser_addr);
+      return SIM_RC_FAIL;
+    }
+  tmp = MIN (port_str - sockser_addr, (int) sizeof hostname - 1);
+  strncpy (hostname, sockser_addr, tmp);
+  hostname[tmp] = '\000';
+  port = atoi (port_str + 1);
+
+  hostent = gethostbyname (hostname);
+  if (! hostent)
+    {
+      sim_io_eprintf (sd, "sockser init: unknown host: %s\n",
+                     hostname);
+      return SIM_RC_FAIL;
+    }
+
+  sockser_listen_fd = socket (PF_INET, SOCK_STREAM, 0);
+  if (sockser_listen_fd < 0)
+    {
+      sim_io_eprintf (sd, "sockser init: unable to get socket: %s\n",
+                     strerror (errno));
+      return SIM_RC_FAIL;
+    }
+
+  sockaddr.sin_family = PF_INET;
+  sockaddr.sin_port = htons(port);
+  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
+         sizeof (struct in_addr));
+
+  if (bind (sockser_listen_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
+    {
+      sim_io_eprintf (sd, "sockser init: unable to bind socket address: %s\n",
+                     strerror (errno));
+      close (sockser_listen_fd);
+      sockser_listen_fd = -1;
+      return SIM_RC_FAIL;
+    }
+  if (listen (sockser_listen_fd, 1) < 0)
+    {
+      sim_io_eprintf (sd, "sockser init: unable to set up listener: %s\n",
+                     strerror (errno));
+      close (sockser_listen_fd);
+      sockser_listen_fd = -1;
+      return SIM_RC_OK;
+    }
+
+  /* Handle writes to missing client -> SIGPIPE.
+     ??? Need a central signal management module.  */
+  {
+    RETSIGTYPE (*orig) ();
+    orig = signal (SIGPIPE, SIG_IGN);
+    /* If a handler is already set up, don't mess with it.  */
+    if (orig != SIG_DFL && orig != SIG_IGN)
+      signal (SIGPIPE, orig);
+  }
+
+  return SIM_RC_OK;
+}
+
+static void
+dv_sockser_uninstall (SIM_DESC sd)
+{
+  if (sockser_listen_fd != -1)
+    {
+      close (sockser_listen_fd);
+      sockser_listen_fd = -1;
+    }
+  if (sockser_fd != -1)
+    {
+      close (sockser_fd);
+      sockser_fd = -1;
+    }
+}
+
+SIM_RC
+dv_sockser_install (SIM_DESC sd)
+{
+  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
+  if (sim_add_option_table (sd, NULL, sockser_options) != SIM_RC_OK)
+    return SIM_RC_FAIL;
+  sim_module_add_init_fn (sd, dv_sockser_init);
+  sim_module_add_uninstall_fn (sd, dv_sockser_uninstall);
+  return SIM_RC_OK;
+}
+
+static int
+connected_p (SIM_DESC sd)
+{
+  int numfds,flags;
+  struct timeval tv;
+  fd_set readfds;
+  struct sockaddr sockaddr;
+  int addrlen;
+
+  if (sockser_listen_fd == -1)
+    return 0;
+
+  if (sockser_fd >= 0)
+    {
+      /* FIXME: has client gone away? */
+      return 1;
+    }
+
+  /* Not connected.  Connect with a client if there is one.  */
+
+  FD_ZERO (&readfds);
+  FD_SET (sockser_listen_fd, &readfds);
+
+  /* ??? One can certainly argue this should be done differently,
+     but for now this is sufficient.  */
+  tv.tv_sec = 0;
+  tv.tv_usec = sockser_timeout;
+
+  numfds = select (sockser_listen_fd + 1, &readfds, 0, 0, &tv);
+  if (numfds <= 0)
+    return 0;
+
+  sockser_fd = accept (sockser_listen_fd, &sockaddr, &addrlen);
+  if (sockser_fd < 0)
+    return 0;
+
+  /* Set non-blocking i/o.  */
+  flags = fcntl (sockser_fd, F_GETFL);
+  flags |= O_NONBLOCK | O_NDELAY;
+  if (fcntl (sockser_fd, F_SETFL, flags) == -1)
+    {
+      sim_io_eprintf (sd, "unable to set nonblocking i/o");
+      close (sockser_fd);
+      sockser_fd = -1;
+      return 0;
+    }
+  return 1;
+}
+
+int
+dv_sockser_status (SIM_DESC sd)
+{
+  int numrfds,numwfds,status;
+  struct timeval tv;
+  fd_set readfds,writefds;
+
+  /* status to return if the socket isn't set up, or select fails */
+  status = DV_SOCKSER_INPUT_EMPTY | DV_SOCKSER_OUTPUT_EMPTY;
+
+  if (! connected_p (sd))
+    return status;
+
+  FD_ZERO (&readfds);
+  FD_ZERO (&writefds);
+  FD_SET (sockser_fd, &readfds);
+  FD_SET (sockser_fd, &writefds);
+
+  /* ??? One can certainly argue this should be done differently,
+     but for now this is sufficient.  The read is done separately
+     from the write to enforce the delay which we heuristically set to
+     once every SOCKSER_TIMEOUT_FREQ tries.
+     No, this isn't great for SMP situations, blah blah blah.  */
+
+  {
+    static int n;
+#define SOCKSER_TIMEOUT_FREQ 42
+    if (++n == SOCKSER_TIMEOUT_FREQ)
+      n = 0;
+    if (n == 0)
+      {
+       tv.tv_sec = 0;
+       tv.tv_usec = sockser_timeout;
+       numrfds = select (sockser_fd + 1, &readfds, 0, 0, &tv);
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       numwfds = select (sockser_fd + 1, 0, &writefds, 0, &tv);
+      }
+    else /* do both selects at once */
+      {
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       numrfds = numwfds = select (sockser_fd + 1, &readfds, &writefds, 0, &tv);
+      }
+  }
+
+  status = 0;
+  if (numrfds <= 0 || ! FD_ISSET (sockser_fd, &readfds))
+    status |= DV_SOCKSER_INPUT_EMPTY;
+  if (numwfds <= 0 || FD_ISSET (sockser_fd, &writefds))
+    status |= DV_SOCKSER_OUTPUT_EMPTY;
+  return status;
+}
+
+int
+dv_sockser_write (SIM_DESC sd, unsigned char c)
+{
+  int n;
+
+  if (! connected_p (sd))
+    return -1;
+  n = write (sockser_fd, &c, 1);
+  if (n == -1)
+    {
+      if (errno == EPIPE)
+       {
+         close (sockser_fd);
+         sockser_fd = -1;
+       }
+      return -1;
+    }
+  if (n != 1)
+    return -1;
+  return 1;
+}
+
+int
+dv_sockser_read (SIM_DESC sd)
+{
+  unsigned char c;
+  int n;
+
+  if (! connected_p (sd))
+    return -1;
+  n = read (sockser_fd, &c, 1);
+  /* ??? We're assuming semantics that may not be correct for all hosts.
+     In particular (from cvssrc/src/server.c), this assumes that we are using
+     BSD or POSIX nonblocking I/O.  System V nonblocking I/O returns zero if
+     there is nothing to read.  */
+  if (n == 0)
+    {
+      close (sockser_fd);
+      sockser_fd = -1;
+      return -1;
+    }
+  if (n != 1)
+    return -1;
+  return c;
+}