From d53cfabd8f11ba5fb239efacfd385e51733d9d54 Mon Sep 17 00:00:00 2001
From: Nathan Fearnley <nfearnley@gmail.com>
Date: Thu, 4 Dec 2014 15:34:53 -0500
Subject: Adds custom baud rate support for serial ports on Mac OS X

---
 dlls/ntdll/serial.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 128 insertions(+), 1 deletion(-)

diff --git a/dlls/ntdll/serial.c b/dlls/ntdll/serial.c
index b03ac61..7083783 100644
--- a/dlls/ntdll/serial.c
+++ b/dlls/ntdll/serial.c
@@ -81,6 +81,10 @@
 #define	TIOCINQ FIONREAD
 #endif
 
+#if defined (__APPLE__)
+#define IOSSIOSPEED 0x80045402
+#endif
+
 WINE_DEFAULT_DEBUG_CHANNEL(comm);
 
 static const char* iocode2str(DWORD ioc)
@@ -429,10 +433,65 @@ static NTSTATUS purge(int fd, DWORD flags)
     return STATUS_SUCCESS;
 }
 
+#if defined(__APPLE__)
+static BOOL is_standard_baud_rate(speed_t baud_rate)
+{
+    BOOL standardBaudRate = FALSE;
+    switch (baud_rate)
+    {
+    case 0:
+    case 50:
+    case 75:
+    case 110:
+    case 134:
+    case 150:
+    case 200:
+    case 300:
+    case 600:
+    case 1200:
+    case 1800:
+    case 2400:
+    case 4800:
+    case 9600:
+    case 19200:
+    case 38400:
+#ifdef B57600
+    case 57600:
+#endif
+#ifdef B115200
+    case 115200:
+#endif
+#ifdef B230400
+    case 230400:
+#endif
+#ifdef B460800
+    case 460800:
+#endif
+        standardBaudRate = TRUE;
+    default:
+        standardBaudRate = FALSE;
+    }
+    return standardBaudRate;
+}
+
+static NTSTATUS set_custom_baud_rate(int fd, speed_t baud_rate)
+{
+    if (ioctl(fd, IOSSIOSPEED, baud_rate) == -1)
+    {
+        ERR("IOSSIOSPEED error '%s'\n", strerror(errno));
+        return FILE_GetNtStatus();
+    }
+    return STATUS_SUCCESS;
+}
+#endif
+
 static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr)
 {
     struct termios port;
-
+    #if defined (__APPLE__)
+    BOOL standardBaudRate = TRUE;
+    #endif
+    
     if (tcgetattr(fd, &port) == -1)
     {
         ERR("tcgetattr error '%s'\n", strerror(errno));
@@ -501,11 +560,20 @@ static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr)
             cfsetospeed( &port, B38400 );
         }
         break;
+#elif defined (__APPLE__) /* Custom Baud Rate for Mac OS X */
+        standardBaudRate = FALSE;
+        break;
 #else     /* Don't have linux/serial.h or lack TIOCSSERIAL */
         ERR("baudrate %d\n", sbr->BaudRate);
         return STATUS_NOT_SUPPORTED;
 #endif    /* Don't have linux/serial.h or lack TIOCSSERIAL */
     }
+#if defined (__APPLE__) /* Custom Baud Rate for Mac OS X */
+    if (!standardBaudRate)
+    {
+        return set_custom_baud_rate(fd, &sbr->BaudRate);
+    }
+#endif
     cfsetispeed( &port, cfgetospeed(&port) );
     if (tcsetattr(fd, TCSANOW, &port) == -1)
     {
@@ -580,11 +648,30 @@ static NTSTATUS set_handflow(int fd, const SERIAL_HANDFLOW* shf)
         port.c_iflag |= IXON;
     else
         port.c_iflag &= ~IXON;
+        
+    #if defined (__APPLE__)
+    BOOL standardBaudRate = is_standard_baud_rate(port.c_ispeed) && is_standard_baud_rate(port.c_ospeed);
+    speed_t customRate = 0;
+    if (!standardBaudRate) /* tcsetattr doesn't support custom rates, so temporarily set a standard rate */
+    {
+        customRate = port.c_ispeed;
+        port.c_ispeed = 9600;
+        port.c_ospeed = 9600;
+    }
+    #endif
+        
     if (tcsetattr(fd, TCSANOW, &port) == -1)
     {
         ERR("tcsetattr error '%s'\n", strerror(errno));
         return FILE_GetNtStatus();
     }
+    
+    #if defined (__APPLE__)
+    if (!standardBaudRate)  /* tcsetattr doesn't support custom rates, so temporarily set a standard rate */
+    {
+        return set_custom_baud_rate(fd, &sbr->BaudRate);
+    }
+    #endif
 
     return STATUS_SUCCESS;
 }
@@ -692,12 +779,32 @@ static NTSTATUS set_line_control(int fd, const SERIAL_LINE_CONTROL* slc)
         FIXME("StopBits %d is not supported\n", stopbits);
         return STATUS_NOT_SUPPORTED;
     }
+    
+    #if defined (__APPLE__)
+    BOOL standardBaudRate = is_standard_baud_rate(port.c_ispeed) && is_standard_baud_rate(port.c_ospeed);
+    speed_t customRate = 0;
+    if (!standardBaudRate) /* tcsetattr doesn't support custom rates, so temporarily set a standard rate */
+    {
+        customRate = port.c_ispeed;
+        port.c_ispeed = 9600;
+        port.c_ospeed = 9600;
+    }
+    #endif
+    
     /* otherwise it hangs with pending input*/
     if (tcsetattr(fd, TCSANOW, &port) == -1)
     {
         ERR("tcsetattr error '%s'\n", strerror(errno));
         return FILE_GetNtStatus();
     }
+    
+    #if defined (__APPLE__)
+    if (!standardBaudRate)  /* tcsetattr doesn't support custom rates, so temporarily set a standard rate */
+    {
+        return set_custom_baud_rate(fd, &sbr->BaudRate);
+    }
+    #endif
+    
     return STATUS_SUCCESS;
 }
 
@@ -724,11 +831,31 @@ static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc)
     port.c_cc[VSTART] = sc->XonChar;
     port.c_cc[VSTOP ] = sc->XoffChar;
     
+        
+    #if defined (__APPLE__)
+    BOOL standardBaudRate = is_standard_baud_rate(port.c_ispeed) && is_standard_baud_rate(port.c_ospeed);
+    speed_t customRate = 0;
+    if (!standardBaudRate) /* tcsetattr doesn't support custom rates, so temporarily set a standard rate */
+    {
+        customRate = port.c_ispeed;
+        port.c_ispeed = 9600;
+        port.c_ospeed = 9600;
+    }
+    #endif
+    
     if (tcsetattr(fd, TCSANOW, &port) == -1)
     {
         ERR("tcsetattr error '%s'\n", strerror(errno));
         return FILE_GetNtStatus();
     }
+    
+    #if defined (__APPLE__)
+    if (!standardBaudRate)  /* tcsetattr doesn't support custom rates, so temporarily set a standard rate */
+    {
+        return set_custom_baud_rate(fd, &sbr->BaudRate);
+    }
+    #endif
+    
     return STATUS_SUCCESS;
 }
 
-- 
2.1.3

