1/** @file
2Serial conole output and string formating.
3
4Copyright (c) 2013-2015 Intel Corporation.
5
6This program and the accompanying materials
7are licensed and made available under the terms and conditions of the BSD License
8which accompanies this distribution.  The full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14**/
15#include "memory_options.h"
16#include "general_definitions.h"
17
18// Resource programmed to PCI bridge, 1MB bound alignment is needed.
19// The default value is overwritten by MRC parameter, assuming code
20// relocated to eSRAM.
21uint32_t UartMmioBase = 0;
22
23// Serial port registers based on SerialPortLib.c
24#define R_UART_BAUD_THR       0
25#define R_UART_LSR            20
26
27#define   B_UART_LSR_RXRDY    BIT0
28#define   B_UART_LSR_TXRDY    BIT5
29#define   B_UART_LSR_TEMT     BIT6
30
31// Print mask see DPF and D_Xxxx
32#define DPF_MASK  DpfPrintMask
33
34// Select class of messages enabled for printing
35uint32_t DpfPrintMask =
36    D_ERROR |
37    D_INFO |
38    // D_REGRD |
39    // D_REGWR |
40    // D_FCALL |
41    // D_TRN |
42    0;
43
44#ifdef NDEBUG
45// Don't generate debug code
46void dpf( uint32_t mask, char_t* bla, ...)
47{
48  return;
49}
50
51uint8_t mgetc(void)
52{
53  return 0;
54}
55
56uint8_t mgetch(void)
57{
58  return 0;
59}
60
61#else
62
63#ifdef SIM
64// Use Vpi console in simulation environment
65#include <vpi_user.h>
66
67void dpf( uint32_t mask, char_t* bla, ...)
68{
69  va_list va;
70
71  if( 0 == (mask & DPF_MASK)) return;
72
73  va_start( va, bla);
74  vpi_vprintf( bla, va);
75  va_end(va);
76}
77
78#else
79
80#ifdef EMU
81// Use standard console in windows environment
82#include <stdio.h>
83#endif
84
85// Read character from serial port
86uint8_t mgetc(void)
87{
88#ifdef EMU
89
90  // Emulation in Windows environment uses console
91  getchar();
92
93#else
94  uint8_t c;
95
96  while ((*(volatile uint8_t*) (UartMmioBase + R_UART_LSR) & B_UART_LSR_RXRDY) == 0);
97  c = *(volatile uint8_t*) (UartMmioBase + R_UART_BAUD_THR);
98
99  return c;
100#endif
101}
102
103
104uint8_t mgetch(void)
105{
106#ifdef EMU
107  return 0;
108#else
109  uint8_t c = 0;
110
111  if((*(volatile uint8_t*) (UartMmioBase + R_UART_LSR) & B_UART_LSR_RXRDY) != 0)
112  {
113    c = *(volatile uint8_t*) (UartMmioBase + R_UART_BAUD_THR);
114  }
115
116  return c;
117#endif
118}
119
120// Print single character
121static void printc(
122    uint8_t c)
123{
124#ifdef EMU
125
126  // Emulation in Windows environment uses console output
127  putchar(c);
128
129#else
130
131  //
132  // Use MMIO access to serial port on PCI
133  //   while( 0 == (0x20 & inp(0x3f8 + 5)));
134  //   outp(0x3f8 + 0, c);
135  //
136  while (0
137      == (B_UART_LSR_TEMT & *((volatile uint8_t*) (UartMmioBase + R_UART_LSR))))
138    ;
139  *((volatile uint8_t*) (UartMmioBase + R_UART_BAUD_THR)) = c;
140#endif
141}
142
143// Print 0 terminated string on serial console
144static void printstr(
145    char_t *str)
146{
147  while (*str)
148  {
149    printc(*str++);
150  }
151}
152// Print 64bit number as hex string on serial console
153// the width parameters allows skipping leading zeros
154static void printhexx(
155    uint64_t val,
156    uint32_t width)
157{
158  uint32_t i;
159  uint8_t c;
160  uint8_t empty = 1;
161
162  // 64bit number has 16 characters in hex representation
163  for (i = 16; i > 0; i--)
164  {
165    c = *(((uint8_t *)&val) + ((i - 1) >> 1));
166    if (((i - 1) & 1) != 0)
167      c = c >> 4;
168    c = c & 0x0F;
169
170    if (c > 9)
171      c += 'A' - 10;
172    else
173      c += '0';
174
175    if (c != '0')
176    {
177      // end of leading zeros
178      empty = 0;
179    }
180
181    // don't print leading zero
182    if (!empty || i <= width)
183    {
184      printc(c);
185    }
186  }
187}
188// Print 32bit number as hex string on serial console
189// the width parameters allows skipping leading zeros
190static void printhex(
191    uint32_t val,
192    uint32_t width)
193{
194  uint32_t i;
195  uint8_t c;
196  uint8_t empty = 1;
197
198  // 32bit number has 8 characters in hex representation
199  for (i = 8; i > 0; i--)
200  {
201    c = (uint8_t) ((val >> 28) & 0x0F);
202    if (c > 9)
203      c += 'A' - 10;
204    else
205      c += '0';
206
207    val = val << 4;
208
209    if (c != '0')
210    {
211      // end of leading zeros
212      empty = 0;
213    }
214
215    // don't print leading zero
216    if (!empty || i <= width)
217    {
218      printc(c);
219    }
220  }
221}
222// Print 32bit number as decimal string on serial console
223// the width parameters allows skipping leading zeros
224static void printdec(
225    uint32_t val,
226    uint32_t width)
227{
228  uint32_t i;
229  uint8_t c = 0;
230  uint8_t empty = 1;
231
232  // Ten digits is enough for 32bit number in decimal
233  uint8_t buf[10];
234
235  for (i = 0; i < sizeof(buf); i++)
236  {
237    c = (uint8_t) (val % 10);
238    buf[i] = c + '0';
239    val = val / 10;
240  }
241
242  while (i > 0)
243  {
244    c = buf[--i];
245
246    if (c != '0')
247    {
248      // end of leading zeros
249      empty = 0;
250    }
251
252    // don't print leading zero
253    if (!empty || i < width)
254    {
255      printc(c);
256    }
257  }
258}
259
260// Consume numeric substring leading the given string
261// Return pointer to the first non-numeric character
262// Buffer reference by width is updated with number
263// converted from the numeric substring.
264static char_t *getwidth(
265    char_t *bla,
266    uint32_t *width)
267{
268  uint32_t val = 0;
269
270  while (*bla >= '0' && *bla <= '9')
271  {
272    val = val * 10 + *bla - '0';
273    bla += 1;
274  }
275
276  if (val > 0)
277  {
278    *width = val;
279  }
280  return bla;
281}
282
283// Consume print format designator from the head of given string
284// Return pointer to first character after format designator
285// input fmt
286// ----- ---
287//  s   -> s
288//  d   -> d
289//  X   -> X
290//  llX -> L
291static char_t *getformat(
292    char_t *bla,
293    uint8_t *fmt)
294{
295  if (bla[0] == 's')
296  {
297    bla += 1;
298    *fmt = 's';
299  }
300  else if (bla[0] == 'd')
301  {
302    bla += 1;
303    *fmt = 'd';
304  }
305  else if (bla[0] == 'X' || bla[0] == 'x')
306  {
307    bla += 1;
308    *fmt = 'X';
309  }
310  else if (bla[0] == 'l' && bla[1] == 'l' && bla[2] == 'X')
311  {
312    bla += 3;
313    *fmt = 'L';
314  }
315
316  return bla;
317}
318
319// Simplified implementation of standard printf function
320// The output is directed to serial console. Only selected
321// class of messages is printed (mask has to match DpfPrintMask)
322// Supported print formats: %[n]s,%[n]d,%[n]X,,%[n]llX
323// The width is ignored for %s format.
324void dpf(
325    uint32_t mask,
326    char_t* bla,
327    ...)
328{
329  uint32_t* arg = (uint32_t*) (&bla + 1);
330
331  // Check UART MMIO base configured
332  if (0 == UartMmioBase)
333    return;
334
335  // Check event not masked
336  if (0 == (mask & DPF_MASK))
337    return;
338
339  for (;;)
340  {
341    uint8_t x = *bla++;
342    if (x == 0)
343      break;
344
345    if (x == '\n')
346    {
347      printc('\r');
348      printc('\n');
349    }
350    else if (x == '%')
351    {
352      uint8_t fmt = 0;
353      uint32_t width = 1;
354
355      bla = getwidth(bla, &width);
356      bla = getformat(bla, &fmt);
357
358      // Print value
359      if (fmt == 'd')
360      {
361        printdec(*arg, width);
362        arg += 1;
363      }
364      else if (fmt == 'X')
365      {
366        printhex(*arg, width);
367        arg += 1;
368      }
369      else if (fmt == 'L')
370      {
371        printhexx(*(uint64_t*) arg, width);
372        arg += 2;
373      }
374      else if (fmt == 's')
375      {
376        printstr(*(char**) arg);
377        arg += 1;
378      }
379    }
380    else
381    {
382      printc(x);
383    }
384  }
385}
386
387#endif  //SIM
388#endif  //NDEBUG
389