Commit 5f519d72 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

tty: expose new methods needed for drivers to get termios right

This adds three new functions (or in one case to be more exact makes it
always available)

tty_termios_copy_hw

Copies all the hardware settings from one termios structure to the other.
This is intended for drivers that support little or no hardware setting

tty_termios_encode_baud_rate

Allows you to set the input and output baud rate in a termios structure.  A
driver is supposed to set the resulting baud rate from a request so most
will want to use this function to set the resulting input and output rates
to match the hardware values.  Internally it knows about keeping Bxxx
encoding when possible to maximise compatibility.

tty_encode_baud_rate

As above but for the tty's own current termios structure

I suspect this will initially need some tweaking as it gets enabled by
driver patches over the next few mm cycles so consider this lot -mm only
for the moment so it can stabilize and end up neat before it goes to base.

I've tried not to break any obscure architectures - if you get a speed you
can't represent the code will print warnings on non updated termios systems
but not break.

Once this is merged and seems sane I've got a growing pile of driver
updates to use it - notably for USB serial drivers.

[akpm@linux-foundation.org: cleanups]
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ca025282
...@@ -206,8 +206,6 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios) ...@@ -206,8 +206,6 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios)
EXPORT_SYMBOL(tty_termios_input_baud_rate); EXPORT_SYMBOL(tty_termios_input_baud_rate);
#ifdef BOTHER
/** /**
* tty_termios_encode_baud_rate * tty_termios_encode_baud_rate
* @termios: ktermios structure holding user requested state * @termios: ktermios structure holding user requested state
...@@ -225,6 +223,9 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate); ...@@ -225,6 +223,9 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate);
* *
* Locking: Caller should hold termios lock. This is already held * Locking: Caller should hold termios lock. This is already held
* when calling this function from the driver termios handler. * when calling this function from the driver termios handler.
*
* The ifdefs deal with platforms whose owners have yet to update them
* and will all go away once this is done.
*/ */
void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud) void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud)
...@@ -234,9 +235,13 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed ...@@ -234,9 +235,13 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed
int iclose = ibaud/50, oclose = obaud/50; int iclose = ibaud/50, oclose = obaud/50;
int ibinput = 0; int ibinput = 0;
if (obaud == 0) /* CD dropped */
ibaud = 0; /* Clear ibaud to be sure */
termios->c_ispeed = ibaud; termios->c_ispeed = ibaud;
termios->c_ospeed = obaud; termios->c_ospeed = obaud;
#ifdef BOTHER
/* If the user asked for a precise weird speed give a precise weird /* If the user asked for a precise weird speed give a precise weird
answer. If they asked for a Bfoo speed they many have problems answer. If they asked for a Bfoo speed they many have problems
digesting non-exact replies so fuzz a bit */ digesting non-exact replies so fuzz a bit */
...@@ -247,32 +252,60 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed ...@@ -247,32 +252,60 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed
iclose = 0; iclose = 0;
if ((termios->c_cflag >> IBSHIFT) & CBAUD) if ((termios->c_cflag >> IBSHIFT) & CBAUD)
ibinput = 1; /* An input speed was specified */ ibinput = 1; /* An input speed was specified */
#endif
termios->c_cflag &= ~CBAUD; termios->c_cflag &= ~CBAUD;
/*
* Our goal is to find a close match to the standard baud rate
* returned. Walk the baud rate table and if we get a very close
* match then report back the speed as a POSIX Bxxxx value by
* preference
*/
do { do {
if (obaud - oclose >= baud_table[i] && obaud + oclose <= baud_table[i]) { if (obaud - oclose >= baud_table[i] && obaud + oclose <= baud_table[i]) {
termios->c_cflag |= baud_bits[i]; termios->c_cflag |= baud_bits[i];
ofound = i; ofound = i;
} }
if (ibaud - iclose >= baud_table[i] && ibaud + iclose <= baud_table[i]) { if (ibaud - iclose >= baud_table[i] && ibaud + iclose <= baud_table[i]) {
/* For the case input == output don't set IBAUD bits if the user didn't do so */ if (ofound == i && !ibinput)
if (ofound != i || ibinput) ifound = i;
termios->c_cflag |= (baud_bits[i] << IBSHIFT); #ifdef IBSHIFT
else {
ifound = i; ifound = i;
termios->c_cflag |= (baud_bits[i] << IBSHIFT);
}
#endif
} }
} while (++i < n_baud_table); } while (++i < n_baud_table);
/*
* If we found no match then use BOTHER if provided or warn
* the user their platform maintainer needs to wake up if not.
*/
#ifdef BOTHER
if (ofound == -1) if (ofound == -1)
termios->c_cflag |= BOTHER; termios->c_cflag |= BOTHER;
/* Set exact input bits only if the input and output differ or the /* Set exact input bits only if the input and output differ or the
user already did */ user already did */
if (ifound == -1 && (ibaud != obaud || ibinput)) if (ifound == -1 && (ibaud != obaud || ibinput))
termios->c_cflag |= (BOTHER << IBSHIFT); termios->c_cflag |= (BOTHER << IBSHIFT);
#else
if (ifound == -1 || ofound == -1) {
static int warned;
if (!warned++)
printk(KERN_WARNING "tty: Unable to return correct "
"speed data as your architecture needs updating.\n");
}
#endif
} }
EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
#endif void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
{
tty_termios_encode_baud_rate(tty->termios, ibaud, obaud);
}
EXPORT_SYMBOL_GPL(tty_encode_baud_rate);
/** /**
* tty_get_baud_rate - get tty bit rates * tty_get_baud_rate - get tty bit rates
...@@ -303,6 +336,29 @@ speed_t tty_get_baud_rate(struct tty_struct *tty) ...@@ -303,6 +336,29 @@ speed_t tty_get_baud_rate(struct tty_struct *tty)
EXPORT_SYMBOL(tty_get_baud_rate); EXPORT_SYMBOL(tty_get_baud_rate);
/**
* tty_termios_copy_hw - copy hardware settings
* @new: New termios
* @old: Old termios
*
* Propogate 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, 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);
/** /**
* change_termios - update termios values * change_termios - update termios values
* @tty: tty to update * @tty: tty to update
...@@ -340,13 +396,12 @@ static void change_termios(struct tty_struct * tty, struct ktermios * new_termio ...@@ -340,13 +396,12 @@ static void change_termios(struct tty_struct * tty, struct ktermios * new_termio
tty->erasing = 0; tty->erasing = 0;
} }
/* This bit should be in the ldisc code */
if (canon_change && !L_ICANON(tty) && tty->read_cnt) if (canon_change && !L_ICANON(tty) && tty->read_cnt)
/* Get characters left over from canonical mode. */ /* Get characters left over from canonical mode. */
wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->read_wait);
/* See if packet mode change of state. */ /* See if packet mode change of state. */
if (tty->link && tty->link->packet) { if (tty->link && tty->link->packet) {
int old_flow = ((old_termios.c_iflag & IXON) && int old_flow = ((old_termios.c_iflag & IXON) &&
(old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTOP] == '\023') &&
...@@ -366,6 +421,8 @@ static void change_termios(struct tty_struct * tty, struct ktermios * new_termio ...@@ -366,6 +421,8 @@ static void change_termios(struct tty_struct * tty, struct ktermios * new_termio
if (tty->driver->set_termios) if (tty->driver->set_termios)
(*tty->driver->set_termios)(tty, &old_termios); (*tty->driver->set_termios)(tty, &old_termios);
else
tty_termios_copy_hw(tty->termios, &old_termios);
ld = tty_ldisc_ref(tty); ld = tty_ldisc_ref(tty);
if (ld != NULL) { if (ld != NULL) {
...@@ -440,6 +497,11 @@ static int set_termios(struct tty_struct * tty, void __user *arg, int opt) ...@@ -440,6 +497,11 @@ static int set_termios(struct tty_struct * tty, void __user *arg, int opt)
} }
change_termios(tty, &tmp_termios); change_termios(tty, &tmp_termios);
/* FIXME: Arguably if tmp_termios == tty->termios AND the
actual requested termios was not tmp_termios then we may
want to return an error as no user requested change has
succeeded */
return 0; return 0;
} }
......
...@@ -316,6 +316,9 @@ extern void tty_flip_buffer_push(struct tty_struct *tty); ...@@ -316,6 +316,9 @@ extern void tty_flip_buffer_push(struct tty_struct *tty);
extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_get_baud_rate(struct tty_struct *tty);
extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_baud_rate(struct ktermios *termios);
extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);
extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud);
extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud);
extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old);
extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *);
extern void tty_ldisc_deref(struct tty_ldisc *); extern void tty_ldisc_deref(struct tty_ldisc *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment