Branch data Line data Source code
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 : : 13 : : struct 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 : : 25 : 0 : static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, 26 : : const unsigned char *fp, size_t count) 27 : : { 28 : 0 : struct serdev_controller *ctrl = port->client_data; 29 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 30 : : int ret; 31 : : 32 : 0 : if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 33 : : return 0; 34 : : 35 : : ret = serdev_controller_receive_buf(ctrl, cp, count); 36 : : 37 : 0 : dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count, 38 : : "receive_buf returns %d (count = %zu)\n", 39 : : ret, count); 40 : 0 : if (ret < 0) 41 : : return 0; 42 : 0 : else if (ret > count) 43 : 0 : return count; 44 : : 45 : : return ret; 46 : : } 47 : : 48 : 0 : static void ttyport_write_wakeup(struct tty_port *port) 49 : : { 50 : 0 : struct serdev_controller *ctrl = port->client_data; 51 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 52 : : struct tty_struct *tty; 53 : : 54 : 0 : tty = tty_port_tty_get(port); 55 : 0 : if (!tty) 56 : 0 : return; 57 : : 58 : 0 : 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 : 0 : wake_up_interruptible(&tty->write_wait); 64 : : 65 : 0 : tty_kref_put(tty); 66 : : } 67 : : 68 : : static 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 : : 77 : 0 : static 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 : 0 : struct tty_struct *tty = serport->tty; 81 : : 82 : 0 : if (!test_bit(SERPORT_ACTIVE, &serport->flags)) 83 : : return 0; 84 : : 85 : 0 : set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 86 : 0 : return tty->ops->write(serport->tty, data, len); 87 : : } 88 : : 89 : 0 : static void ttyport_write_flush(struct serdev_controller *ctrl) 90 : : { 91 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 92 : 0 : struct tty_struct *tty = serport->tty; 93 : : 94 : 0 : tty_driver_flush_buffer(tty); 95 : 0 : } 96 : : 97 : 0 : static int ttyport_write_room(struct serdev_controller *ctrl) 98 : : { 99 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 100 : 0 : struct tty_struct *tty = serport->tty; 101 : : 102 : 0 : return tty_write_room(tty); 103 : : } 104 : : 105 : 0 : static 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 : 0 : tty = tty_init_dev(serport->tty_drv, serport->tty_idx); 113 : 0 : if (IS_ERR(tty)) 114 : 0 : return PTR_ERR(tty); 115 : 0 : serport->tty = tty; 116 : : 117 : 0 : if (!tty->ops->open || !tty->ops->close) { 118 : : ret = -ENODEV; 119 : : goto err_unlock; 120 : : } 121 : : 122 : 0 : ret = tty->ops->open(serport->tty, NULL); 123 : 0 : if (ret) 124 : : goto err_close; 125 : : 126 : 0 : tty_unlock(serport->tty); 127 : : 128 : : /* Bring the UART into a known 8 bits no parity hw fc state */ 129 : 0 : ktermios = tty->termios; 130 : 0 : ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | 131 : : INLCR | IGNCR | ICRNL | IXON); 132 : 0 : ktermios.c_oflag &= ~OPOST; 133 : 0 : ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 134 : 0 : ktermios.c_cflag &= ~(CSIZE | PARENB); 135 : 0 : ktermios.c_cflag |= CS8; 136 : 0 : ktermios.c_cflag |= CRTSCTS; 137 : : /* Hangups are not supported so make sure to ignore carrier detect. */ 138 : 0 : ktermios.c_cflag |= CLOCAL; 139 : 0 : tty_set_termios(tty, &ktermios); 140 : : 141 : 0 : set_bit(SERPORT_ACTIVE, &serport->flags); 142 : : 143 : 0 : return 0; 144 : : 145 : : err_close: 146 : 0 : tty->ops->close(tty, NULL); 147 : : err_unlock: 148 : 0 : tty_unlock(tty); 149 : 0 : tty_release_struct(tty, serport->tty_idx); 150 : : 151 : 0 : return ret; 152 : : } 153 : : 154 : 0 : static void ttyport_close(struct serdev_controller *ctrl) 155 : : { 156 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 157 : 0 : struct tty_struct *tty = serport->tty; 158 : : 159 : 0 : clear_bit(SERPORT_ACTIVE, &serport->flags); 160 : : 161 : 0 : tty_lock(tty); 162 : 0 : if (tty->ops->close) 163 : 0 : tty->ops->close(tty, NULL); 164 : 0 : tty_unlock(tty); 165 : : 166 : 0 : tty_release_struct(tty, serport->tty_idx); 167 : 0 : } 168 : : 169 : 0 : static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed) 170 : : { 171 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 172 : 0 : struct tty_struct *tty = serport->tty; 173 : 0 : struct ktermios ktermios = tty->termios; 174 : : 175 : 0 : ktermios.c_cflag &= ~CBAUD; 176 : 0 : tty_termios_encode_baud_rate(&ktermios, speed, speed); 177 : : 178 : : /* tty_set_termios() return not checked as it is always 0 */ 179 : 0 : tty_set_termios(tty, &ktermios); 180 : 0 : return ktermios.c_ospeed; 181 : : } 182 : : 183 : 0 : static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable) 184 : : { 185 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 186 : 0 : struct tty_struct *tty = serport->tty; 187 : 0 : struct ktermios ktermios = tty->termios; 188 : : 189 : 0 : if (enable) 190 : 0 : ktermios.c_cflag |= CRTSCTS; 191 : : else 192 : 0 : ktermios.c_cflag &= ~CRTSCTS; 193 : : 194 : 0 : tty_set_termios(tty, &ktermios); 195 : 0 : } 196 : : 197 : 0 : static int ttyport_set_parity(struct serdev_controller *ctrl, 198 : : enum serdev_parity parity) 199 : : { 200 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 201 : 0 : struct tty_struct *tty = serport->tty; 202 : 0 : struct ktermios ktermios = tty->termios; 203 : : 204 : 0 : ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR); 205 : 0 : if (parity != SERDEV_PARITY_NONE) { 206 : 0 : ktermios.c_cflag |= PARENB; 207 : 0 : if (parity == SERDEV_PARITY_ODD) 208 : 0 : ktermios.c_cflag |= PARODD; 209 : : } 210 : : 211 : 0 : tty_set_termios(tty, &ktermios); 212 : : 213 : 0 : if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) != 214 : 0 : (ktermios.c_cflag & (PARENB | PARODD | CMSPAR))) 215 : : return -EINVAL; 216 : : 217 : 0 : return 0; 218 : : } 219 : : 220 : 0 : static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) 221 : : { 222 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 223 : 0 : struct tty_struct *tty = serport->tty; 224 : : 225 : 0 : tty_wait_until_sent(tty, timeout); 226 : 0 : } 227 : : 228 : 0 : static int ttyport_get_tiocm(struct serdev_controller *ctrl) 229 : : { 230 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 231 : 0 : struct tty_struct *tty = serport->tty; 232 : : 233 : 0 : if (!tty->ops->tiocmget) 234 : : return -ENOTSUPP; 235 : : 236 : 0 : return tty->ops->tiocmget(tty); 237 : : } 238 : : 239 : 0 : static 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 : 0 : struct tty_struct *tty = serport->tty; 243 : : 244 : 0 : if (!tty->ops->tiocmset) 245 : : return -ENOTSUPP; 246 : : 247 : 0 : return tty->ops->tiocmset(tty, set, clear); 248 : : } 249 : : 250 : : static 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 : : 264 : 3 : struct 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 : 3 : if (!port || !drv || !parent) 273 : : return ERR_PTR(-ENODEV); 274 : : 275 : 3 : ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); 276 : 3 : if (!ctrl) 277 : : return ERR_PTR(-ENOMEM); 278 : : serport = serdev_controller_get_drvdata(ctrl); 279 : : 280 : 3 : serport->port = port; 281 : 3 : serport->tty_idx = idx; 282 : 3 : serport->tty_drv = drv; 283 : : 284 : 3 : ctrl->ops = &ctrl_ops; 285 : : 286 : 3 : port->client_ops = &client_ops; 287 : 3 : port->client_data = ctrl; 288 : : 289 : 3 : ret = serdev_controller_add(ctrl); 290 : 3 : if (ret) 291 : : goto err_reset_data; 292 : : 293 : 0 : dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); 294 : 0 : return &ctrl->dev; 295 : : 296 : : err_reset_data: 297 : 3 : port->client_data = NULL; 298 : 3 : port->client_ops = &tty_port_default_client_ops; 299 : : serdev_controller_put(ctrl); 300 : : 301 : 3 : return ERR_PTR(ret); 302 : : } 303 : : 304 : 0 : int serdev_tty_port_unregister(struct tty_port *port) 305 : : { 306 : 0 : struct serdev_controller *ctrl = port->client_data; 307 : : struct serport *serport = serdev_controller_get_drvdata(ctrl); 308 : : 309 : 0 : if (!serport) 310 : : return -ENODEV; 311 : : 312 : 0 : serdev_controller_remove(ctrl); 313 : 0 : port->client_data = NULL; 314 : 0 : port->client_ops = &tty_port_default_client_ops; 315 : : serdev_controller_put(ctrl); 316 : : 317 : : return 0; 318 : : }