• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <linux/platform_device.h>
11 #include <linux/module.h>
12 
13 #define SERPORT_ACTIVE		1
14 
15 static char *pdev_tty_port;
16 module_param(pdev_tty_port, charp, 0644);
17 MODULE_PARM_DESC(pdev_tty_port, "platform device tty port to claim");
18 
19 struct serport {
20 	struct tty_port *port;
21 	struct tty_struct *tty;
22 	struct tty_driver *tty_drv;
23 	int tty_idx;
24 	unsigned long flags;
25 };
26 
27 /*
28  * Callback functions from the tty port.
29  */
30 
ttyport_receive_buf(struct tty_port * port,const unsigned char * cp,const unsigned char * fp,size_t count)31 static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp,
32 				const unsigned char *fp, size_t count)
33 {
34 	struct serdev_controller *ctrl = port->client_data;
35 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
36 	int ret;
37 
38 	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
39 		return 0;
40 
41 	ret = serdev_controller_receive_buf(ctrl, cp, count);
42 
43 	dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count,
44 				"receive_buf returns %d (count = %zu)\n",
45 				ret, count);
46 	if (ret < 0)
47 		return 0;
48 	else if (ret > count)
49 		return count;
50 
51 	return ret;
52 }
53 
ttyport_write_wakeup(struct tty_port * port)54 static void ttyport_write_wakeup(struct tty_port *port)
55 {
56 	struct serdev_controller *ctrl = port->client_data;
57 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
58 	struct tty_struct *tty;
59 
60 	tty = tty_port_tty_get(port);
61 	if (!tty)
62 		return;
63 
64 	if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
65 	    test_bit(SERPORT_ACTIVE, &serport->flags))
66 		serdev_controller_write_wakeup(ctrl);
67 
68 	/* Wake up any tty_wait_until_sent() */
69 	wake_up_interruptible(&tty->write_wait);
70 
71 	tty_kref_put(tty);
72 }
73 
74 static const struct tty_port_client_operations client_ops = {
75 	.receive_buf = ttyport_receive_buf,
76 	.write_wakeup = ttyport_write_wakeup,
77 };
78 
79 /*
80  * Callback functions from the serdev core.
81  */
82 
ttyport_write_buf(struct serdev_controller * ctrl,const unsigned char * data,size_t len)83 static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len)
84 {
85 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
86 	struct tty_struct *tty = serport->tty;
87 
88 	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
89 		return 0;
90 
91 	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
92 	return tty->ops->write(serport->tty, data, len);
93 }
94 
ttyport_write_flush(struct serdev_controller * ctrl)95 static void ttyport_write_flush(struct serdev_controller *ctrl)
96 {
97 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
98 	struct tty_struct *tty = serport->tty;
99 
100 	tty_driver_flush_buffer(tty);
101 }
102 
ttyport_write_room(struct serdev_controller * ctrl)103 static int ttyport_write_room(struct serdev_controller *ctrl)
104 {
105 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
106 	struct tty_struct *tty = serport->tty;
107 
108 	return tty_write_room(tty);
109 }
110 
ttyport_open(struct serdev_controller * ctrl)111 static int ttyport_open(struct serdev_controller *ctrl)
112 {
113 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
114 	struct tty_struct *tty;
115 	struct ktermios ktermios;
116 	int ret;
117 
118 	tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
119 	if (IS_ERR(tty))
120 		return PTR_ERR(tty);
121 	serport->tty = tty;
122 
123 	if (!tty->ops->open || !tty->ops->close) {
124 		ret = -ENODEV;
125 		goto err_unlock;
126 	}
127 
128 	ret = tty->ops->open(serport->tty, NULL);
129 	if (ret)
130 		goto err_close;
131 
132 	tty_unlock(serport->tty);
133 
134 	/* Bring the UART into a known 8 bits no parity hw fc state */
135 	ktermios = tty->termios;
136 	ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
137 			      INLCR | IGNCR | ICRNL | IXON);
138 	ktermios.c_oflag &= ~OPOST;
139 	ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
140 	ktermios.c_cflag &= ~(CSIZE | PARENB);
141 	ktermios.c_cflag |= CS8;
142 	ktermios.c_cflag |= CRTSCTS;
143 	/* Hangups are not supported so make sure to ignore carrier detect. */
144 	ktermios.c_cflag |= CLOCAL;
145 	tty_set_termios(tty, &ktermios);
146 
147 	set_bit(SERPORT_ACTIVE, &serport->flags);
148 
149 	return 0;
150 
151 err_close:
152 	tty->ops->close(tty, NULL);
153 err_unlock:
154 	tty_unlock(tty);
155 	tty_release_struct(tty, serport->tty_idx);
156 
157 	return ret;
158 }
159 
ttyport_close(struct serdev_controller * ctrl)160 static void ttyport_close(struct serdev_controller *ctrl)
161 {
162 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
163 	struct tty_struct *tty = serport->tty;
164 
165 	clear_bit(SERPORT_ACTIVE, &serport->flags);
166 
167 	tty_lock(tty);
168 	if (tty->ops->close)
169 		tty->ops->close(tty, NULL);
170 	tty_unlock(tty);
171 
172 	tty_release_struct(tty, serport->tty_idx);
173 }
174 
ttyport_set_baudrate(struct serdev_controller * ctrl,unsigned int speed)175 static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)
176 {
177 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
178 	struct tty_struct *tty = serport->tty;
179 	struct ktermios ktermios = tty->termios;
180 
181 	ktermios.c_cflag &= ~CBAUD;
182 	tty_termios_encode_baud_rate(&ktermios, speed, speed);
183 
184 	/* tty_set_termios() return not checked as it is always 0 */
185 	tty_set_termios(tty, &ktermios);
186 	return ktermios.c_ospeed;
187 }
188 
ttyport_set_flow_control(struct serdev_controller * ctrl,bool enable)189 static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
190 {
191 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
192 	struct tty_struct *tty = serport->tty;
193 	struct ktermios ktermios = tty->termios;
194 
195 	if (enable)
196 		ktermios.c_cflag |= CRTSCTS;
197 	else
198 		ktermios.c_cflag &= ~CRTSCTS;
199 
200 	tty_set_termios(tty, &ktermios);
201 }
202 
ttyport_set_parity(struct serdev_controller * ctrl,enum serdev_parity parity)203 static int ttyport_set_parity(struct serdev_controller *ctrl,
204 			      enum serdev_parity parity)
205 {
206 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
207 	struct tty_struct *tty = serport->tty;
208 	struct ktermios ktermios = tty->termios;
209 
210 	ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
211 	if (parity != SERDEV_PARITY_NONE) {
212 		ktermios.c_cflag |= PARENB;
213 		if (parity == SERDEV_PARITY_ODD)
214 			ktermios.c_cflag |= PARODD;
215 	}
216 
217 	tty_set_termios(tty, &ktermios);
218 
219 	if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) !=
220 	    (ktermios.c_cflag & (PARENB | PARODD | CMSPAR)))
221 		return -EINVAL;
222 
223 	return 0;
224 }
225 
ttyport_wait_until_sent(struct serdev_controller * ctrl,long timeout)226 static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
227 {
228 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
229 	struct tty_struct *tty = serport->tty;
230 
231 	tty_wait_until_sent(tty, timeout);
232 }
233 
ttyport_get_tiocm(struct serdev_controller * ctrl)234 static int ttyport_get_tiocm(struct serdev_controller *ctrl)
235 {
236 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
237 	struct tty_struct *tty = serport->tty;
238 
239 	if (!tty->ops->tiocmget)
240 		return -ENOTSUPP;
241 
242 	return tty->ops->tiocmget(tty);
243 }
244 
ttyport_set_tiocm(struct serdev_controller * ctrl,unsigned int set,unsigned int clear)245 static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
246 {
247 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
248 	struct tty_struct *tty = serport->tty;
249 
250 	if (!tty->ops->tiocmset)
251 		return -ENOTSUPP;
252 
253 	return tty->ops->tiocmset(tty, set, clear);
254 }
255 
256 static const struct serdev_controller_ops ctrl_ops = {
257 	.write_buf = ttyport_write_buf,
258 	.write_flush = ttyport_write_flush,
259 	.write_room = ttyport_write_room,
260 	.open = ttyport_open,
261 	.close = ttyport_close,
262 	.set_flow_control = ttyport_set_flow_control,
263 	.set_parity = ttyport_set_parity,
264 	.set_baudrate = ttyport_set_baudrate,
265 	.wait_until_sent = ttyport_wait_until_sent,
266 	.get_tiocm = ttyport_get_tiocm,
267 	.set_tiocm = ttyport_set_tiocm,
268 };
269 
serdev_tty_port_register(struct tty_port * port,struct device * parent,struct tty_driver * drv,int idx)270 struct device *serdev_tty_port_register(struct tty_port *port,
271 					struct device *parent,
272 					struct tty_driver *drv, int idx)
273 {
274 	struct serdev_controller *ctrl;
275 	struct serport *serport;
276 	bool platform = false;
277 	int ret;
278 
279 	if (!port || !drv || !parent)
280 		return ERR_PTR(-ENODEV);
281 
282 	ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
283 	if (!ctrl)
284 		return ERR_PTR(-ENOMEM);
285 	serport = serdev_controller_get_drvdata(ctrl);
286 
287 	serport->port = port;
288 	serport->tty_idx = idx;
289 	serport->tty_drv = drv;
290 
291 	ctrl->ops = &ctrl_ops;
292 
293 	port->client_ops = &client_ops;
294 	port->client_data = ctrl;
295 
296 	/* There is not always a way to bind specific platform devices because
297 	 * they may be defined on platforms without DT or ACPI. When dealing
298 	 * with a platform devices, do not allow direct binding unless it is
299 	 * whitelisted by module parameter. If a platform device is otherwise
300 	 * described by DT or ACPI it will still be bound and this check will
301 	 * be ignored.
302 	 */
303 	if (parent->bus == &platform_bus_type) {
304 		if (pdev_tty_port) {
305 			unsigned long pdev_idx;
306 			int tty_len = strlen(drv->name);
307 
308 			if (!strncmp(pdev_tty_port, drv->name, tty_len)) {
309 				if (!kstrtoul(pdev_tty_port + tty_len, 10,
310 					     &pdev_idx) && pdev_idx == idx) {
311 					platform = true;
312 				}
313 			}
314 		}
315 	}
316 
317 	ret = serdev_controller_add_platform(ctrl, platform);
318 	if (ret)
319 		goto err_reset_data;
320 
321 	dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
322 	return &ctrl->dev;
323 
324 err_reset_data:
325 	port->client_data = NULL;
326 	port->client_ops = &tty_port_default_client_ops;
327 	serdev_controller_put(ctrl);
328 
329 	return ERR_PTR(ret);
330 }
331 
serdev_tty_port_unregister(struct tty_port * port)332 int serdev_tty_port_unregister(struct tty_port *port)
333 {
334 	struct serdev_controller *ctrl = port->client_data;
335 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
336 
337 	if (!serport)
338 		return -ENODEV;
339 
340 	serdev_controller_remove(ctrl);
341 	port->client_data = NULL;
342 	port->client_ops = &tty_port_default_client_ops;
343 	serdev_controller_put(ctrl);
344 
345 	return 0;
346 }
347