This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
AT91 serial driver patch
- From: Thomas Koeller <thomas dot koeller at baslerweb dot com>
- To: ecos-patches at sources dot redhat dot com
- Date: Fri, 7 Nov 2003 15:49:59 +0100
- Subject: AT91 serial driver patch
- Organization: Basler AG
This is an updated version of my previously submitted patch.
It has a configuration option to set the number of data bytes
that must be received before an interrrupt is generated. Setting
this to 1 will give the behavior of my original patch, while by
setting it to anything else, the behavior of Laurent's patch
can be achieved.
diff -ru packages-orig/devs/serial/arm/at91/current/ChangeLog packages/devs/serial/arm/at91/current/ChangeLog
--- packages-orig/devs/serial/arm/at91/current/ChangeLog 2003-10-17 11:55:44.000000000 +0200
+++ packages/devs/serial/arm/at91/current/ChangeLog 2003-11-07 15:31:44.000000000 +0100
@@ -1,3 +1,9 @@
+2003-11-07 Thomas Koeller <thomas.koeller@baslerweb.com>
+
+ * src/at91_serial.c:
+ * cdl/ser_arm_at91.cdl: Major rewrite to achieve reliable
+ operation at higher baud rates.
+
2003-10-16 Nick Garnett <nickg@balti.calivar.com>
* src/at91_serial.c (at91_serial_config_port): Added update of
diff -ru packages-orig/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl packages/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl
--- packages-orig/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl 2003-02-24 15:12:18.000000000 +0100
+++ packages/devs/serial/arm/at91/current/cdl/ser_arm_at91.cdl 2003-11-06 12:41:32.000000000 +0100
@@ -41,7 +41,7 @@
######DESCRIPTIONBEGIN####
#
# Author(s): gthomas
-# Contributors:
+# Contributors: tkoeller
# Date: 2001-07-24
#
#####DESCRIPTIONEND####
@@ -61,7 +61,9 @@
include_files ; # none _exported_ whatsoever
description "
This option enables the serial device drivers for the
- Atmel AT91 (EB40)."
+ Atmel AT91."
+
+ implements CYGINT_IO_SERIAL_BLOCK_TRANSFER
compile -library=libextras.a at91_serial.c
@@ -109,6 +111,21 @@
This option specifies the size of the internal buffers used
for the Atmel AT91 port 0."
}
+
+ cdl_option CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE {
+ display "Receive data chunk size"
+ flavor data
+ legal_values 1 to 65519
+ default_value 1
+ description "
+ This parameter can be used to reduce the number of interrupts
+ that must be processed by the driver. An interrupt will only
+ be generated if either this many data bytes have been received
+ or the receiver has been idle for some time. This reduces
+ overall system load at the expense of making the driver less
+ responsive and using slightly more memory for buffering data.
+ Setting this parameter to 1 will give standard behavior."
+ }
}
cdl_component CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1 {
@@ -149,6 +166,21 @@
This option specifies the size of the internal buffers used
for the Atmel AT91 port 1."
}
+
+ cdl_option CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE {
+ display "Receive data chunk size"
+ flavor data
+ legal_values 1 to 65519
+ default_value 1
+ description "
+ This parameter can be used to reduce the number of interrupts
+ that must be processed by the driver. An interrupt will only
+ be generated if either this many data bytes have been received
+ or the receiver has been idle for some time. This reduces
+ overall system load at the expense of making the driver less
+ responsive and using slightly more memory for buffering data.
+ Setting this parameter to 1 will give standard behavior."
+ }
}
cdl_component CYGPKG_IO_SERIAL_ARM_AT91_OPTIONS {
diff -ru packages-orig/devs/serial/arm/at91/current/src/at91_serial.c packages/devs/serial/arm/at91/current/src/at91_serial.c
--- packages-orig/devs/serial/arm/at91/current/src/at91_serial.c 2003-10-17 11:55:45.000000000 +0200
+++ packages/devs/serial/arm/at91/current/src/at91_serial.c 2003-11-07 12:09:27.000000000 +0100
@@ -1,6 +1,6 @@
//==========================================================================
//
-// io/serial/arm/at91/at91_serial.c
+// devs/serial/arm/at91/at91_serial.c
//
// Atmel AT91/EB40 Serial I/O Interface Module (interrupt driven)
//
@@ -41,7 +41,7 @@
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
-// Contributors: gthomas
+// Contributors: gthomas, tkoeller
// Date: 2001-07-24
// Purpose: Atmel AT91/EB40 Serial I/O module (interrupt driven version)
// Description:
@@ -50,6 +50,8 @@
//
//==========================================================================
+#include <pkgconf/hal.h>
+#include <pkgconf/infra.h>
#include <pkgconf/system.h>
#include <pkgconf/io_serial.h>
#include <pkgconf/io.h>
@@ -61,24 +63,43 @@
#include <cyg/io/devtab.h>
#include <cyg/io/serial.h>
#include <cyg/infra/diag.h>
+#include <cyg/infra/cyg_type.h>
+
+externC void * memcpy( void *, const void *, size_t );
#ifdef CYGPKG_IO_SERIAL_ARM_AT91
#include "at91_serial.h"
+#define RCVBUF_EXTRA 16
+#define RCV_TIMEOUT 10
+
+#define SIFLG_NONE 0x00
+#define SIFLG_TX_READY 0x01
+#define SIFLG_XMIT_BUSY 0x02
+#define SIFLG_XMIT_CONTINUE 0x04
+
typedef struct at91_serial_info {
CYG_ADDRWORD base;
CYG_WORD int_num;
+ CYG_WORD stat;
+ int transmit_size;
cyg_interrupt serial_interrupt;
cyg_handle_t serial_interrupt_handle;
+ cyg_uint8 *rcv_buffer[2];
+ cyg_uint16 rcv_chunk_size;
+ cyg_uint8 curbuf;
+ cyg_uint8 flags;
} at91_serial_info;
static bool at91_serial_init(struct cyg_devtab_entry *tab);
-static bool at91_serial_putc(serial_channel *chan, unsigned char c);
+static bool at91_serial_putc_interrupt(serial_channel *chan, unsigned char c);
+static bool at91_serial_putc_polled(serial_channel *chan, unsigned char c);
static Cyg_ErrNo at91_serial_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name);
-static unsigned char at91_serial_getc(serial_channel *chan);
+static unsigned char at91_serial_getc_interrupt(serial_channel *chan);
+static unsigned char at91_serial_getc_polled(serial_channel *chan);
static Cyg_ErrNo at91_serial_set_config(serial_channel *chan, cyg_uint32 key,
const void *xbuf, cyg_uint32 *len);
static void at91_serial_start_xmit(serial_channel *chan);
@@ -87,23 +108,44 @@
static cyg_uint32 at91_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void at91_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
-static SERIAL_FUNS(at91_serial_funs,
- at91_serial_putc,
- at91_serial_getc,
+#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE > 0) \
+ || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE > 0)
+static SERIAL_FUNS(at91_serial_funs_interrupt,
+ at91_serial_putc_interrupt,
+ at91_serial_getc_interrupt,
at91_serial_set_config,
at91_serial_start_xmit,
at91_serial_stop_xmit
);
+#endif
+
+#if (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE == 0) \
+ || (defined(CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1) && CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE == 0)
+static SERIAL_FUNS(at91_serial_funs_polled,
+ at91_serial_putc_polled,
+ at91_serial_getc_polled,
+ at91_serial_set_config,
+ at91_serial_start_xmit,
+ at91_serial_stop_xmit
+ );
+#endif
#ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL0
-static at91_serial_info at91_serial_info0 = {(CYG_ADDRWORD)AT91_USART0,
- CYGNUM_HAL_INTERRUPT_USART0};
+static cyg_uint8 at91_serial_rcv_buffer_0
+ [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE + RCVBUF_EXTRA];
+static at91_serial_info at91_serial_info0 = {
+ base : (CYG_ADDRWORD) AT91_USART0,
+ int_num : CYGNUM_HAL_INTERRUPT_USART0,
+ rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_RCV_CHUNK_SIZE,
+ rcv_buffer : {at91_serial_rcv_buffer_0[0], at91_serial_rcv_buffer_0[1]}
+};
+
#if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE > 0
static unsigned char at91_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE];
static unsigned char at91_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BUFSIZE];
static SERIAL_CHANNEL_USING_INTERRUPTS(at91_serial_channel0,
- at91_serial_funs,
+ at91_serial_funs_interrupt,
at91_serial_info0,
CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BAUD),
CYG_SERIAL_STOP_DEFAULT,
@@ -115,7 +157,7 @@
);
#else
static SERIAL_CHANNEL(at91_serial_channel0,
- at91_serial_funs,
+ at91_serial_funs_polled,
at91_serial_info0,
CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL0_BAUD),
CYG_SERIAL_STOP_DEFAULT,
@@ -136,14 +178,21 @@
#endif // CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1
#ifdef CYGPKG_IO_SERIAL_ARM_AT91_SERIAL1
-static at91_serial_info at91_serial_info1 = {(CYG_ADDRWORD)AT91_USART1,
- CYGNUM_HAL_INTERRUPT_USART1};
+static cyg_uint8 at91_serial_rcv_buffer_1
+ [2][CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE + RCVBUF_EXTRA];
+static at91_serial_info at91_serial_info1 = {
+ base : (CYG_ADDRWORD) AT91_USART1,
+ int_num : CYGNUM_HAL_INTERRUPT_USART1,
+ rcv_chunk_size : CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_RCV_CHUNK_SIZE,
+ rcv_buffer : {at91_serial_rcv_buffer_1[0], at91_serial_rcv_buffer_1[1]}
+};
+
#if CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE > 0
static unsigned char at91_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE];
static unsigned char at91_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BUFSIZE];
static SERIAL_CHANNEL_USING_INTERRUPTS(at91_serial_channel1,
- at91_serial_funs,
+ at91_serial_funs_interrupt,
at91_serial_info1,
CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BAUD),
CYG_SERIAL_STOP_DEFAULT,
@@ -155,7 +204,7 @@
);
#else
static SERIAL_CHANNEL(at91_serial_channel1,
- at91_serial_funs,
+ at91_serial_funs_polled,
at91_serial_info1,
CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_AT91_SERIAL1_BAUD),
CYG_SERIAL_STOP_DEFAULT,
@@ -179,8 +228,8 @@
static bool
at91_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
{
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
- CYG_ADDRWORD base = at91_chan->base;
+ at91_serial_info * const at91_chan = (at91_serial_info *)chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
cyg_uint32 parity = select_parity[new_config->parity];
cyg_uint32 word_length = select_word_length[new_config->word_length-CYGNUM_SERIAL_WORD_LENGTH_5];
cyg_uint32 stop_bits = select_stop_bits[new_config->stop];
@@ -192,22 +241,29 @@
}
// Reset device
- HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxRESET | AT91_US_CR_TxRESET);
+ HAL_WRITE_UINT32(base + AT91_US_CR, AT91_US_CR_RxRESET | AT91_US_CR_TxRESET);
// Configuration
- HAL_WRITE_UINT32(base+AT91_US_MR, parity | word_length | stop_bits);
+ HAL_WRITE_UINT32(base + AT91_US_MR, parity | word_length | stop_bits);
// Baud rate
- HAL_WRITE_UINT32(base+AT91_US_BRG, AT91_US_BAUD(select_baud[new_config->baud]));
+ HAL_WRITE_UINT32(base + AT91_US_BRG, AT91_US_BAUD(select_baud[new_config->baud]));
// Disable all interrupts
- HAL_WRITE_UINT32(base+AT91_US_IDR, 0xFFFFFFFF);
+ HAL_WRITE_UINT32(base + AT91_US_IDR, 0xFFFFFFFF);
- // Enable Rx interrupts
- HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_RxRDY);
+ // Start receiver
+ at91_chan->curbuf = 0;
+ HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[0]);
+ HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT);
+ HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT);
+ HAL_WRITE_UINT32(base + AT91_US_RCR, at91_chan->rcv_chunk_size);
// Enable RX and TX
- HAL_WRITE_UINT32(base+AT91_US_CR, AT91_US_CR_RxENAB | AT91_US_CR_TxENAB);
+ HAL_WRITE_UINT32(
+ base + AT91_US_CR,
+ AT91_US_CR_RxENAB | AT91_US_CR_TxENAB | AT91_US_CR_RSTATUS | AT91_US_CR_STTTO
+ );
if (new_config != &chan->config) {
chan->config = *new_config;
@@ -220,13 +276,16 @@
static bool
at91_serial_init(struct cyg_devtab_entry *tab)
{
- serial_channel *chan = (serial_channel *)tab->priv;
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
+ serial_channel * const chan = (serial_channel *) tab->priv;
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
int res;
#ifdef CYGDBG_IO_INIT
diag_printf("AT91 SERIAL init - dev: %x.%d\n", at91_chan->base, at91_chan->int_num);
#endif
+ at91_chan->curbuf = 0;
+ at91_chan->flags = SIFLG_NONE;
+ at91_chan->stat = 0;
(chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
if (chan->out_cbuf.len != 0) {
cyg_drv_interrupt_create(at91_chan->int_num,
@@ -249,7 +308,7 @@
struct cyg_devtab_entry *sub_tab,
const char *name)
{
- serial_channel *chan = (serial_channel *)(*tab)->priv;
+ serial_channel * const chan = (serial_channel *) (*tab)->priv;
(chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
return ENOERR;
@@ -258,41 +317,63 @@
// Send a character to the device output buffer.
// Return 'true' if character is sent to device
static bool
-at91_serial_putc(serial_channel *chan, unsigned char c)
+at91_serial_putc_interrupt(serial_channel *chan, unsigned char c)
{
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
- CYG_ADDRWORD base = at91_chan->base;
- cyg_uint32 stat;
-
- // Check status
- HAL_READ_UINT32(base+AT91_US_CSR, stat);
-
- // Send character if possible
- if ((stat & AT91_US_CSR_TxRDY) != 0) {
- HAL_WRITE_UINT32(base+AT91_US_THR, c);
- return true;
- } else {
- return false; // Couldn't send, tx was busy
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const bool res = (at91_chan->flags & SIFLG_XMIT_BUSY) == 0;
+
+ if (res) {
+ const CYG_ADDRWORD base = at91_chan->base;
+ HAL_WRITE_UINT32(base + AT91_US_THR, c);
+ at91_chan->flags |= SIFLG_XMIT_BUSY;
}
+ return res;
+}
+
+static bool
+at91_serial_putc_polled(serial_channel *chan, unsigned char c)
+{
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ CYG_WORD32 w;
+
+ while (HAL_READ_UINT32(base + AT91_US_CSR, w), (w & AT91_US_IER_TxRDY) == 0)
+ CYG_EMPTY_STATEMENT;
+ HAL_WRITE_UINT32(base + AT91_US_THR, c);
+ return true;
}
// Fetch a character from the device input buffer, waiting if necessary
static unsigned char
-at91_serial_getc(serial_channel *chan)
+at91_serial_getc_interrupt(serial_channel *chan)
{
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
- CYG_ADDRWORD base = at91_chan->base;
- cyg_uint32 c;
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ CYG_WORD32 c;
// Read data
- HAL_READ_UINT32(base+AT91_US_RHR, c);
- return c;
+ HAL_READ_UINT32(base + AT91_US_RHR, c);
+ return (unsigned char) c;
+}
+
+static unsigned char
+at91_serial_getc_polled(serial_channel *chan)
+{
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ CYG_WORD32 c;
+
+ while (HAL_READ_UINT32(base + AT91_US_CSR, c), (c & AT91_US_IER_RxRDY) == 0)
+ CYG_EMPTY_STATEMENT;
+ // Read data
+ HAL_READ_UINT32(base + AT91_US_RHR, c);
+ return (unsigned char) c;
}
// Set up the device characteristics; baud rate, etc.
static Cyg_ErrNo
at91_serial_set_config(serial_channel *chan, cyg_uint32 key,
- const void *xbuf, cyg_uint32 *len)
+ const void *xbuf, cyg_uint32 *len)
{
switch (key) {
case CYG_IO_SET_CONFIG_SERIAL_INFO:
@@ -316,57 +397,167 @@
static void
at91_serial_start_xmit(serial_channel *chan)
{
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
- CYG_ADDRWORD base = at91_chan->base;
-
- (chan->callbacks->xmt_char)(chan); // Kick transmitter (if necessary)
- HAL_WRITE_UINT32(base+AT91_US_IER, AT91_US_IER_TxRDY);
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ unsigned char * chars;
+ xmt_req_reply_t res;
+
+ cyg_drv_dsr_lock();
+ if ((at91_chan->flags & SIFLG_XMIT_CONTINUE) == 0) {
+ res = (chan->callbacks->data_xmt_req)(chan, 0xffff, &at91_chan->transmit_size, &chars);
+ switch (res)
+ {
+ case CYG_XMT_OK:
+ HAL_WRITE_UINT32(base + AT91_US_TPR, (CYG_WORD32) chars);
+ HAL_WRITE_UINT32(base + AT91_US_TCR, at91_chan->transmit_size);
+ at91_chan->flags |= SIFLG_XMIT_CONTINUE;
+ HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDTX);
+ break;
+ case CYG_XMT_DISABLED:
+ (chan->callbacks->xmt_char)(chan); // Kick transmitter
+ at91_chan->flags |= SIFLG_XMIT_CONTINUE;
+ HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_TxRDY);
+ break;
+ default:
+ // No data or unknown error - can't do anything about it
+ break;
+ }
+ }
+ cyg_drv_dsr_unlock();
}
// Disable the transmitter on the device
static void
at91_serial_stop_xmit(serial_channel *chan)
{
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
- CYG_ADDRWORD base = at91_chan->base;
-
- HAL_WRITE_UINT32(base+AT91_US_IDR, AT91_US_IER_TxRDY);
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_TxRDY | AT91_US_IER_ENDTX);
+ at91_chan->flags &= ~SIFLG_XMIT_CONTINUE;
}
// Serial I/O - low level interrupt handler (ISR)
static cyg_uint32
at91_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
- cyg_drv_interrupt_mask(vector);
+ serial_channel * const chan = (serial_channel *) data;
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ CYG_WORD32 stat, mask;
+
+ HAL_READ_UINT32(base + AT91_US_CSR, stat);
+ HAL_READ_UINT32(base + AT91_US_IMR, mask);
+ stat &= mask;
+
+ if (stat & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) {
+ cyg_uint32 x;
+ HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT);
+ HAL_WRITE_UINT32(base + AT91_US_RCR, 0);
+ HAL_WRITE_UINT32(base + AT91_US_RTO, 0);
+ HAL_READ_UINT32(base + AT91_US_RPR, x);
+ HAL_WRITE_UINT32(
+ base + AT91_US_RCR,
+ (CYG_ADDRESS) at91_chan->rcv_buffer[at91_chan->curbuf]
+ + at91_chan->rcv_chunk_size + RCVBUF_EXTRA - x
+ );
+ }
+
+ if (stat & (AT91_US_IER_TxRDY | AT91_US_IER_ENDTX))
+ HAL_WRITE_UINT32(base + AT91_US_IDR, AT91_US_IER_TxRDY | AT91_US_IER_ENDTX);
+
+ at91_chan->stat |= stat;
cyg_drv_interrupt_acknowledge(vector);
- return (CYG_ISR_CALL_DSR|CYG_ISR_HANDLED); // Cause DSR to be run
+ return CYG_ISR_CALL_DSR;
}
// Serial I/O - high level interrupt handler (DSR)
static void
at91_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
- serial_channel *chan = (serial_channel *)data;
- at91_serial_info *at91_chan = (at91_serial_info *)chan->dev_priv;
- CYG_ADDRWORD base = at91_chan->base;
- cyg_uint32 stat, c;
-
- // Check status
- HAL_READ_UINT32(base+AT91_US_IMR, stat);
+ serial_channel * const chan = (serial_channel *) data;
+ at91_serial_info * const at91_chan = (at91_serial_info *) chan->dev_priv;
+ const CYG_ADDRWORD base = at91_chan->base;
+ CYG_WORD32 stat;
+
+ cyg_drv_interrupt_mask(vector);
+ stat = at91_chan->stat;
+ at91_chan->stat = 0;
+ cyg_drv_interrupt_unmask(vector);
- if (stat & (AT91_US_IER_TxRDY)) {
+ if (stat & AT91_US_IER_TxRDY) {
+ at91_chan->flags &= ~SIFLG_XMIT_BUSY;
(chan->callbacks->xmt_char)(chan);
+ if (at91_chan->flags & SIFLG_XMIT_CONTINUE)
+ HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_TxRDY);
}
- if (stat & (AT91_US_IER_RxRDY)) {
- while (true) {
- HAL_READ_UINT32(base+AT91_US_CSR, stat);
- if ((stat & AT91_US_CSR_RxRDY) == 0) {
- break;
+
+ if (stat & AT91_US_IER_ENDTX) {
+ (chan->callbacks->data_xmt_done)(chan, at91_chan->transmit_size);
+ if (at91_chan->flags & SIFLG_XMIT_CONTINUE) {
+ unsigned char * chars;
+ xmt_req_reply_t res;
+
+ res = (chan->callbacks->data_xmt_req)(chan, 0xffff, &at91_chan->transmit_size, &chars);
+
+ switch (res)
+ {
+ case CYG_XMT_OK:
+ HAL_WRITE_UINT32(base + AT91_US_TPR, (CYG_WORD32) chars);
+ HAL_WRITE_UINT32(base + AT91_US_TCR, at91_chan->transmit_size);
+ at91_chan->flags |= SIFLG_XMIT_CONTINUE;
+ HAL_WRITE_UINT32(base + AT91_US_IER, AT91_US_IER_ENDTX);
+ break;
+ default:
+ // No data or unknown error - can't do anything about it
+ // CYG_XMT_DISABLED should not happen here!
+ break;
+ }
+ }
+ }
+
+ if (stat & (AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT)) {
+ const cyg_uint8 cb = at91_chan->curbuf, nb = cb ^ 0x01;
+ const cyg_uint8 * p = at91_chan->rcv_buffer[cb], * end;
+
+ at91_chan->curbuf = nb;
+ HAL_WRITE_UINT32(base + AT91_US_RCR, 0);
+ HAL_READ_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) end);
+ HAL_WRITE_UINT32(base + AT91_US_RTO, RCV_TIMEOUT);
+ HAL_WRITE_UINT32(base + AT91_US_CR, AT91_US_CR_RSTATUS | AT91_US_CR_STTTO);
+ HAL_WRITE_UINT32(base + AT91_US_RPR, (CYG_ADDRESS) at91_chan->rcv_buffer[nb]);
+ HAL_WRITE_UINT32(base + AT91_US_RCR, at91_chan->rcv_chunk_size);
+ HAL_WRITE_UINT32(
+ base + AT91_US_IER,
+ AT91_US_IER_ENDRX | AT91_US_IER_TIMEOUT
+ );
+
+ while (p < end) {
+ rcv_req_reply_t res;
+ int space_avail;
+ unsigned char *space;
+
+ res = (chan->callbacks->data_rcv_req)(
+ chan,
+ end - at91_chan->rcv_buffer[cb],
+ &space_avail,
+ &space
+ );
+
+ switch (res)
+ {
+ case CYG_RCV_OK:
+ memcpy(space, p, space_avail);
+ (chan->callbacks->data_rcv_done)(chan, space_avail);
+ p += space_avail;
+ break;
+ case CYG_RCV_DISABLED:
+ (chan->callbacks->rcv_char)(chan, *p++);
+ break;
+ default:
+ // Buffer full or unknown error, can't do anything about it
+ break;
}
- HAL_READ_UINT32(base+AT91_US_RHR, c);
- (chan->callbacks->rcv_char)(chan, c);
}
}
- cyg_drv_interrupt_unmask(vector);
}
#endif
--
--------------------------------------------------
Thomas Koeller, Software Development
Basler Vision Technologies
An der Strusbek 60-62
22926 Ahrensburg
Germany
Tel +49 (4102) 463-162
Fax +49 (4102) 463-239
mailto:thomas.koeller@baslerweb.com
http://www.baslerweb.com
==============================