cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

serdev-ttyport.c (7921B)


      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}