1
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 semaphore sem;
14 bool buf_free;
15};
16
17static struct spk_synth *spk_ttyio_synth;
18static struct tty_struct *speakup_tty;
19
20
21
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
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 == NULL)
51 return -EOPNOTSUPP;
52 speakup_tty = tty;
53
54 ldisc_data = kmalloc(sizeof(struct spk_ldisc_data), GFP_KERNEL);
55 if (!ldisc_data)
56 return -ENOMEM;
57
58 sema_init(&ldisc_data->sem, 0);
59 ldisc_data->buf_free = true;
60 speakup_tty->disc_data = ldisc_data;
61
62 return 0;
63}
64
65static void spk_ttyio_ldisc_close(struct tty_struct *tty)
66{
67 mutex_lock(&speakup_tty_mutex);
68 kfree(speakup_tty->disc_data);
69 speakup_tty = NULL;
70 mutex_unlock(&speakup_tty_mutex);
71}
72
73static int spk_ttyio_receive_buf2(struct tty_struct *tty,
74 const unsigned char *cp, char *fp, int count)
75{
76 struct spk_ldisc_data *ldisc_data = tty->disc_data;
77
78 if (spk_ttyio_synth->read_buff_add) {
79 int i;
80
81 for (i = 0; i < count; i++)
82 spk_ttyio_synth->read_buff_add(cp[i]);
83
84 return count;
85 }
86
87 if (!ldisc_data->buf_free)
88
89 return 0;
90
91
92
93
94 mb();
95
96 ldisc_data->buf = cp[0];
97 ldisc_data->buf_free = false;
98 up(&ldisc_data->sem);
99
100 return 1;
101}
102
103static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
104 .owner = THIS_MODULE,
105 .magic = TTY_LDISC_MAGIC,
106 .name = "speakup_ldisc",
107 .open = spk_ttyio_ldisc_open,
108 .close = spk_ttyio_ldisc_close,
109 .receive_buf2 = spk_ttyio_receive_buf2,
110};
111
112static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
113static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);
114static void spk_ttyio_send_xchar(char ch);
115static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear);
116static unsigned char spk_ttyio_in(void);
117static unsigned char spk_ttyio_in_nowait(void);
118static void spk_ttyio_flush_buffer(void);
119
120struct spk_io_ops spk_ttyio_ops = {
121 .synth_out = spk_ttyio_out,
122 .synth_out_unicode = spk_ttyio_out_unicode,
123 .send_xchar = spk_ttyio_send_xchar,
124 .tiocmset = spk_ttyio_tiocmset,
125 .synth_in = spk_ttyio_in,
126 .synth_in_nowait = spk_ttyio_in_nowait,
127 .flush_buffer = spk_ttyio_flush_buffer,
128};
129EXPORT_SYMBOL_GPL(spk_ttyio_ops);
130
131static inline void get_termios(struct tty_struct *tty, struct ktermios *out_termios)
132{
133 down_read(&tty->termios_rwsem);
134 *out_termios = tty->termios;
135 up_read(&tty->termios_rwsem);
136}
137
138static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
139{
140 int ret = 0;
141 struct tty_struct *tty;
142 struct ktermios tmp_termios;
143 dev_t dev;
144
145 ret = get_dev_to_use(synth, &dev);
146 if (ret)
147 return ret;
148
149 tty = tty_kopen(dev);
150 if (IS_ERR(tty))
151 return PTR_ERR(tty);
152
153 if (tty->ops->open)
154 ret = tty->ops->open(tty, NULL);
155 else
156 ret = -ENODEV;
157
158 if (ret) {
159 tty_unlock(tty);
160 return ret;
161 }
162
163 clear_bit(TTY_HUPPED, &tty->flags);
164
165 get_termios(tty, &tmp_termios);
166 if (!(tmp_termios.c_cflag & CRTSCTS)) {
167 tmp_termios.c_cflag |= CRTSCTS;
168 tty_set_termios(tty, &tmp_termios);
169
170
171
172
173 get_termios(tty, &tmp_termios);
174 if (!(tmp_termios.c_cflag & CRTSCTS))
175 pr_warn("speakup: Failed to set hardware flow control\n");
176 }
177
178 tty_unlock(tty);
179
180 ret = tty_set_ldisc(tty, N_SPEAKUP);
181 if (ret)
182 pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
183
184 return ret;
185}
186
187void spk_ttyio_register_ldisc(void)
188{
189 if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops))
190 pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");
191}
192
193void spk_ttyio_unregister_ldisc(void)
194{
195 if (tty_unregister_ldisc(N_SPEAKUP))
196 pr_warn("speakup: Couldn't unregister ldisc\n");
197}
198
199static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
200{
201 mutex_lock(&speakup_tty_mutex);
202 if (in_synth->alive && speakup_tty && speakup_tty->ops->write) {
203 int ret = speakup_tty->ops->write(speakup_tty, &ch, 1);
204
205 mutex_unlock(&speakup_tty_mutex);
206 if (ret == 0)
207
208 return 0;
209 if (ret < 0) {
210 pr_warn("%s: I/O error, deactivating speakup\n", in_synth->long_name);
211
212
213
214
215 in_synth->alive = 0;
216 speakup_start_ttys();
217 return 0;
218 }
219 return 1;
220 }
221
222 mutex_unlock(&speakup_tty_mutex);
223 return 0;
224}
225
226static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)
227{
228 int ret;
229
230 if (ch < 0x80)
231 ret = spk_ttyio_out(in_synth, ch);
232 else if (ch < 0x800) {
233 ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));
234 ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
235 } else {
236 ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));
237 ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));
238 ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
239 }
240 return ret;
241}
242
243static int check_tty(struct tty_struct *tty)
244{
245 if (!tty) {
246 pr_warn("%s: I/O error, deactivating speakup\n",
247 spk_ttyio_synth->long_name);
248
249
250
251
252 spk_ttyio_synth->alive = 0;
253 speakup_start_ttys();
254 return 1;
255 }
256
257 return 0;
258}
259
260static void spk_ttyio_send_xchar(char ch)
261{
262 mutex_lock(&speakup_tty_mutex);
263 if (check_tty(speakup_tty)) {
264 mutex_unlock(&speakup_tty_mutex);
265 return;
266 }
267
268 speakup_tty->ops->send_xchar(speakup_tty, ch);
269 mutex_unlock(&speakup_tty_mutex);
270}
271
272static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear)
273{
274 mutex_lock(&speakup_tty_mutex);
275 if (check_tty(speakup_tty)) {
276 mutex_unlock(&speakup_tty_mutex);
277 return;
278 }
279
280 speakup_tty->ops->tiocmset(speakup_tty, set, clear);
281 mutex_unlock(&speakup_tty_mutex);
282}
283
284static unsigned char ttyio_in(int timeout)
285{
286 struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data;
287 char rv;
288
289 if (down_timeout(&ldisc_data->sem, usecs_to_jiffies(timeout)) == -ETIME) {
290 if (timeout)
291 pr_warn("spk_ttyio: timeout (%d) while waiting for input\n",
292 timeout);
293 return 0xff;
294 }
295
296 rv = ldisc_data->buf;
297
298
299
300 mb();
301 ldisc_data->buf_free = true;
302
303 tty_schedule_flip(speakup_tty->port);
304
305 return rv;
306}
307
308static unsigned char spk_ttyio_in(void)
309{
310 return ttyio_in(SPK_SYNTH_TIMEOUT);
311}
312
313static unsigned char spk_ttyio_in_nowait(void)
314{
315 u8 rv = ttyio_in(0);
316
317 return (rv == 0xff) ? 0 : rv;
318}
319
320static void spk_ttyio_flush_buffer(void)
321{
322 mutex_lock(&speakup_tty_mutex);
323 if (check_tty(speakup_tty)) {
324 mutex_unlock(&speakup_tty_mutex);
325 return;
326 }
327
328 if (speakup_tty->ops->flush_buffer)
329 speakup_tty->ops->flush_buffer(speakup_tty);
330
331 mutex_unlock(&speakup_tty_mutex);
332}
333
334int spk_ttyio_synth_probe(struct spk_synth *synth)
335{
336 int rv = spk_ttyio_initialise_ldisc(synth);
337
338 if (rv)
339 return rv;
340
341 synth->alive = 1;
342 spk_ttyio_synth = synth;
343
344 return 0;
345}
346EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);
347
348void spk_ttyio_release(void)
349{
350 if (!speakup_tty)
351 return;
352
353 tty_lock(speakup_tty);
354
355 if (speakup_tty->ops->close)
356 speakup_tty->ops->close(speakup_tty, NULL);
357
358 tty_ldisc_flush(speakup_tty);
359 tty_unlock(speakup_tty);
360 tty_kclose(speakup_tty);
361}
362EXPORT_SYMBOL_GPL(spk_ttyio_release);
363
364const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff)
365{
366 u_char ch;
367
368 while ((ch = *buff)) {
369 if (ch == '\n')
370 ch = synth->procspeech;
371 if (tty_write_room(speakup_tty) < 1 || !synth->io_ops->synth_out(synth, ch))
372 return buff;
373 buff++;
374 }
375 return NULL;
376}
377EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);
378