351 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright (c) 2011 Aeroflex Gaisler
 | |
|  *
 | |
|  * BSD license:
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
|  * THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include <stdarg.h>
 | |
| #include <asm-leon/leoncompat.h>
 | |
| #include <asm-leon/leon.h>
 | |
| 
 | |
| static size_t
 | |
| lo_strnlen (const char *s, size_t count)
 | |
| {
 | |
|   const char *sc;
 | |
| 
 | |
|   for (sc = s; count-- && *sc != '\0'; ++sc)
 | |
|     /* nothing */ ;
 | |
|   return sc - s;
 | |
| }
 | |
| 
 | |
| static int
 | |
| lo_vsnprintf (char *buf, size_t size, const char *fmt, va_list args)
 | |
| {
 | |
|   int len;
 | |
|   unsigned long long num;
 | |
|   int i, j, n;
 | |
|   char *str, *end, c;
 | |
|   const char *s;
 | |
|   int flags;
 | |
|   int field_width;
 | |
|   int precision;
 | |
|   int qualifier;
 | |
|   int filler;
 | |
| 
 | |
|   str = buf;
 | |
|   end = buf + size - 1;
 | |
| 
 | |
|   if (end < buf - 1)
 | |
|     {
 | |
|       end = ((void *) -1);
 | |
|       size = end - buf + 1;
 | |
|     }
 | |
| 
 | |
|   for (; *fmt; ++fmt)
 | |
|     {
 | |
|       if (*fmt != '%')
 | |
| 	{
 | |
| 	  if (*fmt == '\n')
 | |
| 	    {
 | |
| 	      if (str <= end)
 | |
| 		{
 | |
| 		  *str = '\r';
 | |
| 		}
 | |
| 	      str++;
 | |
| 	    }
 | |
| 	  if (str <= end)
 | |
| 	    *str = *fmt;
 | |
| 	  ++str;
 | |
| 	  continue;
 | |
| 	}
 | |
| 
 | |
|       /* process flags */
 | |
|       flags = 0;
 | |
|       /* get field width */
 | |
|       field_width = 0;
 | |
|       /* get the precision */
 | |
|       precision = -1;
 | |
|       /* get the conversion qualifier */
 | |
|       qualifier = 'l';
 | |
|       filler = ' ';
 | |
| 
 | |
|       ++fmt;
 | |
| 
 | |
|       if (*fmt == '0')
 | |
| 	{
 | |
| 	  filler = '0';
 | |
| 	  ++fmt;
 | |
| 	}
 | |
| 
 | |
|       while (isdigit (*fmt))
 | |
| 	{
 | |
| 	  field_width = field_width * 10 + ((*fmt) - '0');
 | |
| 	  ++fmt;
 | |
| 	}
 | |
| 
 | |
|       /* default base */
 | |
|       switch (*fmt)
 | |
| 	{
 | |
| 	case 'c':
 | |
| 	  c = (unsigned char) va_arg (args, int);
 | |
| 	  if (str <= end)
 | |
| 	    *str = c;
 | |
| 	  ++str;
 | |
| 	  while (--field_width > 0)
 | |
| 	    {
 | |
| 	      if (str <= end)
 | |
| 		*str = ' ';
 | |
| 	      ++str;
 | |
| 	    }
 | |
| 	  continue;
 | |
| 
 | |
| 	case 's':
 | |
| 	  s = va_arg (args, char *);
 | |
| 	  if (!s)
 | |
| 	    s = "<NULL>";
 | |
| 
 | |
| 	  len = lo_strnlen (s, precision);
 | |
| 
 | |
| 	  for (i = 0; i < len; ++i)
 | |
| 	    {
 | |
| 	      if (str <= end)
 | |
| 		*str = *s;
 | |
| 	      ++str;
 | |
| 	      ++s;
 | |
| 	    }
 | |
| 	  while (len < field_width--)
 | |
| 	    {
 | |
| 	      if (str <= end)
 | |
| 		*str = ' ';
 | |
| 	      ++str;
 | |
| 	    }
 | |
| 	  continue;
 | |
| 
 | |
| 
 | |
| 	case '%':
 | |
| 	  if (str <= end)
 | |
| 	    *str = '%';
 | |
| 	  ++str;
 | |
| 	  continue;
 | |
| 
 | |
| 	case 'x':
 | |
| 	  break;
 | |
| 	case 'd':
 | |
| 	  break;
 | |
| 
 | |
| 	default:
 | |
| 	  if (str <= end)
 | |
| 	    *str = '%';
 | |
| 	  ++str;
 | |
| 	  if (*fmt)
 | |
| 	    {
 | |
| 	      if (str <= end)
 | |
| 		*str = *fmt;
 | |
| 	      ++str;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      --fmt;
 | |
| 	    }
 | |
| 	  continue;
 | |
| 	}
 | |
|       num = va_arg (args, unsigned long);
 | |
|       if (*fmt == 'd')
 | |
| 	{
 | |
| 	  j = 0;
 | |
| 	  while (num && str <= end)
 | |
| 	    {
 | |
| 	      *str = (num % 10) + '0';
 | |
| 	      num = num / 10;
 | |
| 	      ++str;
 | |
| 	      j++;
 | |
| 	    }
 | |
| 	  /* flip */
 | |
| 	  for (i = 0; i < (j / 2); i++)
 | |
| 	    {
 | |
| 	      n = str[(-j) + i];
 | |
| 	      str[(-j) + i] = str[-(i + 1)];
 | |
| 	      str[-(i + 1)] = n;
 | |
| 	    }
 | |
| 	  /* shift */
 | |
| 	  if (field_width > j)
 | |
| 	    {
 | |
| 	      i = field_width - j;
 | |
| 	      for (n = 1; n <= j; n++)
 | |
| 		{
 | |
| 		  if (str + i - n <= end)
 | |
| 		    {
 | |
| 		      str[i - n] = str[-n];
 | |
| 		    }
 | |
| 		}
 | |
| 	      for (i--; i >= 0; i--)
 | |
| 		{
 | |
| 		  str[i - j] = filler;
 | |
| 		}
 | |
| 	      str += field_width - j;
 | |
| 	      j = 1;
 | |
| 	    }
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  for (j = 0, i = 0; i < 8 && str <= end; i++)
 | |
| 	    {
 | |
| 	      if ((n =
 | |
| 		   ((unsigned long) (num & (0xf0000000ul >> (i * 4)))) >>
 | |
| 		   ((7 - i) * 4)) || j != 0)
 | |
| 		{
 | |
| 		  if (n >= 10)
 | |
| 		    n += 'a' - 10;
 | |
| 		  else
 | |
| 		    n += '0';
 | |
| 		  *str = n;
 | |
| 		  ++str;
 | |
| 		  j++;
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	  /* shift */
 | |
| 	  if (field_width > j)
 | |
| 	    {
 | |
| 	      i = field_width - j;
 | |
| 	      for (n = 1; n <= j; n++)
 | |
| 		{
 | |
| 		  if (str + i - n <= end)
 | |
| 		    {
 | |
| 		      str[i - n] = str[-n];
 | |
| 		    }
 | |
| 		}
 | |
| 	      for (i--; i >= 0; i--)
 | |
| 		{
 | |
| 		  str[i - j] = filler;
 | |
| 		}
 | |
| 	      str += field_width - j;
 | |
| 	      j = 1;
 | |
| 	    }
 | |
| 
 | |
| 
 | |
| 	}
 | |
| 
 | |
|       if (j == 0 && str <= end)
 | |
| 	{
 | |
| 	  *str = '0';
 | |
| 	  ++str;
 | |
| 	}
 | |
|     }
 | |
|   if (str <= end)
 | |
|     *str = '\0';
 | |
|   else if (size > 0)
 | |
|     /* don't write out a null byte if the buf size is zero */
 | |
|     *end = '\0';
 | |
|   /* the trailing null byte doesn't count towards the total
 | |
|    * ++str;
 | |
|    */
 | |
|   return str - buf;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * lo_vsprintf - Format a string and place it in a buffer
 | |
|  * @buf: The buffer to place the result into
 | |
|  * @fmt: The format string to use
 | |
|  * @args: Arguments for the format string
 | |
|  *
 | |
|  * Call this function if you are already dealing with a va_list.
 | |
|  * You probably want lo_sprintf instead.
 | |
|  */
 | |
| static int
 | |
| lo_vsprintf (char *buf, const char *fmt, va_list args)
 | |
| {
 | |
|   return lo_vsnprintf (buf, 0xFFFFFFFFUL, fmt, args);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| dbgleon_sprintf (char *buf, size_t size, const char *fmt, ...)
 | |
| {
 | |
|   va_list args;
 | |
|   int printed_len;
 | |
| 
 | |
|   va_start (args, fmt);
 | |
|   printed_len = lo_vsnprintf (buf, size, fmt, args);
 | |
|   va_end (args);
 | |
|   return printed_len;
 | |
| }
 | |
| 
 | |
| #define UART_TIMEOUT 100000
 | |
| static LEON23_APBUART_Regs_Map *uart_regs = 0;
 | |
| int
 | |
| dbgleon_printf (const char *fmt, ...)
 | |
| {
 | |
|   unsigned int i, loops, ch;
 | |
|   amba_apb_device apbdevs[1];
 | |
|   va_list args;
 | |
|   int printed_len;
 | |
|   char printk_buf[1024];
 | |
|   char *p = printk_buf;
 | |
| 
 | |
|   /* Emit the output into the temporary buffer */
 | |
|   va_start (args, fmt);
 | |
|   printed_len = lo_vsnprintf (printk_buf, sizeof (printk_buf), fmt, args);
 | |
|   va_end (args);
 | |
| 
 | |
|   //---------------------
 | |
|   switch (LEONCOMPAT_VERSION)
 | |
|     {
 | |
|     case 3:
 | |
|     default:
 | |
|       {
 | |
| 	if (!uart_regs)
 | |
| 	  {
 | |
| 	    if (i =
 | |
| 		leon3_getapbbase (VENDOR_GAISLER, GAISLER_APBUART, apbdevs,
 | |
| 				  1))
 | |
| 	      {
 | |
| 		uart_regs = (LEON23_APBUART_Regs_Map *) apbdevs[0].start;
 | |
| 	      }
 | |
| 	  }
 | |
| 	if (uart_regs)
 | |
| 	  {
 | |
| 	    while (printed_len-- != 0)
 | |
| 	      {
 | |
| 		ch = *p++;
 | |
| 		if (uart_regs)
 | |
| 		  {
 | |
| 		    loops = 0;
 | |
| 		    while (!(uart_regs->status & LEON_REG_UART_STATUS_THE)
 | |
| 			   && (loops < UART_TIMEOUT))
 | |
| 		      loops++;
 | |
| 		    uart_regs->data = ch;
 | |
| 		    loops = 0;
 | |
| 		    while (!(uart_regs->status & LEON_REG_UART_STATUS_TSE)
 | |
| 			   && (loops < UART_TIMEOUT))
 | |
| 		      loops++;
 | |
| 		  }
 | |
| 	      }
 | |
| 	  }
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|   //---------------------
 | |
| }
 |