diff mbox series

[34/35] tty: make tty_get_byte_size available

Message ID 20210505091928.22010-35-jslaby@suse.cz
State Superseded
Headers show
Series None | expand

Commit Message

Jiri Slaby May 5, 2021, 9:19 a.m. UTC
Many tty drivers contain code to compute bits count depending on termios
cflags. So extract this code from serial core to a separate tty helper
function called tty_get_byte_size.

In the next patch, call to this new function will replace many copies of
this code.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 drivers/tty/serial/serial_core.c | 30 +++--------------------
 drivers/tty/tty_ioctl.c          | 41 ++++++++++++++++++++++++++++++++
 include/linux/tty.h              |  2 ++
 3 files changed, 46 insertions(+), 27 deletions(-)

Comments

Joe Perches May 6, 2021, 7:16 a.m. UTC | #1
On Wed, 2021-05-05 at 11:19 +0200, Jiri Slaby wrote:
> Many tty drivers contain code to compute bits count depending on termios

> cflags. So extract this code from serial core to a separate tty helper

> function called tty_get_byte_size.

[]
> diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c

[]
> +/**

> + *	tty_get_byte_size	-	get size of a byte

> + *	@cflag: termios cflag value

> + *	@account_flags: account for start and stop bits, second stop bit (if

> + *			set), and parity (if set)

> + *

> + *	Get the size of a byte in bits depending on @cflag. Depending on

> + *	@account_flags parameter, the result also accounts start and stop bits,

> + *	the second stop bit, and parity bit.

> + */

> +unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

> +{

> +	unsigned char bits = account_flags ? 2 : 0;

> +

> +	/* byte size and parity */

> +	switch (cflag & CSIZE) {

> +	case CS5:

> +		bits += 5;

> +		break;

> +	case CS6:

> +		bits += 6;

> +		break;

> +	case CS7:

> +		bits += 7;

> +		break;

> +	case CS8:

> +	default:

> +		bits += 8;

> +		break;

> +	}

> +

> +	if (account_flags && (cflag & CSTOPB))

> +		bits++;

> +

> +	if (account_flags && (cflag & PARENB))

> +		bits++;

> +

> +	return bits;

> +}

> +EXPORT_SYMBOL_GPL(tty_get_byte_size);


Perhaps clearer not testing account_flags multiple times.

unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)
{
	unsigned char bits;

	/* byte size and parity */
	switch (cflag & CSIZE) {
	case CS5:
		bits = 5;
		break;
	case CS6:
		bits = 6;
		break;
	case CS7:
		bits = 7;
		break;
	case CS8:
	default:
		bits = 8;
		break;
	}

	if (account_flags) {
		bits += 2;	/* start/stop bits */

		if (cflag & CSTOPB)
			bits++;

		if (cflag & PARENB)
			bits++;
	}

	return bits;
}
Jiri Slaby May 6, 2021, 7:19 a.m. UTC | #2
On 06. 05. 21, 9:16, Joe Perches wrote:
> On Wed, 2021-05-05 at 11:19 +0200, Jiri Slaby wrote:

>> Many tty drivers contain code to compute bits count depending on termios

>> cflags. So extract this code from serial core to a separate tty helper

>> function called tty_get_byte_size.

> []

>> diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c

> []

>> +/**

>> + *	tty_get_byte_size	-	get size of a byte

>> + *	@cflag: termios cflag value

>> + *	@account_flags: account for start and stop bits, second stop bit (if

>> + *			set), and parity (if set)

>> + *

>> + *	Get the size of a byte in bits depending on @cflag. Depending on

>> + *	@account_flags parameter, the result also accounts start and stop bits,

>> + *	the second stop bit, and parity bit.

>> + */

>> +unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

>> +{

>> +	unsigned char bits = account_flags ? 2 : 0;

>> +

>> +	/* byte size and parity */

>> +	switch (cflag & CSIZE) {

>> +	case CS5:

>> +		bits += 5;

>> +		break;

>> +	case CS6:

>> +		bits += 6;

>> +		break;

>> +	case CS7:

>> +		bits += 7;

>> +		break;

>> +	case CS8:

>> +	default:

>> +		bits += 8;

>> +		break;

>> +	}

>> +

>> +	if (account_flags && (cflag & CSTOPB))

>> +		bits++;

>> +

>> +	if (account_flags && (cflag & PARENB))

>> +		bits++;

>> +

>> +	return bits;

>> +}

>> +EXPORT_SYMBOL_GPL(tty_get_byte_size);

> 

> Perhaps clearer not testing account_flags multiple times.


Right. Originally, I had account_start_stop, account_stop, and 
account_parity parameters. But they were either all false or all true. 
So I unified them to account_flags, but the code remained overly complex.

> unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

> {

> 	unsigned char bits;

> 

> 	/* byte size and parity */

> 	switch (cflag & CSIZE) {

> 	case CS5:

> 		bits = 5;

> 		break;

> 	case CS6:

> 		bits = 6;

> 		break;

> 	case CS7:

> 		bits = 7;

> 		break;

> 	case CS8:

> 	default:

> 		bits = 8;

> 		break;

> 	}

> 

> 	if (account_flags) {

> 		bits += 2;	/* start/stop bits */

> 

> 		if (cflag & CSTOPB)

> 			bits++;

> 

> 		if (cflag & PARENB)

> 			bits++;

> 	}

> 

> 	return bits;

> }

> 

> 


thanks,
-- 
js
suse labs
Jiri Slaby May 6, 2021, 8:31 a.m. UTC | #3
On 06. 05. 21, 10:24, Andy Shevchenko wrote:
> if (!account_flags)

>    return bits;

> 

> ?


So I have:

unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)
{
         unsigned char bits;

         switch (cflag & CSIZE) {
         case CS5:
                 bits = 5;
                 break;
         case CS6:
                 bits = 6;
                 break;
         case CS7:
                 bits = 7;
                 break;
         case CS8:
         default:
                 bits = 8;
                 break;
         }

         if (!account_flags)
                 return bits;

         if (cflag & CSTOPB)
                 bits++;
         if (cflag & PARENB)
                 bits++;

         return bits + 2;
}

thanks,
-- 
js
suse labs
Joe Perches May 6, 2021, 8:35 a.m. UTC | #4
On Thu, 2021-05-06 at 11:24 +0300, Andy Shevchenko wrote:
> On Thursday, May 6, 2021, Joe Perches <joe@perches.com> wrote:

[]
> > Perhaps clearer not testing account_flags multiple times.

> > 

> > unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

> > {

> >         unsigned char bits;

> > 

> >         /* byte size and parity */


nit:  byte size, the parity bit is tested later.

> >         switch (cflag & CSIZE) {

> >         case CS5:

> >                 bits = 5;

> >                 break;

> >         case CS6:

> >                 bits = 6;

> >                 break;

> >         case CS7:

> >                 bits = 7;

> >                 break;

> >         case CS8:

> >         default:

> >                 bits = 8;

> >                 break;

> >         }

> > 

> >         if (account_flags) {

> 

> 

> 

> if (!account_flags)

>   return bits;

> 

> ?


<shrug> six vs half dozen: 2 level indentation vs early return.

I don't think 2 level indentation is too many.

> >                 bits += 2;      /* start/stop bits */

> > 

> >                 if (cflag & CSTOPB)

> >                         bits++;

> > 

> >                 if (cflag & PARENB)

> >                         bits++;

> >         }


Maybe comment each test as CSTOPB and PARENB aren't completely obvious.
Andy Shevchenko May 6, 2021, 9:33 a.m. UTC | #5
On Thu, May 6, 2021 at 11:31 AM Jiri Slaby <jirislaby@kernel.org> wrote:
>

> On 06. 05. 21, 10:24, Andy Shevchenko wrote:

> > if (!account_flags)

> >    return bits;

> >

> > ?

>

> So I have:



Good to me, feel free to add
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

to this variant.

> unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

> {

>          unsigned char bits;

>

>          switch (cflag & CSIZE) {

>          case CS5:

>                  bits = 5;

>                  break;

>          case CS6:

>                  bits = 6;

>                  break;

>          case CS7:

>                  bits = 7;

>                  break;

>          case CS8:

>          default:

>                  bits = 8;

>                  break;

>          }

>

>          if (!account_flags)

>                  return bits;

>

>          if (cflag & CSTOPB)

>                  bits++;

>          if (cflag & PARENB)

>                  bits++;

>

>          return bits + 2;

> }

>

> thanks,

> --

> js

> suse labs




-- 
With Best Regards,
Andy Shevchenko
Johan Hovold May 10, 2021, 9:47 a.m. UTC | #6
On Mon, May 10, 2021 at 09:00:54AM +0200, Jiri Slaby wrote:
> Many tty drivers contain code to compute bits count depending on termios

> cflags. So extract this code from serial core to a separate tty helper

> function called tty_get_byte_size.

> 

> In the next patch, call to this new function will replace many copies of

> this code.

> 

> [v2] simplified the code flow as suggested by Joe and Andy

> 

> Signed-off-by: Jiri Slaby <jslaby@suse.cz>

> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

> Cc: Joe Perches <joe@perches.com>

> ---

>  drivers/tty/serial/serial_core.c | 30 +++--------------------

>  drivers/tty/tty_ioctl.c          | 42 ++++++++++++++++++++++++++++++++

>  include/linux/tty.h              |  2 ++

>  3 files changed, 47 insertions(+), 27 deletions(-)

> 

> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c

> index d29329eb52f4..b3fc2b02a705 100644

> --- a/drivers/tty/serial/serial_core.c

> +++ b/drivers/tty/serial/serial_core.c

> @@ -334,39 +334,15 @@ void

>  uart_update_timeout(struct uart_port *port, unsigned int cflag,

>  		    unsigned int baud)

>  {

> -	unsigned int bits;

> +	unsigned int size;

>  

> -	/* byte size and parity */

> -	switch (cflag & CSIZE) {

> -	case CS5:

> -		bits = 7;

> -		break;

> -	case CS6:

> -		bits = 8;

> -		break;

> -	case CS7:

> -		bits = 9;

> -		break;

> -	default:

> -		bits = 10;

> -		break; /* CS8 */

> -	}

> -

> -	if (cflag & CSTOPB)

> -		bits++;

> -	if (cflag & PARENB)

> -		bits++;

> -

> -	/*

> -	 * The total number of bits to be transmitted in the fifo.

> -	 */

> -	bits = bits * port->fifosize;

> +	size = tty_get_byte_size(cflag, true) * port->fifosize;

>

>  	/*

>  	 * Figure the timeout to send the above number of bits.

>  	 * Add .02 seconds of slop

>  	 */

> -	port->timeout = (HZ * bits) / baud + HZ/50;

> +	port->timeout = (HZ * size) / baud + HZ/50;

>  }

>  

>  EXPORT_SYMBOL(uart_update_timeout);

> diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c

> index aa9ecc8be990..13acc3decd87 100644

> --- a/drivers/tty/tty_ioctl.c

> +++ b/drivers/tty/tty_ioctl.c

> @@ -300,6 +300,48 @@ int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)

>  }

>  EXPORT_SYMBOL(tty_termios_hw_change);

>  

> +/**

> + *	tty_get_byte_size	-	get size of a byte

> + *	@cflag: termios cflag value

> + *	@account_flags: account for start and stop bits, second stop bit (if

> + *			set), and parity (if set)

> + *

> + *	Get the size of a byte in bits depending on @cflag. Depending on

> + *	@account_flags parameter, the result also accounts start and stop bits,

> + *	the second stop bit, and parity bit.

> + */

> +unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

> +{

> +	unsigned char bits;

> +

> +	switch (cflag & CSIZE) {

> +	case CS5:

> +		bits = 5;

> +		break;

> +	case CS6:

> +		bits = 6;

> +		break;

> +	case CS7:

> +		bits = 7;

> +		break;

> +	case CS8:

> +	default:

> +		bits = 8;

> +		break;

> +	}

> +

> +	if (!account_flags)

> +		return bits;

> +

> +	if (cflag & CSTOPB)

> +		bits++;

> +	if (cflag & PARENB)

> +		bits++;

> +

> +	return bits + 2;

> +}

> +EXPORT_SYMBOL_GPL(tty_get_byte_size);


This should really be two functions rather than passing a bool argument.

I think naming them

	tty_get_word_size()

and

	tty_get_frame_size()

would be much more clear than than "byte size" + flag.

I realise that the serial-driver interface only uses a cflag argument,
but I think we should consider passing a pointer to the termios
structure instead.

Johan
Jiri Slaby May 13, 2021, 7:24 a.m. UTC | #7
On 10. 05. 21, 11:47, Johan Hovold wrote:
>> --- a/drivers/tty/tty_ioctl.c

>> +++ b/drivers/tty/tty_ioctl.c

>> @@ -300,6 +300,48 @@ int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)

...
>> +unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

>> +{

>> +	unsigned char bits;

>> +

>> +	switch (cflag & CSIZE) {

>> +	case CS5:

>> +		bits = 5;

>> +		break;

>> +	case CS6:

>> +		bits = 6;

>> +		break;

>> +	case CS7:

>> +		bits = 7;

>> +		break;

>> +	case CS8:

>> +	default:

>> +		bits = 8;

>> +		break;

>> +	}

>> +

>> +	if (!account_flags)

>> +		return bits;

>> +

>> +	if (cflag & CSTOPB)

>> +		bits++;

>> +	if (cflag & PARENB)

>> +		bits++;

>> +

>> +	return bits + 2;

>> +}

>> +EXPORT_SYMBOL_GPL(tty_get_byte_size);

> 

> This should really be two functions rather than passing a bool argument.

> 

> I think naming them

> 

> 	tty_get_word_size()

> 

> and

> 

> 	tty_get_frame_size()

> 

> would be much more clear than than "byte size" + flag.


Maybe I am screwed, but word means exactly 2B here. So instead, I would 
go for:
s/word/char/ -- might be confused with C's char, 1B, or maybe not -- or
s/word/data/ -- more generic and generally used in serial terminology.

> I realise that the serial-driver interface only uses a cflag argument,

> but I think we should consider passing a pointer to the termios

> structure instead.


That's impossible as termios is not always at hand. Examples are:
pch_uart_startup -> uart_update_timeout
sunsab_console_setup -> sunsab_convert_to_sab -> uart_update_timeout
sunsu_kbd_ms_init -> sunsu_change_speed -> uart_update_timeout

Let me document that in the commit.

thanks,
-- 
js
suse labs
Johan Hovold May 13, 2021, 9:41 a.m. UTC | #8
On Thu, May 13, 2021 at 09:24:03AM +0200, Jiri Slaby wrote:
> On 10. 05. 21, 11:47, Johan Hovold wrote:

> >> --- a/drivers/tty/tty_ioctl.c

> >> +++ b/drivers/tty/tty_ioctl.c

> >> @@ -300,6 +300,48 @@ int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)

> ...

> >> +unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)

> >> +{

> >> +	unsigned char bits;

> >> +

> >> +	switch (cflag & CSIZE) {

> >> +	case CS5:

> >> +		bits = 5;

> >> +		break;

> >> +	case CS6:

> >> +		bits = 6;

> >> +		break;

> >> +	case CS7:

> >> +		bits = 7;

> >> +		break;

> >> +	case CS8:

> >> +	default:

> >> +		bits = 8;

> >> +		break;

> >> +	}

> >> +

> >> +	if (!account_flags)

> >> +		return bits;

> >> +

> >> +	if (cflag & CSTOPB)

> >> +		bits++;

> >> +	if (cflag & PARENB)

> >> +		bits++;

> >> +

> >> +	return bits + 2;

> >> +}

> >> +EXPORT_SYMBOL_GPL(tty_get_byte_size);

> > 

> > This should really be two functions rather than passing a bool argument.

> > 

> > I think naming them

> > 

> > 	tty_get_word_size()

> > 

> > and

> > 

> > 	tty_get_frame_size()

> > 

> > would be much more clear than than "byte size" + flag.

> 

> Maybe I am screwed, but word means exactly 2B here.


No, not in this context.

Some UART datasheets use "word" and "word length", while others use
"character length" or "data bits" (and variations thereof).

> So instead, I would 

> go for:

> s/word/char/ -- might be confused with C's char, 1B, or maybe not -- or

> s/word/data/ -- more generic and generally used in serial terminology.


But "data size" it not very precise.

I'd go for either

	tty_get_word_size() or tty_get_char_size(), and
	tty_get_frame_size()

or possibly

	tty_get_data_bits(), and
	tty_get_frame_bits()

Since posix and the termios interface use "CSIZE" (even if that "C" is
ambiguous) and our man pages use "character size" perhaps we should
stick with that and use:

	tty_get_char_size(), and
	tty_get_frame_size()

That should be clear enough for everyone.

> > I realise that the serial-driver interface only uses a cflag argument,

> > but I think we should consider passing a pointer to the termios

> > structure instead.

> 

> That's impossible as termios is not always at hand. Examples are:

> pch_uart_startup -> uart_update_timeout

> sunsab_console_setup -> sunsab_convert_to_sab -> uart_update_timeout

> sunsu_kbd_ms_init -> sunsu_change_speed -> uart_update_timeout

> 

> Let me document that in the commit.


Sounds good.

Johan
diff mbox series

Patch

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index d29329eb52f4..b3fc2b02a705 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -334,39 +334,15 @@  void
 uart_update_timeout(struct uart_port *port, unsigned int cflag,
 		    unsigned int baud)
 {
-	unsigned int bits;
+	unsigned int size;
 
-	/* byte size and parity */
-	switch (cflag & CSIZE) {
-	case CS5:
-		bits = 7;
-		break;
-	case CS6:
-		bits = 8;
-		break;
-	case CS7:
-		bits = 9;
-		break;
-	default:
-		bits = 10;
-		break; /* CS8 */
-	}
-
-	if (cflag & CSTOPB)
-		bits++;
-	if (cflag & PARENB)
-		bits++;
-
-	/*
-	 * The total number of bits to be transmitted in the fifo.
-	 */
-	bits = bits * port->fifosize;
+	size = tty_get_byte_size(cflag, true) * port->fifosize;
 
 	/*
 	 * Figure the timeout to send the above number of bits.
 	 * Add .02 seconds of slop
 	 */
-	port->timeout = (HZ * bits) / baud + HZ/50;
+	port->timeout = (HZ * size) / baud + HZ/50;
 }
 
 EXPORT_SYMBOL(uart_update_timeout);
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index aa9ecc8be990..72d036917b14 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -300,6 +300,47 @@  int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
 }
 EXPORT_SYMBOL(tty_termios_hw_change);
 
+/**
+ *	tty_get_byte_size	-	get size of a byte
+ *	@cflag: termios cflag value
+ *	@account_flags: account for start and stop bits, second stop bit (if
+ *			set), and parity (if set)
+ *
+ *	Get the size of a byte in bits depending on @cflag. Depending on
+ *	@account_flags parameter, the result also accounts start and stop bits,
+ *	the second stop bit, and parity bit.
+ */
+unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags)
+{
+	unsigned char bits = account_flags ? 2 : 0;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	case CS5:
+		bits += 5;
+		break;
+	case CS6:
+		bits += 6;
+		break;
+	case CS7:
+		bits += 7;
+		break;
+	case CS8:
+	default:
+		bits += 8;
+		break;
+	}
+
+	if (account_flags && (cflag & CSTOPB))
+		bits++;
+
+	if (account_flags && (cflag & PARENB))
+		bits++;
+
+	return bits;
+}
+EXPORT_SYMBOL_GPL(tty_get_byte_size);
+
 /**
  *	tty_set_termios		-	update termios values
  *	@tty: tty to update
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 5cf6b2e7331b..a14b4588368c 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -496,6 +496,8 @@  static inline speed_t tty_get_baud_rate(struct tty_struct *tty)
 	return tty_termios_baud_rate(&tty->termios);
 }
 
+unsigned char tty_get_byte_size(unsigned int cflag, bool account_flags);
+
 extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old);
 extern int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b);
 extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt);