xref: /linux/drivers/tty/serdev/serdev-ttyport.c (revision e3b3d0f549c1d19b94e6ac55c66643166ea649ef)
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