xref: /openbmc/u-boot/lib/tiny-printf.c (revision a381bcf5)
1 /*
2  * Tiny printf version for SPL
3  *
4  * Copied from:
5  * http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
6  *
7  * Copyright (C) 2004,2008  Kustaa Nyholm
8  *
9  * SPDX-License-Identifier:	LGPL-2.1+
10  */
11 
12 #include <common.h>
13 #include <stdarg.h>
14 #include <serial.h>
15 
16 /*
17  * This code in here may execute before the DRAM is initialised, so
18  * we should make sure that it doesn't touch BSS, which some boards
19  * put in DRAM.
20  */
21 static char *bf __attribute__ ((section(".data")));
22 static char zs __attribute__ ((section(".data")));
23 
24 /* Current position in sprintf() output string */
25 static char *outstr __attribute__ ((section(".data")));
26 
27 static void out(char c)
28 {
29 	*bf++ = c;
30 }
31 
32 static void out_dgt(char dgt)
33 {
34 	out(dgt + (dgt < 10 ? '0' : 'a' - 10));
35 	zs = 1;
36 }
37 
38 static void div_out(unsigned int *num, unsigned int div)
39 {
40 	unsigned char dgt = 0;
41 
42 	while (*num >= div) {
43 		*num -= div;
44 		dgt++;
45 	}
46 
47 	if (zs || dgt > 0)
48 		out_dgt(dgt);
49 }
50 
51 int _vprintf(const char *fmt, va_list va, void (*putc)(const char ch))
52 {
53 	char ch;
54 	char *p;
55 	unsigned int num;
56 	char buf[12];
57 	unsigned int div;
58 
59 	while ((ch = *(fmt++))) {
60 		if (ch != '%') {
61 			putc(ch);
62 		} else {
63 			bool lz = false;
64 			int width = 0;
65 
66 			ch = *(fmt++);
67 			if (ch == '0') {
68 				ch = *(fmt++);
69 				lz = 1;
70 			}
71 
72 			if (ch >= '0' && ch <= '9') {
73 				width = 0;
74 				while (ch >= '0' && ch <= '9') {
75 					width = (width * 10) + ch - '0';
76 					ch = *fmt++;
77 				}
78 			}
79 			bf = buf;
80 			p = bf;
81 			zs = 0;
82 
83 			switch (ch) {
84 			case '\0':
85 				goto abort;
86 			case 'u':
87 			case 'd':
88 				num = va_arg(va, unsigned int);
89 				if (ch == 'd' && (int)num < 0) {
90 					num = -(int)num;
91 					out('-');
92 				}
93 				if (!num) {
94 					out_dgt(0);
95 				} else {
96 					for (div = 1000000000; div; div /= 10)
97 						div_out(&num, div);
98 				}
99 				break;
100 			case 'x':
101 				num = va_arg(va, unsigned int);
102 				if (!num) {
103 					out_dgt(0);
104 				} else {
105 					for (div = 0x10000000; div; div /= 0x10)
106 						div_out(&num, div);
107 				}
108 				break;
109 			case 'c':
110 				out((char)(va_arg(va, int)));
111 				break;
112 			case 's':
113 				p = va_arg(va, char*);
114 				break;
115 			case '%':
116 				out('%');
117 			default:
118 				break;
119 			}
120 
121 			*bf = 0;
122 			bf = p;
123 			while (*bf++ && width > 0)
124 				width--;
125 			while (width-- > 0)
126 				putc(lz ? '0' : ' ');
127 			if (p) {
128 				while ((ch = *p++))
129 					putc(ch);
130 			}
131 		}
132 	}
133 
134 abort:
135 	return 0;
136 }
137 
138 int vprintf(const char *fmt, va_list va)
139 {
140 	return _vprintf(fmt, va, putc);
141 }
142 
143 int printf(const char *fmt, ...)
144 {
145 	va_list va;
146 	int ret;
147 
148 	va_start(va, fmt);
149 	ret = _vprintf(fmt, va, putc);
150 	va_end(va);
151 
152 	return ret;
153 }
154 
155 static void putc_outstr(char ch)
156 {
157 	*outstr++ = ch;
158 }
159 
160 int sprintf(char *buf, const char *fmt, ...)
161 {
162 	va_list va;
163 	int ret;
164 
165 	va_start(va, fmt);
166 	outstr = buf;
167 	ret = _vprintf(fmt, va, putc_outstr);
168 	va_end(va);
169 	*outstr = '\0';
170 
171 	return ret;
172 }
173 
174 /* Note that size is ignored */
175 int snprintf(char *buf, size_t size, const char *fmt, ...)
176 {
177 	va_list va;
178 	int ret;
179 
180 	va_start(va, fmt);
181 	outstr = buf;
182 	ret = _vprintf(fmt, va, putc_outstr);
183 	va_end(va);
184 	*outstr = '\0';
185 
186 	return ret;
187 }
188 
189 void __assert_fail(const char *assertion, const char *file, unsigned line,
190 		   const char *function)
191 {
192 	/* This will not return */
193 	printf("%s:%u: %s: Assertion `%s' failed.", file, line, function,
194 	       assertion);
195 	hang();
196 }
197