/* * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #if MULTI_CONSOLE_API #include #include #include .globl console_register .globl console_unregister .globl console_is_registered .globl console_set_scope .globl console_switch_state .globl console_putc .globl console_getc .globl console_flush /* * The console list pointer is in the data section and not in * .bss even though it is zero-init. In particular, this allows * the console functions to start using this variable before * the runtime memory is initialized for images which do not * need to copy the .data section from ROM to RAM. */ .section .data.console_list ; .align 2 console_list: .word 0x0 .section .data.console_state ; .align 0 console_state: .byte CONSOLE_FLAG_BOOT /* ----------------------------------------------- * int console_register(console_t *console) * Function to insert a new console structure into * the console list. Should usually be called by * console__register implementations. The * data structure passed will be taken over by the * console framework and *MUST* be allocated in * persistent memory (e.g. the data section). * In : r0 - address of console_t structure * Out: r0 - Always 1 (for easier tail calling) * Clobber list: r0, r1 * ----------------------------------------------- */ func console_register push {r6, lr} #if ENABLE_ASSERTIONS /* Assert that r0 isn't a NULL pointer */ cmp r0, #0 ASM_ASSERT(ne) /* Assert that the struct isn't in the stack */ ldr r1, =__STACKS_START__ cmp r0, r1 blo not_on_stack ldr r1, =__STACKS_END__ cmp r0, r1 ASM_ASSERT(hs) not_on_stack: /* Assert that this struct isn't in the list */ mov r1, r0 /* Preserve r0 and lr */ bl console_is_registered cmp r0, #0 ASM_ASSERT(eq) mov r0, r1 #endif /* ENABLE_ASSERTIONS */ ldr r6, =console_list ldr r1, [r6] /* R1 = first struct in list */ str r0, [r6] /* list head = new console */ str r1, [r0, #CONSOLE_T_NEXT] /* new console next ptr = R1 */ mov r0, #1 pop {r6, pc} endfunc console_register /* ----------------------------------------------- * int console_unregister(console_t *console) * Function to find a specific console in the list * of currently active consoles and remove it. * In: r0 - address of console_t struct to remove * Out: r0 - removed address, or NULL if not found * Clobber list: r0, r1 * ----------------------------------------------- */ func console_unregister #if ENABLE_ASSERTIONS /* Assert that r0 isn't a NULL pointer */ cmp r0, #0 ASM_ASSERT(ne) #endif /* ENABLE_ASSERTIONS */ push {r6} ldr r6, =console_list /* R6 = ptr to first struct */ ldr r1, [r6] /* R1 = first struct */ unregister_loop: cmp r1, #0 beq unregister_not_found cmp r0, r1 beq unregister_found ldr r6, [r6] /* R6 = next ptr of struct */ ldr r1, [r6] /* R1 = next struct */ b unregister_loop unregister_found: ldr r1, [r1] /* R1 = next struct */ str r1, [r6] /* prev->next = cur->next */ pop {r6} bx lr unregister_not_found: mov r0, #0 /* return NULL if not found */ pop {r6} bx lr endfunc console_unregister /* ----------------------------------------------- * int console_is_registered(console_t *console) * Function to detect if a specific console is * registered or not. * In: r0 - address of console_t struct to remove * Out: r0 - 1 if it is registered, 0 if not. * Clobber list: r0 * ----------------------------------------------- */ func console_is_registered #if ENABLE_ASSERTIONS /* Assert that r0 isn't a NULL pointer */ cmp r0, #0 ASM_ASSERT(ne) #endif /* ENABLE_ASSERTIONS */ push {r6} ldr r6, =console_list ldr r6, [r6] /* R6 = first console struct */ check_registered_loop: cmp r6, #0 /* Check if end of list */ beq console_not_registered cmp r0, r6 /* Check if the pointers are different */ beq console_registered ldr r6, [r6, #CONSOLE_T_NEXT] /* Get pointer to next struct */ b check_registered_loop console_not_registered: mov r0, #0 pop {r6} bx lr console_registered: mov r0, #1 pop {r6} bx lr endfunc console_is_registered /* ----------------------------------------------- * void console_switch_state(unsigned int new_state) * Function to switch the current console state. * The console state determines which of the * registered consoles are actually used at a time. * In : r0 - global console state to move to * Clobber list: r0, r1 * ----------------------------------------------- */ func console_switch_state ldr r1, =console_state strb r0, [r1] bx lr endfunc console_switch_state /* ----------------------------------------------- * void console_set_scope(console_t *console, * unsigned int scope) * Function to update the states that a given console * may be active in. * In : r0 - pointer to console_t struct * : r1 - new active state mask * Clobber list: r0, r1, r2 * ----------------------------------------------- */ func console_set_scope #if ENABLE_ASSERTIONS ands r2, r1, #~CONSOLE_FLAG_SCOPE_MASK ASM_ASSERT(eq) #endif /* ENABLE_ASSERTIONS */ ldr r2, [r0, #CONSOLE_T_FLAGS] and r2, r2, #~CONSOLE_FLAG_SCOPE_MASK orr r2, r2, r1 str r2, [r0, #CONSOLE_T_FLAGS] bx lr endfunc console_set_scope /* --------------------------------------------- * int console_putc(int c) * Function to output a character. Calls all * active console's putc() handlers in succession. * In : r0 - character to be printed * Out: r0 - printed character on success, or < 0 if at least one console had an error * Clobber list : r0, r1, r2 * --------------------------------------------- */ func console_putc push {r4-r6, lr} mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */ mov r4, r0 /* R4 = character to print */ ldr r6, =console_list ldr r6, [r6] /* R6 = first console struct */ putc_loop: cmp r6, #0 beq putc_done ldr r1, =console_state ldrb r1, [r1] ldr r2, [r6, #CONSOLE_T_FLAGS] tst r1, r2 beq putc_continue ldr r2, [r6, #CONSOLE_T_PUTC] cmp r2, #0 beq putc_continue mov r0, r4 mov r1, r6 blx r2 cmp r5, #ERROR_NO_VALID_CONSOLE /* update R5 if it's NOVALID */ cmpne r0, #0 /* else update it if R0 < 0 */ movlt r5, r0 putc_continue: ldr r6, [r6] /* R6 = next struct */ b putc_loop putc_done: mov r0, r5 pop {r4-r6, pc} endfunc console_putc /* --------------------------------------------- * int console_getc(void) * Function to get a character from any console. * Keeps looping through all consoles' getc() * handlers until one of them returns a * character, then stops iterating and returns * that character to the caller. Will stop looping * if all active consoles report real errors * (other than just not having a char available). * Out : r0 - read character, or < 0 on error * Clobber list : r0, r1 * --------------------------------------------- */ func console_getc push {r5-r6, lr} getc_try_again: mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */ ldr r6, =console_list ldr r6, [r6] /* R6 = first console struct */ cmp r6, #0 bne getc_loop mov r0, r5 /* If no consoles registered */ pop {r5-r6, pc} /* return immediately. */ getc_loop: ldr r0, =console_state ldrb r0, [r0] ldr r1, [r6, #CONSOLE_T_FLAGS] tst r0, r1 beq getc_continue ldr r1, [r6, #CONSOLE_T_GETC] cmp r1, #0 beq getc_continue mov r0, r6 blx r1 cmp r0, #0 /* if R0 >= 0: return */ bge getc_found cmp r5, #ERROR_NO_PENDING_CHAR /* may update R5 (NOCHAR has */ movne r5, r0 /* precedence vs real errors) */ getc_continue: ldr r6, [r6] /* R6 = next struct */ cmp r6, #0 bne getc_loop cmp r5, #ERROR_NO_PENDING_CHAR /* Keep scanning if at least */ beq getc_try_again /* one console returns NOCHAR */ mov r0, r5 getc_found: pop {r5-r6, pc} endfunc console_getc /* --------------------------------------------- * int console_flush(void) * Function to force a write of all buffered * data that hasn't been output. Calls all * console's flush() handlers in succession. * Out: r0 - 0 on success, < 0 if at least one error * Clobber list : r0, r1, r2 * --------------------------------------------- */ func console_flush push {r5-r6, lr} mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */ ldr r6, =console_list ldr r6, [r6] /* R6 = first console struct */ flush_loop: cmp r6, #0 beq flush_done ldr r1, =console_state ldrb r1, [r1] ldr r2, [r6, #CONSOLE_T_FLAGS] tst r1, r2 beq flush_continue ldr r1, [r6, #CONSOLE_T_FLUSH] cmp r1, #0 beq flush_continue mov r0, r6 blx r1 cmp r5, #ERROR_NO_VALID_CONSOLE /* update R5 if it's NOVALID */ cmpne r0, #0 /* else update it if R0 < 0 */ movlt r5, r0 flush_continue: ldr r6, [r6] /* R6 = next struct */ b flush_loop flush_done: mov r0, r5 pop {r5-r6, pc} endfunc console_flush #endif /* MULTI_CONSOLE_API */