]>
Commit | Line | Data |
---|---|---|
bed35c6d RH |
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> | |
b3f80c8f | 17 | #include <linux/poll.h> |
bed35c6d RH |
18 | |
19 | #define SERPORT_ACTIVE 1 | |
20 | ||
21 | struct serport { | |
22 | struct tty_port *port; | |
23 | struct tty_struct *tty; | |
24 | struct tty_driver *tty_drv; | |
25 | int tty_idx; | |
26 | unsigned long flags; | |
27 | }; | |
28 | ||
29 | /* | |
30 | * Callback functions from the tty port. | |
31 | */ | |
32 | ||
33 | static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp, | |
34 | const unsigned char *fp, size_t count) | |
35 | { | |
36 | struct serdev_controller *ctrl = port->client_data; | |
37 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
38 | ||
39 | if (!test_bit(SERPORT_ACTIVE, &serport->flags)) | |
40 | return 0; | |
41 | ||
42 | return serdev_controller_receive_buf(ctrl, cp, count); | |
43 | } | |
44 | ||
45 | static void ttyport_write_wakeup(struct tty_port *port) | |
46 | { | |
47 | struct serdev_controller *ctrl = port->client_data; | |
48 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
49 | ||
b3f80c8f SR |
50 | if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && |
51 | test_bit(SERPORT_ACTIVE, &serport->flags)) | |
bed35c6d | 52 | serdev_controller_write_wakeup(ctrl); |
b3f80c8f SR |
53 | |
54 | wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); | |
bed35c6d RH |
55 | } |
56 | ||
57 | static const struct tty_port_client_operations client_ops = { | |
58 | .receive_buf = ttyport_receive_buf, | |
59 | .write_wakeup = ttyport_write_wakeup, | |
60 | }; | |
61 | ||
62 | /* | |
63 | * Callback functions from the serdev core. | |
64 | */ | |
65 | ||
66 | static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len) | |
67 | { | |
68 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
69 | struct tty_struct *tty = serport->tty; | |
70 | ||
71 | if (!test_bit(SERPORT_ACTIVE, &serport->flags)) | |
72 | return 0; | |
73 | ||
74 | set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | |
75 | return tty->ops->write(serport->tty, data, len); | |
76 | } | |
77 | ||
78 | static void ttyport_write_flush(struct serdev_controller *ctrl) | |
79 | { | |
80 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
81 | struct tty_struct *tty = serport->tty; | |
82 | ||
83 | tty_driver_flush_buffer(tty); | |
84 | } | |
85 | ||
86 | static int ttyport_write_room(struct serdev_controller *ctrl) | |
87 | { | |
88 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
89 | struct tty_struct *tty = serport->tty; | |
90 | ||
91 | return tty_write_room(tty); | |
92 | } | |
93 | ||
94 | static int ttyport_open(struct serdev_controller *ctrl) | |
95 | { | |
96 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
97 | struct tty_struct *tty; | |
98 | struct ktermios ktermios; | |
99 | ||
100 | tty = tty_init_dev(serport->tty_drv, serport->tty_idx); | |
10d258c5 DC |
101 | if (IS_ERR(tty)) |
102 | return PTR_ERR(tty); | |
bed35c6d RH |
103 | serport->tty = tty; |
104 | ||
bed35c6d RH |
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 | ||
b3f80c8f SR |
168 | static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) |
169 | { | |
170 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
171 | struct tty_struct *tty = serport->tty; | |
172 | ||
173 | tty_wait_until_sent(tty, timeout); | |
174 | } | |
175 | ||
5659dab2 SR |
176 | static int ttyport_get_tiocm(struct serdev_controller *ctrl) |
177 | { | |
178 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
179 | struct tty_struct *tty = serport->tty; | |
180 | ||
181 | if (!tty->ops->tiocmget) | |
182 | return -ENOTSUPP; | |
183 | ||
184 | return tty->driver->ops->tiocmget(tty); | |
185 | } | |
186 | ||
187 | static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) | |
188 | { | |
189 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
190 | struct tty_struct *tty = serport->tty; | |
191 | ||
192 | if (!tty->ops->tiocmset) | |
193 | return -ENOTSUPP; | |
194 | ||
195 | return tty->driver->ops->tiocmset(tty, set, clear); | |
196 | } | |
197 | ||
bed35c6d RH |
198 | static const struct serdev_controller_ops ctrl_ops = { |
199 | .write_buf = ttyport_write_buf, | |
200 | .write_flush = ttyport_write_flush, | |
201 | .write_room = ttyport_write_room, | |
202 | .open = ttyport_open, | |
203 | .close = ttyport_close, | |
204 | .set_flow_control = ttyport_set_flow_control, | |
205 | .set_baudrate = ttyport_set_baudrate, | |
b3f80c8f | 206 | .wait_until_sent = ttyport_wait_until_sent, |
5659dab2 SR |
207 | .get_tiocm = ttyport_get_tiocm, |
208 | .set_tiocm = ttyport_set_tiocm, | |
bed35c6d RH |
209 | }; |
210 | ||
211 | struct device *serdev_tty_port_register(struct tty_port *port, | |
212 | struct device *parent, | |
213 | struct tty_driver *drv, int idx) | |
214 | { | |
aee5da78 | 215 | const struct tty_port_client_operations *old_ops; |
bed35c6d RH |
216 | struct serdev_controller *ctrl; |
217 | struct serport *serport; | |
218 | int ret; | |
219 | ||
220 | if (!port || !drv || !parent) | |
221 | return ERR_PTR(-ENODEV); | |
222 | ||
223 | ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); | |
224 | if (!ctrl) | |
225 | return ERR_PTR(-ENOMEM); | |
226 | serport = serdev_controller_get_drvdata(ctrl); | |
227 | ||
228 | serport->port = port; | |
229 | serport->tty_idx = idx; | |
230 | serport->tty_drv = drv; | |
231 | ||
232 | ctrl->ops = &ctrl_ops; | |
233 | ||
aee5da78 JH |
234 | old_ops = port->client_ops; |
235 | port->client_ops = &client_ops; | |
236 | port->client_data = ctrl; | |
237 | ||
bed35c6d RH |
238 | ret = serdev_controller_add(ctrl); |
239 | if (ret) | |
aee5da78 | 240 | goto err_reset_data; |
bed35c6d RH |
241 | |
242 | dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx); | |
243 | return &ctrl->dev; | |
244 | ||
aee5da78 JH |
245 | err_reset_data: |
246 | port->client_data = NULL; | |
247 | port->client_ops = old_ops; | |
bed35c6d | 248 | serdev_controller_put(ctrl); |
aee5da78 | 249 | |
bed35c6d RH |
250 | return ERR_PTR(ret); |
251 | } | |
252 | ||
8cde11b2 | 253 | int serdev_tty_port_unregister(struct tty_port *port) |
bed35c6d RH |
254 | { |
255 | struct serdev_controller *ctrl = port->client_data; | |
256 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | |
257 | ||
258 | if (!serport) | |
8cde11b2 | 259 | return -ENODEV; |
bed35c6d RH |
260 | |
261 | serdev_controller_remove(ctrl); | |
262 | port->client_ops = NULL; | |
263 | port->client_data = NULL; | |
264 | serdev_controller_put(ctrl); | |
8cde11b2 JH |
265 | |
266 | return 0; | |
bed35c6d | 267 | } |