Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/types.h>
3#include <linux/tty.h>
4#include <linux/tty_flip.h>
5#include <linux/slab.h>
6
7#include "speakup.h"
8#include "spk_types.h"
9#include "spk_priv.h"
10
11struct spk_ldisc_data {
12 char buf;
13 struct completion completion;
14 bool buf_free;
15};
16
17static struct spk_synth *spk_ttyio_synth;
18static struct tty_struct *speakup_tty;
19/* mutex to protect against speakup_tty disappearing from underneath us while
20 * we are using it. this can happen when the device physically unplugged,
21 * while in use. it also serialises access to speakup_tty.
22 */
23static DEFINE_MUTEX(speakup_tty_mutex);
24
25static int ser_to_dev(int ser, dev_t *dev_no)
26{
27 if (ser < 0 || ser > (255 - 64)) {
28 pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n");
29 return -EINVAL;
30 }
31
32 *dev_no = MKDEV(4, (64 + ser));
33 return 0;
34}
35
36static int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no)
37{
38 /* use ser only when dev is not specified */
39 if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) ||
40 synth->ser == SYNTH_DEFAULT_SER)
41 return tty_dev_name_to_number(synth->dev_name, dev_no);
42
43 return ser_to_dev(synth->ser, dev_no);
44}
45
46static int spk_ttyio_ldisc_open(struct tty_struct *tty)
47{
48 struct spk_ldisc_data *ldisc_data;
49
50 if (!tty->ops->write)
51 return -EOPNOTSUPP;
52
53 mutex_lock(&speakup_tty_mutex);
54 if (speakup_tty) {
55 mutex_unlock(&speakup_tty_mutex);
56 return -EBUSY;
57 }
58 speakup_tty = tty;
59
60 ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);
61 if (!ldisc_data) {
62 speakup_tty = NULL;
63 mutex_unlock(&speakup_tty_mutex);
64 return -ENOMEM;
65 }
66
67 init_completion(&ldisc_data->completion);
68 ldisc_data->buf_free = true;
69 speakup_tty->disc_data = ldisc_data;
70 mutex_unlock(&speakup_tty_mutex);
71
72 return 0;
73}
74
75static void spk_ttyio_ldisc_close(struct tty_struct *tty)
76{
77 mutex_lock(&speakup_tty_mutex);
78 kfree(speakup_tty->disc_data);
79 speakup_tty = NULL;
80 mutex_unlock(&speakup_tty_mutex);
81}
82
83static int spk_ttyio_receive_buf2(struct tty_struct *tty,
84 const unsigned char *cp, char *fp, int count)
85{
86 struct spk_ldisc_data *ldisc_data = tty->disc_data;
87
88 if (spk_ttyio_synth->read_buff_add) {
89 int i;
90
91 for (i = 0; i < count; i++)
92 spk_ttyio_synth->read_buff_add(cp[i]);
93
94 return count;
95 }
96
97 if (!ldisc_data->buf_free)
98 /* ttyio_in will tty_schedule_flip */
99 return 0;
100
101 /* Make sure the consumer has read buf before we have seen
102 * buf_free == true and overwrite buf
103 */
104 mb();
105
106 ldisc_data->buf = cp[0];
107 ldisc_data->buf_free = false;
108 complete(&ldisc_data->completion);
109
110 return 1;
111}
112
113static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
114 .owner = THIS_MODULE,
115 .magic = TTY_LDISC_MAGIC,
116 .name = "speakup_ldisc",
117 .open = spk_ttyio_ldisc_open,
118 .close = spk_ttyio_ldisc_close,
119 .receive_buf2 = spk_ttyio_receive_buf2,
120};
121
122static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
123static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);
124static void spk_ttyio_send_xchar(char ch);
125static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear);
126static unsigned char spk_ttyio_in(void);
127static unsigned char spk_ttyio_in_nowait(void);
128static void spk_ttyio_flush_buffer(void);
129static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth);
130
131struct spk_io_ops spk_ttyio_ops = {
132 .synth_out = spk_ttyio_out,
133 .synth_out_unicode = spk_ttyio_out_unicode,
134 .send_xchar = spk_ttyio_send_xchar,
135 .tiocmset = spk_ttyio_tiocmset,
136 .synth_in = spk_ttyio_in,
137 .synth_in_nowait = spk_ttyio_in_nowait,
138 .flush_buffer = spk_ttyio_flush_buffer,
139 .wait_for_xmitr = spk_ttyio_wait_for_xmitr,
140};
141EXPORT_SYMBOL_GPL(spk_ttyio_ops);
142
143static inline void get_termios(struct tty_struct *tty,
144 struct ktermios *out_termios)
145{
146 down_read(&tty->termios_rwsem);
147 *out_termios = tty->termios;
148 up_read(&tty->termios_rwsem);
149}
150
151static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
152{
153 int ret = 0;
154 struct tty_struct *tty;
155 struct ktermios tmp_termios;
156 dev_t dev;
157
158 ret = get_dev_to_use(synth, &dev);
159 if (ret)
160 return ret;
161
162 tty = tty_kopen(dev);
163 if (IS_ERR(tty))
164 return PTR_ERR(tty);
165
166 if (tty->ops->open)
167 ret = tty->ops->open(tty, NULL);
168 else
169 ret = -ENODEV;
170
171 if (ret) {
172 tty_unlock(tty);
173 return ret;
174 }
175
176 clear_bit(TTY_HUPPED, &tty->flags);
177 /* ensure hardware flow control is enabled */
178 get_termios(tty, &tmp_termios);
179 if (!(tmp_termios.c_cflag & CRTSCTS)) {
180 tmp_termios.c_cflag |= CRTSCTS;
181 tty_set_termios(tty, &tmp_termios);
182 /*
183 * check c_cflag to see if it's updated as tty_set_termios
184 * may not return error even when no tty bits are
185 * changed by the request.
186 */
187 get_termios(tty, &tmp_termios);
188 if (!(tmp_termios.c_cflag & CRTSCTS))
189 pr_warn("speakup: Failed to set hardware flow control\n");
190 }
191
192 tty_unlock(tty);
193
194 ret = tty_set_ldisc(tty, N_SPEAKUP);
195 if (ret)
196 pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
197
198 return ret;
199}
200
201void spk_ttyio_register_ldisc(void)
202{
203 if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops))
204 pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");
205}
206
207void spk_ttyio_unregister_ldisc(void)
208{
209 if (tty_unregister_ldisc(N_SPEAKUP))
210 pr_warn("speakup: Couldn't unregister ldisc\n");
211}
212
213static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
214{
215 mutex_lock(&speakup_tty_mutex);
216 if (in_synth->alive && speakup_tty && speakup_tty->ops->write) {
217 int ret = speakup_tty->ops->write(speakup_tty, &ch, 1);
218
219 mutex_unlock(&speakup_tty_mutex);
220 if (ret == 0)
221 /* No room */
222 return 0;
223 if (ret < 0) {
224 pr_warn("%s: I/O error, deactivating speakup\n",
225 in_synth->long_name);
226 /* No synth any more, so nobody will restart TTYs,
227 * and we thus need to do it ourselves. Now that there
228 * is no synth we can let application flood anyway
229 */
230 in_synth->alive = 0;
231 speakup_start_ttys();
232 return 0;
233 }
234 return 1;
235 }
236
237 mutex_unlock(&speakup_tty_mutex);
238 return 0;
239}
240
241static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)
242{
243 int ret;
244
245 if (ch < 0x80) {
246 ret = spk_ttyio_out(in_synth, ch);
247 } else if (ch < 0x800) {
248 ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));
249 ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
250 } else {
251 ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));
252 ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));
253 ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
254 }
255 return ret;
256}
257
258static int check_tty(struct tty_struct *tty)
259{
260 if (!tty) {
261 pr_warn("%s: I/O error, deactivating speakup\n",
262 spk_ttyio_synth->long_name);
263 /* No synth any more, so nobody will restart TTYs, and we thus
264 * need to do it ourselves. Now that there is no synth we can
265 * let application flood anyway
266 */
267 spk_ttyio_synth->alive = 0;
268 speakup_start_ttys();
269 return 1;
270 }
271
272 return 0;
273}
274
275static void spk_ttyio_send_xchar(char ch)
276{
277 mutex_lock(&speakup_tty_mutex);
278 if (check_tty(speakup_tty)) {
279 mutex_unlock(&speakup_tty_mutex);
280 return;
281 }
282
283 if (speakup_tty->ops->send_xchar)
284 speakup_tty->ops->send_xchar(speakup_tty, ch);
285 mutex_unlock(&speakup_tty_mutex);
286}
287
288static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear)
289{
290 mutex_lock(&speakup_tty_mutex);
291 if (check_tty(speakup_tty)) {
292 mutex_unlock(&speakup_tty_mutex);
293 return;
294 }
295
296 if (speakup_tty->ops->tiocmset)
297 speakup_tty->ops->tiocmset(speakup_tty, set, clear);
298 mutex_unlock(&speakup_tty_mutex);
299}
300
301static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth)
302{
303 return 1;
304}
305
306static unsigned char ttyio_in(int timeout)
307{
308 struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data;
309 char rv;
310
311 if (!timeout) {
312 if (!try_wait_for_completion(&ldisc_data->completion))
313 return 0xff;
314 } else if (wait_for_completion_timeout(&ldisc_data->completion,
315 usecs_to_jiffies(timeout)) == 0) {
316 pr_warn("spk_ttyio: timeout (%d) while waiting for input\n",
317 timeout);
318 return 0xff;
319 }
320
321 rv = ldisc_data->buf;
322 /* Make sure we have read buf before we set buf_free to let
323 * the producer overwrite it
324 */
325 mb();
326 ldisc_data->buf_free = true;
327 /* Let TTY push more characters */
328 tty_schedule_flip(speakup_tty->port);
329
330 return rv;
331}
332
333static unsigned char spk_ttyio_in(void)
334{
335 return ttyio_in(SPK_SYNTH_TIMEOUT);
336}
337
338static unsigned char spk_ttyio_in_nowait(void)
339{
340 u8 rv = ttyio_in(0);
341
342 return (rv == 0xff) ? 0 : rv;
343}
344
345static void spk_ttyio_flush_buffer(void)
346{
347 mutex_lock(&speakup_tty_mutex);
348 if (check_tty(speakup_tty)) {
349 mutex_unlock(&speakup_tty_mutex);
350 return;
351 }
352
353 if (speakup_tty->ops->flush_buffer)
354 speakup_tty->ops->flush_buffer(speakup_tty);
355
356 mutex_unlock(&speakup_tty_mutex);
357}
358
359int spk_ttyio_synth_probe(struct spk_synth *synth)
360{
361 int rv = spk_ttyio_initialise_ldisc(synth);
362
363 if (rv)
364 return rv;
365
366 synth->alive = 1;
367 spk_ttyio_synth = synth;
368
369 return 0;
370}
371EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);
372
373void spk_ttyio_release(void)
374{
375 if (!speakup_tty)
376 return;
377
378 tty_lock(speakup_tty);
379
380 if (speakup_tty->ops->close)
381 speakup_tty->ops->close(speakup_tty, NULL);
382
383 tty_ldisc_flush(speakup_tty);
384 tty_unlock(speakup_tty);
385 tty_kclose(speakup_tty);
386}
387EXPORT_SYMBOL_GPL(spk_ttyio_release);
388
389const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff)
390{
391 u_char ch;
392
393 while ((ch = *buff)) {
394 if (ch == '\n')
395 ch = synth->procspeech;
396 if (tty_write_room(speakup_tty) < 1 ||
397 !synth->io_ops->synth_out(synth, ch))
398 return buff;
399 buff++;
400 }
401 return NULL;
402}
403EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);