drivers: serial: stm32: rework code for max re-use in wide data support

Restructuring code for poll_in/poll_out/fifo_fill/fifo_read because for
wide data support, all code is identical except the calls to
LL_USART_{ReceiveData8/TransmitData8}.
This allows both implementations, 8 and 9 bit data-width to call a
visitor function, passing the either the 8 bit or 9 bit data-width
function pointer.

Signed-off-by: Jeroen van Dooren <jeroen.van.dooren@nobleo.nl>
diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c
index c02ac85..8407a26 100644
--- a/drivers/serial/uart_stm32.c
+++ b/drivers/serial/uart_stm32.c
@@ -569,7 +569,11 @@
 }
 #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
 
-static int uart_stm32_poll_in(const struct device *dev, unsigned char *c)
+typedef void (*poll_in_fn)(
+	const struct uart_stm32_config *config,
+	void *in);
+
+static int uart_stm32_poll_in_visitor(const struct device *dev, void *in, poll_in_fn get_fn)
 {
 	const struct uart_stm32_config *config = dev->config;
 
@@ -586,13 +590,15 @@
 		return -1;
 	}
 
-	*c = (unsigned char)LL_USART_ReceiveData8(config->usart);
+	get_fn(config, in);
 
 	return 0;
 }
 
-static void uart_stm32_poll_out(const struct device *dev,
-					unsigned char c)
+typedef void (*poll_out_fn)(
+	const struct uart_stm32_config *config, void *out);
+
+static void uart_stm32_poll_out_visitor(const struct device *dev, void *out, poll_out_fn set_fn)
 {
 	const struct uart_stm32_config *config = dev->config;
 #ifdef CONFIG_PM
@@ -635,10 +641,30 @@
 	}
 #endif /* CONFIG_PM */
 
-	LL_USART_TransmitData8(config->usart, (uint8_t)c);
+	set_fn(config, out);
 	irq_unlock(key);
 }
 
+static void uart_stm32_poll_in_u8(const struct uart_stm32_config *config, void *in)
+{
+	*((unsigned char *)in) = (unsigned char)LL_USART_ReceiveData8(config->usart);
+}
+
+static void uart_stm32_poll_out_u8(const struct uart_stm32_config *config, void *out)
+{
+	LL_USART_TransmitData8(config->usart, *((uint8_t *)out));
+}
+
+static int uart_stm32_poll_in(const struct device *dev, unsigned char *c)
+{
+	return uart_stm32_poll_in_visitor(dev, (void *)c, uart_stm32_poll_in_u8);
+}
+
+static void uart_stm32_poll_out(const struct device *dev, unsigned char c)
+{
+	uart_stm32_poll_out_visitor(dev, (void *)&c, uart_stm32_poll_out_u8);
+}
+
 static int uart_stm32_err_check(const struct device *dev)
 {
 	const struct uart_stm32_config *config = dev->config;
@@ -708,9 +734,11 @@
 
 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
 
-static int uart_stm32_fifo_fill(const struct device *dev,
-				  const uint8_t *tx_data,
-				  int size)
+typedef void (*fifo_fill_fn)(const struct uart_stm32_config *config, const void *tx_data,
+			     const uint8_t offset);
+
+static int uart_stm32_fifo_fill_visitor(const struct device *dev, const void *tx_data, int size,
+					fifo_fill_fn fill_fn)
 {
 	const struct uart_stm32_config *config = dev->config;
 	uint8_t num_tx = 0U;
@@ -723,12 +751,12 @@
 	/* Lock interrupts to prevent nested interrupts or thread switch */
 	key = irq_lock();
 
-	while ((size - num_tx > 0) &&
-	       LL_USART_IsActiveFlag_TXE(config->usart)) {
+	while ((size - num_tx > 0) && LL_USART_IsActiveFlag_TXE(config->usart)) {
 		/* TXE flag will be cleared with byte write to DR|RDR register */
 
-		/* Send a character (8bit , parity none) */
-		LL_USART_TransmitData8(config->usart, tx_data[num_tx++]);
+		/* Send a character */
+		fill_fn(config, tx_data, num_tx);
+		num_tx++;
 	}
 
 	irq_unlock(key);
@@ -736,32 +764,70 @@
 	return num_tx;
 }
 
-static int uart_stm32_fifo_read(const struct device *dev, uint8_t *rx_data,
-				  const int size)
+static void fifo_fill_with_u8(const struct uart_stm32_config *config,
+					 const void *tx_data, const uint8_t offset)
+{
+	const uint8_t *data = (const uint8_t *)tx_data;
+	/* Send a character (8bit) */
+	LL_USART_TransmitData8(config->usart, data[offset]);
+}
+
+static int uart_stm32_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
+{
+	if (uart_stm32_ll2cfg_databits(uart_stm32_get_databits(dev), uart_stm32_get_parity(dev)) ==
+	    UART_CFG_DATA_BITS_9) {
+		return -ENOTSUP;
+	}
+	return uart_stm32_fifo_fill_visitor(dev, (const void *)tx_data, size,
+					    fifo_fill_with_u8);
+}
+
+typedef void (*fifo_read_fn)(const struct uart_stm32_config *config, void *rx_data,
+			     const uint8_t offset);
+
+static int uart_stm32_fifo_read_visitor(const struct device *dev, void *rx_data, const int size,
+					fifo_read_fn read_fn)
 {
 	const struct uart_stm32_config *config = dev->config;
 	uint8_t num_rx = 0U;
 
-	while ((size - num_rx > 0) &&
-	       LL_USART_IsActiveFlag_RXNE(config->usart)) {
+	while ((size - num_rx > 0) && LL_USART_IsActiveFlag_RXNE(config->usart)) {
 		/* RXNE flag will be cleared upon read from DR|RDR register */
 
-		/* Receive a character (8bit , parity none) */
-		rx_data[num_rx++] = LL_USART_ReceiveData8(config->usart);
+		read_fn(config, rx_data, num_rx);
+		num_rx++;
 
 		/* Clear overrun error flag */
 		if (LL_USART_IsActiveFlag_ORE(config->usart)) {
 			LL_USART_ClearFlag_ORE(config->usart);
-		/*
-		 * On stm32 F4X, F1X, and F2X, the RXNE flag is affected (cleared) by
-		 * the uart_err_check function call (on errors flags clearing)
-		 */
+			/*
+			 * On stm32 F4X, F1X, and F2X, the RXNE flag is affected (cleared) by
+			 * the uart_err_check function call (on errors flags clearing)
+			 */
 		}
 	}
 
 	return num_rx;
 }
 
+static void fifo_read_with_u8(const struct uart_stm32_config *config, void *rx_data,
+					 const uint8_t offset)
+{
+	uint8_t *data = (uint8_t *)rx_data;
+
+	data[offset] = LL_USART_ReceiveData8(config->usart);
+}
+
+static int uart_stm32_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
+{
+	if (uart_stm32_ll2cfg_databits(uart_stm32_get_databits(dev), uart_stm32_get_parity(dev)) ==
+	    UART_CFG_DATA_BITS_9) {
+		return -ENOTSUP;
+	}
+	return uart_stm32_fifo_read_visitor(dev, (void *)rx_data, size,
+					    fifo_read_with_u8);
+}
+
 static void uart_stm32_irq_tx_enable(const struct device *dev)
 {
 	const struct uart_stm32_config *config = dev->config;