X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=support%2Fserial.c;h=5e3d732c005170bbcd4052841fad7dc3b27b342c;hb=52e98abd6e5c6d10ddea91a529f7b1b2336e0696;hp=34d3943142d064db85829fb41129aef88ee0092d;hpb=4eb352bcb3ef7a71dc9ab62c5cd2d5e7598619f7;p=mono.git diff --git a/support/serial.c b/support/serial.c index 34d3943142d..5e3d732c005 100644 --- a/support/serial.c +++ b/support/serial.c @@ -5,13 +5,25 @@ * Author: Chris Toshok */ +#include "map.h" +#include "mph.h" + #include #include #include #include +#include +#if defined(HAVE_POLL_H) +#include +#elif defined(HAVE_SYS_POLL_H) #include +#endif #include -#include + +/* This is for ASYNC_*, serial_struct on linux */ +#if defined(HAVE_LINUX_SERIAL_H) +#include +#endif #include @@ -20,6 +32,20 @@ #include #endif +/* sys/time.h (for timeval) is required when using osx 10.3 (but not 10.4) */ +/* IOKit is a private framework in iOS, so exclude there */ +#if defined(__APPLE__) && !defined(HOST_IOS) && !defined(HOST_WATCHOS) && !defined(HOST_APPLETVOS) +#define HAVE_IOKIT 1 +#endif + +#if defined(HAVE_IOKIT) +#include +#include +#include +#include +#include +#endif + /* This is a copy of System.IO.Ports.Handshake */ typedef enum { NoneHandshake = 0, @@ -55,32 +81,40 @@ typedef enum { Rts = 16 /* Request to send */ } MonoSerialSignal; +/* + * Silence the compiler, we do not need these prototypes to be public, since these are only + * used by P/Invoke + */ + +int open_serial (char *devfile); +int close_serial (int unix_fd); +guint32 read_serial (int fd, guchar *buffer, int offset, int count); +int write_serial (int fd, guchar *buffer, int offset, int count, int timeout); +int discard_buffer (int fd, gboolean input); +gint32 get_bytes_in_buffer (int fd, gboolean input); +gboolean is_baud_rate_legal (int baud_rate); +int setup_baud_rate (int baud_rate, gboolean *custom_baud_rate); +gboolean set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStopBits stopBits, MonoHandshake handshake); +MonoSerialSignal get_signals (int fd, gint32 *error); +gint32 set_signal (int fd, MonoSerialSignal signal, gboolean value); +int breakprop (int fd); +gboolean poll_serial (int fd, gint32 *error, int timeout); +void *list_serial_devices (void); + int -open_serial (char* devfile) +open_serial (char *devfile) { int fd; - struct termios newtio; - - fd = open (devfile, O_RDWR); - - if (fd == -1) - return -1; - - newtio.c_cflag = CLOCAL | CREAD; - newtio.c_iflag = 0; - newtio.c_oflag = 0; - newtio.c_lflag = 0; - - tcflush(fd, TCIOFLUSH); - tcsetattr(fd,TCSANOW,&newtio); + fd = open (devfile, O_RDWR | O_NOCTTY | O_NONBLOCK); return fd; } -void +int close_serial (int unix_fd) { - close (unix_fd); + // Linus writes: do not retry close after EINTR + return close (unix_fd); } guint32 @@ -93,34 +127,59 @@ read_serial (int fd, guchar *buffer, int offset, int count) return (guint32) n; } -void +int write_serial (int fd, guchar *buffer, int offset, int count, int timeout) { + struct pollfd pinfo; guint32 n; - struct pollfd ufd; - - ufd.fd = fd; - ufd.events = POLLHUP | POLLOUT | POLLERR; - - poll (&ufd, 1, timeout); - - if ((ufd.revents & POLLOUT) != POLLOUT) { - return; + pinfo.fd = fd; + pinfo.events = POLLOUT; + pinfo.revents = POLLOUT; + + n = count; + + while (n > 0) + { + ssize_t t; + + if (timeout != 0) { + int c; + + while ((c = poll (&pinfo, 1, timeout)) == -1 && errno == EINTR) + ; + if (c == -1) + return -1; + } + + do { + t = write (fd, buffer + offset, n); + } while (t == -1 && errno == EINTR); + + if (t < 0) + return -1; + + offset += t; + n -= t; } - n = write (fd, buffer + offset, count); + return 0; } -void +int discard_buffer (int fd, gboolean input) { - tcflush(fd, input ? TCIFLUSH : TCOFLUSH); + return tcflush(fd, input ? TCIFLUSH : TCOFLUSH); } gint32 get_bytes_in_buffer (int fd, gboolean input) { +#if defined(__HAIKU__) + /* FIXME: Haiku doesn't support TIOCOUTQ nor FIONREAD on fds */ + return -1; +#define TIOCOUTQ 0 +#endif gint32 retval; if (ioctl (fd, input ? FIONREAD : TIOCOUTQ, &retval) == -1) { @@ -131,76 +190,145 @@ get_bytes_in_buffer (int fd, gboolean input) } gboolean -set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStopBits stopBits, MonoHandshake handshake) +is_baud_rate_legal (int baud_rate) { - struct termios newtio; + gboolean ignore = FALSE; + return setup_baud_rate (baud_rate, &ignore) != -1; +} - tcgetattr (fd, &newtio); - - switch (baud_rate) { - case 230400: baud_rate = B230400; break; - case 115200: baud_rate = B115200; break; - case 57600: baud_rate = B57600; break; - case 38400: baud_rate = B38400; break; - case 19200: baud_rate = B19200; break; - case 9600: baud_rate = B9600; break; - case 4800: baud_rate = B4800; break; - case 2400: baud_rate = B2400; break; - case 1800: baud_rate = B1800; break; - case 1200: baud_rate = B1200; break; - case 600: baud_rate = B600; break; - case 300: baud_rate = B300; break; - case 200: baud_rate = B200; break; - case 150: baud_rate = B150; break; - case 134: baud_rate = B134; break; - case 110: baud_rate = B110; break; - case 75: baud_rate = B75; break; - case 50: - case 0: - default: +int +setup_baud_rate (int baud_rate, gboolean *custom_baud_rate) +{ + switch (baud_rate) + { +/*Some values are not defined on OSX and *BSD */ +#if defined(B921600) + case 921600: + baud_rate = B921600; + break; +#endif +#if defined(B460800) + case 460800: + baud_rate = B460800; + break; +#endif + case 230400: + baud_rate = B230400; + break; + case 115200: + baud_rate = B115200; + break; + case 57600: + baud_rate = B57600; + break; + case 38400: + baud_rate = B38400; + break; + case 19200: + baud_rate = B19200; + break; + case 9600: baud_rate = B9600; break; - } - - switch (parity) { - case NoneParity: /* None */ - newtio.c_iflag |= IGNPAR; - newtio.c_cflag &= ~(PARENB | PARODD); + case 4800: + baud_rate = B4800; break; - case Odd: /* Odd */ - newtio.c_iflag &= ~IGNPAR; - newtio.c_cflag |= PARENB | PARODD; + case 2400: + baud_rate = B2400; break; - case Even: /* Even */ - newtio.c_iflag &= ~(IGNPAR | PARODD); - newtio.c_cflag |= PARENB; + case 1800: + baud_rate = B1800; break; - case Mark: /* Mark */ - /* XXX unhandled */ + case 1200: + baud_rate = B1200; break; - case Space: /* Space */ - /* XXX unhandled */ + case 600: + baud_rate = B600; + break; + case 300: + baud_rate = B300; + break; + case 200: + baud_rate = B200; + break; + case 150: + baud_rate = B150; + break; + case 134: + baud_rate = B134; + break; + case 110: + baud_rate = B110; + break; + case 75: + baud_rate = B75; + break; + case 50: +#ifdef B50 + baud_rate = B50; +#else + baud_rate = -1; + break; +#endif + case 0: +#ifdef B0 + baud_rate = B0; +#else + baud_rate = -1; +#endif + break; + default: + *custom_baud_rate = TRUE; break; } + return baud_rate; +} + +gboolean +set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStopBits stopBits, MonoHandshake handshake) +{ + struct termios newtio; + gboolean custom_baud_rate = FALSE; + + if (tcgetattr (fd, &newtio) == -1) + return FALSE; + + newtio.c_cflag |= (CLOCAL | CREAD); + newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN ); + newtio.c_oflag &= ~(OPOST); + newtio.c_iflag = IGNBRK; + /* setup baudrate */ + baud_rate = setup_baud_rate (baud_rate, &custom_baud_rate); + + /* char lenght */ newtio.c_cflag &= ~CSIZE; - switch (dataBits) { - case 5: newtio.c_cflag |= CS5; break; - case 6: newtio.c_cflag |= CS6; break; - case 7: newtio.c_cflag |= CS7; break; + switch (dataBits) + { + case 5: + newtio.c_cflag |= CS5; + break; + case 6: + newtio.c_cflag |= CS6; + break; + case 7: + newtio.c_cflag |= CS7; + break; case 8: default: newtio.c_cflag |= CS8; break; } - newtio.c_cflag &= ~CSTOPB; - switch (stopBits) { + /* stopbits */ + switch (stopBits) + { case NoneStopBits: /* Unhandled */ break; case One: /* One */ /* do nothing, the default is one stop bit */ + newtio.c_cflag &= ~CSTOPB; break; case Two: /* Two */ newtio.c_cflag |= CSTOPB; @@ -210,12 +338,39 @@ set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStop break; } - newtio.c_iflag &= ~IXOFF; - newtio.c_oflag &= ~IXON; + /* parity */ + newtio.c_iflag &= ~(INPCK | ISTRIP ); + + switch (parity) + { + case NoneParity: /* None */ + newtio.c_cflag &= ~(PARENB | PARODD); + break; + + case Odd: /* Odd */ + newtio.c_cflag |= PARENB | PARODD; + break; + + case Even: /* Even */ + newtio.c_cflag &= ~(PARODD); + newtio.c_cflag |= (PARENB); + break; + + case Mark: /* Mark */ + /* XXX unhandled */ + break; + case Space: /* Space */ + /* XXX unhandled */ + break; + } + + newtio.c_iflag &= ~(IXOFF | IXON); #ifdef CRTSCTS newtio.c_cflag &= ~CRTSCTS; #endif /* def CRTSCTS */ - switch (handshake) { + + switch (handshake) + { case NoneHandshake: /* None */ /* do nothing */ break; @@ -230,18 +385,61 @@ set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStop #endif /* def CRTSCTS */ /* fall through */ case XOnXOff: /* XOnXOff */ - newtio.c_iflag |= IXOFF; - // newtio.c_oflag |= IXON; + newtio.c_iflag |= IXOFF | IXON; break; } - - if (cfsetospeed (&newtio, baud_rate) < 0 || cfsetispeed (&newtio, baud_rate) < 0 || - tcsetattr (fd, TCSADRAIN, &newtio) < 0) + + if (custom_baud_rate == FALSE) { + if (cfsetospeed (&newtio, baud_rate) < 0 || cfsetispeed (&newtio, baud_rate) < 0) + return FALSE; + } else { +#if __linux__ || defined(HAVE_IOKIT) + + /* On Linux to set a custom baud rate, we must set the + * "standard" baud_rate to 38400. On Apple we set it purely + * so that tcsetattr has something to use (and report back later), but + * the Apple specific API is still opaque to these APIs, see: + * https://developer.apple.com/library/mac/samplecode/SerialPortSample/Listings/SerialPortSample_SerialPortSample_c.html#//apple_ref/doc/uid/DTS10000454-SerialPortSample_SerialPortSample_c-DontLinkElementID_4 + */ + if (cfsetospeed (&newtio, B38400) < 0 || cfsetispeed (&newtio, B38400) < 0) + return FALSE; +#endif + } + + if (tcsetattr (fd, TCSANOW, &newtio) < 0) return FALSE; + if (custom_baud_rate == TRUE){ +#if defined(HAVE_LINUX_SERIAL_H) + struct serial_struct ser; + + if (ioctl (fd, TIOCGSERIAL, &ser) < 0) + { + return FALSE; + } + + ser.custom_divisor = ser.baud_base / baud_rate; + ser.flags &= ~ASYNC_SPD_MASK; + ser.flags |= ASYNC_SPD_CUST; + + if (ioctl (fd, TIOCSSERIAL, &ser) < 0) + { + return FALSE; + } +#elif defined(HAVE_IOKIT) + speed_t speed = baud_rate; + if (ioctl(fd, IOSSIOSPEED, &speed) == -1) + return FALSE; +#else + /* Don't know how to set custom baud rate on this platform. */ + return FALSE; +#endif + } + return TRUE; } + static gint32 get_signal_code (MonoSerialSignal signal) { @@ -322,6 +520,12 @@ set_signal (int fd, MonoSerialSignal signal, gboolean value) return 1; } +int +breakprop (int fd) +{ + return tcsendbreak (fd, 0); +} + gboolean poll_serial (int fd, gint32 *error, int timeout) { @@ -333,12 +537,12 @@ poll_serial (int fd, gint32 *error, int timeout) pinfo.events = POLLIN; pinfo.revents = 0; - if (poll (&pinfo, 1, timeout) == -1) { + 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) + if (errno != EINTR){ + *error = -1; return FALSE; - *error = -1; - return FALSE; + } } return (pinfo.revents & POLLIN) != 0 ? 1 : 0;