blob: 322c0549d3f66ec32b163ca98fd5ca53aea2ad6e [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);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000225
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100226 if (v >= 0) {
227 w = v;
228 } else {
229 w = -v;
230 flags |= FLAG_MINUS;
231 }
232 p++;
233 }
234
235 /* Handle the format specifier. */
236 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100237 case 's': {
238 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000239
Andrew Scull4f170f52018-07-19 12:58:20 +0100240 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100241 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100242 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100243
244 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100245 case 'i': {
246 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000247
Andrew Scull4f170f52018-07-19 12:58:20 +0100248 if (v < 0) {
249 flags |= FLAG_NEG;
250 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100252
253 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100255 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100256
257 case 'X':
258 flags |= FLAG_UPPER;
259 print_num(va_arg(args, size_t), 16, w, flags);
260 break;
261
262 case 'p':
263 print_num(va_arg(args, size_t), 16,
264 sizeof(size_t) * 2, FLAG_ZERO);
265 p++;
266 break;
267
268 case 'x':
269 print_num(va_arg(args, size_t), 16, w, flags);
270 p++;
271 break;
272
273 case 'u':
274 print_num(va_arg(args, size_t), 10, w, flags);
275 p++;
276 break;
277
278 case 'o':
279 print_num(va_arg(args, size_t), 8, w, flags);
280 p++;
281 break;
282
283 case 'c':
284 buf[1] = 0;
285 buf[0] = va_arg(args, int);
286 print_string(buf, buf, w, flags, ' ');
287 p++;
288 break;
289
290 case '%':
291 break;
292
293 default:
294 arch_putchar('%');
295 }
296
297 break;
298 }
299 }
300
Andrew Scullfdd716e2018-12-20 05:37:31 +0000301 if (dlog_lock_enabled) {
302 sl_unlock(&sl);
303 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100304}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100305
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100306/**
307 * Prints the given format string to the debug log.
308 */
309void dlog(const char *fmt, ...)
310{
311 va_list args;
312
313 va_start(args, fmt);
314 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100315 va_end(args);
316}