diff --git a/arch/c5471/include/serial.h b/arch/c5471/include/serial.h index 2202a6450fb3bbaf2e74fbb50ddf193c7705460f..c10fa426913ea7021c20394f8611f4681f60c739 100644 --- a/arch/c5471/include/serial.h +++ b/arch/c5471/include/serial.h @@ -51,11 +51,7 @@ #define TIOCSERGSTRUCT 0x5403 /* Get up_dev_t for port */ /************************************************************ - * Private Data - ************************************************************/ - -/************************************************************ - * Private Functions + * Public Data ************************************************************/ /************************************************************ diff --git a/arch/c5471/src/up_serial.c b/arch/c5471/src/up_serial.c index 4b70ca4e227b2d32bec8cf6f1b9dee2c4158e359..4651b1242c672f82a50ec9dfc6e51fb14e53f57c 100644 --- a/arch/c5471/src/up_serial.c +++ b/arch/c5471/src/up_serial.c @@ -46,7 +46,7 @@ #include <debug.h> #include <nuttx/irq.h> #include <nuttx/arch.h> -#include <nuttx/fs.h> +#include <nuttx/serial.h> #include <arch/serial.h> #include "c5471.h" #include "os_internal.h" @@ -66,30 +66,19 @@ * Private Types ************************************************************/ -/* This is the internal structure for each serial port's state. */ - struct uart_regs_s { uint32 ier; uint32 lcr; uint32 fcr; +#ifdef CONFIG_UART_HWFLOWCONTROL uint32 efr; uint32 tcr; -}; - -struct uart_buffer_s -{ - sem_t sem; /* Used to control exclusive access to the buffer */ - sint16 head; /* Index to the head [IN] index in the buffer */ - sint16 tail; /* Index to the tail [OUT] index in the buffer */ - sint16 size; /* The allocated size of the buffer */ - char *buffer; /* Pointer to the allocated buffer memory */ +#endif }; struct up_dev_s { - int open_count; /* The number of times - * the device has been opened */ unsigned int uartbase; /* Base address of UART * registers */ unsigned int baud_base; /* Base baud for conversions */ @@ -105,54 +94,57 @@ struct up_dev_s #endif boolean stopbits2; /* TRUE: Configure with 2 * stop bits instead of 1 */ - boolean xmitwaiting; /* TRUE: User is waiting - * for space in xmit.buffer */ - boolean recvwaiting; /* TRUE: User is waiting - * for space in recv.buffer */ - boolean isconsole; /* TRUE: This is the serial console */ - sem_t closesem; /* Looks out new opens while - * close is in progress */ - sem_t xmitsem; /* Used to wakeup user waiting - * for space in xmit.buffer */ - sem_t recvsem; /* Used to wakeup user waiting - * for sapce in recv.buffer */ - struct uart_buffer_s xmit; /* Describes transmit buffer */ - struct uart_buffer_s recv; /* Describes receive buffer */ struct uart_regs_s regs; /* Shadow copy of readonly regs */ }; -typedef struct up_dev_s up_dev_t; /************************************************************ * Private Function Prototypes ************************************************************/ -static int up_open(struct file *filep); -static int up_close(struct file *filep); -static ssize_t up_read(struct file *filep, char *buffer, size_t buflen); -static ssize_t up_write(struct file *filep, const char *buffer, size_t buflen); +static int up_setup(struct uart_dev_s *dev); +static void up_shutdown(struct uart_dev_s *dev); +static int up_interrupt(int irq, void *context); static int up_ioctl(struct file *filep, int cmd, unsigned long arg); -static void up_uartsetup(up_dev_t *dev); +static int up_receive(struct uart_dev_s *dev, unsigned int *status); +static void up_rxint(struct uart_dev_s *dev, boolean enable); +static boolean up_rxfifonotempty(struct uart_dev_s *dev); +static void up_send(struct uart_dev_s *dev, int ch); +static void up_txint(struct uart_dev_s *dev, boolean enable); +static boolean up_txfifonotfull(struct uart_dev_s *dev); +static boolean up_txfifoempty(struct uart_dev_s *dev); /************************************************************ * Private Variables ************************************************************/ -struct file_operations g_serialops = -{ - .open = up_open, - .close = up_close, - .read = up_read, - .write = up_write, - .ioctl = up_ioctl, +struct uart_ops_s g_uart_ops = +{ + .setup = up_setup, + .shutdown = up_shutdown, + .handler = up_interrupt, + .ioctl = up_ioctl, + .receive = up_receive, + .rxint = up_rxint, + .rxfifonotempty = up_rxfifonotempty, + .send = up_send, + .txint = up_txint, + .txfifonotfull = up_txfifonotfull, + .txfifoempty = up_txfifoempty, }; +/* I/O buffers */ + +static char g_irdarxbuffer[CONFIG_UART_IRDA_RXBUFSIZE]; +static char g_irdatxbuffer[CONFIG_UART_IRDA_TXBUFSIZE]; +static char g_modemrxbuffer[CONFIG_UART_IRDA_RXBUFSIZE]; +static char g_modemtxbuffer[CONFIG_UART_MODEM_TXBUFSIZE]; + /* This describes the state of the C5471 serial IRDA port. */ -static up_dev_t g_irdaport = +static struct up_dev_s g_irdapriv = { .xmit_fifo_size = UART_IRDA_XMIT_FIFO_SIZE, .baud_base = BASE_BAUD, - .irq = C5471_IRQ_UART_IRDA, .uartbase = UART_IRDA_BASE, .baud = CONFIG_UART_IRDA_BAUD, .parity = CONFIG_UART_IRDA_PARITY, @@ -163,13 +155,29 @@ static up_dev_t g_irdaport = .stopbits2 = CONFIG_UART_IRDA_2STOP, }; +static uart_dev_t g_irdaport = +{ + .irq = C5471_IRQ_UART_IRDA, + .recv = + { + .size = CONFIG_UART_IRDA_RXBUFSIZE, + .buffer = g_irdarxbuffer, + }, + .xmit = + { + .size = CONFIG_UART_IRDA_TXBUFSIZE, + .buffer = g_irdatxbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_irdapriv, +}; + /* This describes the state of the C5471 serial Modem port. */ -static up_dev_t g_modemport = +static struct up_dev_s g_modempriv = { .xmit_fifo_size = UART_XMIT_FIFO_SIZE, .baud_base = BASE_BAUD, - .irq = C5471_IRQ_UART, .uartbase = UART_MODEM_BASE, .baud = CONFIG_UART_MODEM_BAUD, .parity = CONFIG_UART_MODEM_PARITY, @@ -180,128 +188,122 @@ static up_dev_t g_modemport = .stopbits2 = CONFIG_UART_MODEM_2STOP, }; -/* I/O buffers */ - -static char g_irdarxbuffer[CONFIG_UART_IRDA_RXBUFSIZE]; -static char g_irdatxbuffer[CONFIG_UART_IRDA_TXBUFSIZE]; -static char g_modemrxbuffer[CONFIG_UART_IRDA_RXBUFSIZE]; -static char g_modemtxbuffer[CONFIG_UART_MODEM_TXBUFSIZE]; +static uart_dev_t g_modemport = +{ + .irq = C5471_IRQ_UART, + .recv = + { + .size = CONFIG_UART_MODEM_RXBUFSIZE, + .buffer = g_modemrxbuffer, + }, + .xmit = + { + .size = CONFIG_UART_MODEM_TXBUFSIZE, + .buffer = g_modemtxbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_modempriv, +}; /* Now, which one with be tty0/console and which tty1? */ #ifdef CONFIG_SERIAL_IRDA_CONSOLE # define CONSOLE_DEV g_irdaport # define TTYS0_DEV g_irdaport -# define TTYS0_RXBUFFER g_irdarxbuffer -# define TTYS0_RXBUFSIZE CONFIG_UART_IRDA_RXBUFSIZE -# define TTYS0_TXBUFFER g_irdatxbuffer -# define TTYS0_TXBUFSIZE CONFIG_UART_IRDA_TXBUFSIZE # define TTYS1_DEV g_modemport -# define TTYS1_RXBUFFER g_modemrxbuffer -# define TTYS1_RXBUFSIZE CONFIG_UART_MODEM_RXBUFSIZE -# define TTYS1_TXBUFFER g_modemtxbuffer -# define TTYS1_TXBUFSIZE CONFIG_UART_MODEM_TXBUFSIZE #else # define CONSOLE_DEV g_modemport # define TTYS0_DEV g_modemport -# define TTYS0_RXBUFFER g_modemrxbuffer -# define TTYS0_RXBUFSIZE CONFIG_UART_MODEM_RXBUFSIZE -# define TTYS0_TXBUFFER g_modemtxbuffer -# define TTYS0_TXBUFSIZE CONFIG_UART_MODEM_TXBUFSIZE # define TTYS1_DEV g_irdaport -# define TTYS1_RXBUFFER g_irdarxbuffer -# define TTYS1_RXBUFSIZE CONFIG_UART_IRDA_RXBUFSIZE -# define TTYS1_TXBUFFER g_irdatxbuffer -# define TTYS1_TXBUFSIZE CONFIG_UART_IRDA_TXBUFSIZE #endif /************************************************************ * Private Functions ************************************************************/ -static inline uint32 up_inserial(up_dev_t *dev, uint32 offset) -{ - return getreg32(dev->uartbase + offset); -} +/************************************************************ + * Name: up_inserial + ************************************************************/ -static inline void up_serialout(up_dev_t *dev, uint32 offset, uint32 value) +static inline uint32 up_inserial(struct up_dev_s *priv, uint32 offset) { - putreg32(value, dev->uartbase + offset); + return getreg32(priv->uartbase + offset); } -static inline void up_serialreset(up_dev_t *dev) -{ - /* Both the IrDA and MODEM UARTs support RESET and UART mode. */ - - up_serialout(dev, UART_MDR_OFFS, MDR_RESET_MODE); - up_delay(5); - up_serialout(dev, UART_MDR_OFFS, MDR_UART_MODE); - up_delay(5); -} +/************************************************************ + * Name: up_serialout + ************************************************************/ -static inline void up_disabletxint(up_dev_t *dev) +static inline void up_serialout(struct up_dev_s *priv, uint32 offset, uint32 value) { - dev->regs.ier &= ~UART_IER_XmitInt; - up_serialout(dev, UART_IER_OFFS, dev->regs.ier); + putreg32(value, priv->uartbase + offset); } -static inline void up_disablerxint(up_dev_t *dev) -{ - dev->regs.ier &= ~UART_IER_RecvInt; - up_serialout(dev, UART_IER_OFFS, dev->regs.ier); -} +/************************************************************ + * Name: up_disableuartint + ************************************************************/ -static inline void up_enabletxint(up_dev_t *dev) +static inline void up_disableuartint(struct up_dev_s *priv, uint16 *ier) { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - dev->regs.ier |= UART_IER_XmitInt; - up_serialout(dev, UART_IER_OFFS, dev->regs.ier); -#endif + if (ier) + { + *ier = priv->regs.ier & UART_IER_AllInts; + } + priv->regs.ier &= ~UART_IER_AllInts; + up_serialout(priv, UART_IER_OFFS, priv->regs.ier); } -static inline void up_enablerxint(up_dev_t *dev) -{ -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - dev->regs.ier |= UART_IER_RecvInt; - up_serialout(dev, UART_IER_OFFS, dev->regs.ier); -#endif -} +/************************************************************ + * Name: up_restoreuartint + ************************************************************/ -static inline void up_disableuartint(up_dev_t *dev, uint16 *ier) +static inline void up_restoreuartint(struct up_dev_s *priv, uint16 ier) { - *ier = dev->regs.ier & UART_IER_AllInts; - dev->regs.ier &= ~UART_IER_AllInts; - up_serialout(dev, UART_IER_OFFS, dev->regs.ier); + priv->regs.ier |= ier & (UART_IER_RecvInt|UART_IER_XmitInt); + up_serialout(priv, UART_IER_OFFS, priv->regs.ier); } -static inline void up_restoreuartint(up_dev_t *dev, uint16 ier) -{ - dev->regs.ier |= ier & (UART_IER_RecvInt|UART_IER_XmitInt); - up_serialout(dev, UART_IER_OFFS, dev->regs.ier); -} +/************************************************************ + * Name: up_waittxfifonotfull + ************************************************************/ -static inline void up_clearfifos(up_dev_t *dev) +static inline void up_waittxfifonotfull(struct up_dev_s *priv) { - up_serialout(dev, UART_EFR_OFFS, 0x0010); /* Enable fifo control */ - up_serialout(dev, UART_TFCR_OFFS, 0); /* Reset to 0 */ - up_serialout(dev, UART_RFCR_OFFS, UART_FCR_RX_CLR); /* Clear RX fifo */ - up_serialout(dev, UART_TFCR_OFFS, UART_FCR_TX_CLR); /* Clear TX fifo */ - up_serialout(dev, UART_TFCR_OFFS, UART_FCR_FIFO_EN); /* Enable RX/TX fifos */ + int tmp; + + for (tmp = 1000 ; tmp > 0 ; tmp--) + { + if ((up_inserial(priv, UART_SSR_OFFS) & UART_SSR_TXFULL) == 0) + { + break; + } + } } +/************************************************************ + * Name: up_disablebreaks + ************************************************************/ -static inline void up_disablebreaks(up_dev_t *dev) +static inline void up_disablebreaks(struct up_dev_s *priv) { - dev->regs.lcr &= ~UART_LCR_BOC; - up_serialout(dev, UART_LCR_OFFS, dev->regs.lcr); + priv->regs.lcr &= ~UART_LCR_BOC; + up_serialout(priv, UART_LCR_OFFS, priv->regs.lcr); } -static inline void up_enablebreaks(up_dev_t *dev) +/************************************************************ + * Name: up_enablebreaks + ************************************************************/ + +static inline void up_enablebreaks(struct up_dev_s *priv) { - dev->regs.lcr |= UART_LCR_BOC; - up_serialout(dev, UART_LCR_OFFS, dev->regs.lcr); + priv->regs.lcr |= UART_LCR_BOC; + up_serialout(priv, UART_LCR_OFFS, priv->regs.lcr); } -static inline void up_setrate(up_dev_t *dev, unsigned int rate) +/************************************************************ + * Name: up_setrate + ************************************************************/ + +static inline void up_setrate(struct up_dev_s *priv, unsigned int rate) { uint32 div_bit_rate; @@ -334,328 +336,146 @@ static inline void up_setrate(up_dev_t *dev, unsigned int rate) break; } - up_serialout(dev, UART_DIV_BIT_RATE_OFFS, div_bit_rate); -} - -static inline void up_setmode(up_dev_t *dev, uint16 mode) -{ - dev->regs.lcr &= 0xffffffe0; /* clear original field, and... */ - dev->regs.lcr |= (uint32)mode; /* Set new bits in that field. */ - up_serialout(dev, UART_LCR_OFFS, dev->regs.lcr); -} - -static inline void up_setrxtrigger(up_dev_t *dev, uint16 val) -{ - /* does val need to be shifted to the right spot? */ - dev->regs.fcr = (dev->regs.fcr & 0xffffff3f) | (val & 0xc0); - up_serialout(dev, UART_RFCR_OFFS, dev->regs.fcr); -} - -static inline void up_settxtrigger(up_dev_t *dev, uint16 val) -{ - /* does val need to be shifted to the right spot? */ - dev->regs.fcr = (dev->regs.fcr & 0xffffffcf) | (val & 0x30); - up_serialout(dev, UART_RFCR_OFFS, dev->regs.fcr); -} - -static inline void up_outserialchar(up_dev_t *dev, ubyte val) -{ - up_serialout(dev, UART_THR_OFFS, val); + up_serialout(priv, UART_DIV_BIT_RATE_OFFS, div_bit_rate); } -static inline unsigned char up_inserialchar(up_dev_t *dev, - uint16 *status) -{ - uint32 rhr; - uint32 lsr; - - /* Next, construct a 16bit status word that - * uses the high byte to hold the status bits - * associated with framing,parity,break and - * a low byte that holds error bits of LSR for - * conditions such as overflow, etc. - */ - - rhr = up_inserial(dev, UART_RHR_OFFS); - lsr = up_inserial(dev, UART_LSR_OFFS); - - *status = (uint16)((rhr & 0x0000ff00) | (lsr & 0x000000ff)); - - return rhr & 0x000000ff; -} - -static inline int up_errorcondition(uint16 status) -{ - return status & 0x0782; -} - -static inline int up_parityerror(uint16 status) -{ - return status & 0x0100; -} - -static inline int up_framingerror(uint16 status) -{ - return status & 0x0200; -} - -static inline int up_overrunerror(uint16 status) -{ - return status & 0x0002; -} - -static inline uint32 up_getintcause(up_dev_t *dev) -{ - return up_inserial(dev, UART_ISR_OFFS) & 0x0000003f; -} - -static inline int up_xmitint(up_dev_t *dev, - uint32 cause) -{ - return (cause & 0x00000002) == 0x00000002; -} - -static inline int up_recvint(up_dev_t *dev, uint32 cause) -{ - return (cause & 0x0000000c) == 0x00000004; -} - -static inline int up_recvtimeoutint(up_dev_t *dev, uint32 cause) -{ - return (cause & 0x0000000c) == 0x0000000c; -} - -static inline ubyte up_txfifoempty(up_dev_t *dev) -{ - return (up_inserial(dev, UART_LSR_OFFS) & UART_LSR_TREF) != 0; -} +/************************************************************ + * Name: up_setup + * + * Description: + * Configure the UART baud, bits, parity, fifos, etc. This + * method is called the first time that the serial port is + * opened. + * + ************************************************************/ -static inline ubyte up_txfifonotfull(up_dev_t *dev) +static int up_setup(struct uart_dev_s *dev) { - return (up_inserial(dev, UART_SSR_OFFS) & UART_SSR_TXFULL) == 0; -} +#ifdef CONFIG_SUPPRESS_UART_CONFIG + struct up_dev_s *priv = dev->priv; + unsigned int cval; -static inline void up_waittxfifonotfull(up_dev_t *dev) -{ - int tmp; - for (tmp = 1000 ; tmp > 0 ; tmp--) + if (priv->bits == 7) { - if (up_txfifonotfull(dev)) - { - break; - } + cval = UART_LCR_7bits; + } + else + { + cval = UART_LCR_8bits; } -} - -static inline ubyte up_rxfifonotempty(up_dev_t *dev) -{ - return up_inserial(dev, UART_LSR_OFFS) & UART_RX_FIFO_NOEMPTY; -} - -static inline void serial_enable_hw_flow_control(up_dev_t *dev) -{ - /* Set the FIFO level triggers for flow control - * Halt = 48 bytes, resume = 12 bytes - */ - - dev->regs.tcr = (dev->regs.tcr & 0xffffff00) | 0x0000003c; - up_serialout(dev, UART_TCR_OFFS, dev->regs.tcr); - /* Enable RTS/CTS flow control */ + if (priv->stopbits2) + { + cval |= UART_LCR_2stop; + } - dev->regs.efr |= 0x000000c0; - up_serialout(dev, UART_EFR_OFFS, dev->regs.efr); -} + if (priv->parity == 1) /* Odd parity */ + { + cval |= (UART_LCR_ParEn|UART_LCR_ParOdd); + } + else if (priv->parity == 2) /* Even parity */ + { + cval |= (UART_LCR_ParEn|UART_LCR_ParEven); + } -static inline void serial_disable_hw_flow_control(up_dev_t *dev) -{ - /* Disable RTS/CTS flow control */ + /* Both the IrDA and MODEM UARTs support RESET and UART mode. */ - dev->regs.efr &= 0xffffff3f; - up_serialout(dev, UART_EFR_OFFS, dev->regs.efr); -} + up_serialout(priv, UART_MDR_OFFS, MDR_RESET_MODE); + up_delay(5); + up_serialout(priv, UART_MDR_OFFS, MDR_UART_MODE); + up_delay(5); -static inline void up_saveregisters(up_dev_t *dev) -{ - dev->regs.ier = up_inserial(dev, UART_IER_OFFS); - dev->regs.lcr = up_inserial(dev, UART_LCR_OFFS); + priv->regs.ier = up_inserial(priv, UART_IER_OFFS); + priv->regs.lcr = up_inserial(priv, UART_LCR_OFFS); #ifdef CONFIG_UART_HWFLOWCONTROL - if (dev->flowcontrol) + if (priv->flowcontrol) { - dev->regs.efr = up_inserial(dev, UART_EFR_OFFS); - dev->regs.tcr = up_inserial(dev, UART_TCR_OFFS); + priv->regs.efr = up_inserial(priv, UART_EFR_OFFS); + priv->regs.tcr = up_inserial(priv, UART_TCR_OFFS); } #endif -} -/************************************************************ - * up_takesem - ************************************************************/ + up_disableuartint(priv, NULL); -static void up_takesem(sem_t *sem) -{ - while (sem_wait(sem) != 0) - { - /* The only case that an error should occur here is if - * the wait was awakened by a signal. - */ + up_serialout(priv, UART_EFR_OFFS, 0x0010); /* Enable fifo control */ + up_serialout(priv, UART_TFCR_OFFS, 0); /* Reset to 0 */ + up_serialout(priv, UART_RFCR_OFFS, UART_FCR_RX_CLR); /* Clear RX fifo */ + up_serialout(priv, UART_TFCR_OFFS, UART_FCR_TX_CLR); /* Clear TX fifo */ + up_serialout(priv, UART_TFCR_OFFS, UART_FCR_FIFO_EN); /* Enable RX/TX fifos */ - ASSERT(*get_errno_ptr() == EINTR); - } -} + up_disablebreaks(priv); -/************************************************************ - * Name: up_givesem - ************************************************************/ + priv->regs.fcr = (priv->regs.fcr & 0xffffffcf) | (val & 0x30); + up_serialout(priv, UART_RFCR_OFFS, priv->regs.fcr); -static inline void up_givesem(sem_t *sem) -{ - (void)sem_post(sem); -} + priv->regs.fcr = (priv->regs.fcr & 0xffffff3f) | (val & 0xc0); + up_serialout(priv, UART_RFCR_OFFS, priv->regs.fcr); -/************************************************************ - * Name: up_receivechars - ************************************************************/ + up_setrate(priv, priv->baud); -/* Add chars to head of receive buffer. up_read will take - * characters from the tail of the buffer. - */ - -static void up_recvchars(up_dev_t *dev) -{ - uint16 status; - int nexthead = dev->recv.head + 1; + priv->regs.lcr &= 0xffffffe0; /* clear original field, and... */ + priv->regs.lcr |= (uint32)mode; /* Set new bits in that field. */ + up_serialout(priv, UART_LCR_OFFS, priv->regs.lcr); - if (nexthead >= dev->recv.size) +#ifdef CONFIG_UART_HWFLOWCONTROL + if (priv->flowcontrol) { - nexthead = 0; - } + /* Set the FIFO level triggers for flow control + * Halt = 48 bytes, resume = 12 bytes + */ - while (nexthead != dev->recv.tail && up_rxfifonotempty(dev)) - { - dev->recv.buffer[dev->recv.head] = up_inserialchar(dev, &status); + priv->regs.tcr = (priv->regs.tcr & 0xffffff00) | 0x0000003c; + up_serialout(priv, UART_TCR_OFFS, priv->regs.tcr); - dev->recv.head = nexthead; - if (++nexthead >= dev->recv.size) - { - nexthead = 0; - } + /* Enable RTS/CTS flow control */ - if (dev->recvwaiting) - { - dev->recvwaiting = FALSE; - up_givesem(&dev->recvsem); - } + priv->regs.efr |= 0x000000c0; + up_serialout(priv, UART_EFR_OFFS, priv->regs.efr); } -} - -/************************************************************ - * up_xmitchars - ************************************************************/ - -/* Transmit characters from the tail of the xmit buffer while - * up_write adds data to the head of the xmit buffer. - */ - -static void up_xmitchars(up_dev_t *dev) -{ - /* Send while we still have data & room in the fifo */ - - while (dev->xmit.head != dev->xmit.tail && up_txfifonotfull(dev)) + else { - up_outserialchar(dev, dev->xmit.buffer[dev->xmit.tail]); - - if (++(dev->xmit.tail) >= dev->xmit.size) - { - dev->xmit.tail = 0; - } - - if (dev->xmitwaiting) - { - dev->xmitwaiting = FALSE; - up_givesem(&dev->xmitsem); - } - } - - /* When all of the characters have been sent from the buffer - * disable the TX interrupt. - */ + /* Disable RTS/CTS flow control */ - if (dev->xmit.head == dev->xmit.tail) - { - up_disabletxint(dev); + priv->regs.efr &= 0xffffff3f; + up_serialout(priv, UART_EFR_OFFS, priv->regs.efr); } +#endif +#endif + return OK; } /************************************************************ - * Name: up_putxmitchar + * Name: up_shutdown + * + * Description: + * Disable the UART. This method is called when the serial port is closed + * ************************************************************/ -static void up_putxmitchar(up_dev_t *dev, int ch) +static void up_shutdown(struct uart_dev_s *dev) { - int nexthead = dev->xmit.head + 1; - if (nexthead >= dev->xmit.size) - { - nexthead = 0; - } - - for(;;) - { - if (nexthead != dev->xmit.tail) - { - dev->xmit.buffer[dev->xmit.head] = ch; - dev->xmit.head = nexthead; - return; - } - else - { -#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_SERIAL_INTS) - /* Transfer some characters with interrupts disabled */ - - up_xmitchars(dev); - - /* If we unsuccessful in making room in the buffer. - * then transmit the characters with interrupts - * enabled and wait for result. - */ - - if (nexthead == dev->xmit.tail) - { - /* Still no space */ - - up_waittxfifonotfull(dev); - } -#else - /* Inform the interrupt level logic that we are waiting */ - - dev->xmitwaiting = TRUE; - - /* Wait for some characters to be sent from the buffer - * with the TX interrupt enabled. When the TX interrupt - * is enabled, up_xmitchars should execute and remove - * some of the data from the TX buffer. - */ - - up_enabletxint(dev); - up_takesem(&dev->xmitsem); - up_disabletxint(dev); -#endif - } - } + struct up_dev_s *priv = (struct up_dev_s*)CONSOLE_DEV.priv; + up_disableuartint(priv, NULL); } /************************************************************ * Name: up_interrupt + * + * Description: + * This is the UART interrupt handler. It will be invoked + * when an interrupt received on the 'irq' It should call + * uart_transmitchars or uart_receivechar to perform the + * appropriate data transfers. The interrupt handling logic\ + * must be able to map the 'irq' number into the approprite + * uart_dev_s structure in order to call these functions. + * ************************************************************/ -/* This routine is used to handle the "top half" processing for the - * serial driver. - */ - static int up_interrupt(int irq, void *context) { - up_dev_t *dev; - volatile uint32 cause; + struct uart_dev_s *dev = NULL; + struct up_dev_s *priv; + volatile uint32 cause; if (g_irdaport.irq == irq) { @@ -669,10 +489,11 @@ static int up_interrupt(int irq, void *context) { PANIC(OSERR_INTERNAL); } + priv = (struct up_dev_s*)dev->priv; - cause = up_getintcause(dev); + cause = up_inserial(priv, UART_ISR_OFFS) & 0x0000003f; - if (up_recvtimeoutint(dev, cause)) + if ((cause & 0x0000000c) == 0x0000000c) { uint32 ier_val = 0; @@ -685,19 +506,19 @@ static int up_interrupt(int irq, void *context) * below. */ - ier_val = up_inserial(dev, UART_IER_OFFS); + ier_val = up_inserial(priv, UART_IER_OFFS); /* Then disable all IrDA UART interrupts */ - up_serialout(dev, UART_IER_OFFS, 0); + up_serialout(priv, UART_IER_OFFS, 0); } /* Receive characters from the RX fifo */ - up_recvchars(dev); + uart_recvchars(dev); /* read UART_RHR to clear int condition - * toss = up_inserialchar(dev,&status); + * toss = up_inserialchar(priv,&status); */ /* Is this an interrupt from the IrDA UART? */ @@ -706,261 +527,42 @@ static int up_interrupt(int irq, void *context) { /* Restore the IrDA UART interrupt enables */ - up_serialout(dev, UART_IER_OFFS, ier_val); + up_serialout(priv, UART_IER_OFFS, ier_val); } } - else if (up_recvint(dev, cause)) + else if ((cause & 0x0000000c) == 0x00000004) { - up_recvchars(dev); + uart_recvchars(dev); } - if (up_xmitint(dev, cause)) + if ((cause & 0x00000002) != 0) { - up_xmitchars(dev); + uart_xmitchars(dev); } return OK; } -/************************************************************ - * Name: up_uartsetup - ************************************************************/ - -static void up_uartsetup(up_dev_t *dev) -{ -#ifdef CONFIG_SUPPRESS_UART_CONFIG - unsigned int cval; - uint16 mrs; - - if (dev->bits == 7) - { - cval = UART_LCR_7bits; - } - else - { - cval = UART_LCR_8bits; - } - - if (dev->stopbits2) - { - cval |= UART_LCR_2stop; - } - - if (dev->parity == 1) /* Odd parity */ - { - cval |= (UART_LCR_ParEn|UART_LCR_ParOdd); - } - else if (dev->parity == 2) /* Even parity */ - { - cval |= (UART_LCR_ParEn|UART_LCR_ParEven); - } - - up_serialreset(dev); - up_saveregisters(dev); - up_disableuartint(dev, &mrs); - up_clearfifos(dev); - up_disablebreaks(dev); - up_settxtrigger(dev, UART_FCR_FTL); - up_setrxtrigger(dev, UART_FCR_FTL); - up_setrate(dev, dev->baud); - up_setmode(dev, cval); - -#ifdef CONFIG_UART_HWFLOWCONTROL - if (dev->flowcontrol) - { - serial_enable_hw_flow_control(dev); - } - else -#endif - { - serial_disable_hw_flow_control(dev); - } -#endif -} - -/************************************************************ - * shutdown - ************************************************************/ - -/* This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ - -static void shutdown(up_dev_t * dev) -{ - irqstate_t flags; - uint16 msr; - - /* Free the IRQ */ - - flags = irqsave(); /* Disable interrupts */ - up_disable_irq(dev->irq); - irq_detach(dev->irq); - up_disableuartint(dev, &msr); - irqrestore(flags); -} - -/************************************************************ - * Name: up_irqwrite - ************************************************************/ - -static ssize_t up_irqwrite(up_dev_t *dev, const char *buffer, size_t buflen) -{ - ssize_t ret = buflen; - - /* Force each character through the low level interface */ - - for (; buflen; buflen--) - { - int ch = *buffer++; - up_lowputc(ch); - - /* If this is the console, then we should replace LF with LF-CR */ - - if (ch == '\n') - { - up_lowputc('\r'); - } - } - - return ret; -} - -/************************************************************ - * Name: up_write - ************************************************************/ - -static ssize_t up_write(struct file *filep, const char *buffer, size_t buflen) -{ - struct inode *inode = filep->f_inode; - up_dev_t *dev = inode->i_private; - ssize_t ret = buflen; - - /* We may receive console writes through this path from - * interrupt handlers and from debug output in the IDLE task! - * In these cases, we will need to do things a little - * differently. - */ - - if (up_interrupt_context() || getpid() == 0) - { - if (dev->isconsole) - { - irqstate_t flags = irqsave(); - ret = up_irqwrite(dev, buffer, buflen); - irqrestore(flags); - return ret; - } - else - { - return ERROR; - } - } - - /* Only one user can be accessing dev->xmit.head at once */ - - up_takesem(&dev->xmit.sem); - - /* Loop while we still have data to copy to the transmit buffer. - * we add data to the head of the buffer; up_xmitchars takes the - * data from the end of the buffer. - */ - - up_disabletxint(dev); - for (; buflen; buflen--) - { - int ch = *buffer++; - - /* Put the character into the transmit buffer */ - - up_putxmitchar(dev, ch); - - /* If this is the console, then we should replace LF with LF-CR */ - - if (dev->isconsole && ch == '\n') - { - up_putxmitchar(dev, '\r'); - } - } - - if (dev->xmit.head != dev->xmit.tail) - { -#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_SERIAL_INTS) - up_xmitchars(dev); -#else - up_enabletxint(dev); -#endif - } - - up_givesem(&dev->xmit.sem); - return ret; -} - -/************************************************************ - * Name: up_read - ************************************************************/ - -static ssize_t up_read(struct file *filep, char *buffer, size_t buflen) -{ - struct inode *inode = filep->f_inode; - up_dev_t *dev = inode->i_private; - ssize_t ret = buflen; - - /* Only one user can be accessing dev->recv.tail at once */ - - up_takesem(&dev->recv.sem); - - /* Loop while we still have data to copy to the receive buffer. - * we add data to the head of the buffer; up_xmitchars takes the - * data from the end of the buffer. - */ - - up_disablerxint(dev); - while (buflen) - { - if (dev->recv.head != dev->recv.tail) - { - *buffer++ = dev->recv.buffer[dev->recv.tail]; - buflen--; - - if (++(dev->recv.tail) >= dev->recv.size) - { - dev->recv.tail = 0; - } - } - else - { - /* Wait for some characters to be sent from the buffer - * with the TX interrupt disabled. - */ - - dev->recvwaiting = TRUE; - up_enablerxint(dev); - up_takesem(&dev->recvsem); - up_disablerxint(dev); - } - } - - up_enablerxint(dev); - up_givesem(&dev->recv.sem); - return ret; -} - /************************************************************ * Name: up_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * ************************************************************/ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) { - struct inode *inode = filep->f_inode; - up_dev_t *dev = inode->i_private; - int ret = OK; + struct inode *inode = filep->f_inode; + struct uart_dev_s *dev = inode->i_private; + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + int ret = OK; switch (cmd) { case TIOCSERGSTRUCT: { - up_dev_t *user = (up_dev_t*)arg; + struct up_dev_s *user = (struct up_dev_s*)arg; if (!user) { *get_errno_ptr() = EINVAL; @@ -968,7 +570,7 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) } else { - memcpy(user, dev, sizeof(up_dev_t)); + memcpy(user, dev, sizeof(struct up_dev_s)); } } break; @@ -976,7 +578,7 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ { irqstate_t flags = irqsave(); - up_enablebreaks(dev); + up_enablebreaks(priv); irqrestore(flags); } break; @@ -985,7 +587,7 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) { irqstate_t flags; flags = irqsave(); - up_disablebreaks(dev); + up_disablebreaks(priv); irqrestore(flags); } break; @@ -1000,146 +602,139 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) } /************************************************************ - * Name: up_close + * Name: up_receive * * Description: - * This routine is called when the serial port gets closed. - * It waits for the last remaining data to be sent. + * Called (usually) from the interrupt level to receive one character from + * the UART. Error bits associated with the receipt are provided in the + * the return 'status'. * ************************************************************/ -static int up_close(struct file *filep) +static int up_receive(struct uart_dev_s *dev, unsigned int *status) { - struct inode *inode = filep->f_inode; - up_dev_t *dev = inode->i_private; - - up_takesem(&dev->closesem); - if (dev->open_count > 1) - { - dev->open_count--; - up_givesem(&dev->closesem); - return OK; - } + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + uint32 rhr; + uint32 lsr; - /* There are no more references to the port */ + /* Construct a 16bit status word that uses the high byte to + * hold the status bits associated with framing,parity,break + * and a low byte that holds error bits of LSR for + * conditions such as overflow, etc. + */ - dev->open_count = 0; + rhr = up_inserial(priv, UART_RHR_OFFS); + lsr = up_inserial(priv, UART_LSR_OFFS); - /* Stop accepting input */ + *status = (unsigned int)((rhr & 0x0000ff00) | (lsr & 0x000000ff)); - up_disablerxint(dev); + return rhr & 0x000000ff; +} - /* Now we wait for the transmit buffer to clear */ +/************************************************************ + * Name: up_rxint + * + * Description: + * Call to enable or disable RX interrupts + * + ************************************************************/ - while (dev->xmit.head != dev->xmit.tail) +static void up_rxint(struct uart_dev_s *dev, boolean enable) +{ + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + if (enable) { - usleep(500*1000); +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->regs.ier |= UART_IER_RecvInt; + up_serialout(priv, UART_IER_OFFS, priv->regs.ier); +#endif } - - /* And wait for the TX fifo to drain */ - - while (!up_txfifoempty(dev)) + else { - usleep(500*1000); + priv->regs.ier &= ~UART_IER_RecvInt; + up_serialout(priv, UART_IER_OFFS, priv->regs.ier); } - - /* That's it! */ - - shutdown(dev); - up_givesem(&dev->closesem); - return OK; - } +} /************************************************************ - * Name: up_open + * Name: up_rxfifonotempty * * Description: - * This routine is called whenever a serial port is opened. + * Return TRUE if the receive fifo is not empty * ************************************************************/ -static int up_open(struct file *filep) +static boolean up_rxfifonotempty(struct uart_dev_s *dev) { - struct inode *inode = filep->f_inode; - up_dev_t *dev = inode->i_private; - int ret = OK; + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + return up_inserial(priv, UART_LSR_OFFS) & UART_RX_FIFO_NOEMPTY; +} - /* If the port is the middle of closing, wait until the close - * is finished - */ +/************************************************************ + * Name: up_send + * + * Description: + * This method will send one byte on the UART + * + ************************************************************/ - up_takesem(&dev->closesem); +static void up_send(struct uart_dev_s *dev, int ch) +{ + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + up_serialout(priv, UART_THR_OFFS, (ubyte)ch); +} - /* Start up serial port */ +/************************************************************ + * Name: up_txint + * + * Description: + * Call to enable or disable TX interrupts + * + ************************************************************/ - if (++dev->open_count == 1) +static void up_txint(struct uart_dev_s *dev, boolean enable) +{ + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + if (enable) { - irqstate_t flags = irqsave(); - - /* If this is the console, then the UART has already - * been initialized. - */ - - if (!dev->isconsole) - { - up_uartsetup(dev); - } - - /* But, in any event, we do have to configure for - * interrupt driven mode of operation. - */ - - /* Attache and enabled the IRQ */ - #ifndef CONFIG_SUPPRESS_SERIAL_INTS - ret = irq_attach(dev->irq, up_interrupt); - if (ret == OK) + priv->regs.ier |= UART_IER_XmitInt; + up_serialout(priv, UART_IER_OFFS, priv->regs.ier); #endif - { - /* Mark the io buffers empty */ - - dev->xmit.head = 0; - dev->xmit.tail = 0; - dev->recv.head = 0; - dev->recv.tail = 0; - - /* Finally, enable interrupts */ - -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - up_enable_irq(dev->irq); -#endif - up_enablerxint(dev); - } - irqrestore(flags); } - - up_givesem(&dev->closesem); - return ret; + else + { + priv->regs.ier &= ~UART_IER_XmitInt; + up_serialout(priv, UART_IER_OFFS, priv->regs.ier); + } } /************************************************************ - * Name: up_devinit + * Name: up_txfifonotfull + * + * Description: + * Return TRUE if the tranmsit fifo is not full + * ************************************************************/ -static void up_devinit(up_dev_t *dev, - char *rxbuffer, int rxbufsize, - char *txbuffer, int txbufsize) +static boolean up_txfifonotfull(struct uart_dev_s *dev) { - /* Initialize fields in the dev structure that were not - * statically initialized. - */ - - sem_init(&dev->xmit.sem, 0, 1); - dev->xmit.size = txbufsize; - dev->xmit.buffer = txbuffer; + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + return (up_inserial(priv, UART_SSR_OFFS) & UART_SSR_TXFULL) == 0; +} - sem_init(&dev->recv.sem, 0, 1); - dev->recv.size = rxbufsize; - dev->recv.buffer = rxbuffer; +/************************************************************ + * Name: up_txfifoempty + * + * Description: + * Return TRUE if the transmit fifo is empty + * + ************************************************************/ - sem_init(&dev->closesem, 0, 1); - sem_init(&dev->xmitsem, 0, 0); - sem_init(&dev->recvsem, 0, 0); +static boolean up_txfifoempty(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s*)dev->priv; + return (up_inserial(priv, UART_LSR_OFFS) & UART_LSR_TREF) != 0; } /************************************************************ @@ -1158,19 +753,11 @@ static void up_devinit(up_dev_t *dev, void up_earlyserialinit(void) { - /* Set up default port setings */ - - up_devinit(&TTYS0_DEV, - TTYS0_RXBUFFER, TTYS0_RXBUFSIZE, - TTYS0_TXBUFFER, TTYS0_TXBUFSIZE); - up_devinit(&TTYS1_DEV, - TTYS1_RXBUFFER, TTYS1_RXBUFSIZE, - TTYS1_TXBUFFER, TTYS1_TXBUFSIZE); - - /* Configure the console for use now */ + up_disableuartint(TTYS0_DEV.priv, NULL); + up_disableuartint(TTYS1_DEV.priv, NULL); CONSOLE_DEV.isconsole = TRUE; - up_uartsetup(&CONSOLE_DEV); + up_setup(&CONSOLE_DEV); } /************************************************************ @@ -1184,9 +771,9 @@ void up_earlyserialinit(void) void up_serialinit(void) { - (void)register_inode("/dev/console", &g_serialops, 0666, &CONSOLE_DEV); - (void)register_inode("/dev/ttyS0", &g_serialops, 0666, &TTYS0_DEV); - (void)register_inode("/dev/ttyS1", &g_serialops, 0666, &TTYS1_DEV); + (void)uart_register("/dev/console", &CONSOLE_DEV); + (void)uart_register("/dev/ttyS0", &TTYS0_DEV); + (void)uart_register("/dev/ttyS1", &TTYS1_DEV); } /************************************************************ @@ -1200,11 +787,12 @@ void up_serialinit(void) int up_putc(int ch) { + struct up_dev_s *priv = (struct up_dev_s*)CONSOLE_DEV.priv; uint16 ier; - up_disableuartint(&CONSOLE_DEV, &ier); - up_waittxfifonotfull(&CONSOLE_DEV); - up_outserialchar(&CONSOLE_DEV, ch); + up_disableuartint(priv, &ier); + up_waittxfifonotfull(priv); + up_serialout(priv, UART_THR_OFFS, (ubyte)ch); /* Check for LF */ @@ -1212,12 +800,12 @@ int up_putc(int ch) { /* Add CR */ - up_waittxfifonotfull(&CONSOLE_DEV); - up_outserialchar(&CONSOLE_DEV, '\r'); + up_waittxfifonotfull(priv); + up_serialout(priv, UART_THR_OFFS, '\r'); } - up_waittxfifonotfull(&CONSOLE_DEV); - up_restoreuartint(&CONSOLE_DEV, ier); + up_waittxfifonotfull(priv); + up_restoreuartint(priv, ier); return ch; } diff --git a/drivers/Makefile b/drivers/Makefile index 8d687a67c3364d70482bb1f7ba848029b63261b0..f992f583740b58802a8438da161bb221308b5fe1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -40,7 +40,7 @@ MKDEP = $(TOPDIR)/tools/mkdeps.sh ASRCS = AOBJS = $(ASRCS:.S=$(OBJEXT)) -CSRCS = dev_null.c +CSRCS = dev_null.c serial.c COBJS = $(CSRCS:.c=$(OBJEXT)) SRCS = $(ASRCS) $(CSRCS) diff --git a/drivers/serial.c b/drivers/serial.c new file mode 100644 index 0000000000000000000000000000000000000000..fd0e5b1b7ddbb1fbe332791cb8efb92567092756 --- /dev/null +++ b/drivers/serial.c @@ -0,0 +1,578 @@ +/************************************************************************************ + * serial.c + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include <nuttx/config.h> +#include <sys/types.h> +#include <unistd.h> +#include <semaphore.h> +#include <string.h> +#include <errno.h> +#include <debug.h> +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <nuttx/fs.h> +#include <nuttx/serial.h> + +/************************************************************************************ + * Definitions + ************************************************************************************/ + +#ifdef CONFIG_HAVE_LOWPUTC +# define uart_putc(ch) up_lowputc(ch); +#else +# define uart_putc(ch) up_putc(ch); +#endif + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +/************************************************************************************ + * Private Function Prototypes + ************************************************************************************/ + +static int uart_open(struct file *filep); +static int uart_close(struct file *filep); +static ssize_t uart_read(struct file *filep, char *buffer, size_t buflen); +static ssize_t uart_write(struct file *filep, const char *buffer, size_t buflen); +static int uart_ioctl(struct file *filep, int cmd, unsigned long arg); + +/************************************************************************************ + * Private Variables + ************************************************************************************/ + +struct file_operations g_serialops = +{ + uart_open, /* open */ + uart_close, /* close */ + uart_read, /* read */ + uart_write, /* write */ + uart_ioctl /* ioctl */ +}; + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: uart_waittxfifonotfull + ************************************************************************************/ + +#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_SERIAL_INTS) +static inline void uart_waittxfifonotfull(uart_dev_t *dev) +{ + int tmp; + for (tmp = 1000 ; tmp > 0 ; tmp--) + { + if (uart_txfifonotfull(dev)) + { + break; + } + } +} +#endif + +/************************************************************************************ + * Name: uart_takesem + ************************************************************************************/ + +static void uart_takesem(sem_t *sem) +{ + while (sem_wait(sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + ASSERT(*get_errno_ptr() == EINTR); + } +} + +/************************************************************************************ + * Name: uart_givesem + ************************************************************************************/ + +static inline void uart_givesem(sem_t *sem) +{ + (void)sem_post(sem); +} + +/************************************************************************************ + * Name: uart_putxmitchar + ************************************************************************************/ + +static void uart_putxmitchar(uart_dev_t *dev, int ch) +{ + int nexthead = dev->xmit.head + 1; + if (nexthead >= dev->xmit.size) + { + nexthead = 0; + } + + for(;;) + { + if (nexthead != dev->xmit.tail) + { + dev->xmit.buffer[dev->xmit.head] = ch; + dev->xmit.head = nexthead; + return; + } + else + { +#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_SERIAL_INTS) + /* Transfer some characters with interrupts disabled */ + + uart_xmitchars(dev); + + /* If we unsuccessful in making room in the buffer. + * then transmit the characters with interrupts + * enabled and wait for result. + */ + + if (nexthead == dev->xmit.tail) + { + /* Still no space */ + + uart_waittxfifonotfull(dev); + } +#else + /* Inform the interrupt level logic that we are waiting */ + + dev->xmitwaiting = TRUE; + + /* Wait for some characters to be sent from the buffer + * with the TX interrupt enabled. When the TX interrupt + * is enabled, uart_xmitchars should execute and remove + * some of the data from the TX buffer. + */ + + uart_enabletxint(dev); + uart_takesem(&dev->xmitsem); + uart_disabletxint(dev); +#endif + } + } +} + +/************************************************************************************ + * Name: uart_irqwrite + ************************************************************************************/ + +static ssize_t uart_irqwrite(uart_dev_t *dev, const char *buffer, size_t buflen) +{ + ssize_t ret = buflen; + + /* Force each character through the low level interface */ + + for (; buflen; buflen--) + { + int ch = *buffer++; + uart_putc(ch); + + /* If this is the console, then we should replace LF with LF-CR */ + + if (ch == '\n') + { + uart_putc('\r'); + } + } + + return ret; +} + +/************************************************************************************ + * Name: uart_write + ************************************************************************************/ + +static ssize_t uart_write(struct file *filep, const char *buffer, size_t buflen) +{ + struct inode *inode = filep->f_inode; + uart_dev_t *dev = inode->i_private; + ssize_t ret = buflen; + + /* We may receive console writes through this path from + * interrupt handlers and from debug output in the IDLE task! + * In these cases, we will need to do things a little + * differently. + */ + + if (up_interrupt_context() || getpid() == 0) + { + if (dev->isconsole) + { + irqstate_t flags = irqsave(); + ret = uart_irqwrite(dev, buffer, buflen); + irqrestore(flags); + return ret; + } + else + { + return ERROR; + } + } + + /* Only one user can be accessing dev->xmit.head at once */ + + uart_takesem(&dev->xmit.sem); + + /* Loop while we still have data to copy to the transmit buffer. + * we add data to the head of the buffer; uart_xmitchars takes the + * data from the end of the buffer. + */ + + uart_disabletxint(dev); + for (; buflen; buflen--) + { + int ch = *buffer++; + + /* Put the character into the transmit buffer */ + + uart_putxmitchar(dev, ch); + + /* If this is the console, then we should replace LF with LF-CR */ + + if (dev->isconsole && ch == '\n') + { + uart_putxmitchar(dev, '\r'); + } + } + + if (dev->xmit.head != dev->xmit.tail) + { +#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_SERIAL_INTS) + uart_xmitchars(dev); +#else + uart_enabletxint(dev); +#endif + } + + uart_givesem(&dev->xmit.sem); + return ret; +} + +/************************************************************************************ + * Name: uart_read + ************************************************************************************/ + +static ssize_t uart_read(struct file *filep, char *buffer, size_t buflen) +{ + struct inode *inode = filep->f_inode; + uart_dev_t *dev = inode->i_private; + ssize_t ret = buflen; + + /* Only one user can be accessing dev->recv.tail at once */ + + uart_takesem(&dev->recv.sem); + + /* Loop while we still have data to copy to the receive buffer. + * we add data to the head of the buffer; uart_xmitchars takes the + * data from the end of the buffer. + */ + + uart_disablerxint(dev); + while (buflen) + { + if (dev->recv.head != dev->recv.tail) + { + *buffer++ = dev->recv.buffer[dev->recv.tail]; + buflen--; + + if (++(dev->recv.tail) >= dev->recv.size) + { + dev->recv.tail = 0; + } + } + else + { + /* Wait for some characters to be sent from the buffer + * with the TX interrupt disabled. + */ + + dev->recvwaiting = TRUE; + uart_enablerxint(dev); + uart_takesem(&dev->recvsem); + uart_disablerxint(dev); + } + } + + uart_enablerxint(dev); + uart_givesem(&dev->recv.sem); + return ret; +} + +/************************************************************************************ + * Name: uart_ioctl + ************************************************************************************/ + +static int uart_ioctl(struct file *filep, int cmd, unsigned long arg) +{ + struct inode *inode = filep->f_inode; + uart_dev_t *dev = inode->i_private; + + return dev->ops->ioctl(filep, cmd, arg); +} + +/************************************************************************************ + * Name: uart_close + * + * Description: + * This routine is called when the serial port gets closed. + * It waits for the last remaining data to be sent. + * + ************************************************************************************/ + +static int uart_close(struct file *filep) +{ + struct inode *inode = filep->f_inode; + uart_dev_t *dev = inode->i_private; + irqstate_t flags; + + uart_takesem(&dev->closesem); + if (dev->open_count > 1) + { + dev->open_count--; + uart_givesem(&dev->closesem); + return OK; + } + + /* There are no more references to the port */ + + dev->open_count = 0; + + /* Stop accepting input */ + + uart_disablerxint(dev); + + /* Now we wait for the transmit buffer to clear */ + + while (dev->xmit.head != dev->xmit.tail) + { + usleep(500*1000); + } + + /* And wait for the TX fifo to drain */ + + while (!uart_txfifoempty(dev)) + { + usleep(500*1000); + } + + /* Free the IRQ and disable the UART */ + + flags = irqsave(); /* Disable interrupts */ + up_disable_irq(dev->irq); + irq_detach(dev->irq); + uart_shutdown(dev); + irqrestore(flags); + + uart_givesem(&dev->closesem); + return OK; + } + +/************************************************************************************ + * Name: uart_open + * + * Description: + * This routine is called whenever a serial port is opened. + * + ************************************************************************************/ + +static int uart_open(struct file *filep) +{ + struct inode *inode = filep->f_inode; + uart_dev_t *dev = inode->i_private; + int ret = OK; + + /* If the port is the middle of closing, wait until the close + * is finished + */ + + uart_takesem(&dev->closesem); + + /* Start up serial port */ + + if (++dev->open_count == 1) + { + irqstate_t flags = irqsave(); + + /* If this is the console, then the UART has already + * been initialized. + */ + + if (!dev->isconsole) + { + uart_setup(dev); + } + + /* But, in any event, we do have to configure for + * interrupt driven mode of operation. + */ + + /* Attache and enabled the IRQ */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + ret = irq_attach(dev->irq, dev->ops->handler); + if (ret == OK) +#endif + { + /* Mark the io buffers empty */ + + dev->xmit.head = 0; + dev->xmit.tail = 0; + dev->recv.head = 0; + dev->recv.tail = 0; + + /* Finally, enable interrupts */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + up_enable_irq(dev->irq); +#endif + uart_enablerxint(dev); + } + irqrestore(flags); + } + + uart_givesem(&dev->closesem); + return ret; +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: uart_register + * + * Description: + * Register serial console and serial ports. + * + ************************************************************************************/ + +int uart_register(const char *path, uart_dev_t *dev) +{ + sem_init(&dev->xmit.sem, 0, 1); + sem_init(&dev->recv.sem, 0, 1); + sem_init(&dev->closesem, 0, 1); + sem_init(&dev->xmitsem, 0, 0); + sem_init(&dev->recvsem, 0, 0); + + dbg("Registering %s\n", path); + return register_inode(path, &g_serialops, 0666, dev); +} + +/************************************************************************************ + * Name: uart_xmitchars + * + * Description: + * This function is called from the UART interrupt handler when an interrupt + * is received indicating that there is more space in the transmit FIFO. This + * function will send characters from the tail of the xmit buffer while the driver + * write() logic adds data to the head of the xmit buffer. + * + ************************************************************************************/ + +void uart_xmitchars(uart_dev_t *dev) +{ + /* Send while we still have data & room in the fifo */ + + while (dev->xmit.head != dev->xmit.tail && uart_txfifonotfull(dev)) + { + uart_send(dev, dev->xmit.buffer[dev->xmit.tail]); + + if (++(dev->xmit.tail) >= dev->xmit.size) + { + dev->xmit.tail = 0; + } + + if (dev->xmitwaiting) + { + dev->xmitwaiting = FALSE; + uart_givesem(&dev->xmitsem); + } + } + + /* When all of the characters have been sent from the buffer + * disable the TX interrupt. + */ + + if (dev->xmit.head == dev->xmit.tail) + { + uart_disabletxint(dev); + } +} + +/************************************************************************************ + * Name: uart_receivechars + * + * Description: + * This function is called from the UART interrupt handler when an interrupt + * is received indicating that are bytes available in the receive fifo. This + * function will add chars to head of receive buffer. Driver read() logic will take + * characters from the tail of the buffer. + * + ************************************************************************************/ + +void uart_recvchars(uart_dev_t *dev) +{ + unsigned int status; + int nexthead = dev->recv.head + 1; + + if (nexthead >= dev->recv.size) + { + nexthead = 0; + } + + while (nexthead != dev->recv.tail && uart_rxfifonotempty(dev)) + { + dev->recv.buffer[dev->recv.head] = uart_receive(dev, &status); + + dev->recv.head = nexthead; + if (++nexthead >= dev->recv.size) + { + nexthead = 0; + } + + if (dev->recvwaiting) + { + dev->recvwaiting = FALSE; + uart_givesem(&dev->recvsem); + } + } +} + diff --git a/include/nuttx/serial.h b/include/nuttx/serial.h new file mode 100644 index 0000000000000000000000000000000000000000..7719fca6b43946d9f0a671e7fcfac734e7da2af5 --- /dev/null +++ b/include/nuttx/serial.h @@ -0,0 +1,241 @@ +/************************************************************************************ + * serial.h + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __SERIAL_H +#define __SERIAL_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include <nuttx/config.h> +#include <sys/types.h> +#include <semaphore.h> +#include <nuttx/fs.h> + +/************************************************************************************ + * Definitions + ************************************************************************************/ + +#define uart_setup(dev) dev->ops->setup(dev) +#define uart_shutdown(dev) dev->ops->shutdown(dev) +#define uart_enabletxint(dev) dev->ops->txint(dev, TRUE) +#define uart_disabletxint(dev) dev->ops->txint(dev, FALSE) +#define uart_enablerxint(dev) dev->ops->rxint(dev, TRUE) +#define uart_disablerxint(dev) dev->ops->rxint(dev, FALSE) +#define uart_rxfifonotempty(dev) dev->ops->rxfifonotempty(dev) +#define uart_txfifonotfull(dev) dev->ops->txfifonotfull(dev) +#define uart_txfifoempty(dev) dev->ops->txfifoempty(dev) +#define uart_send(dev,ch) dev->ops->send(dev,ch) +#define uart_receive(dev,s) dev->ops->receive(dev,s) + +/************************************************************************************ + * Private Types + ************************************************************************************/ + +/* This structure defines one serial I/O buffer. The serial infrastructure will + * initialize the 'sem' field but all other fields must be initialized by the + * caller of uart_register(). + */ + +struct uart_buffer_s +{ + sem_t sem; /* Used to control exclusive access to the buffer */ + sint16 head; /* Index to the head [IN] index in the buffer */ + sint16 tail; /* Index to the tail [OUT] index in the buffer */ + sint16 size; /* The allocated size of the buffer */ + FAR char *buffer; /* Pointer to the allocated buffer memory */ +}; + +/* This structure defines all of the operations providd by the architecture specific + * logic. All fields must be provided with non-NULL function pointers by the + * caller of uart_register(). + */ + +struct uart_dev_s; +struct uart_ops_s +{ + /* Configure the UART baud, bits, parity, fifos, etc. This method is called + * the first time that the serial port is opened. + */ + + int (*setup)(struct uart_dev_s *dev); + + /* Disable the UART. This method is called when the serial port is closed */ + + void (*shutdown)(struct uart_dev_s *dev); + + /* This is the UART interrupt handler. It will be invoked when an interrupt + * received on the 'irq' It should call uart_transmitchars or uart_receivechar + * to perform the appropriate data transfers. The interrupt handling logic\ + * must be able to map the 'irq' number into the approprite uart_dev_s + * structure in order to call these functions. + */ + + int (*handler)(int irq, void *context); + + /* All ioctl calls will be routed through this method */ + + int (*ioctl)(struct file *filep, int cmd, unsigned long arg); + + /* Called (usually) from the interrupt level to receive one character from + * the UART. Error bits associated with the receipt are provided in the + * the return 'status'. + */ + + int (*receive)(struct uart_dev_s *dev, unsigned int *status); + + /* Call to enable or disable RX interrupts */ + + void (*rxint)(struct uart_dev_s *dev, boolean enable); + + /* Return TRUE if the receive fifo is not empty */ + + boolean (*rxfifonotempty)(struct uart_dev_s *dev); + + /* This method will send one byte on the UART */ + + void (*send)(struct uart_dev_s *dev, int ch); + + /* Call to enable or disable TX interrupts */ + + void (*txint)(struct uart_dev_s *dev, boolean enable); + + /* Return TRUE if the tranmsit fifo is not full. This is used to + * determine if *send can be called. + */ + + boolean (*txfifonotfull)(struct uart_dev_s *dev); + + /* Return TRUE if the transmit fifo is empty. This is used when the + * driver needs to make sure that all characters are "drained" from + * the TX fifo. + */ + + boolean (*txfifoempty)(struct uart_dev_s *dev); +}; + +/* This is the device structure used by the driver. The caller of + * uart_register() must allocate and initialize this structure. The + * calling logic need only set all fields to zero except: + * + * 'irq', 'isconsole', 'xmit.buffer', 'rcv.buffer', the elements + * of 'ops', and 'private' + * + * The common logic will initialize all semaphores. + */ + +struct uart_dev_s +{ + int open_count; /* The number of times + * the device has been opened */ + ubyte irq; /* IRQ associated with + * this UART */ + boolean xmitwaiting; /* TRUE: User is waiting + * for space in xmit.buffer */ + boolean recvwaiting; /* TRUE: User is waiting + * for space in recv.buffer */ + boolean isconsole; /* TRUE: This is the serial console */ + sem_t closesem; /* Locks out new opens while + * close is in progress */ + sem_t xmitsem; /* Used to wakeup user waiting + * for space in xmit.buffer */ + sem_t recvsem; /* Used to wakeup user waiting + * for sapce in recv.buffer */ + struct uart_buffer_s xmit; /* Describes transmit buffer */ + struct uart_buffer_s recv; /* Describes receive buffer */ + const struct uart_ops_s *ops; /* Arch-specifics operations */ + void *priv; /* Used by the arch-specific logic */ +}; +typedef struct uart_dev_s uart_dev_t; + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" { +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Name: uart_register + * + * Description: + * Register serial console and serial ports. + * + ************************************************************************************/ + +EXTERN int uart_register(const char *path, uart_dev_t *dev); + +/************************************************************************************ + * Name: uart_xmitchars + * + * Description: + * This function is called from the UART interrupt handler when an interrupt + * is received indicating that there is more space in the transmit FIFO. This + * function will send characters from the tail of the xmit buffer while the driver + * write() logic adds data to the head of the xmit buffer. + * + ************************************************************************************/ + +EXTERN void uart_xmitchars(uart_dev_t *dev); + +/************************************************************************************ + * Name: uart_receivechars + * + * Description: + * This function is called from the UART interrupt handler when an interrupt + * is received indicating that are bytes available in the receive fifo. This + * function will add chars to head of receive buffer. Driver read() logic will take + * characters from the tail of the buffer. + * + ************************************************************************************/ + +EXTERN void uart_recvchars(uart_dev_t *dev); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __SERIAL_H */