void tty_wait_until_sent(struct tty_struct * tty, long timeout)
{
- DECLARE_WAITQUEUE(wait, current);
-
#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
char buf[64];
#endif
if (!tty->driver->chars_in_buffer)
return;
- add_wait_queue(&tty->write_wait, &wait);
if (!timeout)
timeout = MAX_SCHEDULE_TIMEOUT;
- do {
-#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
- printk(KERN_DEBUG "waiting %s...(%d)\n", tty_name(tty, buf),
- tty->driver->chars_in_buffer(tty));
-#endif
- set_current_state(TASK_INTERRUPTIBLE);
- if (signal_pending(current))
- goto stop_waiting;
- if (!tty->driver->chars_in_buffer(tty))
- break;
- timeout = schedule_timeout(timeout);
- } while (timeout);
+ if (wait_event_interruptible_timeout(tty->write_wait,
+ !tty->driver->chars_in_buffer(tty), timeout))
+ return;
if (tty->driver->wait_until_sent)
tty->driver->wait_until_sent(tty, timeout);
-stop_waiting:
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&tty->write_wait, &wait);
}
EXPORT_SYMBOL(tty_wait_until_sent);
/**
* tty_termios_encode_baud_rate
- * @termios: termios structure
+ * @termios: ktermios structure holding user requested state
* @ispeed: input speed
* @ospeed: output speed
*
* used as a library helper for drivers os that they can report back
* the actual speed selected when it differs from the speed requested
*
- * For now input and output speed must agree.
+ * For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
+ * we need to carefully set the bits when the user does not get the
+ * desired speed. We allow small margins and preserve as much of possible
+ * of the input intent to keep compatiblity.
*
* Locking: Caller should hold termios lock. This is already held
* when calling this function from the driver termios handler.
void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud)
{
int i = 0;
- int ifound = 0, ofound = 0;
+ int ifound = -1, ofound = -1;
+ int iclose = ibaud/50, oclose = obaud/50;
+ int ibinput = 0;
termios->c_ispeed = ibaud;
termios->c_ospeed = obaud;
+ /* 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
+ digesting non-exact replies so fuzz a bit */
+
+ if ((termios->c_cflag & CBAUD) == BOTHER)
+ oclose = 0;
+ if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
+ iclose = 0;
+ if ((termios->c_cflag >> IBSHIFT) & CBAUD)
+ ibinput = 1; /* An input speed was specified */
+
termios->c_cflag &= ~CBAUD;
- /* Identical speed means no input encoding (ie B0 << IBSHIFT)*/
- if (termios->c_ispeed == termios->c_ospeed)
- ifound = 1;
do {
- if (obaud == baud_table[i]) {
+ if (obaud - oclose >= baud_table[i] && obaud + oclose <= baud_table[i]) {
termios->c_cflag |= baud_bits[i];
- ofound = 1;
- /* So that if ibaud == obaud we don't set it */
- continue;
+ ofound = i;
}
- if (ibaud == baud_table[i]) {
- termios->c_cflag |= (baud_bits[i] << IBSHIFT);
- ifound = 1;
+ 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)
+ termios->c_cflag |= (baud_bits[i] << IBSHIFT);
+ ifound = i;
}
- }
- while(++i < n_baud_table);
- if (!ofound)
+ } while (++i < n_baud_table);
+ if (ofound == -1)
termios->c_cflag |= BOTHER;
- if (!ifound)
+ /* Set exact input bits only if the input and output differ or the
+ user already did */
+ if (ifound == -1 && (ibaud != obaud || ibinput))
termios->c_cflag |= (BOTHER << IBSHIFT);
}
if (retval)
return retval;
+ memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios));
+
if (opt & TERMIOS_TERMIO) {
- memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios));
if (user_termio_to_kernel_termios(&tmp_termios,
(struct termio __user *)arg))
return -EFAULT;
#ifdef TCGETS2
} else if (opt & TERMIOS_OLD) {
- memcpy(&tmp_termios, tty->termios, sizeof(struct termios));
if (user_termios_to_kernel_termios_1(&tmp_termios,
- (struct termios_v1 __user *)arg))
+ (struct termios __user *)arg))
return -EFAULT;
-#endif
} else {
if (user_termios_to_kernel_termios(&tmp_termios,
- (struct termios __user *)arg))
+ (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 */
struct sgttyb tmp;
mutex_lock(&tty->termios_mutex);
- tmp.sg_ispeed = tty->c_ispeed;
- tmp.sg_ospeed = tty->c_ospeed;
+ tmp.sg_ispeed = tty->termios->c_ispeed;
+ tmp.sg_ospeed = tty->termios->c_ospeed;
tmp.sg_erase = tty->termios->c_cc[VERASE];
tmp.sg_kill = tty->termios->c_cc[VKILL];
tmp.sg_flags = get_sgflags(tty);
return -EFAULT;
mutex_lock(&tty->termios_mutex);
- termios = *tty->termios;
+ termios = *tty->termios;
termios.c_cc[VERASE] = tmp.sg_erase;
termios.c_cc[VKILL] = tmp.sg_kill;
set_sgflags(&termios, tmp.sg_flags);
return 0;
}
- if (mutex_lock_interruptible(&tty->atomic_write_lock))
+ if (tty_write_lock(tty, 0) < 0)
return -ERESTARTSYS;
if (was_stopped)
tty->driver->write(tty, &ch, 1);
if (was_stopped)
stop_tty(tty);
- mutex_unlock(&tty->atomic_write_lock);
+ tty_write_unlock(tty);
return 0;
}
return 0;
#else
case TCGETS:
- if (kernel_termios_to_user_termios_1((struct termios_v1 __user *)arg, real_tty->termios))
+ if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios))
return -EFAULT;
return 0;
case TCGETS2:
- if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios))
+ if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios))
return -EFAULT;
return 0;
case TCSETSF2: