mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
USB: serial: xr: add TIOCGRS485 and TIOCSRS485 ioctls
Exar devices like XR21B1411 can control an RS485 transceiver by automatically asserting the RTS#/RS485 pin before sending data and deasserting it when the last stop bit has been transmitted. The polarity of the RST#/RS485 signal is configurable and the hardware also supports half-duplex turn-around delay and address matching mode. Add support for enabling and disabling RS-485 mode and configuring the RST#/RS485 signal polarity using the TIOCGRS485 and TIOCSRS485 ioctls. Support for half-duplex turn-around delay and address matching mode are left unimplemented for now. User enables RS-485 mode by setting SER_RS485_ENABLED flag in struct serial_rs485 flags. User should also set either SER_RS485_RTS_ON_SEND or SER_RS485_RTS_AFTER_SEND to select the behaviour of the RTS#/RS485 pin. Setting SER_RS485_RTS_ON_SEND will drive RTS#/RS485 low during transmission. Signed-off-by: Jarkko Sonninen <kasper@iki.fi> [ johan: let SER_RS485_RTS_ON_SEND determine SER_RS485_RTS_AFTER_SEND ] Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
parent
fdf0eaf114
commit
974e2f6a05
@ -93,6 +93,7 @@ struct xr_txrx_clk_mask {
|
||||
#define XR_GPIO_MODE_SEL_DTR_DSR 0x2
|
||||
#define XR_GPIO_MODE_SEL_RS485 0x3
|
||||
#define XR_GPIO_MODE_SEL_RS485_ADDR 0x4
|
||||
#define XR_GPIO_MODE_RS485_TX_H 0x8
|
||||
#define XR_GPIO_MODE_TX_TOGGLE 0x100
|
||||
#define XR_GPIO_MODE_RX_TOGGLE 0x200
|
||||
|
||||
@ -237,6 +238,7 @@ static const struct xr_type xr_types[] = {
|
||||
struct xr_data {
|
||||
const struct xr_type *type;
|
||||
u8 channel; /* zero-based index or interface number */
|
||||
struct serial_rs485 rs485;
|
||||
};
|
||||
|
||||
static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val)
|
||||
@ -629,6 +631,7 @@ static void xr_set_flow_mode(struct tty_struct *tty,
|
||||
struct xr_data *data = usb_get_serial_port_data(port);
|
||||
const struct xr_type *type = data->type;
|
||||
u16 flow, gpio_mode;
|
||||
bool rs485_enabled;
|
||||
int ret;
|
||||
|
||||
ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode);
|
||||
@ -645,7 +648,17 @@ static void xr_set_flow_mode(struct tty_struct *tty,
|
||||
/* Set GPIO mode for controlling the pins manually by default. */
|
||||
gpio_mode &= ~XR_GPIO_MODE_SEL_MASK;
|
||||
|
||||
if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) {
|
||||
rs485_enabled = !!(data->rs485.flags & SER_RS485_ENABLED);
|
||||
if (rs485_enabled) {
|
||||
dev_dbg(&port->dev, "Enabling RS-485\n");
|
||||
gpio_mode |= XR_GPIO_MODE_SEL_RS485;
|
||||
if (data->rs485.flags & SER_RS485_RTS_ON_SEND)
|
||||
gpio_mode &= ~XR_GPIO_MODE_RS485_TX_H;
|
||||
else
|
||||
gpio_mode |= XR_GPIO_MODE_RS485_TX_H;
|
||||
}
|
||||
|
||||
if (C_CRTSCTS(tty) && C_BAUD(tty) != B0 && !rs485_enabled) {
|
||||
dev_dbg(&port->dev, "Enabling hardware flow ctrl\n");
|
||||
gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS;
|
||||
flow = XR_UART_FLOW_MODE_HW;
|
||||
@ -809,6 +822,79 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty,
|
||||
kfree(lc);
|
||||
}
|
||||
|
||||
static void xr_sanitize_serial_rs485(struct serial_rs485 *rs485)
|
||||
{
|
||||
if (!(rs485->flags & SER_RS485_ENABLED)) {
|
||||
memset(rs485, 0, sizeof(*rs485));
|
||||
return;
|
||||
}
|
||||
|
||||
/* RTS always toggles after TX */
|
||||
if (rs485->flags & SER_RS485_RTS_ON_SEND)
|
||||
rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
|
||||
else
|
||||
rs485->flags |= SER_RS485_RTS_AFTER_SEND;
|
||||
|
||||
/* Only the flags are implemented at the moment */
|
||||
rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
|
||||
SER_RS485_RTS_AFTER_SEND;
|
||||
rs485->delay_rts_before_send = 0;
|
||||
rs485->delay_rts_after_send = 0;
|
||||
memset(rs485->padding, 0, sizeof(rs485->padding));
|
||||
}
|
||||
|
||||
static int xr_get_rs485_config(struct tty_struct *tty,
|
||||
struct serial_rs485 __user *argp)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct xr_data *data = usb_get_serial_port_data(port);
|
||||
|
||||
down_read(&tty->termios_rwsem);
|
||||
if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
|
||||
up_read(&tty->termios_rwsem);
|
||||
return -EFAULT;
|
||||
}
|
||||
up_read(&tty->termios_rwsem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xr_set_rs485_config(struct tty_struct *tty,
|
||||
struct serial_rs485 __user *argp)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct xr_data *data = usb_get_serial_port_data(port);
|
||||
struct serial_rs485 rs485;
|
||||
|
||||
if (copy_from_user(&rs485, argp, sizeof(rs485)))
|
||||
return -EFAULT;
|
||||
xr_sanitize_serial_rs485(&rs485);
|
||||
|
||||
down_write(&tty->termios_rwsem);
|
||||
data->rs485 = rs485;
|
||||
xr_set_flow_mode(tty, port, NULL);
|
||||
up_write(&tty->termios_rwsem);
|
||||
|
||||
if (copy_to_user(argp, &rs485, sizeof(rs485)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xr_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case TIOCGRS485:
|
||||
return xr_get_rs485_config(tty, argp);
|
||||
case TIOCSRS485:
|
||||
return xr_set_rs485_config(tty, argp);
|
||||
}
|
||||
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static void xr_set_termios(struct tty_struct *tty,
|
||||
struct usb_serial_port *port,
|
||||
const struct ktermios *old_termios)
|
||||
@ -1010,6 +1096,7 @@ static struct usb_serial_driver xr_device = {
|
||||
.set_termios = xr_set_termios,
|
||||
.tiocmget = xr_tiocmget,
|
||||
.tiocmset = xr_tiocmset,
|
||||
.ioctl = xr_ioctl,
|
||||
.dtr_rts = xr_dtr_rts
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user