From 87b39637ffbae5ac08fd585b64c79629805c96ed Mon Sep 17 00:00:00 2001
From: Joerg Wunsch <j@uriah.heep.sax.de>
Date: Mon, 7 Mar 2022 23:20:50 +0100
Subject: [PATCH] Implement nonstandard baudrate handling on MacOS

Alas, MacOS doesn't handle nonstandard baud rates like other systems
in regular tcsetattr() calls. Instead, they invented a new ioctl
(IOSSIOSPEED). So, if we notice we are going to configure a
nonstandard rate on MacOS, issue that ioctl after configuring
everything else using tcsetattr().
---
 build.sh        |  2 +-
 src/ser_posix.c | 38 ++++++++++++++++++++++++++++++++++----
 2 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/build.sh b/build.sh
index baea4516..4a2cbc5a 100755
--- a/build.sh
+++ b/build.sh
@@ -59,7 +59,7 @@ case "${ostype}" in
 	;;
 esac
 
-cmake ${build_flags} ${extra_enable} -D CMAKE_BUILD_TYPE=${build_type} -B build_${ostype} ||\
+cmake ${build_flags} ${extra_enable} -D CMAKE_VERBOSE_MAKEFILE:BOOL=ON -D CMAKE_BUILD_TYPE=${build_type} -B build_${ostype} ||\
     { echo "CMake failed."; exit 1; }
 cmake --build build_${ostype} ||\
     { echo "Build failed."; exit 1; }
diff --git a/src/ser_posix.c b/src/ser_posix.c
index d2b3031b..50bd2cfe 100644
--- a/src/ser_posix.c
+++ b/src/ser_posix.c
@@ -28,6 +28,7 @@
 #include "ac_cfg.h"
 
 #include <ctype.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -42,6 +43,10 @@
 #include <termios.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+# include <IOKit/serial/ioss.h>
+#endif
+
 #include "avrdude.h"
 #include "libavrdude.h"
 
@@ -78,10 +83,12 @@ static struct baud_mapping baud_lookup_table [] = {
 static struct termios original_termios;
 static int saved_original_termios;
 
-static speed_t serial_baud_lookup(long baud)
+static speed_t serial_baud_lookup(long baud, bool *nonstandard)
 {
   struct baud_mapping *map = baud_lookup_table;
 
+  *nonstandard = false;
+
   while (map->baud) {
     if (map->baud == baud)
       return map->speed;
@@ -95,6 +102,8 @@ static speed_t serial_baud_lookup(long baud)
   avrdude_message(MSG_NOTICE, "%s: serial_baud_lookup(): Using non-standard baud rate: %ld",
               progname, baud);
 
+  *nonstandard = true;
+
   return baud;
 }
 
@@ -102,7 +111,8 @@ static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cfla
 {
   int rc;
   struct termios termios;
-  speed_t speed = serial_baud_lookup (baud);
+  bool nonstandard;
+  speed_t speed = serial_baud_lookup (baud, &nonstandard);
   
   if (!isatty(fd->ifd))
     return -ENOTTY;
@@ -146,8 +156,16 @@ static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cfla
   termios.c_iflag &= ~PARMRK;
 #endif /* PARMRK */
 
-  cfsetospeed(&termios, speed);
-  cfsetispeed(&termios, speed);
+  // MacOS doesn't handle nonstandard baudrate values in
+  // normal tcsetattr(), sigh.
+#ifdef __APPLE__
+  if (!nonstandard) {
+#endif
+    cfsetospeed(&termios, speed);
+    cfsetispeed(&termios, speed);
+#ifdef __APPLE__
+  }
+#endif
 
   termios.c_cflag &= ~CSIZE;
   if (cflags & SERIAL_CS8) {
@@ -204,6 +222,17 @@ static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cfla
     return -errno;
   }
 
+#ifdef __APPLE__
+  // handle nonstandard speed values the MacOS way
+  if (nonstandard) {
+    if (ioctl(fd->ifd, IOSSIOSPEED, &speed) < 0) {
+      avrdude_message(MSG_INFO, "%s: ser_setparams(): ioctrl(IOSSIOSPEED) failed\n",
+            progname);
+      return -errno;
+    }
+  }
+#endif // __APPLE__
+
   tcflush(fd->ifd, TCIFLUSH);
   
   return 0;
@@ -564,3 +593,4 @@ struct serial_device serial_serdev =
 struct serial_device *serdev = &serial_serdev;
 
 #endif  /* WIN32 */
+