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