xref: /rk3399_rockchip-uboot/lib/tiny-printf.c (revision cdce1f762029619e80061833b153cdbf3d0a0860)
17d9cde10SStefan Roese /*
27d9cde10SStefan Roese  * Tiny printf version for SPL
37d9cde10SStefan Roese  *
47d9cde10SStefan Roese  * Copied from:
57d9cde10SStefan Roese  * http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
67d9cde10SStefan Roese  *
77d9cde10SStefan Roese  * Copyright (C) 2004,2008  Kustaa Nyholm
87d9cde10SStefan Roese  *
97d9cde10SStefan Roese  * SPDX-License-Identifier:	LGPL-2.1+
107d9cde10SStefan Roese  */
117d9cde10SStefan Roese 
127d9cde10SStefan Roese #include <common.h>
137d9cde10SStefan Roese #include <stdarg.h>
147d9cde10SStefan Roese #include <serial.h>
15*cdce1f76SVignesh R #include <linux/ctype.h>
167d9cde10SStefan Roese 
1745313e83SSimon Glass struct printf_info {
1845313e83SSimon Glass 	char *bf;	/* Digit buffer */
1945313e83SSimon Glass 	char zs;	/* non-zero if a digit has been written */
2045313e83SSimon Glass 	char *outstr;	/* Next output position for sprintf() */
217d9cde10SStefan Roese 
2245313e83SSimon Glass 	/* Output a character */
2345313e83SSimon Glass 	void (*putc)(struct printf_info *info, char ch);
2445313e83SSimon Glass };
255c411d88SSimon Glass 
putc_normal(struct printf_info * info,char ch)26433cbfb3SMasahiro Yamada static void putc_normal(struct printf_info *info, char ch)
277d9cde10SStefan Roese {
2845313e83SSimon Glass 	putc(ch);
297d9cde10SStefan Roese }
307d9cde10SStefan Roese 
out(struct printf_info * info,char c)3145313e83SSimon Glass static void out(struct printf_info *info, char c)
327d9cde10SStefan Roese {
3345313e83SSimon Glass 	*info->bf++ = c;
347d9cde10SStefan Roese }
357d9cde10SStefan Roese 
out_dgt(struct printf_info * info,char dgt)3645313e83SSimon Glass static void out_dgt(struct printf_info *info, char dgt)
3745313e83SSimon Glass {
3845313e83SSimon Glass 	out(info, dgt + (dgt < 10 ? '0' : 'a' - 10));
3945313e83SSimon Glass 	info->zs = 1;
4045313e83SSimon Glass }
4145313e83SSimon Glass 
div_out(struct printf_info * info,unsigned long * num,unsigned long div)42a28e1d98SAndre Przywara static void div_out(struct printf_info *info, unsigned long *num,
43a28e1d98SAndre Przywara 		    unsigned long div)
447d9cde10SStefan Roese {
457d9cde10SStefan Roese 	unsigned char dgt = 0;
467d9cde10SStefan Roese 
47a5ecdd08SStefan Roese 	while (*num >= div) {
48a5ecdd08SStefan Roese 		*num -= div;
497d9cde10SStefan Roese 		dgt++;
507d9cde10SStefan Roese 	}
517d9cde10SStefan Roese 
5245313e83SSimon Glass 	if (info->zs || dgt > 0)
5345313e83SSimon Glass 		out_dgt(info, dgt);
547d9cde10SStefan Roese }
557d9cde10SStefan Roese 
56*cdce1f76SVignesh R #ifdef CONFIG_SPL_NET_SUPPORT
string(struct printf_info * info,char * s)57*cdce1f76SVignesh R static void string(struct printf_info *info, char *s)
58*cdce1f76SVignesh R {
59*cdce1f76SVignesh R 	char ch;
60*cdce1f76SVignesh R 
61*cdce1f76SVignesh R 	while ((ch = *s++))
62*cdce1f76SVignesh R 		out(info, ch);
63*cdce1f76SVignesh R }
64*cdce1f76SVignesh R 
65*cdce1f76SVignesh R static const char hex_asc[] = "0123456789abcdef";
66*cdce1f76SVignesh R #define hex_asc_lo(x)	hex_asc[((x) & 0x0f)]
67*cdce1f76SVignesh R #define hex_asc_hi(x)	hex_asc[((x) & 0xf0) >> 4]
68*cdce1f76SVignesh R 
pack_hex_byte(char * buf,u8 byte)69*cdce1f76SVignesh R static inline char *pack_hex_byte(char *buf, u8 byte)
70*cdce1f76SVignesh R {
71*cdce1f76SVignesh R 	*buf++ = hex_asc_hi(byte);
72*cdce1f76SVignesh R 	*buf++ = hex_asc_lo(byte);
73*cdce1f76SVignesh R 	return buf;
74*cdce1f76SVignesh R }
75*cdce1f76SVignesh R 
mac_address_string(struct printf_info * info,u8 * addr,bool separator)76*cdce1f76SVignesh R static void mac_address_string(struct printf_info *info, u8 *addr,
77*cdce1f76SVignesh R 				bool separator)
78*cdce1f76SVignesh R {
79*cdce1f76SVignesh R 	/* (6 * 2 hex digits), 5 colons and trailing zero */
80*cdce1f76SVignesh R 	char mac_addr[6 * 3];
81*cdce1f76SVignesh R 	char *p = mac_addr;
82*cdce1f76SVignesh R 	int i;
83*cdce1f76SVignesh R 
84*cdce1f76SVignesh R 	for (i = 0; i < 6; i++) {
85*cdce1f76SVignesh R 		p = pack_hex_byte(p, addr[i]);
86*cdce1f76SVignesh R 		if (separator && i != 5)
87*cdce1f76SVignesh R 			*p++ = ':';
88*cdce1f76SVignesh R 	}
89*cdce1f76SVignesh R 	*p = '\0';
90*cdce1f76SVignesh R 
91*cdce1f76SVignesh R 	string(info, mac_addr);
92*cdce1f76SVignesh R }
93*cdce1f76SVignesh R 
put_dec_trunc(char * buf,unsigned int q)94*cdce1f76SVignesh R static char *put_dec_trunc(char *buf, unsigned int q)
95*cdce1f76SVignesh R {
96*cdce1f76SVignesh R 	unsigned int d3, d2, d1, d0;
97*cdce1f76SVignesh R 	d1 = (q >> 4) & 0xf;
98*cdce1f76SVignesh R 	d2 = (q >> 8) & 0xf;
99*cdce1f76SVignesh R 	d3 = (q >> 12);
100*cdce1f76SVignesh R 
101*cdce1f76SVignesh R 	d0 = 6 * (d3 + d2 + d1) + (q & 0xf);
102*cdce1f76SVignesh R 	q = (d0 * 0xcd) >> 11;
103*cdce1f76SVignesh R 	d0 = d0 - 10 * q;
104*cdce1f76SVignesh R 	*buf++ = d0 + '0'; /* least significant digit */
105*cdce1f76SVignesh R 	d1 = q + 9 * d3 + 5 * d2 + d1;
106*cdce1f76SVignesh R 	if (d1 != 0) {
107*cdce1f76SVignesh R 		q = (d1 * 0xcd) >> 11;
108*cdce1f76SVignesh R 		d1 = d1 - 10 * q;
109*cdce1f76SVignesh R 		*buf++ = d1 + '0'; /* next digit */
110*cdce1f76SVignesh R 
111*cdce1f76SVignesh R 		d2 = q + 2 * d2;
112*cdce1f76SVignesh R 		if ((d2 != 0) || (d3 != 0)) {
113*cdce1f76SVignesh R 			q = (d2 * 0xd) >> 7;
114*cdce1f76SVignesh R 			d2 = d2 - 10 * q;
115*cdce1f76SVignesh R 			*buf++ = d2 + '0'; /* next digit */
116*cdce1f76SVignesh R 
117*cdce1f76SVignesh R 			d3 = q + 4 * d3;
118*cdce1f76SVignesh R 			if (d3 != 0) {
119*cdce1f76SVignesh R 				q = (d3 * 0xcd) >> 11;
120*cdce1f76SVignesh R 				d3 = d3 - 10 * q;
121*cdce1f76SVignesh R 				*buf++ = d3 + '0';  /* next digit */
122*cdce1f76SVignesh R 				if (q != 0)
123*cdce1f76SVignesh R 					*buf++ = q + '0'; /* most sign. digit */
124*cdce1f76SVignesh R 			}
125*cdce1f76SVignesh R 		}
126*cdce1f76SVignesh R 	}
127*cdce1f76SVignesh R 	return buf;
128*cdce1f76SVignesh R }
129*cdce1f76SVignesh R 
ip4_addr_string(struct printf_info * info,u8 * addr)130*cdce1f76SVignesh R static void ip4_addr_string(struct printf_info *info, u8 *addr)
131*cdce1f76SVignesh R {
132*cdce1f76SVignesh R 	/* (4 * 3 decimal digits), 3 dots and trailing zero */
133*cdce1f76SVignesh R 	char ip4_addr[4 * 4];
134*cdce1f76SVignesh R 	char temp[3];	/* hold each IP quad in reverse order */
135*cdce1f76SVignesh R 	char *p = ip4_addr;
136*cdce1f76SVignesh R 	int i, digits;
137*cdce1f76SVignesh R 
138*cdce1f76SVignesh R 	for (i = 0; i < 4; i++) {
139*cdce1f76SVignesh R 		digits = put_dec_trunc(temp, addr[i]) - temp;
140*cdce1f76SVignesh R 		/* reverse the digits in the quad */
141*cdce1f76SVignesh R 		while (digits--)
142*cdce1f76SVignesh R 			*p++ = temp[digits];
143*cdce1f76SVignesh R 		if (i != 3)
144*cdce1f76SVignesh R 			*p++ = '.';
145*cdce1f76SVignesh R 	}
146*cdce1f76SVignesh R 	*p = '\0';
147*cdce1f76SVignesh R 
148*cdce1f76SVignesh R 	string(info, ip4_addr);
149*cdce1f76SVignesh R }
150*cdce1f76SVignesh R #endif
151*cdce1f76SVignesh R 
152*cdce1f76SVignesh R /*
153*cdce1f76SVignesh R  * Show a '%p' thing.  A kernel extension is that the '%p' is followed
154*cdce1f76SVignesh R  * by an extra set of characters that are extended format
155*cdce1f76SVignesh R  * specifiers.
156*cdce1f76SVignesh R  *
157*cdce1f76SVignesh R  * Right now we handle:
158*cdce1f76SVignesh R  *
159*cdce1f76SVignesh R  * - 'M' For a 6-byte MAC address, it prints the address in the
160*cdce1f76SVignesh R  *       usual colon-separated hex notation.
161*cdce1f76SVignesh R  * - 'm' Same as above except there is no colon-separator.
162*cdce1f76SVignesh R  * - 'I4'for IPv4 addresses printed in the usual way (dot-separated
163*cdce1f76SVignesh R  *       decimal).
164*cdce1f76SVignesh R  */
165*cdce1f76SVignesh R 
pointer(struct printf_info * info,const char * fmt,void * ptr)166*cdce1f76SVignesh R static void pointer(struct printf_info *info, const char *fmt, void *ptr)
167*cdce1f76SVignesh R {
168*cdce1f76SVignesh R #ifdef DEBUG
169*cdce1f76SVignesh R 	unsigned long num = (uintptr_t)ptr;
170*cdce1f76SVignesh R 	unsigned long div;
171*cdce1f76SVignesh R #endif
172*cdce1f76SVignesh R 
173*cdce1f76SVignesh R 	switch (*fmt) {
174*cdce1f76SVignesh R #ifdef DEBUG
175*cdce1f76SVignesh R 	case 'a':
176*cdce1f76SVignesh R 
177*cdce1f76SVignesh R 		switch (fmt[1]) {
178*cdce1f76SVignesh R 		case 'p':
179*cdce1f76SVignesh R 		default:
180*cdce1f76SVignesh R 			num = *(phys_addr_t *)ptr;
181*cdce1f76SVignesh R 			break;
182*cdce1f76SVignesh R 		}
183*cdce1f76SVignesh R 		break;
184*cdce1f76SVignesh R #endif
185*cdce1f76SVignesh R #ifdef CONFIG_SPL_NET_SUPPORT
186*cdce1f76SVignesh R 	case 'm':
187*cdce1f76SVignesh R 		return mac_address_string(info, ptr, false);
188*cdce1f76SVignesh R 	case 'M':
189*cdce1f76SVignesh R 		return mac_address_string(info, ptr, true);
190*cdce1f76SVignesh R 	case 'I':
191*cdce1f76SVignesh R 		if (fmt[1] == '4')
192*cdce1f76SVignesh R 			return ip4_addr_string(info, ptr);
193*cdce1f76SVignesh R #endif
194*cdce1f76SVignesh R 	default:
195*cdce1f76SVignesh R 		break;
196*cdce1f76SVignesh R 	}
197*cdce1f76SVignesh R #ifdef DEBUG
198*cdce1f76SVignesh R 	div = 1UL << (sizeof(long) * 8 - 4);
199*cdce1f76SVignesh R 	for (; div; div /= 0x10)
200*cdce1f76SVignesh R 		div_out(info, &num, div);
201*cdce1f76SVignesh R #endif
202*cdce1f76SVignesh R }
203*cdce1f76SVignesh R 
_vprintf(struct printf_info * info,const char * fmt,va_list va)204433cbfb3SMasahiro Yamada static int _vprintf(struct printf_info *info, const char *fmt, va_list va)
2057d9cde10SStefan Roese {
2067d9cde10SStefan Roese 	char ch;
2077d9cde10SStefan Roese 	char *p;
208a28e1d98SAndre Przywara 	unsigned long num;
209a5ecdd08SStefan Roese 	char buf[12];
210a28e1d98SAndre Przywara 	unsigned long div;
2117d9cde10SStefan Roese 
2127d9cde10SStefan Roese 	while ((ch = *(fmt++))) {
2137d9cde10SStefan Roese 		if (ch != '%') {
21445313e83SSimon Glass 			info->putc(info, ch);
2157d9cde10SStefan Roese 		} else {
2161fb67608SSimon Glass 			bool lz = false;
2171fb67608SSimon Glass 			int width = 0;
218a28e1d98SAndre Przywara 			bool islong = false;
2197d9cde10SStefan Roese 
2207d9cde10SStefan Roese 			ch = *(fmt++);
2211c853629SAndre Przywara 			if (ch == '-')
2221c853629SAndre Przywara 				ch = *(fmt++);
2231c853629SAndre Przywara 
2247d9cde10SStefan Roese 			if (ch == '0') {
2257d9cde10SStefan Roese 				ch = *(fmt++);
2267d9cde10SStefan Roese 				lz = 1;
2277d9cde10SStefan Roese 			}
2287d9cde10SStefan Roese 
2297d9cde10SStefan Roese 			if (ch >= '0' && ch <= '9') {
2301fb67608SSimon Glass 				width = 0;
2317d9cde10SStefan Roese 				while (ch >= '0' && ch <= '9') {
2321fb67608SSimon Glass 					width = (width * 10) + ch - '0';
2337d9cde10SStefan Roese 					ch = *fmt++;
2347d9cde10SStefan Roese 				}
2357d9cde10SStefan Roese 			}
236a28e1d98SAndre Przywara 			if (ch == 'l') {
237a28e1d98SAndre Przywara 				ch = *(fmt++);
238a28e1d98SAndre Przywara 				islong = true;
239a28e1d98SAndre Przywara 			}
240a28e1d98SAndre Przywara 
24145313e83SSimon Glass 			info->bf = buf;
24245313e83SSimon Glass 			p = info->bf;
24345313e83SSimon Glass 			info->zs = 0;
2447d9cde10SStefan Roese 
2457d9cde10SStefan Roese 			switch (ch) {
2461fb67608SSimon Glass 			case '\0':
2477d9cde10SStefan Roese 				goto abort;
2487d9cde10SStefan Roese 			case 'u':
2497d9cde10SStefan Roese 			case 'd':
250a28e1d98SAndre Przywara 				div = 1000000000;
251a28e1d98SAndre Przywara 				if (islong) {
252a28e1d98SAndre Przywara 					num = va_arg(va, unsigned long);
253a28e1d98SAndre Przywara 					if (sizeof(long) > 4)
254a28e1d98SAndre Przywara 						div *= div * 10;
255a28e1d98SAndre Przywara 				} else {
2567d9cde10SStefan Roese 					num = va_arg(va, unsigned int);
257a28e1d98SAndre Przywara 				}
258a28e1d98SAndre Przywara 
259a28e1d98SAndre Przywara 				if (ch == 'd') {
260a28e1d98SAndre Przywara 					if (islong && (long)num < 0) {
261a28e1d98SAndre Przywara 						num = -(long)num;
262a28e1d98SAndre Przywara 						out(info, '-');
263a28e1d98SAndre Przywara 					} else if (!islong && (int)num < 0) {
2647d9cde10SStefan Roese 						num = -(int)num;
26545313e83SSimon Glass 						out(info, '-');
2667d9cde10SStefan Roese 					}
267a28e1d98SAndre Przywara 				}
26874b1320aSSimon Glass 				if (!num) {
26945313e83SSimon Glass 					out_dgt(info, 0);
27074b1320aSSimon Glass 				} else {
271a28e1d98SAndre Przywara 					for (; div; div /= 10)
27245313e83SSimon Glass 						div_out(info, &num, div);
27374b1320aSSimon Glass 				}
2747d9cde10SStefan Roese 				break;
2757d9cde10SStefan Roese 			case 'x':
276a28e1d98SAndre Przywara 				if (islong) {
277a28e1d98SAndre Przywara 					num = va_arg(va, unsigned long);
278a28e1d98SAndre Przywara 					div = 1UL << (sizeof(long) * 8 - 4);
279a28e1d98SAndre Przywara 				} else {
2807d9cde10SStefan Roese 					num = va_arg(va, unsigned int);
281a28e1d98SAndre Przywara 					div = 0x10000000;
282a28e1d98SAndre Przywara 				}
28374b1320aSSimon Glass 				if (!num) {
28445313e83SSimon Glass 					out_dgt(info, 0);
28574b1320aSSimon Glass 				} else {
286a28e1d98SAndre Przywara 					for (; div; div /= 0x10)
28745313e83SSimon Glass 						div_out(info, &num, div);
28874b1320aSSimon Glass 				}
2897d9cde10SStefan Roese 				break;
2907d9cde10SStefan Roese 			case 'c':
29145313e83SSimon Glass 				out(info, (char)(va_arg(va, int)));
2927d9cde10SStefan Roese 				break;
2937d9cde10SStefan Roese 			case 's':
2947d9cde10SStefan Roese 				p = va_arg(va, char*);
2957d9cde10SStefan Roese 				break;
296*cdce1f76SVignesh R 			case 'p':
297*cdce1f76SVignesh R 				pointer(info, fmt, va_arg(va, void *));
298*cdce1f76SVignesh R 				while (isalnum(fmt[0]))
299*cdce1f76SVignesh R 					fmt++;
300*cdce1f76SVignesh R 				break;
3017d9cde10SStefan Roese 			case '%':
30245313e83SSimon Glass 				out(info, '%');
3037d9cde10SStefan Roese 			default:
3047d9cde10SStefan Roese 				break;
3057d9cde10SStefan Roese 			}
3067d9cde10SStefan Roese 
30745313e83SSimon Glass 			*info->bf = 0;
30845313e83SSimon Glass 			info->bf = p;
30945313e83SSimon Glass 			while (*info->bf++ && width > 0)
3101fb67608SSimon Glass 				width--;
3111fb67608SSimon Glass 			while (width-- > 0)
31245313e83SSimon Glass 				info->putc(info, lz ? '0' : ' ');
3138e31681cSSimon Glass 			if (p) {
3147d9cde10SStefan Roese 				while ((ch = *p++))
31545313e83SSimon Glass 					info->putc(info, ch);
3167d9cde10SStefan Roese 			}
3177d9cde10SStefan Roese 		}
3188e31681cSSimon Glass 	}
3197d9cde10SStefan Roese 
3207d9cde10SStefan Roese abort:
3217d9cde10SStefan Roese 	return 0;
3227d9cde10SStefan Roese }
323962a43ccSSjoerd Simons 
vprintf(const char * fmt,va_list va)324da70b4d1SHans de Goede int vprintf(const char *fmt, va_list va)
325da70b4d1SHans de Goede {
32645313e83SSimon Glass 	struct printf_info info;
32745313e83SSimon Glass 
32845313e83SSimon Glass 	info.putc = putc_normal;
32945313e83SSimon Glass 	return _vprintf(&info, fmt, va);
330da70b4d1SHans de Goede }
331da70b4d1SHans de Goede 
printf(const char * fmt,...)332962a43ccSSjoerd Simons int printf(const char *fmt, ...)
333962a43ccSSjoerd Simons {
33445313e83SSimon Glass 	struct printf_info info;
33545313e83SSimon Glass 
336962a43ccSSjoerd Simons 	va_list va;
337962a43ccSSjoerd Simons 	int ret;
338962a43ccSSjoerd Simons 
33945313e83SSimon Glass 	info.putc = putc_normal;
340962a43ccSSjoerd Simons 	va_start(va, fmt);
34145313e83SSimon Glass 	ret = _vprintf(&info, fmt, va);
342962a43ccSSjoerd Simons 	va_end(va);
343962a43ccSSjoerd Simons 
344962a43ccSSjoerd Simons 	return ret;
345962a43ccSSjoerd Simons }
3465c411d88SSimon Glass 
putc_outstr(struct printf_info * info,char ch)34745313e83SSimon Glass static void putc_outstr(struct printf_info *info, char ch)
3485c411d88SSimon Glass {
34945313e83SSimon Glass 	*info->outstr++ = ch;
3505c411d88SSimon Glass }
3515c411d88SSimon Glass 
sprintf(char * buf,const char * fmt,...)352abeb272dSMarek Vasut int sprintf(char *buf, const char *fmt, ...)
3535c411d88SSimon Glass {
35445313e83SSimon Glass 	struct printf_info info;
3555c411d88SSimon Glass 	va_list va;
3565c411d88SSimon Glass 	int ret;
3575c411d88SSimon Glass 
3585c411d88SSimon Glass 	va_start(va, fmt);
35945313e83SSimon Glass 	info.outstr = buf;
36045313e83SSimon Glass 	info.putc = putc_outstr;
36145313e83SSimon Glass 	ret = _vprintf(&info, fmt, va);
3625c411d88SSimon Glass 	va_end(va);
36345313e83SSimon Glass 	*info.outstr = '\0';
3645c411d88SSimon Glass 
3655c411d88SSimon Glass 	return ret;
3665c411d88SSimon Glass }
367abeb272dSMarek Vasut 
368abeb272dSMarek Vasut /* Note that size is ignored */
snprintf(char * buf,size_t size,const char * fmt,...)369abeb272dSMarek Vasut int snprintf(char *buf, size_t size, const char *fmt, ...)
370abeb272dSMarek Vasut {
37145313e83SSimon Glass 	struct printf_info info;
372abeb272dSMarek Vasut 	va_list va;
373abeb272dSMarek Vasut 	int ret;
374abeb272dSMarek Vasut 
375abeb272dSMarek Vasut 	va_start(va, fmt);
37645313e83SSimon Glass 	info.outstr = buf;
37745313e83SSimon Glass 	info.putc = putc_outstr;
37845313e83SSimon Glass 	ret = _vprintf(&info, fmt, va);
379abeb272dSMarek Vasut 	va_end(va);
38045313e83SSimon Glass 	*info.outstr = '\0';
381abeb272dSMarek Vasut 
382abeb272dSMarek Vasut 	return ret;
383abeb272dSMarek Vasut }
384e2409139SSimon Glass 
__assert_fail(const char * assertion,const char * file,unsigned line,const char * function)385e2409139SSimon Glass void __assert_fail(const char *assertion, const char *file, unsigned line,
386e2409139SSimon Glass 		   const char *function)
387e2409139SSimon Glass {
388e2409139SSimon Glass 	/* This will not return */
389e2409139SSimon Glass 	printf("%s:%u: %s: Assertion `%s' failed.", file, line, function,
390e2409139SSimon Glass 	       assertion);
391e2409139SSimon Glass 	hang();
392e2409139SSimon Glass }
393