/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup RTEMSBSPsPowerPCQorIQUartBridge * * @brief UART bridge slave implementation. */ /* * Copyright (c) 2011 embedded brains GmbH. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define TRANSMIT_EVENT RTEMS_EVENT_13 static rtems_mode disable_preemption(void) { rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_mode prev_mode = 0; sc = rtems_task_mode (RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &prev_mode); assert(sc == RTEMS_SUCCESSFUL); return prev_mode; } static void restore_preemption(rtems_mode prev_mode) { rtems_status_code sc = RTEMS_SUCCESSFUL; sc = rtems_task_mode (prev_mode, RTEMS_PREEMPT_MASK, &prev_mode); assert(sc == RTEMS_SUCCESSFUL); } static void uart_bridge_slave_service(intercom_packet *packet, void *arg) { uart_bridge_slave_context *ctx = arg; struct rtems_termios_tty *tty = ctx->tty; /* Workaround for https://www.rtems.org/bugzilla/show_bug.cgi?id=1736 */ rtems_mode prev_mode = disable_preemption(); rtems_termios_enqueue_raw_characters(tty, packet->data, (int) packet->size); qoriq_intercom_free_packet(packet); restore_preemption(prev_mode); } static void transmit_task(rtems_task_argument arg) { rtems_status_code sc = RTEMS_SUCCESSFUL; uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) arg; rtems_chain_control *fifo = &ctx->transmit_fifo; struct rtems_termios_tty *tty = ctx->tty; while (true) { intercom_packet *packet = NULL; sc = rtems_chain_get_with_wait( fifo, TRANSMIT_EVENT, RTEMS_NO_TIMEOUT, (rtems_chain_node **) &packet ); assert(sc == RTEMS_SUCCESSFUL); /* Workaround for https://www.rtems.org/bugzilla/show_bug.cgi?id=1736 */ rtems_mode prev_mode = disable_preemption(); size_t size = packet->size; qoriq_intercom_send_packet(QORIQ_UART_BRIDGE_MASTER_CORE, packet); rtems_termios_dequeue_characters(tty, (int) size); restore_preemption(prev_mode); } } static void create_transmit_task( uart_bridge_slave_context *ctx ) { rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_id task = RTEMS_ID_NONE; char index = (char) ('0' + ctx->type - INTERCOM_TYPE_UART_0); sc = rtems_task_create( rtems_build_name('U', 'B', 'T', index), QORIQ_UART_BRIDGE_TASK_PRIORITY, 0, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &task ); assert(sc == RTEMS_SUCCESSFUL); sc = rtems_task_start( task, transmit_task, (rtems_task_argument) ctx ); assert(sc == RTEMS_SUCCESSFUL); ctx->transmit_task = task; } static bool first_open( struct rtems_termios_tty *tty, rtems_termios_device_context *base, struct termios *term, rtems_libio_open_close_args_t *args ) { uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; intercom_type type = ctx->type; ctx->tty = tty; rtems_termios_set_initial_baud(tty, 115200); create_transmit_task(ctx); qoriq_intercom_service_install(type, uart_bridge_slave_service, ctx); return true; } static void last_close( struct rtems_termios_tty *tty, rtems_termios_device_context *base, rtems_libio_open_close_args_t *args ) { uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; qoriq_intercom_service_remove(ctx->type); } static void write_with_interrupts( rtems_termios_device_context *base, const char *buf, size_t len ) { if (len > 0) { rtems_status_code sc = RTEMS_SUCCESSFUL; uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; intercom_packet *packet = qoriq_intercom_allocate_packet( ctx->type, INTERCOM_SIZE_64 ); packet->size = len; memcpy(packet->data, buf, len); /* * Due to the lovely Termios implementation we have to hand this over to * another context. */ sc = rtems_chain_append_with_notification( &ctx->transmit_fifo, &packet->glue.node, ctx->transmit_task, TRANSMIT_EVENT ); assert(sc == RTEMS_SUCCESSFUL); } } static bool set_attributes( rtems_termios_device_context *base, const struct termios *term ) { return false; } const rtems_termios_device_handler qoriq_uart_bridge_slave = { .first_open = first_open, .last_close = last_close, .write = write_with_interrupts, .set_attributes = set_attributes, .mode = TERMIOS_IRQ_DRIVEN };