1 /*
2 * Copyright (c) 2021 Chipsea Technologies (Shenzhen) Corp., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <stdarg.h>
16
17 #include "plf.h"
18 #include "dbg_assert.h"
19
20 #include "dbg.h"
21
22 #ifndef NULL
23 #define NULL (void *)0
24 #endif
25 #define co_min(A, B) (((A) < (B)) ? (A) : (B))
26 #define CO_BIT(pos) (1UL<<(pos))
27
28 #include "uart.h"
29 #include "reg_ipc_mutex.h"
30
31 #ifdef CFG_RTOS
32 #include "rtos_ohos_al.h"
33 #endif
34
35 #define FLAG_SHORT_SUPPORTED
36 #define SUPPORT_LOWER_CASE_HEX
37
38 /// States values
39 enum
40 {
41 S_COPY, // Initial state; copy chars of the format str
42 S_PERCENT, // just read '%'
43 S_FLAGS, // just read flag character
44 S_WIDTH, // just read width specifier
45 S_DOT, // just read '.'
46 S_PRECIS, // just read field_precision specifier
47 S_SIZE, // just read size specifier
48 S_TYPE, // just read type specifier
49 S_MAX
50 };
51
52 /// character type values
53 enum
54 {
55 C_OTHER, // character with no special meaning
56 C_PERCENT, // '%'
57 C_DOT, // '.'
58 C_ZERO, // '0'
59 C_DIGIT, // '1'..'9'
60 C_FLAG, // ' ', '+', '-',
61 C_SIZE, // 'h', 'l', 'L'
62 C_TYPE, // type specifying character
63 C_MAX
64 };
65
66
67 /// field_flags used to store the format information
68 enum
69 {
70 FLAG_SHORT = (1 << 0), // short value
71 FLAG_LONG = (1 << 1), // long value
72 FLAG_SIGNED = (1 << 2), // signed value
73 FLAG_SIGN = (1 << 3), // Add a - or + in the front of the field
74 FLAG_SIGN_SPACE = (1 << 4), // Add a space or - in the front of the field
75 FLAG_LEFT = (1 << 5), // left justify
76 FLAG_LEAD_ZERO = (1 << 6), // padding with 0
77 FLAG_NEGATIVE = (1 << 7), // the value is negative
78 };
79
80
81 /// Transition table
82 static const uint8_t transition_table[S_MAX][C_MAX] =
83 { // OTHER PERCENT DOT ZERO DIGIT FLAG SIZE TYPE
84 /* S_COPY */ { S_COPY, S_PERCENT, S_COPY, S_COPY, S_COPY, S_COPY, S_COPY, S_COPY },
85 /* S_PERCENT */ { S_COPY, S_COPY, S_DOT, S_FLAGS , S_WIDTH, S_FLAGS , S_SIZE, S_TYPE },
86 /* S_FLAGS */ { S_COPY, S_COPY, S_DOT, S_FLAGS, S_WIDTH, S_FLAGS, S_SIZE, S_TYPE },
87 /* S_WIDTH */ { S_COPY, S_COPY, S_DOT, S_WIDTH, S_WIDTH, S_COPY, S_SIZE, S_TYPE },
88 /* S_DOT */ { S_COPY, S_COPY, S_COPY, S_PRECIS, S_PRECIS, S_COPY, S_SIZE, S_TYPE },
89 /* S_PRECIS */ { S_COPY, S_COPY, S_COPY, S_PRECIS, S_PRECIS, S_COPY, S_SIZE, S_TYPE },
90 /* S_SIZE */ { S_COPY, S_COPY, S_COPY, S_COPY, S_COPY, S_COPY, S_COPY, S_TYPE },
91 /* S_TYPE */ { S_COPY, S_PERCENT, S_COPY, S_COPY, S_COPY, S_COPY, S_COPY, S_COPY },
92 };
93
94
95 /// Upper case hexadecimal table
96 static const char hex_upper_table[] = "0123456789ABCDEF";
97 #ifdef SUPPORT_LOWER_CASE_HEX
98 /// Lower case hexadecimal table
99 static const char hex_lower_table[] = "0123456789abcdef";
100 #endif
101
102 #if defined(CFG_RTOS) && DBG_MUTEX_ENABLED
103 rtos_mutex dbg_mutex;
104 #endif
105
106
107 /**
108 ****************************************************************************************
109 * @brief Function to read a particular character and map its type
110 *
111 * This function is called to read a particular character and fetch its type
112 *
113 * @param[in] c Input character
114 *
115 * @return Type of the character
116 ****************************************************************************************
117 */
type_get(char c)118 static uint32_t type_get(char c)
119 {
120 uint32_t res;
121
122 switch(c)
123 {
124 case '%':
125 res = C_PERCENT;
126 break;
127
128 case '.':
129 res = C_DOT;
130 break;
131
132 case '0':
133 res = C_ZERO;
134 break;
135
136 case ' ':
137 case '+':
138 case '-':
139 res = C_FLAG;
140 break;
141
142 case 'h':
143 case 'l':
144 res = C_SIZE;
145 break;
146
147 case 'x':
148 case 'X':
149 case 'd':
150 case 'b':
151 case 'i':
152 case 'c':
153 case 'u':
154 case 's':
155 case 'm':
156 case 'M':
157 case 'a':
158 case 'A':
159 case 'p':
160 res = C_TYPE;
161 break;
162
163 case '*':
164 res = C_DIGIT;
165 break;
166
167 default:
168 if(('1' <= c) && (c <= '9'))
169 {
170 res = C_DIGIT;
171 }
172 else
173 {
174 res = C_OTHER;
175 }
176 break;
177 }
178
179 return res;
180 }
181
182 /**
183 ****************************************************************************************
184 * @brief Execute a pseudo vsnprintf function
185 *
186 * @param[out] buffer Output buffer
187 * @param[in] size Size of the output buffer
188 * @param[in] offset Offset of final string at which the writing in buffer should start
189 * @param[in] fmt Format string
190 * @param[in] args Variable list of arguments
191 *
192 * @return Upon successful return, returns the number of characters printed (excluding the
193 * null byte used to end output to strings). If the output was truncated due to the size limit,
194 * then the return value is the number of characters (excluding the terminating
195 * null byte) which would have been written to the final string if enough space had been
196 * available. Thus, a return value of size or more means that the output was truncated.
197 *
198 ****************************************************************************************
199 */
dbg_vsnprintf_offset(char * buffer,uint32_t size,uint32_t offset,const char * fmt,va_list args)200 uint32_t dbg_vsnprintf_offset(char *buffer, uint32_t size, uint32_t offset, const char *fmt, va_list args)
201 {
202 uint32_t state_current = S_COPY; // Initial state
203 char c;
204
205 char *fmt_field_ptr = NULL;
206 int fmt_field_size = 0;
207
208 char buffer_tmp[64];
209
210 int field_width = 0;
211 char field_flags = 0;
212 int field_precision = 0;
213 int field_padding = 0;
214
215 int32_t value;
216 char *tmp_ptr = NULL;
217
218 uint32_t res = 0, remain;
219
220 #define WRITE_CHAR(_c) if (remain && res >= offset) {*buffer++ = (_c); remain--;} res++
221
222 // Check parameters: If buffer is NULL then size MUST be 0.
223 if (buffer == NULL)
224 size = 0;
225
226 remain = size;
227
228 // For each char in format string
229 while ((c = *fmt++) != 0)
230 {
231 state_current = transition_table[state_current][type_get(c)];
232
233 switch (state_current)
234 {
235 case S_COPY :
236 WRITE_CHAR(c);
237 break;
238
239 case S_PERCENT:
240 // Assign default value for the conversion parameters
241 field_width = 0;
242 field_flags = 0;
243 field_precision = -1;
244 break;
245
246 case S_FLAGS:
247 // set flag based on which flag character
248 switch(c)
249 {
250 case '-':
251 // left justify
252 field_flags |= FLAG_LEFT;
253 break;
254 case '+':
255 // force sign indicator
256 field_flags |= FLAG_SIGN;
257 break;
258 case ' ':
259 // force sign or space
260 field_flags |= FLAG_SIGN_SPACE;
261 break;
262 case '0':
263 // pad with leading 0
264 field_flags |= FLAG_LEAD_ZERO;
265 break;
266 default:
267 ASSERT_ERR(0);
268 break;
269 }
270 break;
271
272 case S_WIDTH:
273 if (c != '*')
274 {
275 // add digit to current field width
276 field_width = field_width * 10 + (c - '0');
277 }
278 else
279 {
280 field_width = (int)va_arg(args, int);
281 }
282 break;
283
284 case S_DOT:
285 // Clear the field_precision variable
286 field_precision = 0;
287 break;
288
289 case S_PRECIS:
290 if (c != '*')
291 {
292 // Add digit to field_precision variable
293 field_precision = field_precision * 10 + (c - '0');
294 }
295 else
296 {
297 field_precision = (int)va_arg(args, int);
298 }
299 break;
300
301 case S_SIZE:
302 // currently ignored
303 switch (c)
304 {
305 case 'l':
306 // 'l' => long int
307 field_flags |= FLAG_LONG;
308 break;
309
310 #ifdef FLAG_SHORT_SUPPORTED
311 case 'h':
312 // 'h' => short int
313 field_flags |= FLAG_SHORT;
314 break;
315 #endif // FLAG_SHORT_SUPPORTED
316
317 default:
318 ASSERT_ERR(0);
319 break;
320 }
321 break;
322
323 case S_TYPE:
324
325 // Now the options have been decoded
326 switch (c)
327 {
328 // c
329 case 'c':
330 // Store byte in Tx buffer
331 buffer_tmp[0] = (char)va_arg(args, int);
332 fmt_field_ptr = buffer_tmp;
333 fmt_field_size = 1;
334 break;
335
336
337 // String
338 case 's':
339 // Read parameter (pointer on string)
340 fmt_field_ptr = va_arg(args, char *);
341 fmt_field_size = 0;
342 if (fmt_field_ptr != NULL)
343 {
344 // Compute the length of the string
345 // field_precision is the maximum number of character to be display
346 tmp_ptr = fmt_field_ptr;
347 while (*tmp_ptr != '\0')
348 {
349 if (field_precision == 0)
350 break;
351 if (field_precision > 0)
352 field_precision --;
353 tmp_ptr++;
354 fmt_field_size++;
355 }
356 }
357 break;
358
359 // MAC address
360 case 'm':
361 case 'M':
362 {
363 int i;
364 fmt_field_ptr = buffer_tmp;
365 tmp_ptr = va_arg(args, char *);
366 fmt_field_size = 17;
367 for(i = 5;;)
368 {
369 value = (unsigned char) *tmp_ptr++;
370 *fmt_field_ptr++ = hex_upper_table[value >> 4];
371 *fmt_field_ptr++ = hex_upper_table[value & 0xF];
372 if (i-- == 0) break;
373 *fmt_field_ptr++ = ':';
374 }
375 fmt_field_ptr = buffer_tmp;
376 break;
377 }
378
379 case 'a':
380 case 'A':
381 fmt_field_ptr = buffer_tmp;
382 tmp_ptr = va_arg(args, char *);
383 // prevent overflow
384 field_width = co_min(field_width, sizeof(buffer_tmp)/3);
385 // if no width given
386 if (!field_width) field_width = 16;
387 fmt_field_size = field_width * 3 - 1 ;
388 for(;;)
389 {
390 value = (unsigned char) *tmp_ptr++;
391 *fmt_field_ptr++ = hex_upper_table[value >> 4];
392 *fmt_field_ptr++ = hex_upper_table[value & 0xF];
393 if (--field_width == 0) break;
394 // sep . (or : on align)
395 if (3 & (uint32_t) tmp_ptr) *fmt_field_ptr++ = '.';
396 else *fmt_field_ptr++ = ':';
397 }
398 fmt_field_ptr = buffer_tmp;
399 break;
400
401 case 'i': // signed decimal
402 c = 'd';
403 case 'd': // signed decimal
404 case 'u': // unsigned
405 case 'X': // hexa
406 case 'x': // hexa
407 case 'b': // binary
408 case 'p': // pointer
409
410 // Point to the last byte of the buffer (go backward during conversion)
411 fmt_field_ptr = buffer_tmp + sizeof(buffer_tmp);
412 fmt_field_size = 0;
413
414 // Get the value
415 if (field_flags & FLAG_LONG)
416 {
417 // long
418 value = va_arg(args, uint32_t);
419 }
420 #ifdef FLAG_SHORT_SUPPORTED
421 else if(field_flags & FLAG_SHORT)
422 {
423 if (c == 'd')
424 {
425 // extend the sign
426 value = (int16_t) va_arg(args, int);
427 }
428 else
429 {
430 value = (uint16_t) va_arg(args, int);
431 }
432 }
433 #endif // FLAG_SHORT_SUPPORTED
434 else
435 {
436 // int
437 // extend the sign
438 value = va_arg(args, int);
439 }
440
441 switch (c)
442 {
443 case 'd':
444 // Separate the sign to display it before the number
445 if (value < 0)
446 {
447 value = (uint32_t)(-value);
448 // remember negative sign
449 field_flags |= FLAG_NEGATIVE;
450 }
451
452 case 'u':
453 do
454 {
455 // go backward
456 fmt_field_ptr--;
457 *fmt_field_ptr = ('0'+ ((char) (((uint32_t)value) % 10)));
458 value = ((uint32_t)value) / 10;
459 fmt_field_size++;
460 }
461 while (value != 0);
462
463 // Add the sign
464 if (field_flags & FLAG_NEGATIVE)
465 { // prefix with a '-'
466 // go backward
467 fmt_field_ptr--;
468
469 *fmt_field_ptr = '-';
470 fmt_field_size++;
471 }
472 else if (field_flags & FLAG_SIGN)
473 { // prefix with a '+' (sign is forced)
474 // go backward
475 fmt_field_ptr--;
476 *fmt_field_ptr = '+';
477 fmt_field_size++;
478 }
479 else if (field_flags & FLAG_SIGN_SPACE)
480 { // prefix with a ' ' (used instead of '+')
481 // go backward
482 fmt_field_ptr--;
483 *fmt_field_ptr = ' ';
484 fmt_field_size++;
485 }
486 break;
487
488 case 'p':
489 field_width = sizeof(void *) << 1;
490 field_flags |= FLAG_LEAD_ZERO;
491 case 'x':
492 #ifdef SUPPORT_LOWER_CASE_HEX
493 do
494 {
495 // go backward
496 fmt_field_ptr--;
497 *fmt_field_ptr = hex_lower_table[value & 0XFUL];
498 value = ((uint32_t) value) >> 4;
499 fmt_field_size++;
500 }
501 while (value != 0);
502 break;
503 #endif
504
505 case 'X':
506 do
507 {
508 // go backward
509 fmt_field_ptr--;
510 *fmt_field_ptr = hex_upper_table[value & 0XFUL];
511 value = ((uint32_t) value) >> 4;
512 fmt_field_size++;
513 }
514 while (value != 0);
515 break;
516
517 case 'b':
518 do
519 {
520 // go backward
521 fmt_field_ptr--;
522 *fmt_field_ptr = '0' + ((char) (value & 0x01UL));
523 value = ((uint32_t) value) >> 1;
524 fmt_field_size++;
525 }
526 while (value != 0);
527 break;
528 default:
529 //ASSERT_ERR(0);
530 break;
531 } // embedded switch type
532
533 default:
534 //ASSERT_ERR(0);
535 break;
536 } // switch type
537
538
539 // Add padding
540 field_padding = field_width - fmt_field_size;
541
542 // put out the padding, prefix, and text, in the correct order
543 if (field_flags & FLAG_LEAD_ZERO)
544 {
545 // Add leading zeros
546 while (field_padding > 0)
547 {
548 WRITE_CHAR('0');
549 field_padding--;
550 }
551 }
552 else if (!(field_flags & FLAG_LEFT))
553 {
554 while (field_padding > 0)
555 {
556 WRITE_CHAR(' ');
557 field_padding--;
558 }
559 }
560
561 // Copy the formated field
562 while (fmt_field_size > 0)
563 {
564 WRITE_CHAR(*fmt_field_ptr);
565 fmt_field_ptr++;
566 fmt_field_size--;
567 }
568
569 // Add blanks at the rigth (means (field_flags & FLAG_LEFT))
570 while ((field_padding > 0))
571 {
572 WRITE_CHAR(' ');
573 field_padding--;
574 }
575 break;
576
577 default:
578 ASSERT_ERR(0);
579 } // switch state
580 } // while
581
582 #undef WRITE_CHAR
583
584 if (remain)
585 {
586 *buffer = '\0';
587 }
588 else if (size)
589 {
590 buffer[-1] = '\0';
591 }
592
593 return res;
594 }
595
uart_printf(const char * format,va_list arg)596 int uart_printf(const char *format, va_list arg)
597 {
598 char buffer[256];
599 uint32_t isr_active = __get_IPSR();
600 #if defined(CFG_RTOS) && (DBG_MUTEX_ENABLED || DBG_SUSPEND_ENABLED)
601 int norm_task = ((__get_CONTROL() & (0x01UL << 1)) && // psp
602 (rtos_get_task_handle() != rtos_get_idle_task_handle())) ? 1 : 0;
603 #endif
604 int size = dbg_vsnprintf(buffer, 256, format, arg);
605 if (size > 0) {
606 if (!stdio_uart_inited) {
607 stdio_uart_init();
608 }
609 if (!isr_active
610 #if defined(CFG_RTOS)
611 && norm_task
612 #endif
613 ) {
614 #if defined(CFG_RTOS) && DBG_MUTEX_ENABLED
615 rtos_mutex_lock(dbg_mutex, -1);
616 #endif
617 while(!ipc_mutex_get(IPC_MUTEX_UART_OUTPUT)) { // lock mutex
618 #if defined(CFG_RTOS) && DBG_SUSPEND_ENABLED
619 rtos_task_suspend(1);
620 #endif
621 }
622 }
623 #if 0
624 for (int i = 0; i < size; i++) {
625 stdio_uart_putc(buffer[i]);
626 }
627 #else
628 char stdio_out_prev = '\0';
629 for (int i = 0; i < size; i++) {
630 if (buffer[i] == '\n' && stdio_out_prev != '\r') {
631 stdio_uart_putc('\r');
632 }
633 stdio_uart_putc(buffer[i]);
634 stdio_out_prev = buffer[i];
635 }
636 #endif
637 if (!isr_active
638 #if defined(CFG_RTOS)
639 && norm_task
640 #endif
641 ) {
642 ipc_mutex_set(IPC_MUTEX_UART_OUTPUT, 1); // unlock mutex
643 #if defined(CFG_RTOS) && DBG_MUTEX_ENABLED
644 rtos_mutex_unlock(dbg_mutex);
645 #endif
646 }
647 }
648 return size;
649 }
650
uart_puts(const char * str)651 void uart_puts(const char* str)
652 {
653 char stdio_out_prev = '\0';
654 while (*str) {
655 if (*str == '\n' && stdio_out_prev != '\r') {
656 stdio_uart_putc('\r');
657 }
658 stdio_uart_putc(*str);
659 stdio_out_prev = *str;
660 str++;
661 }
662 }
663
dbg_print(const char * fmt,...)664 void dbg_print(const char *fmt, ...)
665 {
666 // print
667 va_list args;
668 va_start(args, fmt);
669
670 uart_printf(fmt, args);
671
672 va_end(args);
673 }
674
675 /**
676 ****************************************************************************************
677 * @brief Function formatting a string and sending it to the defined output
678 *
679 * @param[in] fmt Format string
680 *
681 ****************************************************************************************
682 */
dbg_test_print(const char * fmt,...)683 void dbg_test_print(const char *fmt, ...)
684 {
685 const char *fmt_usr = (const char*) fmt;
686 uint32_t severity = 0;
687
688 //printf ("%d %d %d %d\n", fmt_usr[0], fmt_usr[1], DBG_MOD_MAX, DBG_SEV_MIN);
689
690 if (dbg_env.filter_severity == 0) return;
691
692 do
693 {
694 // Get the prefix
695 unsigned char prefix = ((unsigned char)*fmt_usr) & 0xFF;
696
697 // ASCII char, start of the user string
698 if (prefix < DBG_MOD_MIN) break;
699
700 if (prefix < DBG_MOD_MAX)
701 {
702 // test module, if filtered returns
703 if (~dbg_env.filter_module & CO_BIT(prefix - DBG_MOD_MIN)) return;
704 }
705 else
706 {
707 // must be severity code
708 ASSERT_ERR(DBG_SEV_MIN <= prefix && prefix < DBG_SEV_MAX);
709 severity = (uint32_t)(prefix - DBG_SEV_MIN);
710
711 // test severity, if filtered returns
712 if (dbg_env.filter_severity <= severity) return;
713 }
714
715 // Check first and second char
716 fmt_usr++;
717 }
718 while (fmt_usr != fmt + 2);
719
720 // print
721 va_list args;
722 va_start(args, fmt);
723
724 uart_printf(fmt_usr, args);
725
726 va_end(args);
727 }
728
729 /**
730 ****************************************************************************************
731 * @brief Execute a pseudo snprintf function
732 *
733 * @param[out] buffer Output buffer
734 * @param[in] size Size of the output buffer
735 * @param[in] fmt Format string
736 *
737 * @return Upon successful return, returns the number of characters printed (excluding the
738 * null byte used to end output to strings). If the output was truncated due to the size limit,
739 * then the return value is the number of characters (excluding the terminating
740 * null byte) which would have been written to the final string if enough space had been
741 * available. Thus, a return value of size or more means that the output was truncated.
742 *
743 ****************************************************************************************
744 */
dbg_snprintf(char * buffer,uint32_t size,const char * fmt,...)745 uint32_t dbg_snprintf(char *buffer, uint32_t size, const char *fmt, ...)
746 {
747 uint32_t ret;
748 const char *fmt_usr = (const char*) fmt;
749
750 // print
751 va_list args;
752 va_start(args, fmt);
753
754 ret = dbg_vsnprintf(buffer, size, fmt_usr, args);
755
756 va_end(args);
757
758 return ret;
759 }
760