All of lore.kernel.org
 help / color / mirror / Atom feed
From: Johan Hovold <johan@kernel.org>
To: Michael Hanselmann <public@hansmi.ch>
Cc: linux-usb@vger.kernel.org, Johan Hovold <johan@kernel.org>,
	Michael Dreher <michael@5dot1.de>,
	Jonathan Olds <jontio@i4free.co.nz>
Subject: Re: [PATCH v2 6/6] USB: serial: ch341: Simulate break condition if not supported
Date: Thu, 14 May 2020 16:47:21 +0200	[thread overview]
Message-ID: <20200514144721.GG25962@localhost> (raw)
In-Reply-To: <91bacfa4097350b4731724f5820e06bc03e7e8f3.1585697281.git.public@hansmi.ch>

On Tue, Mar 31, 2020 at 11:37:22PM +0000, Michael Hanselmann wrote:
> A subset of all CH341 devices don't support a real break condition. This
> fact is already used in the "ch341_detect_quirks" function. With this
> change a quirk is implemented to simulate a break condition by
> temporarily lowering the baud rate and sending a NUL byte.
> 
> The primary drawback of this approach is that the duration of the break
> can't be controlled by userland.
> 
> The "TTY_DRIVER_HARDWARE_BREAK" serial driver flag was investigated as
> an alternative. It's a driver-wide flag and would've required
> significant changes to the serial and USB-serial driver frameworks to
> expose it for individual USB-serial adapters.
> 
> Tested by sending a break condition and watching the TX pin using an
> oscilloscope.
> 
> Signed-off-by: Michael Hanselmann <public@hansmi.ch>
> ---
>  drivers/usb/serial/ch341.c | 95 ++++++++++++++++++++++++++++++++++----
>  1 file changed, 85 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
> index c820772e6a07..fc8ef816d143 100644
> --- a/drivers/usb/serial/ch341.c
> +++ b/drivers/usb/serial/ch341.c
> @@ -25,6 +25,7 @@
>  #define DEFAULT_TIMEOUT   1000
>  
>  #define CH341_QUIRK_LIMITED_PRESCALER 0x01
> +#define CH341_QUIRK_SIMULATE_BREAK    0x02
>
>  /* flags for IO-Bits */
>  #define CH341_BIT_RTS (1 << 6)
> @@ -94,6 +95,7 @@ struct ch341_private {
>  	u8 msr;
>  	u8 lcr;
>  	u8 quirks;
> +	unsigned long break_end;
>  };
>  
>  static void ch341_set_termios(struct tty_struct *tty,
> @@ -203,14 +205,11 @@ static int ch341_calc_divisor(speed_t speed, unsigned int ps,
>   *		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)
>  {
> -	speed_t speed;
>  	unsigned int fact, div, clk_div;
>  	int ps;
>  
> -	speed = priv->baud_rate;
> -
>  	/*
>  	 * Clamp to supported range, making the later range sanity checks
>  	 * redundant.
> @@ -282,15 +281,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,
> +				  unsigned baud_rate, u8 lcr)

Use speed_t.

>  {
>  	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;
>  
> @@ -369,7 +369,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;
>  
> @@ -401,7 +401,7 @@ static int ch341_detect_quirks(struct usb_device *dev)
>  	if (r == -EPIPE) {
>  		dev_dbg(&dev->dev, "%s - reading break condition register"
>  			" failed (%d)\n", __func__, r);
> -		r = CH341_QUIRK_LIMITED_PRESCALER;
> +		r = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
>  		goto out;
>  	}
>  
> @@ -579,7 +579,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);
> @@ -598,15 +599,89 @@ 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.
> + *
> + * 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 1s / 46bd * 10bit = 217ms.
> + */
> +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 delay;
> +	int r;
> +
> +	if (break_state != 0) {
> +		dev_dbg(&port->dev, "%s - Enter break state requested\n",
> +			__func__);
> +
> +		r = ch341_set_baudrate_lcr(port->serial->dev, priv,
> +			CH341_MIN_BPS,
> +			CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX |

Hmm. This would corrupt incoming data as well during the break.

> +			CH341_LCR_CS8);
> +		if (r < 0) {
> +			dev_err(&port->dev, "%s - baud rate status %d\n",
> +				__func__, r);

Spell out error messages instead of relying on __func__; "failed to
change baudrate: %d\n"?

> +			goto restore;
> +		}
> +
> +		r = tty_put_char(tty, '\0');
> +		if (r < 0) {
> +			dev_err(&port->dev, "%s - write status %d\n",
> +				__func__, r);

ditto

> +			goto restore;
> +		}
> +
> +		priv->break_end = jiffies + (10 * HZ / CH341_MIN_BPS);
> +
> +		return;
> +	}
> +
> +	dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
> +
> +	if (time_before(jiffies, priv->break_end)) {
> +		/* Wait until NUL byte is written */
> +		delay = min_t(unsigned long, HZ, priv->break_end - jiffies);

Looks like this can underflow if you're preempted after the check.

> +
> +		dev_dbg(&port->dev, "%s - sleep for %d ms\n", __func__,
> +			jiffies_to_msecs(delay));
> +		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, "%s - baud rate status %d\n", __func__, 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) {
> +		dev_warn_once(&port->dev,
> +			      "%s - hardware doesn't support real break"
> +			      " condition, simulating instead\n",
> +			      __func__);

Don't break the string, and drop the __func__.

> +		ch341_simulate_break(tty, break_state);
> +		return;
> +	}
> +
>  	break_reg = kmalloc(2, GFP_KERNEL);
>  	if (!break_reg)
>  		return;

Johan

  reply	other threads:[~2020-05-14 14:47 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-31 23:37 [PATCH v2 0/6] USB: serial: ch341: Add support for limited chips (was: Add support for HL340 devices) Michael Hanselmann
2020-03-31 23:37 ` [PATCH v2 1/6] USB: serial: ch341: Reduce special cases in clock calculation Michael Hanselmann
2020-03-31 23:37 ` [PATCH v2 2/6] USB: serial: ch341: Add basis for quirk detection Michael Hanselmann
2020-05-14 14:09   ` Johan Hovold
2020-03-31 23:37 ` [PATCH v2 3/6] USB: serial: ch341: Limit prescaler on quirky chips Michael Hanselmann
2020-05-14 14:17   ` Johan Hovold
2020-05-27 13:16     ` Johan Hovold
2020-05-27 15:41       ` Michael Hanselmann
2020-05-29  7:15         ` Johan Hovold
2020-03-31 23:37 ` [PATCH v2 4/6] USB: serial: ch341: Name prescaler, divisor registers Michael Hanselmann
2020-05-14 14:24   ` Johan Hovold
2020-05-27 20:59     ` Michael Hanselmann
2020-06-29  9:51       ` Johan Hovold
2020-03-31 23:37 ` [PATCH v2 5/6] USB: serial: ch341: Compute minimum baud rate Michael Hanselmann
2020-05-27 22:19   ` Michael Hanselmann
2020-06-30  9:57     ` Johan Hovold
2020-03-31 23:37 ` [PATCH v2 6/6] USB: serial: ch341: Simulate break condition if not supported Michael Hanselmann
2020-05-14 14:47   ` Johan Hovold [this message]
2020-05-27 22:21     ` Michael Hanselmann
2020-06-30 11:39       ` Johan Hovold
2020-07-04 18:25         ` Michael Hanselmann
2020-07-06  9:31           ` Johan Hovold
2020-05-14 14:02 ` [PATCH v2 0/6] USB: serial: ch341: Add support for limited chips (was: Add support for HL340 devices) Johan Hovold

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200514144721.GG25962@localhost \
    --to=johan@kernel.org \
    --cc=jontio@i4free.co.nz \
    --cc=linux-usb@vger.kernel.org \
    --cc=michael@5dot1.de \
    --cc=public@hansmi.ch \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.