468 lines
14 KiB
C
468 lines
14 KiB
C
/****************************************************************************
|
|
* drivers/segger/serial_rtt.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#include <syslog.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/segger/rtt.h>
|
|
#include <nuttx/serial/serial.h>
|
|
#include <nuttx/wdog.h>
|
|
|
|
#include <SEGGER_RTT.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifdef SEGGER_RTT_BUFFER_SECTION
|
|
# define SERIAL_RTT_BUFFER_SECTION locate_data(SEGGER_RTT_BUFFER_SECTION)
|
|
#else
|
|
# define SERIAL_RTT_BUFFER_SECTION
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct serial_rtt_s
|
|
{
|
|
struct uart_dev_s uart;
|
|
struct wdog_s wdog;
|
|
int channel;
|
|
FAR char *up_buffer;
|
|
FAR char *down_buffer;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int serial_rtt_setup(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_shutdown(FAR struct uart_dev_s *dev);
|
|
static int serial_rtt_attach(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_detach(FAR struct uart_dev_s *dev);
|
|
static int serial_rtt_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
static int serial_rtt_receive(FAR struct uart_dev_s *dev,
|
|
FAR unsigned int *status);
|
|
static void serial_rtt_rxint(FAR struct uart_dev_s *dev, bool enable);
|
|
static bool serial_rtt_rxavailable(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_dmasend(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_dmareceive(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_dmarxfree(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_dmatxavail(FAR struct uart_dev_s *dev);
|
|
static void serial_rtt_send(FAR struct uart_dev_s *dev, int ch);
|
|
static void serial_rtt_txint(FAR struct uart_dev_s *dev, bool enable);
|
|
static bool serial_rtt_txready(FAR struct uart_dev_s *dev);
|
|
static bool serial_rtt_txempty(FAR struct uart_dev_s *dev);
|
|
|
|
static void serial_rtt_timeout(wdparm_t arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct uart_ops_s g_serial_rtt_ops =
|
|
{
|
|
serial_rtt_setup,
|
|
serial_rtt_shutdown,
|
|
serial_rtt_attach,
|
|
serial_rtt_detach,
|
|
serial_rtt_ioctl,
|
|
serial_rtt_receive,
|
|
serial_rtt_rxint,
|
|
serial_rtt_rxavailable,
|
|
#ifdef CONFIG_SERIAL_IFLOWCONTROL
|
|
NULL,
|
|
#endif
|
|
serial_rtt_dmasend,
|
|
serial_rtt_dmareceive,
|
|
serial_rtt_dmarxfree,
|
|
serial_rtt_dmatxavail,
|
|
serial_rtt_send,
|
|
serial_rtt_txint,
|
|
serial_rtt_txready,
|
|
serial_rtt_txempty,
|
|
};
|
|
|
|
#ifdef CONFIG_SERIAL_RTT0
|
|
static char g_rtt0_xmit_buffer[CONFIG_SEGGER_RTT_BUFFER_SIZE_UP];
|
|
static char g_rtt0_recv_buffer[CONFIG_SEGGER_RTT_BUFFER_SIZE_DOWN];
|
|
|
|
static struct serial_rtt_s g_serial_rtt0 =
|
|
{
|
|
.uart =
|
|
{
|
|
#ifdef CONFIG_SERIAL_RTT_CONSOLE
|
|
.isconsole = CONFIG_SERIAL_RTT_CONSOLE_CHANNEL == 0,
|
|
#endif
|
|
.recv =
|
|
{
|
|
.buffer = g_rtt0_recv_buffer,
|
|
.size = CONFIG_SEGGER_RTT_BUFFER_SIZE_DOWN,
|
|
},
|
|
.xmit =
|
|
{
|
|
.buffer = g_rtt0_xmit_buffer,
|
|
.size = CONFIG_SEGGER_RTT_BUFFER_SIZE_UP,
|
|
},
|
|
.ops = &g_serial_rtt_ops,
|
|
.priv = &g_serial_rtt0,
|
|
},
|
|
.channel = 0,
|
|
.up_buffer = NULL,
|
|
.down_buffer = NULL,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_SERIAL_RTT1
|
|
static char g_rtt1_xmit_buffer[CONFIG_SEGGER_RTT1_BUFFER_SIZE_UP];
|
|
static char g_rtt1_recv_buffer[CONFIG_SEGGER_RTT1_BUFFER_SIZE_DOWN];
|
|
|
|
static char SERIAL_RTT_BUFFER_SECTION
|
|
g_rtt1_up_buffer[CONFIG_SEGGER_RTT1_BUFFER_SIZE_UP];
|
|
static char SERIAL_RTT_BUFFER_SECTION
|
|
g_rtt1_down_buffer[CONFIG_SEGGER_RTT1_BUFFER_SIZE_DOWN];
|
|
|
|
static struct serial_rtt_s g_serial_rtt1 =
|
|
{
|
|
.uart =
|
|
{
|
|
#ifdef CONFIG_SERIAL_RTT_CONSOLE
|
|
.isconsole = CONFIG_SERIAL_RTT_CONSOLE_CHANNEL == 1,
|
|
#endif
|
|
.recv =
|
|
{
|
|
.buffer = g_rtt1_recv_buffer,
|
|
.size = CONFIG_SEGGER_RTT1_BUFFER_SIZE_DOWN,
|
|
},
|
|
.xmit =
|
|
{
|
|
.buffer = g_rtt1_xmit_buffer,
|
|
.size = CONFIG_SEGGER_RTT1_BUFFER_SIZE_UP,
|
|
},
|
|
.ops = &g_serial_rtt_ops,
|
|
.priv = &g_serial_rtt1,
|
|
},
|
|
.channel = 1,
|
|
.up_buffer = g_rtt1_up_buffer,
|
|
.down_buffer = g_rtt1_down_buffer,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_SERIAL_RTT2
|
|
static char g_rtt2_xmit_buffer[CONFIG_SEGGER_RTT2_BUFFER_SIZE_UP];
|
|
static char g_rtt2_recv_buffer[CONFIG_SEGGER_RTT2_BUFFER_SIZE_DOWN];
|
|
|
|
static char SERIAL_RTT_BUFFER_SECTION
|
|
g_rtt2_up_buffer[CONFIG_SEGGER_RTT2_BUFFER_SIZE_UP];
|
|
static char SERIAL_RTT_BUFFER_SECTION
|
|
g_rtt2_down_buffer[CONFIG_SEGGER_RTT2_BUFFER_SIZE_DOWN];
|
|
|
|
static struct serial_rtt_s g_serial_rtt2 =
|
|
{
|
|
.uart =
|
|
{
|
|
#ifdef CONFIG_SERIAL_RTT_CONSOLE
|
|
.isconsole = CONFIG_SERIAL_RTT_CONSOLE_CHANNEL == 2,
|
|
#endif
|
|
.recv =
|
|
{
|
|
.buffer = g_rtt2_recv_buffer,
|
|
.size = CONFIG_SEGGER_RTT2_BUFFER_SIZE_DOWN,
|
|
},
|
|
.xmit =
|
|
{
|
|
.buffer = g_rtt2_xmit_buffer,
|
|
.size = CONFIG_SEGGER_RTT2_BUFFER_SIZE_UP,
|
|
},
|
|
.ops = &g_serial_rtt_ops,
|
|
.priv = &g_serial_rtt2,
|
|
},
|
|
.channel = 2,
|
|
.up_buffer = g_rtt2_up_buffer,
|
|
.down_buffer = g_rtt2_down_buffer,
|
|
};
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_setup
|
|
****************************************************************************/
|
|
|
|
static int serial_rtt_setup(FAR struct uart_dev_s *dev)
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_shutdown
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_shutdown(FAR struct uart_dev_s *dev)
|
|
{
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_attach
|
|
****************************************************************************/
|
|
|
|
static int serial_rtt_attach(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
wd_start(&rtt->wdog, USEC2TICK(CONFIG_SERIAL_RTT_POLLING_INTERVAL),
|
|
serial_rtt_timeout, (wdparm_t)dev);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_detach
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_detach(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
wd_cancel(&rtt->wdog);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_ioctl
|
|
****************************************************************************/
|
|
|
|
static int serial_rtt_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
return -ENOTTY;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_receive
|
|
****************************************************************************/
|
|
|
|
static int serial_rtt_receive(FAR struct uart_dev_s *dev,
|
|
FAR unsigned int *status)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
int ret;
|
|
int ch;
|
|
|
|
ret = SEGGER_RTT_ReadNoLock(rtt->channel, &ch, 1);
|
|
*status = ret == 1 ? 0 : -EAGAIN;
|
|
return ch;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_rxint
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_rxint(FAR struct uart_dev_s *dev, bool enable)
|
|
{
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_rxavailable
|
|
****************************************************************************/
|
|
|
|
static bool serial_rtt_rxavailable(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
return SEGGER_RTT_HasData(rtt->channel) != 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_dmasend
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_dmasend(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
FAR struct uart_dmaxfer_s *xfer = &dev->dmatx;
|
|
size_t len;
|
|
|
|
SEGGER_RTT_BLOCK_IF_FIFO_FULL(rtt->channel);
|
|
len = SEGGER_RTT_WriteNoLock(rtt->channel, xfer->buffer, xfer->length);
|
|
if (len == xfer->length && xfer->nlength)
|
|
{
|
|
len += SEGGER_RTT_WriteNoLock(rtt->channel, xfer->nbuffer,
|
|
xfer->nlength);
|
|
}
|
|
|
|
xfer->nbytes = len;
|
|
uart_xmitchars_done(dev);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_dmareceive
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_dmareceive(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
FAR struct uart_dmaxfer_s *xfer = &dev->dmarx;
|
|
size_t len;
|
|
|
|
len = SEGGER_RTT_ReadNoLock(rtt->channel, xfer->buffer, xfer->length);
|
|
if (len == xfer->length && xfer->nbuffer &&
|
|
SEGGER_RTT_HasData(rtt->channel))
|
|
{
|
|
len += SEGGER_RTT_ReadNoLock(rtt->channel, xfer->nbuffer,
|
|
xfer->nlength);
|
|
}
|
|
|
|
xfer->nbytes = len;
|
|
uart_recvchars_done(dev);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_dmarxfree
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_dmarxfree(FAR struct uart_dev_s *dev)
|
|
{
|
|
/* When the DMA buffer is empty, check whether there is data to read */
|
|
|
|
if (serial_rtt_rxavailable(dev))
|
|
{
|
|
uart_recvchars_dma(dev);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_dmatxavail
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_dmatxavail(FAR struct uart_dev_s *dev)
|
|
{
|
|
if (serial_rtt_txready(dev))
|
|
{
|
|
uart_xmitchars_dma(dev);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_send
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_send(FAR struct uart_dev_s *dev, int ch)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
|
|
SEGGER_RTT_BLOCK_IF_FIFO_FULL(rtt->channel);
|
|
SEGGER_RTT_PutChar(rtt->channel, ch);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_txint
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_txint(FAR struct uart_dev_s *dev, bool enable)
|
|
{
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_txready
|
|
****************************************************************************/
|
|
|
|
static bool serial_rtt_txready(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
return SEGGER_RTT_GetAvailWriteSpace(rtt->channel) != 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_txempty
|
|
****************************************************************************/
|
|
|
|
static bool serial_rtt_txempty(FAR struct uart_dev_s *dev)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = dev->priv;
|
|
return SEGGER_RTT_GetBytesInBuffer(rtt->channel) == 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_timeout
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_timeout(wdparm_t arg)
|
|
{
|
|
FAR struct serial_rtt_s *rtt = (FAR struct serial_rtt_s *)arg;
|
|
|
|
serial_rtt_dmarxfree(&rtt->uart);
|
|
serial_rtt_dmatxavail(&rtt->uart);
|
|
wd_start(&rtt->wdog, USEC2TICK(CONFIG_SERIAL_RTT_POLLING_INTERVAL),
|
|
serial_rtt_timeout, arg);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: serial_rtt_register
|
|
****************************************************************************/
|
|
|
|
static void serial_rtt_register(FAR const char *name,
|
|
FAR struct serial_rtt_s *rtt)
|
|
{
|
|
SEGGER_RTT_ConfigUpBuffer(rtt->channel, name, rtt->up_buffer,
|
|
rtt->uart.xmit.size,
|
|
SEGGER_RTT_MODE_NO_BLOCK_TRIM);
|
|
SEGGER_RTT_ConfigDownBuffer(rtt->channel, name, rtt->down_buffer,
|
|
rtt->uart.recv.size,
|
|
SEGGER_RTT_MODE_NO_BLOCK_TRIM);
|
|
|
|
#ifdef CONFIG_SERIAL_RTT_CONSOLE
|
|
if (rtt->uart.isconsole)
|
|
{
|
|
uart_register("/dev/console", &rtt->uart);
|
|
}
|
|
#endif
|
|
|
|
uart_register(name, &rtt->uart);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* serial_rtt_initialize
|
|
****************************************************************************/
|
|
|
|
void serial_rtt_initialize(void)
|
|
{
|
|
#ifdef CONFIG_SERIAL_RTT0
|
|
serial_rtt_register("/dev/ttyR0", &g_serial_rtt0);
|
|
#endif
|
|
|
|
#ifdef CONFIG_SERIAL_RTT1
|
|
serial_rtt_register("/dev/ttyR1", &g_serial_rtt1);
|
|
#endif
|
|
|
|
#ifdef CONFIG_SERIAL_RTT2
|
|
serial_rtt_register("/dev/ttyR2", &g_serial_rtt2);
|
|
#endif
|
|
}
|