xref: /openbmc/qemu/chardev/char-serial.c (revision bc5c4f21)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "qemu/osdep.h"
25 #include "qemu/sockets.h"
26 #include "io/channel-file.h"
27 #include "qapi/error.h"
28 
29 #ifdef _WIN32
30 #include "char-win.h"
31 #else
32 #include <sys/ioctl.h>
33 #include <termios.h>
34 #include "char-fd.h"
35 #endif
36 
37 #include "char-serial.h"
38 
39 #ifdef _WIN32
40 
41 static void qmp_chardev_open_serial(Chardev *chr,
42                                     ChardevBackend *backend,
43                                     bool *be_opened,
44                                     Error **errp)
45 {
46     ChardevHostdev *serial = backend->u.serial.data;
47 
48     win_chr_init(chr, serial->device, errp);
49 }
50 
51 #elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__)      \
52     || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
53     || defined(__GLIBC__)
54 
55 static void tty_serial_init(int fd, int speed,
56                             int parity, int data_bits, int stop_bits)
57 {
58     struct termios tty;
59     speed_t spd;
60 
61 #if 0
62     printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
63            speed, parity, data_bits, stop_bits);
64 #endif
65     tcgetattr(fd, &tty);
66 
67 #define check_speed(val) if (speed <= val) { spd = B##val; break; }
68     speed = speed * 10 / 11;
69     do {
70         check_speed(50);
71         check_speed(75);
72         check_speed(110);
73         check_speed(134);
74         check_speed(150);
75         check_speed(200);
76         check_speed(300);
77         check_speed(600);
78         check_speed(1200);
79         check_speed(1800);
80         check_speed(2400);
81         check_speed(4800);
82         check_speed(9600);
83         check_speed(19200);
84         check_speed(38400);
85         /* Non-Posix values follow. They may be unsupported on some systems. */
86         check_speed(57600);
87         check_speed(115200);
88 #ifdef B230400
89         check_speed(230400);
90 #endif
91 #ifdef B460800
92         check_speed(460800);
93 #endif
94 #ifdef B500000
95         check_speed(500000);
96 #endif
97 #ifdef B576000
98         check_speed(576000);
99 #endif
100 #ifdef B921600
101         check_speed(921600);
102 #endif
103 #ifdef B1000000
104         check_speed(1000000);
105 #endif
106 #ifdef B1152000
107         check_speed(1152000);
108 #endif
109 #ifdef B1500000
110         check_speed(1500000);
111 #endif
112 #ifdef B2000000
113         check_speed(2000000);
114 #endif
115 #ifdef B2500000
116         check_speed(2500000);
117 #endif
118 #ifdef B3000000
119         check_speed(3000000);
120 #endif
121 #ifdef B3500000
122         check_speed(3500000);
123 #endif
124 #ifdef B4000000
125         check_speed(4000000);
126 #endif
127         spd = B115200;
128     } while (0);
129 
130     cfsetispeed(&tty, spd);
131     cfsetospeed(&tty, spd);
132 
133     tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
134                      | INLCR | IGNCR | ICRNL | IXON);
135     tty.c_oflag |= OPOST;
136     tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
137     tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
138     switch (data_bits) {
139     default:
140     case 8:
141         tty.c_cflag |= CS8;
142         break;
143     case 7:
144         tty.c_cflag |= CS7;
145         break;
146     case 6:
147         tty.c_cflag |= CS6;
148         break;
149     case 5:
150         tty.c_cflag |= CS5;
151         break;
152     }
153     switch (parity) {
154     default:
155     case 'N':
156         break;
157     case 'E':
158         tty.c_cflag |= PARENB;
159         break;
160     case 'O':
161         tty.c_cflag |= PARENB | PARODD;
162         break;
163     }
164     if (stop_bits == 2) {
165         tty.c_cflag |= CSTOPB;
166     }
167 
168     tcsetattr(fd, TCSANOW, &tty);
169 }
170 
171 static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
172 {
173     FDChardev *s = FD_CHARDEV(chr);
174     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
175 
176     switch (cmd) {
177     case CHR_IOCTL_SERIAL_SET_PARAMS:
178         {
179             QEMUSerialSetParams *ssp = arg;
180             tty_serial_init(fioc->fd,
181                             ssp->speed, ssp->parity,
182                             ssp->data_bits, ssp->stop_bits);
183         }
184         break;
185     case CHR_IOCTL_SERIAL_SET_BREAK:
186         {
187             int enable = *(int *)arg;
188             if (enable) {
189                 tcsendbreak(fioc->fd, 1);
190             }
191         }
192         break;
193     case CHR_IOCTL_SERIAL_GET_TIOCM:
194         {
195             int sarg = 0;
196             int *targ = (int *)arg;
197             ioctl(fioc->fd, TIOCMGET, &sarg);
198             *targ = 0;
199             if (sarg & TIOCM_CTS) {
200                 *targ |= CHR_TIOCM_CTS;
201             }
202             if (sarg & TIOCM_CAR) {
203                 *targ |= CHR_TIOCM_CAR;
204             }
205             if (sarg & TIOCM_DSR) {
206                 *targ |= CHR_TIOCM_DSR;
207             }
208             if (sarg & TIOCM_RI) {
209                 *targ |= CHR_TIOCM_RI;
210             }
211             if (sarg & TIOCM_DTR) {
212                 *targ |= CHR_TIOCM_DTR;
213             }
214             if (sarg & TIOCM_RTS) {
215                 *targ |= CHR_TIOCM_RTS;
216             }
217         }
218         break;
219     case CHR_IOCTL_SERIAL_SET_TIOCM:
220         {
221             int sarg = *(int *)arg;
222             int targ = 0;
223             ioctl(fioc->fd, TIOCMGET, &targ);
224             targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
225                      | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
226             if (sarg & CHR_TIOCM_CTS) {
227                 targ |= TIOCM_CTS;
228             }
229             if (sarg & CHR_TIOCM_CAR) {
230                 targ |= TIOCM_CAR;
231             }
232             if (sarg & CHR_TIOCM_DSR) {
233                 targ |= TIOCM_DSR;
234             }
235             if (sarg & CHR_TIOCM_RI) {
236                 targ |= TIOCM_RI;
237             }
238             if (sarg & CHR_TIOCM_DTR) {
239                 targ |= TIOCM_DTR;
240             }
241             if (sarg & CHR_TIOCM_RTS) {
242                 targ |= TIOCM_RTS;
243             }
244             ioctl(fioc->fd, TIOCMSET, &targ);
245         }
246         break;
247     default:
248         return -ENOTSUP;
249     }
250     return 0;
251 }
252 
253 static void qmp_chardev_open_serial(Chardev *chr,
254                                     ChardevBackend *backend,
255                                     bool *be_opened,
256                                     Error **errp)
257 {
258     ChardevHostdev *serial = backend->u.serial.data;
259     int fd;
260 
261     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
262     if (fd < 0) {
263         return;
264     }
265     qemu_set_nonblock(fd);
266     tty_serial_init(fd, 115200, 'N', 8, 1);
267 
268     qemu_chr_open_fd(chr, fd, fd);
269 }
270 #endif /* __linux__ || __sun__ */
271 
272 #ifdef HAVE_CHARDEV_SERIAL
273 static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
274                                   Error **errp)
275 {
276     const char *device = qemu_opt_get(opts, "path");
277     ChardevHostdev *serial;
278 
279     if (device == NULL) {
280         error_setg(errp, "chardev: serial/tty: no device path given");
281         return;
282     }
283     backend->type = CHARDEV_BACKEND_KIND_SERIAL;
284     serial = backend->u.serial.data = g_new0(ChardevHostdev, 1);
285     qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial));
286     serial->device = g_strdup(device);
287 }
288 
289 static void char_serial_class_init(ObjectClass *oc, void *data)
290 {
291     ChardevClass *cc = CHARDEV_CLASS(oc);
292 
293     cc->parse = qemu_chr_parse_serial;
294     cc->open = qmp_chardev_open_serial;
295 #ifndef _WIN32
296     cc->chr_ioctl = tty_serial_ioctl;
297 #endif
298 }
299 
300 
301 static const TypeInfo char_serial_type_info = {
302     .name = TYPE_CHARDEV_SERIAL,
303 #ifdef _WIN32
304     .parent = TYPE_CHARDEV_WIN,
305 #else
306     .parent = TYPE_CHARDEV_FD,
307 #endif
308     .class_init = char_serial_class_init,
309 };
310 
311 static void register_types(void)
312 {
313     type_register_static(&char_serial_type_info);
314 }
315 
316 type_init(register_types);
317 
318 #endif
319