Commit fa56dd91 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'usb-serial-5.9-rc1' of...

Merge tag 'usb-serial-5.9-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next

Johan writes:

USB-serial updates for 5.9-rc1

Here are the USB-serial updates for 5.9-rc1, including:

 - console flow-control support
 - simulated line-breaks on some ch341
 - hardware flow-control fixes for cp210x
 - break-detection and sysrq fixes for ftdi_sio
 - sysrq optimisations
 - input parity checking for cp210x

Included are also some new device ids and various clean ups.

All have been in linux-next with no reported issues.

* tag 'usb-serial-5.9-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial: (31 commits)
  USB: serial: qcserial: add EM7305 QDL product ID
  USB: serial: iuu_phoenix: fix led-activity helpers
  USB: serial: sierra: clean up special-interface handling
  USB: serial: cp210x: use in-kernel types in port data
  USB: serial: cp210x: drop unnecessary packed attributes
  USB: serial: cp210x: add support for TIOCGICOUNT
  USB: serial: cp210x: add support for line-status events
  USB: serial: cp210x: disable interface on errors in open
  USB: serial: drop redundant transfer-buffer casts
  USB: serial: drop extern keyword from function declarations
  USB: serial: drop unnecessary sysrq include
  USB: serial: add sysrq break-handler dummy
  USB: serial: inline sysrq dummy function
  USB: serial: only process sysrq when enabled
  USB: serial: only set sysrq timestamp for consoles
  USB: serial: ftdi_sio: fix break and sysrq handling
  USB: serial: ftdi_sio: clean up receive processing
  USB: serial: ftdi_sio: make process-packet buffer unsigned
  USB: serial: use fallthrough pseudo-keyword
  USB: serial: ch341: fix missing simulated-break margin
  ...
parents 25252919 d2a4309c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -117,7 +117,7 @@ static int aircable_process_packet(struct usb_serial_port *port,
static void aircable_process_read_urb(struct urb *urb)
{
	struct usb_serial_port *port = urb->context;
	char *data = (char *)urb->transfer_buffer;
	char *data = urb->transfer_buffer;
	int has_headers;
	int count;
	int len;
+115 −12
Original line number Diff line number Diff line
@@ -59,7 +59,11 @@
#define CH341_REQ_MODEM_CTRL   0xA4

#define CH341_REG_BREAK        0x05
#define CH341_REG_PRESCALER    0x12
#define CH341_REG_DIVISOR      0x13
#define CH341_REG_LCR          0x18
#define CH341_REG_LCR2         0x25

#define CH341_NBREAK_BITS      0x01

#define CH341_LCR_ENABLE_RX    0x80
@@ -74,6 +78,7 @@
#define CH341_LCR_CS5          0x00

#define CH341_QUIRK_LIMITED_PRESCALER	BIT(0)
#define CH341_QUIRK_SIMULATE_BREAK	BIT(1)

static const struct usb_device_id id_table[] = {
	{ USB_DEVICE(0x4348, 0x5523) },
@@ -91,6 +96,7 @@ struct ch341_private {
	u8 msr;
	u8 lcr;
	unsigned long quirks;
	unsigned long break_end;
};

static void ch341_set_termios(struct tty_struct *tty,
@@ -153,6 +159,10 @@ static const speed_t ch341_min_rates[] = {
	CH341_MIN_RATE(3),
};

/* Supported range is 46 to 3000000 bps. */
#define CH341_MIN_BPS	DIV_ROUND_UP(CH341_CLKRATE, CH341_CLK_DIV(0, 0) * 256)
#define CH341_MAX_BPS	(CH341_CLKRATE / (CH341_CLK_DIV(3, 0) * 2))

/*
 * The device line speed is given by the following equation:
 *
@@ -163,10 +173,9 @@ static const speed_t ch341_min_rates[] = {
 *		2 <= div <= 256 if fact = 0, or
 *		9 <= div <= 256 if fact = 1
 */
static int ch341_get_divisor(struct ch341_private *priv)
static int ch341_get_divisor(struct ch341_private *priv, speed_t speed)
{
	unsigned int fact, div, clk_div;
	speed_t speed = priv->baud_rate;
	bool force_fact0 = false;
	int ps;

@@ -174,7 +183,7 @@ static int ch341_get_divisor(struct ch341_private *priv)
	 * Clamp to supported range, this makes the (ps < 0) and (div < 2)
	 * sanity checks below redundant.
	 */
	speed = clamp(speed, 46U, 3000000U);
	speed = clamp_val(speed, CH341_MIN_BPS, CH341_MAX_BPS);

	/*
	 * Start with highest possible base clock (fact = 1) that will give a
@@ -229,15 +238,16 @@ static int ch341_get_divisor(struct ch341_private *priv)
}

static int ch341_set_baudrate_lcr(struct usb_device *dev,
				  struct ch341_private *priv, u8 lcr)
				  struct ch341_private *priv,
				  speed_t baud_rate, u8 lcr)
{
	int val;
	int r;

	if (!priv->baud_rate)
	if (!baud_rate)
		return -EINVAL;

	val = ch341_get_divisor(priv);
	val = ch341_get_divisor(priv, baud_rate);
	if (val < 0)
		return -EINVAL;

@@ -247,11 +257,20 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
	 */
	val |= BIT(7);

	r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, val);
	r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
			      CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER,
			      val);
	if (r)
		return r;

	r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
	/*
	 * Chip versions before version 0x30 as read using
	 * CH341_REQ_READ_VERSION used separate registers for line control
	 * (stop bits, parity and word length). Version 0x30 and above use
	 * CH341_REG_LCR only and CH341_REG_LCR2 is always set to zero.
	 */
	r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
			      CH341_REG_LCR2 << 8 | CH341_REG_LCR, lcr);
	if (r)
		return r;

@@ -308,7 +327,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
	if (r < 0)
		goto out;

	r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
	r = ch341_set_baudrate_lcr(dev, priv, priv->baud_rate, priv->lcr);
	if (r < 0)
		goto out;

@@ -341,8 +360,8 @@ static int ch341_detect_quirks(struct usb_serial_port *port)
			    USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
			    CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT);
	if (r == -EPIPE) {
		dev_dbg(&port->dev, "break control not supported\n");
		quirks = CH341_QUIRK_LIMITED_PRESCALER;
		dev_info(&port->dev, "break control not supported, using simulated break\n");
		quirks = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
		r = 0;
		goto out;
	}
@@ -523,7 +542,8 @@ static void ch341_set_termios(struct tty_struct *tty,
	if (baud_rate) {
		priv->baud_rate = baud_rate;

		r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr);
		r = ch341_set_baudrate_lcr(port->serial->dev, priv,
					   priv->baud_rate, lcr);
		if (r < 0 && old_termios) {
			priv->baud_rate = tty_termios_baud_rate(old_termios);
			tty_termios_copy_hw(&tty->termios, old_termios);
@@ -542,15 +562,98 @@ static void ch341_set_termios(struct tty_struct *tty,
	ch341_set_handshake(port->serial->dev, priv->mcr);
}

/*
 * A subset of all CH34x devices don't support a real break condition and
 * reading CH341_REG_BREAK fails (see also ch341_detect_quirks). This function
 * simulates a break condition by lowering the baud rate to the minimum
 * supported by the hardware upon enabling the break condition and sending
 * a NUL byte.
 *
 * Incoming data is corrupted while the break condition is being simulated.
 *
 * Normally the duration of the break condition can be controlled individually
 * by userspace using TIOCSBRK and TIOCCBRK or by passing an argument to
 * TCSBRKP. Due to how the simulation is implemented the duration can't be
 * controlled. The duration is always about (1s / 46bd * 9bit) = 196ms.
 */
static void ch341_simulate_break(struct tty_struct *tty, int break_state)
{
	struct usb_serial_port *port = tty->driver_data;
	struct ch341_private *priv = usb_get_serial_port_data(port);
	unsigned long now, delay;
	int r;

	if (break_state != 0) {
		dev_dbg(&port->dev, "enter break state requested\n");

		r = ch341_set_baudrate_lcr(port->serial->dev, priv,
				CH341_MIN_BPS,
				CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8);
		if (r < 0) {
			dev_err(&port->dev,
				"failed to change baud rate to %u: %d\n",
				CH341_MIN_BPS, r);
			goto restore;
		}

		r = tty_put_char(tty, '\0');
		if (r < 0) {
			dev_err(&port->dev,
				"failed to write NUL byte for simulated break condition: %d\n",
				r);
			goto restore;
		}

		/*
		 * Compute expected transmission duration including safety
		 * margin. The original baud rate is only restored after the
		 * computed point in time.
		 *
		 * 11 bits = 1 start, 8 data, 1 stop, 1 margin
		 */
		priv->break_end = jiffies + (11 * HZ / CH341_MIN_BPS);

		return;
	}

	dev_dbg(&port->dev, "leave break state requested\n");

	now = jiffies;

	if (time_before(now, priv->break_end)) {
		/* Wait until NUL byte is written */
		delay = priv->break_end - now;
		dev_dbg(&port->dev,
			"wait %d ms while transmitting NUL byte at %u baud\n",
			jiffies_to_msecs(delay), CH341_MIN_BPS);
		schedule_timeout_interruptible(delay);
	}

restore:
	/* Restore original baud rate */
	r = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate,
				   priv->lcr);
	if (r < 0)
		dev_err(&port->dev,
			"restoring original baud rate of %u failed: %d\n",
			priv->baud_rate, r);
}

static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
	const uint16_t ch341_break_reg =
			((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
	struct usb_serial_port *port = tty->driver_data;
	struct ch341_private *priv = usb_get_serial_port_data(port);
	int r;
	uint16_t reg_contents;
	uint8_t *break_reg;

	if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) {
		ch341_simulate_break(tty, break_state);
		return;
	}

	break_reg = kmalloc(2, GFP_KERNEL);
	if (!break_reg)
		return;
+4 −1
Original line number Diff line number Diff line
@@ -102,6 +102,9 @@ static int usb_console_setup(struct console *co, char *options)
		break;
	}

	if (doflow)
		cflag |= CRTSCTS;

	/*
	 * no need to check the index here: if the index is wrong, console
	 * code won't call us
+220 −8
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ static void cp210x_release(struct usb_serial *);
static int cp210x_port_probe(struct usb_serial_port *);
static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
static void cp210x_process_read_urb(struct urb *urb);
static void cp210x_enable_event_mode(struct usb_serial_port *port);
static void cp210x_disable_event_mode(struct usb_serial_port *port);

static const struct usb_device_id id_table[] = {
	{ USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */
@@ -253,9 +256,21 @@ struct cp210x_serial_private {
	bool			use_actual_rate;
};

enum cp210x_event_state {
	ES_DATA,
	ES_ESCAPE,
	ES_LSR,
	ES_LSR_DATA_0,
	ES_LSR_DATA_1,
	ES_MSR
};

struct cp210x_port_private {
	__u8			bInterfaceNumber;
	u8			bInterfaceNumber;
	bool			has_swapped_line_ctl;
	bool			event_mode;
	enum cp210x_event_state event_state;
	u8 lsr;
};

static struct usb_serial_driver cp210x_device = {
@@ -272,14 +287,18 @@ static struct usb_serial_driver cp210x_device = {
	.break_ctl		= cp210x_break_ctl,
	.set_termios		= cp210x_set_termios,
	.tx_empty		= cp210x_tx_empty,
	.throttle		= usb_serial_generic_throttle,
	.unthrottle		= usb_serial_generic_unthrottle,
	.tiocmget		= cp210x_tiocmget,
	.tiocmset		= cp210x_tiocmset,
	.get_icount		= usb_serial_generic_get_icount,
	.attach			= cp210x_attach,
	.disconnect		= cp210x_disconnect,
	.release		= cp210x_release,
	.port_probe		= cp210x_port_probe,
	.port_remove		= cp210x_port_remove,
	.dtr_rts		= cp210x_dtr_rts
	.dtr_rts		= cp210x_dtr_rts,
	.process_read_urb	= cp210x_process_read_urb,
};

static struct usb_serial_driver * const serial_drivers[] = {
@@ -401,13 +420,22 @@ struct cp210x_comm_status {
 */
#define PURGE_ALL		0x000f

/* CP210X_EMBED_EVENTS */
#define CP210X_ESCCHAR		0xec

#define CP210X_LSR_OVERRUN	BIT(1)
#define CP210X_LSR_PARITY	BIT(2)
#define CP210X_LSR_FRAME	BIT(3)
#define CP210X_LSR_BREAK	BIT(4)


/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
struct cp210x_flow_ctl {
	__le32	ulControlHandshake;
	__le32	ulFlowReplace;
	__le32	ulXonLimit;
	__le32	ulXoffLimit;
} __packed;
};

/* cp210x_flow_ctl::ulControlHandshake */
#define CP210X_SERIAL_DTR_MASK		GENMASK(1, 0)
@@ -441,7 +469,7 @@ struct cp210x_flow_ctl {
struct cp210x_pin_mode {
	u8	eci;
	u8	sci;
} __packed;
};

#define CP210X_PIN_MODE_MODEM		0
#define CP210X_PIN_MODE_GPIO		BIT(0)
@@ -504,7 +532,7 @@ struct cp210x_single_port_config {
struct cp210x_gpio_write {
	u8	mask;
	u8	state;
} __packed;
};

/*
 * Helper to get interface number when we only have struct usb_serial.
@@ -807,6 +835,7 @@ static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl)

static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
{
	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
	int result;

	result = cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_ENABLE);
@@ -818,21 +847,144 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
	/* Configure the termios structure */
	cp210x_get_termios(tty, port);

	if (tty) {
		/* The baud rate must be initialised on cp2104 */
	if (tty)
		cp210x_change_speed(tty, port, NULL);

	return usb_serial_generic_open(tty, port);
		if (I_INPCK(tty))
			cp210x_enable_event_mode(port);
	}

	result = usb_serial_generic_open(tty, port);
	if (result)
		goto err_disable;

	return 0;

err_disable:
	cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE);
	port_priv->event_mode = false;

	return result;
}

static void cp210x_close(struct usb_serial_port *port)
{
	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);

	usb_serial_generic_close(port);

	/* Clear both queues; cp2108 needs this to avoid an occasional hang */
	cp210x_write_u16_reg(port, CP210X_PURGE, PURGE_ALL);

	cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE);

	/* Disabling the interface disables event-insertion mode. */
	port_priv->event_mode = false;
}

static void cp210x_process_lsr(struct usb_serial_port *port, unsigned char lsr, char *flag)
{
	if (lsr & CP210X_LSR_BREAK) {
		port->icount.brk++;
		*flag = TTY_BREAK;
	} else if (lsr & CP210X_LSR_PARITY) {
		port->icount.parity++;
		*flag = TTY_PARITY;
	} else if (lsr & CP210X_LSR_FRAME) {
		port->icount.frame++;
		*flag = TTY_FRAME;
	}

	if (lsr & CP210X_LSR_OVERRUN) {
		port->icount.overrun++;
		tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
	}
}

static bool cp210x_process_char(struct usb_serial_port *port, unsigned char *ch, char *flag)
{
	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);

	switch (port_priv->event_state) {
	case ES_DATA:
		if (*ch == CP210X_ESCCHAR) {
			port_priv->event_state = ES_ESCAPE;
			break;
		}
		return false;
	case ES_ESCAPE:
		switch (*ch) {
		case 0:
			dev_dbg(&port->dev, "%s - escape char\n", __func__);
			*ch = CP210X_ESCCHAR;
			port_priv->event_state = ES_DATA;
			return false;
		case 1:
			port_priv->event_state = ES_LSR_DATA_0;
			break;
		case 2:
			port_priv->event_state = ES_LSR;
			break;
		case 3:
			port_priv->event_state = ES_MSR;
			break;
		default:
			dev_err(&port->dev, "malformed event 0x%02x\n", *ch);
			port_priv->event_state = ES_DATA;
			break;
		}
		break;
	case ES_LSR_DATA_0:
		port_priv->lsr = *ch;
		port_priv->event_state = ES_LSR_DATA_1;
		break;
	case ES_LSR_DATA_1:
		dev_dbg(&port->dev, "%s - lsr = 0x%02x, data = 0x%02x\n",
				__func__, port_priv->lsr, *ch);
		cp210x_process_lsr(port, port_priv->lsr, flag);
		port_priv->event_state = ES_DATA;
		return false;
	case ES_LSR:
		dev_dbg(&port->dev, "%s - lsr = 0x%02x\n", __func__, *ch);
		port_priv->lsr = *ch;
		cp210x_process_lsr(port, port_priv->lsr, flag);
		port_priv->event_state = ES_DATA;
		break;
	case ES_MSR:
		dev_dbg(&port->dev, "%s - msr = 0x%02x\n", __func__, *ch);
		/* unimplemented */
		port_priv->event_state = ES_DATA;
		break;
	}

	return true;
}

static void cp210x_process_read_urb(struct urb *urb)
{
	struct usb_serial_port *port = urb->context;
	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
	unsigned char *ch = urb->transfer_buffer;
	char flag;
	int i;

	if (!urb->actual_length)
		return;

	if (port_priv->event_mode) {
		for (i = 0; i < urb->actual_length; i++, ch++) {
			flag = TTY_NORMAL;

			if (cp210x_process_char(port, ch, &flag))
				continue;

			tty_insert_flip_char(&port->port, *ch, flag);
		}
	} else {
		tty_insert_flip_string(&port->port, ch, urb->actual_length);
	}
	tty_flip_buffer_push(&port->port);
}

/*
@@ -915,6 +1067,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
	u32 baud;
	u16 bits;
	u32 ctl_hs;
	u32 flow_repl;

	cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud);

@@ -1015,6 +1168,22 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
	ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
	if (ctl_hs & CP210X_SERIAL_CTS_HANDSHAKE) {
		dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
		/*
		 * When the port is closed, the CP210x hardware disables
		 * auto-RTS and RTS is deasserted but it leaves auto-CTS when
		 * in hardware flow control mode. When re-opening the port, if
		 * auto-CTS is enabled on the cp210x, then auto-RTS must be
		 * re-enabled in the driver.
		 */
		flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace);
		flow_repl &= ~CP210X_SERIAL_RTS_MASK;
		flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_FLOW_CTL);
		flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
		cp210x_write_reg_block(port,
				CP210X_SET_FLOW,
				&flow_ctl,
				sizeof(flow_ctl));

		cflag |= CRTSCTS;
	} else {
		dev_dbg(dev, "%s - flow control = NONE\n", __func__);
@@ -1148,6 +1317,41 @@ static void cp210x_change_speed(struct tty_struct *tty,
	tty_encode_baud_rate(tty, baud, baud);
}

static void cp210x_enable_event_mode(struct usb_serial_port *port)
{
	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
	int ret;

	if (port_priv->event_mode)
		return;

	port_priv->event_state = ES_DATA;
	port_priv->event_mode = true;

	ret = cp210x_write_u16_reg(port, CP210X_EMBED_EVENTS, CP210X_ESCCHAR);
	if (ret) {
		dev_err(&port->dev, "failed to enable events: %d\n", ret);
		port_priv->event_mode = false;
	}
}

static void cp210x_disable_event_mode(struct usb_serial_port *port)
{
	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
	int ret;

	if (!port_priv->event_mode)
		return;

	ret = cp210x_write_u16_reg(port, CP210X_EMBED_EVENTS, 0);
	if (ret) {
		dev_err(&port->dev, "failed to disable events: %d\n", ret);
		return;
	}

	port_priv->event_mode = false;
}

static void cp210x_set_termios(struct tty_struct *tty,
		struct usb_serial_port *port, struct ktermios *old_termios)
{
@@ -1270,6 +1474,14 @@ static void cp210x_set_termios(struct tty_struct *tty,
				sizeof(flow_ctl));
	}

	/*
	 * Enable event-insertion mode only if input parity checking is
	 * enabled for now.
	 */
	if (I_INPCK(tty))
		cp210x_enable_event_mode(port);
	else
		cp210x_disable_event_mode(port);
}

static int cp210x_tiocmset(struct tty_struct *tty,
+2 −2
Original line number Diff line number Diff line
@@ -1048,7 +1048,7 @@ static void cypress_read_int_callback(struct urb *urb)
		return;
	case -EPIPE:
		/* Can't call usb_clear_halt while in_interrupt */
		/* FALLS THROUGH */
		fallthrough;
	default:
		/* something ugly is going on... */
		dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
@@ -1197,7 +1197,7 @@ static void cypress_write_int_callback(struct urb *urb)
		return;
	case -EPIPE:
		/* Cannot call usb_clear_halt while in_interrupt */
		/* FALLTHROUGH */
		fallthrough;
	default:
		dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
			__func__, status);
Loading