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