/* * Copyright (C) 2025 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * Generic UARTs * */ #include #include #include #include /** List of registered UARTs */ LIST_HEAD ( uarts ); static void null_uart_transmit ( struct uart *uart __unused, uint8_t byte __unused ) { } static int null_uart_data_ready ( struct uart *uart __unused ) { return 0; } static uint8_t null_uart_receive ( struct uart *uart __unused ) { return 0; } static int null_uart_init ( struct uart *uart __unused ) { return 0; } static void null_uart_flush ( struct uart *uart __unused ) { } /** Null UART operations */ struct uart_operations null_uart_operations = { .transmit = null_uart_transmit, .data_ready = null_uart_data_ready, .receive = null_uart_receive, .init = null_uart_init, .flush = null_uart_flush, }; /** * Allocate UART * * @v priv_len Length of private data * @ret uart UART, or NULL on error */ struct uart * alloc_uart ( size_t priv_len ) { struct uart *uart; /* Allocate and initialise UART */ uart = zalloc ( sizeof ( *uart ) + priv_len ); if ( ! uart ) return NULL; uart->priv = ( ( ( void * ) uart ) + sizeof ( *uart ) ); return uart; } /** * Register fixed UARTs (when not provided by platform) * * @ret rc Return status code */ __weak int uart_register_fixed ( void ) { return 0; } /** * Register UART * * @v uart UART * @ret rc Return status code */ int uart_register ( struct uart *uart ) { /* Add to list of registered UARTs */ uart_get ( uart ); list_add_tail ( &uart->list, &uarts ); DBGC ( uart, "UART %s registered\n", uart->name ); return 0; } /** * Unregister UART * * @v uart UART */ void uart_unregister ( struct uart *uart ) { /* Remove from list of registered UARTs */ list_del ( &uart->list ); uart_put ( uart ); } /** * Find named UART * * @v name UART name * @ret uart UART, or NULL if not found */ struct uart * uart_find ( const char *name ) { struct uart *uart; unsigned int index; char *endp; int rc; /* Register fixed platform UARTs if not already registered */ if ( list_empty ( &uarts ) ) { if ( ( rc = uart_register_fixed() ) != 0 ) { DBGC ( &uarts, "UART could not register fixed UARTs: " "%s\n", strerror ( rc ) ); /* Continue anyway */ } } /* Try parsing name as a numeric index */ index = strtoul ( name, &endp, 10 ); /* Find matching UART, if any */ list_for_each_entry ( uart, &uarts, list ) { /* Check for a matching name */ if ( strcasecmp ( name, uart->name ) == 0 ) return uart; /* Check for a matching numeric index */ if ( ( *endp == '\0' ) && ( index-- == 0 ) ) return uart; } DBGC ( &uarts, "UART %s not found\n", name ); return NULL; }