linux/drivers/tty/serdev/serdev-ttyport.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org>
   4 */
   5#include <linux/kernel.h>
   6#include <linux/serdev.h>
   7#include <linux/tty.h>
   8#include <linux/tty_driver.h>
   9#include <linux/poll.h>
  10
  11#define SERPORT_ACTIVE          1
  12
  13struct serport {
  14        struct tty_port *port;
  15        struct tty_struct *tty;
  16        struct tty_driver *tty_drv;
  17        int tty_idx;
  18        unsigned long flags;
  19};
  20
  21/*
  22 * Callback functions from the tty port.
  23 */
  24
  25static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp,
  26                                const unsigned char *fp, size_t count)
  27{
  28        struct serdev_controller *ctrl = port->client_data;
  29        struct serport *serport = serdev_controller_get_drvdata(ctrl);
  30        int ret;
  31
  32        if (!test_bit(SERPORT_ACTIVE, &serport->flags))
  33                return 0;
  34
  35        ret = serdev_controller_receive_buf(ctrl, cp, count);
  36
  37        dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count,
  38                                "receive_buf returns %d (count = %zu)\n",
  39                                ret, count);
  40        if (ret < 0)
  41                return 0;
  42        else if (ret > count)
  43                return count;
  44
  45        return ret;
  46}
  47
  48static void ttyport_write_wakeup(struct tty_port *port)
  49{
  50        struct serdev_controller *ctrl = port->client_data;
  51        struct serport *serport = serdev_controller_get_drvdata(ctrl);
  52        struct tty_struct *tty;
  53
  54        tty = tty_port_tty_get(port);
  55        if (!tty)
  56                return;
  57
  58        if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
  59            test_bit(SERPORT_ACTIVE, &serport->flags))
  60                serdev_controller_write_wakeup(ctrl);
  61
  62        /* Wake up any tty_wait_until_sent() */
  63        wake_up_interruptible(&tty->write_wait);
  64
  65        tty_kref_put(tty);
  66}
  67
  68static const struct tty_port_client_operations client_ops = {
  69        .receive_buf = ttyport_receive_buf,
  70        .write_wakeup = ttyport_write_wakeup,
  71};
  72
  73/*
  74 * Callback functions from the serdev core.
  75 */
  76
  77static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len)
  78{
  79        struct serport *serport = serdev_controller_get_drvdata(ctrl);
  80        struct tty_struct *tty = serport->tty;
  81
  82        if (!test_bit(SERPORT_ACTIVE, &serport->flags))
  83                return 0;
  84
  85        set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
  86        return tty->ops->write(serport->tty, data, len);
  87}
  88
  89static void ttyport_write_flush(struct serdev_controller *ctrl)
  90{
  91        struct serport *serport = serdev_controller_get_drvdata(ctrl);
  92        struct tty_struct *tty = serport->tty;
  93
  94        tty_driver_flush_buffer(tty);
  95}
  96
  97static int ttyport_write_room(struct serdev_controller *ctrl)
  98{
  99        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 100        struct tty_struct *tty = serport->tty;
 101
 102        return tty_write_room(tty);
 103}
 104
 105static int ttyport_open(struct serdev_controller *ctrl)
 106{
 107        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 108        struct tty_struct *tty;
 109        struct ktermios ktermios;
 110        int ret;
 111
 112        tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
 113        if (IS_ERR(tty))
 114                return PTR_ERR(tty);
 115        serport->tty = tty;
 116
 117        if (!tty->ops->open || !tty->ops->close) {
 118                ret = -ENODEV;
 119                goto err_unlock;
 120        }
 121
 122        ret = tty->ops->open(serport->tty, NULL);
 123        if (ret)
 124                goto err_close;
 125
 126        tty_unlock(serport->tty);
 127
 128        /* Bring the UART into a known 8 bits no parity hw fc state */
 129        ktermios = tty->termios;
 130        ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
 131                              INLCR | IGNCR | ICRNL | IXON);
 132        ktermios.c_oflag &= ~OPOST;
 133        ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
 134        ktermios.c_cflag &= ~(CSIZE | PARENB);
 135        ktermios.c_cflag |= CS8;
 136        ktermios.c_cflag |= CRTSCTS;
 137        /* Hangups are not supported so make sure to ignore carrier detect. */
 138        ktermios.c_cflag |= CLOCAL;
 139        tty_set_termios(tty, &ktermios);
 140
 141        set_bit(SERPORT_ACTIVE, &serport->flags);
 142
 143        return 0;
 144
 145err_close:
 146        tty->ops->close(tty, NULL);
 147err_unlock:
 148        tty_unlock(tty);
 149        tty_release_struct(tty, serport->tty_idx);
 150
 151        return ret;
 152}
 153
 154static void ttyport_close(struct serdev_controller *ctrl)
 155{
 156        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 157        struct tty_struct *tty = serport->tty;
 158
 159        clear_bit(SERPORT_ACTIVE, &serport->flags);
 160
 161        tty_lock(tty);
 162        if (tty->ops->close)
 163                tty->ops->close(tty, NULL);
 164        tty_unlock(tty);
 165
 166        tty_release_struct(tty, serport->tty_idx);
 167}
 168
 169static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)
 170{
 171        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 172        struct tty_struct *tty = serport->tty;
 173        struct ktermios ktermios = tty->termios;
 174
 175        ktermios.c_cflag &= ~CBAUD;
 176        tty_termios_encode_baud_rate(&ktermios, speed, speed);
 177
 178        /* tty_set_termios() return not checked as it is always 0 */
 179        tty_set_termios(tty, &ktermios);
 180        return ktermios.c_ospeed;
 181}
 182
 183static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
 184{
 185        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 186        struct tty_struct *tty = serport->tty;
 187        struct ktermios ktermios = tty->termios;
 188
 189        if (enable)
 190                ktermios.c_cflag |= CRTSCTS;
 191        else
 192                ktermios.c_cflag &= ~CRTSCTS;
 193
 194        tty_set_termios(tty, &ktermios);
 195}
 196
 197static int ttyport_set_parity(struct serdev_controller *ctrl,
 198                              enum serdev_parity parity)
 199{
 200        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 201        struct tty_struct *tty = serport->tty;
 202        struct ktermios ktermios = tty->termios;
 203
 204        ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
 205        if (parity != SERDEV_PARITY_NONE) {
 206                ktermios.c_cflag |= PARENB;
 207                if (parity == SERDEV_PARITY_ODD)
 208                        ktermios.c_cflag |= PARODD;
 209        }
 210
 211        tty_set_termios(tty, &ktermios);
 212
 213        if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) !=
 214            (ktermios.c_cflag & (PARENB | PARODD | CMSPAR)))
 215                return -EINVAL;
 216
 217        return 0;
 218}
 219
 220static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
 221{
 222        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 223        struct tty_struct *tty = serport->tty;
 224
 225        tty_wait_until_sent(tty, timeout);
 226}
 227
 228static int ttyport_get_tiocm(struct serdev_controller *ctrl)
 229{
 230        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 231        struct tty_struct *tty = serport->tty;
 232
 233        if (!tty->ops->tiocmget)
 234                return -ENOTSUPP;
 235
 236        return tty->ops->tiocmget(tty);
 237}
 238
 239static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
 240{
 241        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 242        struct tty_struct *tty = serport->tty;
 243
 244        if (!tty->ops->tiocmset)
 245                return -ENOTSUPP;
 246
 247        return tty->ops->tiocmset(tty, set, clear);
 248}
 249
 250static const struct serdev_controller_ops ctrl_ops = {
 251        .write_buf = ttyport_write_buf,
 252        .write_flush = ttyport_write_flush,
 253        .write_room = ttyport_write_room,
 254        .open = ttyport_open,
 255        .close = ttyport_close,
 256        .set_flow_control = ttyport_set_flow_control,
 257        .set_parity = ttyport_set_parity,
 258        .set_baudrate = ttyport_set_baudrate,
 259        .wait_until_sent = ttyport_wait_until_sent,
 260        .get_tiocm = ttyport_get_tiocm,
 261        .set_tiocm = ttyport_set_tiocm,
 262};
 263
 264struct device *serdev_tty_port_register(struct tty_port *port,
 265                                        struct device *parent,
 266                                        struct tty_driver *drv, int idx)
 267{
 268        struct serdev_controller *ctrl;
 269        struct serport *serport;
 270        int ret;
 271
 272        if (!port || !drv || !parent)
 273                return ERR_PTR(-ENODEV);
 274
 275        ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
 276        if (!ctrl)
 277                return ERR_PTR(-ENOMEM);
 278        serport = serdev_controller_get_drvdata(ctrl);
 279
 280        serport->port = port;
 281        serport->tty_idx = idx;
 282        serport->tty_drv = drv;
 283
 284        ctrl->ops = &ctrl_ops;
 285
 286        port->client_ops = &client_ops;
 287        port->client_data = ctrl;
 288
 289        ret = serdev_controller_add(ctrl);
 290        if (ret)
 291                goto err_reset_data;
 292
 293        dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
 294        return &ctrl->dev;
 295
 296err_reset_data:
 297        port->client_data = NULL;
 298        port->client_ops = &tty_port_default_client_ops;
 299        serdev_controller_put(ctrl);
 300
 301        return ERR_PTR(ret);
 302}
 303
 304int serdev_tty_port_unregister(struct tty_port *port)
 305{
 306        struct serdev_controller *ctrl = port->client_data;
 307        struct serport *serport = serdev_controller_get_drvdata(ctrl);
 308
 309        if (!serport)
 310                return -ENODEV;
 311
 312        serdev_controller_remove(ctrl);
 313        port->client_data = NULL;
 314        port->client_ops = &tty_port_default_client_ops;
 315        serdev_controller_put(ctrl);
 316
 317        return 0;
 318}
 319