| /* |
| * Copyright (C) 2014-2018 Amlogic, Inc. All rights reserved. |
| * |
| * All information contained herein is Amlogic confidential. |
| * |
| * This software is provided to you pursuant to Software License Agreement |
| * (SLA) with Amlogic Inc ("Amlogic"). This software may be used |
| * only in accordance with the terms of this agreement. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification is strictly prohibited without prior written permission from |
| * Amlogic. |
| * |
| * 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. |
| */ |
| |
| /* Printf-like functionality for Chrome EC */ |
| |
| #include "aml_printf.h" |
| #include "projdefs.h" |
| #include "util.h" |
| //#include <stdarg.h> |
| //#include <stdlib.h> |
| //#include <stdio.h> |
| #include <string.h> |
| #include "uart.h" |
| #include "FreeRTOS.h" |
| #include "task.h" /* RTOS task related API prototypes. */ |
| |
| static const char error_str[] = "ERROR"; |
| |
| #define MAX_FORMAT 1024 /* Maximum chars in a single format field */ |
| static char printbuffer[512]; |
| |
| /** |
| * Convert the lowest nibble of a number to hex |
| * |
| * @param c Number to extract lowest nibble from |
| * |
| * @return The corresponding ASCII character ('0' - 'f'). |
| */ |
| static int hexdigit(int c) |
| { |
| /* Strip off just the last nibble */ |
| c &= 0x0f; |
| |
| return c > 9 ? (c + 'a' - 10) : (c + '0'); |
| } |
| |
| /* Flags for vfnprintf() flags */ |
| #define PF_LEFT (1 << 0) /* Left-justify */ |
| #define PF_PADZERO (1 << 1) /* Pad with 0's not spaces */ |
| #define PF_NEGATIVE (1 << 2) /* Number is negative */ |
| #define PF_64BIT (1 << 3) /* Number is 64-bit */ |
| #if 1 |
| int vfnprintf(int (*addchar)(void *context, int c), void *context, |
| const char *format, va_list args) |
| { |
| /* |
| * Longest uint64 in decimal = 20 |
| * Longest uint32 in binary = 32 |
| * + sign bit |
| * + terminating null |
| */ |
| char intbuf[34]; |
| int flags; |
| int pad_width; |
| int precision; |
| char *vstr; |
| int vlen; |
| |
| while (*format) { |
| int c = *format++; |
| |
| /* Copy normal characters */ |
| if (c != '%') { |
| if (addchar(context, c)) |
| return pdFREERTOS_ERRNO_EINVAL; |
| continue; |
| } |
| |
| /* Zero flags, now that we're in a format */ |
| flags = 0; |
| |
| /* Get first format character */ |
| c = *format++; |
| |
| /* Send "%" for "%%" input */ |
| if (c == '%' || c == '\0') { |
| if (addchar(context, '%')) |
| return pdFREERTOS_ERRNO_EINVAL; |
| continue; |
| } |
| |
| /* Handle %c */ |
| if (c == 'c') { |
| c = va_arg(args, int); |
| |
| if (addchar(context, c)) |
| return pdFREERTOS_ERRNO_EINVAL; |
| continue; |
| } |
| |
| /* Handle left-justification ("%-5s") */ |
| if (c == '-') { |
| flags |= PF_LEFT; |
| c = *format++; |
| } |
| |
| /* Handle padding with 0's */ |
| if (c == '0') { |
| flags |= PF_PADZERO; |
| c = *format++; |
| } |
| |
| /* Count padding length */ |
| pad_width = 0; |
| if (c == '*') { |
| pad_width = va_arg(args, int); |
| |
| c = *format++; |
| } else { |
| while (c >= '0' && c <= '9') { |
| pad_width = (10 * pad_width) + c - '0'; |
| c = *format++; |
| } |
| } |
| if (pad_width < 0 || pad_width > MAX_FORMAT) { |
| /* Sanity check for precision failed */ |
| format = error_str; |
| continue; |
| } |
| /* Count precision */ |
| precision = 0; |
| if (c == '.') { |
| c = *format++; |
| if (c == '*') { |
| precision = va_arg(args, int); |
| |
| c = *format++; |
| } else { |
| while (c >= '0' && c <= '9') { |
| precision = (10 * precision) + c - '0'; |
| c = *format++; |
| } |
| } |
| if (precision < 0 || precision > MAX_FORMAT) { |
| /* Sanity check for precision failed */ |
| format = error_str; |
| continue; |
| } |
| } |
| |
| if (c == 's') { |
| vstr = va_arg(args, char *); |
| |
| if (vstr == NULL) { /*Fix me */ |
| ; //vstr = "(NULL)"; |
| } |
| } else if (c == 'h') { |
| /* Hex dump output */ |
| vstr = va_arg(args, char *); |
| |
| if (!precision) { |
| /* Hex dump requires precision */ |
| format = error_str; |
| continue; |
| } |
| |
| for (; precision; precision--, vstr++) { |
| if (addchar(context, hexdigit(*vstr >> 4)) || |
| addchar(context, hexdigit(*vstr))) |
| return pdFREERTOS_ERRNO_EINVAL; |
| } |
| |
| continue; |
| } else { |
| uint64_t v; |
| int base = 10; |
| |
| /* Handle length */ |
| if (c == 'l') { |
| if (sizeof(long) == 8) |
| flags |= PF_64BIT; |
| c = *format++; |
| if (c == 'l') { |
| flags |= PF_64BIT; |
| c = *format++; // long long is 64bit at LP64 |
| } |
| } |
| |
| /* Special-case: %T = current time */ |
| if (c == 'T') { |
| //v = get_time().val; |
| flags |= PF_64BIT; |
| precision = 6; |
| } else if (flags & PF_64BIT) { |
| v = va_arg(args, uint64_t); |
| } else { |
| v = va_arg(args, uint32_t); |
| } |
| |
| switch (c) { |
| case 'd': |
| if (flags & PF_64BIT) { |
| if ((int64_t) v < 0) { |
| flags |= PF_NEGATIVE; |
| if (v != (1ULL << 63)) |
| v = -v; |
| } |
| } else { |
| if ((int)v < 0) { |
| flags |= PF_NEGATIVE; |
| if (v != (1ULL << 31)) |
| v = -(int)v; |
| } |
| } |
| break; |
| case 'u': |
| case 'T': |
| break; |
| case 'X': |
| case 'x': |
| case 'p': |
| base = 16; |
| break; |
| case 'b': |
| base = 2; |
| break; |
| default: |
| format = error_str; |
| } |
| if (format == error_str) |
| continue; /* Bad format specifier */ |
| |
| /* |
| * Convert integer to string, starting at end of |
| * buffer and working backwards. |
| */ |
| vstr = intbuf + sizeof(intbuf) - 1; |
| *(vstr) = '\0'; |
| |
| /* |
| * Fixed-point precision must fit in our buffer. |
| * Leave space for "0." and the terminating null. |
| */ |
| if (precision > (int)(sizeof(intbuf) - 3)) |
| precision = (int)(sizeof(intbuf) - 3); |
| |
| /* |
| * Handle digits to right of decimal for fixed point |
| * numbers. |
| */ |
| for (vlen = 0; vlen < precision; vlen++) |
| *(--vstr) = '0' + uint64divmod(&v, 10); |
| if (precision) |
| *(--vstr) = '.'; |
| |
| if (!v) |
| *(--vstr) = '0'; |
| |
| while (v) { |
| int digit = uint64divmod(&v, base); |
| |
| if (digit < 10) |
| *(--vstr) = '0' + digit; |
| else if (c == 'X') |
| *(--vstr) = 'A' + digit - 10; |
| else |
| *(--vstr) = 'a' + digit - 10; |
| } |
| |
| if (flags & PF_NEGATIVE) |
| *(--vstr) = '-'; |
| |
| /* |
| * Precision field was interpreted by fixed-point |
| * logic, so clear it. |
| */ |
| precision = 0; |
| } |
| |
| /* Copy string (or stringified integer) */ |
| if (vstr != NULL) |
| vlen = strlen(vstr); |
| else |
| vlen = 0; |
| |
| /* No padding strings to wider than the precision */ |
| if (precision > 0 && pad_width > precision) |
| pad_width = precision; |
| |
| /* If precision is zero, print everything */ |
| if (!precision) |
| precision = MAX(vlen, pad_width); |
| |
| while (vlen < pad_width && !(flags & PF_LEFT)) { |
| if (addchar(context, flags & PF_PADZERO ? '0' : ' ')) |
| return pdFREERTOS_ERRNO_EINVAL; |
| vlen++; |
| } |
| |
| if (vstr != NULL) { |
| while (*vstr && --precision >= 0) |
| if (addchar(context, *vstr++)) |
| return pdFREERTOS_ERRNO_EINVAL; |
| } |
| while (vlen < pad_width && flags & PF_LEFT) { |
| if (addchar(context, ' ')) |
| return pdFREERTOS_ERRNO_EINVAL; |
| vlen++; |
| } |
| } |
| |
| /* If we're still here, we consumed all output */ |
| return pdFREERTOS_ERRNO_NONE; |
| } |
| #endif |
| /* Context for snprintf() */ |
| struct snprintf_context { |
| char *str; |
| size_t size; |
| }; |
| |
| /** |
| * Add a character to the string context. |
| * |
| * @param context Context receiving character |
| * @param c Character to add |
| * @return 0 if character added, 1 if character dropped because no space. |
| */ |
| static int snprintf_addchar(void *context, int c) |
| { |
| struct snprintf_context *ctx = (struct snprintf_context *)context; |
| |
| if (!ctx->size) |
| return 1; |
| |
| *(ctx->str++) = c; |
| ctx->size--; |
| |
| return 0; |
| } |
| |
| int sPrintf_ext(char *str, size_t size, const char *format, va_list args) |
| { |
| struct snprintf_context ctx; |
| int rv; |
| |
| if (!str || !size) |
| return pdFREERTOS_ERRNO_EINVAL; |
| |
| ctx.str = str; |
| ctx.size = size - 1; /* Reserve space for terminating '\0' */ |
| |
| rv = vfnprintf(snprintf_addchar, &ctx, format, args); |
| |
| /* Terminate string */ |
| *ctx.str = '\0'; |
| |
| return rv; |
| } |
| |
| int sPrintf(char *str, size_t size, const char *fmt, ...) |
| { |
| va_list args; |
| int i; |
| |
| va_start(args, fmt); |
| |
| i = sPrintf_ext(str, size, fmt, args); |
| va_end(args); |
| |
| return i; |
| } |
| |
| //int iprintf(const char *fmt, ...) |
| int printf(const char *fmt, ...) |
| { |
| va_list args; |
| int i; |
| UBaseType_t uxSavedInterruptStatus; |
| |
| //char buf[20] = {0}; |
| |
| uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); |
| |
| va_start(args, fmt); |
| |
| /* For this to work, printbuffer must be larger than |
| * anything we ever want to print. |
| */ |
| i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args); |
| va_end(args); |
| { |
| /* Print the string */ |
| //sprintf(buf, "%lld : ", (long long int)xTaskGetTickCount()); |
| //vSerialPutString(ConsoleSerial, buf); |
| //vSerialPutString(ConsoleSerial, printbuffer); |
| vUartPuts(printbuffer); |
| } |
| portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); |
| return i; |
| } |
| |
| int iprint_string(char *str) |
| { |
| //vSerialPutString(ConsoleSerial, str); |
| vUartPuts(str); |
| |
| return 0; |
| } |
| |
| extern int puts(const char *str); |
| |
| int puts(const char *str) |
| { |
| vUartPuts(str); |
| |
| return 0; |
| } |
| |