blob: 9471cea1f91accc9eb94e04c157b18d619d74212 [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 Scull18c78fc2018-08-20 12:57:41 +010022#include "hf/arch.h"
23#include "hf/spinlock.h"
24#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025
Andrew Scull4f170f52018-07-19 12:58:20 +010026/* Keep macro alignment */
27/* clang-format off */
28
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010029#define FLAG_SPACE 0x01
30#define FLAG_ZERO 0x02
31#define FLAG_MINUS 0x04
32#define FLAG_PLUS 0x08
33#define FLAG_ALT 0x10
34#define FLAG_UPPER 0x20
35#define FLAG_NEG 0x40
36
Andrew Scull4f170f52018-07-19 12:58:20 +010037/* clang-format on */
38
Andrew Scullfdd716e2018-12-20 05:37:31 +000039static bool dlog_lock_enabled = false;
40
41/**
42 * Enables the lock protecting the serial device.
43 */
44void dlog_enable_lock(void)
45{
46 dlog_lock_enabled = true;
47}
48
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010049/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010050 * Prints a raw string to the debug log and returns its length.
51 */
52static size_t print_raw_string(const char *str)
53{
54 const char *c = str;
Andrew Scull7364a8e2018-07-19 15:39:29 +010055 while (*c != '\0') {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010056 arch_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010057 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010058 return c - str;
59}
60
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010061/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010062 * Prints a formatted string to the debug log. The format includes a minimum
63 * width, the fill character, and flags (whether to align to left or right).
64 *
65 * str is the full string, while suffix is a pointer within str that indicates
66 * where the suffix begins. This is used when printing right-aligned numbers
67 * with a zero fill; for example, -10 with width 4 should be padded to -010,
68 * so suffix would point to index one of the "-10" string .
69 */
70static void print_string(const char *str, const char *suffix, size_t width,
71 int flags, char fill)
72{
73 size_t len = suffix - str;
74
75 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010076 while (str != suffix) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010077 arch_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010078 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010079
80 if (flags & FLAG_MINUS) {
81 /* Left-aligned. Print suffix, then print padding if needed. */
82 len += print_raw_string(suffix);
83 while (len < width) {
84 arch_putchar(' ');
85 len++;
86 }
87 return;
88 }
89
90 /* Fill until we reach the desired length. */
91 len += strlen(suffix);
92 while (len < width) {
93 arch_putchar(fill);
94 len++;
95 }
96
97 /* Now print the rest of the string. */
98 print_raw_string(suffix);
99}
100
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100101/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100102 * Prints a number to the debug log. The caller specifies the base, its minimum
103 * width and printf-style flags.
104 */
105static void print_num(size_t v, size_t base, size_t width, int flags)
106{
107 static const char *digits_lower = "0123456789abcdefx";
108 static const char *digits_upper = "0123456789ABCDEFX";
109 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
110 char buf[51];
Andrew Scullf3d45592018-09-20 14:30:22 +0100111 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100112 char *num;
113 *ptr = '\0';
114 do {
115 --ptr;
116 *ptr = d[v % base];
117 v /= base;
118 } while (v);
119
120 /* Num stores where the actual number begins. */
121 num = ptr;
122
123 /* Add prefix if requested. */
124 if (flags & FLAG_ALT) {
125 switch (base) {
126 case 16:
127 ptr -= 2;
128 ptr[0] = '0';
129 ptr[1] = d[16];
130 break;
131
132 case 8:
133 ptr--;
134 *ptr = '0';
135 break;
136 }
137 }
138
139 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100140 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100141 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100142 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100143 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100144 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100145 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100146 }
147 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100148 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100149 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100150 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100151 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100152}
153
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100154/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100155 * Parses the optional flags field of a printf-style format. It returns the spot
156 * on the string where a non-flag character was found.
157 */
158static const char *parse_flags(const char *p, int *flags)
159{
160 for (;;) {
161 switch (*p) {
162 case ' ':
163 *flags |= FLAG_SPACE;
164 break;
165
166 case '0':
167 *flags |= FLAG_ZERO;
168 break;
169
170 case '-':
171 *flags |= FLAG_MINUS;
172 break;
173
174 case '+':
175 *flags |= FLAG_PLUS;
176
177 case '#':
178 *flags |= FLAG_ALT;
179 break;
180
181 default:
182 return p;
183 }
184 p++;
185 }
186}
187
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100188/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000189 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100190 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000191void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100192{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000193 static struct spinlock sl = SPINLOCK_INIT;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100194 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100195 size_t w;
196 int flags;
197 char buf[2];
198
Andrew Scullfdd716e2018-12-20 05:37:31 +0000199 if (dlog_lock_enabled) {
200 sl_lock(&sl);
201 }
202
Andrew Scull020ae692018-07-19 16:20:14 +0100203 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100204 switch (*p) {
205 default:
206 arch_putchar(*p);
207 break;
208
209 case '%':
210 /* Read optional flags. */
211 flags = 0;
212 p = parse_flags(p + 1, &flags) - 1;
213
214 /* Read the minimum width, if one is specified. */
215 w = 0;
216 while (p[1] >= '0' && p[1] <= '9') {
217 w = (w * 10) + (p[1] - '0');
218 p++;
219 }
220
221 /* Read minimum width from arguments. */
222 if (w == 0 && p[1] == '*') {
223 int v = va_arg(args, int);
224 if (v >= 0) {
225 w = v;
226 } else {
227 w = -v;
228 flags |= FLAG_MINUS;
229 }
230 p++;
231 }
232
233 /* Handle the format specifier. */
234 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100235 case 's': {
236 char *str = va_arg(args, char *);
237 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100238 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100239 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240
241 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100242 case 'i': {
243 int v = va_arg(args, int);
244 if (v < 0) {
245 flags |= FLAG_NEG;
246 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100248
249 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100250 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100251 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100252
253 case 'X':
254 flags |= FLAG_UPPER;
255 print_num(va_arg(args, size_t), 16, w, flags);
256 break;
257
258 case 'p':
259 print_num(va_arg(args, size_t), 16,
260 sizeof(size_t) * 2, FLAG_ZERO);
261 p++;
262 break;
263
264 case 'x':
265 print_num(va_arg(args, size_t), 16, w, flags);
266 p++;
267 break;
268
269 case 'u':
270 print_num(va_arg(args, size_t), 10, w, flags);
271 p++;
272 break;
273
274 case 'o':
275 print_num(va_arg(args, size_t), 8, w, flags);
276 p++;
277 break;
278
279 case 'c':
280 buf[1] = 0;
281 buf[0] = va_arg(args, int);
282 print_string(buf, buf, w, flags, ' ');
283 p++;
284 break;
285
286 case '%':
287 break;
288
289 default:
290 arch_putchar('%');
291 }
292
293 break;
294 }
295 }
296
Andrew Scullfdd716e2018-12-20 05:37:31 +0000297 if (dlog_lock_enabled) {
298 sl_unlock(&sl);
299 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100300}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100301
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100302/**
303 * Prints the given format string to the debug log.
304 */
305void dlog(const char *fmt, ...)
306{
307 va_list args;
308
309 va_start(args, fmt);
310 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100311 va_end(args);
312}