diff options
-rw-r--r-- | drivers/tty/serdev/Kconfig | 8 | ||||
-rw-r--r-- | drivers/tty/serdev/Makefile | 2 | ||||
-rw-r--r-- | drivers/tty/serdev/serdev-ttyport.c | 224 | ||||
-rw-r--r-- | include/linux/serdev.h | 21 |
4 files changed, 255 insertions, 0 deletions
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig index 3b6ecd187bef..cdc6b820cf93 100644 --- a/drivers/tty/serdev/Kconfig +++ b/drivers/tty/serdev/Kconfig | |||
@@ -6,3 +6,11 @@ menuconfig SERIAL_DEV_BUS | |||
6 | help | 6 | help |
7 | Core support for devices connected via a serial port. | 7 | Core support for devices connected via a serial port. |
8 | 8 | ||
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 | ||
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile index 01a9b62183f4..0cbdb9444d9d 100644 --- a/drivers/tty/serdev/Makefile +++ b/drivers/tty/serdev/Makefile | |||
@@ -1,3 +1,5 @@ | |||
1 | serdev-objs := core.o | 1 | serdev-objs := core.o |
2 | 2 | ||
3 | obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o | 3 | obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o |
4 | |||
5 | obj-$(CONFIG_SERIAL_DEV_CTRL_TTYPORT) += serdev-ttyport.o | ||
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c new file mode 100644 index 000000000000..683320b81a2b --- /dev/null +++ b/drivers/tty/serdev/serdev-ttyport.c | |||
@@ -0,0 +1,224 @@ | |||
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 | |||
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 | } | ||
diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 3ac26c1a8aab..9519da6253a8 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h | |||
@@ -238,4 +238,25 @@ static inline int serdev_device_write_room(struct serdev_device *sdev) | |||
238 | 238 | ||
239 | #endif /* CONFIG_SERIAL_DEV_BUS */ | 239 | #endif /* CONFIG_SERIAL_DEV_BUS */ |
240 | 240 | ||
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 | |||
241 | #endif /*_LINUX_SERDEV_H */ | 262 | #endif /*_LINUX_SERDEV_H */ |