/* * Copyright : (C) 2022 Phytium Information Technology, Inc. * All Rights Reserved. * * This program is OPEN SOURCE software: you can redistribute it and/or modify it * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, * either version 1.0 of the License, or (at your option) 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 Phytium Public License for more details. * * * FilePath: f_printk.c * Date: 2022-02-10 14:53:42 * LastEditTime: 2022-02-17 18:01:29 * Description: This files is for * * Modify History: * Ver Who Date Changes * ----- ------ -------- -------------------------------------- */ #include <stdarg.h> #include <string.h> #include <stdio.h> #include "fkernel.h" #include "ftypes.h" #include "fearly_uart.h" #ifndef __fallthrough #if __GNUC__ >= 7 #define __fallthrough __attribute__((fallthrough)) #else #define __fallthrough #endif /* __GNUC__ >= 7 */ #endif typedef int (*cbprintf_cb)(int c, void *ctx); #define CONFIG_CBPRINTF_FULL_INTEGRAL #ifdef CONFIG_CBPRINTF_FULL_INTEGRAL typedef intmax_t int_value_type; typedef uintmax_t uint_value_type; #define DIGITS_BUFLEN 21 #else typedef s32 int_value_type; typedef u32 uint_value_type; #define DIGITS_BUFLEN 10 #endif #define ALPHA(fmt) (((fmt)&0x60) - '0' - 10 + 1) struct str_context { char *str; int max; int count; }; static int char_out(int c, void *ctx_p) { struct str_context *ctx = ctx_p; ctx->count++; OutByte((s8)c); return 1; } /* Convert value to string, storing characters downwards */ static inline int convert_value(uint_value_type num, unsigned int base, unsigned int alpha, char *buftop) { int i = 0; do { unsigned int c = num % base; if (c >= 10) { c += alpha; } buftop[--i] = c + '0'; num /= base; } while (num); return -i; } #define OUTC(_c) \ do \ { \ out((int)(_c), ctx); \ count++; \ } while (0) #define PAD_ZERO BIT(0) #define PAD_TAIL BIT(1) /** * @brief Printk internals * * See printk() for description. * @param fmt Format string * @param ap Variable parameters * * @return printed byte count if CONFIG_CBPRINTF_LIBC_SUBSTS is set */ int cbvprintf(cbprintf_cb out, void *ctx, const char *fmt, va_list ap) { size_t count = 0; char buf[DIGITS_BUFLEN]; char *prefix, *data; int min_width, precision, data_len; char padding_mode, length_mod, special; /* we pre-increment in the loop afterwards */ fmt--; start: while (*++fmt != '%') { if (*fmt == '\0') { return count; } OUTC(*fmt); } min_width = -1; precision = -1; prefix = ""; padding_mode = 0; length_mod = 0; special = 0; for (fmt++;; fmt++) { switch (*fmt) { case 0: return count; case '%': OUTC('%'); goto start; case '-': padding_mode = PAD_TAIL; continue; case '.': precision = 0; padding_mode &= (char)~PAD_ZERO; continue; case '0': if (min_width < 0 && precision < 0 && !padding_mode) { padding_mode = PAD_ZERO; continue; } __fallthrough; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (precision >= 0) { precision = 10 * precision + *fmt - '0'; } else { if (min_width < 0) { min_width = 0; } min_width = 10 * min_width + *fmt - '0'; } continue; case '*': if (precision >= 0) { precision = va_arg(ap, int); } else { min_width = va_arg(ap, int); if (min_width < 0) { min_width = -min_width; padding_mode = PAD_TAIL; } } continue; case '+': case ' ': case '#': special = *fmt; continue; case 'h': case 'l': case 'z': if (*fmt == 'h' && length_mod == 'h') { length_mod = 'H'; } else if (*fmt == 'l' && length_mod == 'l') { length_mod = 'L'; } else if (length_mod == '\0') { length_mod = *fmt; } else { OUTC('%'); OUTC(*fmt); goto start; } continue; case 'd': case 'i': case 'u': { uint_value_type d; if (length_mod == 'z') { d = va_arg(ap, ssize_t); } else if (length_mod == 'l') { d = va_arg(ap, long); } else if (length_mod == 'L') { long long lld = va_arg(ap, long long); if (sizeof(int_value_type) < 8U && lld != (int_value_type)lld) { data = "ERR"; data_len = 3; precision = 0; break; } d = (uint_value_type)lld; } else if (*fmt == 'u') { d = va_arg(ap, unsigned int); } else { d = va_arg(ap, int); } if (*fmt != 'u' && (int_value_type)d < 0) { d = -d; prefix = "-"; min_width--; } else if (special == ' ') { prefix = " "; min_width--; } else if (special == '+') { prefix = "+"; min_width--; } else { ; } data_len = convert_value(d, 10, 0, buf + sizeof(buf)); data = buf + sizeof(buf) - data_len; break; } case 'p': case 'x': case 'X': { uint_value_type x; if (*fmt == 'p') { x = (uintptr_t)va_arg(ap, void *); if (x == (uint_value_type)0) { data = "(nil)"; data_len = 5; precision = 0; break; } special = '#'; } else if (length_mod == 'l') { x = va_arg(ap, unsigned long); } else if (length_mod == 'L') { x = va_arg(ap, unsigned long long); } else { x = va_arg(ap, unsigned int); } if (special == '#') { prefix = (*fmt & 0x20) ? "0x" : "0x"; min_width -= 2; } data_len = convert_value(x, 16, ALPHA(*fmt), buf + sizeof(buf)); data = buf + sizeof(buf) - data_len; break; } case 's': { data = va_arg(ap, char *); data_len = strlen(data); if (precision >= 0 && data_len > precision) { data_len = precision; } precision = 0; break; } case 'c': { int c = va_arg(ap, int); buf[0] = c; data = buf; data_len = 1; precision = 0; break; } default: OUTC('%'); OUTC(*fmt); goto start; } if (precision < 0 && (padding_mode & PAD_ZERO)) { precision = min_width; } min_width -= data_len; precision -= data_len; if (precision > 0) { min_width -= precision; } if (!(padding_mode & PAD_TAIL)) { while (--min_width >= 0) { OUTC(' '); } } while (*prefix) { OUTC(*prefix++); } while (--precision >= 0) { OUTC('0'); } while (--data_len >= 0) { OUTC(*data++); } while (--min_width >= 0) { OUTC(' '); } goto start; } } static int f_vprintf(const char *restrict format, va_list vargs) { struct str_context ctx = {0}; cbvprintf(char_out, &ctx, format, vargs); } void f_printk(const char *fmt, ...) { va_list ap; va_start(ap, fmt); f_vprintf(fmt, ap); va_end(ap); }