Blame view

lib/tiny-printf.c 7.25 KB
7d9cde103   Stefan Roese   lib/tiny-printf.c...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Tiny printf version for SPL
   *
   * Copied from:
   * http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
   *
   * Copyright (C) 2004,2008  Kustaa Nyholm
   *
   * SPDX-License-Identifier:	LGPL-2.1+
   */
  
  #include <common.h>
  #include <stdarg.h>
  #include <serial.h>
cdce1f762   Vignesh R   tiny-printf: Add ...
15
  #include <linux/ctype.h>
7d9cde103   Stefan Roese   lib/tiny-printf.c...
16

45313e83b   Simon Glass   tiny-printf: Adju...
17
18
19
20
  struct printf_info {
  	char *bf;	/* Digit buffer */
  	char zs;	/* non-zero if a digit has been written */
  	char *outstr;	/* Next output position for sprintf() */
7d9cde103   Stefan Roese   lib/tiny-printf.c...
21

45313e83b   Simon Glass   tiny-printf: Adju...
22
23
24
  	/* Output a character */
  	void (*putc)(struct printf_info *info, char ch);
  };
5c411d88b   Simon Glass   tiny-printf: Supp...
25

433cbfb3b   Masahiro Yamada   tiny-printf: add ...
26
  static void putc_normal(struct printf_info *info, char ch)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
27
  {
45313e83b   Simon Glass   tiny-printf: Adju...
28
  	putc(ch);
7d9cde103   Stefan Roese   lib/tiny-printf.c...
29
  }
45313e83b   Simon Glass   tiny-printf: Adju...
30
  static void out(struct printf_info *info, char c)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
31
  {
45313e83b   Simon Glass   tiny-printf: Adju...
32
  	*info->bf++ = c;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
33
  }
45313e83b   Simon Glass   tiny-printf: Adju...
34
35
36
37
38
  static void out_dgt(struct printf_info *info, char dgt)
  {
  	out(info, dgt + (dgt < 10 ? '0' : 'a' - 10));
  	info->zs = 1;
  }
a28e1d983   Andre Przywara   SPL: tiny-printf:...
39
40
  static void div_out(struct printf_info *info, unsigned long *num,
  		    unsigned long div)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
41
42
  {
  	unsigned char dgt = 0;
a5ecdd08f   Stefan Roese   lib/tiny-printf.c...
43
44
  	while (*num >= div) {
  		*num -= div;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
45
46
  		dgt++;
  	}
45313e83b   Simon Glass   tiny-printf: Adju...
47
48
  	if (info->zs || dgt > 0)
  		out_dgt(info, dgt);
7d9cde103   Stefan Roese   lib/tiny-printf.c...
49
  }
cdce1f762   Vignesh R   tiny-printf: Add ...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  #ifdef CONFIG_SPL_NET_SUPPORT
  static void string(struct printf_info *info, char *s)
  {
  	char ch;
  
  	while ((ch = *s++))
  		out(info, ch);
  }
  
  static const char hex_asc[] = "0123456789abcdef";
  #define hex_asc_lo(x)	hex_asc[((x) & 0x0f)]
  #define hex_asc_hi(x)	hex_asc[((x) & 0xf0) >> 4]
  
  static inline char *pack_hex_byte(char *buf, u8 byte)
  {
  	*buf++ = hex_asc_hi(byte);
  	*buf++ = hex_asc_lo(byte);
  	return buf;
  }
  
  static void mac_address_string(struct printf_info *info, u8 *addr,
  				bool separator)
  {
  	/* (6 * 2 hex digits), 5 colons and trailing zero */
  	char mac_addr[6 * 3];
  	char *p = mac_addr;
  	int i;
  
  	for (i = 0; i < 6; i++) {
  		p = pack_hex_byte(p, addr[i]);
  		if (separator && i != 5)
  			*p++ = ':';
  	}
  	*p = '\0';
  
  	string(info, mac_addr);
  }
  
  static char *put_dec_trunc(char *buf, unsigned int q)
  {
  	unsigned int d3, d2, d1, d0;
  	d1 = (q >> 4) & 0xf;
  	d2 = (q >> 8) & 0xf;
  	d3 = (q >> 12);
  
  	d0 = 6 * (d3 + d2 + d1) + (q & 0xf);
  	q = (d0 * 0xcd) >> 11;
  	d0 = d0 - 10 * q;
  	*buf++ = d0 + '0'; /* least significant digit */
  	d1 = q + 9 * d3 + 5 * d2 + d1;
  	if (d1 != 0) {
  		q = (d1 * 0xcd) >> 11;
  		d1 = d1 - 10 * q;
  		*buf++ = d1 + '0'; /* next digit */
  
  		d2 = q + 2 * d2;
  		if ((d2 != 0) || (d3 != 0)) {
  			q = (d2 * 0xd) >> 7;
  			d2 = d2 - 10 * q;
  			*buf++ = d2 + '0'; /* next digit */
  
  			d3 = q + 4 * d3;
  			if (d3 != 0) {
  				q = (d3 * 0xcd) >> 11;
  				d3 = d3 - 10 * q;
  				*buf++ = d3 + '0';  /* next digit */
  				if (q != 0)
  					*buf++ = q + '0'; /* most sign. digit */
  			}
  		}
  	}
  	return buf;
  }
  
  static void ip4_addr_string(struct printf_info *info, u8 *addr)
  {
  	/* (4 * 3 decimal digits), 3 dots and trailing zero */
  	char ip4_addr[4 * 4];
  	char temp[3];	/* hold each IP quad in reverse order */
  	char *p = ip4_addr;
  	int i, digits;
  
  	for (i = 0; i < 4; i++) {
  		digits = put_dec_trunc(temp, addr[i]) - temp;
  		/* reverse the digits in the quad */
  		while (digits--)
  			*p++ = temp[digits];
  		if (i != 3)
  			*p++ = '.';
  	}
  	*p = '\0';
  
  	string(info, ip4_addr);
  }
  #endif
  
  /*
   * Show a '%p' thing.  A kernel extension is that the '%p' is followed
   * by an extra set of characters that are extended format
   * specifiers.
   *
   * Right now we handle:
   *
   * - 'M' For a 6-byte MAC address, it prints the address in the
   *       usual colon-separated hex notation.
   * - 'm' Same as above except there is no colon-separator.
   * - 'I4'for IPv4 addresses printed in the usual way (dot-separated
   *       decimal).
   */
  
  static void pointer(struct printf_info *info, const char *fmt, void *ptr)
  {
  #ifdef DEBUG
  	unsigned long num = (uintptr_t)ptr;
  	unsigned long div;
  #endif
  
  	switch (*fmt) {
  #ifdef DEBUG
  	case 'a':
  
  		switch (fmt[1]) {
  		case 'p':
  		default:
  			num = *(phys_addr_t *)ptr;
  			break;
  		}
  		break;
  #endif
  #ifdef CONFIG_SPL_NET_SUPPORT
  	case 'm':
  		return mac_address_string(info, ptr, false);
  	case 'M':
  		return mac_address_string(info, ptr, true);
  	case 'I':
  		if (fmt[1] == '4')
  			return ip4_addr_string(info, ptr);
  #endif
  	default:
  		break;
  	}
  #ifdef DEBUG
  	div = 1UL << (sizeof(long) * 8 - 4);
  	for (; div; div /= 0x10)
  		div_out(info, &num, div);
  #endif
  }
433cbfb3b   Masahiro Yamada   tiny-printf: add ...
197
  static int _vprintf(struct printf_info *info, const char *fmt, va_list va)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
198
  {
7d9cde103   Stefan Roese   lib/tiny-printf.c...
199
200
  	char ch;
  	char *p;
a28e1d983   Andre Przywara   SPL: tiny-printf:...
201
  	unsigned long num;
a5ecdd08f   Stefan Roese   lib/tiny-printf.c...
202
  	char buf[12];
a28e1d983   Andre Przywara   SPL: tiny-printf:...
203
  	unsigned long div;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
204

7d9cde103   Stefan Roese   lib/tiny-printf.c...
205
206
  	while ((ch = *(fmt++))) {
  		if (ch != '%') {
45313e83b   Simon Glass   tiny-printf: Adju...
207
  			info->putc(info, ch);
7d9cde103   Stefan Roese   lib/tiny-printf.c...
208
  		} else {
1fb67608b   Simon Glass   tiny-printf: Tidy...
209
210
  			bool lz = false;
  			int width = 0;
a28e1d983   Andre Przywara   SPL: tiny-printf:...
211
  			bool islong = false;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
212
213
  
  			ch = *(fmt++);
1c853629d   Andre Przywara   SPL: tiny-printf:...
214
215
  			if (ch == '-')
  				ch = *(fmt++);
7d9cde103   Stefan Roese   lib/tiny-printf.c...
216
217
218
219
220
221
  			if (ch == '0') {
  				ch = *(fmt++);
  				lz = 1;
  			}
  
  			if (ch >= '0' && ch <= '9') {
1fb67608b   Simon Glass   tiny-printf: Tidy...
222
  				width = 0;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
223
  				while (ch >= '0' && ch <= '9') {
1fb67608b   Simon Glass   tiny-printf: Tidy...
224
  					width = (width * 10) + ch - '0';
7d9cde103   Stefan Roese   lib/tiny-printf.c...
225
226
227
  					ch = *fmt++;
  				}
  			}
a28e1d983   Andre Przywara   SPL: tiny-printf:...
228
229
230
231
  			if (ch == 'l') {
  				ch = *(fmt++);
  				islong = true;
  			}
45313e83b   Simon Glass   tiny-printf: Adju...
232
233
234
  			info->bf = buf;
  			p = info->bf;
  			info->zs = 0;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
235
236
  
  			switch (ch) {
1fb67608b   Simon Glass   tiny-printf: Tidy...
237
  			case '\0':
7d9cde103   Stefan Roese   lib/tiny-printf.c...
238
239
240
  				goto abort;
  			case 'u':
  			case 'd':
a28e1d983   Andre Przywara   SPL: tiny-printf:...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  				div = 1000000000;
  				if (islong) {
  					num = va_arg(va, unsigned long);
  					if (sizeof(long) > 4)
  						div *= div * 10;
  				} else {
  					num = va_arg(va, unsigned int);
  				}
  
  				if (ch == 'd') {
  					if (islong && (long)num < 0) {
  						num = -(long)num;
  						out(info, '-');
  					} else if (!islong && (int)num < 0) {
  						num = -(int)num;
  						out(info, '-');
  					}
7d9cde103   Stefan Roese   lib/tiny-printf.c...
258
  				}
74b1320ae   Simon Glass   tiny-printf: Alwa...
259
  				if (!num) {
45313e83b   Simon Glass   tiny-printf: Adju...
260
  					out_dgt(info, 0);
74b1320ae   Simon Glass   tiny-printf: Alwa...
261
  				} else {
a28e1d983   Andre Przywara   SPL: tiny-printf:...
262
  					for (; div; div /= 10)
45313e83b   Simon Glass   tiny-printf: Adju...
263
  						div_out(info, &num, div);
74b1320ae   Simon Glass   tiny-printf: Alwa...
264
  				}
7d9cde103   Stefan Roese   lib/tiny-printf.c...
265
266
  				break;
  			case 'x':
a28e1d983   Andre Przywara   SPL: tiny-printf:...
267
268
269
270
271
272
273
  				if (islong) {
  					num = va_arg(va, unsigned long);
  					div = 1UL << (sizeof(long) * 8 - 4);
  				} else {
  					num = va_arg(va, unsigned int);
  					div = 0x10000000;
  				}
74b1320ae   Simon Glass   tiny-printf: Alwa...
274
  				if (!num) {
45313e83b   Simon Glass   tiny-printf: Adju...
275
  					out_dgt(info, 0);
74b1320ae   Simon Glass   tiny-printf: Alwa...
276
  				} else {
a28e1d983   Andre Przywara   SPL: tiny-printf:...
277
  					for (; div; div /= 0x10)
45313e83b   Simon Glass   tiny-printf: Adju...
278
  						div_out(info, &num, div);
74b1320ae   Simon Glass   tiny-printf: Alwa...
279
  				}
7d9cde103   Stefan Roese   lib/tiny-printf.c...
280
281
  				break;
  			case 'c':
45313e83b   Simon Glass   tiny-printf: Adju...
282
  				out(info, (char)(va_arg(va, int)));
7d9cde103   Stefan Roese   lib/tiny-printf.c...
283
284
285
286
  				break;
  			case 's':
  				p = va_arg(va, char*);
  				break;
cdce1f762   Vignesh R   tiny-printf: Add ...
287
288
289
290
291
  			case 'p':
  				pointer(info, fmt, va_arg(va, void *));
  				while (isalnum(fmt[0]))
  					fmt++;
  				break;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
292
  			case '%':
45313e83b   Simon Glass   tiny-printf: Adju...
293
  				out(info, '%');
7d9cde103   Stefan Roese   lib/tiny-printf.c...
294
295
296
  			default:
  				break;
  			}
45313e83b   Simon Glass   tiny-printf: Adju...
297
298
299
  			*info->bf = 0;
  			info->bf = p;
  			while (*info->bf++ && width > 0)
1fb67608b   Simon Glass   tiny-printf: Tidy...
300
301
  				width--;
  			while (width-- > 0)
45313e83b   Simon Glass   tiny-printf: Adju...
302
  				info->putc(info, lz ? '0' : ' ');
8e31681c5   Simon Glass   tiny-printf: Avoi...
303
304
  			if (p) {
  				while ((ch = *p++))
45313e83b   Simon Glass   tiny-printf: Adju...
305
  					info->putc(info, ch);
8e31681c5   Simon Glass   tiny-printf: Avoi...
306
  			}
7d9cde103   Stefan Roese   lib/tiny-printf.c...
307
308
309
310
  		}
  	}
  
  abort:
7d9cde103   Stefan Roese   lib/tiny-printf.c...
311
312
  	return 0;
  }
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
313

da70b4d14   Hans de Goede   tinyprintf: Add v...
314
315
  int vprintf(const char *fmt, va_list va)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
316
317
318
319
  	struct printf_info info;
  
  	info.putc = putc_normal;
  	return _vprintf(&info, fmt, va);
da70b4d14   Hans de Goede   tinyprintf: Add v...
320
  }
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
321
322
  int printf(const char *fmt, ...)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
323
  	struct printf_info info;
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
324
325
  	va_list va;
  	int ret;
45313e83b   Simon Glass   tiny-printf: Adju...
326
  	info.putc = putc_normal;
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
327
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
328
  	ret = _vprintf(&info, fmt, va);
5c411d88b   Simon Glass   tiny-printf: Supp...
329
330
331
332
  	va_end(va);
  
  	return ret;
  }
45313e83b   Simon Glass   tiny-printf: Adju...
333
  static void putc_outstr(struct printf_info *info, char ch)
5c411d88b   Simon Glass   tiny-printf: Supp...
334
  {
45313e83b   Simon Glass   tiny-printf: Adju...
335
  	*info->outstr++ = ch;
5c411d88b   Simon Glass   tiny-printf: Supp...
336
  }
abeb272d2   Marek Vasut   tiny-printf: Supp...
337
  int sprintf(char *buf, const char *fmt, ...)
5c411d88b   Simon Glass   tiny-printf: Supp...
338
  {
45313e83b   Simon Glass   tiny-printf: Adju...
339
  	struct printf_info info;
5c411d88b   Simon Glass   tiny-printf: Supp...
340
341
342
343
  	va_list va;
  	int ret;
  
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
344
345
346
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
347
  	va_end(va);
45313e83b   Simon Glass   tiny-printf: Adju...
348
  	*info.outstr = '\0';
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
349
350
351
  
  	return ret;
  }
abeb272d2   Marek Vasut   tiny-printf: Supp...
352
353
354
355
  
  /* Note that size is ignored */
  int snprintf(char *buf, size_t size, const char *fmt, ...)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
356
  	struct printf_info info;
abeb272d2   Marek Vasut   tiny-printf: Supp...
357
358
359
360
  	va_list va;
  	int ret;
  
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
361
362
363
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
abeb272d2   Marek Vasut   tiny-printf: Supp...
364
  	va_end(va);
45313e83b   Simon Glass   tiny-printf: Adju...
365
  	*info.outstr = '\0';
abeb272d2   Marek Vasut   tiny-printf: Supp...
366
367
368
  
  	return ret;
  }
e24091398   Simon Glass   tiny-printf: Supp...
369
370
371
372
373
374
375
376
377
  
  void __assert_fail(const char *assertion, const char *file, unsigned line,
  		   const char *function)
  {
  	/* This will not return */
  	printf("%s:%u: %s: Assertion `%s' failed.", file, line, function,
  	       assertion);
  	hang();
  }