blob: adee95fd6b8db376bc233de6365b6cb90c36730f [file] [log] [blame]
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +08001/*
yang.lib06e0a82022-01-10 17:35:09 +08002 * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +08003 *
yang.lib06e0a82022-01-10 17:35:09 +08004 * SPDX-License-Identifier: MIT
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +08005 */
6
Xiaohu.Huang448314d2021-12-31 17:12:48 +08007#include <projdefs.h>
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +08008#include <string.h>
Xiaohu.Huang448314d2021-12-31 17:12:48 +08009#include <FreeRTOS.h>
10#include <task.h>
11#include "aml_printf.h"
12#if (1 == CONFIG_ARM64)
13 #include "serial.h"
14#else
15 #include "uart.h"
16#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080017static const char error_str[] = "ERROR";
18
19#define MAX_FORMAT 1024 /* Maximum chars in a single format field */
20static char printbuffer[512];
21
Xiaohu.Huangaa18c4c2021-11-16 17:26:41 +080022#ifndef MAX
23#define MAX(a, b) \
24 ({ \
25 __typeof__(a) temp_a = (a); \
26 __typeof__(b) temp_b = (b); \
27 \
28 temp_a > temp_b ? temp_a : temp_b; \
29 })
30#endif
31
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080032/**
33 * Convert the lowest nibble of a number to hex
34 *
35 * @param c Number to extract lowest nibble from
36 *
37 * @return The corresponding ASCII character ('0' - 'f').
38 */
39static int hexdigit(int c)
40{
41 /* Strip off just the last nibble */
42 c &= 0x0f;
43
44 return c > 9 ? (c + 'a' - 10) : (c + '0');
45}
46
Xiaohu.Huangaa18c4c2021-11-16 17:26:41 +080047static int uint64divmod(uint64_t *n, int d)
48{
49 uint64_t q = 0, mask;
50 int r = 0;
51
52 /* Divide-by-zero returns zero */
53 if (!d) {
54 *n = 0;
55 return 0;
56 }
57
58 /* Common powers of 2 = simple shifts */
59 if (d == 2) {
60 r = *n & 1;
61 *n >>= 1;
62 return r;
63 } else if (d == 16) {
64 r = *n & 0xf;
65 *n >>= 4;
66 return r;
67 }
68
69 /* If v fits in 32-bit, we're done. */
70 if (*n <= 0xffffffff) {
71 uint32_t v32 = *n;
72 r = v32 % d;
73 *n = v32 / d;
74 return r;
75 }
76
77 /* Otherwise do integer division the slow way. */
78 for (mask = (1ULL << 63); mask; mask >>= 1) {
79 r <<= 1;
80 if (*n & mask)
81 r |= 1;
82 if (r >= d) {
83 r -= d;
84 q |= mask;
85 }
86 }
87 *n = q;
88 return r;
89}
90
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080091/* Flags for vfnprintf() flags */
92#define PF_LEFT (1 << 0) /* Left-justify */
93#define PF_PADZERO (1 << 1) /* Pad with 0's not spaces */
94#define PF_NEGATIVE (1 << 2) /* Number is negative */
95#define PF_64BIT (1 << 3) /* Number is 64-bit */
96#if 1
97int vfnprintf(int (*addchar)(void *context, int c), void *context,
98 const char *format, va_list args)
99{
100 /*
101 * Longest uint64 in decimal = 20
102 * Longest uint32 in binary = 32
103 * + sign bit
104 * + terminating null
105 */
106 char intbuf[34];
107 int flags;
108 int pad_width;
109 int precision;
110 char *vstr;
111 int vlen;
112
113 while (*format) {
114 int c = *format++;
115
116 /* Copy normal characters */
117 if (c != '%') {
118 if (addchar(context, c))
119 return pdFREERTOS_ERRNO_EINVAL;
120 continue;
121 }
122
123 /* Zero flags, now that we're in a format */
124 flags = 0;
125
126 /* Get first format character */
127 c = *format++;
128
129 /* Send "%" for "%%" input */
130 if (c == '%' || c == '\0') {
131 if (addchar(context, '%'))
132 return pdFREERTOS_ERRNO_EINVAL;
133 continue;
134 }
135
136 /* Handle %c */
137 if (c == 'c') {
138 c = va_arg(args, int);
139
140 if (addchar(context, c))
141 return pdFREERTOS_ERRNO_EINVAL;
142 continue;
143 }
144
145 /* Handle left-justification ("%-5s") */
146 if (c == '-') {
147 flags |= PF_LEFT;
148 c = *format++;
149 }
150
151 /* Handle padding with 0's */
152 if (c == '0') {
153 flags |= PF_PADZERO;
154 c = *format++;
155 }
156
157 /* Count padding length */
158 pad_width = 0;
159 if (c == '*') {
160 pad_width = va_arg(args, int);
161
162 c = *format++;
163 } else {
164 while (c >= '0' && c <= '9') {
165 pad_width = (10 * pad_width) + c - '0';
166 c = *format++;
167 }
168 }
169 if (pad_width < 0 || pad_width > MAX_FORMAT) {
170 /* Sanity check for precision failed */
171 format = error_str;
172 continue;
173 }
174 /* Count precision */
175 precision = 0;
176 if (c == '.') {
177 c = *format++;
178 if (c == '*') {
179 precision = va_arg(args, int);
180
181 c = *format++;
182 } else {
183 while (c >= '0' && c <= '9') {
184 precision = (10 * precision) + c - '0';
185 c = *format++;
186 }
187 }
188 if (precision < 0 || precision > MAX_FORMAT) {
189 /* Sanity check for precision failed */
190 format = error_str;
191 continue;
192 }
193 }
194
195 if (c == 's') {
196 vstr = va_arg(args, char *);
197
198 if (vstr == NULL) { /*Fix me */
199 ; //vstr = "(NULL)";
200 }
201 } else if (c == 'h') {
202 /* Hex dump output */
203 vstr = va_arg(args, char *);
204
205 if (!precision) {
206 /* Hex dump requires precision */
207 format = error_str;
208 continue;
209 }
210
211 for (; precision; precision--, vstr++) {
212 if (addchar(context, hexdigit(*vstr >> 4)) ||
213 addchar(context, hexdigit(*vstr)))
214 return pdFREERTOS_ERRNO_EINVAL;
215 }
216
217 continue;
218 } else {
219 uint64_t v;
220 int base = 10;
221
222 /* Handle length */
223 if (c == 'l') {
224 if (sizeof(long) == 8)
225 flags |= PF_64BIT;
226 c = *format++;
227 if (c == 'l') {
228 flags |= PF_64BIT;
229 c = *format++; // long long is 64bit at LP64
230 }
231 }
232
233 /* Special-case: %T = current time */
234 if (c == 'T') {
235 //v = get_time().val;
236 flags |= PF_64BIT;
237 precision = 6;
238 } else if (flags & PF_64BIT) {
239 v = va_arg(args, uint64_t);
240 } else {
241 v = va_arg(args, uint32_t);
242 }
243
244 switch (c) {
245 case 'd':
246 if (flags & PF_64BIT) {
247 if ((int64_t) v < 0) {
248 flags |= PF_NEGATIVE;
249 if (v != (1ULL << 63))
250 v = -v;
251 }
252 } else {
253 if ((int)v < 0) {
254 flags |= PF_NEGATIVE;
255 if (v != (1ULL << 31))
256 v = -(int)v;
257 }
258 }
259 break;
260 case 'u':
261 case 'T':
262 break;
263 case 'X':
264 case 'x':
265 case 'p':
266 base = 16;
267 break;
268 case 'b':
269 base = 2;
270 break;
271 default:
272 format = error_str;
273 }
274 if (format == error_str)
275 continue; /* Bad format specifier */
276
277 /*
278 * Convert integer to string, starting at end of
279 * buffer and working backwards.
280 */
281 vstr = intbuf + sizeof(intbuf) - 1;
282 *(vstr) = '\0';
283
284 /*
285 * Fixed-point precision must fit in our buffer.
286 * Leave space for "0." and the terminating null.
287 */
288 if (precision > (int)(sizeof(intbuf) - 3))
289 precision = (int)(sizeof(intbuf) - 3);
290
291 /*
292 * Handle digits to right of decimal for fixed point
293 * numbers.
294 */
295 for (vlen = 0; vlen < precision; vlen++)
296 *(--vstr) = '0' + uint64divmod(&v, 10);
297 if (precision)
298 *(--vstr) = '.';
299
300 if (!v)
301 *(--vstr) = '0';
302
303 while (v) {
304 int digit = uint64divmod(&v, base);
305
306 if (digit < 10)
307 *(--vstr) = '0' + digit;
308 else if (c == 'X')
309 *(--vstr) = 'A' + digit - 10;
310 else
311 *(--vstr) = 'a' + digit - 10;
312 }
313
314 if (flags & PF_NEGATIVE)
315 *(--vstr) = '-';
316
317 /*
318 * Precision field was interpreted by fixed-point
319 * logic, so clear it.
320 */
321 precision = 0;
322 }
323
324 /* Copy string (or stringified integer) */
325 if (vstr != NULL)
326 vlen = strlen(vstr);
327 else
328 vlen = 0;
329
330 /* No padding strings to wider than the precision */
331 if (precision > 0 && pad_width > precision)
332 pad_width = precision;
333
334 /* If precision is zero, print everything */
335 if (!precision)
336 precision = MAX(vlen, pad_width);
337
338 while (vlen < pad_width && !(flags & PF_LEFT)) {
339 if (addchar(context, flags & PF_PADZERO ? '0' : ' '))
340 return pdFREERTOS_ERRNO_EINVAL;
341 vlen++;
342 }
343
344 if (vstr != NULL) {
345 while (*vstr && --precision >= 0)
346 if (addchar(context, *vstr++))
347 return pdFREERTOS_ERRNO_EINVAL;
348 }
349 while (vlen < pad_width && flags & PF_LEFT) {
350 if (addchar(context, ' '))
351 return pdFREERTOS_ERRNO_EINVAL;
352 vlen++;
353 }
354 }
355
356 /* If we're still here, we consumed all output */
357 return pdFREERTOS_ERRNO_NONE;
358}
359#endif
360/* Context for snprintf() */
361struct snprintf_context {
362 char *str;
363 size_t size;
364};
365
366/**
367 * Add a character to the string context.
368 *
369 * @param context Context receiving character
370 * @param c Character to add
371 * @return 0 if character added, 1 if character dropped because no space.
372 */
373static int snprintf_addchar(void *context, int c)
374{
375 struct snprintf_context *ctx = (struct snprintf_context *)context;
376
377 if (!ctx->size)
378 return 1;
379
380 *(ctx->str++) = c;
381 ctx->size--;
382
383 return 0;
384}
385
386int sPrintf_ext(char *str, size_t size, const char *format, va_list args)
387{
388 struct snprintf_context ctx;
389 int rv;
390
391 if (!str || !size)
392 return pdFREERTOS_ERRNO_EINVAL;
393
394 ctx.str = str;
395 ctx.size = size - 1; /* Reserve space for terminating '\0' */
396
397 rv = vfnprintf(snprintf_addchar, &ctx, format, args);
398
399 /* Terminate string */
400 *ctx.str = '\0';
401
402 return rv;
403}
404
405int sPrintf(char *str, size_t size, const char *fmt, ...)
406{
407 va_list args;
408 int i;
409
410 va_start(args, fmt);
411
412 i = sPrintf_ext(str, size, fmt, args);
413 va_end(args);
414
415 return i;
416}
417
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800418int iprintf(const char *fmt, ...)
419{
420 va_list args;
421 int i;
422 UBaseType_t uxSavedInterruptStatus;
423
424 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
425
426 va_start(args, fmt);
427
428 i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args);
429 va_end(args);
430 {
431 /* Print the string */
432#if (1 == CONFIG_ARM64)
433 vSerialPutString(ConsoleSerial, printbuffer);
434#else
435 vUartPuts(printbuffer);
436#endif
437 }
438 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
439 return i;
440}
441
bin.chenf2a61ab2022-01-07 17:29:02 +0800442int printk(const char *fmt, ...)
443{
444 va_list args;
445 int i;
446 UBaseType_t uxSavedInterruptStatus;
447
448 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
449
450 va_start(args, fmt);
451
452 i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args);
453 va_end(args);
454 {
455 /* Print the string */
456#if (1 == CONFIG_ARM64)
457 vSerialPutString(ConsoleSerial, printbuffer);
458#else
459 vUartPuts(printbuffer);
460#endif
461 }
462 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
463 return i;
464}
465
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800466int printf(const char *fmt, ...)
467{
468 va_list args;
469 int i;
470 UBaseType_t uxSavedInterruptStatus;
471
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800472 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
473
474 va_start(args, fmt);
475
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800476 i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args);
477 va_end(args);
478 {
479 /* Print the string */
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800480#if (1 == CONFIG_ARM64)
481 vSerialPutString(ConsoleSerial, printbuffer);
482#else
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800483 vUartPuts(printbuffer);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800484#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800485 }
486 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
487 return i;
488}
489
490int iprint_string(char *str)
491{
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800492#if (1 == CONFIG_ARM64)
493 vSerialPutString(ConsoleSerial, str);
494#else
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800495 vUartPuts(str);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800496#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800497
498 return 0;
499}
500
501extern int puts(const char *str);
502
503int puts(const char *str)
504{
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800505#if (1 == CONFIG_ARM64)
506 vSerialPutString(ConsoleSerial, str);
507#else
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800508 vUartPuts(str);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800509#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800510
511 return 0;
512}
513