serial.c: round baudrates to valid ones

MSDN says: "The baud rate at which the communications device
operates. This member can be an actual baud rate value, or one of
the following indexes." But it is not specified what happens for
different values, so we round down to valid ones (or to the minimum
of 9600) by reusing the existing struct baudentry sp_baudtable[] and
the new function round_baud().

Do similarly on non-Windows where arbitrary values are not allowed
anyway.

Corresponding to flashrom svn r1660.

Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
diff --git a/serial.c b/serial.c
index 18562be..9a759ae 100644
--- a/serial.c
+++ b/serial.c
@@ -47,62 +47,95 @@
 	exit(1);
 }
 
-#ifndef _WIN32
+#ifdef _WIN32
+struct baudentry {
+	DWORD flag;
+	unsigned int baud;
+};
+#define BAUDENTRY(baud) { CBR_##baud, baud },
+#else
 struct baudentry {
 	int flag;
 	unsigned int baud;
 };
-
-/* I'd like if the C preprocessor could have directives in macros */
 #define BAUDENTRY(baud) { B##baud, baud },
+#endif
+
+/* I'd like if the C preprocessor could have directives in macros.
+ * See TERMIOS(3) and http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx and
+ * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/uapi/asm-generic/termbits.h */
 static const struct baudentry sp_baudtable[] = {
-	BAUDENTRY(9600)
+	BAUDENTRY(9600) /* unconditional default */
+#if defined(B19200) || defined(CBR_19200)
 	BAUDENTRY(19200)
+#endif
+#if defined(B38400) || defined(CBR_38400)
 	BAUDENTRY(38400)
+#endif
+#if defined(B57600) || defined(CBR_57600)
 	BAUDENTRY(57600)
+#endif
+#if defined(B115200) || defined(CBR_115200)
 	BAUDENTRY(115200)
-#ifdef B230400
+#endif
+#if defined(B230400) || defined(CBR_230400)
 	BAUDENTRY(230400)
 #endif
-#ifdef B460800
+#if defined(B460800) || defined(CBR_460800)
 	BAUDENTRY(460800)
 #endif
-#ifdef B500000
+#if defined(B500000) || defined(CBR_500000)
 	BAUDENTRY(500000)
 #endif
-#ifdef B576000
+#if defined(B576000) || defined(CBR_576000)
 	BAUDENTRY(576000)
 #endif
-#ifdef B921600
+#if defined(B921600) || defined(CBR_921600)
 	BAUDENTRY(921600)
 #endif
-#ifdef B1000000
+#if defined(B1000000) || defined(CBR_1000000)
 	BAUDENTRY(1000000)
 #endif
-#ifdef B1152000
+#if defined(B1152000) || defined(CBR_1152000)
 	BAUDENTRY(1152000)
 #endif
-#ifdef B1500000
+#if defined(B1500000) || defined(CBR_1500000)
 	BAUDENTRY(1500000)
 #endif
-#ifdef B2000000
+#if defined(B2000000) || defined(CBR_2000000)
 	BAUDENTRY(2000000)
 #endif
-#ifdef B2500000
+#if defined(B2500000) || defined(CBR_2500000)
 	BAUDENTRY(2500000)
 #endif
-#ifdef B3000000
+#if defined(B3000000) || defined(CBR_3000000)
 	BAUDENTRY(3000000)
 #endif
-#ifdef B3500000
+#if defined(B3500000) || defined(CBR_3500000)
 	BAUDENTRY(3500000)
 #endif
-#ifdef B4000000
+#if defined(B4000000) || defined(CBR_4000000)
 	BAUDENTRY(4000000)
 #endif
 	{0, 0}			/* Terminator */
 };
-#endif
+
+const struct baudentry *round_baud(unsigned int baud)
+{
+	int i;
+	/* Round baud rate to next lower entry in sp_baudtable if it exists, else use the lowest entry. */
+	for (i = ARRAY_SIZE(sp_baudtable) - 2; i >= 0 ; i--) {
+		if (sp_baudtable[i].baud == baud)
+			return &sp_baudtable[i];
+
+		if (sp_baudtable[i].baud < baud) {
+			msg_pinfo("Warning: given baudrate %d rounded down to %d.\n",
+				  baud, sp_baudtable[i].baud);
+			return &sp_baudtable[i];
+		}
+	}
+	return &sp_baudtable[0];
+}
 
 /* Uses msg_perr to print the last system error.
  * Prints "Error: " followed first by \c msg and then by the description of the last error retrieved via
@@ -155,15 +188,8 @@
 		msg_perr_strerror("Could not fetch serial port configuration: ");
 		return SER_INV_FD;
 	}
-	switch (baud) {
-		case 9600: dcb.BaudRate = CBR_9600; break;
-		case 19200: dcb.BaudRate = CBR_19200; break;
-		case 38400: dcb.BaudRate = CBR_38400; break;
-		case 57600: dcb.BaudRate = CBR_57600; break;
-		case 115200: dcb.BaudRate = CBR_115200; break;
-		default: msg_perr("Error: Could not set baud rate: %s\n", strerror(errno));
-			 return SER_INV_FD;
-	}
+	const struct baudentry *entry = round_baud(baud);
+	dcb.BaudRate = entry->baud;
 	dcb.ByteSize = 8;
 	dcb.Parity = NOPARITY;
 	dcb.StopBits = ONESTOPBIT;
@@ -171,10 +197,15 @@
 		msg_perr_strerror("Could not change serial port configuration: ");
 		return SER_INV_FD;
 	}
+	if (!GetCommState(fd, &dcb)) {
+		msg_perr_strerror("Could not fetch serial port configuration: ");
+		return SER_INV_FD;
+	}
+	msg_pdbg("Baud rate is %ld.\n", dcb.BaudRate);
 	return fd;
 #else
 	struct termios options;
-	int fd, i;
+	int fd;
 	fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
 	if (fd < 0) {
 		msg_perr_strerror("Cannot open serial port: ");
@@ -182,18 +213,10 @@
 	}
 	fcntl(fd, F_SETFL, 0);
 	tcgetattr(fd, &options);
-	for (i = 0;; i++) {
-		if (sp_baudtable[i].baud == 0) {
-			close(fd);
-			msg_perr("Error: cannot configure for baudrate %d\n", baud);
-			return SER_INV_FD;
-		}
-		if (sp_baudtable[i].baud == baud) {
-			cfsetispeed(&options, sp_baudtable[i].flag);
-			cfsetospeed(&options, sp_baudtable[i].flag);
-			break;
-		}
-	}
+	const struct baudentry *entry = round_baud(baud);
+	cfsetispeed(&options, entry->flag);
+	cfsetospeed(&options, entry->flag);
+	msg_pdbg("Setting baud rate to %d.\n", entry->baud);
 	options.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
 	options.c_cflag |= (CS8 | CLOCAL | CREAD);
 	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);