Blame view

lib/tiny-printf.c 7.06 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
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
  #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 ...
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
261
  				break;
  			case 'x':
a28e1d983   Andre Przywara   SPL: tiny-printf:...
262
263
264
265
266
267
268
  				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...
269
  				if (!num) {
45313e83b   Simon Glass   tiny-printf: Adju...
270
  					out_dgt(info, 0);
74b1320ae   Simon Glass   tiny-printf: Alwa...
271
  				} else {
a28e1d983   Andre Przywara   SPL: tiny-printf:...
272
  					for (; div; div /= 0x10)
45313e83b   Simon Glass   tiny-printf: Adju...
273
  						div_out(info, &num, div);
74b1320ae   Simon Glass   tiny-printf: Alwa...
274
  				}
7d9cde103   Stefan Roese   lib/tiny-printf.c...
275
276
  				break;
  			case 'c':
45313e83b   Simon Glass   tiny-printf: Adju...
277
  				out(info, (char)(va_arg(va, int)));
7d9cde103   Stefan Roese   lib/tiny-printf.c...
278
279
280
281
  				break;
  			case 's':
  				p = va_arg(va, char*);
  				break;
cdce1f762   Vignesh R   tiny-printf: Add ...
282
283
284
285
286
  			case 'p':
  				pointer(info, fmt, va_arg(va, void *));
  				while (isalnum(fmt[0]))
  					fmt++;
  				break;
7d9cde103   Stefan Roese   lib/tiny-printf.c...
287
  			case '%':
45313e83b   Simon Glass   tiny-printf: Adju...
288
  				out(info, '%');
7d9cde103   Stefan Roese   lib/tiny-printf.c...
289
290
291
  			default:
  				break;
  			}
45313e83b   Simon Glass   tiny-printf: Adju...
292
293
294
  			*info->bf = 0;
  			info->bf = p;
  			while (*info->bf++ && width > 0)
1fb67608b   Simon Glass   tiny-printf: Tidy...
295
296
  				width--;
  			while (width-- > 0)
45313e83b   Simon Glass   tiny-printf: Adju...
297
  				info->putc(info, lz ? '0' : ' ');
8e31681c5   Simon Glass   tiny-printf: Avoi...
298
299
  			if (p) {
  				while ((ch = *p++))
45313e83b   Simon Glass   tiny-printf: Adju...
300
  					info->putc(info, ch);
8e31681c5   Simon Glass   tiny-printf: Avoi...
301
  			}
7d9cde103   Stefan Roese   lib/tiny-printf.c...
302
303
304
305
  		}
  	}
  
  abort:
7d9cde103   Stefan Roese   lib/tiny-printf.c...
306
307
  	return 0;
  }
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
308

4f1eed752   Alex Kiernan   spl: Disable prin...
309
310
311
312
313
  #if CONFIG_IS_ENABLED(PRINTF)
  static void putc_normal(struct printf_info *info, char ch)
  {
  	putc(ch);
  }
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;
  }
4f1eed752   Alex Kiernan   spl: Disable prin...
333
  #endif
5c411d88b   Simon Glass   tiny-printf: Supp...
334

45313e83b   Simon Glass   tiny-printf: Adju...
335
  static void putc_outstr(struct printf_info *info, char ch)
5c411d88b   Simon Glass   tiny-printf: Supp...
336
  {
45313e83b   Simon Glass   tiny-printf: Adju...
337
  	*info->outstr++ = ch;
5c411d88b   Simon Glass   tiny-printf: Supp...
338
  }
abeb272d2   Marek Vasut   tiny-printf: Supp...
339
  int sprintf(char *buf, const char *fmt, ...)
5c411d88b   Simon Glass   tiny-printf: Supp...
340
  {
45313e83b   Simon Glass   tiny-printf: Adju...
341
  	struct printf_info info;
5c411d88b   Simon Glass   tiny-printf: Supp...
342
343
344
345
  	va_list va;
  	int ret;
  
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
346
347
348
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
349
  	va_end(va);
45313e83b   Simon Glass   tiny-printf: Adju...
350
  	*info.outstr = '\0';
962a43cc9   Sjoerd Simons   lib/tiny-printf.c...
351
352
353
  
  	return ret;
  }
abeb272d2   Marek Vasut   tiny-printf: Supp...
354
355
356
357
  
  /* Note that size is ignored */
  int snprintf(char *buf, size_t size, const char *fmt, ...)
  {
45313e83b   Simon Glass   tiny-printf: Adju...
358
  	struct printf_info info;
abeb272d2   Marek Vasut   tiny-printf: Supp...
359
360
361
362
  	va_list va;
  	int ret;
  
  	va_start(va, fmt);
45313e83b   Simon Glass   tiny-printf: Adju...
363
364
365
  	info.outstr = buf;
  	info.putc = putc_outstr;
  	ret = _vprintf(&info, fmt, va);
abeb272d2   Marek Vasut   tiny-printf: Supp...
366
  	va_end(va);
45313e83b   Simon Glass   tiny-printf: Adju...
367
  	*info.outstr = '\0';
abeb272d2   Marek Vasut   tiny-printf: Supp...
368
369
370
  
  	return ret;
  }