* HttpWebRequest.cs: Allow Proxy to be set to null on 2.0 profile.
[mono.git] / support / serial.c
index 515f9860a019b62febeb6e2e4980e60ea1f52dcd..f7746c41a866419171a091c593fd838600a71ca7 100644 (file)
 #include <fcntl.h>
 #include <string.h>
 #include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <errno.h>
 
 #include <glib.h>
 
+/* This is for FIONREAD on solaris */
+#if defined(sun)
+#include <sys/filio.h>
+#endif
+
+/* This is a copy of System.IO.Ports.Handshake */
+typedef enum {
+       NoneHandshake = 0,
+       XOnXOff = 1,
+       RequestToSend = 2,
+       RequestToSendXOnXOff = 3
+} MonoHandshake;
+
+/* This is a copy of System.IO.Ports.Parity */
+typedef enum {
+       NoneParity = 0,
+       Odd = 1,
+       Even = 2,
+       Mark = 3,
+       Space = 4
+} MonoParity;
+
+/* This is a copy of System.IO.Ports.StopBits */
+typedef enum {
+       NoneStopBits = 0,
+       One = 1,
+       Two = 2,
+       OnePointFive = 3
+} MonoStopBits;
+
+/* This is a copy of System.IO.Ports.SerialSignal */
+typedef enum {
+       NoneSignal,
+       Cd = 1, /* Carrier detect */
+       Cts = 2, /* Clear to send */
+       Dsr = 4, /* Data set ready */
+       Dtr = 8, /* Data terminal ready */
+       Rts = 16  /* Request to send */
+} MonoSerialSignal;
+
 int
 open_serial (char* devfile)
 {
@@ -32,8 +74,6 @@ open_serial (char* devfile)
        tcflush(fd, TCIOFLUSH);
        tcsetattr(fd,TCSANOW,&newtio);
 
-       fcntl (fd, F_SETFL, O_NONBLOCK);
-
        return fd;
 }
 
@@ -44,19 +84,9 @@ close_serial (int unix_fd)
 }
 
 guint32
-read_serial (int fd, guchar *buffer, int offset, int count, int timeout)
+read_serial (int fd, guchar *buffer, int offset, int count)
 {
        guint32 n;
-       struct pollfd ufd;
-
-       ufd.fd = fd;
-       ufd.events = POLLHUP | POLLIN | POLLERR;
-
-       poll (&ufd, 1, timeout);
-
-       if ((ufd.revents & POLLIN) != POLLIN) {
-               return -1;
-       }
  
        n = read (fd, buffer + offset, count);
 
@@ -73,7 +103,9 @@ write_serial (int fd, guchar *buffer, int offset, int count, int timeout)
        ufd.fd = fd;
        ufd.events = POLLHUP | POLLOUT | POLLERR;
 
-       poll (&ufd, 1, timeout);
+       while (poll (&ufd, 1, timeout) == -1 && errno == EINTR){
+               
+       }
 
        if ((ufd.revents & POLLOUT) != POLLOUT) {
                return;
@@ -88,8 +120,20 @@ discard_buffer (int fd, gboolean input)
        tcflush(fd, input ? TCIFLUSH : TCOFLUSH);
 }
 
+gint32
+get_bytes_in_buffer (int fd, gboolean input)
+{
+       gint32 retval;
+
+       if (ioctl (fd, input ? FIONREAD : TIOCOUTQ, &retval) == -1) {
+               return -1;
+       }
+
+       return retval;
+}
+
 gboolean
-set_attributes (int fd, int baud_rate, int parity, int dataBits, int stopBits, int handshake)
+set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStopBits stopBits, MonoHandshake handshake)
 {
        struct termios newtio;
 
@@ -121,22 +165,22 @@ set_attributes (int fd, int baud_rate, int parity, int dataBits, int stopBits, i
        }
 
        switch (parity) {
-       case 0: /* Even */
-               newtio.c_iflag &= ~IGNPAR;
-               newtio.c_cflag |= PARENB;
-               break;
-       case 1: /* Mark */
-               /* XXX unhandled */
-               break;
-       case 2: /* None */
+       case NoneParity: /* None */
                newtio.c_iflag |= IGNPAR;
                newtio.c_cflag &= ~(PARENB | PARODD);
                break;
-       case 3: /* Odd */
+       case Odd: /* Odd */
                newtio.c_iflag &= ~IGNPAR;
                newtio.c_cflag |= PARENB | PARODD;
                break;
-       case 4: /* Space */
+       case Even: /* Even */
+               newtio.c_iflag &= ~(IGNPAR | PARODD);
+               newtio.c_cflag |= PARENB;
+               break;
+       case Mark: /* Mark */
+               /* XXX unhandled */
+               break;
+       case Space: /* Space */
                /* XXX unhandled */
                break;
        }
@@ -154,44 +198,154 @@ set_attributes (int fd, int baud_rate, int parity, int dataBits, int stopBits, i
 
        newtio.c_cflag &= ~CSTOPB;
        switch (stopBits) {
-       case 0: /* One */
-               /* do nothing, the default is one stop bit */
+       case NoneStopBits:
+               /* Unhandled */
                break;
-       case 1: /* OnePointFive */
-               /* XXX unhandled */
+       case One: /* One */
+               /* do nothing, the default is one stop bit */
                break;
-       case 2: /* Two */
+       case Two: /* Two */
                newtio.c_cflag |= CSTOPB;
                break;
+       case OnePointFive: /* OnePointFive */
+               /* XXX unhandled */
+               break;
        }
 
        newtio.c_iflag &= ~IXOFF;
        newtio.c_oflag &= ~IXON;
+#ifdef CRTSCTS
        newtio.c_cflag &= ~CRTSCTS;
+#endif /* def CRTSCTS */
        switch (handshake) {
-       case 0: /* None */
+       case NoneHandshake: /* None */
                /* do nothing */
                break;
-       case 1: /* RequestToSend (RTS) */
+       case RequestToSend: /* RequestToSend (RTS) */
+#ifdef CRTSCTS
                newtio.c_cflag |= CRTSCTS;
+#endif /* def CRTSCTS */
                break;
-       case 2: /* RequestToSendXOnXOff (RTS + XON/XOFF) */
+       case RequestToSendXOnXOff: /* RequestToSendXOnXOff (RTS + XON/XOFF) */
+#ifdef CRTSCTS
                newtio.c_cflag |= CRTSCTS;
+#endif /* def CRTSCTS */
                /* fall through */
-       case 3: /* XOnXOff */
+       case XOnXOff: /* XOnXOff */
                newtio.c_iflag |= IXOFF;
                //              newtio.c_oflag |= IXON;
                break;
        }
        
-       cfsetospeed (&newtio, baud_rate);
-       cfsetispeed (&newtio, baud_rate);
-
-       tcsetattr(fd,TCSADRAIN,&newtio);
+       if (cfsetospeed (&newtio, baud_rate) < 0 || cfsetispeed (&newtio, baud_rate) < 0 ||
+                       tcsetattr (fd, TCSADRAIN, &newtio) < 0)
+               return FALSE;
 
        return TRUE;
 }
 
+static gint32
+get_signal_code (MonoSerialSignal signal)
+{
+       switch (signal) {
+               case Cd:
+                       return TIOCM_CAR;
+               case Cts:
+                       return TIOCM_CTS;
+               case Dsr:
+                       return TIOCM_DSR;
+               case Dtr:
+                       return TIOCM_DTR;
+               case Rts:
+                       return TIOCM_RTS;
+               default:
+                       return 0;
+       }
+
+       /* Not reached */
+       return 0;
+}
+
+static MonoSerialSignal
+get_mono_signal_codes (int signals)
+{
+       MonoSerialSignal retval = NoneSignal;
+
+       if ((signals & TIOCM_CAR) != 0)
+               retval |= Cd;
+       if ((signals & TIOCM_CTS) != 0)
+               retval |= Cts;
+       if ((signals & TIOCM_DSR) != 0)
+               retval |= Dsr;
+       if ((signals & TIOCM_DTR) != 0)
+               retval |= Dtr;
+       if ((signals & TIOCM_RTS) != 0)
+               retval |= Rts;
+
+       return retval;
+}
+
+MonoSerialSignal
+get_signals (int fd, gint32 *error)
+{
+       int signals;
+
+       *error = 0;
+       
+       if (ioctl (fd, TIOCMGET, &signals) == -1) {
+               *error = -1;
+               return NoneSignal;
+       }
+       
+       return get_mono_signal_codes (signals);
+}
+
+gint32
+set_signal (int fd, MonoSerialSignal signal, gboolean value)
+{
+       int signals, expected, activated;
+
+       expected = get_signal_code (signal);
+       if (ioctl (fd, TIOCMGET, &signals) == -1)
+               return -1;
+       
+       activated = (signals & expected) != 0;
+       if (activated == value) /* Already set */
+               return 1;
+       
+       if (value)
+               signals |= expected;
+       else
+               signals &= ~expected;
+       
+       if (ioctl (fd, TIOCMSET, &signals) == -1)
+               return -1;
+       
+       return 1;
+}
+
+gboolean
+poll_serial (int fd, gint32 *error, int timeout)
+{
+       struct pollfd pinfo;
+       
+       *error = 0;
+       
+       pinfo.fd = fd;
+       pinfo.events = POLLIN;
+       pinfo.revents = 0;
+
+       while (poll (&pinfo, 1, timeout) == -1 && errno == EINTR) {
+               /* EINTR is an OK condition, we should not throw in the upper layer an IOException */
+               if (errno != EINTR){
+                       *error = -1;
+                       return FALSE;
+               }
+       }
+
+       return (pinfo.revents & POLLIN) != 0 ? 1 : 0;
+}
+
 /*
  * mono internals should not be used here.
  * this serial stuff needs to be implemented with icalls.