blob: 51ba6fbc8dbf11778c80dfc797b991692ed971ce [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/dlog.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018
19#include <stdbool.h>
20#include <stddef.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010021
Andrew Scull11a4a0c2018-12-29 11:38:31 +000022#include "hf/arch/console.h"
23
Andrew Scull18c78fc2018-08-20 12:57:41 +010024#include "hf/spinlock.h"
25#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010026
Andrew Scull4f170f52018-07-19 12:58:20 +010027/* Keep macro alignment */
28/* clang-format off */
29
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010030#define FLAG_SPACE 0x01
31#define FLAG_ZERO 0x02
32#define FLAG_MINUS 0x04
33#define FLAG_PLUS 0x08
34#define FLAG_ALT 0x10
35#define FLAG_UPPER 0x20
36#define FLAG_NEG 0x40
37
Andrew Scull4f170f52018-07-19 12:58:20 +010038/* clang-format on */
39
Andrew Scullfdd716e2018-12-20 05:37:31 +000040static bool dlog_lock_enabled = false;
41
42/**
43 * Enables the lock protecting the serial device.
44 */
45void dlog_enable_lock(void)
46{
47 dlog_lock_enabled = true;
48}
49
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010050/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010051 * Prints a raw string to the debug log and returns its length.
52 */
53static size_t print_raw_string(const char *str)
54{
55 const char *c = str;
Andrew Scull7364a8e2018-07-19 15:39:29 +010056 while (*c != '\0') {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010057 arch_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010058 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010059 return c - str;
60}
61
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010062/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010063 * Prints a formatted string to the debug log. The format includes a minimum
64 * width, the fill character, and flags (whether to align to left or right).
65 *
66 * str is the full string, while suffix is a pointer within str that indicates
67 * where the suffix begins. This is used when printing right-aligned numbers
68 * with a zero fill; for example, -10 with width 4 should be padded to -010,
69 * so suffix would point to index one of the "-10" string .
70 */
71static void print_string(const char *str, const char *suffix, size_t width,
72 int flags, char fill)
73{
74 size_t len = suffix - str;
75
76 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010077 while (str != suffix) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010078 arch_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010079 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010080
81 if (flags & FLAG_MINUS) {
82 /* Left-aligned. Print suffix, then print padding if needed. */
83 len += print_raw_string(suffix);
84 while (len < width) {
85 arch_putchar(' ');
86 len++;
87 }
88 return;
89 }
90
91 /* Fill until we reach the desired length. */
92 len += strlen(suffix);
93 while (len < width) {
94 arch_putchar(fill);
95 len++;
96 }
97
98 /* Now print the rest of the string. */
99 print_raw_string(suffix);
100}
101
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100102/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100103 * Prints a number to the debug log. The caller specifies the base, its minimum
104 * width and printf-style flags.
105 */
106static void print_num(size_t v, size_t base, size_t width, int flags)
107{
108 static const char *digits_lower = "0123456789abcdefx";
109 static const char *digits_upper = "0123456789ABCDEFX";
110 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
111 char buf[51];
Andrew Scullf3d45592018-09-20 14:30:22 +0100112 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100113 char *num;
114 *ptr = '\0';
115 do {
116 --ptr;
117 *ptr = d[v % base];
118 v /= base;
119 } while (v);
120
121 /* Num stores where the actual number begins. */
122 num = ptr;
123
124 /* Add prefix if requested. */
125 if (flags & FLAG_ALT) {
126 switch (base) {
127 case 16:
128 ptr -= 2;
129 ptr[0] = '0';
130 ptr[1] = d[16];
131 break;
132
133 case 8:
134 ptr--;
135 *ptr = '0';
136 break;
137 }
138 }
139
140 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100141 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100142 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100143 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100144 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100145 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100146 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100147 }
148 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100149 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100150 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100151 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100152 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100153}
154
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100155/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100156 * Parses the optional flags field of a printf-style format. It returns the spot
157 * on the string where a non-flag character was found.
158 */
159static const char *parse_flags(const char *p, int *flags)
160{
161 for (;;) {
162 switch (*p) {
163 case ' ':
164 *flags |= FLAG_SPACE;
165 break;
166
167 case '0':
168 *flags |= FLAG_ZERO;
169 break;
170
171 case '-':
172 *flags |= FLAG_MINUS;
173 break;
174
175 case '+':
176 *flags |= FLAG_PLUS;
177
178 case '#':
179 *flags |= FLAG_ALT;
180 break;
181
182 default:
183 return p;
184 }
185 p++;
186 }
187}
188
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100189/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000190 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100191 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000192void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100193{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000194 static struct spinlock sl = SPINLOCK_INIT;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100195 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100196 size_t w;
197 int flags;
198 char buf[2];
199
Andrew Scullfdd716e2018-12-20 05:37:31 +0000200 if (dlog_lock_enabled) {
201 sl_lock(&sl);
202 }
203
Andrew Scull020ae692018-07-19 16:20:14 +0100204 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100205 switch (*p) {
206 default:
207 arch_putchar(*p);
208 break;
209
210 case '%':
211 /* Read optional flags. */
212 flags = 0;
213 p = parse_flags(p + 1, &flags) - 1;
214
215 /* Read the minimum width, if one is specified. */
216 w = 0;
217 while (p[1] >= '0' && p[1] <= '9') {
218 w = (w * 10) + (p[1] - '0');
219 p++;
220 }
221
222 /* Read minimum width from arguments. */
223 if (w == 0 && p[1] == '*') {
224 int v = va_arg(args, int);
225 if (v >= 0) {
226 w = v;
227 } else {
228 w = -v;
229 flags |= FLAG_MINUS;
230 }
231 p++;
232 }
233
234 /* Handle the format specifier. */
235 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100236 case 's': {
237 char *str = va_arg(args, char *);
238 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100239 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100240 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100241
242 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100243 case 'i': {
244 int v = va_arg(args, int);
245 if (v < 0) {
246 flags |= FLAG_NEG;
247 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100248 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100249
250 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100252 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100253
254 case 'X':
255 flags |= FLAG_UPPER;
256 print_num(va_arg(args, size_t), 16, w, flags);
257 break;
258
259 case 'p':
260 print_num(va_arg(args, size_t), 16,
261 sizeof(size_t) * 2, FLAG_ZERO);
262 p++;
263 break;
264
265 case 'x':
266 print_num(va_arg(args, size_t), 16, w, flags);
267 p++;
268 break;
269
270 case 'u':
271 print_num(va_arg(args, size_t), 10, w, flags);
272 p++;
273 break;
274
275 case 'o':
276 print_num(va_arg(args, size_t), 8, w, flags);
277 p++;
278 break;
279
280 case 'c':
281 buf[1] = 0;
282 buf[0] = va_arg(args, int);
283 print_string(buf, buf, w, flags, ' ');
284 p++;
285 break;
286
287 case '%':
288 break;
289
290 default:
291 arch_putchar('%');
292 }
293
294 break;
295 }
296 }
297
Andrew Scullfdd716e2018-12-20 05:37:31 +0000298 if (dlog_lock_enabled) {
299 sl_unlock(&sl);
300 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100301}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100302
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100303/**
304 * Prints the given format string to the debug log.
305 */
306void dlog(const char *fmt, ...)
307{
308 va_list args;
309
310 va_start(args, fmt);
311 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100312 va_end(args);
313}