summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-10-29 15:47:15 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-10-29 15:47:15 -0600
commit3efbf10e85c862e7256fc107d5eb4277f8075f04 (patch)
treec0888e2aa2d59dd1ac513ca4e2685a65ffefe6b3
parent8bcb9dd25ee2794e367a0afa943ca2a6faf7fad9 (diff)
downloadnuttx-3efbf10e85c862e7256fc107d5eb4277f8075f04.tar.gz
nuttx-3efbf10e85c862e7256fc107d5eb4277f8075f04.tar.bz2
nuttx-3efbf10e85c862e7256fc107d5eb4277f8075f04.zip
Add support for SAM3/4 basic TERMIOS and flow control. There are issues with IFLOW control: PDC or DMAC support is required
-rw-r--r--nuttx/arch/arm/src/sam34/Kconfig10
-rw-r--r--nuttx/arch/arm/src/sam34/sam_serial.c226
2 files changed, 224 insertions, 12 deletions
diff --git a/nuttx/arch/arm/src/sam34/Kconfig b/nuttx/arch/arm/src/sam34/Kconfig
index fa4b7bd99..464bec6b7 100644
--- a/nuttx/arch/arm/src/sam34/Kconfig
+++ b/nuttx/arch/arm/src/sam34/Kconfig
@@ -583,12 +583,14 @@ config SAM34_UART0
default y
depends on ARCH_CHIP_SAM3U || ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4CM || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
select ARCH_HAVE_UART0
+ select ARCH_HAVE_SERIAL_TERMIOS
config SAM34_UART1
bool "UART 1"
default n
depends on ARCH_CHIP_SAM4CM || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
select ARCH_HAVE_UART1
+ select ARCH_HAVE_SERIAL_TERMIOS
config SAM34_UDP
bool "USB Device Full Speed (UDP)"
@@ -615,23 +617,27 @@ config SAM34_USART0
bool "USART 0"
default n
select ARCH_HAVE_USART0
+ select ARCH_HAVE_SERIAL_TERMIOS
config SAM34_USART1
bool "USART 1"
default n
select ARCH_HAVE_USART1
+ select ARCH_HAVE_SERIAL_TERMIOS
config SAM34_USART2
bool "USART 2"
default n
- select ARCH_HAVE_USART2
depends on ARCH_CHIP_SAM3U || ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4CM || ARCH_CHIP_SAM4L
+ select ARCH_HAVE_USART2
+ select ARCH_HAVE_SERIAL_TERMIOS
config SAM34_USART3
bool "USART 3"
default n
- select ARCH_HAVE_USART3
depends on ARCH_CHIP_SAM3U || ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4CM || ARCH_CHIP_SAM4L
+ select ARCH_HAVE_USART3
+ select ARCH_HAVE_SERIAL_TERMIOS
config SAM34_WDT
bool "Watchdog Timer (WDT)"
diff --git a/nuttx/arch/arm/src/sam34/sam_serial.c b/nuttx/arch/arm/src/sam34/sam_serial.c
index 6aec44070..b558ae7f9 100644
--- a/nuttx/arch/arm/src/sam34/sam_serial.c
+++ b/nuttx/arch/arm/src/sam34/sam_serial.c
@@ -48,6 +48,10 @@
#include <errno.h>
#include <debug.h>
+#ifdef CONFIG_SERIAL_TERMIOS
+# include <termios.h>
+#endif
+
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/serial/serial.h>
@@ -71,7 +75,7 @@
#endif
/****************************************************************************
- * Definitions
+ * Pre-processor Definitions
****************************************************************************/
/* Some sanity checks *******************************************************/
@@ -106,6 +110,17 @@
# define HAVE_USART
#endif
+/* Hardware flow control requires using the PDC or DMAC channel for reception */
+
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+# warning PDC or DMAC support is required for RTS hardware flow control
+# undef CONFIG_SERIAL_IFLOWCONTROL
+# undef CONFIG_USART0_IFLOWCONTROL
+# undef CONFIG_USART1_IFLOWCONTROL
+# undef CONFIG_USART2_IFLOWCONTROL
+# undef CONFIG_USART3_IFLOWCONTROL
+#endif
+
/* Is there a serial console? It could be on UART0-1 or USART0-3 */
#if defined(CONFIG_UART0_SERIAL_CONSOLE) && defined(CONFIG_SAM34_UART0)
@@ -339,13 +354,16 @@
struct up_dev_s
{
- uint32_t usartbase; /* Base address of USART registers */
- uint32_t baud; /* Configured baud */
- uint32_t sr; /* Saved status bits */
- uint8_t irq; /* IRQ associated with this USART */
- uint8_t parity; /* 0=none, 1=odd, 2=even */
- uint8_t bits; /* Number of bits (7 or 8) */
- bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */
+ const uint32_t usartbase; /* Base address of USART registers */
+ uint32_t baud; /* Configured baud */
+ uint32_t sr; /* Saved status bits */
+ uint8_t irq; /* IRQ associated with this USART */
+ uint8_t parity; /* 0=none, 1=odd, 2=even */
+ uint8_t bits; /* Number of bits (5-9) */
+ bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+ bool flowc; /* input flow control (RTS) enabled */
+#endif
};
/****************************************************************************
@@ -487,6 +505,9 @@ static struct up_dev_s g_usart0priv =
.parity = CONFIG_USART0_PARITY,
.bits = CONFIG_USART0_BITS,
.stopbits2 = CONFIG_USART0_2STOP,
+#if defined(CONFIG_USART0_OFLOWCONTROL) || defined(CONFIG_USART0_IFLOWCONTROL)
+ .flowc = true,
+#endif
};
static uart_dev_t g_usart0port =
@@ -517,6 +538,9 @@ static struct up_dev_s g_usart1priv =
.parity = CONFIG_USART1_PARITY,
.bits = CONFIG_USART1_BITS,
.stopbits2 = CONFIG_USART1_2STOP,
+#if defined(CONFIG_USART1_OFLOWCONTROL) || defined(CONFIG_USART1_IFLOWCONTROL)
+ .flowc = true,
+#endif
};
static uart_dev_t g_usart1port =
@@ -547,6 +571,9 @@ static struct up_dev_s g_usart2priv =
.parity = CONFIG_USART2_PARITY,
.bits = CONFIG_USART2_BITS,
.stopbits2 = CONFIG_USART2_2STOP,
+#if defined(CONFIG_USART2_OFLOWCONTROL) || defined(CONFIG_USART2_IFLOWCONTROL)
+ .flowc = true,
+#endif
};
static uart_dev_t g_usart2port =
@@ -577,6 +604,9 @@ static struct up_dev_s g_usart3priv =
.parity = CONFIG_USART3_PARITY,
.bits = CONFIG_USART3_BITS,
.stopbits2 = CONFIG_USART3_2STOP,
+#if defined(CONFIG_USART3_OFLOWCONTROL) || defined(CONFIG_USART3_IFLOWCONTROL)
+ .flowc = true,
+#endif
};
static uart_dev_t g_usart3port =
@@ -680,7 +710,28 @@ static int up_setup(struct uart_dev_s *dev)
* as the timing source
*/
- regval = (UART_MR_MODE_NORMAL | SAM_MR_USCLKS);
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+ /* "Setting the USART to operate with hardware handshaking is performed by
+ * writing the USART_MODE field in the Mode Register (US_MR) to the value
+ * 0x2. ... Using this mode requires using the PDC or DMAC channel for
+ * reception. The transmitter can handle hardware handshaking in any case."
+ */
+
+ if (priv->flowc)
+ {
+ /* Enable hardware flow control and MCK as the timing source */
+
+ regval = (UART_MR_MODE_HWHS | SAM_MR_USCLKS);
+ }
+ else
+#endif
+ {
+ /* Set up the mode register. Start with normal UART mode and the MCK
+ * as the timing source
+ */
+
+ regval = (UART_MR_MODE_NORMAL | SAM_MR_USCLKS);
+ }
/* OR in settings for the selected number of bits */
@@ -760,6 +811,7 @@ static int up_setup(struct uart_dev_s *dev)
up_serialout(priv, SAM_UART_CR_OFFSET, (UART_CR_RXEN|UART_CR_TXEN));
#endif
+
return OK;
}
@@ -959,7 +1011,7 @@ static int up_interrupt(int irq, void *context)
static int up_ioctl(struct file *filep, int cmd, unsigned long arg)
{
-#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
+#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT)
struct inode *inode = filep->f_inode;
struct uart_dev_s *dev = inode->i_private;
#endif
@@ -983,6 +1035,160 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg)
break;
#endif
+#ifdef CONFIG_SERIAL_TERMIOS
+ case TCGETS:
+ {
+ struct termios *termiosp = (struct termios*)arg;
+ struct up_dev_s *priv = (struct up_dev_s *)dev->priv;
+
+ if (!termiosp)
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Return baud */
+
+ cfsetispeed(termiosp, priv->baud);
+
+ /* Return parity */
+
+ termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) |
+ ((priv->parity == 1) ? PARODD : 0);
+
+ /* Return stop bits */
+
+ termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0;
+
+ /* Return flow control */
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+ termiosp->c_cflag |= (priv->flowc) ? (CCTS_OFLOW | CRTS_IFLOW): 0;
+#endif
+ /* Return number of bits */
+
+ switch (priv->bits)
+ {
+ case 5:
+ termiosp->c_cflag |= CS5;
+ break;
+
+ case 6:
+ termiosp->c_cflag |= CS6;
+ break;
+
+ case 7:
+ termiosp->c_cflag |= CS7;
+ break;
+
+ default:
+ case 8:
+ termiosp->c_cflag |= CS8;
+ break;
+
+ case 9:
+ termiosp->c_cflag |= CS8 /* CS9 */;
+ break;
+ }
+ }
+ break;
+
+ case TCSETS:
+ {
+ struct termios *termiosp = (struct termios*)arg;
+ struct up_dev_s *priv = (struct up_dev_s *)dev->priv;
+ uint32_t baud;
+ uint8_t parity;
+ uint8_t nbits;
+ bool stop2;
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+ bool flowc;
+#endif
+
+ if (!termiosp)
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Decode baud. */
+
+ ret = OK;
+ baud = cfgetispeed(termiosp);
+
+ /* Decode number of bits */
+
+ switch (priv->bits)
+ {
+ case CS5:
+ nbits = 5;
+ break;
+
+ case CS6:
+ nbits = 6;
+ break;
+
+ case CS7:
+ nbits = 7;
+ break;
+
+ case CS8:
+ nbits = 8;
+ break;
+#if 0
+ case CS9:
+ nbits = 9;
+ break;
+#endif
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Decode parity */
+
+ if (termiosp->c_cflag & PARENB)
+ {
+ parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
+ }
+ else
+ {
+ parity = 0;
+ }
+
+ /* Decode stop bits */
+
+ stop2 = (termiosp->c_cflag & CSTOPB) != 0;
+
+ /* Decode flow control */
+
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+ flowc = (termiosp->c_cflag & (CCTS_OFLOW | CRTS_IFLOW)) != 0;
+#endif
+ /* Verify that all settings are valid before committing */
+
+ if (ret == OK)
+ {
+ /* Commit */
+
+ priv->baud = baud;
+ priv->parity = parity;
+ priv->bits = nbits;
+ priv->stopbits2 = stop2;
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+ priv->flowc = flowc;
+#endif
+
+ /* effect the changes immediately - note that we do not
+ * implement TCSADRAIN / TCSAFLUSH
+ */
+
+ up_setup(dev);
+ }
+ }
+ break;
+#endif /* CONFIG_SERIAL_TERMIOS */
+
default:
ret = -ENOTTY;
break;