| /* |
| * Copyright (C) 2018 Synaptics Incorporated. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND |
| * SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, |
| * INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY |
| * INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR |
| * CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE |
| * OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND |
| * BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF |
| * COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT |
| * DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY |
| * TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS. |
| */ |
| |
| #include <stdint.h> |
| #include <stdarg.h> |
| |
| #include "util.h" |
| #include "apb_uart_drv.h" |
| |
| #if 0 |
| #include "usb_common.h" |
| |
| static uint32_t output_string_buf_head; |
| void reset_usb_console(void) |
| { |
| output_string_buf_head = *(uint32_t*)USB_SOC_OUTPUT_WRITE_PTR; |
| if (output_string_buf_head == 0){ |
| output_string_buf_head = USB_SOC_OUTPUT_STRING_BUF; |
| } |
| } |
| |
| void usb_putc(int ch) |
| { |
| *(char *)((uintmax_t)output_string_buf_head) = (char) ch; |
| output_string_buf_head ++; |
| if (output_string_buf_head >= USB_SOC_OUTPUT_STRING_BUF_ROLL_POINT){ |
| //roll back to start of the buffer, for next entire string. |
| *(uint32_t *)USB_SOC_OUTPUT_EOS_PTR = output_string_buf_head; |
| output_string_buf_head = USB_SOC_OUTPUT_STRING_BUF; |
| } |
| *(uint32_t *)USB_SOC_OUTPUT_WRITE_PTR = output_string_buf_head; |
| } |
| #endif |
| |
| int putchar(int c){ |
| APB_UART_putc(0, c) ; |
| #if 0 |
| usb_putc(c); |
| #endif |
| if (c == '\n'){ |
| APB_UART_putc(0, '\r'); |
| #if 0 |
| usb_putc('\r'); |
| flush_dcache_range((void *)USB_SOC_OUTPUT_WRITE_PTR, |
| (void *)ALIGNED(USB_SOC_OUTPUT_STRING_BUF + USB_SOC_STRING_BUF_SIZE)); |
| #endif |
| } |
| //Both usb_putc and UART_putc return void hence return 0 |
| return 0; |
| } |
| |
| void printchar(char **str, unsigned long *remain, int c) |
| { |
| |
| if (str) { |
| if (remain) { |
| if (*remain == 0) |
| return; |
| |
| // Only one spot left, nul terminate. |
| if (*remain == 1) |
| c = '\0'; |
| --(*remain); |
| } |
| |
| **str = c; |
| ++(*str); |
| } |
| else (void)putchar(c); |
| } |
| |
| #define PAD_RIGHT 1 |
| #define PAD_ZERO 2 |
| |
| int prints(char **out, unsigned long *remain, const char *string, int width, int pad) |
| { |
| register int pc = 0, padchar = ' '; |
| |
| if (width > 0) { |
| register int len = 0; |
| register const char *ptr; |
| for (ptr = string; *ptr; ++ptr) ++len; |
| if (len >= width) width = 0; |
| else width -= len; |
| if (pad & PAD_ZERO) padchar = '0'; |
| } |
| if (!(pad & PAD_RIGHT)) { |
| for ( ; width > 0; --width) { |
| printchar (out, remain, padchar); |
| ++pc; |
| } |
| } |
| for ( ; *string ; ++string) { |
| printchar (out, remain, *string); |
| ++pc; |
| } |
| for ( ; width > 0; --width) { |
| printchar (out, remain, padchar); |
| ++pc; |
| } |
| |
| return pc; |
| } |
| |
| /* the following should be enough for 32 bit int */ |
| #define PRINT_BUF_LEN 12 |
| |
| int printi(char **out, unsigned long* remain, int i, int b, int sg, int width, int pad, int letbase) |
| { |
| char print_buf[PRINT_BUF_LEN]; |
| register char *s; |
| register int t, neg = 0, pc = 0; |
| register unsigned int u = i; |
| |
| if (i == 0) { |
| print_buf[0] = '0'; |
| print_buf[1] = '\0'; |
| return prints (out, remain, print_buf, width, pad); |
| } |
| |
| if (sg && b == 10 && i < 0) { |
| neg = 1; |
| u = -i; |
| } |
| |
| s = print_buf + PRINT_BUF_LEN-1; |
| *s = '\0'; |
| |
| while (u) { |
| t = u % b; |
| if( t >= 10 ) |
| t += letbase - '0' - 10; |
| *--s = t + '0'; |
| u /= b; |
| } |
| |
| if (neg) { |
| if( width && (pad & PAD_ZERO) ) { |
| printchar (out, remain, '-'); |
| ++pc; |
| --width; |
| } |
| else { |
| *--s = '-'; |
| } |
| } |
| |
| return pc + prints (out, remain, s, width, pad); |
| } |
| |
| static int print(char **out, unsigned long *remain, const char *format, va_list ap) |
| { |
| register int width, pad; |
| register int pc = 0; |
| char scr[2]; |
| |
| for (; *format != 0; ++format) { |
| if (*format == '%') { |
| ++format; |
| width = pad = 0; |
| if (*format == '\0') break; |
| if (*format == '%') goto out; |
| if (*format == '-') { |
| ++format; |
| pad = PAD_RIGHT; |
| } |
| while (*format == '0') { |
| ++format; |
| pad |= PAD_ZERO; |
| } |
| for ( ; *format >= '0' && *format <= '9'; ++format) { |
| width *= 10; |
| width += *format - '0'; |
| } |
| if( *format == 's' ) { |
| register char *s = va_arg(ap, char *); |
| pc += prints (out, remain, s?s:"(null)", width, pad); |
| continue; |
| } |
| if( *format == 'd' ) { |
| pc += printi (out, remain, va_arg(ap, int), 10, 1, width, pad, 'a'); |
| continue; |
| } |
| if( *format == 'x' ) { |
| pc += printi (out, remain, va_arg(ap, int), 16, 0, width, pad, 'a'); |
| continue; |
| } |
| if( *format == 'X' ) { |
| pc += printi (out, remain, va_arg(ap, int), 16, 0, width, pad, 'A'); |
| continue; |
| } |
| if( *format == 'u' ) { |
| pc += printi (out, remain, va_arg(ap, int), 10, 0, width, pad, 'a'); |
| continue; |
| } |
| if( *format == 'c' ) { |
| /* char are converted to int then pushed on the stack */ |
| scr[0] = va_arg(ap, int); |
| scr[1] = '\0'; |
| pc += prints (out, remain, scr, width, pad); |
| continue; |
| } |
| } |
| else { |
| out: |
| printchar (out, remain, *format); |
| ++pc; |
| } |
| } |
| if (out) **out = '\0'; |
| return pc; |
| } |
| |
| #if 1 |
| int lgpl_printf(const char *format, ...) |
| { |
| int rv; |
| va_list ap; |
| |
| va_start(ap, format); |
| rv = print(0, NULL, format, ap); |
| va_end(ap); |
| |
| return rv; |
| } |
| #endif |
| |
| int printf(const char *format, ...) |
| { |
| int rv; |
| va_list ap; |
| |
| va_start(ap, format); |
| rv = print(0, NULL, format, ap); |
| va_end(ap); |
| |
| return rv; |
| } |
| |
| #if 1 |
| int lgpl_sprintf(char *out, const char *format, ...) |
| { |
| int rv; |
| va_list ap; |
| |
| va_start(ap, format); |
| rv = print(&out, NULL, format, ap); |
| va_end(ap); |
| |
| return rv; |
| } |
| #endif |
| |
| int sprintf(char *out, const char *format, ...) |
| { |
| int rv; |
| va_list ap; |
| |
| va_start(ap, format); |
| rv = print(&out, NULL, format, ap); |
| va_end(ap); |
| |
| return rv; |
| } |
| |
| int snprintf(char *out, unsigned long size, const char *format, ...) |
| { |
| int rv; |
| va_list ap; |
| |
| va_start(ap, format); |
| rv = print(&out, &size, format, ap); |
| va_end(ap); |
| |
| return rv; |
| } |
| |
| /* |
| * if you compile this file with |
| * gcc -Wall $(YOUR_C_OPTIONS) -DTEST_PRINTF -c printf.c |
| * you will get a normal warning: |
| * printf.c:214: warning: spurious trailing `%' in format |
| * this line is testing an invalid % at the end of the format string. |
| * |
| * this should display (on 32bit int machine) : |
| * |
| * Hello world! |
| * printf test |
| * (null) is null pointer |
| * 5 = 5 |
| * -2147483647 = - max int |
| * char a = 'a' |
| * hex ff = ff |
| * hex 00 = 00 |
| * signed -3 = unsigned 4294967293 = hex fffffffd |
| * 0 message(s) |
| * 0 message(s) with % |
| * justif: "left " |
| * justif: " right" |
| * 3: 0003 zero padded |
| * 3: 3 left justif. |
| * 3: 3 right justif. |
| * -3: -003 zero padded |
| * -3: -3 left justif. |
| * -3: -3 right justif. |
| */ |
| |