Blame view

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

45313e83b   Simon Glass   tiny-printf: Adju...
16
17
18
19
  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...
20

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

45313e83b   Simon Glass   tiny-printf: Adju...
25
  static void out(struct printf_info *info, char c)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
26
  {
45313e83b   Simon Glass   tiny-printf: Adju...
27
  	*info->bf++ = c;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
28
  }
45313e83b   Simon Glass   tiny-printf: Adju...
29
30
31
32
33
  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:...
34
35
  static void div_out(struct printf_info *info, unsigned long *num,
  		    unsigned long div)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
36
37
  {
  	unsigned char dgt = 0;
a5ecdd08f   Stefan Roese   lib/tiny-printf.c...
38
39
  	while (*num >= div) {
  		*num -= div;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
40
41
  		dgt++;
  	}
45313e83b   Simon Glass   tiny-printf: Adju...
42
43
  	if (info->zs || dgt > 0)
  		out_dgt(info, dgt);
7d9cde103   Stefan Roese   lib/tiny-printf.c...
44
  }
cdce1f762   Vignesh R   tiny-printf: Add ...
45
46
47
48
49
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
  #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).
   */
831c16111   Simon Glass   tiny-printf: Reor...
154
155
  static void __maybe_unused pointer(struct printf_info *info, const char *fmt,
  				   void *ptr)
cdce1f762   Vignesh R   tiny-printf: Add ...
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
  {
  #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 ...
192
  static int _vprintf(struct printf_info *info, const char *fmt, va_list va)
7d9cde103   Stefan Roese   lib/tiny-printf.c...
193
  {
7d9cde103   Stefan Roese   lib/tiny-printf.c...
194
195
  	char ch;
  	char *p;
a28e1d983   Andre Przywara   SPL: tiny-printf:...
196
  	unsigned long num;
a5ecdd08f   Stefan Roese   lib/tiny-printf.c...
197
  	char buf[12];
a28e1d983   Andre Przywara   SPL: tiny-printf:...
198
  	unsigned long div;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
199

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

4f1eed752   Alex Kiernan   spl: Disable prin...
319
320
321
322
323
  #if CONFIG_IS_ENABLED(PRINTF)
  static void putc_normal(struct printf_info *info, char ch)
  {
  	putc(ch);
  }
da70b4d14   Hans de Goede   tinyprintf: Add v...
324
325
  int vprintf(const char *fmt, va_list va)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
326
327
328
329
  	struct printf_info info;
  
  	info.putc = putc_normal;
  	return _vprintf(&info, fmt, va);
da70b4d14   Hans de Goede   tinyprintf: Add v...
330
  }
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
331
332
  int printf(const char *fmt, ...)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
333
  	struct printf_info info;
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
334
335
  	va_list va;
  	int ret;
45313e83b   Simon Glass   tiny-printf: Adju...
336
  	info.putc = putc_normal;
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
337
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
338
  	ret = _vprintf(&info, fmt, va);
5c411d88b   Simon Glass   tiny-printf: Supp...
339
340
341
342
  	va_end(va);
  
  	return ret;
  }
4f1eed752   Alex Kiernan   spl: Disable prin...
343
  #endif
5c411d88b   Simon Glass   tiny-printf: Supp...
344

45313e83b   Simon Glass   tiny-printf: Adju...
345
  static void putc_outstr(struct printf_info *info, char ch)
5c411d88b   Simon Glass   tiny-printf: Supp...
346
  {
45313e83b   Simon Glass   tiny-printf: Adju...
347
  	*info->outstr++ = ch;
5c411d88b   Simon Glass   tiny-printf: Supp...
348
  }
abeb272d2   Marek Vasut   tiny-printf: Supp...
349
  int sprintf(char *buf, const char *fmt, ...)
5c411d88b   Simon Glass   tiny-printf: Supp...
350
  {
45313e83b   Simon Glass   tiny-printf: Adju...
351
  	struct printf_info info;
5c411d88b   Simon Glass   tiny-printf: Supp...
352
353
354
355
  	va_list va;
  	int ret;
  
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
356
357
358
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
359
  	va_end(va);
45313e83b   Simon Glass   tiny-printf: Adju...
360
  	*info.outstr = '\0';
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
361
362
363
  
  	return ret;
  }
abeb272d2   Marek Vasut   tiny-printf: Supp...
364

9b3fbb2b4   Simon South   tiny-printf: Supp...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  #if CONFIG_IS_ENABLED(LOG)
  /* Note that size is ignored */
  int vsnprintf(char *buf, size_t size, const char *fmt, va_list va)
  {
  	struct printf_info info;
  	int ret;
  
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
  	*info.outstr = '\0';
  
  	return ret;
  }
  #endif
abeb272d2   Marek Vasut   tiny-printf: Supp...
380
381
382
  /* Note that size is ignored */
  int snprintf(char *buf, size_t size, const char *fmt, ...)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
383
  	struct printf_info info;
abeb272d2   Marek Vasut   tiny-printf: Supp...
384
385
386
387
  	va_list va;
  	int ret;
  
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
388
389
390
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
abeb272d2   Marek Vasut   tiny-printf: Supp...
391
  	va_end(va);
45313e83b   Simon Glass   tiny-printf: Adju...
392
  	*info.outstr = '\0';
abeb272d2   Marek Vasut   tiny-printf: Supp...
393
394
395
  
  	return ret;
  }
dee74e6cc   Simon Glass   tiny-printf: Add ...
396
397
398
399
400
401
  
  void print_grouped_ull(unsigned long long int_val, int digits)
  {
  	/* Don't try to print the upper 32-bits */
  	printf("%ld ", (ulong)int_val);
  }