DISABLE_IRQSAVE/ENABLE_IRQRESTORE: Convert to IRQ_SAVE_DISABLE/IRQ_RESTORE.
[bertos.git] / drv / kdebug.c
index 091f0b7f3fcee7cbd95026e57e711501d709eef6..03a86430787ad181e27902deecde4337b79800a0 100755 (executable)
@@ -1,21 +1,39 @@
 /*!
  * \file
  * <!--
- * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
- * Copyright 2000,2001,2002 Bernardo Innocenti <bernie@codewiz.org>
+ * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000, 2001, 2002 Bernardo Innocenti <bernie@codewiz.org>
  * This file is part of DevLib - See devlib/README for information.
  * -->
  *
- * \brief General pourpose debug functions.
+ * \brief General pourpose debug support for embedded systems (implementation).
  *
  * \version $Id$
- *
  * \author Bernardo Innocenti <bernie@develer.com>
  * \author Stefano Fedrigo <aleph@develer.com>
  */
 
 /*#*
  *#* $Log$
+ *#* Revision 1.18  2004/12/08 08:52:00  bernie
+ *#* Save some more RAM on AVR.
+ *#*
+ *#* Revision 1.17  2004/10/03 18:41:28  bernie
+ *#* Restore \version header killed by mistake in previous commit.
+ *#*
+ *#* Revision 1.16  2004/10/03 18:40:50  bernie
+ *#* Use new CPU macros.
+ *#*
+ *#* Revision 1.15  2004/09/14 21:03:46  bernie
+ *#* Use debug.h instead of kdebug.h.
+ *#*
+ *#* Revision 1.14  2004/09/06 21:39:56  bernie
+ *#* Allow partial redefinition of BUS macros.
+ *#*
+ *#* Revision 1.13  2004/08/29 22:04:26  bernie
+ *#* Convert 485 macros to generic BUS macros;
+ *#* kputchar(): New public function.
+ *#*
  *#* Revision 1.12  2004/08/25 14:12:08  rasky
  *#* Aggiornato il comment block dei log RCS
  *#*
@@ -54,7 +72,8 @@
  *#*
  *#*/
 
-#include "kdebug.h"
+#include <debug.h>
+#include <cpu.h>
 #include "hw.h"
 #include "config.h"
 
@@ -67,8 +86,8 @@
        #define KDBG_WAIT_READY()      do { /*nop*/ } while(0)
        #define KDBG_WRITE_CHAR(c)     putchar((c))
        #define KDBG_MASK_IRQ(old)     do { (void)(old); } while(0)
-       #define KDBG_RESTORE_IRQ()     do { /*nop*/ } while(0)
-       typedef kdbg_irqsave_t         char; /* unused */
+       #define KDBG_RESTORE_IRQ(old)  do { /*nop*/ } while(0)
+       typedef char kdbg_irqsave_t; /* unused */
 #elif CPU_I196
        #include "Util196.h"
        #define KDBG_WAIT_READY()      do {} while (!(SP_STAT & (SPSF_TX_EMPTY | SPSF_TX_INT)))
                        INT_MASK1 &= ~INT1F_TI; \
                } while(0)
        #define KDBG_RESTORE_IRQ(old)  do { INT_MASK1 |= (old); }
-       typedef kdbg_irqsave_t         uint16_t; /* FIXME: unconfirmed */
+       typedef uint16_t kdbg_irqsave_t; /* FIXME: unconfirmed */
 #elif CPU_AVR
        #include <avr/io.h>
+
        #if CONFIG_KDEBUG_PORT == 0
 
-               /* External 485 transceiver on UART0 (to be overridden in "hw.h").  */
-               #if !defined(SER_UART0_485_INIT)
-                       #if defined(SER_UART0_485_RX) || defined(SER_UART0_485_TX)
-                               #error SER_UART0_485_INIT, SER_UART0_485_RX and SER_UART0_485_TX must be defined together
-                       #endif
-                       #define SER_UART0_485_INIT  do {} while (0)
-                       #define SER_UART0_485_TX    do {} while (0)
-                       #define SER_UART0_485_RX    do {} while (0)
-               #elif !defined(SER_UART0_485_RX) || !defined(SER_UART0_485_TX)
-                       #error SER_UART0_485_INIT, SER_UART0_485_RX and SER_UART0_485_TX must be defined together
+               /*
+                * Support for special bus policies or external transceivers
+                * on UART0 (to be overridden in "hw.h").
+                */
+               #ifndef KDBG_UART0_BUS_INIT
+               #define KDBG_UART0_BUS_INIT  do {} while (0)
+               #endif
+               #ifndef KDBG_UART0_BUS_RX
+               #define KDBG_UART0_BUS_RX    do {} while (0)
+               #endif
+               #ifndef KDBG_UART0_BUS_TX
+               #define KDBG_UART0_BUS_TX    do {} while (0)
                #endif
 
-               #if defined(__AVR_ATmega64__)
+               #if CPU_AVR_ATMEGA64
                        #define UCR UCSR0B
                        #define UDR UDR0
                        #define USR UCSR0A
-               #elif defined(__AVR_ATmega8__)
+               #elif CPU_AVR_ATMEGA8
                        #define UCR UCSRB
                        #define USR UCSRA
+               #else
+                       #error Unknown CPU
                #endif
 
                #define KDBG_WAIT_READY()     do { loop_until_bit_is_set(USR, UDRE); } while(0)
                #define KDBG_WAIT_TXDONE()    do { loop_until_bit_is_set(USR, TXC); } while(0)
+
                /*
-                * BUG: before sending a new character the TXC flag is cleared to allow
-                * KDBG_WAIT_TXDONE() to work properly, but, if KDBG_WRITE_CHAR() is called
-                * after the RXC flag is set by hardware, a new TXC could be generated
-                * after we clear it and before the new character is put in UDR. In this
-                * case if a 485 is used the transceiver will be put in RX mode while
-                * transmitting the last char.
+                * We must clear the TXC flag before sending a new character to allow
+                * KDBG_WAIT_TXDONE() to work properly.
+                *
+                * BUG: if KDBG_WRITE_CHAR() is called after the TXC flag is set by hardware,
+                * a new TXC could be generated after we've cleared it and before the new
+                * character is written to UDR.  On a 485 bus, the transceiver will be put
+                * in RX mode while still transmitting the last char.
                 */
                #define KDBG_WRITE_CHAR(c)    do { USR |= BV(TXC); UDR = (c); } while(0)
 
                #define KDBG_MASK_IRQ(old)    do { \
-                       SER_UART0_485_TX; \
                        (old) = UCR; \
                        UCR |= BV(TXEN); \
                        UCR &= ~(BV(TXCIE) | BV(UDRIE)); \
+                       KDBG_UART0_BUS_TX; \
                } while(0)
 
                #define KDBG_RESTORE_IRQ(old) do { \
                        KDBG_WAIT_TXDONE(); \
-                       SER_UART0_485_RX; \
+                       KDBG_UART0_BUS_RX; \
                        UCR = (old); \
                } while(0)
 
-               typedef kdbg_irqsave_t uint8_t;
+               typedef uint8_t kdbg_irqsave_t;
 
        #elif CONFIG_KDEBUG_PORT == 1
 
-               /* External 485 transceiver on UART1 (to be overridden in "hw.h").  */
-               #ifndef SER_UART1_485_INIT
-                       #if defined(SER_UART1_485_RX) || defined(SER_UART1_485_TX)
-                               #error SER_UART1_485_INIT, SER_UART1_485_RX and SER_UART1_485_TX must be defined together
-                       #endif
-                       #define SER_UART1_485_INIT  do {} while (0)
-                       #define SER_UART1_485_TX    do {} while (0)
-                       #define SER_UART1_485_RX    do {} while (0)
-               #elif !defined(SER_UART1_485_RX) || !defined(SER_UART1_485_TX)
-                       #error SER_UART1_485_INIT, SER_UART1_485_RX and SER_UART1_485_TX must be defined together
+               /*
+                * Support for special bus policies or external transceivers
+                * on UART1 (to be overridden in "hw.h").
+                */
+               #ifndef KDBG_UART1_BUS_INIT
+               #define KDBG_UART1_BUS_INIT  do {} while (0)
+               #endif
+               #ifndef KDBG_UART1_BUS_RX
+               #define KDBG_UART1_BUS_RX    do {} while (0)
+               #endif
+               #ifndef KDBG_UART1_BUS_TX
+               #define KDBG_UART1_BUS_TX    do {} while (0)
                #endif
 
                #define KDBG_WAIT_READY()     do { loop_until_bit_is_set(UCSR1A, UDRE); } while(0)
                #define KDBG_WRITE_CHAR(c)    do { UCSR1A |= BV(TXC); UDR1 = (c); } while(0)
 
                #define KDBG_MASK_IRQ(old)    do { \
-                       SER_UART1_485_TX; \
                        (old) = UCSR1B; \
                        UCSR1B |= BV(TXEN); \
                        UCSR1B &= ~(BV(TXCIE) | BV(UDRIE)); \
+                       KDBG_UART1_BUS_TX; \
                } while(0)
 
                #define KDBG_RESTORE_IRQ(old) do { \
                        KDBG_WAIT_TXDONE(); \
-                       SER_UART1_485_RX; \
+                       KDBG_UART1_BUS_RX; \
                        UCSR1B = (old); \
                } while(0)
 
-               typedef kdbg_irqsave_t uint8_t;
+               typedef uint8_t kdbg_irqsave_t;
        #else
                #error CONFIG_KDEBUG_PORT should be either 0 or 1
        #endif
        #define KDBG_WRITE_CHAR(c)        __put_char(c, stdout)
        #define KDBG_MASK_IRQ(old)        do { (void)(old); } while (0)
        #define KDBG_RESTORE_IRQ(old)     do { (void)(old); } while (0)
-       typedef kdbg_irqsave_t            uint8_t; /* unused */
+       typedef uint8_t kdbg_irqsave_t; /* unused */
 #else
        #error Unknown architecture
 #endif
@@ -198,26 +226,26 @@ void kdbg_init(void)
        /* Compute the baud rate */
        uint16_t period = (((CLOCK_FREQ / 16UL) + (CONFIG_KDEBUG_BAUDRATE / 2)) / CONFIG_KDEBUG_BAUDRATE) - 1;
 
-       #if defined(__AVR_ATmega64__)
+       #if CPU_AVR_ATMEGA64
                #if CONFIG_KDEBUG_PORT == 0
                        UBRR0H = (uint8_t)(period>>8);
                        UBRR0L = (uint8_t)period;
-                       SER_UART0_485_INIT;
+                       KDBG_UART0_BUS_INIT;
                #elif CONFIG_KDEBUG_PORT == 1
                        UBRR1H = (uint8_t)(period>>8);
                        UBRR1L = (uint8_t)period;
-                       SER_UART1_485_INIT;
+                       KDBG_UART1_BUS_INIT;
                #else
                        #error CONFIG_KDEBUG_PORT must be either 0 or 1
                #endif
-       #elif defined(__AVR_ATmega8__)
+       #elif CPU_AVR_ATMEGA8
                UBRRH = (uint8_t)(period>>8);
                UBRRL = (uint8_t)period;
-       #elif defined(__AVR_ATmega103__)
+       #elif CPU_AVR_ATMEGA103
                UBRR = (uint8_t)period;
-               SER_UART0_485_INIT;
+               KDBG_UART0_BUS_INIT;
        #else
-               #error Unknown arch
+               #error Unknown CPU
        #endif
 
 #endif /* !CPU_I196 && !CPU_AVR */
@@ -260,6 +288,8 @@ void kputchar(char c)
 
 void PGM_FUNC(kprintf)(const char * PGM_ATTR fmt, ...)
 {
+
+#if CONFIG_PRINTF
        va_list ap;
 
        /* Mask serial TX intr */
@@ -272,6 +302,10 @@ void PGM_FUNC(kprintf)(const char * PGM_ATTR fmt, ...)
 
        /* Restore serial TX intr */
        KDBG_RESTORE_IRQ(irqsave);
+#else
+       /* A better than nothing printf() surrogate. */
+       PGM_FUNC(kputs)(fmt);
+#endif /* CONFIG_PRINTF */
 }
 
 
@@ -290,22 +324,61 @@ void PGM_FUNC(kputs)(const char * PGM_ATTR str)
 }
 
 
-int PGM_FUNC(__assert)(const char * PGM_ATTR cond, const char *file, int line)
+/*!
+ * Cheap function to print small integers without using printf().
+ */
+static int kputnum(int num)
+{
+       int output_len = 0;
+       int divisor = 10000;
+       int digit;
+
+       do
+       {
+               digit = num / divisor;
+               num %= divisor;
+
+               if (digit || output_len || divisor == 1)
+               {
+                       kputchar(digit + '0');
+                       ++output_len;
+               }
+       }
+       while (divisor /= 10);
+
+       return output_len;
+}
+
+
+static void klocation(const char * PGM_ATTR file, int line)
 {
        PGM_FUNC(kputs)(file);
-       PGM_FUNC(kprintf)(PSTR(":%d: Assertion failed: "), line);
+       kputchar(':');
+       kputnum(line);
+       PGM_FUNC(kputs)(PSTR(": "));
+}
+
+int PGM_FUNC(__assert)(const char * PGM_ATTR cond, const char * PGM_ATTR file, int line)
+{
+       klocation(file, line);
+       PGM_FUNC(kputs)(PSTR("Assertion failed: "));
        PGM_FUNC(kputs)(cond);
-       PGM_FUNC(kputs)(PSTR("\n"));
+       kputchar('\n');
        return 1;
 }
 
 
 int PGM_FUNC(__invalid_ptr)(void *value, const char * PGM_ATTR name, const char * PGM_ATTR file, int line)
 {
-       PGM_FUNC(kputs)(file);
-       PGM_FUNC(kprintf)(PSTR(":%d: Invalid pointer: "), line);
+       klocation(file, line);
+       PGM_FUNC(kputs)(PSTR("Invalid ptr: "));
        PGM_FUNC(kputs)(name);
-       PGM_FUNC(kprintf)(PSTR(" = 0x%x\n"), value);
+       #if CONFIG_PRINTF
+               PGM_FUNC(kprintf)(PSTR(" = 0x%x\n"), value);
+       #else
+               (void)value;
+               kputchar('\n');
+       #endif
        return 1;
 }
 
@@ -317,7 +390,7 @@ void __init_wall(long *wall, int size)
 }
 
 
-int __check_wall(long *wall, int size, const char *name, const char *file, int line)
+int PGM_FUNC(__check_wall)(long *wall, int size, const char * PGM_ATTR name, const char * PGM_ATTR file, int line)
 {
        int i, fail = 0;
 
@@ -325,8 +398,14 @@ int __check_wall(long *wall, int size, const char *name, const char *file, int l
        {
                if (wall[i] != WALL_VALUE)
                {
-                       kprintf("%s:%d: Wall broken: %s[%d] (0x%p) = 0x%lx\n",
-                               file, line, name, i, wall + i, wall[i]);
+                       klocation(file, line);
+                       PGM_FUNC(kputs)(PSTR("Wall broken: "));
+                       PGM_FUNC(kputs)(name);
+                       #if CONFIG_PRINTF
+                               PGM_FUNC(kprintf)(PSTR("[%d] (0x%p) = 0x%lx\n"), i, wall + i, wall[i]);
+                       #else
+                               kputchar('\n');
+                       #endif
                        fail = 1;
                }
        }
@@ -335,6 +414,8 @@ int __check_wall(long *wall, int size, const char *name, const char *file, int l
 }
 
 
+#if CONFIG_PRINTF
+
 /*!
  * Dump binary data in hex
  */
@@ -344,7 +425,9 @@ void kdump(const void *_buf, size_t len)
 
        while (len--)
                kprintf("%02X", *buf++);
-       kputs("\n");
+       kputchar('\n');
 }
 
+#endif /* CONFIG_PRINTF */
+
 #endif /* _DEBUG */