--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright (C) 1999,2003 Bernardo Innocenti <bernie@develer.com>
+ * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Buzzer driver
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.5 2004/04/04 17:44:51 aleph
+ * Move event.h from mware to kern dir
+ *
+ * Revision 1.4 2004/03/24 15:03:45 bernie
+ * Use explicit include paths; clean Doxygen comments
+ *
+ * Revision 1.3 2004/03/03 18:27:44 bernie
+ * Fixed race conds with IRQ when fiddling with I/O ports
+ *
+ * Revision 1.2 2003/12/18 18:15:24 aleph
+ * Use new IRQ disable/enable reentrant macros
+ *
+ * Revision 1.1 2003/12/13 23:53:37 aleph
+ * Add buzzer driver
+ *
+ */
+
+#include "hw.h"
+#include "kdebug.h"
+#include "timer.h"
+#include "buzzer.h"
+#include <kern/event.h>
+
+
+#if (ARCH & ARCH_EMUL)
+
+ int Emul_IsBuzzerOn(void);
+ void Emul_BuzzerOn(void);
+ void Emul_BuzzerOff(void);
+ void Emul_BuzzerInit(void);
+
+# define IS_BUZZER_ON (Emul_IsBuzzerOn())
+# define BUZZER_ON (Emul_BuzzerOn())
+# define BUZZER_OFF (Emul_BuzzerOff())
+# define BUZZER_INIT (Emul_BuzzerInit())
+
+#elif defined(__AVR__)
+
+# define IS_BUZZER_ON (PORTG & BV(PORTG0))
+
+ /**
+ * Buzzer manipulation macros
+ *
+ * \note Some PORTG functions are being used from
+ * interrupt code, so we must be careful to
+ * avoid race conditions.
+ */
+# define BUZZER_ON \
+ do { \
+ cpuflags_t _flags; \
+ DISABLE_IRQSAVE(_flags); \
+ PORTG |= BV(PORTG0); \
+ ENABLE_IRQRESTORE(_flags); \
+ } while (0)
+
+# define BUZZER_OFF \
+ do { \
+ cpuflags_t _flags; \
+ DISABLE_IRQSAVE(_flags); \
+ PORTG &= ~BV(PORTG0); \
+ ENABLE_IRQRESTORE(_flags); \
+ } while (0)
+
+# define BUZZER_INIT \
+ do { \
+ cpuflags_t _flags; \
+ DISABLE_IRQSAVE(_flags); \
+ PORTG &= ~BV(PORTG0); \
+ DDRG |= BV(PORTG0); \
+ ENABLE_IRQRESTORE(_flags); \
+ } while (0)
+
+#elif defined(__IAR_SYSTEMS_ICC) || defined(__IAR_SYSTEMS_ICC__) /* 80C196 */
+
+# define IS_BUZZER_ON (cpld->Buzzer & 1)
+# define BUZZER_ON (cpld->Buzzer = 1)
+# define BUZZER_OFF (cpld->Buzzer = 0)
+# define BUZZER_INIT (cpld->Buzzer = 0)
+
+#endif /* ARCH, __AVR__, __IAR_SYSTEM_ICC */
+
+
+/* Local vars */
+static Timer *buz_timer;
+static bool buz_timer_running;
+static time_t buz_repeat_interval;
+static time_t buz_repeat_duration;
+
+
+/*!
+ * Turn off buzzer, called by software timer
+ */
+static void buz_softint(void)
+{
+ if (IS_BUZZER_ON)
+ {
+ BUZZER_OFF;
+ if (buz_repeat_interval)
+ {
+ /* Wait for interval time */
+ buz_timer->delay = buz_repeat_interval;
+ timer_add(buz_timer);
+ }
+ else
+ buz_timer_running = false;
+ }
+ else if (buz_repeat_interval)
+ {
+ /* Wait for beep time */
+ BUZZER_ON;
+ buz_timer->delay = buz_repeat_duration;
+ timer_add(buz_timer);
+ }
+ else
+ buz_timer_running = false;
+}
+
+
+/*!
+ * Beep for the specified ms time
+ */
+void buz_beep(time_t time)
+{
+ cpuflags_t flags;
+
+ /* Remove the software interrupt if it was already queued */
+ DISABLE_IRQSAVE(flags);
+ if (buz_timer_running)
+ timer_abort(buz_timer);
+
+ /* Turn on buzzer */
+ BUZZER_ON;
+
+ /* Add software interrupt to turn the buzzer off later */
+ buz_timer_running = true;
+ buz_timer->delay = time;
+ timer_add(buz_timer);
+
+ ENABLE_IRQRESTORE(flags);
+}
+
+
+/*!
+ * Start buzzer repetition
+ */
+void buz_repeat_start(time_t duration, time_t interval)
+{
+ buz_repeat_interval = interval;
+ buz_repeat_duration = duration;
+ buz_beep(duration);
+}
+
+
+/*!
+ * Stop buzzer repetition
+ */
+void buz_repeat_stop(void)
+{
+ cpuflags_t flags;
+ DISABLE_IRQSAVE(flags);
+
+ /* Remove the software interrupt if it was already queued */
+ if (buz_timer_running)
+ {
+ timer_abort(buz_timer);
+ buz_timer_running = false;
+ }
+
+ buz_repeat_interval = 0;
+ BUZZER_OFF;
+
+ ENABLE_IRQRESTORE(flags);
+}
+
+
+/*!
+ * Initialize buzzer
+ */
+void buz_init(void)
+{
+ BUZZER_INIT;
+
+ /* Inizializza software interrupt */
+ buz_timer = timer_new();
+ ASSERT(buz_timer != NULL);
+ INITEVENT_INT(&buz_timer->expire, (Hook)buz_softint, 0);
+}
--- /dev/null
+/*!
+ * \file
+ * Copyright (C) 1999,2003 Bernardo Innocenti <bernie@develer.com>
+ * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Buzzer driver
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.1 2003/12/13 23:53:37 aleph
+ * Add buzzer driver
+ *
+ */
+#ifndef BUZZER_H
+#define BUZZER_H
+
+extern void buz_init(void);
+extern void buz_beep(time_t time);
+extern void buz_repeat_start(time_t duration, time_t interval);
+extern void buz_repeat_stop(void);
+
+#endif /* BUZZER_H */
--- /dev/null
+/*
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000,2001,2002 Bernardo Innocenti <bernie@codewiz.org>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief General pourpose debug functions.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ * \author Stefano Fedrigo <aleph@develer.com>
+ */
+
+/*
+ */
+
+#include "kdebug.h"
+#include "hw.h"
+#include "config.h"
+
+#include <mware/formatwr.h> /* for _formatted_write() */
+
+#ifdef _DEBUG
+
+#if defined(_EMUL)
+ #include <stdio.h>
+ #define KDBG_WAIT_READY() do {/*nop*/} while(0)
+ #define KDBG_WRITE_CHAR(c) putchar((c))
+ #define KDBG_MASK_IRQ(old) do {/*nop*/} while(0)
+ #define KDBG_RESTORE_IRQ() do {/*nop*/} while(0)
+#elif defined(__I196__)
+ #include "Util196.h"
+ #define KDBG_WAIT_READY() do {} while (!(SP_STAT & (SPSF_TX_EMPTY | SPSF_TX_INT)))
+ #define KDBG_WRITE_CHAR(c) do { SBUF = (c); } while(0)
+ #define KDBG_MASK_IRQ(old) \
+ do { \
+ (old) = INT_MASK1 & INT1F_TI; \
+ INT_MASK1 &= ~INT1F_TI; \
+ } while(0)
+ #define KDBG_RESTORE_IRQ(old) do { INT_MASK1 |= (old); }
+#elif defined(__AVR__)
+ #include <avr/io.h>
+ #if CONFIG_KDEBUG_PORT == 0
+ #ifndef __AVR_ATmega103__
+ #define UCR UCSR0B
+ #define UDR UDR0
+ #define USR UCSR0A
+ #endif
+ #define KDBG_WAIT_READY() do { loop_until_bit_is_set(USR, UDRE); } while(0)
+ #define KDBG_WRITE_CHAR(c) do { UCR |= BV(TXEN); UDR = (c); } while(0)
+ #define KDBG_MASK_IRQ(old) do { (old) = UCR & BV(TXCIE); cbi(UCR, TXCIE); } while(0)
+ #define KDBG_RESTORE_IRQ(old) do { UCR |= (old); } while(0)
+ #elif CONFIG_KDEBUG_PORT == 1
+ #define KDBG_WAIT_READY() do { loop_until_bit_is_set(UCSR1A, UDRE); } while(0)
+ #define KDBG_WRITE_CHAR(c) do { UCSR1B |= BV(TXEN); UDR1 = (c); } while(0)
+ #define KDBG_MASK_IRQ(old) do { (old) = UCSR1B & BV(TXCIE); cbi(UCSR1B, TXCIE); } while(0)
+ #define KDBG_RESTORE_IRQ(old) do { UCSR1B |= (old); } while(0)
+ #else
+ #error CONFIG_KDEBUG_PORT should be either 0 or 1
+ #endif
+#else
+ #error Unknown architecture
+#endif
+
+
+void kdbg_init(void)
+{
+#if defined(__I196__)
+
+ /* Set serial port for 19200bps 8N1 */
+ INT_MASK1 &= ~(INT1F_TI | INT1F_RI);
+ SP_CON = SPCF_RECEIVE_ENABLE | SPCF_MODE1;
+ ioc1_img |= IOC1F_TXD_SEL | IOC1F_EXTINT_SRC;
+ IOC1 = ioc1_img;
+ BAUD_RATE = 0x33;
+ BAUD_RATE = 0x80;
+
+#elif defined(__AVR__)
+
+ /* Compute the baud rate */
+ uint16_t period = (((CLOCK_FREQ / 16UL) + (CONFIG_KDEBUG_BAUDRATE / 2)) / CONFIG_KDEBUG_BAUDRATE) - 1;
+
+ #ifdef __AVR_ATmega64__
+ #if CONFIG_KDEBUG_PORT == 0
+
+ /* Set the baud rate */
+ UBRR0H = (uint8_t)(period>>8);
+ UBRR0L = (uint8_t)period;
+
+ #elif CONFIG_KDEBUG_PORT == 1
+
+ UBRR1H = (uint8_t)(period>>8);
+ UBRR1L = (uint8_t)period;
+
+ #else
+ #error CONFIG_KDEBUG_PORT should be either 0 or 1
+ #endif
+ #elif defined (__AVR_ATmega103__)
+
+ /* Set the baud rate */
+ UBRR = (uint8_t)period;
+
+ #else
+ #error Unknown arch
+ #endif
+
+#endif /* !__I196__ && !__AVR__ */
+
+ kputs("\n\n*** DBG START ***\n");
+}
+
+
+/*!
+ * Output one character to the debug console
+ */
+static void kputchar(char c, UNUSED(void *unused))
+{
+ /* Poll while serial buffer is still busy */
+ KDBG_WAIT_READY();
+
+ /* Send '\n' as '\r\n' for dumb terminals */
+ if (c == '\n')
+ {
+ KDBG_WRITE_CHAR('\r');
+ KDBG_WAIT_READY();
+ }
+
+ KDBG_WRITE_CHAR(c);
+}
+
+
+void PGM_FUNC(kprintf)(const char * PGM_ATTR fmt, ...)
+{
+ va_list ap;
+
+ /* Mask serial TX intr */
+ unsigned char irqsave;
+ KDBG_MASK_IRQ(irqsave);
+
+ va_start(ap, fmt);
+ PGM_FUNC(_formatted_write)(fmt, kputchar, 0, ap);
+ va_end(ap);
+
+ /* Restore serial TX intr */
+ KDBG_RESTORE_IRQ(irqsave);
+}
+
+
+void PGM_FUNC(kputs)(const char * PGM_ATTR str)
+{
+ char c;
+
+ /* Mask serial TX intr */
+ unsigned char irqsave;
+ KDBG_MASK_IRQ(irqsave);
+
+ while ((c = PGM_READ_CHAR(str++)))
+ kputchar(c, 0);
+
+ KDBG_RESTORE_IRQ(irqsave);
+}
+
+
+int PGM_FUNC(__assert)(const char * PGM_ATTR cond, const char *file, int line)
+{
+ PGM_FUNC(kputs)(file);
+ PGM_FUNC(kprintf)(PSTR(":%d: Assertion failed: \""), line);
+ PGM_FUNC(kputs)(cond);
+ PGM_FUNC(kputs)(PSTR("\"\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);
+ PGM_FUNC(kputs)(name);
+ PGM_FUNC(kprintf)(PSTR(" = 0x%x\n"), value);
+ return 1;
+}
+
+
+void __init_wall(long *wall, int size)
+{
+ while(size--)
+ *wall++ = WALL_VALUE;
+}
+
+
+int __check_wall(long *wall, int size, const char *name, const char *file, int line)
+{
+ int i, fail = 0;
+
+ for (i = 0; i < size; i++)
+ {
+ if (wall[i] != WALL_VALUE)
+ {
+ kprintf("%s:%d: Wall broken: %s[%d] (0x%p) = %ld\n",
+ file, line, name, i, wall + i, wall[i]);
+ fail = 1;
+ }
+ }
+
+ return fail;
+}
+
+
+/*!
+ * Dump binary data in hex
+ */
+void kdump(const void *_buf, size_t len)
+{
+ const unsigned char *buf = (const unsigned char *)_buf;
+
+ while (len--)
+ kprintf("%02X", *buf++);
+ kputs("\n");
+}
+
+#endif /* _DEBUG */
--- /dev/null
+/*
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000,2001,2002 Bernardo Innocenti <bernie@codewiz.org>
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Definition of handy debug macros. These macros are no-ops
+ * when the preprocessor symbol _DEBUG isn't defined.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ */
+#ifndef KDEBUG_H
+#define KDEBUG_H
+
+#include "compiler.h"
+
+/* Avoid name clashes with Win32 headers */
+#ifdef ASSERT
+#undef ASSERT
+#endif
+
+#if defined(_DEBUG)
+ void kdbg_init(void);
+ void kdump(const void *buf, size_t len);
+
+ #ifdef __AVR__
+
+ #include <avr/pgmspace.h>
+ void kputs_P(const char *PROGMEM str);
+ void kprintf_P(const char *PROGMEM fmt, ...) FORMAT(__printf__, 1, 2);
+ int __assert_P(const char *PROGMEM cond, const char *PROGMEM file, int line);
+ int __invalid_ptr_P(void *p, const char *PROGMEM name, const char *PROGMEM file, int line);
+ #define kputs(str) kputs_P(PSTR(str))
+ #define kprintf(fmt, ...) kprintf_P(PSTR(fmt) ,## __VA_ARGS__)
+ #define __assert(cond, file, line) __assert_P(PSTR(cond), PSTR(file), (line))
+ #define __invalid_ptr(p, name, file, line) __invalid_ptr_P((p), PSTR(name), PSTR(file), (line))
+
+ #else
+ void kputs(const char *str);
+ void kprintf(const char * fmt, ...) FORMAT(__printf__, 1, 2);
+ int __assert(const char *cond, const char *file, int line);
+ int __invalid_ptr(void *p, const char *name, const char *file, int line);
+ #endif
+
+ void __init_wall(long *wall, int size);
+ int __check_wall(long *wall, int size, const char *name, const char *file, int line);
+
+ #define THIS_FILE __FILE__
+ #define ASSERT(x) ((x) ? 0 : __assert(#x, THIS_FILE, __LINE__))
+ #define ASSERT_VALID_PTR(p) ((p >= 0x200) ? 0 : __invalid_ptr(p, #p, THIS_FILE, __LINE__))
+ #define ASSERT_VALID_PTR_OR_NULL(p) (((p == NULL) || (p >= 0x200)) ? 0 : __invalid_ptr(p, #p, THIS_FILE, __LINE__))
+ #define TRACE kprintf("%s()\n", __FUNCTION__)
+ #define TRACEMSG(msg,...) kprintf("%s(): " msg "\n", __FUNCTION__, ## __VA_ARGS__)
+ #define DB(x) x
+
+ /* Walls to detect data corruption */
+ #define WALL_SIZE 8
+ #define WALL_VALUE (long)0xABADCAFEL
+ #define DECLARE_WALL(name,size) static long name[(size) / sizeof(long)];
+ #define INIT_WALL(name) __init_wall((name), countof(name))
+ #define CHECK_WALL(name) __check_wall((name), countof(name), #name, THIS_FILE, __LINE__)
+
+#else /* !_DEBUG */
+
+ #define ASSERT(x)
+ #define ASSERT_VALID_PTR(p)
+ #define ASSERT_VALID_PTR_OR_NULL(p)
+ #define TRACE do {} while(0)
+ #define TRACEMSG(x,...) do {} while(0)
+ #define DB(x)
+
+ #define DECLARE_WALL(name, size)
+ #define INIT_WALL(name)
+ #define CHECK_WALL(name)
+
+#endif /* !_DEBUG */
+
+#endif /* KDEBUG_H */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief Buffered serial I/O driver
+ *
+ * The serial rx interrupt buffers incoming data in a software FIFO
+ * to decouple the higher level protocols from the line speed.
+ * Outgoing data is buffered as well for better performance.
+ * This driver is not optimized for best performance, but it
+ * has proved to be fast enough to handle transfer rates up to
+ * 38400bps on a 16MHz 80196.
+ *
+ * MODULE CONFIGURATION
+ * \li \c CONFIG_SER_HWHANDSHAKE define this preprocessor symbol to enable
+ * support for RTS/CTS handshake. Support is incomplete/untested
+ * for 80196.
+ * \li \c CONFIG_SER_TXTIMEOUT - Enable software serial transmission timeouts
+ *
+ *
+ * \version $Id$
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.5 2004/05/14 12:47:26 rasky
+ * Importato nuovo supporto seriale per AVR da Stefano
+ *
+ * Revision 1.4 2004/04/29 16:40:23 rasky
+ * Durante i busy loop della seriale, chiama la proc_switch() per cambiare processo
+ *
+ * Revision 1.3 2004/04/21 17:38:24 rasky
+ * New application
+ *
+ * Revision 1.16 2004/04/03 18:30:49 aleph
+ * Move timeout defines in config, private define in .c
+ *
+ * Revision 1.15 2004/03/29 17:01:02 aleph
+ * Add function to set serial parity, fix it when ser_open is used
+ *
+ * Revision 1.14 2004/03/29 16:19:33 aleph
+ * Add ser_cleanup function; Various code improvements
+ *
+ * Revision 1.13 2004/03/24 15:48:53 bernie
+ * Remove Copyright messages from Doxygen output
+ */
+
+#include <mware/formatwr.h>
+#include <drv/kdebug.h>
+#include "ser.h"
+#include "ser_p.h"
+#include "hw.h"
+
+#ifdef CONFIG_KERNEL
+ #include <kern/proc.h>
+#endif
+#if defined(CONFIG_SER_TXTIMEOUT) || defined(CONFIG_SER_RXTIMEOUT)
+ #include <drv/timer.h>
+#endif
+
+
+/* Serial configuration parameters */
+#define SER_CTSDELAY 70 /*!< CTS line retry interval (ms) */
+#define SER_TXPOLLDELAY 2 /*!< Transmit buffer full retry interval (ms) */
+#define SER_RXPOLLDELAY 2 /*!< Receive buffer empty retry interval (ms) */
+
+
+struct Serial ser_handles[SER_CNT];
+
+
+/*!
+ * Inserisce il carattere c nel buffer di trasmissione.
+ * Questa funzione mette il processo chiamante in attesa
+ * quando il buffer e' pieno.
+ *
+ * \return EOF in caso di errore o timeout, altrimenti
+ * il carattere inviato.
+ */
+int ser_putchar(int c, struct Serial *port)
+{
+ if (fifo_isfull_locked(&port->txfifo))
+ {
+#ifdef CONFIG_SER_TXTIMEOUT
+ time_t start_time = timer_gettick();
+#endif
+
+ /* Attende finche' il buffer e' pieno... */
+ do
+ {
+#ifdef CONFIG_KERN_SCHED
+ /* Give up timeslice to other processes. */
+ proc_switch();
+#endif
+#ifdef CONFIG_SER_TXTIMEOUT
+ if (timer_gettick() - start_time >= port->txtimeout)
+ {
+ port->status |= SERRF_TXTIMEOUT;
+ return EOF;
+ }
+#endif /* CONFIG_SER_TXTIMEOUT */
+ }
+ while (fifo_isfull_locked(&port->txfifo));
+ }
+
+ fifo_push(&port->txfifo, (unsigned char)c);
+
+ /* (re)trigger tx interrupt */
+ port->hw->table->enabletxirq(port->hw);
+
+ /* Avoid returning signed estended char */
+ return (int)((unsigned char)c);
+}
+
+
+/*!
+ * Preleva un carattere dal buffer di ricezione.
+ * Questa funzione mette il processo chiamante in attesa
+ * quando il buffer e' vuoto. L'attesa ha un timeout
+ * di ser_rxtimeout millisecondi.
+ *
+ * \return EOF in caso di errore o timeout, altrimenti
+ * il carattere ricevuto.
+ */
+int ser_getchar(struct Serial *port)
+{
+ int result;
+
+ if (fifo_isempty_locked(&port->rxfifo))
+ {
+#ifdef CONFIG_SER_RXTIMEOUT
+ time_t start_time = timer_gettick();
+#endif
+ /* Wait while buffer is empty */
+ do
+ {
+#ifdef CONFIG_KERN_SCHED
+ /* Give up timeslice to other processes. */
+ proc_switch();
+#endif
+#ifdef CONFIG_SER_RXTIMEOUT
+ if (timer_gettick() - start_time >= port->rxtimeout)
+ {
+ port->status |= SERRF_RXTIMEOUT;
+ return EOF;
+ }
+#endif /* CONFIG_SER_RXTIMEOUT */
+ }
+ while (fifo_isempty_locked(&port->rxfifo));
+ }
+
+ /*
+ * Get a byte from the FIFO (avoiding sign-extension),
+ * re-enable RTS, then return result.
+ */
+ result = (int)(unsigned char)fifo_pop(&port->rxfifo);
+ return port->status ? EOF : result;
+}
+
+
+/*!
+ * Preleva un carattere dal buffer di ricezione.
+ * Se il buffer e' vuoto, ser_getchar_nowait() ritorna
+ * immediatamente EOF.
+ */
+int ser_getchar_nowait(struct Serial *port)
+{
+ if (fifo_isempty_locked(&port->rxfifo))
+ return EOF;
+
+ /* NOTE: the double cast prevents unwanted sign extension */
+ return (int)(unsigned char)fifo_pop(&port->rxfifo);
+}
+
+
+/*!
+ * Read a line long at most as size and puts it
+ * in buf.
+ * \return number of chars read or EOF in case
+ * of error.
+ */
+int ser_gets(struct Serial *port, char *buf, int size)
+{
+ return ser_gets_echo(port, buf, size, false);
+}
+
+
+/*!
+ * Read a line long at most as size and puts it
+ * in buf, with optional echo.
+ * \return number of chars read or EOF in case
+ * of error.
+ */
+int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo)
+{
+ int i = 0;
+ int c;
+
+ for (;;)
+ {
+ if ((c = ser_getchar(port)) == EOF)
+ return -1;
+ /* FIXME */
+ if (c == '\r' || c == '\n' || i >= size-1)
+ {
+ buf[i] = '\0';
+ if (echo)
+ ser_print(port, "\r\n");
+ break;
+ }
+ buf[i++] = c;
+ if (echo)
+ ser_putchar(c, port);
+ }
+
+ return i;
+}
+
+
+/*!
+ * Read at most size bytes and puts them
+ * in buf.
+ * \return number of bytes read or EOF in case
+ * of error.
+ */
+int ser_read(struct Serial *port, char *buf, size_t size)
+{
+ size_t i = 0;
+ int c;
+
+ while (i < size)
+ {
+ if ((c = ser_getchar(port)) == EOF)
+ return EOF;
+ buf[i++] = c;
+ }
+
+ return i;
+}
+
+
+/*!
+ * Write a string to serial.
+ * \return 0 if OK, EOF in case of error.
+ */
+int ser_print(struct Serial *port, const char *s)
+{
+ while (*s)
+ {
+ if (ser_putchar(*s++, port) == EOF)
+ return EOF;
+ }
+ return 0;
+}
+
+
+/*!
+ * \brief Write a buffer to serial.
+ *
+ * \return 0 if OK, EOF in case of error.
+ */
+int ser_write(struct Serial *port, const void *buf, size_t len)
+{
+ while (len--)
+ {
+ if (ser_putchar(*((const char *)buf)++, port) == EOF)
+ return EOF;
+ }
+ return 0;
+}
+
+
+/*!
+ * Formatted write
+ */
+int ser_printf(struct Serial *port, const char *format, ...)
+{
+ va_list ap;
+ int len;
+
+ ser_setstatus(port, 0);
+ va_start(ap, format);
+ len = _formatted_write(format, (void (*)(char, void *))ser_putchar, port, ap);
+ va_end(ap);
+
+ return len;
+}
+
+#if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT)
+void ser_settimeouts(struct Serial *port, time_t rxtimeout, time_t txtimeout)
+{
+ port->rxtimeout = rxtimeout;
+ port->txtimeout = txtimeout;
+}
+#endif /* defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT) */
+
+
+void ser_setbaudrate(struct Serial *port, unsigned long rate)
+{
+ port->hw->table->setbaudrate(port->hw, rate);
+}
+
+
+void ser_setparity(struct Serial *port, int parity)
+{
+ port->hw->table->setparity(port->hw, parity);
+}
+
+
+/*!
+ * Flush both the RX and TX buffers.
+ */
+void ser_purge(struct Serial *ser)
+{
+ fifo_flush(&ser->rxfifo);
+ fifo_flush(&ser->txfifo);
+}
+
+
+/*!
+ * Initialize serial
+ */
+struct Serial *ser_open(unsigned int unit)
+{
+ struct Serial *port;
+
+ ASSERT(unit < countof(ser_handles));
+
+ port = &ser_handles[unit];
+
+ ASSERT(!port->is_open);
+
+ port->unit = unit;
+ port->is_open = true;
+
+ /* Initialize circular buffer */
+ fifo_init(&port->rxfifo, port->rxbuffer, sizeof(port->rxbuffer));
+ fifo_init(&port->txfifo, port->txbuffer, sizeof(port->txbuffer));
+
+ port->hw = ser_hw_getdesc(unit);
+ port->hw->table->init(port->hw, port);
+
+ /* Set default values */
+#if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT)
+ ser_settimeouts(port, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT);
+#endif
+ ser_setbaudrate(port, CONFIG_SER_DEFBAUDRATE);
+
+ return port;
+}
+
+
+/*!
+ * Clean up serial port, disabling the associated hardware.
+ */
+void ser_close(struct Serial *port)
+{
+ ASSERT(port->is_open);
+
+ port->is_open = false;
+ port->hw->table->cleanup(port->hw);
+ port->hw = NULL;
+}
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \brief High level serial I/O API
+ *
+ * \version $Id$
+ * \author Bernardo Innocenti <bernie@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.2 2004/04/21 17:38:24 rasky
+ * New application
+ *
+ * Revision 1.16 2004/04/03 18:30:49 aleph
+ * Move timeout defines in config, private define in .c
+ *
+ * Revision 1.15 2004/03/29 17:01:02 aleph
+ * Add function to set serial parity, fix it when ser_open is used
+ *
+ * Revision 1.14 2004/03/29 16:19:33 aleph
+ * Add ser_cleanup function; Various code improvements
+ *
+ * Revision 1.13 2004/03/24 15:22:27 aleph
+ * Removed subdirs -I, fix header inclusion, move config.h in board_kf, kctrl
+ *
+ * Revision 1.12 2004/03/17 17:30:30 bernie
+ * Add GCC format checks to printf()-like functions.
+ *
+ * Revision 1.11 2004/03/16 23:06:42 aleph
+ * Doc fix
+ *
+ * Revision 1.10 2004/03/12 18:46:53 bernie
+ * ser_read(): New function.
+ *
+ * Revision 1.9 2004/03/11 18:11:51 bernie
+ * Cosmetic fixes
+ */
+#ifndef SER_H
+#define SER_H
+
+#include "compiler.h"
+#include <mware/fifobuf.h>
+#include "config.h"
+
+/*!
+ * \name Serial Error/status flags
+ *
+ * Some of these flags map directly to the flags
+ * in AVR UART Status Register(USR).
+ * \todo flags of DSP56k aren't mapped to these flags. Luckily
+ * these flags doesn't collide with the DSP56k ones,
+ * which are from 0x0100 to 0x8000
+ */
+/*\{*/
+#if defined(__AVR__)
+ /* Software errors */
+ #define SERRF_RXFIFOOVERRUN BV(0) /*!< Rx FIFO buffer overrun */
+ #define SERRF_RXTIMEOUT BV(5) /*!< Receive timeout */
+ #define SERRF_TXTIMEOUT BV(6) /*!< Transmit timeout */
+
+ /* Hardware errors */
+ #define SERRF_RXSROVERRUN BV(3) /*!< Rx shift register overrun */
+ #define SERRF_FRAMEERROR BV(4) /*!< Stop bit missing */
+ #define SERRF_PARITYERROR BV(7) /*!< Parity error */
+#elif defined(__m56800__)
+ /* Software errors */
+ #define SERRF_RXFIFOOVERRUN BV(0) /*!< Rx FIFO buffer overrun */
+ #define SERRF_RXTIMEOUT BV(1) /*!< Receive timeout */
+ #define SERRF_TXTIMEOUT BV(2) /*!< Transmit timeout */
+
+ /* Hardware errors */
+ #define SERRF_PARITYERROR BV(8) /*!< Parity error */
+ #define SERRF_FRAMEERROR BV(9) /*!< Stop bit missing */
+ #define SERRF_NOISEERROR BV(10) /*!< Noise error */
+ #define SERRF_RXSROVERRUN BV(11) /*!< Rx shift register overrun */
+#else
+ #error unknown architecture
+#endif
+/*\}*/
+
+/*! \name Parity settings for ser_setparity() */
+/*\{*/
+#define SER_PARITY_NONE 0
+#define SER_PARITY_EVEN 2
+#define SER_PARITY_ODD 3
+/*\}*/
+
+/*!
+ * \name Serial hw numbers
+ *
+ * \{
+ */
+enum
+{
+ #if defined(__AVR_ATmega64__)
+ SER_UART0,
+ SER_UART1,
+ SER_SPI,
+
+ #elif defined(__AVR_ATmega103__)
+ SER_UART0,
+ SER_SPI,
+
+ #elif defined (__m56800__)
+ SER_UART0,
+ SER_UART1,
+
+ #else
+ #error unknown architecture
+ #endif
+
+ SER_CNT /**< Number of serial ports */
+};
+/* @} */
+
+
+struct SerialHardware;
+
+/*! Human-readable serial error descriptions */
+extern const char * const serial_errors[8];
+
+/*! Serial handle structure */
+struct Serial
+{
+ /*! Physical port number */
+ unsigned int unit;
+
+ bool is_open;
+
+ /*!
+ * \name FIFO transmit and receive buffers.
+ *
+ * Declared volatile because handled asinchronously by interrupts.
+ *
+ * \{
+ */
+ volatile FIFOBuffer txfifo;
+ volatile FIFOBuffer rxfifo;
+ unsigned char txbuffer[CONFIG_SER_TXBUFSIZE];
+ unsigned char rxbuffer[CONFIG_SER_RXBUFSIZE];
+ /* \} */
+
+#ifdef CONFIG_SER_RXTIMEOUT
+ time_t rxtimeout;
+#endif
+#ifdef CONFIG_SER_TXTIMEOUT
+ time_t txtimeout;
+#endif
+
+ /*! Holds the flags defined above. Will be 0 when no errors have occurred. */
+ REGISTER uint16_t status;
+
+ /*! Low-level interface to hardware. */
+ struct SerialHardware* hw;
+
+};
+
+
+/* Function prototypes */
+extern int ser_putchar(int c, struct Serial *port);
+extern int ser_getchar(struct Serial *port);
+extern int ser_getchar_nowait(struct Serial *port);
+extern int ser_write(struct Serial *port, const void *buf, size_t len);
+extern int ser_read(struct Serial *port, char *buf, size_t size);
+extern int ser_print(struct Serial *port, const char *s);
+extern int ser_gets(struct Serial *port, char *buf, int size);
+extern int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo);
+extern int ser_printf(struct Serial *port, const char *format, ...) FORMAT(__printf__, 2, 3);
+extern void ser_setbaudrate(struct Serial *port, unsigned long rate);
+extern void ser_setparity(struct Serial *port, int parity);
+extern void ser_purge(struct Serial *port);
+extern struct Serial *ser_open(unsigned int unit);
+extern void ser_close(struct Serial *port);
+#if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT)
+ extern void ser_settimeouts(struct Serial *port, time_t rxtimeout, time_t txtimeout);
+#endif
+
+/**
+ * @name Additional functions implemented as macros
+ *
+ * @{
+ */
+#define ser_getstatus(h) ((h)->status)
+#define ser_setstatus(h, x) ((h)->status = (x))
+/* @} */
+
+#endif /* SER_H */
--- /dev/null
+/**
+ * \file
+ * <!--
+ * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
+ * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief AVR UART and SPI I/O driver
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.30 2004/05/19 17:06:11 bernie
+ * Serial TX fill mode
+ *
+ * Revision 1.29 2004/05/16 19:16:46 aleph
+ * Serial always transmitting, first try
+ *
+ * Revision 1.28 2004/05/14 12:09:00 aleph
+ * Fix TX port pull-ups
+ *
+ * Revision 1.27 2004/05/08 13:56:02 aleph
+ * Adapt avr serial driver to new design
+ *
+ * Revision 1.25 2004/04/28 13:42:16 aleph
+ * Serial port fixes
+ *
+ * Revision 1.24 2004/04/08 14:17:27 bernie
+ * Change serial to disable TX when not sending data
+ *
+ * Revision 1.23 2004/04/03 20:39:41 aleph
+ * Remove strobe
+ *
+ * Revision 1.22 2004/03/29 17:01:02 aleph
+ * Add function to set serial parity, fix it when ser_open is used
+ */
+
+#include "ser.h"
+#include "ser_p.h"
+#include "kdebug.h"
+#include "config.h"
+#include "hw.h"
+#include <mware/fifobuf.h>
+
+extern struct Serial ser_handles[SER_CNT];
+
+struct AvrSerial
+{
+ struct SerialHardware hw;
+ struct Serial* serial;
+};
+
+
+/* Hardware handshake */
+#define RTS_ON
+#define RTS_OFF
+#define IS_CTS_ON true
+#define IS_CTS_OFF false
+
+
+/* SPI port and pin configuration */
+#define SPI_PORT PORTB
+#define SPI_DDR DDRB
+#define SPI_SCK_BIT PORTB1
+#define SPI_MOSI_BIT PORTB2
+#define SPI_MISO_BIT PORTB3
+
+
+#ifdef __AVR_ATmega103__
+ /* Macro for ATmega103 compatibility */
+ #define UCSR0B UCR
+ #define UDR0 UDR
+ #define UCSR0A USR
+ #define UBRR0L UBRR
+#else
+ #define UCR UCSR0B
+ #define UDR UDR0
+ #define USR UCSR0A
+#endif
+
+
+/* Transmission fill byte */
+#define SER_FILL_BYTE 0xAA
+
+
+static void uart0_enabletxirq(UNUSED(struct SerialHardware *ctx))
+{
+#ifdef CONFIG_SER_TXFILL
+ UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN) | BV(UCSZ2);
+#else
+ UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
+#endif
+}
+
+static void uart0_init(struct SerialHardware *_hw, struct Serial *ser)
+{
+ struct AvrSerial *hw = (struct AvrSerial *)_hw;
+ hw->serial = ser;
+
+ /* Set TX port as input with pull-up enabled to avoid
+ * noise on the remote RX when TX is disabled */
+ cpuflags_t flags;
+ DISABLE_IRQSAVE(flags);
+ DDRE &= ~BV(PORTE1);
+ PORTE |= BV(PORTE1);
+ ENABLE_IRQRESTORE(flags);
+
+ /* TODO: explain why TX is disabled whenever possible */
+#ifdef CONFIG_SER_TXFILL
+ /*!
+ * Set multiprocessor mode and 9 bit data frame.
+ * The receiver keep MPCM bit always on. When useful data
+ * is trasmitted the ninth bit is set. Receiver consider the
+ * frame as address info and receive it.
+ * When useless fill bytes are sent the ninth bit is cleared
+ * and the receiver will ignore them, avoiding useless triggering
+ * of RXC interrupt.
+ */
+ UCSR0A = BV(MPCM);
+ UCSR0B = BV(RXCIE) | BV(RXEN) | BV(UCSZ2);
+#else
+ UCSR0B = BV(RXCIE) | BV(RXEN);
+#endif
+
+ RTS_ON;
+}
+
+static void uart0_cleanup(UNUSED(struct SerialHardware *ctx))
+{
+ UCSR0B = 0;
+}
+
+static void uart0_setbaudrate(UNUSED(struct SerialHardware *ctx), unsigned long rate)
+{
+ // Compute baud-rate period
+ uint16_t period = (((CLOCK_FREQ / 16UL) + (rate / 2)) / rate) - 1;
+
+#ifndef __AVR_ATmega103__
+ UBRR0H = (period) >> 8;
+#endif
+ UBRR0L = (period);
+}
+
+
+#ifndef __AVR_ATmega103__
+
+static void uart1_enabletxirq(UNUSED(struct SerialHardware *ctx))
+{
+ UCSR1B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
+}
+
+static void uart1_init(struct SerialHardware *_hw, struct Serial *ser)
+{
+ struct AvrSerial *hw = (struct AvrSerial *)_hw;
+ hw->serial = ser;
+
+ /* Set TX port as input with pull-up enabled to avoid
+ * noise on the remote RX when TX is disabled */
+ cpuflags_t flags;
+ DISABLE_IRQSAVE(flags);
+ DDRD &= ~BV(PORTD3);
+ PORTD |= BV(PORTD3);
+ ENABLE_IRQRESTORE(flags);
+
+ /* TODO: explain why TX is disabled whenever possible */
+ UCSR1B = BV(RXCIE) | BV(RXEN);
+
+ RTS_ON;
+}
+
+static void uart1_cleanup(UNUSED(struct SerialHardware *ctx))
+{
+ UCSR1B = 0;
+}
+
+static void uart1_setbaudrate(UNUSED(struct SerialHardware *ctx), unsigned long rate)
+{
+ // Compute baud-rate period
+ uint16_t period = (((CLOCK_FREQ / 16UL) + (rate / 2)) / rate) - 1;
+
+ UBRR1H = (period) >> 8;
+ UBRR1L = (period);
+}
+
+static void uart0_setparity(UNUSED(struct SerialHardware *ctx), int parity)
+{
+ UCSR0C |= (parity) << UPM0;
+}
+
+static void uart1_setparity(UNUSED(struct SerialHardware *ctx), int parity)
+{
+ UCSR1C |= (parity) << UPM0;
+}
+
+#endif /* !__AVR_ATmega103__ */
+
+
+static void spi_init(struct SerialHardware *_hw, struct Serial *ser)
+{
+ struct AvrSerial *hw = (struct AvrSerial *)_hw;
+ hw->serial = ser;
+
+ /* MOSI and SCK out, MISO in */
+ SPI_DDR |= BV(SPI_MOSI_BIT) | BV(SPI_SCK_BIT);
+ SPI_DDR &= ~BV(SPI_MISO_BIT);
+ /* Enable SPI, IRQ on, Master, CPU_CLOCK/16 */
+ SPCR = BV(SPE) | BV(SPIE) | BV(MSTR) | BV(SPR0);
+}
+
+static void spi_cleanup(UNUSED(struct SerialHardware *ctx))
+{
+ SPCR = 0;
+ /* Set all pins as inputs */
+ SPI_DDR &= ~(BV(SPI_MISO_BIT) | BV(SPI_MOSI_BIT) | BV(SPI_SCK_BIT));
+}
+
+
+
+#if defined(CONFIG_SER_HW_HANDSHAKE)
+
+//! This interrupt is triggered when the CTS line goes high
+SIGNAL(SIG_CTS)
+{
+ // Re-enable UDR empty interrupt and TX, then disable CTS interrupt
+ UCR = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
+ cbi(EIMSK, EIMSKB_CTS);
+}
+
+#endif // CONFIG_SER_HW_HANDSHAKE
+
+
+/*!
+ * Serial 0 TX interrupt handler
+ */
+#ifdef __AVR_ATmega103__
+SIGNAL(SIG_UART_DATA)
+#else
+SIGNAL(SIG_UART0_DATA)
+#endif
+{
+ if (fifo_isempty(&ser_handles[SER_UART0].txfifo))
+ {
+#ifdef CONFIG_SER_TXFILL
+ /*
+ * To avoid audio interference: always transmit useless char.
+ * Send the byte with the ninth bit cleared, the receiver in MCPM mode
+ * will ignore it.
+ */
+ UCSR0B &= ~BV(TXB8);
+ UDR0 = SER_FILL_BYTE;
+#else
+ /* Disable UDR empty interrupt and transmitter */
+ UCR = BV(RXCIE) | BV(RXEN);
+#endif
+ }
+#if defined(CONFIG_SER_HWHANDSHAKE)
+ else if (IS_CTS_OFF)
+ {
+ // disable rx interrupt and tx, enable CTS interrupt
+ UCR = BV(RXCIE) | BV(RXEN);
+ sbi(EIFR, EIMSKB_CTS);
+ sbi(EIMSK, EIMSKB_CTS);
+ }
+#endif // CONFIG_SER_HWHANDSHAKE
+ else
+ {
+#ifdef CONFIG_SER_TXFILL
+ /* Send with ninth bit set. Receiver in MCPM mode will receive it */
+ UCSR0B |= BV(TXB8);
+#endif
+ UDR = fifo_pop(&ser_handles[SER_UART0].txfifo);
+ }
+}
+
+/*!
+ * Serial 1 TX interrupt handler
+ */
+#ifndef __AVR_ATmega103__
+SIGNAL(SIG_UART1_DATA)
+{
+ if (fifo_isempty(&ser_handles[SER_UART1].txfifo))
+ {
+ /* Disable UDR empty interrupt and transmitter */
+ UCSR1B = BV(RXCIE) | BV(RXEN);
+ }
+#if defined(CONFIG_SER_HWHANDSHAKE)
+ else if (IS_CTS_OFF)
+ {
+ // disable rx interrupt and tx, enable CTS interrupt
+ UCSR1B = BV(RXCIE) | BV(RXEN);
+ sbi(EIFR, EIMSKB_CTS);
+ sbi(EIMSK, EIMSKB_CTS);
+ }
+#endif // CONFIG_SER_HWHANDSHAKE
+ else
+ UDR1 = fifo_pop(&ser_handles[SER_UART1].txfifo);
+}
+#endif /* !__AVR_ATmega103__ */
+
+
+/*!
+ * Serial 0 RX complete interrupt handler
+ */
+#ifdef __AVR_ATmega103__
+SIGNAL(SIG_UART_RECV)
+#else
+SIGNAL(SIG_UART0_RECV)
+#endif
+{
+ /* Should be read before UDR */
+ ser_handles[SER_UART0].status |= USR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
+
+ /* To clear the RXC flag we must _always_ read the UDR even when we're
+ * not going to accept the incoming data, otherwise a new interrupt
+ * will occur once the handler terminates.
+ */
+ char c = UDR;
+
+ if (fifo_isfull(&ser_handles[SER_UART0].rxfifo))
+ ser_handles[SER_UART0].status |= SERRF_RXFIFOOVERRUN;
+ else
+ {
+ fifo_push(&ser_handles[SER_UART0].rxfifo, c);
+#if defined(CONFIG_SER_HW_HANDSHAKE)
+ if (fifo_isfull(&ser_handles[SER_UART0].rxfifo))
+ RTS_OFF;
+#endif
+ }
+}
+
+/*!
+ * Serial 1 RX complete interrupt handler
+ */
+#ifndef __AVR_ATmega103__
+SIGNAL(SIG_UART1_RECV)
+{
+ /* Should be read before UDR */
+ ser_handles[SER_UART1].status |= UCSR1A & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
+
+ /* To avoid an IRQ storm, we must _always_ read the UDR even when we're
+ * not going to accept the incoming data
+ */
+ char c = UDR1;
+
+ if (fifo_isfull(&ser_handles[SER_UART1].rxfifo))
+ ser_handles[SER_UART1].status |= SERRF_RXFIFOOVERRUN;
+ else
+ {
+ fifo_push(&ser_handles[SER_UART1].rxfifo, c);
+#if defined(CONFIG_SER_HW_HANDSHAKE)
+ if (fifo_isfull(&ser_handles[SER_UART1].rxfifo))
+ RTS_OFF;
+#endif
+ }
+}
+#endif /* !__AVR_ATmega103__ */
+
+
+/*
+ * SPI Flag: true if we are transmitting/receiving with the SPI.
+ *
+ * This kludge is necessary because the SPI sends and receives bytes
+ * at the same time and the SPI IRQ is unique for send/receive.
+ * The only way to start transmission is to write data in SPDR (this
+ * is done by ser_spi_starttx()). We do this *only* if a transfer is
+ * not already started.
+ */
+static volatile bool spi_sending = false;
+
+static void spi_starttx(UNUSED(struct SerialHardware *ctx))
+{
+ cpuflags_t flags;
+
+ DISABLE_IRQSAVE(flags);
+
+ /* Send data only if the SPI is not already transmitting */
+ if (!spi_sending && !fifo_isempty(&ser_handles[SER_SPI].txfifo))
+ {
+ SPDR = fifo_pop(&ser_handles[SER_SPI].txfifo);
+ spi_sending = true;
+ }
+
+ ENABLE_IRQRESTORE(flags);
+}
+
+/*!
+ * SPI interrupt handler
+ */
+SIGNAL(SIG_SPI)
+{
+ /* Read incoming byte. */
+ if (!fifo_isfull(&ser_handles[SER_SPI].rxfifo))
+ fifo_push(&ser_handles[SER_SPI].rxfifo, SPDR);
+ /*
+ * FIXME
+ else
+ ser_handles[SER_SPI].status |= SERRF_RXFIFOOVERRUN;
+ */
+
+ /* Send */
+ if (!fifo_isempty(&ser_handles[SER_SPI].txfifo))
+ SPDR = fifo_pop(&ser_handles[SER_SPI].txfifo);
+ else
+ spi_sending = false;
+}
+
+
+/*
+
+#pragma vector = UART_TXC_vect
+__interrupt void UART_TXC_interrupt(void)
+{
+ UCSRB &= ~TXCIE;
+ ReceiveMode();
+ UCSRB = RXCIE | RXEN | TXEN; //Abilito l'Interrupt in ricezione e RX e TX
+}
+*/
+
+
+static const struct SerialHardwareVT UART0_VT =
+{
+ .init = uart0_init,
+ .cleanup = uart0_cleanup,
+ .setbaudrate = uart0_setbaudrate,
+ .setparity = uart0_setparity,
+ .enabletxirq = uart0_enabletxirq,
+};
+
+static const struct SerialHardwareVT UART1_VT =
+{
+ .init = uart1_init,
+ .cleanup = uart1_cleanup,
+ .setbaudrate = uart1_setbaudrate,
+ .setparity = uart1_setparity,
+ .enabletxirq = uart1_enabletxirq,
+};
+
+static const struct SerialHardwareVT SPI_VT =
+{
+ .init = spi_init,
+ .cleanup = spi_cleanup,
+ .enabletxirq = spi_starttx,
+};
+
+static struct AvrSerial UARTDescs[SER_CNT] =
+{
+ {
+ .hw = { .table = &UART0_VT },
+ },
+
+ {
+ .hw = { .table = &UART1_VT },
+ },
+
+ {
+ .hw = { .table = &SPI_VT },
+ },
+};
+
+struct SerialHardware* ser_hw_getdesc(int unit)
+{
+ ASSERT(unit < SER_CNT);
+ return &UARTDescs[unit].hw;
+}
--- /dev/null
+/**
+ * \file
+ * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ *
+ * \version $Id$
+ *
+ * \author Stefano Fedrigo <aleph@develer.com>
+ *
+ * \brief DSP5680x CPU specific serial I/O driver
+ */
+
+#include <DSP56F807.H>
+#include <drv/kdebug.h>
+#include <hw.h>
+#include "ser.h"
+#include "ser_p.h"
+
+// GPIO E is shared with SPI (in DSP56807). Pins 0&1 are TXD0 and RXD0. To use
+// the serial, we need to disable the GPIO functions on them.
+#define REG_GPIO_SERIAL REG_GPIO_E
+#define REG_GPIO_SERIAL_MASK 0x3
+
+// Check flag consistency
+#if (SERRF_PARITYERROR != REG_SCI_SR_PF) || \
+ (SERRF_RXSROVERRUN != REG_SCI_SR_OR) || \
+ (SERRF_FRAMEERROR != REG_SCI_SR_FE) || \
+ (SERRF_NOISEERROR != REG_SCI_SR_NF)
+ #error error flags do not match with register bits
+#endif
+
+struct SCI
+{
+ struct SerialHardware hw;
+ struct Serial* serial;
+ volatile struct REG_SCI_STRUCT* regs;
+ uint16_t irq_tx;
+ uint16_t irq_rx;
+};
+
+
+static inline void enable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
+{
+ regs->CR |= REG_SCI_CR_TEIE | REG_SCI_CR_TIIE;
+}
+
+static inline void enable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
+{
+ regs->CR |= REG_SCI_CR_RIE | REG_SCI_CR_REIE;
+}
+
+static inline void disable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
+{
+ regs->CR &= ~(REG_SCI_CR_TEIE | REG_SCI_CR_TIIE);
+}
+
+static inline void disable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
+{
+ regs->CR &= ~(REG_SCI_CR_RIE | REG_SCI_CR_REIE);
+}
+
+static inline void disable_tx_irq(struct SerialHardware* _hw)
+{
+ struct SCI* hw = (struct SCI*)_hw;
+ volatile struct REG_SCI_STRUCT* regs = hw->regs;
+
+ disable_tx_irq_bare(regs);
+}
+
+static inline void enable_tx_irq(struct SerialHardware* _hw)
+{
+ struct SCI* hw = (struct SCI*)_hw;
+ volatile struct REG_SCI_STRUCT* regs = hw->regs;
+
+ enable_tx_irq_bare(regs);
+}
+
+static inline void enable_rx_irq(struct SerialHardware* _hw)
+{
+ struct SCI* hw = (struct SCI*)_hw;
+ volatile struct REG_SCI_STRUCT* regs = hw->regs;
+
+ enable_rx_irq_bare(regs);
+}
+
+INLINE void tx_isr(struct SCI *hw)
+{
+ volatile struct REG_SCI_STRUCT* regs = hw->regs;
+
+ if (fifo_isempty(&hw->serial->txfifo))
+ disable_tx_irq_bare(regs);
+ else
+ {
+ // Clear transmitter flags before sending data
+ (void)regs->SR;
+ regs->DR = fifo_pop(&hw->serial->txfifo);
+ }
+}
+
+INLINE void rx_isr(struct SCI *hw)
+{
+ volatile struct REG_SCI_STRUCT* regs = hw->regs;
+
+ hw->serial->status |= regs->SR & (SERRF_PARITYERROR |
+ SERRF_RXSROVERRUN |
+ SERRF_FRAMEERROR |
+ SERRF_NOISEERROR);
+
+ if (fifo_isfull(&hw->serial->rxfifo))
+ hw->serial->status |= SERRF_RXFIFOOVERRUN;
+ else
+ fifo_push(&hw->serial->rxfifo, regs->DR);
+
+ // Writing anything to the status register clear the
+ // error bits.
+ regs->SR = 0;
+}
+
+static void init(struct SerialHardware* _hw, struct Serial* ser)
+{
+ struct SCI* hw = (struct SCI*)_hw;
+ volatile struct REG_SCI_STRUCT* regs = hw->regs;
+
+ // Clear status register (IRQ/status flags)
+ (void)regs->SR;
+ regs->SR = 0;
+
+ // Clear data register
+ (void)regs->DR;
+
+ // Set priorities for both IRQs
+ irq_setpriority(hw->irq_tx, IRQ_PRIORITY_SCI_TX);
+ irq_setpriority(hw->irq_rx, IRQ_PRIORITY_SCI_RX);
+
+ // Activate the RX error interrupts, and RX/TX transmissions
+ regs->CR = REG_SCI_CR_TE | REG_SCI_CR_RE;
+ enable_rx_irq_bare(regs);
+
+ // Disable GPIO pins for TX and RX lines
+ REG_GPIO_SERIAL->PER |= REG_GPIO_SERIAL_MASK;
+
+ hw->serial = ser;
+}
+
+static void cleanup(struct SerialHardware* _hw)
+{
+ // TODO!
+ ASSERT(0);
+}
+
+static void setbaudrate(struct SerialHardware* _hw, unsigned long rate)
+{
+ struct SCI* hw = (struct SCI*)_hw;
+
+ // SCI has an internal 16x divider on the input clock, which comes
+ // from the IPbus (see the scheme in user manual, 12.7.3). We apply
+ // it to calculate the period to store in the register.
+ hw->regs->BR = (IPBUS_FREQ + rate * 8ul) / (rate * 16ul);
+}
+
+static void setparity(struct SerialHardware* _hw, int parity)
+{
+ // ???
+ ASSERT(0);
+}
+
+
+static const struct SerialHardwareVT SCI_VT =
+{
+ .init = init,
+ .cleanup = cleanup,
+ .setbaudrate = setbaudrate,
+ .setparity = setparity,
+ .enabletxirq = enable_tx_irq,
+};
+
+static struct SCI SCIDescs[2] =
+{
+ {
+ .hw = { .table = &SCI_VT },
+ .regs = ®_SCI[0],
+ .irq_rx = IRQ_SCI0_RECEIVER_FULL,
+ .irq_tx = IRQ_SCI0_TRANSMITTER_READY,
+ },
+
+ {
+ .hw = { .table = &SCI_VT },
+ .regs = ®_SCI[1],
+ .irq_rx = IRQ_SCI1_RECEIVER_FULL,
+ .irq_tx = IRQ_SCI1_TRANSMITTER_READY,
+ },
+};
+
+
+
+void ser_hw_tx_isr_0(void);
+void ser_hw_tx_isr_0(void)
+{
+#pragma interrupt warn
+ tx_isr(&SCIDescs[0]);
+}
+
+void ser_hw_rx_isr_0(void);
+void ser_hw_rx_isr_0(void)
+{
+#pragma interrupt warn
+ rx_isr(&SCIDescs[0]);
+}
+
+void ser_hw_tx_isr_1(void);
+void ser_hw_tx_isr_1(void)
+{
+#pragma interrupt warn
+ tx_isr(&SCIDescs[1]);
+}
+
+void ser_hw_rx_isr_1(void);
+void ser_hw_rx_isr_1(void)
+{
+#pragma interrupt warn
+ rx_isr(&SCIDescs[1]);
+}
+
+struct SerialHardware* ser_hw_getdesc(int unit)
+{
+ ASSERT(unit < 2);
+ return &SCIDescs[unit].hw;
+}
--- /dev/null
+/**
+ * \file
+ * Copyright (C) 2000 Bernardo Innocenti <bernie@codewiz.org>
+ * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief CPU specific serial I/O driver
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.1 2003/11/20 22:30:21 aleph
+ * Add serial driver
+ *
+ */
+
+#include "hw.h"
+#include "serhw.h"
+
+#define SER_HW_ENABLE_TX \
+ DISABLE_INTS; \
+ if (!ser_sending) \
+ { \
+ ser_sending = true; \
+ (INT_PEND1 |= INT1F_TI) \
+ } \
+ ENABLE_INTS;
+
+static volatile bool ser_sending;
+
+// Serial TX intr
+INTERRUPT(0x30) void TI_interrupt(void)
+{
+ if (CANT_SEND)
+ {
+ ser_sending = false;
+ return;
+ }
+
+ /* Can we send two bytes at the same time? */
+ if (SP_STAT & SPSF_TX_EMPTY)
+ {
+ SBUF = fifo_pop(&ser_txfifo);
+
+ if (CANT_SEND)
+ {
+ ser_sending = false;
+ return;
+ }
+ }
+
+ SBUF = fifo_pop(&ser_txfifo);
+}
+
+INTERRUPT(0x32) void RI_interrupt(void)
+{
+ ser_status |= SP_STAT &
+ (SPSF_OVERRUN_ERROR | SPSF_PARITY_ERROR | SPSF_FRAMING_ERROR);
+ if (fifo_isfull(&ser_rxfifo))
+ ser_status |= SERRF_RXFIFOOVERRUN;
+ else
+ fifo_push(&ser_rxfifo, SBUF);
+}
+
+static void ser_setbaudrate(unsigned long rate)
+{
+ // Calcola il periodo per la generazione del baud rate richiesto
+ uint16_t baud = (uint16_t)(((CLOCK_FREQ / 16) / rate) - 1) | 0x8000;
+ BAUD_RATE = (uint8_t)baud;
+ BAUD_RATE = (uint8_t)(baud >> 8);
+}
+
+static void ser_hw_init(void)
+{
+ // Inizializza la porta seriale
+ SP_CON = SPCF_RECEIVE_ENABLE | SPCF_MODE1;
+ ioc1_img |= IOC1F_TXD_SEL | IOC1F_EXTINT_SRC;
+ IOC1 = ioc1_img;
+
+ // Svuota il buffer di ricezione
+ {
+ uint8_t dummy = SBUF;
+ }
+
+ // Abilita gli interrupt
+ INT_MASK1 |= INT1F_TI | INT1F_RI;
+}
+
--- /dev/null
+/**
+ * \file
+ * Copyright (C) 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ *
+ * \brief Hardware dependent serial driver (interface)
+ *
+ * \version $Id$
+ *
+ * \author Stefano Fedrigo <aleph@develer.com>
+ * \author Giovanni Bajo <rasky@develer.com>
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:10:11 bernie
+ * Import drv/ modules.
+ *
+ * Revision 1.1 2004/05/14 12:47:26 rasky
+ * Importato nuovo supporto seriale per AVR da Stefano
+ *
+ * Revision 1.3 2004/05/08 13:59:08 aleph
+ * Fix header guard
+ *
+ * Revision 1.2 2004/05/08 13:58:36 aleph
+ * Add log comment
+ *
+ */
+
+#ifndef _DRV_SER_P_H
+#define _DRV_SER_P_H
+
+struct SerialHardware;
+struct Serial;
+
+struct SerialHardwareVT
+{
+ void (*init)(struct SerialHardware* ctx, struct Serial* ser);
+ void (*cleanup)(struct SerialHardware* ctx);
+ void (*setbaudrate)(struct SerialHardware* ctx, unsigned long rate);
+ void (*setparity)(struct SerialHardware* ctx, int parity);
+ void (*enabletxirq)(struct SerialHardware* ctx);
+};
+
+struct SerialHardware
+{
+ const struct SerialHardwareVT* table;
+};
+
+struct SerialHardware* ser_hw_getdesc(int unit);
+
+#endif // _DRV_SER_P_H