Commit cdce1f762029619e80061833b153cdbf3d0a0860
Committed by
Tom Rini
1 parent
b997a73ee6
Exists in
smarc_8mq_lf_v2020.04
and in
17 other branches
tiny-printf: Add support for %p format
Add support for %p, %pa[p], %pM, %pm and %pI4 formats to tiny-printf. %pM and %pI4 are widely used by SPL networking stack and is required if networking support is desired in SPL. %p, %pa and %pap are mostly used by debug prints and hence supported only when DEBUG is enabled. Before this patch: $ size spl/u-boot-spl text data bss dec hex filename 99325 4899 218584 322808 4ecf8 spl/u-boot-spl After this patch (with CONFIG_SPL_NET_SUPPORT): $ size spl/u-boot-spl text data bss dec hex filename 99666 4899 218584 323149 4ee4d spl/u-boot-spl So, this patch adds ~350 bytes to code size. If CONFIG_SPL_NET_SUPPORT is not enabled, this adds ~25 bytes. If CONFIG_USE_TINY_PRINTF is disabled then: $ size spl/u-boot-spl text data bss dec hex filename 101116 4899 218584 324599 4f3f7 spl/u-boot-spl So, there is still ~1.4K space saved even with support for %pM/%pI4. Compiler used is to build is: arm-linux-gnueabihf-gcc (Linaro GCC 6.2-2016.11) 6.2.1 20161016 Signed-off-by: Vignesh R <vigneshr@ti.com> Reviewed-by: Tom Rini <trini@konsulko.com> Reviewed-by: Simon Glass <sjg@chromium.org>
Showing 1 changed file with 154 additions and 0 deletions Side-by-side Diff
lib/tiny-printf.c
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | #include <common.h> |
13 | 13 | #include <stdarg.h> |
14 | 14 | #include <serial.h> |
15 | +#include <linux/ctype.h> | |
15 | 16 | |
16 | 17 | struct printf_info { |
17 | 18 | char *bf; /* Digit buffer */ |
... | ... | @@ -52,6 +53,154 @@ |
52 | 53 | out_dgt(info, dgt); |
53 | 54 | } |
54 | 55 | |
56 | +#ifdef CONFIG_SPL_NET_SUPPORT | |
57 | +static void string(struct printf_info *info, char *s) | |
58 | +{ | |
59 | + char ch; | |
60 | + | |
61 | + while ((ch = *s++)) | |
62 | + out(info, ch); | |
63 | +} | |
64 | + | |
65 | +static const char hex_asc[] = "0123456789abcdef"; | |
66 | +#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] | |
67 | +#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] | |
68 | + | |
69 | +static inline char *pack_hex_byte(char *buf, u8 byte) | |
70 | +{ | |
71 | + *buf++ = hex_asc_hi(byte); | |
72 | + *buf++ = hex_asc_lo(byte); | |
73 | + return buf; | |
74 | +} | |
75 | + | |
76 | +static void mac_address_string(struct printf_info *info, u8 *addr, | |
77 | + bool separator) | |
78 | +{ | |
79 | + /* (6 * 2 hex digits), 5 colons and trailing zero */ | |
80 | + char mac_addr[6 * 3]; | |
81 | + char *p = mac_addr; | |
82 | + int i; | |
83 | + | |
84 | + for (i = 0; i < 6; i++) { | |
85 | + p = pack_hex_byte(p, addr[i]); | |
86 | + if (separator && i != 5) | |
87 | + *p++ = ':'; | |
88 | + } | |
89 | + *p = '\0'; | |
90 | + | |
91 | + string(info, mac_addr); | |
92 | +} | |
93 | + | |
94 | +static char *put_dec_trunc(char *buf, unsigned int q) | |
95 | +{ | |
96 | + unsigned int d3, d2, d1, d0; | |
97 | + d1 = (q >> 4) & 0xf; | |
98 | + d2 = (q >> 8) & 0xf; | |
99 | + d3 = (q >> 12); | |
100 | + | |
101 | + d0 = 6 * (d3 + d2 + d1) + (q & 0xf); | |
102 | + q = (d0 * 0xcd) >> 11; | |
103 | + d0 = d0 - 10 * q; | |
104 | + *buf++ = d0 + '0'; /* least significant digit */ | |
105 | + d1 = q + 9 * d3 + 5 * d2 + d1; | |
106 | + if (d1 != 0) { | |
107 | + q = (d1 * 0xcd) >> 11; | |
108 | + d1 = d1 - 10 * q; | |
109 | + *buf++ = d1 + '0'; /* next digit */ | |
110 | + | |
111 | + d2 = q + 2 * d2; | |
112 | + if ((d2 != 0) || (d3 != 0)) { | |
113 | + q = (d2 * 0xd) >> 7; | |
114 | + d2 = d2 - 10 * q; | |
115 | + *buf++ = d2 + '0'; /* next digit */ | |
116 | + | |
117 | + d3 = q + 4 * d3; | |
118 | + if (d3 != 0) { | |
119 | + q = (d3 * 0xcd) >> 11; | |
120 | + d3 = d3 - 10 * q; | |
121 | + *buf++ = d3 + '0'; /* next digit */ | |
122 | + if (q != 0) | |
123 | + *buf++ = q + '0'; /* most sign. digit */ | |
124 | + } | |
125 | + } | |
126 | + } | |
127 | + return buf; | |
128 | +} | |
129 | + | |
130 | +static void ip4_addr_string(struct printf_info *info, u8 *addr) | |
131 | +{ | |
132 | + /* (4 * 3 decimal digits), 3 dots and trailing zero */ | |
133 | + char ip4_addr[4 * 4]; | |
134 | + char temp[3]; /* hold each IP quad in reverse order */ | |
135 | + char *p = ip4_addr; | |
136 | + int i, digits; | |
137 | + | |
138 | + for (i = 0; i < 4; i++) { | |
139 | + digits = put_dec_trunc(temp, addr[i]) - temp; | |
140 | + /* reverse the digits in the quad */ | |
141 | + while (digits--) | |
142 | + *p++ = temp[digits]; | |
143 | + if (i != 3) | |
144 | + *p++ = '.'; | |
145 | + } | |
146 | + *p = '\0'; | |
147 | + | |
148 | + string(info, ip4_addr); | |
149 | +} | |
150 | +#endif | |
151 | + | |
152 | +/* | |
153 | + * Show a '%p' thing. A kernel extension is that the '%p' is followed | |
154 | + * by an extra set of characters that are extended format | |
155 | + * specifiers. | |
156 | + * | |
157 | + * Right now we handle: | |
158 | + * | |
159 | + * - 'M' For a 6-byte MAC address, it prints the address in the | |
160 | + * usual colon-separated hex notation. | |
161 | + * - 'm' Same as above except there is no colon-separator. | |
162 | + * - 'I4'for IPv4 addresses printed in the usual way (dot-separated | |
163 | + * decimal). | |
164 | + */ | |
165 | + | |
166 | +static void pointer(struct printf_info *info, const char *fmt, void *ptr) | |
167 | +{ | |
168 | +#ifdef DEBUG | |
169 | + unsigned long num = (uintptr_t)ptr; | |
170 | + unsigned long div; | |
171 | +#endif | |
172 | + | |
173 | + switch (*fmt) { | |
174 | +#ifdef DEBUG | |
175 | + case 'a': | |
176 | + | |
177 | + switch (fmt[1]) { | |
178 | + case 'p': | |
179 | + default: | |
180 | + num = *(phys_addr_t *)ptr; | |
181 | + break; | |
182 | + } | |
183 | + break; | |
184 | +#endif | |
185 | +#ifdef CONFIG_SPL_NET_SUPPORT | |
186 | + case 'm': | |
187 | + return mac_address_string(info, ptr, false); | |
188 | + case 'M': | |
189 | + return mac_address_string(info, ptr, true); | |
190 | + case 'I': | |
191 | + if (fmt[1] == '4') | |
192 | + return ip4_addr_string(info, ptr); | |
193 | +#endif | |
194 | + default: | |
195 | + break; | |
196 | + } | |
197 | +#ifdef DEBUG | |
198 | + div = 1UL << (sizeof(long) * 8 - 4); | |
199 | + for (; div; div /= 0x10) | |
200 | + div_out(info, &num, div); | |
201 | +#endif | |
202 | +} | |
203 | + | |
55 | 204 | static int _vprintf(struct printf_info *info, const char *fmt, va_list va) |
56 | 205 | { |
57 | 206 | char ch; |
... | ... | @@ -143,6 +292,11 @@ |
143 | 292 | break; |
144 | 293 | case 's': |
145 | 294 | p = va_arg(va, char*); |
295 | + break; | |
296 | + case 'p': | |
297 | + pointer(info, fmt, va_arg(va, void *)); | |
298 | + while (isalnum(fmt[0])) | |
299 | + fmt++; | |
146 | 300 | break; |
147 | 301 | case '%': |
148 | 302 | out(info, '%'); |