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