Commit 3bbb0896 authored by Russell King's avatar Russell King

[SERIAL] Move make modem control signals accessible to line discplines

We also parse modem control signals in the tty layer, and fail with
EINVAL if the driver does not provide the methods.  All tty drivers
which require modem control support should be updated to provide
the new tiocmset and tiocmget methods.
parent 96069ef3
...@@ -1691,6 +1691,55 @@ static int send_break(struct tty_struct *tty, int duration) ...@@ -1691,6 +1691,55 @@ static int send_break(struct tty_struct *tty, int duration)
return 0; return 0;
} }
static int
tty_tiocmget(struct tty_struct *tty, struct file *file, unsigned long arg)
{
int retval = -EINVAL;
if (tty->driver.tiocmget) {
retval = tty->driver.tiocmget(tty, file);
if (retval >= 0)
retval = put_user(retval, (int *)arg);
}
return retval;
}
static int
tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
unsigned long arg)
{
int retval = -EINVAL;
if (tty->driver.tiocmset) {
unsigned int set, clear, val;
retval = get_user(val, (unsigned int *)arg);
if (retval)
return retval;
set = clear = 0;
switch (cmd) {
case TIOCMBIS:
set = val;
break;
case TIOCMBIC:
clear = val;
break;
case TIOCMSET:
set = val;
clear = ~val;
break;
}
set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2;
clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2;
retval = tty->driver.tiocmset(tty, file, set, clear);
}
return retval;
}
/* /*
* Split this up, as gcc can choke on it otherwise.. * Split this up, as gcc can choke on it otherwise..
*/ */
...@@ -1816,6 +1865,14 @@ int tty_ioctl(struct inode * inode, struct file * file, ...@@ -1816,6 +1865,14 @@ int tty_ioctl(struct inode * inode, struct file * file,
return 0; return 0;
case TCSBRKP: /* support for POSIX tcsendbreak() */ case TCSBRKP: /* support for POSIX tcsendbreak() */
return send_break(tty, arg ? arg*(HZ/10) : HZ/4); return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
case TIOCMGET:
return tty_tiocmget(tty, file, arg);
case TIOCMSET:
case TIOCMBIC:
case TIOCMBIS:
return tty_tiocmset(tty, file, cmd, arg);
} }
if (tty->driver.ioctl) { if (tty->driver.ioctl) {
int retval = (tty->driver.ioctl)(tty, file, cmd, arg); int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
......
...@@ -180,32 +180,29 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed) ...@@ -180,32 +180,29 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed)
static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts)
{ {
struct sirtty_cb *priv = dev->priv; struct sirtty_cb *priv = dev->priv;
int arg = 0; int set = 0;
int clear = 0;
ASSERT(priv != NULL, return -1;); ASSERT(priv != NULL, return -1;);
ASSERT(priv->magic == IRTTY_MAGIC, return -1;); ASSERT(priv->magic == IRTTY_MAGIC, return -1;);
#ifdef TIOCM_OUT2 /* Not defined for ARM */
arg = TIOCM_OUT2;
#endif
if (rts) if (rts)
arg |= TIOCM_RTS; set |= TIOCM_RTS;
else
clear |= TIOCM_RTS;
if (dtr) if (dtr)
arg |= TIOCM_DTR; set |= TIOCM_DTR;
else
clear |= TIOCM_DTR;
/* /*
* The ioctl() function, or actually set_modem_info() in serial.c * We can't use ioctl() because it expects a non-null file structure,
* expects a pointer to the argument in user space. This is working * and we don't have that here.
* here because we are always called from the kIrDAd thread which * This function is not yet defined for all tty driver, so
* has set_fs(KERNEL_DS) permanently set. Therefore copy_from_user() * let's be careful... Jean II
* is happy with our arg-parameter being local here in kernel space.
*/ */
ASSERT(priv->tty->driver.tiocmset != NULL, return -1;);
lock_kernel(); priv->tty->driver.tiocmset(priv->tty, NULL, set, clear);
if (priv->tty->driver.ioctl(priv->tty, NULL, TIOCMSET, (unsigned long) &arg)) {
IRDA_DEBUG(2, "%s(), error doing ioctl!\n", __FUNCTION__);
}
unlock_kernel();
return 0; return 0;
} }
......
...@@ -873,45 +873,38 @@ static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) ...@@ -873,45 +873,38 @@ static int uart_get_lsr_info(struct uart_state *state, unsigned int *value)
return put_user(result, value); return put_user(result, value);
} }
static int uart_get_modem_info(struct uart_port *port, unsigned int *value) static int uart_tiocmget(struct tty_struct *tty, struct file *file)
{ {
unsigned int result = port->mctrl; struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
int result = -EIO;
down(&state->sem);
if ((!file || !tty_hung_up_p(file)) &&
!(tty->flags & (1 << TTY_IO_ERROR))) {
result = port->mctrl;
result |= port->ops->get_mctrl(port); result |= port->ops->get_mctrl(port);
}
up(&state->sem);
return put_user(result, value); return result;
} }
static int static int
uart_set_modem_info(struct uart_port *port, unsigned int cmd, uart_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int *value) unsigned int set, unsigned int clear)
{ {
unsigned int arg, set, clear; struct uart_state *state = tty->driver_data;
int ret = 0; struct uart_port *port = state->port;
int ret = -EIO;
if (get_user(arg, value))
return -EFAULT;
arg &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2;
set = clear = 0; down(&state->sem);
switch (cmd) { if ((!file || !tty_hung_up_p(file)) &&
case TIOCMBIS: !(tty->flags & (1 << TTY_IO_ERROR))) {
set = arg;
break;
case TIOCMBIC:
clear = arg;
break;
case TIOCMSET:
set = arg;
clear = ~arg;
break;
default:
ret = -EINVAL;
break;
}
if (ret == 0)
uart_update_mctrl(port, set, clear); uart_update_mctrl(port, set, clear);
ret = 0;
}
up(&state->sem);
return ret; return ret;
} }
...@@ -922,8 +915,12 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) ...@@ -922,8 +915,12 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state)
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
down(&state->sem);
if (port->type != PORT_UNKNOWN) if (port->type != PORT_UNKNOWN)
port->ops->break_ctl(port, break_state); port->ops->break_ctl(port, break_state);
up(&state->sem);
} }
static int uart_do_autoconfig(struct uart_state *state) static int uart_do_autoconfig(struct uart_state *state)
...@@ -1130,17 +1127,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, ...@@ -1130,17 +1127,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
* protected against the tty being hung up. * protected against the tty being hung up.
*/ */
switch (cmd) { switch (cmd) {
case TIOCMGET:
ret = uart_get_modem_info(state->port, (unsigned int *)arg);
break;
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
ret = uart_set_modem_info(state->port, cmd,
(unsigned int *)arg);
break;
case TIOCSERGETLSR: /* Get line status register */ case TIOCSERGETLSR: /* Get line status register */
ret = uart_get_lsr_info(state, (unsigned int *)arg); ret = uart_get_lsr_info(state, (unsigned int *)arg);
break; break;
...@@ -2162,6 +2148,8 @@ int uart_register_driver(struct uart_driver *drv) ...@@ -2162,6 +2148,8 @@ int uart_register_driver(struct uart_driver *drv)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
normal->read_proc = uart_read_proc; normal->read_proc = uart_read_proc;
#endif #endif
normal->tiocmget = uart_tiocmget;
normal->tiocmset = uart_tiocmset;
/* /*
* Initialise the UART state(s). * Initialise the UART state(s).
......
...@@ -172,6 +172,9 @@ struct tty_driver { ...@@ -172,6 +172,9 @@ struct tty_driver {
int count, int *eof, void *data); int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char *buffer, int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data); unsigned long count, void *data);
int (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
struct list_head tty_drivers; struct list_head tty_drivers;
}; };
......
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