blob: df6fbdab2d0885678c8dd5c229dc52a96b615696 [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)
xiaohu.huang1fd6f112022-05-24 11:02:05 +080013#include "serial.h"
Xiaohu.Huang448314d2021-12-31 17:12:48 +080014#else
xiaohu.huang1fd6f112022-05-24 11:02:05 +080015#include "uart.h"
Xiaohu.Huang448314d2021-12-31 17:12:48 +080016#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080017static const char error_str[] = "ERROR";
18
xiaohu.huang1fd6f112022-05-24 11:02:05 +080019#define MAX_FORMAT 1024 /* Maximum chars in a single format field */
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080020static char printbuffer[512];
21
Xiaohu.Huangaa18c4c2021-11-16 17:26:41 +080022#ifndef MAX
xiaohu.huang1fd6f112022-05-24 11:02:05 +080023#define MAX(a, b) \
24 ({ \
25 __typeof__(a) temp_a = (a); \
26 __typeof__(b) temp_b = (b); \
27 temp_a > temp_b ? temp_a : temp_b; \
Xiaohu.Huangaa18c4c2021-11-16 17:26:41 +080028 })
29#endif
30
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080031/**
32 * Convert the lowest nibble of a number to hex
33 *
34 * @param c Number to extract lowest nibble from
35 *
36 * @return The corresponding ASCII character ('0' - 'f').
37 */
38static int hexdigit(int c)
39{
40 /* Strip off just the last nibble */
41 c &= 0x0f;
42
43 return c > 9 ? (c + 'a' - 10) : (c + '0');
44}
45
xiaohu.huangf3ffd7b2022-11-01 15:14:52 +080046int uint64divmod(uint64_t *n, int d)
Xiaohu.Huangaa18c4c2021-11-16 17:26:41 +080047{
48 uint64_t q = 0, mask;
49 int r = 0;
50
51 /* Divide-by-zero returns zero */
52 if (!d) {
53 *n = 0;
54 return 0;
55 }
56
57 /* Common powers of 2 = simple shifts */
58 if (d == 2) {
59 r = *n & 1;
60 *n >>= 1;
61 return r;
62 } else if (d == 16) {
63 r = *n & 0xf;
64 *n >>= 4;
65 return r;
66 }
67
68 /* If v fits in 32-bit, we're done. */
69 if (*n <= 0xffffffff) {
70 uint32_t v32 = *n;
xiaohu.huang1fd6f112022-05-24 11:02:05 +080071
Xiaohu.Huangaa18c4c2021-11-16 17:26:41 +080072 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 */
xiaohu.huang1fd6f112022-05-24 11:02:05 +080092#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
97int vfnprintf(int (*addchar)(void *context, int c), void *context, const char *format,
98 va_list args)
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +080099{
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
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800198 if (vstr == NULL) { /*Fix me */
199 ; // vstr = "(NULL)";
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800200 }
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;
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800229 c = *format++; // long long is 64bit at LP64
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800230 }
231 }
232
233 /* Special-case: %T = current time */
234 if (c == 'T') {
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800235 // v = get_time().val;
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800236 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) {
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800247 if ((int64_t)v < 0) {
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800248 flags |= PF_NEGATIVE;
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800249 v = (v != (1ULL << 63)) ? -v : v;
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800250 }
251 } else {
252 if ((int)v < 0) {
253 flags |= PF_NEGATIVE;
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800254 v = (v != (1ULL << 31)) ? -(int)v : v;
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800255 }
256 }
257 break;
258 case 'u':
259 case 'T':
260 break;
261 case 'X':
262 case 'x':
263 case 'p':
264 base = 16;
265 break;
266 case 'b':
267 base = 2;
268 break;
269 default:
270 format = error_str;
271 }
272 if (format == error_str)
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800273 continue; /* Bad format specifier */
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800274
275 /*
276 * Convert integer to string, starting at end of
277 * buffer and working backwards.
278 */
279 vstr = intbuf + sizeof(intbuf) - 1;
280 *(vstr) = '\0';
281
282 /*
283 * Fixed-point precision must fit in our buffer.
284 * Leave space for "0." and the terminating null.
285 */
286 if (precision > (int)(sizeof(intbuf) - 3))
287 precision = (int)(sizeof(intbuf) - 3);
288
289 /*
290 * Handle digits to right of decimal for fixed point
291 * numbers.
292 */
293 for (vlen = 0; vlen < precision; vlen++)
294 *(--vstr) = '0' + uint64divmod(&v, 10);
295 if (precision)
296 *(--vstr) = '.';
297
298 if (!v)
299 *(--vstr) = '0';
300
301 while (v) {
302 int digit = uint64divmod(&v, base);
303
304 if (digit < 10)
305 *(--vstr) = '0' + digit;
306 else if (c == 'X')
307 *(--vstr) = 'A' + digit - 10;
308 else
309 *(--vstr) = 'a' + digit - 10;
310 }
311
312 if (flags & PF_NEGATIVE)
313 *(--vstr) = '-';
314
315 /*
316 * Precision field was interpreted by fixed-point
317 * logic, so clear it.
318 */
319 precision = 0;
320 }
321
322 /* Copy string (or stringified integer) */
323 if (vstr != NULL)
324 vlen = strlen(vstr);
325 else
326 vlen = 0;
327
328 /* No padding strings to wider than the precision */
329 if (precision > 0 && pad_width > precision)
330 pad_width = precision;
331
332 /* If precision is zero, print everything */
333 if (!precision)
334 precision = MAX(vlen, pad_width);
335
336 while (vlen < pad_width && !(flags & PF_LEFT)) {
337 if (addchar(context, flags & PF_PADZERO ? '0' : ' '))
338 return pdFREERTOS_ERRNO_EINVAL;
339 vlen++;
340 }
341
342 if (vstr != NULL) {
343 while (*vstr && --precision >= 0)
344 if (addchar(context, *vstr++))
345 return pdFREERTOS_ERRNO_EINVAL;
346 }
347 while (vlen < pad_width && flags & PF_LEFT) {
348 if (addchar(context, ' '))
349 return pdFREERTOS_ERRNO_EINVAL;
350 vlen++;
351 }
352 }
353
354 /* If we're still here, we consumed all output */
355 return pdFREERTOS_ERRNO_NONE;
356}
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800357
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800358/* Context for snprintf() */
359struct snprintf_context {
360 char *str;
361 size_t size;
362};
363
364/**
365 * Add a character to the string context.
366 *
367 * @param context Context receiving character
368 * @param c Character to add
369 * @return 0 if character added, 1 if character dropped because no space.
370 */
371static int snprintf_addchar(void *context, int c)
372{
373 struct snprintf_context *ctx = (struct snprintf_context *)context;
374
375 if (!ctx->size)
376 return 1;
377
378 *(ctx->str++) = c;
379 ctx->size--;
380
381 return 0;
382}
383
384int sPrintf_ext(char *str, size_t size, const char *format, va_list args)
385{
386 struct snprintf_context ctx;
387 int rv;
388
389 if (!str || !size)
390 return pdFREERTOS_ERRNO_EINVAL;
391
392 ctx.str = str;
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800393 ctx.size = size - 1; /* Reserve space for terminating '\0' */
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800394
395 rv = vfnprintf(snprintf_addchar, &ctx, format, args);
396
397 /* Terminate string */
398 *ctx.str = '\0';
399
400 return rv;
401}
402
403int sPrintf(char *str, size_t size, const char *fmt, ...)
404{
405 va_list args;
406 int i;
407
408 va_start(args, fmt);
409
410 i = sPrintf_ext(str, size, fmt, args);
411 va_end(args);
412
413 return i;
414}
415
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800416int iprintf(const char *fmt, ...)
417{
418 va_list args;
419 int i;
420 UBaseType_t uxSavedInterruptStatus;
421
422 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
423
424 va_start(args, fmt);
425
426 i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args);
427 va_end(args);
428 {
429 /* Print the string */
430#if (1 == CONFIG_ARM64)
431 vSerialPutString(ConsoleSerial, printbuffer);
432#else
433 vUartPuts(printbuffer);
434#endif
435 }
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800436 portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800437 return i;
438}
439
bin.chenf2a61ab2022-01-07 17:29:02 +0800440int printk(const char *fmt, ...)
441{
442 va_list args;
443 int i;
444 UBaseType_t uxSavedInterruptStatus;
445
446 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
447
448 va_start(args, fmt);
449
450 i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args);
451 va_end(args);
452 {
453 /* Print the string */
454#if (1 == CONFIG_ARM64)
455 vSerialPutString(ConsoleSerial, printbuffer);
456#else
457 vUartPuts(printbuffer);
458#endif
459 }
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800460 portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
bin.chenf2a61ab2022-01-07 17:29:02 +0800461 return i;
462}
463
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800464int printf(const char *fmt, ...)
465{
466 va_list args;
467 int i;
468 UBaseType_t uxSavedInterruptStatus;
469
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800470 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
471
472 va_start(args, fmt);
473
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800474 i = sPrintf_ext(printbuffer, sizeof(printbuffer), fmt, args);
475 va_end(args);
476 {
477 /* Print the string */
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800478#if (1 == CONFIG_ARM64)
479 vSerialPutString(ConsoleSerial, printbuffer);
480#else
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800481 vUartPuts(printbuffer);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800482#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800483 }
xiaohu.huang1fd6f112022-05-24 11:02:05 +0800484 portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800485 return i;
486}
487
488int iprint_string(char *str)
489{
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800490#if (1 == CONFIG_ARM64)
491 vSerialPutString(ConsoleSerial, str);
492#else
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800493 vUartPuts(str);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800494#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800495
496 return 0;
497}
498
499extern int puts(const char *str);
500
501int puts(const char *str)
502{
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800503#if (1 == CONFIG_ARM64)
504 vSerialPutString(ConsoleSerial, str);
505#else
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800506 vUartPuts(str);
Xiaohu.Huang448314d2021-12-31 17:12:48 +0800507#endif
Xiaohu.Huang60a7f2f2021-10-25 15:40:57 +0800508
509 return 0;
510}