Skip to content

Commit bed35c6

Browse files
robherringgregkh
authored andcommitted
serdev: add a tty port controller driver
Add a serdev controller driver for tty ports. The controller is registered with serdev when tty ports are registered with the TTY core. As the TTY core is built-in only, this has the side effect of making serdev built-in as well. Signed-off-by: Rob Herring <[email protected]> Reviewed-By: Sebastian Reichel <[email protected]> Tested-By: Sebastian Reichel <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent cd6484e commit bed35c6

File tree

4 files changed

+255
-0
lines changed

4 files changed

+255
-0
lines changed

drivers/tty/serdev/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,11 @@ menuconfig SERIAL_DEV_BUS
66
help
77
Core support for devices connected via a serial port.
88

9+
if SERIAL_DEV_BUS
10+
11+
config SERIAL_DEV_CTRL_TTYPORT
12+
bool "Serial device TTY port controller"
13+
depends on TTY
14+
depends on SERIAL_DEV_BUS != m
15+
16+
endif

drivers/tty/serdev/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
serdev-objs := core.o
22

33
obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
4+
5+
obj-$(CONFIG_SERIAL_DEV_CTRL_TTYPORT) += serdev-ttyport.o
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <[email protected]>
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+
20+
struct 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+
32+
static 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+
44+
static 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+
56+
static 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+
65+
static 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+
77+
static 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+
85+
static 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+
93+
static 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+
serport->tty = tty;
101+
102+
serport->port->client_ops = &client_ops;
103+
serport->port->client_data = ctrl;
104+
105+
if (tty->ops->open)
106+
tty->ops->open(serport->tty, NULL);
107+
else
108+
tty_port_open(serport->port, tty, NULL);
109+
110+
/* Bring the UART into a known 8 bits no parity hw fc state */
111+
ktermios = tty->termios;
112+
ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
113+
INLCR | IGNCR | ICRNL | IXON);
114+
ktermios.c_oflag &= ~OPOST;
115+
ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
116+
ktermios.c_cflag &= ~(CSIZE | PARENB);
117+
ktermios.c_cflag |= CS8;
118+
ktermios.c_cflag |= CRTSCTS;
119+
tty_set_termios(tty, &ktermios);
120+
121+
set_bit(SERPORT_ACTIVE, &serport->flags);
122+
123+
tty_unlock(serport->tty);
124+
return 0;
125+
}
126+
127+
static void ttyport_close(struct serdev_controller *ctrl)
128+
{
129+
struct serport *serport = serdev_controller_get_drvdata(ctrl);
130+
struct tty_struct *tty = serport->tty;
131+
132+
clear_bit(SERPORT_ACTIVE, &serport->flags);
133+
134+
if (tty->ops->close)
135+
tty->ops->close(tty, NULL);
136+
137+
tty_release_struct(tty, serport->tty_idx);
138+
}
139+
140+
static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)
141+
{
142+
struct serport *serport = serdev_controller_get_drvdata(ctrl);
143+
struct tty_struct *tty = serport->tty;
144+
struct ktermios ktermios = tty->termios;
145+
146+
ktermios.c_cflag &= ~CBAUD;
147+
tty_termios_encode_baud_rate(&ktermios, speed, speed);
148+
149+
/* tty_set_termios() return not checked as it is always 0 */
150+
tty_set_termios(tty, &ktermios);
151+
return speed;
152+
}
153+
154+
static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
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+
if (enable)
161+
ktermios.c_cflag |= CRTSCTS;
162+
else
163+
ktermios.c_cflag &= ~CRTSCTS;
164+
165+
tty_set_termios(tty, &ktermios);
166+
}
167+
168+
static const struct serdev_controller_ops ctrl_ops = {
169+
.write_buf = ttyport_write_buf,
170+
.write_flush = ttyport_write_flush,
171+
.write_room = ttyport_write_room,
172+
.open = ttyport_open,
173+
.close = ttyport_close,
174+
.set_flow_control = ttyport_set_flow_control,
175+
.set_baudrate = ttyport_set_baudrate,
176+
};
177+
178+
struct device *serdev_tty_port_register(struct tty_port *port,
179+
struct device *parent,
180+
struct tty_driver *drv, int idx)
181+
{
182+
struct serdev_controller *ctrl;
183+
struct serport *serport;
184+
int ret;
185+
186+
if (!port || !drv || !parent)
187+
return ERR_PTR(-ENODEV);
188+
189+
ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
190+
if (!ctrl)
191+
return ERR_PTR(-ENOMEM);
192+
serport = serdev_controller_get_drvdata(ctrl);
193+
194+
serport->port = port;
195+
serport->tty_idx = idx;
196+
serport->tty_drv = drv;
197+
198+
ctrl->ops = &ctrl_ops;
199+
200+
ret = serdev_controller_add(ctrl);
201+
if (ret)
202+
goto err_controller_put;
203+
204+
dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
205+
return &ctrl->dev;
206+
207+
err_controller_put:
208+
serdev_controller_put(ctrl);
209+
return ERR_PTR(ret);
210+
}
211+
212+
void serdev_tty_port_unregister(struct tty_port *port)
213+
{
214+
struct serdev_controller *ctrl = port->client_data;
215+
struct serport *serport = serdev_controller_get_drvdata(ctrl);
216+
217+
if (!serport)
218+
return;
219+
220+
serdev_controller_remove(ctrl);
221+
port->client_ops = NULL;
222+
port->client_data = NULL;
223+
serdev_controller_put(ctrl);
224+
}

include/linux/serdev.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,25 @@ static inline int serdev_device_write_room(struct serdev_device *sdev)
238238

239239
#endif /* CONFIG_SERIAL_DEV_BUS */
240240

241+
/*
242+
* serdev hooks into TTY core
243+
*/
244+
struct tty_port;
245+
struct tty_driver;
246+
247+
#ifdef CONFIG_SERIAL_DEV_CTRL_TTYPORT
248+
struct device *serdev_tty_port_register(struct tty_port *port,
249+
struct device *parent,
250+
struct tty_driver *drv, int idx);
251+
void serdev_tty_port_unregister(struct tty_port *port);
252+
#else
253+
static inline struct device *serdev_tty_port_register(struct tty_port *port,
254+
struct device *parent,
255+
struct tty_driver *drv, int idx)
256+
{
257+
return ERR_PTR(-ENODEV);
258+
}
259+
static inline void serdev_tty_port_unregister(struct tty_port *port) {}
260+
#endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */
261+
241262
#endif /*_LINUX_SERDEV_H */

0 commit comments

Comments
 (0)