• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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