Commit cdce1f762029619e80061833b153cdbf3d0a0860

Authored by Vignesh R
Committed by Tom Rini
1 parent b997a73ee6

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

... ... @@ -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, '%');