tty: rework break handling

Some hardware needs to do break handling itself and may have partial
support only. Make break_ctl return an error code. Add a tty driver flag
so you can indicate driver hardware side break support.

Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Alan Cox 2008-07-22 11:18:03 +01:00 committed by Linus Torvalds
parent abbe629ae4
commit 9e98966c7b
20 changed files with 97 additions and 84 deletions

View file

@ -218,7 +218,7 @@ config MOXA_SMARTIO
config ISI
tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
depends on SERIAL_NONSTANDARD && PCI
depends on SERIAL_NONSTANDARD && PCI && BROKEN
select FW_LOADER
help
This is a driver for the Multi-Tech cards which provide several

View file

@ -1248,7 +1248,7 @@ static int rs_tiocmset(struct tty_struct *tty, struct file *file,
/*
* rs_break() --- routine which turns the break handling on or off
*/
static void rs_break(struct tty_struct *tty, int break_state)
static int rs_break(struct tty_struct *tty, int break_state)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@ -1263,6 +1263,7 @@ static void rs_break(struct tty_struct *tty, int break_state)
custom.adkcon = AC_UARTBRK;
mb();
local_irq_restore(flags);
return 0;
}

View file

@ -3700,14 +3700,15 @@ cy_tiocmset(struct tty_struct *tty, struct file *file,
/*
* cy_break() --- routine which turns the break handling on or off
*/
static void cy_break(struct tty_struct *tty, int break_state)
static int cy_break(struct tty_struct *tty, int break_state)
{
struct cyclades_port *info = tty->driver_data;
struct cyclades_card *card;
unsigned long flags;
int retval = 0;
if (serial_paranoia_check(info, tty->name, "cy_break"))
return;
return -EINVAL;
card = info->card;
@ -3736,8 +3737,6 @@ static void cy_break(struct tty_struct *tty, int break_state)
}
}
} else {
int retval;
if (break_state == -1) {
retval = cyz_issue_cmd(card,
info->line - card->first_line,
@ -3758,6 +3757,7 @@ static void cy_break(struct tty_struct *tty, int break_state)
}
}
spin_unlock_irqrestore(&card->card_lock, flags);
return retval;
} /* cy_break */
static int get_mon_info(struct cyclades_port *info,

View file

@ -1725,13 +1725,13 @@ static int esp_tiocmset(struct tty_struct *tty, struct file *file,
/*
* rs_break() --- routine which turns the break handling on or off
*/
static void esp_break(struct tty_struct *tty, int break_state)
static int esp_break(struct tty_struct *tty, int break_state)
{
struct esp_struct *info = tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->name, "esp_break"))
return;
return -EINVAL;
if (break_state == -1) {
spin_lock_irqsave(&info->lock, flags);
@ -1747,6 +1747,7 @@ static void esp_break(struct tty_struct *tty, int break_state)
serial_out(info, UART_ESI_CMD2, 0x00);
spin_unlock_irqrestore(&info->lock, flags);
}
return 0;
}
static int rs_ioctl(struct tty_struct *tty, struct file *file,

View file

@ -609,7 +609,7 @@ static void stli_unthrottle(struct tty_struct *tty);
static void stli_stop(struct tty_struct *tty);
static void stli_start(struct tty_struct *tty);
static void stli_flushbuffer(struct tty_struct *tty);
static void stli_breakctl(struct tty_struct *tty, int state);
static int stli_breakctl(struct tty_struct *tty, int state);
static void stli_waituntilsent(struct tty_struct *tty, int timeout);
static void stli_sendxchar(struct tty_struct *tty, char ch);
static void stli_hangup(struct tty_struct *tty);
@ -1909,7 +1909,7 @@ static void stli_flushbuffer(struct tty_struct *tty)
/*****************************************************************************/
static void stli_breakctl(struct tty_struct *tty, int state)
static int stli_breakctl(struct tty_struct *tty, int state)
{
struct stlibrd *brdp;
struct stliport *portp;
@ -1917,15 +1917,16 @@ static void stli_breakctl(struct tty_struct *tty, int state)
portp = tty->driver_data;
if (portp == NULL)
return;
return -EINVAL;
if (portp->brdnr >= stli_nrbrds)
return;
return -EINVAL;
brdp = stli_brds[portp->brdnr];
if (brdp == NULL)
return;
return -EINVAL;
arg = (state == -1) ? BREAKON : BREAKOFF;
stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0);
return 0;
}
/*****************************************************************************/

View file

@ -374,12 +374,13 @@ copy:
return ret;
}
static void moxa_break_ctl(struct tty_struct *tty, int state)
static int moxa_break_ctl(struct tty_struct *tty, int state)
{
struct moxa_port *port = tty->driver_data;
moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak,
Magic_code);
return 0;
}
static const struct tty_operations moxa_ops = {

View file

@ -2183,7 +2183,7 @@ static void mxser_hangup(struct tty_struct *tty)
/*
* mxser_rs_break() --- routine which turns the break handling on or off
*/
static void mxser_rs_break(struct tty_struct *tty, int break_state)
static int mxser_rs_break(struct tty_struct *tty, int break_state)
{
struct mxser_port *info = tty->driver_data;
unsigned long flags;
@ -2196,6 +2196,7 @@ static void mxser_rs_break(struct tty_struct *tty, int break_state)
outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC,
info->ioaddr + UART_LCR);
spin_unlock_irqrestore(&info->slock, flags);
return 0;
}
static void mxser_receive_chars(struct mxser_port *port, int *status)

View file

@ -2230,7 +2230,7 @@ static int tiocmset(struct tty_struct *tty, struct file *file,
* Arguments: tty pointer to tty instance data
* break_state -1=set break condition, 0=clear
*/
static void mgslpc_break(struct tty_struct *tty, int break_state)
static int mgslpc_break(struct tty_struct *tty, int break_state)
{
MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
unsigned long flags;
@ -2240,7 +2240,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state)
__FILE__,__LINE__, info->device_name, break_state);
if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break"))
return;
return -EINVAL;
spin_lock_irqsave(&info->lock,flags);
if (break_state == -1)
@ -2248,6 +2248,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state)
else
clear_reg_bits(info, CHA+DAFO, BIT6);
spin_unlock_irqrestore(&info->lock,flags);
return 0;
}
/* Service an IOCTL request

View file

@ -1236,13 +1236,13 @@ static void rp_set_termios(struct tty_struct *tty,
}
}
static void rp_break(struct tty_struct *tty, int break_state)
static int rp_break(struct tty_struct *tty, int break_state)
{
struct r_port *info = (struct r_port *) tty->driver_data;
unsigned long flags;
if (rocket_paranoia_check(info, "rp_break"))
return;
return -EINVAL;
spin_lock_irqsave(&info->slock, flags);
if (break_state == -1)
@ -1250,6 +1250,7 @@ static void rp_break(struct tty_struct *tty, int break_state)
else
sClrBreak(&info->channel);
spin_unlock_irqrestore(&info->slock, flags);
return 0;
}
/*

View file

@ -1840,7 +1840,7 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
return rc;
}
static void sx_break(struct tty_struct *tty, int flag)
static int sx_break(struct tty_struct *tty, int flag)
{
struct sx_port *port = tty->driver_data;
int rv;
@ -1857,6 +1857,7 @@ static void sx_break(struct tty_struct *tty, int flag)
read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
unlock_kernel();
func_exit();
return 0;
}
static int sx_tiocmget(struct tty_struct *tty, struct file *file)

View file

@ -2897,9 +2897,9 @@ static int tiocmset(struct tty_struct *tty, struct file *file,
*
* Arguments: tty pointer to tty instance data
* break_state -1=set break condition, 0=clear
* Return Value: None
* Return Value: error code
*/
static void mgsl_break(struct tty_struct *tty, int break_state)
static int mgsl_break(struct tty_struct *tty, int break_state)
{
struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
unsigned long flags;
@ -2909,7 +2909,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state)
__FILE__,__LINE__, info->device_name, break_state);
if (mgsl_paranoia_check(info, tty->name, "mgsl_break"))
return;
return -EINVAL;
spin_lock_irqsave(&info->irq_spinlock,flags);
if (break_state == -1)
@ -2917,6 +2917,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state)
else
usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7));
spin_unlock_irqrestore(&info->irq_spinlock,flags);
return 0;
} /* end of mgsl_break() */

View file

@ -165,7 +165,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v
static int chars_in_buffer(struct tty_struct *tty);
static void throttle(struct tty_struct * tty);
static void unthrottle(struct tty_struct * tty);
static void set_break(struct tty_struct *tty, int break_state);
static int set_break(struct tty_struct *tty, int break_state);
/*
* generic HDLC support and callbacks
@ -513,7 +513,7 @@ static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr);
static int tiocmget(struct tty_struct *tty, struct file *file);
static int tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
static void set_break(struct tty_struct *tty, int break_state);
static int set_break(struct tty_struct *tty, int break_state);
static int get_interface(struct slgt_info *info, int __user *if_mode);
static int set_interface(struct slgt_info *info, int if_mode);
static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio);
@ -1452,14 +1452,14 @@ static void unthrottle(struct tty_struct * tty)
* set or clear transmit break condition
* break_state -1=set break condition, 0=clear
*/
static void set_break(struct tty_struct *tty, int break_state)
static int set_break(struct tty_struct *tty, int break_state)
{
struct slgt_info *info = tty->driver_data;
unsigned short value;
unsigned long flags;
if (sanity_check(info, tty->name, "set_break"))
return;
return -EINVAL;
DBGINFO(("%s set_break(%d)\n", info->device_name, break_state));
spin_lock_irqsave(&info->lock,flags);
@ -1470,6 +1470,7 @@ static void set_break(struct tty_struct *tty, int break_state)
value &= ~BIT6;
wr_reg16(info, TCR, value);
spin_unlock_irqrestore(&info->lock,flags);
return 0;
}
#if SYNCLINK_GENERIC_HDLC

View file

@ -527,7 +527,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v
static int chars_in_buffer(struct tty_struct *tty);
static void throttle(struct tty_struct * tty);
static void unthrottle(struct tty_struct * tty);
static void set_break(struct tty_struct *tty, int break_state);
static int set_break(struct tty_struct *tty, int break_state);
#if SYNCLINK_GENERIC_HDLC
#define dev_to_port(D) (dev_to_hdlc(D)->priv)
@ -552,7 +552,7 @@ static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr);
static int tiocmget(struct tty_struct *tty, struct file *file);
static int tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
static void set_break(struct tty_struct *tty, int break_state);
static int set_break(struct tty_struct *tty, int break_state);
static void add_device(SLMP_INFO *info);
static void device_init(int adapter_num, struct pci_dev *pdev);
@ -1587,7 +1587,7 @@ static void unthrottle(struct tty_struct * tty)
/* set or clear transmit break condition
* break_state -1=set break condition, 0=clear
*/
static void set_break(struct tty_struct *tty, int break_state)
static int set_break(struct tty_struct *tty, int break_state)
{
unsigned char RegValue;
SLMP_INFO * info = (SLMP_INFO *)tty->driver_data;
@ -1598,7 +1598,7 @@ static void set_break(struct tty_struct *tty, int break_state)
__FILE__,__LINE__, info->device_name, break_state);
if (sanity_check(info, tty->name, "set_break"))
return;
return -EINVAL;
spin_lock_irqsave(&info->lock,flags);
RegValue = read_reg(info, CTL);
@ -1608,6 +1608,7 @@ static void set_break(struct tty_struct *tty, int break_state)
RegValue &= ~BIT3;
write_reg(info, CTL, RegValue);
spin_unlock_irqrestore(&info->lock,flags);
return 0;
}
#if SYNCLINK_GENERIC_HDLC

View file

@ -2849,16 +2849,29 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
static int send_break(struct tty_struct *tty, unsigned int duration)
{
if (tty_write_lock(tty, 0) < 0)
return -EINTR;
tty->ops->break_ctl(tty, -1);
if (!signal_pending(current))
msleep_interruptible(duration);
tty->ops->break_ctl(tty, 0);
tty_write_unlock(tty);
if (signal_pending(current))
return -EINTR;
return 0;
int retval;
if (tty->ops->break_ctl == NULL)
return 0;
if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
retval = tty->ops->break_ctl(tty, duration);
else {
/* Do the work ourselves */
if (tty_write_lock(tty, 0) < 0)
return -EINTR;
retval = tty->ops->break_ctl(tty, -1);
if (retval)
goto out;
if (!signal_pending(current))
msleep_interruptible(duration);
retval = tty->ops->break_ctl(tty, 0);
out:
tty_write_unlock(tty);
if (signal_pending(current))
retval = -EINTR;
}
return retval;
}
/**
@ -2949,36 +2962,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
tty->driver->subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
/*
* Break handling by driver
*/
retval = -EINVAL;
if (!tty->ops->break_ctl) {
switch (cmd) {
case TIOCSBRK:
case TIOCCBRK:
if (tty->ops->ioctl)
retval = tty->ops->ioctl(tty, file, cmd, arg);
if (retval != -EINVAL && retval != -ENOIOCTLCMD)
printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name);
return retval;
/* These two ioctl's always return success; even if */
/* the driver doesn't support them. */
case TCSBRK:
case TCSBRKP:
if (!tty->ops->ioctl)
return 0;
retval = tty->ops->ioctl(tty, file, cmd, arg);
if (retval != -EINVAL && retval != -ENOIOCTLCMD)
printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name);
if (retval == -ENOIOCTLCMD)
retval = 0;
return retval;
}
}
/*
* Factor out some common prep work
@ -3000,6 +2983,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
/*
* Now do the stuff.
*/
switch (cmd) {
case TIOCSTI:
return tiocsti(tty, p);
@ -3043,12 +3029,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
*/
case TIOCSBRK: /* Turn break on, unconditionally */
if (tty->ops->break_ctl)
tty->ops->break_ctl(tty, -1);
return tty->ops->break_ctl(tty, -1);
return 0;
case TIOCCBRK: /* Turn break off, unconditionally */
if (tty->ops->break_ctl)
tty->ops->break_ctl(tty, 0);
return tty->ops->break_ctl(tty, 0);
return 0;
case TCSBRK: /* SVID version: non-zero arg --> no break */
/* non-zero arg means wait for all output data

View file

@ -85,7 +85,7 @@ static irqreturn_t scc_rx_int(int irq, void *data);
static irqreturn_t scc_stat_int(int irq, void *data);
static irqreturn_t scc_spcond_int(int irq, void *data);
static void scc_setsignals(struct scc_port *port, int dtr, int rts);
static void scc_break_ctl(struct tty_struct *tty, int break_state);
static int scc_break_ctl(struct tty_struct *tty, int break_state);
static struct tty_driver *scc_driver;
@ -942,7 +942,7 @@ static int scc_ioctl(struct tty_struct *tty, struct file *file,
}
static void scc_break_ctl(struct tty_struct *tty, int break_state)
static int scc_break_ctl(struct tty_struct *tty, int break_state)
{
struct scc_port *port = (struct scc_port *)tty->driver_data;
unsigned long flags;
@ -952,6 +952,7 @@ static void scc_break_ctl(struct tty_struct *tty, int break_state)
SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK,
break_state ? TCR_SEND_BREAK : 0);
local_irq_restore(flags);
return 0;
}

View file

@ -1302,11 +1302,12 @@ static void capinc_tty_hangup(struct tty_struct *tty)
#endif
}
static void capinc_tty_break_ctl(struct tty_struct *tty, int state)
static int capinc_tty_break_ctl(struct tty_struct *tty, int state)
{
#ifdef _DEBUG_TTYFUNCS
printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
#endif
return 0;
}
static void capinc_tty_flush_buffer(struct tty_struct *tty)

View file

@ -934,7 +934,7 @@ uart_tiocmset(struct tty_struct *tty, struct file *file,
return ret;
}
static void uart_break_ctl(struct tty_struct *tty, int break_state)
static int uart_break_ctl(struct tty_struct *tty, int break_state)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
@ -945,6 +945,7 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state)
port->ops->break_ctl(port, break_state);
mutex_unlock(&state->mutex);
return 0;
}
static int uart_do_autoconfig(struct uart_state *state)

View file

@ -732,13 +732,16 @@ static void acm_tty_unthrottle(struct tty_struct *tty)
tasklet_schedule(&acm->urb_task);
}
static void acm_tty_break_ctl(struct tty_struct *tty, int state)
static int acm_tty_break_ctl(struct tty_struct *tty, int state)
{
struct acm *acm = tty->driver_data;
int retval;
if (!ACM_READY(acm))
return;
if (acm_send_break(acm, state ? 0xffff : 0))
return -EINVAL;
retval = acm_send_break(acm, state ? 0xffff : 0);
if (retval < 0)
dbg("send break failed");
return retval;
}
static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)

View file

@ -395,7 +395,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)
tty_termios_copy_hw(tty->termios, old);
}
static void serial_break(struct tty_struct *tty, int break_state)
static int serial_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
@ -409,6 +409,7 @@ static void serial_break(struct tty_struct *tty, int break_state)
port->serial->type->break_ctl(tty, break_state);
unlock_kernel();
}
return 0;
}
static int serial_read_proc(char *page, char **start, off_t off, int count,

View file

@ -135,7 +135,7 @@
*
* Optional:
*
* void (*break_ctl)(struct tty_stuct *tty, int state);
* int (*break_ctl)(struct tty_stuct *tty, int state);
*
* This optional routine requests the tty driver to turn on or
* off BREAK status on the RS-232 port. If state is -1,
@ -146,6 +146,10 @@
* handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,
* TIOCCBRK.
*
* If the driver sets TTY_DRIVER_HARDWARE_BREAK then the interface
* will also be called with actual times and the hardware is expected
* to do the delay work itself. 0 and -1 are still used for on/off.
*
* Optional: Required for TCSBRK/BRKP/etc handling.
*
* void (*wait_until_sent)(struct tty_struct *tty, int timeout);
@ -192,7 +196,7 @@ struct tty_operations {
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
void (*break_ctl)(struct tty_struct *tty, int state);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
@ -285,12 +289,18 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line);
* TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead
* use dynamic memory keyed through the devpts filesystem. This
* is only applicable to the pty driver.
*
* TTY_DRIVER_HARDWARE_BREAK -- hardware handles break signals. Pass
* the requested timeout to the caller instead of using a simple
* on/off interface.
*
*/
#define TTY_DRIVER_INSTALLED 0x0001
#define TTY_DRIVER_RESET_TERMIOS 0x0002
#define TTY_DRIVER_REAL_RAW 0x0004
#define TTY_DRIVER_DYNAMIC_DEV 0x0008
#define TTY_DRIVER_DEVPTS_MEM 0x0010
#define TTY_DRIVER_HARDWARE_BREAK 0x0020
/* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM 0x0001