tty-handler: Add baud rate setting

Add local-tty-baud setting for configuration file.
Optionally change tty baud rate using termios.

For errors, use prints to stderr where warn/error calls are not
appropriate (i.e. errno hasn't been set by the error-causing call).

Fixes openbmc/openbmc#992.

Change-Id: Ia9226d1666f2626b2c04f30297755d93aee4994e
Signed-off-by: Xo Wang <xow@google.com>
diff --git a/tty-handler.c b/tty-handler.c
index 00ada7a..e640354 100644
--- a/tty-handler.c
+++ b/tty-handler.c
@@ -21,7 +21,9 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
+#include <termios.h>
 
 #include "console-server.h"
 
@@ -32,6 +34,11 @@
 	int		fd;
 };
 
+struct terminal_speed_name {
+	speed_t		speed;
+	const char	*name;
+};
+
 static struct tty_handler *to_tty_handler(struct handler *handler)
 {
 	return container_of(handler, struct tty_handler, handler);
@@ -59,11 +66,88 @@
 	return POLLER_OK;
 }
 
+static int baud_string_to_speed(speed_t *speed, const char *baud_string) {
+	const struct terminal_speed_name terminal_speeds[] = {
+		{ B50, "50" },
+		{ B75, "75" },
+		{ B110, "110" },
+		{ B134, "134" },
+		{ B150, "150" },
+		{ B200, "200" },
+		{ B300, "300" },
+		{ B600, "600" },
+		{ B1200, "1200" },
+		{ B1800, "1800" },
+		{ B2400, "2400" },
+		{ B4800, "4800" },
+		{ B9600, "9600" },
+		{ B19200, "19200" },
+		{ B38400, "38400" },
+		{ B57600, "57600" },
+		{ B115200, "115200" },
+		{ B230400, "230400" },
+		{ B460800, "460800" },
+		{ B500000, "500000" },
+		{ B576000, "576000" },
+		{ B921600, "921600" },
+		{ B1000000, "1000000" },
+		{ B1152000, "1152000" },
+		{ B1500000, "1500000" },
+		{ B2000000, "2000000" },
+		{ B2500000, "2500000" },
+		{ B3000000, "3000000" },
+		{ B3500000, "3500000" },
+		{ B4000000, "4000000" },
+	};
+	const size_t num_terminal_speeds = sizeof(terminal_speeds) /
+		sizeof(struct terminal_speed_name);
+	size_t i;
+
+	for (i = 0; i < num_terminal_speeds; i++) {
+		if (strcmp(baud_string, terminal_speeds[i].name) == 0) {
+			*speed = terminal_speeds[i].speed;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static int set_terminal_baud(struct tty_handler *th, const char *tty_name,
+		const char *desired_baud) {
+	struct termios term_options;
+	speed_t speed;
+
+	if (baud_string_to_speed(&speed, desired_baud) != 0) {
+		fprintf(stderr, "%s is not a valid baud rate for terminal %s\n",
+				desired_baud, tty_name);
+		return -1;
+	}
+
+	if (tcgetattr(th->fd, &term_options) < 0) {
+		warn("Can't get config for %s", tty_name);
+		return -1;
+	}
+
+	if (cfsetspeed(&term_options, speed) < 0) {
+		warn("Couldn't set speeds for %s", tty_name);
+		return -1;
+	}
+
+	if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
+		warn("Couldn't commit terminal options for %s", tty_name);
+		return -1;
+	}
+	printf("Set %s terminal baud rate to %s\n", tty_name, desired_baud);
+
+	return 0;
+}
+
 static int tty_init(struct handler *handler, struct console *console,
 		struct config *config __attribute__((unused)))
 {
 	struct tty_handler *th = to_tty_handler(handler);
 	const char *tty_name;
+	const char *tty_baud;
 	char *tty_path;
 	int rc, flags;
 
@@ -89,6 +173,12 @@
 	flags |= FNDELAY;
 	fcntl(th->fd, F_SETFL, flags);
 
+	tty_baud = config_get_value(config, "local-tty-baud");
+	if (tty_baud != NULL)
+		if (set_terminal_baud(th, tty_name, tty_baud) != 0)
+			fprintf(stderr, "Couldn't set baud rate for %s to %s\n",
+					tty_name, tty_baud);
+
 	th->poller = console_register_poller(console, handler, tty_poll,
 			th->fd, POLLIN, NULL);
 	th->console = console;