985 lines · 25.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines * which can be dynamically activated and de-activated by the line * discipline handling modules (like SLIP). */ #include <linux/bits.h>#include <linux/types.h>#include <linux/termios.h>#include <linux/errno.h>#include <linux/sched/signal.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/tty.h>#include <linux/fcntl.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/bitops.h>#include <linux/mutex.h>#include <linux/compat.h>#include <linux/termios_internal.h>#include "tty.h" #include <asm/io.h>#include <linux/uaccess.h> #undef	DEBUG /* * Internal flag options for termios setting behavior */#define TERMIOS_FLUSH	BIT(0)#define TERMIOS_WAIT	BIT(1)#define TERMIOS_TERMIO	BIT(2)#define TERMIOS_OLD	BIT(3) /** * tty_chars_in_buffer - characters pending * @tty: terminal * * Returns: the number of bytes of data in the device private output queue. If * no private method is supplied there is assumed to be no queue on the device. */unsigned int tty_chars_in_buffer(struct tty_struct *tty){	if (tty->ops->chars_in_buffer)		return tty->ops->chars_in_buffer(tty);	return 0;}EXPORT_SYMBOL(tty_chars_in_buffer); /** * tty_write_room - write queue space * @tty: terminal * * Returns: the number of bytes that can be queued to this device at the present * time. The result should be treated as a guarantee and the driver cannot * offer a value it later shrinks by more than the number of bytes written. If * no method is provided, 2K is always returned and data may be lost as there * will be no flow control. */unsigned int tty_write_room(struct tty_struct *tty){	if (tty->ops->write_room)		return tty->ops->write_room(tty);	return 2048;}EXPORT_SYMBOL(tty_write_room); /** * tty_driver_flush_buffer - discard internal buffer * @tty: terminal * * Discard the internal output buffer for this device. If no method is provided, * then either the buffer cannot be hardware flushed or there is no buffer * driver side. */void tty_driver_flush_buffer(struct tty_struct *tty){	if (tty->ops->flush_buffer)		tty->ops->flush_buffer(tty);}EXPORT_SYMBOL(tty_driver_flush_buffer); /** * tty_unthrottle - flow control * @tty: terminal * * Indicate that a @tty may continue transmitting data down the stack. Takes * the &tty_struct->termios_rwsem to protect against parallel * throttle/unthrottle and also to ensure the driver can consistently reference * its own termios data at this point when implementing software flow control. * * Drivers should however remember that the stack can issue a throttle, then * change flow control method, then unthrottle. */void tty_unthrottle(struct tty_struct *tty){	down_write(&tty->termios_rwsem);	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&	    tty->ops->unthrottle)		tty->ops->unthrottle(tty);	tty->flow_change = TTY_FLOW_NO_CHANGE;	up_write(&tty->termios_rwsem);}EXPORT_SYMBOL(tty_unthrottle); /** * tty_throttle_safe - flow control * @tty: terminal * * Indicate that a @tty should stop transmitting data down the stack. * tty_throttle_safe() will only attempt throttle if @tty->flow_change is * %TTY_THROTTLE_SAFE. Prevents an accidental throttle due to race conditions * when throttling is conditional on factors evaluated prior to throttling. * * Returns: %true if @tty is throttled (or was already throttled) */bool tty_throttle_safe(struct tty_struct *tty){	guard(mutex)(&tty->throttle_mutex); 	if (tty_throttled(tty))		return true; 	if (tty->flow_change != TTY_THROTTLE_SAFE)		return false; 	set_bit(TTY_THROTTLED, &tty->flags);	if (tty->ops->throttle)		tty->ops->throttle(tty); 	return true;} /** * tty_unthrottle_safe - flow control * @tty: terminal * * Similar to tty_unthrottle() but will only attempt unthrottle if * @tty->flow_change is %TTY_UNTHROTTLE_SAFE. Prevents an accidental unthrottle * due to race conditions when unthrottling is conditional on factors evaluated * prior to unthrottling. * * Returns: %true if @tty is unthrottled (or was already unthrottled) */bool tty_unthrottle_safe(struct tty_struct *tty){	guard(mutex)(&tty->throttle_mutex); 	if (!tty_throttled(tty))		return true; 	if (tty->flow_change != TTY_UNTHROTTLE_SAFE)		return false; 	clear_bit(TTY_THROTTLED, &tty->flags);	if (tty->ops->unthrottle)		tty->ops->unthrottle(tty); 	return true;} /** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for * @timeout: how long we will wait * * Wait for characters pending in a tty driver to hit the wire, or for a * timeout to occur (eg due to flow control). * * Locking: none */ void tty_wait_until_sent(struct tty_struct *tty, long timeout){	if (!timeout)		timeout = MAX_SCHEDULE_TIMEOUT; 	timeout = wait_event_interruptible_timeout(tty->write_wait,			!tty_chars_in_buffer(tty), timeout);	if (timeout <= 0)		return; 	if (timeout == MAX_SCHEDULE_TIMEOUT)		timeout = 0; 	if (tty->ops->wait_until_sent)		tty->ops->wait_until_sent(tty, timeout);}EXPORT_SYMBOL(tty_wait_until_sent);  /* *		Termios Helper Methods */ static void unset_locked_termios(struct tty_struct *tty, const struct ktermios *old){	struct ktermios *termios = &tty->termios;	struct ktermios *locked  = &tty->termios_locked;	int	i; #define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z))) 	NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);	NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);	NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);	NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);	termios->c_line = locked->c_line ? old->c_line : termios->c_line;	for (i = 0; i < NCCS; i++)		termios->c_cc[i] = locked->c_cc[i] ?			old->c_cc[i] : termios->c_cc[i];	/* FIXME: What should we do for i/ospeed */} /** * tty_termios_copy_hw - copy hardware settings * @new: new termios * @old: old termios * * Propagate the hardware specific terminal setting bits from the @old termios * structure to the @new one. This is used in cases where the hardware does not * support reconfiguration or as a helper in some cases where only minimal * reconfiguration is supported. */void tty_termios_copy_hw(struct ktermios *new, const struct ktermios *old){	/* The bits a dumb device handles in software. Smart devices need	   to always provide a set_termios method */	new->c_cflag &= HUPCL | CREAD | CLOCAL;	new->c_cflag |= old->c_cflag & ~(HUPCL | CREAD | CLOCAL);	new->c_ispeed = old->c_ispeed;	new->c_ospeed = old->c_ospeed;}EXPORT_SYMBOL(tty_termios_copy_hw); /** * tty_termios_hw_change - check for setting change * @a: termios * @b: termios to compare * * Check if any of the bits that affect a dumb device have changed between the * two termios structures, or a speed change is needed. * * Returns: %true if change is needed */bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b){	if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed)		return true;	if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL))		return true;	return false;}EXPORT_SYMBOL(tty_termios_hw_change); /** * tty_get_char_size - get size of a character * @cflag: termios cflag value * * Returns: size (in bits) of a character depending on @cflag's %CSIZE setting */unsigned char tty_get_char_size(unsigned int cflag){	switch (cflag & CSIZE) {	case CS5:		return 5;	case CS6:		return 6;	case CS7:		return 7;	case CS8:	default:		return 8;	}}EXPORT_SYMBOL_GPL(tty_get_char_size); /** * tty_get_frame_size - get size of a frame * @cflag: termios cflag value * * Get the size (in bits) of a frame depending on @cflag's %CSIZE, %CSTOPB, and * %PARENB setting. The result is a sum of character size, start and stop bits * -- one bit each -- second stop bit (if set), and parity bit (if set). * * Returns: size (in bits) of a frame depending on @cflag's setting. */unsigned char tty_get_frame_size(unsigned int cflag){	unsigned char bits = 2 + tty_get_char_size(cflag); 	if (cflag & CSTOPB)		bits++;	if (cflag & PARENB)		bits++;	if (cflag & ADDRB)		bits++; 	return bits;}EXPORT_SYMBOL_GPL(tty_get_frame_size); /** * tty_set_termios - update termios values * @tty: tty to update * @new_termios: desired new value * * Perform updates to the termios values set on this @tty. A master pty's * termios should never be set. * * Locking: &tty_struct->termios_rwsem */int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios){	struct ktermios old_termios;	struct tty_ldisc *ld; 	WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&		tty->driver->subtype == PTY_TYPE_MASTER);	/*	 *	Perform the actual termios internal changes under lock.	 */  	/* FIXME: we need to decide on some locking/ordering semantics	   for the set_termios notification eventually */	down_write(&tty->termios_rwsem);	old_termios = tty->termios;	tty->termios = *new_termios;	unset_locked_termios(tty, &old_termios);	/* Reset any ADDRB changes, ADDRB is changed through ->rs485_config() */	tty->termios.c_cflag ^= (tty->termios.c_cflag ^ old_termios.c_cflag) & ADDRB; 	if (tty->ops->set_termios)		tty->ops->set_termios(tty, &old_termios);	else		tty_termios_copy_hw(&tty->termios, &old_termios); 	ld = tty_ldisc_ref(tty);	if (ld != NULL) {		if (ld->ops->set_termios)			ld->ops->set_termios(tty, &old_termios);		tty_ldisc_deref(ld);	}	up_write(&tty->termios_rwsem);	return 0;}EXPORT_SYMBOL_GPL(tty_set_termios);  /* * Translate a "termio" structure into a "termios". Ugh. */__weak int user_termio_to_kernel_termios(struct ktermios *termios,						struct termio __user *termio){	struct termio v; 	if (copy_from_user(&v, termio, sizeof(struct termio)))		return -EFAULT; 	termios->c_iflag = (0xffff0000 & termios->c_iflag) | v.c_iflag;	termios->c_oflag = (0xffff0000 & termios->c_oflag) | v.c_oflag;	termios->c_cflag = (0xffff0000 & termios->c_cflag) | v.c_cflag;	termios->c_lflag = (0xffff0000 & termios->c_lflag) | v.c_lflag;	termios->c_line = (0xffff0000 & termios->c_lflag) | v.c_line;	memcpy(termios->c_cc, v.c_cc, NCC);	return 0;} /* * Translate a "termios" structure into a "termio". Ugh. */__weak 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);	return copy_to_user(termio, &v, sizeof(struct termio));} #ifdef TCGETS2__weak int user_termios_to_kernel_termios(struct ktermios *k,						 struct termios2 __user *u){	return copy_from_user(k, u, sizeof(struct termios2));}__weak int kernel_termios_to_user_termios(struct termios2 __user *u,						 struct ktermios *k){	return copy_to_user(u, k, sizeof(struct termios2));}__weak int user_termios_to_kernel_termios_1(struct ktermios *k,						   struct termios __user *u){	return copy_from_user(k, u, sizeof(struct termios));}__weak int kernel_termios_to_user_termios_1(struct termios __user *u,						   struct ktermios *k){	return copy_to_user(u, k, sizeof(struct termios));} #else __weak int user_termios_to_kernel_termios(struct ktermios *k,						 struct termios __user *u){	return copy_from_user(k, u, sizeof(struct termios));}__weak int kernel_termios_to_user_termios(struct termios __user *u,						 struct ktermios *k){	return copy_to_user(u, k, sizeof(struct termios));}#endif /* TCGETS2 */ /** * set_termios - set termios values for a tty * @tty: terminal device * @arg: user data * @opt: option information * * Helper function to prepare termios data and run necessary other functions * before using tty_set_termios() to do the actual changes. * * Locking: called functions take &tty_struct->ldisc_sem and * &tty_struct->termios_rwsem locks * * Returns: 0 on success, an error otherwise */static int set_termios(struct tty_struct *tty, void __user *arg, int opt){	struct ktermios tmp_termios;	struct tty_ldisc *ld;	int retval = tty_check_change(tty); 	if (retval)		return retval; 	down_read(&tty->termios_rwsem);	tmp_termios = tty->termios;	up_read(&tty->termios_rwsem); 	if (opt & TERMIOS_TERMIO) {		if (user_termio_to_kernel_termios(&tmp_termios,						(struct termio __user *)arg))			return -EFAULT;#ifdef TCGETS2	} else if (opt & TERMIOS_OLD) {		if (user_termios_to_kernel_termios_1(&tmp_termios,						(struct termios __user *)arg))			return -EFAULT;	} else {		if (user_termios_to_kernel_termios(&tmp_termios,						(struct termios2 __user *)arg))			return -EFAULT;	}#else	} else if (user_termios_to_kernel_termios(&tmp_termios,					(struct termios __user *)arg))		return -EFAULT;#endif 	/* If old style Bfoo values are used then load c_ispeed/c_ospeed	 * with the real speed so its unconditionally usable */	tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);	tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios); 	if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {retry_write_wait:		retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty));		if (retval < 0)			return retval; 		if (tty_write_lock(tty, false) < 0)			goto retry_write_wait; 		/* Racing writer? */		if (tty_chars_in_buffer(tty)) {			tty_write_unlock(tty);			goto retry_write_wait;		} 		ld = tty_ldisc_ref(tty);		if (ld != NULL) {			if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)				ld->ops->flush_buffer(tty);

Lines 1–500 of 985.