Re: [PATCH 10/11] serial: make uart_console_write->putchar()'s character a char

From: Maciej W. Rozycki
Date: Wed Jan 26 2022 - 12:57:32 EST


On Mon, 24 Jan 2022, Jiri Slaby wrote:

> diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
> index e9edabc5a211..3493e201d67f 100644
> --- a/drivers/tty/serial/dz.c
> +++ b/drivers/tty/serial/dz.c
> @@ -802,7 +802,7 @@ static void __init dz_init_ports(void)
> * restored. Welcome to the world of PDP-11!
> * -------------------------------------------------------------------
> */
> -static void dz_console_putchar(struct uart_port *uport, int ch)
> +static void dz_console_putchar(struct uart_port *uport, char ch)
> {
> struct dz_port *dport = to_dport(uport);
> unsigned long flags;

Hmm, this is unsafe, because on the MIPS target the lone `char' type is
signed and therefore a call to `->putchar' will see `ch' sign-extended
from bit #7 to the width of the argument register used. Which means that
if a character is sent to the console that has its bit #7 set, then the
call to:

dz_out(dport, DZ_TDR, ch);

i.e.:

static void dz_out(struct dz_port *dport, unsigned offset, u16 value)

will send a value to DZ_TDR with bits #15:8 set to all-ones. And bits
#11:8 there are the BREAK control bits, active high, for serial lines #3:0
respectively.

We could handle this with a preparatory change by calling:

dz_out(dport, DZ_TDR, ch & 0xffu);

instead, but perhaps `->putchar' should simply take `unsigned char' or
maybe even `u8' as its third argument?

> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
> index c58cc142d23f..68e62703eaa6 100644
> --- a/include/linux/serial_core.h
> +++ b/include/linux/serial_core.h
> @@ -399,7 +399,7 @@ int uart_set_options(struct uart_port *port, struct console
> *co, int baud,
> struct tty_driver *uart_console_device(struct console *co, int *index);
> void uart_console_write(struct uart_port *port, const char *s,
> unsigned int count,
> - void (*putchar)(struct uart_port *, int));
> + void (*putchar)(struct uart_port *, char));
>
> /*
> * Port/driver registration/removal

I.e.:

void (*putchar)(struct uart_port *, unsigned char));

I can see we get it right already with:

unsigned char x_char; /* xon/xoff char */

and for `dz_transmit_chars' we have:

unsigned char tmp;
[...]
tmp = xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1);
dz_out(dport, DZ_TDR, tmp);

(because `struct circ_buf' is generic and not limited to unsigned buffer
contents interpretation; it's not clear to me if that has been intended
though).

Maciej