| /* |
| * QEMU System Emulator |
| * |
| * Copyright (c) 2003-2008 Fabrice Bellard |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/module.h" |
| #include "qemu/option.h" |
| #include "qemu/sockets.h" |
| #include "io/channel-file.h" |
| #include "qapi/error.h" |
| |
| #ifdef _WIN32 |
| #include "chardev/char-win.h" |
| #else |
| #include <sys/ioctl.h> |
| #include <termios.h> |
| #include "chardev/char-fd.h" |
| #endif |
| |
| #include "chardev/char-serial.h" |
| |
| #ifdef _WIN32 |
| |
| static void qmp_chardev_open_serial(Chardev *chr, |
| ChardevBackend *backend, |
| bool *be_opened, |
| Error **errp) |
| { |
| ChardevHostdev *serial = backend->u.serial.data; |
| |
| win_chr_serial_init(chr, serial->device, errp); |
| } |
| |
| #elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ |
| || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ |
| || defined(__GLIBC__) || defined(__APPLE__) |
| |
| static void tty_serial_init(int fd, int speed, |
| int parity, int data_bits, int stop_bits) |
| { |
| struct termios tty = {0}; |
| speed_t spd; |
| |
| #if 0 |
| printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n", |
| speed, parity, data_bits, stop_bits); |
| #endif |
| tcgetattr(fd, &tty); |
| |
| #define check_speed(val) \ |
| if (speed <= val) { \ |
| spd = B##val; \ |
| goto done; \ |
| } |
| |
| speed = speed * 10 / 11; |
| check_speed(50); |
| check_speed(75); |
| check_speed(110); |
| check_speed(134); |
| check_speed(150); |
| check_speed(200); |
| check_speed(300); |
| check_speed(600); |
| check_speed(1200); |
| check_speed(1800); |
| check_speed(2400); |
| check_speed(4800); |
| check_speed(9600); |
| check_speed(19200); |
| check_speed(38400); |
| /* Non-Posix values follow. They may be unsupported on some systems. */ |
| check_speed(57600); |
| check_speed(115200); |
| #ifdef B230400 |
| check_speed(230400); |
| #endif |
| #ifdef B460800 |
| check_speed(460800); |
| #endif |
| #ifdef B500000 |
| check_speed(500000); |
| #endif |
| #ifdef B576000 |
| check_speed(576000); |
| #endif |
| #ifdef B921600 |
| check_speed(921600); |
| #endif |
| #ifdef B1000000 |
| check_speed(1000000); |
| #endif |
| #ifdef B1152000 |
| check_speed(1152000); |
| #endif |
| #ifdef B1500000 |
| check_speed(1500000); |
| #endif |
| #ifdef B2000000 |
| check_speed(2000000); |
| #endif |
| #ifdef B2500000 |
| check_speed(2500000); |
| #endif |
| #ifdef B3000000 |
| check_speed(3000000); |
| #endif |
| #ifdef B3500000 |
| check_speed(3500000); |
| #endif |
| #ifdef B4000000 |
| check_speed(4000000); |
| #endif |
| spd = B115200; |
| |
| #undef check_speed |
| done: |
| cfsetispeed(&tty, spd); |
| cfsetospeed(&tty, spd); |
| |
| tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
| | INLCR | IGNCR | ICRNL | IXON); |
| tty.c_oflag &= ~OPOST; |
| tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); |
| tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB); |
| switch (data_bits) { |
| default: |
| case 8: |
| tty.c_cflag |= CS8; |
| break; |
| case 7: |
| tty.c_cflag |= CS7; |
| break; |
| case 6: |
| tty.c_cflag |= CS6; |
| break; |
| case 5: |
| tty.c_cflag |= CS5; |
| break; |
| } |
| switch (parity) { |
| default: |
| case 'N': |
| break; |
| case 'E': |
| tty.c_cflag |= PARENB; |
| break; |
| case 'O': |
| tty.c_cflag |= PARENB | PARODD; |
| break; |
| } |
| if (stop_bits == 2) { |
| tty.c_cflag |= CSTOPB; |
| } |
| |
| tcsetattr(fd, TCSANOW, &tty); |
| } |
| |
| static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg) |
| { |
| FDChardev *s = FD_CHARDEV(chr); |
| QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in); |
| |
| switch (cmd) { |
| case CHR_IOCTL_SERIAL_SET_PARAMS: |
| { |
| QEMUSerialSetParams *ssp = arg; |
| tty_serial_init(fioc->fd, |
| ssp->speed, ssp->parity, |
| ssp->data_bits, ssp->stop_bits); |
| } |
| break; |
| case CHR_IOCTL_SERIAL_SET_BREAK: |
| { |
| int enable = *(int *)arg; |
| if (enable) { |
| tcsendbreak(fioc->fd, 1); |
| } |
| } |
| break; |
| case CHR_IOCTL_SERIAL_GET_TIOCM: |
| { |
| int sarg = 0; |
| int *targ = (int *)arg; |
| ioctl(fioc->fd, TIOCMGET, &sarg); |
| *targ = 0; |
| if (sarg & TIOCM_CTS) { |
| *targ |= CHR_TIOCM_CTS; |
| } |
| if (sarg & TIOCM_CAR) { |
| *targ |= CHR_TIOCM_CAR; |
| } |
| if (sarg & TIOCM_DSR) { |
| *targ |= CHR_TIOCM_DSR; |
| } |
| if (sarg & TIOCM_RI) { |
| *targ |= CHR_TIOCM_RI; |
| } |
| if (sarg & TIOCM_DTR) { |
| *targ |= CHR_TIOCM_DTR; |
| } |
| if (sarg & TIOCM_RTS) { |
| *targ |= CHR_TIOCM_RTS; |
| } |
| } |
| break; |
| case CHR_IOCTL_SERIAL_SET_TIOCM: |
| { |
| int sarg = *(int *)arg; |
| int targ = 0; |
| ioctl(fioc->fd, TIOCMGET, &targ); |
| targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR |
| | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS); |
| if (sarg & CHR_TIOCM_CTS) { |
| targ |= TIOCM_CTS; |
| } |
| if (sarg & CHR_TIOCM_CAR) { |
| targ |= TIOCM_CAR; |
| } |
| if (sarg & CHR_TIOCM_DSR) { |
| targ |= TIOCM_DSR; |
| } |
| if (sarg & CHR_TIOCM_RI) { |
| targ |= TIOCM_RI; |
| } |
| if (sarg & CHR_TIOCM_DTR) { |
| targ |= TIOCM_DTR; |
| } |
| if (sarg & CHR_TIOCM_RTS) { |
| targ |= TIOCM_RTS; |
| } |
| ioctl(fioc->fd, TIOCMSET, &targ); |
| } |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| return 0; |
| } |
| |
| static void qmp_chardev_open_serial(Chardev *chr, |
| ChardevBackend *backend, |
| bool *be_opened, |
| Error **errp) |
| { |
| ChardevHostdev *serial = backend->u.serial.data; |
| int fd; |
| |
| fd = qmp_chardev_open_file_source(serial->device, O_RDWR | O_NONBLOCK, |
| errp); |
| if (fd < 0) { |
| return; |
| } |
| if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { |
| error_setg_errno(errp, errno, "Failed to set FD nonblocking"); |
| return; |
| } |
| tty_serial_init(fd, 115200, 'N', 8, 1); |
| |
| qemu_chr_open_fd(chr, fd, fd); |
| } |
| #endif /* __linux__ || __sun__ */ |
| |
| #ifdef HAVE_CHARDEV_SERIAL |
| static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, |
| Error **errp) |
| { |
| const char *device = qemu_opt_get(opts, "path"); |
| ChardevHostdev *serial; |
| |
| if (device == NULL) { |
| error_setg(errp, "chardev: serial/tty: no device path given"); |
| return; |
| } |
| backend->type = CHARDEV_BACKEND_KIND_SERIAL; |
| serial = backend->u.serial.data = g_new0(ChardevHostdev, 1); |
| qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(serial)); |
| serial->device = g_strdup(device); |
| } |
| |
| static void char_serial_class_init(ObjectClass *oc, void *data) |
| { |
| ChardevClass *cc = CHARDEV_CLASS(oc); |
| |
| cc->parse = qemu_chr_parse_serial; |
| cc->open = qmp_chardev_open_serial; |
| #ifndef _WIN32 |
| cc->chr_ioctl = tty_serial_ioctl; |
| #endif |
| } |
| |
| |
| static const TypeInfo char_serial_type_info = { |
| .name = TYPE_CHARDEV_SERIAL, |
| #ifdef _WIN32 |
| .parent = TYPE_CHARDEV_WIN, |
| #else |
| .parent = TYPE_CHARDEV_FD, |
| #endif |
| .class_init = char_serial_class_init, |
| }; |
| |
| static void register_types(void) |
| { |
| type_register_static(&char_serial_type_info); |
| } |
| |
| type_init(register_types); |
| |
| #endif |