blob: b9a5afb3a02d374a923ce8efe533c770de1c5477 [file] [log] [blame]
Jeremy Kerrbc1e8932016-04-28 12:27:30 +08001/**
2 * Copyright © 2016 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define _GNU_SOURCE
18
19#include <assert.h>
20#include <err.h>
21#include <fcntl.h>
22#include <stdio.h>
23#include <stdlib.h>
Xo Wangc5ef8ea2017-04-17 16:20:43 -070024#include <string.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080025#include <unistd.h>
Xo Wangc5ef8ea2017-04-17 16:20:43 -070026#include <termios.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080027
28#include "console-server.h"
29
30struct tty_handler {
31 struct handler handler;
32 struct console *console;
33 struct poller *poller;
34 int fd;
35};
36
Xo Wangc5ef8ea2017-04-17 16:20:43 -070037struct terminal_speed_name {
38 speed_t speed;
39 const char *name;
40};
41
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080042static struct tty_handler *to_tty_handler(struct handler *handler)
43{
44 return container_of(handler, struct tty_handler, handler);
45}
46
47static enum poller_ret tty_poll(struct handler *handler,
48 int events, void __attribute__((unused)) *data)
49{
50 struct tty_handler *th = to_tty_handler(handler);
51 uint8_t buf[4096];
52 ssize_t len;
53
54 if (!(events & POLLIN))
55 return POLLER_OK;
56
57 len = read(th->fd, buf, sizeof(buf));
58 if (len <= 0) {
59 th->poller = NULL;
60 close(th->fd);
61 return POLLER_REMOVE;
62 }
63
64 console_data_out(th->console, buf, len);
65
66 return POLLER_OK;
67}
68
Xo Wangc5ef8ea2017-04-17 16:20:43 -070069static int baud_string_to_speed(speed_t *speed, const char *baud_string) {
70 const struct terminal_speed_name terminal_speeds[] = {
71 { B50, "50" },
72 { B75, "75" },
73 { B110, "110" },
74 { B134, "134" },
75 { B150, "150" },
76 { B200, "200" },
77 { B300, "300" },
78 { B600, "600" },
79 { B1200, "1200" },
80 { B1800, "1800" },
81 { B2400, "2400" },
82 { B4800, "4800" },
83 { B9600, "9600" },
84 { B19200, "19200" },
85 { B38400, "38400" },
86 { B57600, "57600" },
87 { B115200, "115200" },
88 { B230400, "230400" },
89 { B460800, "460800" },
90 { B500000, "500000" },
91 { B576000, "576000" },
92 { B921600, "921600" },
93 { B1000000, "1000000" },
94 { B1152000, "1152000" },
95 { B1500000, "1500000" },
96 { B2000000, "2000000" },
97 { B2500000, "2500000" },
98 { B3000000, "3000000" },
99 { B3500000, "3500000" },
100 { B4000000, "4000000" },
101 };
102 const size_t num_terminal_speeds = sizeof(terminal_speeds) /
103 sizeof(struct terminal_speed_name);
104 size_t i;
105
106 for (i = 0; i < num_terminal_speeds; i++) {
107 if (strcmp(baud_string, terminal_speeds[i].name) == 0) {
108 *speed = terminal_speeds[i].speed;
109 return 0;
110 }
111 }
112 return -1;
113}
114
115static int set_terminal_baud(struct tty_handler *th, const char *tty_name,
116 const char *desired_baud) {
117 struct termios term_options;
118 speed_t speed;
119
120 if (baud_string_to_speed(&speed, desired_baud) != 0) {
121 fprintf(stderr, "%s is not a valid baud rate for terminal %s\n",
122 desired_baud, tty_name);
123 return -1;
124 }
125
126 if (tcgetattr(th->fd, &term_options) < 0) {
127 warn("Can't get config for %s", tty_name);
128 return -1;
129 }
130
131 if (cfsetspeed(&term_options, speed) < 0) {
132 warn("Couldn't set speeds for %s", tty_name);
133 return -1;
134 }
135
136 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
137 warn("Couldn't commit terminal options for %s", tty_name);
138 return -1;
139 }
140 printf("Set %s terminal baud rate to %s\n", tty_name, desired_baud);
141
142 return 0;
143}
144
Xo Wang81408bd2017-04-18 16:43:07 -0700145static int make_terminal_raw(struct tty_handler *th, const char *tty_name) {
146 struct termios term_options;
147
148 if (tcgetattr(th->fd, &term_options) < 0) {
149 warn("Can't get config for %s", tty_name);
150 return -1;
151 }
152
153 /* Disable various input and output processing including character
154 * translation, line edit (canonical) mode, flow control, and special signal
155 * generating characters. */
156 cfmakeraw(&term_options);
157
158 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
159 warn("Couldn't commit terminal options for %s", tty_name);
160 return -1;
161 }
162 printf("Set %s for raw byte handling\n", tty_name);
163
164 return 0;
165}
166
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800167static int tty_init(struct handler *handler, struct console *console,
168 struct config *config __attribute__((unused)))
169{
170 struct tty_handler *th = to_tty_handler(handler);
171 const char *tty_name;
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700172 const char *tty_baud;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800173 char *tty_path;
174 int rc, flags;
175
176 tty_name = config_get_value(config, "local-tty");
177 if (!tty_name)
178 return -1;
179
180 rc = asprintf(&tty_path, "/dev/%s", tty_name);
181 if (!rc)
182 return -1;
183
184 th->fd = open(tty_path, O_RDWR);
185 if (th->fd < 0) {
186 warn("Can't open %s; disabling local tty", tty_name);
187 free(tty_path);
188 return -1;
189 }
190
191 free(tty_path);
192
193 /* initial tty setup */
194 flags = fcntl(th->fd, F_GETFL, 0);
195 flags |= FNDELAY;
196 fcntl(th->fd, F_SETFL, flags);
197
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700198 tty_baud = config_get_value(config, "local-tty-baud");
199 if (tty_baud != NULL)
200 if (set_terminal_baud(th, tty_name, tty_baud) != 0)
201 fprintf(stderr, "Couldn't set baud rate for %s to %s\n",
202 tty_name, tty_baud);
203
Xo Wang81408bd2017-04-18 16:43:07 -0700204 if (make_terminal_raw(th, tty_name) != 0)
205 fprintf(stderr, "Couldn't make %s a raw terminal\n", tty_name);
206
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800207 th->poller = console_register_poller(console, handler, tty_poll,
208 th->fd, POLLIN, NULL);
209 th->console = console;
210
211 return 0;
212}
213
214static int tty_data(struct handler *handler, uint8_t *buf, size_t len)
215{
216 struct tty_handler *th = to_tty_handler(handler);
217 return write_buf_to_fd(th->fd, buf, len);
218}
219
220static void tty_fini(struct handler *handler)
221{
222 struct tty_handler *th = to_tty_handler(handler);
223 if (th->poller)
224 console_unregister_poller(th->console, th->poller);
225 close(th->fd);
226}
227
228static struct tty_handler tty_handler = {
229 .handler = {
230 .name = "tty",
231 .init = tty_init,
232 .data_in = tty_data,
233 .fini = tty_fini,
234 },
235};
236
237console_register_handler(&tty_handler.handler);
238