* [PATCH v11 1/4] dmaengine: 8250_mtk_dma: add MediaTek uart DMA support
From: Long Cheng @ 2019-03-07 1:45 UTC (permalink / raw)
To: Vinod Koul, Randy Dunlap, Rob Herring, Mark Rutland, Ryder Lee,
Sean Wang, Nicolas Boichat, Matthias Brugger
Cc: Dan Williams, Greg Kroah-Hartman, Jiri Slaby, Sean Wang,
dmaengine, devicetree, linux-arm-kernel, linux-mediatek,
linux-kernel, linux-serial, srv_heupstream, Yingjoe Chen, YT Shen,
Zhenbao Liu, Long Cheng
In-Reply-To: <1551923135-32479-1-git-send-email-long.cheng@mediatek.com>
In DMA engine framework, add 8250 uart dma to support MediaTek uart.
If MediaTek uart enabled(SERIAL_8250_MT6577), and want to improve
the performance, can enable the function.
Signed-off-by: Long Cheng <long.cheng@mediatek.com>
---
drivers/dma/mediatek/Kconfig | 11 +
drivers/dma/mediatek/Makefile | 1 +
drivers/dma/mediatek/mtk-uart-apdma.c | 660 +++++++++++++++++++++++++++++++++
3 files changed, 672 insertions(+)
create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c
diff --git a/drivers/dma/mediatek/Kconfig b/drivers/dma/mediatek/Kconfig
index 680fc05..ac49eb6 100644
--- a/drivers/dma/mediatek/Kconfig
+++ b/drivers/dma/mediatek/Kconfig
@@ -24,3 +24,14 @@ config MTK_CQDMA
This controller provides the channels which is dedicated to
memory-to-memory transfer to offload from CPU.
+
+config MTK_UART_APDMA
+ tristate "MediaTek SoCs APDMA support for UART"
+ depends on OF && SERIAL_8250_MT6577
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Support for the UART DMA engine found on MediaTek MTK SoCs.
+ When SERIAL_8250_MT6577 is enabled, and if you want to use DMA,
+ you can enable the config. The DMA engine can only be used
+ with MediaTek SoCs.
diff --git a/drivers/dma/mediatek/Makefile b/drivers/dma/mediatek/Makefile
index 41bb381..61a6d29 100644
--- a/drivers/dma/mediatek/Makefile
+++ b/drivers/dma/mediatek/Makefile
@@ -1,2 +1,3 @@
+obj-$(CONFIG_MTK_UART_APDMA) += mtk-uart-apdma.o
obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
obj-$(CONFIG_MTK_CQDMA) += mtk-cqdma.o
diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c
new file mode 100644
index 0000000..9ed7a49
--- /dev/null
+++ b/drivers/dma/mediatek/mtk-uart-apdma.c
@@ -0,0 +1,660 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek Uart APDMA driver.
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Long Cheng <long.cheng@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "../virt-dma.h"
+
+/* The default number of virtual channel */
+#define MTK_UART_APDMA_NR_VCHANS 8
+
+#define VFF_EN_B BIT(0)
+#define VFF_STOP_B BIT(0)
+#define VFF_FLUSH_B BIT(0)
+#define VFF_4G_SUPPORT_B BIT(0)
+#define VFF_RX_INT_EN0_B BIT(0) /* rx valid size >= vff thre */
+#define VFF_RX_INT_EN1_B BIT(1)
+#define VFF_TX_INT_EN_B BIT(0) /* tx left size >= vff thre */
+#define VFF_WARM_RST_B BIT(0)
+#define VFF_RX_INT_CLR_B (BIT(0) | BIT(1))
+#define VFF_TX_INT_CLR_B 0
+#define VFF_STOP_CLR_B 0
+#define VFF_INT_EN_CLR_B 0
+#define VFF_4G_SUPPORT_CLR_B 0
+
+/* interrupt trigger level for tx */
+#define VFF_TX_THRE(n) ((n) * 7 / 8)
+/* interrupt trigger level for rx */
+#define VFF_RX_THRE(n) ((n) * 3 / 4)
+
+#define VFF_RING_SIZE 0xffffU
+/* invert this bit when wrap ring head again */
+#define VFF_RING_WRAP 0x10000U
+
+#define VFF_INT_FLAG 0x00
+#define VFF_INT_EN 0x04
+#define VFF_EN 0x08
+#define VFF_RST 0x0c
+#define VFF_STOP 0x10
+#define VFF_FLUSH 0x14
+#define VFF_ADDR 0x1c
+#define VFF_LEN 0x24
+#define VFF_THRE 0x28
+#define VFF_WPT 0x2c
+#define VFF_RPT 0x30
+/* TX: the buffer size HW can read. RX: the buffer size SW can read. */
+#define VFF_VALID_SIZE 0x3c
+/* TX: the buffer size SW can write. RX: the buffer size HW can write. */
+#define VFF_LEFT_SIZE 0x40
+#define VFF_DEBUG_STATUS 0x50
+#define VFF_4G_SUPPORT 0x54
+
+struct mtk_uart_apdmadev {
+ struct dma_device ddev;
+ struct clk *clk;
+ bool support_33bits;
+ unsigned int dma_requests;
+ unsigned int *dma_irq;
+};
+
+struct mtk_uart_apdma_desc {
+ struct virt_dma_desc vd;
+
+ unsigned int avail_len;
+};
+
+struct mtk_chan {
+ struct virt_dma_chan vc;
+ struct dma_slave_config cfg;
+ void __iomem *base;
+ struct mtk_uart_apdma_desc *desc;
+
+ enum dma_transfer_direction dir;
+
+ bool requested;
+
+ unsigned int rx_status;
+};
+
+static inline struct mtk_uart_apdmadev *
+to_mtk_uart_apdma_dev(struct dma_device *d)
+{
+ return container_of(d, struct mtk_uart_apdmadev, ddev);
+}
+
+static inline struct mtk_chan *to_mtk_uart_apdma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct mtk_chan, vc.chan);
+}
+
+static inline struct mtk_uart_apdma_desc *to_mtk_uart_apdma_desc
+ (struct dma_async_tx_descriptor *t)
+{
+ return container_of(t, struct mtk_uart_apdma_desc, vd.tx);
+}
+
+static void mtk_uart_apdma_write(struct mtk_chan *c,
+ unsigned int reg, unsigned int val)
+{
+ writel(val, c->base + reg);
+}
+
+static unsigned int mtk_uart_apdma_read(struct mtk_chan *c, unsigned int reg)
+{
+ return readl(c->base + reg);
+}
+
+static void mtk_uart_apdma_desc_free(struct virt_dma_desc *vd)
+{
+ struct dma_chan *chan = vd->tx.chan;
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ kfree(c->desc);
+}
+
+static void mtk_uart_apdma_start_tx(struct mtk_chan *c)
+{
+ unsigned int len, send, left, wpt, d_wpt, tmp;
+ int ret;
+
+ left = mtk_uart_apdma_read(c, VFF_LEFT_SIZE);
+ if (!left) {
+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+ return;
+ }
+
+ /* Wait 1sec for flush, can't sleep */
+ ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
+ tmp != VFF_FLUSH_B, 0, 1000000);
+ if (ret)
+ dev_warn(c->vc.chan.device->dev, "tx: fail, debug=0x%x\n",
+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
+
+ send = min_t(unsigned int, left, c->desc->avail_len);
+ wpt = mtk_uart_apdma_read(c, VFF_WPT);
+ len = c->cfg.dst_port_window_size;
+
+ d_wpt = wpt + send;
+ if ((d_wpt & VFF_RING_SIZE) >= len) {
+ d_wpt = d_wpt - len;
+ d_wpt = d_wpt ^ VFF_RING_WRAP;
+ }
+ mtk_uart_apdma_write(c, VFF_WPT, d_wpt);
+
+ c->desc->avail_len -= send;
+
+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_TX_INT_EN_B);
+ if (mtk_uart_apdma_read(c, VFF_FLUSH) == 0U)
+ mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
+}
+
+static void mtk_uart_apdma_start_rx(struct mtk_chan *c)
+{
+ struct mtk_uart_apdma_desc *d = c->desc;
+ unsigned int len, wg, rg;
+ int cnt;
+
+ if ((mtk_uart_apdma_read(c, VFF_VALID_SIZE) == 0U) ||
+ !d || !vchan_next_desc(&c->vc))
+ return;
+
+ len = c->cfg.src_port_window_size;
+ rg = mtk_uart_apdma_read(c, VFF_RPT);
+ wg = mtk_uart_apdma_read(c, VFF_WPT);
+ cnt = (wg & VFF_RING_SIZE) - (rg & VFF_RING_SIZE);
+ /*
+ * The buffer is ring buffer. If wrap bit different,
+ * represents the start of the next cycle for WPT
+ */
+ if ((rg ^ wg) & VFF_RING_WRAP)
+ cnt += len;
+
+ c->rx_status = d->avail_len - cnt;
+ mtk_uart_apdma_write(c, VFF_RPT, wg);
+
+ list_del(&d->vd.node);
+ vchan_cookie_complete(&d->vd);
+}
+
+static irqreturn_t mtk_uart_apdma_irq_handler(int irq, void *dev_id)
+{
+ struct dma_chan *chan = (struct dma_chan *)dev_id;
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdma_desc *d;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (c->dir == DMA_DEV_TO_MEM) {
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
+ mtk_uart_apdma_start_rx(c);
+ } else if (c->dir == DMA_MEM_TO_DEV) {
+ d = c->desc;
+
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
+
+ if (d->avail_len != 0U) {
+ mtk_uart_apdma_start_tx(c);
+ } else {
+ list_del(&d->vd.node);
+ vchan_cookie_complete(&d->vd);
+ }
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ unsigned int tmp;
+ int ret;
+
+ pm_runtime_get_sync(mtkd->ddev.dev);
+
+ mtk_uart_apdma_write(c, VFF_ADDR, 0);
+ mtk_uart_apdma_write(c, VFF_THRE, 0);
+ mtk_uart_apdma_write(c, VFF_LEN, 0);
+ mtk_uart_apdma_write(c, VFF_RST, VFF_WARM_RST_B);
+
+ ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
+ if (ret) {
+ dev_err(chan->device->dev, "dma reset: fail, timeout\n");
+ return ret;
+ }
+
+ if (!c->requested) {
+ c->requested = true;
+ ret = request_irq(mtkd->dma_irq[chan->chan_id],
+ mtk_uart_apdma_irq_handler, IRQF_TRIGGER_NONE,
+ KBUILD_MODNAME, chan);
+ if (ret < 0) {
+ dev_err(chan->device->dev, "Can't request dma IRQ\n");
+ return -EINVAL;
+ }
+ }
+
+ if (mtkd->support_33bits)
+ mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
+
+ return ret;
+}
+
+static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan)
+{
+ struct mtk_uart_apdmadev *mtkd = to_mtk_uart_apdma_dev(chan->device);
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ if (c->requested) {
+ c->requested = false;
+ free_irq(mtkd->dma_irq[chan->chan_id], chan);
+ }
+
+ tasklet_kill(&c->vc.task);
+
+ vchan_free_chan_resources(&c->vc);
+
+ pm_runtime_put_sync(mtkd->ddev.dev);
+}
+
+static enum dma_status mtk_uart_apdma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ enum dma_status ret;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+
+ dma_set_residue(txstate, c->rx_status);
+
+ return ret;
+}
+
+static void mtk_uart_apdma_config_write(struct dma_chan *chan,
+ struct dma_slave_config *cfg,
+ enum dma_transfer_direction dir)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdmadev *mtkd =
+ to_mtk_uart_apdma_dev(c->vc.chan.device);
+ unsigned int tmp;
+
+ if (mtk_uart_apdma_read(c, VFF_EN) == VFF_EN_B)
+ return;
+
+ c->dir = dir;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ tmp = cfg->src_port_window_size;
+
+ mtk_uart_apdma_write(c, VFF_ADDR, cfg->src_addr);
+ mtk_uart_apdma_write(c, VFF_LEN, tmp);
+ mtk_uart_apdma_write(c, VFF_THRE, VFF_RX_THRE(tmp));
+ mtk_uart_apdma_write(c, VFF_INT_EN,
+ VFF_RX_INT_EN0_B | VFF_RX_INT_EN1_B);
+ mtk_uart_apdma_write(c, VFF_RPT, 0);
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
+ } else if (dir == DMA_MEM_TO_DEV) {
+ tmp = cfg->dst_port_window_size;
+
+ mtk_uart_apdma_write(c, VFF_ADDR, cfg->dst_addr);
+ mtk_uart_apdma_write(c, VFF_LEN, tmp);
+ mtk_uart_apdma_write(c, VFF_THRE, VFF_TX_THRE(tmp));
+ mtk_uart_apdma_write(c, VFF_WPT, 0);
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
+ }
+
+ mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B);
+
+ if (mtkd->support_33bits)
+ mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_B);
+
+ if (mtk_uart_apdma_read(c, VFF_EN) != VFF_EN_B)
+ dev_err(chan->device->dev, "dir[%d] fail\n", dir);
+}
+
+/*
+ * dmaengine_prep_slave_single will call the function. and sglen is 1.
+ * 8250 uart using one ring buffer, and deal with one sg.
+ */
+static struct dma_async_tx_descriptor *mtk_uart_apdma_prep_slave_sg
+ (struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sglen, enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct mtk_uart_apdma_desc *d;
+
+ if (!is_slave_direction(dir))
+ return NULL;
+
+ mtk_uart_apdma_config_write(chan, &c->cfg, dir);
+
+ /* Now allocate and setup the descriptor */
+ d = kzalloc(sizeof(*d), GFP_ATOMIC);
+ if (!d)
+ return NULL;
+
+ /* sglen is 1 */
+ d->avail_len = sg_dma_len(sgl);
+ c->rx_status = d->avail_len;
+
+ return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static void mtk_uart_apdma_issue_pending(struct dma_chan *chan)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ struct virt_dma_desc *vd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (vchan_issue_pending(&c->vc)) {
+ vd = vchan_next_desc(&c->vc);
+ c->desc = to_mtk_uart_apdma_desc(&vd->tx);
+ }
+
+ if (c->dir == DMA_DEV_TO_MEM)
+ mtk_uart_apdma_start_rx(c);
+ else if (c->dir == DMA_MEM_TO_DEV)
+ mtk_uart_apdma_start_tx(c);
+
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static int mtk_uart_apdma_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+
+ memcpy(&c->cfg, config, sizeof(*config));
+
+ return 0;
+}
+
+static int mtk_uart_apdma_terminate_all(struct dma_chan *chan)
+{
+ struct mtk_chan *c = to_mtk_uart_apdma_chan(chan);
+ unsigned long flags;
+ unsigned int tmp;
+ int ret;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+
+ mtk_uart_apdma_write(c, VFF_FLUSH, VFF_FLUSH_B);
+ /* Wait 1sec for flush, can't sleep */
+ ret = readx_poll_timeout(readl, c->base + VFF_FLUSH, tmp,
+ tmp != VFF_FLUSH_B, 0, 1000000);
+ if (ret)
+ dev_err(c->vc.chan.device->dev, "flush: fail, debug=0x%x\n",
+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
+
+ /* set stop as 1 -> wait until en is 0 -> set stop as 0 */
+ mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_B);
+ ret = readx_poll_timeout(readl, c->base + VFF_EN, tmp, !tmp, 10, 100);
+ if (ret)
+ dev_err(c->vc.chan.device->dev, "stop: fail, debug=0x%x\n",
+ mtk_uart_apdma_read(c, VFF_DEBUG_STATUS));
+
+ mtk_uart_apdma_write(c, VFF_STOP, VFF_STOP_CLR_B);
+ mtk_uart_apdma_write(c, VFF_INT_EN, VFF_INT_EN_CLR_B);
+
+ if (c->dir == DMA_DEV_TO_MEM)
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
+ else if (c->dir == DMA_MEM_TO_DEV)
+ mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
+
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return 0;
+}
+
+static int mtk_uart_apdma_device_pause(struct dma_chan *chan)
+{
+ /* just for check caps pass */
+ dev_err(chan->device->dev, "Pause can't support\n");
+
+ return 0;
+}
+
+static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd)
+{
+ while (!list_empty(&mtkd->ddev.channels)) {
+ struct mtk_chan *c = list_first_entry(&mtkd->ddev.channels,
+ struct mtk_chan, vc.chan.device_node);
+
+ list_del(&c->vc.chan.device_node);
+ tasklet_kill(&c->vc.task);
+ }
+}
+
+static const struct of_device_id mtk_uart_apdma_match[] = {
+ { .compatible = "mediatek,mt6577-uart-dma", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match);
+
+static int mtk_uart_apdma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mtk_uart_apdmadev *mtkd;
+ struct resource *res;
+ struct mtk_chan *c;
+ int bit_mask = 32, rc;
+ unsigned int i;
+
+ mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
+ if (!mtkd)
+ return -ENOMEM;
+
+ mtkd->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mtkd->clk)) {
+ dev_err(&pdev->dev, "No clock specified\n");
+ rc = PTR_ERR(mtkd->clk);
+ return rc;
+ }
+
+ if (of_property_read_bool(np, "mediatek,dma-33bits"))
+ mtkd->support_33bits = true;
+
+ if (mtkd->support_33bits)
+ bit_mask = 33;
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask));
+ if (rc)
+ return rc;
+
+ dma_cap_set(DMA_SLAVE, mtkd->ddev.cap_mask);
+ mtkd->ddev.device_alloc_chan_resources =
+ mtk_uart_apdma_alloc_chan_resources;
+ mtkd->ddev.device_free_chan_resources =
+ mtk_uart_apdma_free_chan_resources;
+ mtkd->ddev.device_tx_status = mtk_uart_apdma_tx_status;
+ mtkd->ddev.device_issue_pending = mtk_uart_apdma_issue_pending;
+ mtkd->ddev.device_prep_slave_sg = mtk_uart_apdma_prep_slave_sg;
+ mtkd->ddev.device_config = mtk_uart_apdma_slave_config;
+ mtkd->ddev.device_pause = mtk_uart_apdma_device_pause;
+ mtkd->ddev.device_terminate_all = mtk_uart_apdma_terminate_all;
+ mtkd->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+ mtkd->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE);
+ mtkd->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ mtkd->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+ mtkd->ddev.dev = &pdev->dev;
+ INIT_LIST_HEAD(&mtkd->ddev.channels);
+
+ mtkd->dma_requests = MTK_UART_APDMA_NR_VCHANS;
+ if (of_property_read_u32(np, "dma-requests", &mtkd->dma_requests)) {
+ dev_info(&pdev->dev,
+ "Using %u as missing dma-requests property\n",
+ MTK_UART_APDMA_NR_VCHANS);
+ }
+
+ mtkd->dma_irq = devm_kcalloc(&pdev->dev, mtkd->dma_requests,
+ sizeof(*mtkd->dma_irq), GFP_KERNEL);
+ if (!mtkd->dma_irq)
+ return -ENOMEM;
+
+ for (i = 0; i < mtkd->dma_requests; i++) {
+ c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ rc = -ENODEV;
+ goto err_no_dma;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ rc = -ENODEV;
+ goto err_no_dma;
+ }
+
+ c->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(c->base)) {
+ rc = PTR_ERR(c->base);
+ goto err_no_dma;
+ }
+ c->requested = false;
+ c->vc.desc_free = mtk_uart_apdma_desc_free;
+ vchan_init(&c->vc, &mtkd->ddev);
+
+ mtkd->dma_irq[i] = platform_get_irq(pdev, i);
+ if ((int)mtkd->dma_irq[i] < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ[%d]\n", i);
+ rc = -EINVAL;
+ goto err_no_dma;
+ }
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+
+ rc = dma_async_device_register(&mtkd->ddev);
+ if (rc)
+ goto rpm_disable;
+
+ platform_set_drvdata(pdev, mtkd);
+
+ /* Device-tree DMA controller registration */
+ rc = of_dma_controller_register(np, of_dma_xlate_by_chan_id, mtkd);
+ if (rc)
+ goto dma_remove;
+
+ return rc;
+
+dma_remove:
+ dma_async_device_unregister(&mtkd->ddev);
+rpm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_no_dma:
+ mtk_uart_apdma_free(mtkd);
+ return rc;
+}
+
+static int mtk_uart_apdma_remove(struct platform_device *pdev)
+{
+ struct mtk_uart_apdmadev *mtkd = platform_get_drvdata(pdev);
+
+ if (pdev->dev.of_node)
+ of_dma_controller_free(pdev->dev.of_node);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ dma_async_device_unregister(&mtkd->ddev);
+ mtk_uart_apdma_free(mtkd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_uart_apdma_suspend(struct device *dev)
+{
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ clk_disable_unprepare(mtkd->clk);
+
+ return 0;
+}
+
+static int mtk_uart_apdma_resume(struct device *dev)
+{
+ int ret;
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev)) {
+ ret = clk_prepare_enable(mtkd->clk);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int mtk_uart_apdma_runtime_suspend(struct device *dev)
+{
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(mtkd->clk);
+
+ return 0;
+}
+
+static int mtk_uart_apdma_runtime_resume(struct device *dev)
+{
+ int ret;
+ struct mtk_uart_apdmadev *mtkd = dev_get_drvdata(dev);
+
+ ret = clk_prepare_enable(mtkd->clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops mtk_uart_apdma_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mtk_uart_apdma_suspend, mtk_uart_apdma_resume)
+ SET_RUNTIME_PM_OPS(mtk_uart_apdma_runtime_suspend,
+ mtk_uart_apdma_runtime_resume, NULL)
+};
+
+static struct platform_driver mtk_uart_apdma_driver = {
+ .probe = mtk_uart_apdma_probe,
+ .remove = mtk_uart_apdma_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &mtk_uart_apdma_pm_ops,
+ .of_match_table = of_match_ptr(mtk_uart_apdma_match),
+ },
+};
+
+module_platform_driver(mtk_uart_apdma_driver);
+
+MODULE_DESCRIPTION("MediaTek UART APDMA Controller Driver");
+MODULE_AUTHOR("Long Cheng <long.cheng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
--
1.7.9.5
^ permalink raw reply related
* [PATCH v11 0/4] add uart DMA function
From: Long Cheng @ 2019-03-07 1:45 UTC (permalink / raw)
To: Vinod Koul, Randy Dunlap, Rob Herring, Mark Rutland, Ryder Lee,
Sean Wang, Nicolas Boichat, Matthias Brugger
Cc: Dan Williams, Greg Kroah-Hartman, Jiri Slaby, Sean Wang,
dmaengine, devicetree, linux-arm-kernel, linux-mediatek,
linux-kernel, linux-serial, srv_heupstream, Yingjoe Chen, YT Shen,
Zhenbao Liu, Long Cheng
In Mediatek SOCs, the uart can support DMA function.
Base on DMA engine formwork, we add the DMA code to support uart. And put the code under drivers/dma/mediatek.
This series contains document bindings, Kconfig to control the function enable or not,
device tree including interrupt and dma device node, the code of UART DMA
Changes compared to v10
-modify DMA tx status function
-modify 8250_mtk for DMA rx
-add notes to binding Document.
Changes compared to v9
-rename dt-bindings file
-remove direction from device_config
-simplified code
Changes compared to v8
-revise missing items
Changes compared to v7:
-modify apdma uart tx
Changes compared to v6:
-Correct spelling
Changes compared to v5:
-move 'requst irqs' to alloc channel
-remove tasklet.
Changes compared to v4:
-modify Kconfig depends on.
Changes compared to v3:
-fix CONFIG_PM, will cause build fail
Changes compared to v2:
-remove unimportant parameters
-instead of cookie, use APIs of virtual channel.
-use of_dma_xlate_by_chan_id.
Changes compared to v1:
-mian revised file, 8250_mtk_dma.c
--parameters renamed for standard
--remove atomic operation
Long Cheng (4):
dmaengine: 8250_mtk_dma: add MediaTek uart DMA support
arm: dts: mt2712: add uart APDMA to device tree
dt-bindings: dma: uart: rename binding
serial: 8250-mtk: modify uart DMA rx
.../devicetree/bindings/dma/8250_mtk_dma.txt | 33 -
.../devicetree/bindings/dma/mtk-uart-apdma.txt | 55 ++
arch/arm64/boot/dts/mediatek/mt2712e.dtsi | 51 ++
drivers/dma/mediatek/Kconfig | 11 +
drivers/dma/mediatek/Makefile | 1 +
drivers/dma/mediatek/mtk-uart-apdma.c | 660 ++++++++++++++++++++
drivers/tty/serial/8250/8250_mtk.c | 53 +-
7 files changed, 801 insertions(+), 63 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/dma/8250_mtk_dma.txt
create mode 100644 Documentation/devicetree/bindings/dma/mtk-uart-apdma.txt
create mode 100644 drivers/dma/mediatek/mtk-uart-apdma.c
--
1.7.9.5
^ permalink raw reply
* Re: [GIT PULL] TTY/Serial patches for 5.1-rc1
From: pr-tracker-bot @ 2019-03-07 1:20 UTC (permalink / raw)
To: Greg KH
Cc: Linus Torvalds, Jiri Slaby, Stephen Rothwell, Andrew Morton,
linux-kernel, linux-serial
In-Reply-To: <20190306103447.GA10773@kroah.com>
The pull request you sent on Wed, 6 Mar 2019 11:34:47 +0100:
> git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tags/tty-5.1-rc1
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/67e79a6dc2664a3ef85113440e60f7aaca3c7815
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.wiki.kernel.org/userdoc/prtracker
^ permalink raw reply
* Re: Serial console is causing system lock-up
From: John Ogness @ 2019-03-06 22:43 UTC (permalink / raw)
To: Steven Rostedt
Cc: Petr Mladek, Nigel Croxon, Theodore Y. Ts'o,
Greg Kroah-Hartman, Sergey Senozhatsky, dm-devel, Mikulas Patocka,
linux-serial
In-Reply-To: <20190306171943.12345598@oasis.local.home>
On 2019-03-06, Steven Rostedt <rostedt@goodmis.org> wrote:
>> This bug only happens if we select large logbuffer (millions of
>> characters). With smaller log buffer, there are messages "** X printk
>> messages dropped", but there's no lockup.
>>
>> The kernel apparently puts 2 million characters into a console log
>> buffer, then takes some lock and than tries to write all of them to a
>> slow serial line.
>>
>> [...]
>>
>> The MD-RAID is supposed to recalculate data for the corrupted device
>> and bring it back to life. However, scrubbing the MD-RAID device
>> resulted in a lot of reads from the device with bad checksums, these
>> were reported to the log and killed the machine.
>>
>> I made a patch to dm-integrity to rate-limit the error messages. But
>> anyway - killing the machine in case of too many log messages seems
>> bad. If the log messages are produced faster than the kernel can
>> write them, the kernel should discard some of them, not kill itself.
>
> Sounds like another aurgment for the new printk design.
Assuming the bad checksum messages are considered an emergency (for
example, at least loglevel KERN_WARN), then the new printk design would
print those messages synchronously to the slow serial line in the
context of the driver as the driver is producing them.
There wouldn't be a lock-up, but it would definitely slow down the
driver. The situation of "messages being produced faster than the kernel
can write them" would never exist because the printk() call will only
return after the writing is completed. I am curious if that would be
acceptable here?
John Ogness
^ permalink raw reply
* Re: [RFC PATCH v1 08/25] printk: add ring buffer and kthread
From: John Ogness @ 2019-03-06 22:22 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, linux-kernel, Peter Zijlstra, Steven Rostedt,
Daniel Wang, Andrew Morton, Linus Torvalds, Greg Kroah-Hartman,
Alan Cox, Jiri Slaby, Peter Feiner, linux-serial,
Sergey Senozhatsky
In-Reply-To: <87r2bjbt47.fsf@linutronix.de>
On 2019-03-06, Petr Mladek <pmladek@suse.com> wrote:
>> _Both_ categories are important for the user, but their requirements
>> are different:
>>
>> informational: non-disturbing
>> emergency: reliable
>
> Isn't this already handled by the console_level?
>
> The informational messages can be reliably read via syslog, /dev/kmsg.
> They are related to the normal works when the system works well.
>
> The emergency messages (errors, warnings) are printed in emergency
> situations. They are printed as reliably as possible to the console
> because the userspace might not be reliable enough.
I've never viewed console_level this way. _If_ console_level really is
supposed to define the emergency/informational boundary, all
informational messages are supposed to be handled by userspace, and
console printing's main objective is reliability... then I would change
my proposal such that:
- if a console supports write_atomic(), _all_ console printing for that
console would use write_atomic()
- only consoles without write_atomic() will be printing via the
printk-kthread(s)
IMO, for consoles with write_atomic(), this would increase reliability
over the current mainline implementation. It would also simplify
write_atomic() implementations because they would no longer need to
synchronize against write().
For those consoles that cannot implement write_atomic() (vt and
netconsole come to mind), or as a transition period until remaining
console drivers have implemented write_atomic(), these would use the
"fallback" of printing fully preemptively in their own kthread using
write().
Does this better align with the concept of the console_loglevel and the
purpose of console printing?
John Ogness
^ permalink raw reply
* Re: Serial console is causing system lock-up
From: Steven Rostedt @ 2019-03-06 22:19 UTC (permalink / raw)
To: Mikulas Patocka
Cc: Petr Mladek, Nigel Croxon, Theodore Y. Ts'o, John Ogness,
Kroah-Hartman, Sergey Senozhatsky, dm-devel, Greg, linux-serial
In-Reply-To: <alpine.LRH.2.02.1903061157530.3129@file01.intranet.prod.int.rdu2.redhat.com>
On Wed, 6 Mar 2019 12:11:10 -0500 (EST)
Mikulas Patocka <mpatocka@redhat.com> wrote:
> On Wed, 6 Mar 2019, Theodore Y. Ts'o wrote:
>
> > On Wed, Mar 06, 2019 at 11:07:55AM -0500, Mikulas Patocka wrote:
> > > This bug only happens if we select large logbuffer (millions of
> > > characters). With smaller log buffer, there are messages "** X printk
> > > messages dropped", but there's no lockup.
> > >
> > > The kernel apparently puts 2 million characters into a console log buffer,
> > > then takes some lock and than tries to write all of them to a slow serial
> > > line.
> >
> > What are the messages; from what kernel subsystem? Why are you seeing
> > so many log messages?
> >
> > - Ted
>
> The dm-integity subsystem (drivers/md/dm-integrity.c) can be attached to a
> block device to provide checksum protection. It will return -EILSEQ and
> print a message to a log for every corrupted block.
>
> Nigel Croxon was testing MD-RAID recovery capabilities in such a way that
> he activated RAID-5 array with one leg replaced by a dm-integrity block
> device that had all checksums invalid.
>
> The MD-RAID is supposed to recalculate data for the corrupted device and
> bring it back to life. However, scrubbing the MD-RAID device resulted in a
> lot of reads from the device with bad checksums, these were reported to
> the log and killed the machine.
>
>
> I made a patch to dm-integrity to rate-limit the error messages. But
> anyway - killing the machine in case of too many log messages seems bad.
> If the log messages are produced faster than the kernel can write them,
> the kernel should discard some of them, not kill itself.
Sounds like another aurgment for the new printk design.
-- Steve
^ permalink raw reply
* Re: [RFC PATCH v1 08/25] printk: add ring buffer and kthread
From: John Ogness @ 2019-03-06 21:17 UTC (permalink / raw)
To: Petr Mladek
Cc: Sergey Senozhatsky, linux-kernel, Peter Zijlstra, Steven Rostedt,
Daniel Wang, Andrew Morton, Linus Torvalds, Greg Kroah-Hartman,
Alan Cox, Jiri Slaby, Peter Feiner, linux-serial,
Sergey Senozhatsky
In-Reply-To: <20190306155701.wc22i2no5swdcids@pathway.suse.cz>
On 2019-03-06, Petr Mladek <pmladek@suse.com> wrote:
>> I would like to clarify that message supression (i.e. console loglevel)
>> is a method of reducing what is printed. It does nothing to address the
>> issues related to console printing. My proposal focusses on addressing
>> the issues related to console printing.
>>
>> Console printing is a convenient feature to allow a kernel to
>> communicate information to a user without any reliance on
>> userspace. IMHO there are 2 categories of messages that the kernel will
>> communicate. The first is informational (usb events, wireless and
>> ethernet connectivity, filesystem events, etc.). Since this category of
>> messages occurs during normal runtime, we should expect that it does not
>> cause adverse effects to the rest of the system (such as latencies and
>> non-deterministic behavior).
>>
>> The second category is for emergency situations, where the kernel needs
>> to report something unusual (panic, BUG, WARN, etc.). In some of these
>> situations, it may be the last thing the kernel ever does. We should
>> expect this category to focus on getting the message out as reliably as
>> possible. Even if it means disturbing the system with large latencies.
>>
>> _Both_ categories are important for the user, but their requirements are
>> different:
>>
>> informational: non-disturbing
>> emergency: reliable
>
> Isn't this already handled by the console_level?
You mean that the current console level is being used to set the
boundary between emergency and informational messages? Definitely no!
Take any Linux distribution and look at their default console_level
setting. Even the kernel code defaults to a value of 7!
> The informational messages can be reliably read via syslog, /dev/kmsg.
> They are related to the normal works when the system works well.
Yes, this is how things _could_ be. But why are users currently using
the kernel's console printing for informational messages? And why is the
kernel code encouraging it? Perhaps because users like being able to
receive messages without relying on userspace tools? IMO it is this mass
use of console printing for informational messages that is preventing
the implementation from becoming optimally reliable.
My proposal is making this distinction clearer: a significant increase
in reliability for emergency messages, and a fully preemptible printer
for informational messages. The fully preemptible printer will work just
as well as any userspace tool, but doesn't require userspace. Not
requiring userspace seems to me to be the part users are interested
in.
(But I might be wrong on this. Perhaps Linux is just "marketing" its
console printing feature incorrectly and users aren't aware that it is
only meant for emergencies.)
> The emergency messages (errors, warnings) are printed in emergency
> situations. They are printed as reliably as possible to the console
> because the userspace might not be reliable enough.
As reliably as _possible_? I hope that my series at least helps to show
that we can do a lot better about reliability.
> That said, the "atomic" consoles brings new possibilities
> and might be very useful in some scenarios. Also a more grained
> prioritization might be helpful.
>
> But each solution might just bring new problems. For example,
> the atomic consoles are still serialized between CPUs. It might
> slow down the entire system and not only on task.
Why is that a problem? The focus is reliabilty. We are talking about
emergency messages here. Messages that should never occur for a
correctly functioning system. It does not matter if the entire system
slows down because of it.
> If it gets blocked for some reasons (nobody is perfect) it would
> block all the other serialized CPUs as well.
Yes, blocking in an atomic context would be bad for any code.
> In each case, we really need to be careful about the complexity.
> printk() is already complex enough. It might be acceptable if
> it makes the design cleaner and less tangled. printk() would
> deserve a redesign.
It is my belief that I am significantly simplifying printk because there
are no more exotic contexts and situations. Emergency messages are
atomic and immediate. Context does not matter. Informational messages
are printed fully preemptible, so console drivers are free to do
whatever magic they want to do. Do you see that as more complex than the
current implementation of safe buffers, defers, hand-offs, exclusive
consoles, and cond_rescheds?
John Ogness
^ permalink raw reply
* Re: Serial console is causing system lock-up
From: Mikulas Patocka @ 2019-03-06 17:11 UTC (permalink / raw)
To: Theodore Y. Ts'o
Cc: Petr Mladek, Nigel Croxon, Greg Kroah-Hartman, Steven Rostedt,
Sergey Senozhatsky, dm-devel, linux-serial
In-Reply-To: <20190306163003.GA31858@mit.edu>
On Wed, 6 Mar 2019, Theodore Y. Ts'o wrote:
> On Wed, Mar 06, 2019 at 11:07:55AM -0500, Mikulas Patocka wrote:
> > This bug only happens if we select large logbuffer (millions of
> > characters). With smaller log buffer, there are messages "** X printk
> > messages dropped", but there's no lockup.
> >
> > The kernel apparently puts 2 million characters into a console log buffer,
> > then takes some lock and than tries to write all of them to a slow serial
> > line.
>
> What are the messages; from what kernel subsystem? Why are you seeing
> so many log messages?
>
> - Ted
The dm-integity subsystem (drivers/md/dm-integrity.c) can be attached to a
block device to provide checksum protection. It will return -EILSEQ and
print a message to a log for every corrupted block.
Nigel Croxon was testing MD-RAID recovery capabilities in such a way that
he activated RAID-5 array with one leg replaced by a dm-integrity block
device that had all checksums invalid.
The MD-RAID is supposed to recalculate data for the corrupted device and
bring it back to life. However, scrubbing the MD-RAID device resulted in a
lot of reads from the device with bad checksums, these were reported to
the log and killed the machine.
I made a patch to dm-integrity to rate-limit the error messages. But
anyway - killing the machine in case of too many log messages seems bad.
If the log messages are produced faster than the kernel can write them,
the kernel should discard some of them, not kill itself.
Mikulas
^ permalink raw reply
* Re: Serial console is causing system lock-up
From: Theodore Y. Ts'o @ 2019-03-06 16:30 UTC (permalink / raw)
To: Mikulas Patocka
Cc: Petr Mladek, Nigel Croxon, Greg Kroah-Hartman, Steven Rostedt,
Sergey Senozhatsky, dm-devel, linux-serial
In-Reply-To: <alpine.LRH.2.02.1903061031420.16905@file01.intranet.prod.int.rdu2.redhat.com>
On Wed, Mar 06, 2019 at 11:07:55AM -0500, Mikulas Patocka wrote:
> This bug only happens if we select large logbuffer (millions of
> characters). With smaller log buffer, there are messages "** X printk
> messages dropped", but there's no lockup.
>
> The kernel apparently puts 2 million characters into a console log buffer,
> then takes some lock and than tries to write all of them to a slow serial
> line.
What are the messages; from what kernel subsystem? Why are you seeing
so many log messages?
- Ted
^ permalink raw reply
* Re: Serial console is causing system lock-up
From: Mikulas Patocka @ 2019-03-06 16:07 UTC (permalink / raw)
To: Petr Mladek
Cc: Nigel Croxon, Greg Kroah-Hartman, Steven Rostedt,
Sergey Senozhatsky, dm-devel, linux-serial
In-Reply-To: <20190306152218.eocv4zulf7tv2mkc@pathway.suse.cz>
On Wed, 6 Mar 2019, Petr Mladek wrote:
> On Wed 2019-03-06 09:27:13, Mikulas Patocka wrote:
> > Hi
> >
> > I was debugging some kernel lockup with storage drivers and it turned out
> > that the lockup is caused by the serial console subsystem. If we use
> > serial console and if we write to it excessively, the kernel sometimes
> > lockup, sometimes reports rcu stalls and NMI backtraces. Sometimes it will
> > just print the console messages without donig anything else.
>
> This is a very old problem that we have been trying to solve for
> years. There are two conflicting requirements on printk():
> be fast and reliable.
>
> The historical solution is that printk() callers store the messages
> into the log buffer and then just _try_ to take the console lock.
> The winner who succeeds is responsible for flushing all
> pending messages to the console. As a result a random victim
> might get blocked by the console handling for a long time.
This bug only happens if we select large logbuffer (millions of
characters). With smaller log buffer, there are messages "** X printk
messages dropped", but there's no lockup.
The kernel apparently puts 2 million characters into a console log buffer,
then takes some lock and than tries to write all of them to a slow serial
line.
> An obvious solution is offloading the console handling. But
> it is against the reliability. There are no guarantees that
> the offload mechanism (kthread, irq) would happen when the
> system is on their knees.
>
> Anyway, which kernel version are you using, please?
RHEL8-4.18, Debian-4.19, Upstream 5.0. I didn't try older versions.
> I wonder if you already have the dbdda842fe96f8932 ("printk: Add
> console owner and waiter logic to load balance console writes").
> It improves the situation a lot. There was a hope that it would
> be enough in the real life.
Yes - this patch is present in the kernels that I tried.
> > This program tests the issue - on framebuffer console, the system is
> > sluggish, but it is possible to unload the module with rmmod. On serial
> > console, it locks up to the point that unloading the module is not
> > possible.
>
> Is there any chance to send us logs from the original (real life)
> problem, please?
>
> Best regards,
> Petr
I uploaded the logs here:
http://people.redhat.com/~mpatocka/testcases/console-lockup/
Mikulas
^ permalink raw reply
* Re: [RFC PATCH v1 08/25] printk: add ring buffer and kthread
From: Petr Mladek @ 2019-03-06 15:57 UTC (permalink / raw)
To: John Ogness
Cc: Sergey Senozhatsky, linux-kernel, Peter Zijlstra, Steven Rostedt,
Daniel Wang, Andrew Morton, Linus Torvalds, Greg Kroah-Hartman,
Alan Cox, Jiri Slaby, Peter Feiner, linux-serial,
Sergey Senozhatsky
In-Reply-To: <87o96p9gtx.fsf@linutronix.de>
On Tue 2019-03-05 22:00:58, John Ogness wrote:
> Hi Sergey,
>
> Thanks for your feedback.
>
> I am responding to this comment ahead of your previous comments because
> it really cuts at the heart of the proposed design. After addressing
> this point it will make it easier for me to respond to your other
> comments.
>
> NOTE: This is a lengthy response.
>
> On 2019-03-04, Sergey Senozhatsky <sergey.senozhatsky@gmail.com> wrote:
> >> But in general, channels which depend on preemptible printk will
> >> become totally useless in some cases.
> >
> > Which brings me to a question - what are those messages/channels? Not
> > important enough to be printed on consoles immediately, yet important
> > enough to pass the suppress_message_printing() check.
>
> I would like to clarify that message supression (i.e. console loglevel)
> is a method of reducing what is printed. It does nothing to address the
> issues related to console printing. My proposal focusses on addressing
> the issues related to console printing.
>
> Console printing is a convenient feature to allow a kernel to
> communicate information to a user without any reliance on
> userspace. IMHO there are 2 categories of messages that the kernel will
> communicate. The first is informational (usb events, wireless and
> ethernet connectivity, filesystem events, etc.). Since this category of
> messages occurs during normal runtime, we should expect that it does not
> cause adverse effects to the rest of the system (such as latencies and
> non-deterministic behavior).
>
> The second category is for emergency situations, where the kernel needs
> to report something unusual (panic, BUG, WARN, etc.). In some of these
> situations, it may be the last thing the kernel ever does. We should
> expect this category to focus on getting the message out as reliably as
> possible. Even if it means disturbing the system with large latencies.
>
> _Both_ categories are important for the user, but their requirements are
> different:
>
> informational: non-disturbing
> emergency: reliable
Isn't this already handled by the console_level?
The informational messages can be reliably read via syslog, /dev/kmsg.
They are related to the normal works when the system works well.
The emergency messages (errors, warnings) are printed in emergency
situations. They are printed as reliably as possible to the console
because the userspace might not be reliable enough.
That said, the "atomic" consoles brings new possibilities
and might be very useful in some scenarios. Also a more grained
prioritization might be helpful.
But each solution might just bring new problems. For example,
the atomic consoles are still serialized between CPUs. It might
slow down the entire system and not only on task. If it gets
blocked for some reasons (nobody is perfect) it would block
all the other serialized CPUs as well.
In each case, we really need to be careful about the complexity.
printk() is already complex enough. It might be acceptable if
it makes the design cleaner and less tangled. printk() would
deserve a redesign.
Best Regards,
Petr
^ permalink raw reply
* Re: Serial console is causing system lock-up
From: Petr Mladek @ 2019-03-06 15:22 UTC (permalink / raw)
To: Mikulas Patocka
Cc: Nigel Croxon, Greg Kroah-Hartman, Steven Rostedt,
Sergey Senozhatsky, dm-devel, linux-serial
In-Reply-To: <alpine.LRH.2.02.1903060917280.5335@file01.intranet.prod.int.rdu2.redhat.com>
On Wed 2019-03-06 09:27:13, Mikulas Patocka wrote:
> Hi
>
> I was debugging some kernel lockup with storage drivers and it turned out
> that the lockup is caused by the serial console subsystem. If we use
> serial console and if we write to it excessively, the kernel sometimes
> lockup, sometimes reports rcu stalls and NMI backtraces. Sometimes it will
> just print the console messages without donig anything else.
This is a very old problem that we have been trying to solve for
years. There are two conflicting requirements on printk():
be fast and reliable.
The historical solution is that printk() callers store the messages
into the log buffer and then just _try_ to take the console lock.
The winner who succeeds is responsible for flushing all
pending messages to the console. As a result a random victim
might get blocked by the console handling for a long time.
An obvious solution is offloading the console handling. But
it is against the reliability. There are no guarantees that
the offload mechanism (kthread, irq) would happen when the
system is on their knees.
Anyway, which kernel version are you using, please?
I wonder if you already have the dbdda842fe96f8932 ("printk: Add
console owner and waiter logic to load balance console writes").
It improves the situation a lot. There was a hope that it would
be enough in the real life.
> This program tests the issue - on framebuffer console, the system is
> sluggish, but it is possible to unload the module with rmmod. On serial
> console, it locks up to the point that unloading the module is not
> possible.
Is there any chance to send us logs from the original (real life)
problem, please?
Best regards,
Petr
^ permalink raw reply
* Serial console is causing system lock-up
From: Mikulas Patocka @ 2019-03-06 14:27 UTC (permalink / raw)
To: Greg Kroah-Hartman, Petr Mladek, Sergey Senozhatsky, linux-serial
Cc: dm-devel, Nigel Croxon
Hi
I was debugging some kernel lockup with storage drivers and it turned out
that the lockup is caused by the serial console subsystem. If we use
serial console and if we write to it excessively, the kernel sometimes
lockup, sometimes reports rcu stalls and NMI backtraces. Sometimes it will
just print the console messages without donig anything else.
This program tests the issue - on framebuffer console, the system is
sluggish, but it is possible to unload the module with rmmod. On serial
console, it locks up to the point that unloading the module is not
possible.
Mikulas
------------
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/kthread.h>
int n_threads = 0;
int sleep_ms = 0;
module_param_named(n_threads, n_threads, int, S_IRUGO);
MODULE_PARM_DESC(n_threads, "The number of kernel threads");
module_param_named(sleep_ms, sleep_ms, int, S_IRUGO);
MODULE_PARM_DESC(sleep_ms, "Sleep time in milliseconds");
struct task_struct **tasks;
static int console_dumper(void *data)
{
int t = (int)(long)data;
while (!kthread_should_stop()) {
printk("line from thread %d ---------------------------------------------------------------------------------------------------------------------------------------\n", t);
if (sleep_ms > 0)
msleep(sleep_ms);
cond_resched();
}
return 0;
}
static int __init dump_init(void)
{
int i;
if (n_threads <= 0)
n_threads = num_online_cpus();
tasks = kmalloc(n_threads * sizeof(struct task_struct *), GFP_KERNEL);
if (!tasks)
return -ENOMEM;
for (i = 0; i < n_threads; i++) {
tasks[i] = kthread_create(console_dumper, (void *)(long)i, "console_dumper");
if (!tasks[i]) {
while (i--)
kthread_stop(tasks[i]);
kfree(tasks);
return -ENOMEM;
}
wake_up_process(tasks[i]);
}
return 0;
}
static void __exit dump_exit(void)
{
int i;
for (i = 0; i < n_threads; i++) {
kthread_stop(tasks[i]);
}
kfree(tasks);
}
module_init(dump_init)
module_exit(dump_exit)
MODULE_LICENSE("GPL");
^ permalink raw reply
* Re: [PATCH v2] tty: xilinx_uartps: Correct return value in probe
From: Michal Simek @ 2019-03-06 12:34 UTC (permalink / raw)
To: Rajan Vaja, gregkh, jslaby
Cc: linux-kernel, JOLLYS, anirudh, linux-serial, linux-arm-kernel
In-Reply-To: <1551433028-25207-1-git-send-email-rajan.vaja@xilinx.com>
On 01. 03. 19 10:37, Rajan Vaja wrote:
> Existing driver checks for alternate clock if devm_clk_get() fails
> and returns error code for last clock failure. If xilinx_uartps is
> called before clock driver, devm_clk_get() returns -EPROBE_DEFER.
> In this case, probe should not check for alternate clock as main
> clock is already present in DTS and return -EPROBE_DEFER only.
>
> This patch fixes it by not checking for alternate clock when main
> clock get returns -EPROBE_DEFER.
>
> Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com>
> ---
> Changes in v2:
> * Handle dynamic port allocation error cases
> ---
> drivers/tty/serial/xilinx_uartps.c | 32 +++++++++++++++++++-------------
> 1 file changed, 19 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
> index 094f295..f7ae73e 100644
> --- a/drivers/tty/serial/xilinx_uartps.c
> +++ b/drivers/tty/serial/xilinx_uartps.c
> @@ -1547,27 +1547,33 @@ static int cdns_uart_probe(struct platform_device *pdev)
> }
>
> cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk");
> + if (PTR_ERR(cdns_uart_data->pclk) == -EPROBE_DEFER) {
> + rc = PTR_ERR(cdns_uart_data->pclk);
> + goto err_out_unregister_driver;
> + }
> +
> if (IS_ERR(cdns_uart_data->pclk)) {
> cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk");
> - if (!IS_ERR(cdns_uart_data->pclk))
> - dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
> + if (IS_ERR(cdns_uart_data->pclk)) {
> + rc = PTR_ERR(cdns_uart_data->pclk);
> + goto err_out_unregister_driver;
> + }
> + dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
> }
> - if (IS_ERR(cdns_uart_data->pclk)) {
> - dev_err(&pdev->dev, "pclk clock not found.\n");
> - rc = PTR_ERR(cdns_uart_data->pclk);
> +
> + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk");
> + if (PTR_ERR(cdns_uart_data->uartclk) == -EPROBE_DEFER) {
> + rc = PTR_ERR(cdns_uart_data->uartclk);
> goto err_out_unregister_driver;
> }
>
> - cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk");
> if (IS_ERR(cdns_uart_data->uartclk)) {
> cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk");
> - if (!IS_ERR(cdns_uart_data->uartclk))
> - dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
> - }
> - if (IS_ERR(cdns_uart_data->uartclk)) {
> - dev_err(&pdev->dev, "uart_clk clock not found.\n");
> - rc = PTR_ERR(cdns_uart_data->uartclk);
> - goto err_out_unregister_driver;
> + if (IS_ERR(cdns_uart_data->uartclk)) {
> + rc = PTR_ERR(cdns_uart_data->uartclk);
> + goto err_out_unregister_driver;
> + }
> + dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
> }
>
> rc = clk_prepare_enable(cdns_uart_data->pclk);
>
Reviewed-by: Michal Simek <michal.simek@xilinx.com>
Thanks,
Michal
^ permalink raw reply
* Re: [PATCH v3 0/2] tty: serial: add DT bindings and serial driver for the SiFive FU540 UART
From: Andreas Schwab @ 2019-03-06 11:08 UTC (permalink / raw)
To: Paul Walmsley
Cc: linux-serial, Greg Kroah-Hartman, Jiri Slaby, Palmer Dabbelt,
Wesley Terpstra, linux-riscv, linux-kernel, devicetree
In-Reply-To: <20181020101045.15991-1-paul.walmsley@sifive.com>
Trying to log in on the serial console causes the system to freeze. The
last message is:
[ 115.597858] sifive-serial 10010000.serial: BRKINT/PARMRK flag not supported
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply
* [GIT PULL] TTY/Serial patches for 5.1-rc1
From: Greg KH @ 2019-03-06 10:34 UTC (permalink / raw)
To: Linus Torvalds
Cc: Jiri Slaby, Stephen Rothwell, Andrew Morton, linux-kernel,
linux-serial
The following changes since commit d13937116f1e82bf508a6325111b322c30c85eb9:
Linux 5.0-rc6 (2019-02-10 14:42:20 -0800)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tags/tty-5.1-rc1
for you to fetch changes up to f694936c3b5a4c140ded5b673555d95aedc866b9:
tty: xilinx_uartps: Correct return value in probe (2019-03-01 18:07:34 +0100)
----------------------------------------------------------------
TTY/Serial patches for 5.1-rc1
Here is the "big" patchset for the tty/serial driver layer for 5.1-rc1.
It's really not all that big, nothing major here.
There are a lot of tiny driver fixes and updates, combined with other
cleanups for different serial drivers and the vt layer. Full details
are in the shortlog.
All of these have been in linux-next with no reported issues.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----------------------------------------------------------------
Alexander Shiyan (2):
serial: clps711x: Remove board support
serial: max310x: Correction of the initial setting of the MODE1 bits for various supported ICs.
Alexandre Belloni (1):
tty: serial: lpc32xx_hs: fix missing console boot messages
Andreas Schwab (1):
tty/serial: use uart_console_write in the RISC-V SBL early console
Anssi Hannula (1):
serial: uartps: Fix stuck ISR if RX disabled with non-empty FIFO
Atsushi Nemoto (3):
serial: fsl_lpuart: specify transmit FIFO size for 32-bit variant
serial: fsl_lpuart: consider TX FIFO too in lpuart32_tx_empty
serial: fsl_lpuart: DMA support for 32-bit variant
Chengguang Xu (1):
tty: serial: remove redundant likely annotation
Christoph Hellwig (1):
tty/synclink: remove ISA support
Geert Uytterhoeven (4):
serial: sh-sci: Extract sci_dma_rx_chan_invalidate()
serial: sh-sci: Extract sci_dma_rx_reenable_irq()
serial: sh-sci: Fix fallback to PIO in sci_dma_rx_complete()
serial: sh-sci: Make RX/TX DMA function names consistent
Greg Kroah-Hartman (4):
Merge 5.0-rc2 into tty-next
Merge 5.0-rc4 into tty-next
tty: ldisc: add sysctl to prevent autoloading of ldiscs
Merge 5.0-rc6 into tty-next
Gustavo A. R. Silva (5):
sc16is7xx: Use struct_size() in devm_kzalloc()
serial: max310x: Use struct_size() in devm_kzalloc()
tty/n_hdlc: mark expected switch fall-through
tty: n_gsm: Mark expected switch fall-throughs
tty: hvc_xen: Mark expected switch fall-through
Heikki Krogerus (1):
serial: 8250_pci: Replace custom code with pci_match_id()
Hugo Lefeuvre (1):
tty/nozomi: use pci_iomap instead of ioremap_nocache
Jay Dolan (2):
serial: 8250_pci: Fix number of ports for ACCES serial cards
serial: 8250_pci: Have ACCES cards that use the four port Pericom PI7C9X7954 chip use the pci_pericom_setup()
Jeffrey Hugo (1):
tty: serial: msm_serial: Remove __init from msm_console_setup()
Johan Hovold (1):
serdev: ttyport: call tiocmget and tiocmset ops directly
Kangjie Lu (1):
serial: max310x: pass return value of spi_register_driver
Lanqing Liu (1):
serial: sprd: Modify the baud rate calculation formula
Linus Walleij (1):
serial: 8250: Add OF support for Xscale variant
Loys Ollivier (1):
tty: serial: meson: if no alias specified use an available id
Lubomir Rintel (2):
serial: 8250_pxa: honor the port number from devicetree
serial: 8250_of: assume reg-shift of 2 for mrvl,mmp-uart
Manfred Schlaegl (1):
tty: increase the default flip buffer limit to 2*640K
Martin Hostettler (4):
vt: refactor vc_ques to allow of other private sequences.
vt: Implement parsing for >, =, < private sequences.
vt: ignore csi sequences with intermediate characters.
vt: ignore sequences that contain ':' in parameters.
Mathieu Malaterre (1):
vt: annotate implicit fall throughs
Mikko Perttunen (1):
dt-bindings: serial: Add bindings for nvidia,tegra194-tcu
Nathan Chancellor (1):
serial: mps2-uart: Add parentheses around conditional in mps2_uart_shutdown
Nicolas Pitre (5):
vcsa: clamp header values when they don't fit
vcs: poll(): cope with a deallocated vt
vcs: fasync(): make it consistent with poll()
vcs: restore and document initial POLLPRI event
vt: perform safe console erase in the right order
Petr Mladek (2):
sysrq: Restore original console_loglevel when sysrq disabled
sysrq: Remove duplicated sysrq message
Rajan Vaja (1):
tty: xilinx_uartps: Correct return value in probe
Rob Herring (3):
dt-bindings: serial: Convert snps,dw-apb-uart to json-schema
dt-bindings: serial: Move renesas,rzn1-uart into the snps-dw-apb-uart binding
dt-bindings: serial: Convert arm,pl011 to json-schema
Ryan Case (4):
tty: serial: qcom_geni_serial: Remove use of *_relaxed() and mb()
tty: serial: qcom_geni_serial: Remove set_rfr_wm() and related variables
tty: serial: qcom_geni_serial: Remove xfer_mode variable
tty: serial: qcom_geni_serial: Use u32 for register variables
Stuart Menefy (1):
tty: serial: samsung: Enable baud clock during initialisation
Sugaya Taichi (1):
dt-bindings: serial: Add Milbeaut serial driver description
Thierry Reding (1):
serial: Add Tegra Combined UART driver
Tony Lindgren (2):
tty: n_gsm: Add copy_config() and gsm_config() to prepare for serdev
n_gsm: Constify u8 and unsigned char usage
Ulrich Hecht (1):
dt-bindings: serial: sh-sci: Document r8a7778/9 HSCIF bindings
Valentin Vidic (1):
n_tty: update comment for WAKEUP_CHARS define
Vignesh R (3):
serial: 8250_omap: Drop check for of_node
dt-bindings: serial: omap_serial: add clocks entry
serial: 8250_omap: Use clk_get_rate() to obtain fclk frequency
Vladimir Murzin (2):
serial: mps2-uart: move to dynamic port allocation
serial: mps2-uart: support combined irq
YueHaibing (1):
tty: ipwireless: Fix potential NULL pointer dereference
Zhou Yanjie (2):
Serial: Ingenic: Add support for the X1000.
Dt-bindings: Serial: Add X1000 serial bindings.
Documentation/devicetree/bindings/serial/8250.txt | 1 +
.../devicetree/bindings/serial/ingenic,uart.txt | 3 +-
.../devicetree/bindings/serial/milbeaut-uart.txt | 21 ++
.../bindings/serial/nvidia,tegra194-tcu.txt | 35 +++
.../devicetree/bindings/serial/omap_serial.txt | 2 +
Documentation/devicetree/bindings/serial/pl011.txt | 51 ----
.../devicetree/bindings/serial/pl011.yaml | 126 +++++++++
.../bindings/serial/renesas,rzn1-uart.txt | 10 -
.../bindings/serial/renesas,sci-serial.txt | 2 +
.../bindings/serial/snps-dw-apb-uart.txt | 76 ------
.../bindings/serial/snps-dw-apb-uart.yaml | 140 ++++++++++
drivers/tty/Kconfig | 24 ++
drivers/tty/hvc/hvc_xen.c | 2 +-
drivers/tty/ipwireless/hardware.c | 2 +
drivers/tty/n_gsm.c | 246 ++++++++---------
drivers/tty/n_hdlc.c | 2 +-
drivers/tty/n_tty.c | 6 +-
drivers/tty/nozomi.c | 10 +-
drivers/tty/serdev/serdev-ttyport.c | 4 +-
drivers/tty/serial/8250/8250_ingenic.c | 13 +-
drivers/tty/serial/8250/8250_of.c | 5 +
drivers/tty/serial/8250/8250_omap.c | 75 +++---
drivers/tty/serial/8250/8250_pci.c | 170 +++++++++---
drivers/tty/serial/8250/8250_pxa.c | 4 +
drivers/tty/serial/Kconfig | 22 ++
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/clps711x.c | 23 +-
drivers/tty/serial/fsl_lpuart.c | 208 +++++++++++---
drivers/tty/serial/lpc32xx_hs.c | 4 +
drivers/tty/serial/max310x.c | 21 +-
drivers/tty/serial/meson_uart.c | 13 +-
drivers/tty/serial/mps2-uart.c | 138 +++++++---
drivers/tty/serial/msm_serial.c | 2 +-
drivers/tty/serial/qcom_geni_serial.c | 279 ++++++++-----------
drivers/tty/serial/samsung.c | 42 +++
drivers/tty/serial/sc16is7xx.c | 4 +-
drivers/tty/serial/serial_core.c | 2 +-
drivers/tty/serial/sh-sci.c | 71 +++--
drivers/tty/serial/sprd_serial.c | 2 +-
drivers/tty/serial/tegra-tcu.c | 298 +++++++++++++++++++++
drivers/tty/serial/xilinx_uartps.c | 40 ++-
drivers/tty/synclink.c | 54 ----
drivers/tty/sysrq.c | 8 +-
drivers/tty/tty_buffer.c | 2 +-
drivers/tty/tty_io.c | 3 +
drivers/tty/tty_ldisc.c | 47 ++++
drivers/tty/vt/vc_screen.c | 48 +++-
drivers/tty/vt/vt.c | 77 ++++--
include/linux/console_struct.h | 2 +-
include/uapi/linux/serial_core.h | 3 +
50 files changed, 1672 insertions(+), 772 deletions(-)
create mode 100644 Documentation/devicetree/bindings/serial/milbeaut-uart.txt
create mode 100644 Documentation/devicetree/bindings/serial/nvidia,tegra194-tcu.txt
delete mode 100644 Documentation/devicetree/bindings/serial/pl011.txt
create mode 100644 Documentation/devicetree/bindings/serial/pl011.yaml
delete mode 100644 Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt
delete mode 100644 Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt
create mode 100644 Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
create mode 100644 drivers/tty/serial/tegra-tcu.c
^ permalink raw reply
* Re: [PATCH v2] tty/serial: Add a serial port simulator
From: Randy Dunlap @ 2019-03-06 2:04 UTC (permalink / raw)
To: cminyard; +Cc: minyard, Greg Kroah-Hartman, linux-serial, linux-kernel
In-Reply-To: <20190306015149.GD4290@minyard.net>
On 3/5/19 5:51 PM, Corey Minyard wrote:
> On Tue, Mar 05, 2019 at 03:29:51PM -0800, Randy Dunlap wrote:
>> Hi Corey,
>>
>> Just some doc comments.
>
> Thanks a bunch. A few comments inline on things I didn't do quite
> like you suggested..
>
>>
>> On 3/5/19 9:12 AM, minyard@acm.org wrote:
>>> diff --git a/Documentation/serial/serialsim.rst b/Documentation/serial/serialsim.rst
>>> new file mode 100644
>>> index 000000000000..655e10b4908e
>>> --- /dev/null
>>> +++ b/Documentation/serial/serialsim.rst
>>> @@ -0,0 +1,149 @@
>>> +.. SPDX-License-Identifier: GPL-2.0+
>>> +=====================================
>>> +serialsim - A kernel serial simualtor
>> xxxxxxxxx
>> serial device simulator
>>
>>> +=====================================
>>> +
>>> +:Author: Corey Minyard <minyard@mvista.com> / <minyard@acm.org>
>>> +
>>> +The serialsim device is a serial simulator with echo and pipe devices.
>>> +It is quite useful for testing programs that use serial ports.
>>> +
>>> +This attempts to emulate a basic serial device. It uses the baud rate
>>> +and sends the bytes through the loopback or pipe at approximately the
>>> +speed it would on a normal serial device.
>>> +
>>> +There is a python interface to the special ioctls for controlling the
>>> +remote end of the termios in addition to the standard ioctl interface
>>> +documented below. See https://github.com/cminyard/serialsim
>>> +
>>> +=====
>>> +Using
>>> +=====
>>> +
>>> +The serialsim.ko module creates two types of devices. Echo devices
>>> +simply echo back the data to the same device. These devices will
>>> +appear as /dev/ttyEcho<n>.
>>> +
>>> +Pipe devices will transfer the data between two devices. The
>>> +devices will appear as /dev/ttyPipeA<n> and /dev/ttyPipeB<n>. And
>>
>> Any
>>
>>> +data written to PipeA reads from PipeB, and vice-versa.
>>> +
>>> +You may create an arbitrary number of devices by setting the
>>> +nr_echo ports and nr_pipe_ports module parameters. The default is
>>
>> nr_echo_ports
>>
>>> +four for both.
>>
>> or for each.
>>
>>> +
>>> +This driver supports modifying the modem control lines and
>>> +injecting various serial errors. It also supports a simulated null
>>> +modem between the two pipes, or in a loopback on the echo device.
>>> +
>>> +By default a pipe or echo comes up in null modem configuration,
>>> +meaning that the DTR line is hooked to the DSR and CD lines on the
>>> +other side and the RTS line on one side is hooked to the CTS line
>>> +on the other side.
>>> +
>>> +The RTS and CTS lines don't currently do anything for flow control.
>>> +
>>> +You can modify null modem and control the lines individually
>>> +through an interface in /sys/class/tty/ttyECHO<n>/ctrl,
>>> +/sys/class/tty/ttyPipeA<n>/ctrl, and
>>> +/sys/class/tty/ttyPipeB<n>/ctrl. The following may be written to
>>> +those files:
>>> +
>>> +[+-]nullmodem
>>> + enable/disable null modem
>>> +
>>> +[+-]cd
>>> + enable/disable Carrier Detect (no effect if +nullmodem)
>>> +
>>> +[+-]dsr
>>> + enable/disable Data Set Ready (no effect if +nullmodem)
>>> +
>>> +[+-]cts
>>> + enable/disable Clear To Send (no effect if +nullmodem)
>>> +
>>> +[+-]ring
>>> + enable/disable Ring
>>> +
>>> +frame
>>> + inject a frame error on the next byte
>>> +
>>> +parity
>>> + inject a parity error on the next byte
>>> +
>>> +overrun
>>> + inject an overrun error on the next byte
>>> +
>>> +The contents of the above files has the following format:
>>
>> have
>
> This intrigued me a bit. I assumed, even though "contents" can
> be plural, it is used in a singular fashion here because it is one
> "thing". So I did some research. I couldn't really find
> anything definitive, and there seems to be a lot of debate on
> this. But if you look at:
> https://dictionary.cambridge.org/grammar/british-grammar/content-or-contents
> you will see, when they use "contents", they use a singular verb
> with it:
> The contents of a book is the list of chapters or articles...
> So if it's good enough for Cambridge, it's good enough for me :).
> Though I'm certainly no grammar expert.
OK :)
>>
>>> +
>>> +tty[Echo|PipeA|PipeB]<n>
>>> + <mctrl values>
>>> +
>>> +where <mctrl values> is the modem control values above (not frame,
>>> +parity, or overrun) with the following added:
>>> +
>>> +[+-]dtr
>>> + value of the Data Terminal Ready
>>> +
>>> +[+-]rts
>>> + value of the Request To Send
>>> +
>>> +The above values are not settable through this interface, they are
>>> +set through the serial port interface itself.
>>> +
>>> +So, for instance, ttyEcho0 comes up in the following state::
>>> +
>>> + # cat /sys/class/tty/ttyEcho0/ctrl
>>> + ttyEcho0: +nullmodem -cd -dsr -cts -ring -dtr -rts
>>> +
>>> +If something connects, it will become::
>>> +
>>> + ttyEcho0: +nullmodem +cd +dsr +cts -ring +dtr +rts
>>> +
>>> +To enable ring::
>>> +
>>> + # echo "+ring" >/sys/class/tty/ttyEcho0/ctrl
>>> + # cat /sys/class/tty/ttyEcho0/ctrl
>>> + ttyEcho0: +nullmodem +cd +dsr +cts +ring +dtr +rts
>>> +
>>> +Now disable NULL modem and the CD line::
>>> +
>>> + # echo "-nullmodem -cd" >/sys/class/tty/ttyEcho0/ctrl
>>> + # cat /sys/class/tty/ttyEcho0/ctrl
>>> + ttyEcho0: -nullmodem -cd -dsr -cts +ring -dtr -rts
>>> +
>>> +Note that these settings are for the side you are modifying. So if
>>> +you set nullmodem on ttyPipeA0, that controls whether the DTR/RTS
>>> +lines from ttyPipeB0 affect ttyPipeA0. It doesn't affect ttyPipeB's
>>> +modem control lines.
>>> +
>>> +The PIPEA and PIPEB devices also have the ability to set these
>>> +values for the other end via an ioctl. The following ioctls are
>>> +available:
>>> +
>>> +TIOCSERSNULLMODEM
>>> + Set the null modem value, the arg is a boolean.
>>> +
>>> +TIOCSERSREMMCTRL
>>> + Set the modem control lines, bits 16-31 of the arg is
>>
>> are
>
> Same comment as above. IMHO, it's one set of bits.
>
>>
>>> + a 16-bit mask telling which values to set, bits 0-15 are the
>>> + actual values. Settable values are TIOCM_CAR, TIOCM_CTS,
>>> + TIOCM_DSR, and TIOC_RNG. If NULLMODEM is set to true, then only
>>> + TIOC_RNG is settable. The DTR and RTS lines are not here, you can
>>> + set them through the normal interface.
>>> +
>>> +TIOCSERSREMERR
>>> + Send an error or errors on the next sent byte. arg is
>>> + a bitwise OR of (1 << TTY_xxx). Allowed errors are TTY_BREAK,
>>
>> is this better: (or I don't understand?)
>> a bitwise OR of (1 << TTY_xxx) and one (or more of) the
>> allowed error flags TTY_BREAK, TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
>
> Well, not really. But what I wrote isn't great, so, how about:
>
> Send an error or errors on the next sent byte. arg is a bitwise
> OR of (1 << TTY_BREAK), (1 << TTY_FRAME), (1 << TTY_PARITY), and
> (1 << TTY_OVERRUN). If none of those are set, then no error
> is sent.
I see. Thanks.
> Thanks,
>
> -corey
>
>>
>>> + TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
>>> +
>>> +TIOCSERGREMTERMIOS
>>> + Return the termios structure for the other side of the pipe.
>>> + arg is a pointer to a standard termios struct.
>>> +
>>> +TIOCSERGREMRS485
>>> + Return the remote RS485 settings, arg is a pointer to a struct
>>> + serial_rs485.
>>> +
>>> +Note that unlike the sysfs interface, these ioctls affect the other
>>> +end. So setting nullmodem on the ttyPipeB0 interface sets whether
>>> +the DTR/RTS lines on ttyPipeB0 affect ttyPipeA0.
>>
>>
>> cheers.
>> --
>> ~Randy
--
~Randy
^ permalink raw reply
* Re: [PATCH v2] tty/serial: Add a serial port simulator
From: Corey Minyard @ 2019-03-06 1:51 UTC (permalink / raw)
To: Randy Dunlap; +Cc: minyard, Greg Kroah-Hartman, linux-serial, linux-kernel
In-Reply-To: <eea0ad95-8344-371a-1dd0-58c8366a355b@infradead.org>
On Tue, Mar 05, 2019 at 03:29:51PM -0800, Randy Dunlap wrote:
> Hi Corey,
>
> Just some doc comments.
Thanks a bunch. A few comments inline on things I didn't do quite
like you suggested..
>
> On 3/5/19 9:12 AM, minyard@acm.org wrote:
> > diff --git a/Documentation/serial/serialsim.rst b/Documentation/serial/serialsim.rst
> > new file mode 100644
> > index 000000000000..655e10b4908e
> > --- /dev/null
> > +++ b/Documentation/serial/serialsim.rst
> > @@ -0,0 +1,149 @@
> > +.. SPDX-License-Identifier: GPL-2.0+
> > +=====================================
> > +serialsim - A kernel serial simualtor
> xxxxxxxxx
> serial device simulator
>
> > +=====================================
> > +
> > +:Author: Corey Minyard <minyard@mvista.com> / <minyard@acm.org>
> > +
> > +The serialsim device is a serial simulator with echo and pipe devices.
> > +It is quite useful for testing programs that use serial ports.
> > +
> > +This attempts to emulate a basic serial device. It uses the baud rate
> > +and sends the bytes through the loopback or pipe at approximately the
> > +speed it would on a normal serial device.
> > +
> > +There is a python interface to the special ioctls for controlling the
> > +remote end of the termios in addition to the standard ioctl interface
> > +documented below. See https://github.com/cminyard/serialsim
> > +
> > +=====
> > +Using
> > +=====
> > +
> > +The serialsim.ko module creates two types of devices. Echo devices
> > +simply echo back the data to the same device. These devices will
> > +appear as /dev/ttyEcho<n>.
> > +
> > +Pipe devices will transfer the data between two devices. The
> > +devices will appear as /dev/ttyPipeA<n> and /dev/ttyPipeB<n>. And
>
> Any
>
> > +data written to PipeA reads from PipeB, and vice-versa.
> > +
> > +You may create an arbitrary number of devices by setting the
> > +nr_echo ports and nr_pipe_ports module parameters. The default is
>
> nr_echo_ports
>
> > +four for both.
>
> or for each.
>
> > +
> > +This driver supports modifying the modem control lines and
> > +injecting various serial errors. It also supports a simulated null
> > +modem between the two pipes, or in a loopback on the echo device.
> > +
> > +By default a pipe or echo comes up in null modem configuration,
> > +meaning that the DTR line is hooked to the DSR and CD lines on the
> > +other side and the RTS line on one side is hooked to the CTS line
> > +on the other side.
> > +
> > +The RTS and CTS lines don't currently do anything for flow control.
> > +
> > +You can modify null modem and control the lines individually
> > +through an interface in /sys/class/tty/ttyECHO<n>/ctrl,
> > +/sys/class/tty/ttyPipeA<n>/ctrl, and
> > +/sys/class/tty/ttyPipeB<n>/ctrl. The following may be written to
> > +those files:
> > +
> > +[+-]nullmodem
> > + enable/disable null modem
> > +
> > +[+-]cd
> > + enable/disable Carrier Detect (no effect if +nullmodem)
> > +
> > +[+-]dsr
> > + enable/disable Data Set Ready (no effect if +nullmodem)
> > +
> > +[+-]cts
> > + enable/disable Clear To Send (no effect if +nullmodem)
> > +
> > +[+-]ring
> > + enable/disable Ring
> > +
> > +frame
> > + inject a frame error on the next byte
> > +
> > +parity
> > + inject a parity error on the next byte
> > +
> > +overrun
> > + inject an overrun error on the next byte
> > +
> > +The contents of the above files has the following format:
>
> have
This intrigued me a bit. I assumed, even though "contents" can
be plural, it is used in a singular fashion here because it is one
"thing". So I did some research. I couldn't really find
anything definitive, and there seems to be a lot of debate on
this. But if you look at:
https://dictionary.cambridge.org/grammar/british-grammar/content-or-contents
you will see, when they use "contents", they use a singular verb
with it:
The contents of a book is the list of chapters or articles...
So if it's good enough for Cambridge, it's good enough for me :).
Though I'm certainly no grammar expert.
>
> > +
> > +tty[Echo|PipeA|PipeB]<n>
> > + <mctrl values>
> > +
> > +where <mctrl values> is the modem control values above (not frame,
> > +parity, or overrun) with the following added:
> > +
> > +[+-]dtr
> > + value of the Data Terminal Ready
> > +
> > +[+-]rts
> > + value of the Request To Send
> > +
> > +The above values are not settable through this interface, they are
> > +set through the serial port interface itself.
> > +
> > +So, for instance, ttyEcho0 comes up in the following state::
> > +
> > + # cat /sys/class/tty/ttyEcho0/ctrl
> > + ttyEcho0: +nullmodem -cd -dsr -cts -ring -dtr -rts
> > +
> > +If something connects, it will become::
> > +
> > + ttyEcho0: +nullmodem +cd +dsr +cts -ring +dtr +rts
> > +
> > +To enable ring::
> > +
> > + # echo "+ring" >/sys/class/tty/ttyEcho0/ctrl
> > + # cat /sys/class/tty/ttyEcho0/ctrl
> > + ttyEcho0: +nullmodem +cd +dsr +cts +ring +dtr +rts
> > +
> > +Now disable NULL modem and the CD line::
> > +
> > + # echo "-nullmodem -cd" >/sys/class/tty/ttyEcho0/ctrl
> > + # cat /sys/class/tty/ttyEcho0/ctrl
> > + ttyEcho0: -nullmodem -cd -dsr -cts +ring -dtr -rts
> > +
> > +Note that these settings are for the side you are modifying. So if
> > +you set nullmodem on ttyPipeA0, that controls whether the DTR/RTS
> > +lines from ttyPipeB0 affect ttyPipeA0. It doesn't affect ttyPipeB's
> > +modem control lines.
> > +
> > +The PIPEA and PIPEB devices also have the ability to set these
> > +values for the other end via an ioctl. The following ioctls are
> > +available:
> > +
> > +TIOCSERSNULLMODEM
> > + Set the null modem value, the arg is a boolean.
> > +
> > +TIOCSERSREMMCTRL
> > + Set the modem control lines, bits 16-31 of the arg is
>
> are
Same comment as above. IMHO, it's one set of bits.
>
> > + a 16-bit mask telling which values to set, bits 0-15 are the
> > + actual values. Settable values are TIOCM_CAR, TIOCM_CTS,
> > + TIOCM_DSR, and TIOC_RNG. If NULLMODEM is set to true, then only
> > + TIOC_RNG is settable. The DTR and RTS lines are not here, you can
> > + set them through the normal interface.
> > +
> > +TIOCSERSREMERR
> > + Send an error or errors on the next sent byte. arg is
> > + a bitwise OR of (1 << TTY_xxx). Allowed errors are TTY_BREAK,
>
> is this better: (or I don't understand?)
> a bitwise OR of (1 << TTY_xxx) and one (or more of) the
> allowed error flags TTY_BREAK, TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
Well, not really. But what I wrote isn't great, so, how about:
Send an error or errors on the next sent byte. arg is a bitwise
OR of (1 << TTY_BREAK), (1 << TTY_FRAME), (1 << TTY_PARITY), and
(1 << TTY_OVERRUN). If none of those are set, then no error
is sent.
Thanks,
-corey
>
> > + TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
> > +
> > +TIOCSERGREMTERMIOS
> > + Return the termios structure for the other side of the pipe.
> > + arg is a pointer to a standard termios struct.
> > +
> > +TIOCSERGREMRS485
> > + Return the remote RS485 settings, arg is a pointer to a struct
> > + serial_rs485.
> > +
> > +Note that unlike the sysfs interface, these ioctls affect the other
> > +end. So setting nullmodem on the ttyPipeB0 interface sets whether
> > +the DTR/RTS lines on ttyPipeB0 affect ttyPipeA0.
>
>
> cheers.
> --
> ~Randy
^ permalink raw reply
* Re: [PATCH v2] tty/serial: Add a serial port simulator
From: Randy Dunlap @ 2019-03-05 23:29 UTC (permalink / raw)
To: minyard, Greg Kroah-Hartman, linux-serial; +Cc: linux-kernel, Corey Minyard
In-Reply-To: <20190305171231.22133-1-minyard@acm.org>
Hi Corey,
Just some doc comments.
On 3/5/19 9:12 AM, minyard@acm.org wrote:
> diff --git a/Documentation/serial/serialsim.rst b/Documentation/serial/serialsim.rst
> new file mode 100644
> index 000000000000..655e10b4908e
> --- /dev/null
> +++ b/Documentation/serial/serialsim.rst
> @@ -0,0 +1,149 @@
> +.. SPDX-License-Identifier: GPL-2.0+
> +=====================================
> +serialsim - A kernel serial simualtor
xxxxxxxxx
serial device simulator
> +=====================================
> +
> +:Author: Corey Minyard <minyard@mvista.com> / <minyard@acm.org>
> +
> +The serialsim device is a serial simulator with echo and pipe devices.
> +It is quite useful for testing programs that use serial ports.
> +
> +This attempts to emulate a basic serial device. It uses the baud rate
> +and sends the bytes through the loopback or pipe at approximately the
> +speed it would on a normal serial device.
> +
> +There is a python interface to the special ioctls for controlling the
> +remote end of the termios in addition to the standard ioctl interface
> +documented below. See https://github.com/cminyard/serialsim
> +
> +=====
> +Using
> +=====
> +
> +The serialsim.ko module creates two types of devices. Echo devices
> +simply echo back the data to the same device. These devices will
> +appear as /dev/ttyEcho<n>.
> +
> +Pipe devices will transfer the data between two devices. The
> +devices will appear as /dev/ttyPipeA<n> and /dev/ttyPipeB<n>. And
Any
> +data written to PipeA reads from PipeB, and vice-versa.
> +
> +You may create an arbitrary number of devices by setting the
> +nr_echo ports and nr_pipe_ports module parameters. The default is
nr_echo_ports
> +four for both.
or for each.
> +
> +This driver supports modifying the modem control lines and
> +injecting various serial errors. It also supports a simulated null
> +modem between the two pipes, or in a loopback on the echo device.
> +
> +By default a pipe or echo comes up in null modem configuration,
> +meaning that the DTR line is hooked to the DSR and CD lines on the
> +other side and the RTS line on one side is hooked to the CTS line
> +on the other side.
> +
> +The RTS and CTS lines don't currently do anything for flow control.
> +
> +You can modify null modem and control the lines individually
> +through an interface in /sys/class/tty/ttyECHO<n>/ctrl,
> +/sys/class/tty/ttyPipeA<n>/ctrl, and
> +/sys/class/tty/ttyPipeB<n>/ctrl. The following may be written to
> +those files:
> +
> +[+-]nullmodem
> + enable/disable null modem
> +
> +[+-]cd
> + enable/disable Carrier Detect (no effect if +nullmodem)
> +
> +[+-]dsr
> + enable/disable Data Set Ready (no effect if +nullmodem)
> +
> +[+-]cts
> + enable/disable Clear To Send (no effect if +nullmodem)
> +
> +[+-]ring
> + enable/disable Ring
> +
> +frame
> + inject a frame error on the next byte
> +
> +parity
> + inject a parity error on the next byte
> +
> +overrun
> + inject an overrun error on the next byte
> +
> +The contents of the above files has the following format:
have
> +
> +tty[Echo|PipeA|PipeB]<n>
> + <mctrl values>
> +
> +where <mctrl values> is the modem control values above (not frame,
> +parity, or overrun) with the following added:
> +
> +[+-]dtr
> + value of the Data Terminal Ready
> +
> +[+-]rts
> + value of the Request To Send
> +
> +The above values are not settable through this interface, they are
> +set through the serial port interface itself.
> +
> +So, for instance, ttyEcho0 comes up in the following state::
> +
> + # cat /sys/class/tty/ttyEcho0/ctrl
> + ttyEcho0: +nullmodem -cd -dsr -cts -ring -dtr -rts
> +
> +If something connects, it will become::
> +
> + ttyEcho0: +nullmodem +cd +dsr +cts -ring +dtr +rts
> +
> +To enable ring::
> +
> + # echo "+ring" >/sys/class/tty/ttyEcho0/ctrl
> + # cat /sys/class/tty/ttyEcho0/ctrl
> + ttyEcho0: +nullmodem +cd +dsr +cts +ring +dtr +rts
> +
> +Now disable NULL modem and the CD line::
> +
> + # echo "-nullmodem -cd" >/sys/class/tty/ttyEcho0/ctrl
> + # cat /sys/class/tty/ttyEcho0/ctrl
> + ttyEcho0: -nullmodem -cd -dsr -cts +ring -dtr -rts
> +
> +Note that these settings are for the side you are modifying. So if
> +you set nullmodem on ttyPipeA0, that controls whether the DTR/RTS
> +lines from ttyPipeB0 affect ttyPipeA0. It doesn't affect ttyPipeB's
> +modem control lines.
> +
> +The PIPEA and PIPEB devices also have the ability to set these
> +values for the other end via an ioctl. The following ioctls are
> +available:
> +
> +TIOCSERSNULLMODEM
> + Set the null modem value, the arg is a boolean.
> +
> +TIOCSERSREMMCTRL
> + Set the modem control lines, bits 16-31 of the arg is
are
> + a 16-bit mask telling which values to set, bits 0-15 are the
> + actual values. Settable values are TIOCM_CAR, TIOCM_CTS,
> + TIOCM_DSR, and TIOC_RNG. If NULLMODEM is set to true, then only
> + TIOC_RNG is settable. The DTR and RTS lines are not here, you can
> + set them through the normal interface.
> +
> +TIOCSERSREMERR
> + Send an error or errors on the next sent byte. arg is
> + a bitwise OR of (1 << TTY_xxx). Allowed errors are TTY_BREAK,
is this better: (or I don't understand?)
a bitwise OR of (1 << TTY_xxx) and one (or more of) the
allowed error flags TTY_BREAK, TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
> + TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
> +
> +TIOCSERGREMTERMIOS
> + Return the termios structure for the other side of the pipe.
> + arg is a pointer to a standard termios struct.
> +
> +TIOCSERGREMRS485
> + Return the remote RS485 settings, arg is a pointer to a struct
> + serial_rs485.
> +
> +Note that unlike the sysfs interface, these ioctls affect the other
> +end. So setting nullmodem on the ttyPipeB0 interface sets whether
> +the DTR/RTS lines on ttyPipeB0 affect ttyPipeA0.
cheers.
--
~Randy
^ permalink raw reply
* Re: [RFC PATCH v1 08/25] printk: add ring buffer and kthread
From: John Ogness @ 2019-03-05 21:00 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: linux-kernel, Peter Zijlstra, Petr Mladek, Steven Rostedt,
Daniel Wang, Andrew Morton, Linus Torvalds, Greg Kroah-Hartman,
Alan Cox, Jiri Slaby, Peter Feiner, linux-serial,
Sergey Senozhatsky
In-Reply-To: <20190304110703.GA960@tigerII.localdomain>
Hi Sergey,
Thanks for your feedback.
I am responding to this comment ahead of your previous comments because
it really cuts at the heart of the proposed design. After addressing
this point it will make it easier for me to respond to your other
comments.
NOTE: This is a lengthy response.
On 2019-03-04, Sergey Senozhatsky <sergey.senozhatsky@gmail.com> wrote:
>> But in general, channels which depend on preemptible printk will
>> become totally useless in some cases.
>
> Which brings me to a question - what are those messages/channels? Not
> important enough to be printed on consoles immediately, yet important
> enough to pass the suppress_message_printing() check.
I would like to clarify that message supression (i.e. console loglevel)
is a method of reducing what is printed. It does nothing to address the
issues related to console printing. My proposal focusses on addressing
the issues related to console printing.
Console printing is a convenient feature to allow a kernel to
communicate information to a user without any reliance on
userspace. IMHO there are 2 categories of messages that the kernel will
communicate. The first is informational (usb events, wireless and
ethernet connectivity, filesystem events, etc.). Since this category of
messages occurs during normal runtime, we should expect that it does not
cause adverse effects to the rest of the system (such as latencies and
non-deterministic behavior).
The second category is for emergency situations, where the kernel needs
to report something unusual (panic, BUG, WARN, etc.). In some of these
situations, it may be the last thing the kernel ever does. We should
expect this category to focus on getting the message out as reliably as
possible. Even if it means disturbing the system with large latencies.
_Both_ categories are important for the user, but their requirements are
different:
informational: non-disturbing
emergency: reliable
But what if a console doesn't support the write_atomic() that the
emergency category requires? Then implement it. We currently have about
80 console drivers.
But what if can't be implemented? vt console, for example? Yes, the vt
console would be tricky. It doesn't even support the current
bust_spinlocks/oops_in_progress. But since the emergency category has a
clear requirement (reliability), it means that a vt write_atomic() does
not need to be concerned with system disturbance. That could help to
find an implementation that will work, even for vt.
> We may wave those semi-important messages good bye, I'm afraid,
> preemptible printk will take care of it.
You are talking about a system that is overloaded with messages to print
to the console. The current printk implementation will do a better job
of getting the informational messages out, but at an enormous cost to
all the tasks on the system (including the realtime tasks). I am
proposing a printk implementation where the tasks are not affected by
console printing floods. When the CPU is allowed to dedicate itself to
tasks, this obviously reduces the CPU available for console printing,
and thus more messages will be lost. It is a choice to clarify printk's
role (non-disturbance) and at the same time guarantee more determinism
for the kernel and its tasks.
As I've said, the messages of the informational category are also
important. There are things that can be done to help get these messages
out. For example:
- Creating printk-kthreads per console (and thus per-console locks) so
that printk-buffer readers are not slowing each other down.
- Having printk-threads use priority-buckets based on loglevels so that
(like the rt scheduler) more important messages are printed first.
- Assigning the printk-kthread of more important consoles an appropriate
realtime priority.
> So... do we have a case here? Do we really need printk-kthread?
Obviously I answer yes to that.
I want messages of the information category to cause no disturbance to
the system. Give the kernel the freedom to communicate to users without
destroying its own performance. This can only be achieved if the
messages are printed from a _fully_ preemptible context.
And I want messages of the emergency category to be as reliable as
possible, regardless of the costs to the system. Give the kernel a clear
mechanism to _reliably_ communicate critical information. Such messages
should never appear on a correctly functioning system.
And again, both of the above have nothing to do with message
suppression. Here I am addressing the console printing issues:
reliability and disturbance.
John Ogness
^ permalink raw reply
* [PATCH v2] tty/serial: Add a serial port simulator
From: minyard @ 2019-03-05 17:12 UTC (permalink / raw)
To: Greg Kroah-Hartman, linux-serial; +Cc: minyard, linux-kernel, Corey Minyard
From: Corey Minyard <cminyard@mvista.com>
This creates simulated serial ports, both as echo devices and pipe
devices. The driver reasonably approximates the serial port speed
and simulates some modem control lines. It allows error injection
and direct control of modem control lines.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
---
Changes since v1:
* Fixed a build error on sparc due to a missing include.
* Fixed a build error on powerpc due to the termios mess.
* Fixed a use of an uninitialized lock at startup. I didn't
realize exactly how the register worked and a callback
happened that I wasn't expecting.
Documentation/serial/serialsim.rst | 149 ++++
MAINTAINERS | 7 +
drivers/tty/serial/Kconfig | 10 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/serialsim.c | 1101 ++++++++++++++++++++++++++++
include/uapi/linux/serialsim.h | 32 +
6 files changed, 1300 insertions(+)
create mode 100644 Documentation/serial/serialsim.rst
create mode 100644 drivers/tty/serial/serialsim.c
create mode 100644 include/uapi/linux/serialsim.h
diff --git a/Documentation/serial/serialsim.rst b/Documentation/serial/serialsim.rst
new file mode 100644
index 000000000000..655e10b4908e
--- /dev/null
+++ b/Documentation/serial/serialsim.rst
@@ -0,0 +1,149 @@
+.. SPDX-License-Identifier: GPL-2.0+
+=====================================
+serialsim - A kernel serial simualtor
+=====================================
+
+:Author: Corey Minyard <minyard@mvista.com> / <minyard@acm.org>
+
+The serialsim device is a serial simulator with echo and pipe devices.
+It is quite useful for testing programs that use serial ports.
+
+This attempts to emulate a basic serial device. It uses the baud rate
+and sends the bytes through the loopback or pipe at approximately the
+speed it would on a normal serial device.
+
+There is a python interface to the special ioctls for controlling the
+remote end of the termios in addition to the standard ioctl interface
+documented below. See https://github.com/cminyard/serialsim
+
+=====
+Using
+=====
+
+The serialsim.ko module creates two types of devices. Echo devices
+simply echo back the data to the same device. These devices will
+appear as /dev/ttyEcho<n>.
+
+Pipe devices will transfer the data between two devices. The
+devices will appear as /dev/ttyPipeA<n> and /dev/ttyPipeB<n>. And
+data written to PipeA reads from PipeB, and vice-versa.
+
+You may create an arbitrary number of devices by setting the
+nr_echo ports and nr_pipe_ports module parameters. The default is
+four for both.
+
+This driver supports modifying the modem control lines and
+injecting various serial errors. It also supports a simulated null
+modem between the two pipes, or in a loopback on the echo device.
+
+By default a pipe or echo comes up in null modem configuration,
+meaning that the DTR line is hooked to the DSR and CD lines on the
+other side and the RTS line on one side is hooked to the CTS line
+on the other side.
+
+The RTS and CTS lines don't currently do anything for flow control.
+
+You can modify null modem and control the lines individually
+through an interface in /sys/class/tty/ttyECHO<n>/ctrl,
+/sys/class/tty/ttyPipeA<n>/ctrl, and
+/sys/class/tty/ttyPipeB<n>/ctrl. The following may be written to
+those files:
+
+[+-]nullmodem
+ enable/disable null modem
+
+[+-]cd
+ enable/disable Carrier Detect (no effect if +nullmodem)
+
+[+-]dsr
+ enable/disable Data Set Ready (no effect if +nullmodem)
+
+[+-]cts
+ enable/disable Clear To Send (no effect if +nullmodem)
+
+[+-]ring
+ enable/disable Ring
+
+frame
+ inject a frame error on the next byte
+
+parity
+ inject a parity error on the next byte
+
+overrun
+ inject an overrun error on the next byte
+
+The contents of the above files has the following format:
+
+tty[Echo|PipeA|PipeB]<n>
+ <mctrl values>
+
+where <mctrl values> is the modem control values above (not frame,
+parity, or overrun) with the following added:
+
+[+-]dtr
+ value of the Data Terminal Ready
+
+[+-]rts
+ value of the Request To Send
+
+The above values are not settable through this interface, they are
+set through the serial port interface itself.
+
+So, for instance, ttyEcho0 comes up in the following state::
+
+ # cat /sys/class/tty/ttyEcho0/ctrl
+ ttyEcho0: +nullmodem -cd -dsr -cts -ring -dtr -rts
+
+If something connects, it will become::
+
+ ttyEcho0: +nullmodem +cd +dsr +cts -ring +dtr +rts
+
+To enable ring::
+
+ # echo "+ring" >/sys/class/tty/ttyEcho0/ctrl
+ # cat /sys/class/tty/ttyEcho0/ctrl
+ ttyEcho0: +nullmodem +cd +dsr +cts +ring +dtr +rts
+
+Now disable NULL modem and the CD line::
+
+ # echo "-nullmodem -cd" >/sys/class/tty/ttyEcho0/ctrl
+ # cat /sys/class/tty/ttyEcho0/ctrl
+ ttyEcho0: -nullmodem -cd -dsr -cts +ring -dtr -rts
+
+Note that these settings are for the side you are modifying. So if
+you set nullmodem on ttyPipeA0, that controls whether the DTR/RTS
+lines from ttyPipeB0 affect ttyPipeA0. It doesn't affect ttyPipeB's
+modem control lines.
+
+The PIPEA and PIPEB devices also have the ability to set these
+values for the other end via an ioctl. The following ioctls are
+available:
+
+TIOCSERSNULLMODEM
+ Set the null modem value, the arg is a boolean.
+
+TIOCSERSREMMCTRL
+ Set the modem control lines, bits 16-31 of the arg is
+ a 16-bit mask telling which values to set, bits 0-15 are the
+ actual values. Settable values are TIOCM_CAR, TIOCM_CTS,
+ TIOCM_DSR, and TIOC_RNG. If NULLMODEM is set to true, then only
+ TIOC_RNG is settable. The DTR and RTS lines are not here, you can
+ set them through the normal interface.
+
+TIOCSERSREMERR
+ Send an error or errors on the next sent byte. arg is
+ a bitwise OR of (1 << TTY_xxx). Allowed errors are TTY_BREAK,
+ TTY_FRAME, TTY_PARITY, and TTY_OVERRUN.
+
+TIOCSERGREMTERMIOS
+ Return the termios structure for the other side of the pipe.
+ arg is a pointer to a standard termios struct.
+
+TIOCSERGREMRS485
+ Return the remote RS485 settings, arg is a pointer to a struct
+ serial_rs485.
+
+Note that unlike the sysfs interface, these ioctls affect the other
+end. So setting nullmodem on the ttyPipeB0 interface sets whether
+the DTR/RTS lines on ttyPipeB0 affect ttyPipeA0.
diff --git a/MAINTAINERS b/MAINTAINERS
index 9919840d54cd..aa1eb3c07f44 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13702,6 +13702,13 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/serial_ir.c
+SERIAL PORT SIMULATOR
+M: Corey Minyard <cminyard@mvista.com>
+S: Maintained
+F: Documentation/serial/serialsim.rst
+F: drivers/tty/serial/serialsim.c
+F: include/uapi/linux/serialsim.h
+
SFC NETWORK DRIVER
M: Solarflare linux maintainers <linux-net-drivers@solarflare.com>
M: Edward Cree <ecree@solarflare.com>
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 089a6f285d5e..789113055e2f 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1565,4 +1565,14 @@ endmenu
config SERIAL_MCTRL_GPIO
tristate
+config SERIAL_SIMULATOR
+ tristate "Serial port simulator"
+ default n
+ help
+ Support for a simulated device that behaves like a normal
+ serial port. It has echo devices and pipe devices. This
+ is useful for testing programs that use serial ports.
+
+ If unsure, say N.
+
endif # TTY
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 1511e8a9f856..dbd9ee0f1c6b 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o
obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
+obj-$(CONFIG_SERIAL_SIMULATOR) += serialsim.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/serialsim.c b/drivers/tty/serial/serialsim.c
new file mode 100644
index 000000000000..2336a53014cf
--- /dev/null
+++ b/drivers/tty/serial/serialsim.c
@@ -0,0 +1,1101 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * serialsim - Emulate a serial device in a loopback and/or pipe
+ *
+ * See the docs for this for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/kthread.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#include <linux/serialsim.h>
+
+#define PORT_SERIALECHO 72549
+#define PORT_SERIALPIPEA 72550
+#define PORT_SERIALPIPEB 72551
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+#define SERIALSIM_WAKES_PER_SEC 1000
+#else
+#define SERIALSIM_WAKES_PER_SEC HZ
+#endif
+
+#define SERIALSIM_XBUFSIZE 32
+
+/* For things to send on the line, in flags field. */
+#define DO_FRAME_ERR (1 << TTY_FRAME)
+#define DO_PARITY_ERR (1 << TTY_PARITY)
+#define DO_OVERRUN_ERR (1 << TTY_OVERRUN)
+#define DO_BREAK (1 << TTY_BREAK)
+#define FLAGS_MASK (DO_FRAME_ERR | DO_PARITY_ERR | DO_OVERRUN_ERR | DO_BREAK)
+
+struct serialsim_intf {
+ struct uart_port port;
+
+ /*
+ * This is my transmit buffer, my thread picks this up and
+ * injects them into the other port's uart.
+ */
+ unsigned char xmitbuf[SERIALSIM_XBUFSIZE];
+ struct circ_buf buf;
+
+ /* Error flags to send. */
+ bool break_reported;
+ unsigned int flags;
+
+ /* Modem state. */
+ unsigned int mctrl;
+ bool do_null_modem;
+ spinlock_t mctrl_lock;
+ struct tasklet_struct mctrl_tasklet;
+
+ /* My transmitter is enabled. */
+ bool tx_enabled;
+
+ /* I can receive characters. */
+ bool rx_enabled;
+
+ /* Is the port registered with the uart driver? */
+ bool registered;
+
+ /*
+ * The serial echo port on the other side of this pipe (or points
+ * to myself in loopback mode.
+ */
+ struct serialsim_intf *ointf;
+
+ unsigned int div;
+ unsigned int bytes_per_interval;
+ unsigned int per_interval_residual;
+
+ struct ktermios termios;
+
+ const char *threadname;
+ struct task_struct *thread;
+
+ struct serial_rs485 rs485;
+};
+
+#define circ_sbuf_space(buf) CIRC_SPACE((buf)->head, (buf)->tail, \
+ SERIALSIM_XBUFSIZE)
+#define circ_sbuf_empty(buf) ((buf)->head == (buf)->tail)
+#define circ_sbuf_next(idx) (((idx) + 1) % SERIALSIM_XBUFSIZE)
+
+static struct serialsim_intf *serialsim_port_to_intf(struct uart_port *port)
+{
+ return container_of(port, struct serialsim_intf, port);
+}
+
+static unsigned int serialsim_tx_empty(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+
+ if (circ_sbuf_empty(&intf->buf))
+ return TIOCSER_TEMT;
+ return 0;
+}
+
+/*
+ * We have to lock multiple locks, make sure to do it in the same order all
+ * the time.
+ */
+static void serialsim_null_modem_lock_irq(struct serialsim_intf *intf)
+ __acquires(intf->port.lock) __acquires(intf->mctrl_lock)
+ __acquires(intf->ointf->mctrl_lock)
+{
+ spin_lock_irq(&intf->port.lock);
+ if (intf == intf->ointf) {
+ spin_lock(&intf->mctrl_lock);
+ } else if (intf < intf->ointf) {
+ spin_lock(&intf->mctrl_lock);
+ spin_lock(&intf->ointf->mctrl_lock);
+ } else {
+ spin_lock(&intf->ointf->mctrl_lock);
+ spin_lock(&intf->mctrl_lock);
+ }
+}
+
+static void serialsim_null_modem_unlock_irq(struct serialsim_intf *intf)
+ __releases(intf->port.lock) __releases(intf->mctrl_lock)
+ __releases(intf->ointf->mctrl_lock)
+{
+ if (intf == intf->ointf) {
+ spin_unlock(&intf->mctrl_lock);
+ } else {
+ /* Order doesn't matter here. */
+ spin_unlock(&intf->mctrl_lock);
+ spin_unlock(&intf->ointf->mctrl_lock);
+ }
+ spin_unlock_irq(&intf->port.lock);
+}
+
+/*
+ * This must be called holding intf->port.lock and intf->mctrl_lock.
+ */
+static void _serialsim_set_modem_lines(struct serialsim_intf *intf,
+ unsigned int mask,
+ unsigned int new_mctrl)
+{
+ unsigned int changes;
+ unsigned int mctrl = (intf->mctrl & ~mask) | (new_mctrl & mask);
+
+ if (mctrl == intf->mctrl)
+ return;
+
+ if (!intf->rx_enabled) {
+ intf->mctrl = mctrl;
+ return;
+ }
+
+ changes = mctrl ^ intf->mctrl;
+ intf->mctrl = mctrl;
+ if (changes & TIOCM_CAR)
+ uart_handle_dcd_change(&intf->port, mctrl & TIOCM_CAR);
+ if (changes & TIOCM_CTS)
+ uart_handle_cts_change(&intf->port, mctrl & TIOCM_CTS);
+ if (changes & TIOCM_RNG)
+ intf->port.icount.rng++;
+ if (changes & TIOCM_DSR)
+ intf->port.icount.dsr++;
+}
+
+#define NULL_MODEM_MCTRL (TIOCM_CAR | TIOCM_CTS | TIOCM_DSR)
+#define LOCAL_MCTRL (NULL_MODEM_MCTRL | TIOCM_RNG)
+
+/*
+ * Must be called holding intf->port.lock, intf->mctrl_lock, and
+ * intf->ointf.mctrl_lock.
+ */
+static void serialsim_handle_null_modem_update(struct serialsim_intf *intf)
+{
+ unsigned int mctrl = 0;
+
+ /* Pull the values from the remote side for myself. */
+ if (intf->ointf->mctrl & TIOCM_DTR)
+ mctrl |= TIOCM_CAR | TIOCM_DSR;
+ if (intf->ointf->mctrl & TIOCM_RTS)
+ mctrl |= TIOCM_CTS;
+
+ _serialsim_set_modem_lines(intf, NULL_MODEM_MCTRL, mctrl);
+}
+
+static void serialsim_set_null_modem(struct serialsim_intf *intf, bool val)
+{
+ serialsim_null_modem_lock_irq(intf);
+
+ if (!!val == !!intf->do_null_modem)
+ goto out_unlock;
+
+ if (!val) {
+ intf->do_null_modem = false;
+ goto out_unlock;
+ }
+
+ /* Enabling NULL modem. */
+ intf->do_null_modem = true;
+
+ serialsim_handle_null_modem_update(intf);
+
+out_unlock:
+ serialsim_null_modem_unlock_irq(intf);
+}
+
+static void serialsim_set_modem_lines(struct serialsim_intf *intf,
+ unsigned int mask,
+ unsigned int new_mctrl)
+{
+ mask &= LOCAL_MCTRL;
+
+ spin_lock_irq(&intf->port.lock);
+ spin_lock(&intf->mctrl_lock);
+
+ if (intf->do_null_modem)
+ mask &= ~NULL_MODEM_MCTRL;
+
+ _serialsim_set_modem_lines(intf, mask, new_mctrl);
+
+ spin_unlock(&intf->mctrl_lock);
+ spin_unlock_irq(&intf->port.lock);
+}
+
+static void mctrl_tasklet(unsigned long data)
+{
+ struct serialsim_intf *intf = (void *) data;
+
+ serialsim_null_modem_lock_irq(intf);
+ if (intf->ointf->do_null_modem)
+ serialsim_handle_null_modem_update(intf);
+ serialsim_null_modem_unlock_irq(intf);
+}
+
+#define SETTABLE_MCTRL (TIOCM_RTS | TIOCM_DTR)
+
+static void serialsim_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+
+ spin_lock(&intf->mctrl_lock);
+ intf->mctrl &= ~SETTABLE_MCTRL;
+ intf->mctrl |= mctrl & SETTABLE_MCTRL;
+ spin_unlock(&intf->mctrl_lock);
+
+ /*
+ * We are called holding port->lock, but we must be able to claim
+ * intf->ointf->port.lock, and that can result in deadlock. So
+ * we have to run this elsewhere. Note that we run the other
+ * end's tasklet.
+ */
+ tasklet_schedule(&intf->ointf->mctrl_tasklet);
+}
+
+static unsigned int serialsim_get_mctrl(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ unsigned int rv;
+
+ spin_lock(&intf->mctrl_lock);
+ rv = intf->mctrl;
+ spin_unlock(&intf->mctrl_lock);
+
+ return rv;
+}
+
+static void serialsim_stop_tx(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+
+ intf->tx_enabled = false;
+}
+
+static void serialsim_set_baud_rate(struct serialsim_intf *intf,
+ unsigned int baud, unsigned int cflag)
+{
+ unsigned int bits_per_char;
+
+ switch (cflag & CSIZE) {
+ case CS5:
+ bits_per_char = 7;
+ break;
+ case CS6:
+ bits_per_char = 8;
+ break;
+ case CS7:
+ bits_per_char = 9;
+ break;
+ default:
+ bits_per_char = 10;
+ break; /* CS8 and others. */
+ }
+ if (cflag & CSTOPB)
+ bits_per_char++;
+
+ intf->div = SERIALSIM_WAKES_PER_SEC * bits_per_char;
+ intf->bytes_per_interval = baud / intf->div;
+ intf->per_interval_residual = baud % intf->div;
+}
+
+static void serialsim_transfer_data(struct uart_port *port,
+ struct circ_buf *tbuf)
+{
+ struct circ_buf *cbuf = &port->state->xmit;
+
+ while (!uart_circ_empty(cbuf) && circ_sbuf_space(tbuf)) {
+ unsigned char c = cbuf->buf[cbuf->tail];
+
+ cbuf->tail = (cbuf->tail + 1) % UART_XMIT_SIZE;
+ tbuf->buf[tbuf->head] = c;
+ tbuf->head = circ_sbuf_next(tbuf->head);
+ port->icount.tx++;
+ }
+ if (uart_circ_chars_pending(cbuf) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+}
+
+static unsigned int serialsim_get_flag(struct serialsim_intf *intf,
+ unsigned int *status)
+{
+ unsigned int flags = intf->flags;
+
+ *status = flags;
+
+ /* Overrun is always reported through a different way. */
+ if (flags & DO_OVERRUN_ERR) {
+ intf->port.icount.overrun++;
+ intf->flags &= ~DO_OVERRUN_ERR;
+ }
+
+ if (flags & DO_BREAK && !intf->break_reported) {
+ intf->port.icount.brk++;
+ intf->break_reported = true;
+ return TTY_BREAK;
+ }
+ if (flags & DO_FRAME_ERR) {
+ intf->port.icount.frame++;
+ intf->flags &= ~DO_FRAME_ERR;
+ return TTY_FRAME;
+ }
+ if (flags & DO_PARITY_ERR) {
+ intf->port.icount.parity++;
+ intf->flags &= ~DO_PARITY_ERR;
+ return TTY_PARITY;
+ }
+
+ return TTY_NORMAL;
+}
+
+static void serialsim_set_flags(struct serialsim_intf *intf,
+ unsigned int status)
+{
+ spin_lock_irq(&intf->port.lock);
+ intf->flags |= status;
+ spin_unlock_irq(&intf->port.lock);
+}
+
+static void serialsim_thread_delay(void)
+{
+#ifdef CONFIG_HIGH_RES_TIMERS
+ ktime_t timeout;
+
+ timeout = ns_to_ktime(1000000000 / SERIALSIM_WAKES_PER_SEC);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_hrtimeout(&timeout, HRTIMER_MODE_REL);
+#else
+ schedule_timeout_interruptible(1);
+#endif
+}
+
+static int serialsim_thread(void *data)
+{
+ struct serialsim_intf *intf = data;
+ struct serialsim_intf *ointf = intf->ointf;
+ struct uart_port *port = &intf->port;
+ struct uart_port *oport = &ointf->port;
+ struct circ_buf *tbuf = &intf->buf;
+ unsigned int residual = 0;
+
+ while (!kthread_should_stop()) {
+ unsigned int to_send;
+ unsigned int div;
+ unsigned char buf[SERIALSIM_XBUFSIZE];
+ unsigned int pos = 0;
+ unsigned int flag;
+ unsigned int status = 0;
+
+ spin_lock_irq(&oport->lock);
+ if (ointf->tx_enabled)
+ /*
+ * Move bytes from the other port's transmit buffer to
+ * the interface buffer.
+ */
+ serialsim_transfer_data(oport, tbuf);
+ spin_unlock_irq(&oport->lock);
+
+ /*
+ * Move from the interface buffer into the local
+ * buffer based on the simulated serial speed.
+ */
+ to_send = intf->bytes_per_interval;
+ residual += intf->per_interval_residual;
+ div = intf->div;
+ while (!circ_sbuf_empty(tbuf) && to_send) {
+ buf[pos++] = tbuf->buf[tbuf->tail];
+ tbuf->tail = circ_sbuf_next(tbuf->tail);
+ to_send--;
+ }
+ if (residual >= div) {
+ residual -= div;
+ if (!circ_sbuf_empty(tbuf)) {
+ buf[pos++] = tbuf->buf[tbuf->tail];
+ tbuf->tail = circ_sbuf_next(tbuf->tail);
+ }
+ }
+
+ /*
+ * Move from the internal buffer into my receive
+ * buffer.
+ */
+ spin_lock_irq(&port->lock);
+ flag = serialsim_get_flag(intf, &status);
+ if (intf->rx_enabled) {
+ for (to_send = 0; to_send < pos; to_send++) {
+ port->icount.rx++;
+ uart_insert_char(port, status,
+ DO_OVERRUN_ERR,
+ buf[to_send], flag);
+ flag = 0;
+ status = 0;
+ }
+ }
+ spin_unlock_irq(&port->lock);
+
+ if (pos)
+ tty_flip_buffer_push(&port->state->port);
+
+ serialsim_thread_delay();
+ }
+
+ return 0;
+}
+
+static void serialsim_start_tx(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+
+ intf->tx_enabled = true;
+}
+
+static void serialsim_stop_rx(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+
+ intf->rx_enabled = false;
+}
+
+static void serialsim_break_ctl(struct uart_port *port, int break_state)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ struct serialsim_intf *ointf = intf->ointf;
+
+ spin_lock_irq(&ointf->port.lock);
+ if (!break_state && ointf->flags & DO_BREAK) {
+ /* Turning break off. */
+ ointf->break_reported = false;
+ ointf->flags &= ~DO_BREAK;
+ } else if (break_state) {
+ ointf->flags |= DO_BREAK;
+ }
+ spin_unlock_irq(&ointf->port.lock);
+}
+
+static int serialsim_startup(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ int rv = 0;
+ struct circ_buf *cbuf = &port->state->xmit;
+ unsigned long flags;
+
+ /*
+ * This is technically wrong, but otherwise tests using it
+ * can get stale data.
+ */
+ spin_lock_irqsave(&port->lock, flags);
+ cbuf->head = cbuf->tail = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ intf->buf.head = intf->buf.tail = 0;
+ intf->thread = kthread_run(serialsim_thread, intf,
+ "%s%d", intf->threadname, port->line);
+ if (IS_ERR(intf->thread)) {
+ rv = PTR_ERR(intf->thread);
+ intf->thread = NULL;
+ pr_err("serialsim: Could not start thread: %d", rv);
+ } else {
+ spin_lock_irqsave(&port->lock, flags);
+ intf->tx_enabled = true;
+ intf->rx_enabled = true;
+
+ serialsim_set_baud_rate(intf, 9600, CS8);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+ return rv;
+}
+
+static void serialsim_shutdown(struct uart_port *port)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ spin_unlock_irqrestore(&port->lock, flags);
+ kthread_stop(intf->thread);
+}
+
+static void serialsim_release_port(struct uart_port *port)
+{
+}
+
+static void
+serialsim_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ unsigned int baud = uart_get_baud_rate(port, termios, old,
+ 10, 100000000);
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ serialsim_set_baud_rate(intf, baud, termios->c_cflag);
+ intf->termios = *termios;
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int serialsim_rs485(struct uart_port *port,
+ struct serial_rs485 *newrs485)
+{
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+
+ intf->rs485 = *newrs485;
+ return 0;
+}
+
+static const char *serialecho_type(struct uart_port *port)
+{
+ return "SerialEcho";
+}
+
+static const char *serialpipea_type(struct uart_port *port)
+{
+ return "SerialPipeA";
+}
+
+static const char *serialpipeb_type(struct uart_port *port)
+{
+ return "SerialPipeB";
+}
+
+static void serialecho_config_port(struct uart_port *port, int type)
+{
+ port->type = PORT_SERIALECHO;
+}
+
+static void serialpipea_config_port(struct uart_port *port, int type)
+{
+ port->type = PORT_SERIALPIPEA;
+}
+
+static void serialpipeb_config_port(struct uart_port *port, int type)
+{
+ port->type = PORT_SERIALPIPEB;
+}
+
+static int serialpipe_ioctl(struct uart_port *port, unsigned int cmd,
+ unsigned long arg)
+{
+ int rv = 0;
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ unsigned int mask;
+ int val;
+
+ switch (cmd) {
+ case TIOCSERSREMNULLMODEM:
+ serialsim_set_null_modem(intf->ointf, !!arg);
+ break;
+
+ case TIOCSERGREMNULLMODEM:
+ val = intf->ointf->do_null_modem;
+ if (copy_to_user((int __user *) arg, &val, sizeof(int)))
+ rv = -EFAULT;
+ break;
+
+ case TIOCSERSREMMCTRL:
+ mask = (arg >> 16) & 0xffff;
+ arg &= 0xffff;
+ if (mask & ~LOCAL_MCTRL || arg & ~LOCAL_MCTRL)
+ rv = -EINVAL;
+ else
+ serialsim_set_modem_lines(intf->ointf, mask, arg);
+ break;
+
+ case TIOCSERGREMMCTRL:
+ if (copy_to_user((unsigned int __user *) arg,
+ &intf->ointf->mctrl,
+ sizeof(unsigned int)))
+ rv = -EFAULT;
+ break;
+
+ case TIOCSERSREMERR:
+ if (arg & ~FLAGS_MASK)
+ rv = -EINVAL;
+ else
+ serialsim_set_flags(intf, arg);
+ break;
+
+ case TIOCSERGREMERR:
+ if (copy_to_user((unsigned int __user *) arg, &intf->flags,
+ sizeof(unsigned int)))
+ rv = -EFAULT;
+ break;
+
+ case TIOCSERGREMTERMIOS:
+ {
+ struct ktermios otermios;
+
+ spin_lock_irq(&intf->ointf->port.lock);
+ otermios = intf->ointf->termios;
+ spin_unlock_irq(&intf->ointf->port.lock);
+#ifdef TCGETS2
+ rv = kernel_termios_to_user_termios((struct termios2 __user *)
+ arg,
+ &otermios);
+#else
+ rv = kernel_termios_to_user_termios((struct termios __user *)
+ arg,
+ &otermios);
+#endif
+ if (rv)
+ rv = -EFAULT;
+ break;
+ }
+
+ case TIOCSERGREMRS485:
+ {
+ struct serial_rs485 ors485;
+
+ spin_lock_irq(&intf->ointf->port.lock);
+ ors485 = intf->ointf->rs485;
+ spin_unlock_irq(&intf->ointf->port.lock);
+
+ if (copy_to_user((struct serial_rs485 __user *) arg,
+ &ors485, sizeof(ors485)))
+ rv = -EFAULT;
+ break;
+ }
+
+ default:
+ rv = -ENOIOCTLCMD;
+ }
+
+ return rv;
+}
+
+static struct serialsim_intf *serialecho_ports;
+static struct serialsim_intf *serialpipe_ports;
+
+static unsigned int nr_echo_ports = 4;
+module_param(nr_echo_ports, uint, 0444);
+MODULE_PARM_DESC(nr_echo_ports,
+ "The number of echo ports to create. Defaults to 4");
+
+static unsigned int nr_pipe_ports = 4;
+module_param(nr_pipe_ports, uint, 0444);
+MODULE_PARM_DESC(nr_pipe_ports,
+ "The number of pipe ports to create. Defaults to 4");
+
+static char *gettok(char **s)
+{
+ char *t = skip_spaces(*s);
+ char *p = t;
+
+ while (*p && !isspace(*p))
+ p++;
+ if (*p)
+ *p++ = '\0';
+ *s = p;
+
+ return t;
+}
+
+static bool tokeq(const char *t, const char *m)
+{
+ return strcmp(t, m) == 0;
+}
+
+static unsigned int parse_modem_line(char op, unsigned int flag,
+ unsigned int *mctrl)
+{
+ if (op == '+')
+ *mctrl |= flag;
+ else
+ *mctrl &= ~flag;
+ return flag;
+}
+
+static void serialsim_ctrl_append(char **val, int *left, char *n, bool enabled)
+{
+ int count;
+
+ count = snprintf(*val, *left, " %c%s", enabled ? '+' : '-', n);
+ *left -= count;
+ *val += count;
+}
+
+static ssize_t serialsim_ctrl_read(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tty_port *tport = dev_get_drvdata(dev);
+ struct uart_state *state = container_of(tport, struct uart_state, port);
+ struct uart_port *port = state->uart_port;
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ unsigned int mctrl = intf->mctrl;
+ char *val = buf;
+ int left = PAGE_SIZE;
+ int count;
+
+ count = snprintf(val, left, "%s:", dev->kobj.name);
+ val += count;
+ left -= count;
+ serialsim_ctrl_append(&val, &left, "nullmodem", intf->do_null_modem);
+ serialsim_ctrl_append(&val, &left, "cd", mctrl & TIOCM_CAR);
+ serialsim_ctrl_append(&val, &left, "dsr", mctrl & TIOCM_DSR);
+ serialsim_ctrl_append(&val, &left, "cts", mctrl & TIOCM_CTS);
+ serialsim_ctrl_append(&val, &left, "ring", mctrl & TIOCM_RNG);
+ serialsim_ctrl_append(&val, &left, "dtr", mctrl & TIOCM_DTR);
+ serialsim_ctrl_append(&val, &left, "rts", mctrl & TIOCM_RTS);
+ *val++ = '\n';
+
+ return val - buf;
+}
+
+static ssize_t serialsim_ctrl_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *val, size_t count)
+{
+ struct tty_port *tport = dev_get_drvdata(dev);
+ struct uart_state *state = container_of(tport, struct uart_state, port);
+ struct uart_port *port = state->uart_port;
+ struct serialsim_intf *intf = serialsim_port_to_intf(port);
+ char *str = kstrndup(val, count, GFP_KERNEL);
+ char *p, *s = str;
+ int rv = count;
+ unsigned int flags = 0;
+ unsigned int nullmodem = 0;
+ unsigned int mctrl_mask = 0, mctrl = 0;
+
+ if (!str)
+ return -ENOMEM;
+
+ p = gettok(&s);
+ while (*p) {
+ char op = '\0';
+ int err = 0;
+
+ switch (*p) {
+ case '+':
+ case '-':
+ op = *p++;
+ break;
+ default:
+ break;
+ }
+
+ if (tokeq(p, "frame"))
+ flags |= DO_FRAME_ERR;
+ else if (tokeq(p, "parity"))
+ flags |= DO_PARITY_ERR;
+ else if (tokeq(p, "overrun"))
+ flags |= DO_OVERRUN_ERR;
+ else if (tokeq(p, "nullmodem"))
+ nullmodem = op;
+ else if (tokeq(p, "dsr"))
+ mctrl_mask |= parse_modem_line(op, TIOCM_DSR, &mctrl);
+ else if (tokeq(p, "cts"))
+ mctrl_mask |= parse_modem_line(op, TIOCM_CTS, &mctrl);
+ else if (tokeq(p, "cd"))
+ mctrl_mask |= parse_modem_line(op, TIOCM_CAR, &mctrl);
+ else if (tokeq(p, "ring"))
+ mctrl_mask |= parse_modem_line(op, TIOCM_RNG, &mctrl);
+ else
+ err = -EINVAL;
+
+ if (err) {
+ rv = err;
+ goto out;
+ }
+ p = gettok(&s);
+ }
+
+ if (flags)
+ serialsim_set_flags(intf, flags);
+ if (nullmodem)
+ serialsim_set_null_modem(intf, nullmodem == '+');
+ if (mctrl_mask)
+ serialsim_set_modem_lines(intf, mctrl_mask, mctrl);
+
+out:
+ kfree(str);
+
+ return rv;
+}
+
+static DEVICE_ATTR(ctrl, 0660, serialsim_ctrl_read, serialsim_ctrl_write);
+
+static struct attribute *serialsim_dev_attrs[] = {
+ &dev_attr_ctrl.attr,
+ NULL,
+};
+
+static struct attribute_group serialsim_dev_attr_group = {
+ .attrs = serialsim_dev_attrs,
+};
+
+static const struct uart_ops serialecho_ops = {
+ .tx_empty = serialsim_tx_empty,
+ .set_mctrl = serialsim_set_mctrl,
+ .get_mctrl = serialsim_get_mctrl,
+ .stop_tx = serialsim_stop_tx,
+ .start_tx = serialsim_start_tx,
+ .stop_rx = serialsim_stop_rx,
+ .break_ctl = serialsim_break_ctl,
+ .startup = serialsim_startup,
+ .shutdown = serialsim_shutdown,
+ .release_port = serialsim_release_port,
+ .set_termios = serialsim_set_termios,
+ .type = serialecho_type,
+ .config_port = serialecho_config_port
+};
+
+static const struct uart_ops serialpipea_ops = {
+ .tx_empty = serialsim_tx_empty,
+ .set_mctrl = serialsim_set_mctrl,
+ .get_mctrl = serialsim_get_mctrl,
+ .stop_tx = serialsim_stop_tx,
+ .start_tx = serialsim_start_tx,
+ .stop_rx = serialsim_stop_rx,
+ .break_ctl = serialsim_break_ctl,
+ .startup = serialsim_startup,
+ .shutdown = serialsim_shutdown,
+ .release_port = serialsim_release_port,
+ .set_termios = serialsim_set_termios,
+ .type = serialpipea_type,
+ .config_port = serialpipea_config_port,
+ .ioctl = serialpipe_ioctl
+};
+
+static const struct uart_ops serialpipeb_ops = {
+ .tx_empty = serialsim_tx_empty,
+ .set_mctrl = serialsim_set_mctrl,
+ .get_mctrl = serialsim_get_mctrl,
+ .stop_tx = serialsim_stop_tx,
+ .start_tx = serialsim_start_tx,
+ .stop_rx = serialsim_stop_rx,
+ .break_ctl = serialsim_break_ctl,
+ .startup = serialsim_startup,
+ .shutdown = serialsim_shutdown,
+ .release_port = serialsim_release_port,
+ .set_termios = serialsim_set_termios,
+ .type = serialpipeb_type,
+ .config_port = serialpipeb_config_port,
+ .ioctl = serialpipe_ioctl
+};
+
+static struct uart_driver serialecho_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyEcho",
+ .dev_name = "ttyEcho"
+};
+
+static struct uart_driver serialpipea_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyPipeA",
+ .dev_name = "ttyPipeA"
+};
+
+static struct uart_driver serialpipeb_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyPipeB",
+ .dev_name = "ttyPipeB"
+};
+
+
+static int __init serialsim_init(void)
+{
+ unsigned int i;
+ int rv;
+
+ serialecho_ports = kcalloc(nr_echo_ports,
+ sizeof(*serialecho_ports),
+ GFP_KERNEL);
+ if (!serialecho_ports) {
+ pr_err("serialsim: Unable to allocate echo ports.\n");
+ rv = ENOMEM;
+ goto out;
+ }
+
+ serialpipe_ports = kcalloc(nr_pipe_ports * 2,
+ sizeof(*serialpipe_ports),
+ GFP_KERNEL);
+ if (!serialpipe_ports) {
+ kfree(serialecho_ports);
+ pr_err("serialsim: Unable to allocate pipe ports.\n");
+ rv = ENOMEM;
+ goto out;
+ }
+
+ serialecho_driver.nr = nr_echo_ports;
+ rv = uart_register_driver(&serialecho_driver);
+ if (rv) {
+ kfree(serialecho_ports);
+ kfree(serialpipe_ports);
+ pr_err("serialsim: Unable to register driver.\n");
+ goto out;
+ }
+
+ serialpipea_driver.nr = nr_pipe_ports;
+ rv = uart_register_driver(&serialpipea_driver);
+ if (rv) {
+ uart_unregister_driver(&serialecho_driver);
+ kfree(serialecho_ports);
+ kfree(serialpipe_ports);
+ pr_err("serialsim: Unable to register driver.\n");
+ goto out;
+ }
+
+ serialpipeb_driver.nr = nr_pipe_ports;
+ rv = uart_register_driver(&serialpipeb_driver);
+ if (rv) {
+ uart_unregister_driver(&serialpipea_driver);
+ uart_unregister_driver(&serialecho_driver);
+ kfree(serialecho_ports);
+ kfree(serialpipe_ports);
+ pr_err("serialsim: Unable to register driver.\n");
+ goto out;
+ }
+
+ for (i = 0; i < nr_echo_ports; i++) {
+ struct serialsim_intf *intf = &serialecho_ports[i];
+ struct uart_port *port = &intf->port;
+
+ intf->buf.buf = intf->xmitbuf;
+ intf->ointf = intf;
+ intf->threadname = "serialecho";
+ intf->do_null_modem = true;
+ spin_lock_init(&intf->mctrl_lock);
+ tasklet_init(&intf->mctrl_tasklet, mctrl_tasklet, (long) intf);
+ /* Won't configure without some I/O or mem address set. */
+ port->iobase = 1;
+ port->line = i;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->ops = &serialecho_ops;
+ spin_lock_init(&port->lock);
+ port->attr_group = &serialsim_dev_attr_group;
+ rv = uart_add_one_port(&serialecho_driver, port);
+ if (rv)
+ pr_err("serialsim: Unable to add uart port %d: %d\n",
+ i, rv);
+ else
+ intf->registered = true;
+ }
+
+ for (i = 0; i < nr_pipe_ports * 2; i += 2) {
+ struct serialsim_intf *intfa = &serialpipe_ports[i];
+ struct serialsim_intf *intfb = &serialpipe_ports[i + 1];
+ struct uart_port *porta = &intfa->port;
+ struct uart_port *portb = &intfb->port;
+
+ intfa->buf.buf = intfa->xmitbuf;
+ intfb->buf.buf = intfb->xmitbuf;
+ intfa->ointf = intfb;
+ intfb->ointf = intfa;
+ intfa->threadname = "serialpipea";
+ intfb->threadname = "serialpipeb";
+ spin_lock_init(&intfa->mctrl_lock);
+ spin_lock_init(&intfb->mctrl_lock);
+ tasklet_init(&intfa->mctrl_tasklet, mctrl_tasklet,
+ (long) intfa);
+ tasklet_init(&intfb->mctrl_tasklet, mctrl_tasklet,
+ (long) intfb);
+
+ /* Won't configure without some I/O or mem address set. */
+ porta->iobase = 1;
+ porta->line = i / 2;
+ porta->flags = UPF_BOOT_AUTOCONF;
+ porta->ops = &serialpipea_ops;
+ spin_lock_init(&porta->lock);
+ porta->attr_group = &serialsim_dev_attr_group;
+ porta->rs485_config = serialsim_rs485;
+
+ /*
+ * uart_add_one_port() does an mctrl operation, which will
+ * claim the other port's lock. So everything needs to be
+ * full initialized, and we need null modem off until we
+ * get things added.
+ */
+ portb->iobase = 1;
+ portb->line = i / 2;
+ portb->flags = UPF_BOOT_AUTOCONF;
+ portb->ops = &serialpipeb_ops;
+ portb->attr_group = &serialsim_dev_attr_group;
+ spin_lock_init(&portb->lock);
+ portb->rs485_config = serialsim_rs485;
+
+ rv = uart_add_one_port(&serialpipea_driver, porta);
+ if (rv) {
+ pr_err("serialsim: Unable to add uart pipe a port %d: %d\n",
+ i, rv);
+ continue;
+ } else {
+ intfa->registered = true;
+ }
+
+ rv = uart_add_one_port(&serialpipeb_driver, portb);
+ if (rv) {
+ pr_err("serialsim: Unable to add uart pipe b port %d: %d\n",
+ i, rv);
+ intfa->registered = false;
+ uart_remove_one_port(&serialpipea_driver, porta);
+ } else {
+ intfb->registered = true;
+ }
+
+ if (intfa->registered && intfb->registered) {
+ serialsim_set_null_modem(intfa, true);
+ serialsim_set_null_modem(intfb, true);
+ }
+ }
+ rv = 0;
+
+ pr_info("serialsim ready\n");
+out:
+ return rv;
+}
+
+static void __exit serialsim_exit(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr_echo_ports; i++) {
+ struct serialsim_intf *intf = &serialecho_ports[i];
+ struct uart_port *port = &intf->port;
+
+ if (intf->registered)
+ uart_remove_one_port(&serialecho_driver, port);
+ tasklet_kill(&intf->mctrl_tasklet);
+ }
+
+ for (i = 0; i < nr_pipe_ports * 2; i += 2) {
+ struct serialsim_intf *intfa = &serialpipe_ports[i];
+ struct serialsim_intf *intfb = &serialpipe_ports[i + 1];
+ struct uart_port *porta = &intfa->port;
+ struct uart_port *portb = &intfb->port;
+
+ if (intfa->registered)
+ uart_remove_one_port(&serialpipea_driver, porta);
+ if (intfb->registered)
+ uart_remove_one_port(&serialpipeb_driver, portb);
+ tasklet_kill(&intfa->mctrl_tasklet);
+ tasklet_kill(&intfb->mctrl_tasklet);
+ }
+ uart_unregister_driver(&serialecho_driver);
+ uart_unregister_driver(&serialpipea_driver);
+ uart_unregister_driver(&serialpipeb_driver);
+
+ kfree(serialecho_ports);
+ kfree(serialpipe_ports);
+
+ pr_info("serialsim unloaded\n");
+}
+
+module_init(serialsim_init);
+module_exit(serialsim_exit);
+
+MODULE_AUTHOR("Corey Minyard");
+MODULE_DESCRIPTION("Serial simulation device");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/serialsim.h b/include/uapi/linux/serialsim.h
new file mode 100644
index 000000000000..34369f1a85b0
--- /dev/null
+++ b/include/uapi/linux/serialsim.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+
+/*
+ * serialsim - Emulate a serial device in a loopback and/or pipe
+ */
+
+/*
+ * TTY IOCTLs for controlling the modem control and for error injection.
+ * See drivers/tty/serial/serialsim.c and Documentation/serial/serialsim.rst
+ * for details.
+ */
+
+#ifndef LINUX_SERIALSIM_H
+#define LINUX_SERIALSIM_H
+
+#include <linux/ioctl.h>
+#include <asm/termbits.h>
+
+#define TIOCSERSREMNULLMODEM 0x54e4
+#define TIOCSERSREMMCTRL 0x54e5
+#define TIOCSERSREMERR 0x54e6
+#ifdef TCGETS2
+#define TIOCSERGREMTERMIOS _IOR('T', 0xe7, struct termios2)
+#else
+#define TIOCSERGREMTERMIOS _IOR('T', 0xe7, struct termios)
+#endif
+#define TIOCSERGREMNULLMODEM _IOR('T', 0xe8, int)
+#define TIOCSERGREMMCTRL _IOR('T', 0xe9, unsigned int)
+#define TIOCSERGREMERR _IOR('T', 0xea, unsigned int)
+#define TIOCSERGREMRS485 _IOR('T', 0xeb, struct serial_rs485)
+
+#endif
--
2.17.1
^ permalink raw reply related
* [PATCH v2] serial: imx: Rename URTX0 to UTXD, URXD0 to URXD
From: Christina Quast @ 2019-03-05 11:45 UTC (permalink / raw)
Cc: shawnguo, s.hauer, u.kleine-koenig, Christina Quast,
Greg Kroah-Hartman, Jiri Slaby, linux-serial, linux-kernel
In-Reply-To: <20190222205520.fvqpttlzq5lycbvu@pengutronix.de>
Rename to be coherent with the datasheet.
Signed-off-by: Christina Quast <cquast@hanoverdisplays.com>
---
drivers/tty/serial/imx.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index dff75dc94731..8edb368690d9 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -39,8 +39,8 @@
#include "serial_mctrl_gpio.h"
/* Register definitions */
-#define URXD0 0x0 /* Receiver Register */
-#define URTX0 0x40 /* Transmitter Register */
+#define URXD 0x0 /* Receiver Register */
+#define UTXD 0x40 /* Transmitter Register */
#define UCR1 0x80 /* Control Register 1 */
#define UCR2 0x84 /* Control Register 2 */
#define UCR3 0x88 /* Control Register 3 */
@@ -502,7 +502,7 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
if (sport->port.x_char) {
/* Send next char */
- imx_uart_writel(sport, sport->port.x_char, URTX0);
+ imx_uart_writel(sport, sport->port.x_char, UTXD);
sport->port.icount.tx++;
sport->port.x_char = 0;
return;
@@ -536,7 +536,7 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
* out the port here */
- imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
+ imx_uart_writel(sport, xmit->buf[xmit->tail], UTXD);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx++;
}
@@ -743,7 +743,7 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
flg = TTY_NORMAL;
sport->port.icount.rx++;
- rx = imx_uart_readl(sport, URXD0);
+ rx = imx_uart_readl(sport, URXD);
usr2 = imx_uart_readl(sport, USR2);
if (usr2 & USR2_BRCD) {
@@ -870,7 +870,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
* the respective irq source is enabled. This prevents some undesired
* actions, for example if a character that sits in the RX FIFO and that
* should be fetched via DMA is tried to be fetched using PIO. Or the
- * receiver is currently off and so reading from URXD0 results in an
+ * receiver is currently off and so reading from URXD results in an
* exception. So just mask the (raw) status bits for disabled irqs.
*/
if ((ucr1 & UCR1_RRDYEN) == 0)
@@ -1231,7 +1231,7 @@ static int imx_uart_dma_init(struct imx_port *sport)
}
slave_config.direction = DMA_DEV_TO_MEM;
- slave_config.src_addr = sport->port.mapbase + URXD0;
+ slave_config.src_addr = sport->port.mapbase + URXD;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
/* one byte less than the watermark level to enable the aging timer */
slave_config.src_maxburst = RXTL_DMA - 1;
@@ -1257,7 +1257,7 @@ static int imx_uart_dma_init(struct imx_port *sport)
}
slave_config.direction = DMA_MEM_TO_DEV;
- slave_config.dst_addr = sport->port.mapbase + URTX0;
+ slave_config.dst_addr = sport->port.mapbase + UTXD;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.dst_maxburst = TXTL_DMA;
ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
@@ -1798,7 +1798,7 @@ static int imx_uart_poll_get_char(struct uart_port *port)
if (!(imx_uart_readl(sport, USR2) & USR2_RDR))
return NO_POLL_CHAR;
- return imx_uart_readl(sport, URXD0) & URXD_RX_DATA;
+ return imx_uart_readl(sport, URXD) & URXD_RX_DATA;
}
static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c)
@@ -1812,7 +1812,7 @@ static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c)
} while (~status & USR1_TRDY);
/* write */
- imx_uart_writel(sport, c, URTX0);
+ imx_uart_writel(sport, c, UTXD);
/* flush */
do {
@@ -1894,7 +1894,7 @@ static void imx_uart_console_putchar(struct uart_port *port, int ch)
while (imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)
barrier();
- imx_uart_writel(sport, ch, URTX0);
+ imx_uart_writel(sport, ch, UTXD);
}
/*
@@ -2091,7 +2091,7 @@ static void imx_uart_console_early_putchar(struct uart_port *port, int ch)
while (imx_uart_readl(sport, IMX21_UTS) & UTS_TXFULL)
cpu_relax();
- imx_uart_writel(sport, ch, URTX0);
+ imx_uart_writel(sport, ch, UTXD);
}
static void imx_uart_console_early_write(struct console *con, const char *s,
--
2.20.1
Please consider the environment before printing this email
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited.
If you received this in error, please contact the sender or postmaster (postmaster@hanoverdisplays.com) and delete the material from any computer.
Although we routinely screen for viruses, addressees should check this e-mail and any attachment for viruses. We make no warranty as to absence of viruses in this e-mail or any attachments.
Our Company's email policy is to permit incidental personal use. If this email is of a personal nature, it must not be relied upon as expressing the views or opinions of the company.
^ permalink raw reply related
* Re: [PATCH 4/4] printk: Add a device attribute for the per-console loglevel
From: Calvin Owens @ 2019-03-04 19:10 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: Petr Mladek, Sergey Senozhatsky, Steven Rostedt,
Greg Kroah-Hartman, Jonathan Corbet, linux-kernel@vger.kernel.org,
linux-serial@vger.kernel.org
In-Reply-To: <20190304080630.GA4495@jagdpanzerIV>
On Monday 03/04 at 17:06 +0900, Sergey Senozhatsky wrote:
> On (03/01/19 16:48), Calvin Owens wrote:
> > +static struct attribute *console_sysfs_attrs[] = {
> > + &dev_attr_loglevel.attr,
> > + NULL,
> > +};
> > +ATTRIBUTE_GROUPS(console_sysfs);
> > +
> > static struct bus_type console_subsys = {
> > .name = "console",
> > + .dev_groups = console_sysfs_groups,
> > };
>
> Do we really need to change this dynamically? Console options are
> traditionally static (boot param or DT). Can we also be happy with
> the static per-console loglevel?
It really does need to be runtime configurable: there are a lot of usecases
that enables, like turning the fast console up to KERN_DEBUG on a pile of
machines you want to take a closer look at. The 'kernel.printk' global
loglevel is also already changable at runtime, and since that setting
interacts with this one it would be strange if only the former were able
to be changed.
I also want to add more attribute knobs related to extended consoles,
so the plumbing to get things exposed in sysfs is worth it for me.
Thanks,
Calvin
^ permalink raw reply
* Re: tty: uart: custom speed
From: Grant Edwards @ 2019-03-04 18:47 UTC (permalink / raw)
To: kernelnewbies; +Cc: linux-serial
In-Reply-To: <CAPY=qRTEh5yksQH9o2bBSRWA8J1xSdjLd4dknXt7S9QFrKsfSQ@mail.gmail.com>
On 2019-03-04, Subhashini Rao Beerisetty <subhashbeerisetty@gmail.com> wrote:
> On Mon, Mar 4, 2019 at 8:27 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
>> Have you read the documentation on how to set custom baud rates? I
>> can't find the link to it at the moment, but it is very possible to do
>> that today, no special ioctls are needed at all. I think someone was
>> finally working on getting glibc to support it directly, but I do not
>> know if those patches ever got merged, so you would just have to "open
>> code" it in userspace if you want to do this.
>
> I could not find clear documentation on this, I’m very much thankful
> if someone point me on this. I thought of exploring TIOCGSERIAL and
> TIOCSSERIAL Ioctl’s to set custom baud rates. Now it looks like I
> should read the kernel code to understand how to achieve this without
> special ioctls.
You use the termios2 structure and TCGETS2/TCSETS ioctl calls:
http://www.panix.com/~grante/arbitrary-baud.c
--
Grant
_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@kernelnewbies.org
https://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
^ permalink raw reply
* Re: tty: uart: custom speed
From: Vladimir Zapolskiy @ 2019-03-04 17:16 UTC (permalink / raw)
To: Subhashini Rao Beerisetty; +Cc: Greg KH, linux-serial, kernelnewbies
In-Reply-To: <CAPY=qRTEh5yksQH9o2bBSRWA8J1xSdjLd4dknXt7S9QFrKsfSQ@mail.gmail.com>
On 03/04/2019 05:23 PM, Subhashini Rao Beerisetty wrote:
> On Mon, Mar 4, 2019 at 8:27 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>>
>> On Mon, Mar 04, 2019 at 08:19:44PM +0530, Subhashini Rao Beerisetty wrote:
>>> On Mon, Mar 4, 2019 at 6:53 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>>>>
>>>> On Mon, Mar 04, 2019 at 05:46:54PM +0530, Subhashini Rao Beerisetty wrote:
>>>>> Hi All,
>>>>>
>>>>>
>>>>> I’ve an UART ports on Xilinx FPGA board and it gets connected to PC
>>>>> via PCIe bus. I could not find any kernel serial driver which supports
>>>>> our hardware so I plan to develop a new driver. I see two approaches
>>>>> to develop an UART driver i.e. either by using tty_register_driver()
>>>>> or an uart_register_driver().
>>>>>
>>>>>
>>>>> Regarding my UART module, it has a counter of 16 bits and runs on a
>>>>> 32Mhz clock. It supports all the standard & non-standard baud’s up to
>>>>> 4Mbps.
>>>>
>>>> What type of UART is it? Odds are it is based on an existing design, no
>>>> one creates a brand-new UART anymore. Hopefully. If not, what a
>>>> waste...
>>>
>>> For UART type, I see the permitted types are none, 8250, 16450, 16550,
>>> 16550A, 16650, 16650V2, 16654, 16750, 16850, 16950, and 16954 etc.
>>> Looking into the data sheet I haven’t found any register or parameter
>>> defining any of those UART types. Is ‘UART type’ is determined from a
>>> register settings point of view or a pinout point of view?
>>
>> register settings point of view.
>>
>> I suggest you do some basic research into how UARTs work before
>> continuing with this effort, it will save you a lot of time in the end
>> :)
>
> Thanks for you suggestion. I will study on this, I’m glad if you can
> point some good documentation links on this.
>
>>
>>>>> If I used struct tty_operations, I noticed that baud rate changing is
>>>>> done via “.set_termios” API, but this method only supports standard
>>>>> baud rates. I’d like to know why this API does not support
>>>>> non-standard baud rates?
>>>>
>>>> Why do you think it does not?
>>> From data sheet point of view I'm clear on how to set the non-standard
>>> baud rate, even I exposed a custom Ioctl for this. But here I’m trying
>>> to understand how it is achieved by using available UART kernel
>>> framework.
>>> For non-standard baud rate requests, observed that
>>> tty->termios.c_ispeed & tty->termios.c_ospeed set to 38400. I did not
>>> understood why it changes?
>>
>> Have you read the documentation on how to set custom baud rates? I
>> can't find the link to it at the moment, but it is very possible to do
>> that today, no special ioctls are needed at all. I think someone was
>> finally working on getting glibc to support it directly, but I do not
>> know if those patches ever got merged, so you would just have to "open
>> code" it in userspace if you want to do this.
>
> I could not find clear documentation on this, I’m very much thankful
> if someone point me on this. I thought of exploring TIOCGSERIAL and
> TIOCSSERIAL Ioctl’s to set custom baud rates. Now it looks like I
> should read the kernel code to understand how to achieve this without
> special ioctls.
>
You should try to set a custom baudrate with help of TCSETS2:
----8<----
static int set_speed(int fd, speed_t speed)
{
struct termios2 tio;
int ret;
ret = ioctl(fd, TCGETS2, &tio);
if (ret) {
printf("TCGETS2 failed: %d\n", ret);
return ret;
}
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = speed;
tio.c_ospeed = speed;
ret = ioctl(fd, TCSETS2, &tio);
if (ret) {
printf("TCSETS2 failed to set speed %u: %d\n", speed, ret);
return ret;
}
return 0;
}
----8<----
--
Best wishes,
Vladimir
_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@kernelnewbies.org
https://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox