1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 and 7 * only version 2 as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 #include <linux/kernel.h> 15 #include <linux/serdev.h> 16 #include <linux/tty.h> 17 #include <linux/tty_driver.h> 18 #include <linux/poll.h> 19 20 #define SERPORT_ACTIVE 1 21 22 struct serport { 23 struct tty_port *port; 24 struct tty_struct *tty; 25 struct tty_driver *tty_drv; 26 int tty_idx; 27 unsigned long flags; 28 }; 29 30 /* 31 * Callback functions from the tty port. 32 */ 33 34 static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, 35 const unsigned char *fp, size_t count) 36 { 37 struct serdev_controller *ctrl = port->client_data; 38 struct serport *serport = serdev_controller_get_drvdata(ctrl); 39 40 if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 41 return 0; 42 43 return serdev_controller_receive_buf(ctrl, cp, count); 44 } 45 46 static void ttyport_write_wakeup(struct tty_port *port) 47 { 48 struct serdev_controller *ctrl = port->client_data; 49 struct serport *serport = serdev_controller_get_drvdata(ctrl); 50 51 if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && 52 test_bit(SERPORT_ACTIVE, &serport->flags)) 53 serdev_controller_write_wakeup(ctrl); 54 55 wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); 56 } 57 58 static const struct tty_port_client_operations client_ops = { 59 .receive_buf = ttyport_receive_buf, 60 .write_wakeup = ttyport_write_wakeup, 61 }; 62 63 /* 64 * Callback functions from the serdev core. 65 */ 66 67 static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len) 68 { 69 struct serport *serport = serdev_controller_get_drvdata(ctrl); 70 struct tty_struct *tty = serport->tty; 71 72 if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 73 return 0; 74 75 set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 76 return tty->ops->write(serport->tty, data, len); 77 } 78 79 static void ttyport_write_flush(struct serdev_controller *ctrl) 80 { 81 struct serport *serport = serdev_controller_get_drvdata(ctrl); 82 struct tty_struct *tty = serport->tty; 83 84 tty_driver_flush_buffer(tty); 85 } 86 87 static int ttyport_write_room(struct serdev_controller *ctrl) 88 { 89 struct serport *serport = serdev_controller_get_drvdata(ctrl); 90 struct tty_struct *tty = serport->tty; 91 92 return tty_write_room(tty); 93 } 94 95 static int ttyport_open(struct serdev_controller *ctrl) 96 { 97 struct serport *serport = serdev_controller_get_drvdata(ctrl); 98 struct tty_struct *tty; 99 struct ktermios ktermios; 100 int ret; 101 102 tty = tty_init_dev(serport->tty_drv, serport->tty_idx); 103 if (IS_ERR(tty)) 104 return PTR_ERR(tty); 105 serport->tty = tty; 106 107 if (!tty->ops->open || !tty->ops->close) { 108 ret = -ENODEV; 109 goto err_unlock; 110 } 111 112 ret = tty->ops->open(serport->tty, NULL); 113 if (ret) 114 goto err_close; 115 116 /* Bring the UART into a known 8 bits no parity hw fc state */ 117 ktermios = tty->termios; 118 ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | 119 INLCR | IGNCR | ICRNL | IXON); 120 ktermios.c_oflag &= ~OPOST; 121 ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 122 ktermios.c_cflag &= ~(CSIZE | PARENB); 123 ktermios.c_cflag |= CS8; 124 ktermios.c_cflag |= CRTSCTS; 125 tty_set_termios(tty, &ktermios); 126 127 set_bit(SERPORT_ACTIVE, &serport->flags); 128 129 tty_unlock(serport->tty); 130 return 0; 131 132 err_close: 133 tty->ops->close(tty, NULL); 134 err_unlock: 135 tty_unlock(tty); 136 tty_release_struct(tty, serport->tty_idx); 137 138 return ret; 139 } 140 141 static void ttyport_close(struct serdev_controller *ctrl) 142 { 143 struct serport *serport = serdev_controller_get_drvdata(ctrl); 144 struct tty_struct *tty = serport->tty; 145 146 clear_bit(SERPORT_ACTIVE, &serport->flags); 147 148 if (tty->ops->close) 149 tty->ops->close(tty, NULL); 150 151 tty_release_struct(tty, serport->tty_idx); 152 } 153 154 static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed) 155 { 156 struct serport *serport = serdev_controller_get_drvdata(ctrl); 157 struct tty_struct *tty = serport->tty; 158 struct ktermios ktermios = tty->termios; 159 160 ktermios.c_cflag &= ~CBAUD; 161 tty_termios_encode_baud_rate(&ktermios, speed, speed); 162 163 /* tty_set_termios() return not checked as it is always 0 */ 164 tty_set_termios(tty, &ktermios); 165 return ktermios.c_ospeed; 166 } 167 168 static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) 169 { 170 struct serport *serport = serdev_controller_get_drvdata(ctrl); 171 struct tty_struct *tty = serport->tty; 172 struct ktermios ktermios = tty->termios; 173 174 if (enable) 175 ktermios.c_cflag |= CRTSCTS; 176 else 177 ktermios.c_cflag &= ~CRTSCTS; 178 179 tty_set_termios(tty, &ktermios); 180 } 181 182 static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) 183 { 184 struct serport *serport = serdev_controller_get_drvdata(ctrl); 185 struct tty_struct *tty = serport->tty; 186 187 tty_wait_until_sent(tty, timeout); 188 } 189 190 static int ttyport_get_tiocm(struct serdev_controller *ctrl) 191 { 192 struct serport *serport = serdev_controller_get_drvdata(ctrl); 193 struct tty_struct *tty = serport->tty; 194 195 if (!tty->ops->tiocmget) 196 return -ENOTSUPP; 197 198 return tty->driver->ops->tiocmget(tty); 199 } 200 201 static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) 202 { 203 struct serport *serport = serdev_controller_get_drvdata(ctrl); 204 struct tty_struct *tty = serport->tty; 205 206 if (!tty->ops->tiocmset) 207 return -ENOTSUPP; 208 209 return tty->driver->ops->tiocmset(tty, set, clear); 210 } 211 212 static const struct serdev_controller_ops ctrl_ops = { 213 .write_buf = ttyport_write_buf, 214 .write_flush = ttyport_write_flush, 215 .write_room = ttyport_write_room, 216 .open = ttyport_open, 217 .close = ttyport_close, 218 .set_flow_control = ttyport_set_flow_control, 219 .set_baudrate = ttyport_set_baudrate, 220 .wait_until_sent = ttyport_wait_until_sent, 221 .get_tiocm = ttyport_get_tiocm, 222 .set_tiocm = ttyport_set_tiocm, 223 }; 224 225 struct device *serdev_tty_port_register(struct tty_port *port, 226 struct device *parent, 227 struct tty_driver *drv, int idx) 228 { 229 const struct tty_port_client_operations *old_ops; 230 struct serdev_controller *ctrl; 231 struct serport *serport; 232 int ret; 233 234 if (!port || !drv || !parent) 235 return ERR_PTR(-ENODEV); 236 237 ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); 238 if (!ctrl) 239 return ERR_PTR(-ENOMEM); 240 serport = serdev_controller_get_drvdata(ctrl); 241 242 serport->port = port; 243 serport->tty_idx = idx; 244 serport->tty_drv = drv; 245 246 ctrl->ops = &ctrl_ops; 247 248 old_ops = port->client_ops; 249 port->client_ops = &client_ops; 250 port->client_data = ctrl; 251 252 ret = serdev_controller_add(ctrl); 253 if (ret) 254 goto err_reset_data; 255 256 dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); 257 return &ctrl->dev; 258 259 err_reset_data: 260 port->client_data = NULL; 261 port->client_ops = old_ops; 262 serdev_controller_put(ctrl); 263 264 return ERR_PTR(ret); 265 } 266 267 int serdev_tty_port_unregister(struct tty_port *port) 268 { 269 struct serdev_controller *ctrl = port->client_data; 270 struct serport *serport = serdev_controller_get_drvdata(ctrl); 271 272 if (!serport) 273 return -ENODEV; 274 275 serdev_controller_remove(ctrl); 276 port->client_ops = NULL; 277 port->client_data = NULL; 278 serdev_controller_put(ctrl); 279 280 return 0; 281 } 282