| // SPDX-License-Identifier: GPL-2.0 |
| #include <linux/termios_internal.h> |
| |
| /* |
| * c_cc characters in the termio structure. Oh, how I love being |
| * backwardly compatible. Notice that character 4 and 5 are |
| * interpreted differently depending on whether ICANON is set in |
| * c_lflag. If it's set, they are used as _VEOF and _VEOL, otherwise |
| * as _VMIN and V_TIME. This is for compatibility with OSF/1 (which |
| * is compatible with sysV)... |
| */ |
| #define _VMIN 4 |
| #define _VTIME 5 |
| |
| int kernel_termios_to_user_termio(struct termio __user *termio, |
| struct ktermios *termios) |
| { |
| struct termio v; |
| memset(&v, 0, sizeof(struct termio)); |
| v.c_iflag = termios->c_iflag; |
| v.c_oflag = termios->c_oflag; |
| v.c_cflag = termios->c_cflag; |
| v.c_lflag = termios->c_lflag; |
| v.c_line = termios->c_line; |
| memcpy(v.c_cc, termios->c_cc, NCC); |
| if (!(v.c_lflag & ICANON)) { |
| v.c_cc[_VMIN] = termios->c_cc[VMIN]; |
| v.c_cc[_VTIME] = termios->c_cc[VTIME]; |
| } |
| return copy_to_user(termio, &v, sizeof(struct termio)); |
| } |
| |
| int user_termios_to_kernel_termios(struct ktermios *k, |
| struct termios2 __user *u) |
| { |
| int err; |
| err = get_user(k->c_iflag, &u->c_iflag); |
| err |= get_user(k->c_oflag, &u->c_oflag); |
| err |= get_user(k->c_cflag, &u->c_cflag); |
| err |= get_user(k->c_lflag, &u->c_lflag); |
| err |= get_user(k->c_line, &u->c_line); |
| err |= copy_from_user(k->c_cc, u->c_cc, NCCS); |
| if (k->c_lflag & ICANON) { |
| err |= get_user(k->c_cc[VEOF], &u->c_cc[VEOF]); |
| err |= get_user(k->c_cc[VEOL], &u->c_cc[VEOL]); |
| } else { |
| err |= get_user(k->c_cc[VMIN], &u->c_cc[_VMIN]); |
| err |= get_user(k->c_cc[VTIME], &u->c_cc[_VTIME]); |
| } |
| err |= get_user(k->c_ispeed, &u->c_ispeed); |
| err |= get_user(k->c_ospeed, &u->c_ospeed); |
| return err; |
| } |
| |
| int kernel_termios_to_user_termios(struct termios2 __user *u, |
| struct ktermios *k) |
| { |
| int err; |
| err = put_user(k->c_iflag, &u->c_iflag); |
| err |= put_user(k->c_oflag, &u->c_oflag); |
| err |= put_user(k->c_cflag, &u->c_cflag); |
| err |= put_user(k->c_lflag, &u->c_lflag); |
| err |= put_user(k->c_line, &u->c_line); |
| err |= copy_to_user(u->c_cc, k->c_cc, NCCS); |
| if (!(k->c_lflag & ICANON)) { |
| err |= put_user(k->c_cc[VMIN], &u->c_cc[_VMIN]); |
| err |= put_user(k->c_cc[VTIME], &u->c_cc[_VTIME]); |
| } else { |
| err |= put_user(k->c_cc[VEOF], &u->c_cc[VEOF]); |
| err |= put_user(k->c_cc[VEOL], &u->c_cc[VEOL]); |
| } |
| err |= put_user(k->c_ispeed, &u->c_ispeed); |
| err |= put_user(k->c_ospeed, &u->c_ospeed); |
| return err; |
| } |
| |
| int user_termios_to_kernel_termios_1(struct ktermios *k, |
| struct termios __user *u) |
| { |
| int err; |
| err = get_user(k->c_iflag, &u->c_iflag); |
| err |= get_user(k->c_oflag, &u->c_oflag); |
| err |= get_user(k->c_cflag, &u->c_cflag); |
| err |= get_user(k->c_lflag, &u->c_lflag); |
| err |= get_user(k->c_line, &u->c_line); |
| err |= copy_from_user(k->c_cc, u->c_cc, NCCS); |
| if (k->c_lflag & ICANON) { |
| err |= get_user(k->c_cc[VEOF], &u->c_cc[VEOF]); |
| err |= get_user(k->c_cc[VEOL], &u->c_cc[VEOL]); |
| } else { |
| err |= get_user(k->c_cc[VMIN], &u->c_cc[_VMIN]); |
| err |= get_user(k->c_cc[VTIME], &u->c_cc[_VTIME]); |
| } |
| return err; |
| } |
| |
| int kernel_termios_to_user_termios_1(struct termios __user *u, |
| struct ktermios *k) |
| { |
| int err; |
| err = put_user(k->c_iflag, &u->c_iflag); |
| err |= put_user(k->c_oflag, &u->c_oflag); |
| err |= put_user(k->c_cflag, &u->c_cflag); |
| err |= put_user(k->c_lflag, &u->c_lflag); |
| err |= put_user(k->c_line, &u->c_line); |
| err |= copy_to_user(u->c_cc, k->c_cc, NCCS); |
| if (!(k->c_lflag & ICANON)) { |
| err |= put_user(k->c_cc[VMIN], &u->c_cc[_VMIN]); |
| err |= put_user(k->c_cc[VTIME], &u->c_cc[_VTIME]); |
| } else { |
| err |= put_user(k->c_cc[VEOF], &u->c_cc[VEOF]); |
| err |= put_user(k->c_cc[VEOL], &u->c_cc[VEOL]); |
| } |
| return err; |
| } |