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