18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/types.h>
38c2ecf20Sopenharmony_ci#include <linux/tty.h>
48c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
58c2ecf20Sopenharmony_ci#include <linux/slab.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "speakup.h"
88c2ecf20Sopenharmony_ci#include "spk_types.h"
98c2ecf20Sopenharmony_ci#include "spk_priv.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistruct spk_ldisc_data {
128c2ecf20Sopenharmony_ci	char buf;
138c2ecf20Sopenharmony_ci	struct completion completion;
148c2ecf20Sopenharmony_ci	bool buf_free;
158c2ecf20Sopenharmony_ci};
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic struct spk_synth *spk_ttyio_synth;
188c2ecf20Sopenharmony_cistatic struct tty_struct *speakup_tty;
198c2ecf20Sopenharmony_ci/* mutex to protect against speakup_tty disappearing from underneath us while
208c2ecf20Sopenharmony_ci * we are using it. this can happen when the device physically unplugged,
218c2ecf20Sopenharmony_ci * while in use. it also serialises access to speakup_tty.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(speakup_tty_mutex);
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int ser_to_dev(int ser, dev_t *dev_no)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	if (ser < 0 || ser > (255 - 64)) {
288c2ecf20Sopenharmony_ci		pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n");
298c2ecf20Sopenharmony_ci		return -EINVAL;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	*dev_no = MKDEV(4, (64 + ser));
338c2ecf20Sopenharmony_ci	return 0;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	/* use ser only when dev is not specified */
398c2ecf20Sopenharmony_ci	if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) ||
408c2ecf20Sopenharmony_ci	    synth->ser == SYNTH_DEFAULT_SER)
418c2ecf20Sopenharmony_ci		return tty_dev_name_to_number(synth->dev_name, dev_no);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return ser_to_dev(synth->ser, dev_no);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int spk_ttyio_ldisc_open(struct tty_struct *tty)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct spk_ldisc_data *ldisc_data;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (tty != speakup_tty)
518c2ecf20Sopenharmony_ci		/* Somebody tried to use this line discipline outside speakup */
528c2ecf20Sopenharmony_ci		return -ENODEV;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!tty->ops->write)
558c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);
588c2ecf20Sopenharmony_ci	if (!ldisc_data)
598c2ecf20Sopenharmony_ci		return -ENOMEM;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	init_completion(&ldisc_data->completion);
628c2ecf20Sopenharmony_ci	ldisc_data->buf_free = true;
638c2ecf20Sopenharmony_ci	tty->disc_data = ldisc_data;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void spk_ttyio_ldisc_close(struct tty_struct *tty)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
718c2ecf20Sopenharmony_ci	kfree(speakup_tty->disc_data);
728c2ecf20Sopenharmony_ci	speakup_tty = NULL;
738c2ecf20Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int spk_ttyio_receive_buf2(struct tty_struct *tty,
778c2ecf20Sopenharmony_ci				  const unsigned char *cp, char *fp, int count)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct spk_ldisc_data *ldisc_data = tty->disc_data;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (spk_ttyio_synth->read_buff_add) {
828c2ecf20Sopenharmony_ci		int i;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++)
858c2ecf20Sopenharmony_ci			spk_ttyio_synth->read_buff_add(cp[i]);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		return count;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (!ldisc_data->buf_free)
918c2ecf20Sopenharmony_ci		/* ttyio_in will tty_flip_buffer_push */
928c2ecf20Sopenharmony_ci		return 0;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Make sure the consumer has read buf before we have seen
958c2ecf20Sopenharmony_ci	 * buf_free == true and overwrite buf
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	mb();
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	ldisc_data->buf = cp[0];
1008c2ecf20Sopenharmony_ci	ldisc_data->buf_free = false;
1018c2ecf20Sopenharmony_ci	complete(&ldisc_data->completion);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 1;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
1078c2ecf20Sopenharmony_ci	.owner          = THIS_MODULE,
1088c2ecf20Sopenharmony_ci	.magic          = TTY_LDISC_MAGIC,
1098c2ecf20Sopenharmony_ci	.name           = "speakup_ldisc",
1108c2ecf20Sopenharmony_ci	.open           = spk_ttyio_ldisc_open,
1118c2ecf20Sopenharmony_ci	.close          = spk_ttyio_ldisc_close,
1128c2ecf20Sopenharmony_ci	.receive_buf2	= spk_ttyio_receive_buf2,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
1168c2ecf20Sopenharmony_cistatic int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);
1178c2ecf20Sopenharmony_cistatic void spk_ttyio_send_xchar(char ch);
1188c2ecf20Sopenharmony_cistatic void spk_ttyio_tiocmset(unsigned int set, unsigned int clear);
1198c2ecf20Sopenharmony_cistatic unsigned char spk_ttyio_in(void);
1208c2ecf20Sopenharmony_cistatic unsigned char spk_ttyio_in_nowait(void);
1218c2ecf20Sopenharmony_cistatic void spk_ttyio_flush_buffer(void);
1228c2ecf20Sopenharmony_cistatic int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistruct spk_io_ops spk_ttyio_ops = {
1258c2ecf20Sopenharmony_ci	.synth_out = spk_ttyio_out,
1268c2ecf20Sopenharmony_ci	.synth_out_unicode = spk_ttyio_out_unicode,
1278c2ecf20Sopenharmony_ci	.send_xchar = spk_ttyio_send_xchar,
1288c2ecf20Sopenharmony_ci	.tiocmset = spk_ttyio_tiocmset,
1298c2ecf20Sopenharmony_ci	.synth_in = spk_ttyio_in,
1308c2ecf20Sopenharmony_ci	.synth_in_nowait = spk_ttyio_in_nowait,
1318c2ecf20Sopenharmony_ci	.flush_buffer = spk_ttyio_flush_buffer,
1328c2ecf20Sopenharmony_ci	.wait_for_xmitr = spk_ttyio_wait_for_xmitr,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_ops);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic inline void get_termios(struct tty_struct *tty,
1378c2ecf20Sopenharmony_ci			       struct ktermios *out_termios)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
1408c2ecf20Sopenharmony_ci	*out_termios = tty->termios;
1418c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int ret = 0;
1478c2ecf20Sopenharmony_ci	struct tty_struct *tty;
1488c2ecf20Sopenharmony_ci	struct ktermios tmp_termios;
1498c2ecf20Sopenharmony_ci	dev_t dev;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ret = get_dev_to_use(synth, &dev);
1528c2ecf20Sopenharmony_ci	if (ret)
1538c2ecf20Sopenharmony_ci		return ret;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	tty = tty_kopen(dev);
1568c2ecf20Sopenharmony_ci	if (IS_ERR(tty))
1578c2ecf20Sopenharmony_ci		return PTR_ERR(tty);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (tty->ops->open)
1608c2ecf20Sopenharmony_ci		ret = tty->ops->open(tty, NULL);
1618c2ecf20Sopenharmony_ci	else
1628c2ecf20Sopenharmony_ci		ret = -ENODEV;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (ret) {
1658c2ecf20Sopenharmony_ci		tty_unlock(tty);
1668c2ecf20Sopenharmony_ci		return ret;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	clear_bit(TTY_HUPPED, &tty->flags);
1708c2ecf20Sopenharmony_ci	/* ensure hardware flow control is enabled */
1718c2ecf20Sopenharmony_ci	get_termios(tty, &tmp_termios);
1728c2ecf20Sopenharmony_ci	if (!(tmp_termios.c_cflag & CRTSCTS)) {
1738c2ecf20Sopenharmony_ci		tmp_termios.c_cflag |= CRTSCTS;
1748c2ecf20Sopenharmony_ci		tty_set_termios(tty, &tmp_termios);
1758c2ecf20Sopenharmony_ci		/*
1768c2ecf20Sopenharmony_ci		 * check c_cflag to see if it's updated as tty_set_termios
1778c2ecf20Sopenharmony_ci		 * may not return error even when no tty bits are
1788c2ecf20Sopenharmony_ci		 * changed by the request.
1798c2ecf20Sopenharmony_ci		 */
1808c2ecf20Sopenharmony_ci		get_termios(tty, &tmp_termios);
1818c2ecf20Sopenharmony_ci		if (!(tmp_termios.c_cflag & CRTSCTS))
1828c2ecf20Sopenharmony_ci			pr_warn("speakup: Failed to set hardware flow control\n");
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	tty_unlock(tty);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
1888c2ecf20Sopenharmony_ci	speakup_tty = tty;
1898c2ecf20Sopenharmony_ci	ret = tty_set_ldisc(tty, N_SPEAKUP);
1908c2ecf20Sopenharmony_ci	if (ret)
1918c2ecf20Sopenharmony_ci		speakup_tty = NULL;
1928c2ecf20Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (!ret)
1958c2ecf20Sopenharmony_ci		/* Success */
1968c2ecf20Sopenharmony_ci		return 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	tty_lock(tty);
2018c2ecf20Sopenharmony_ci	if (tty->ops->close)
2028c2ecf20Sopenharmony_ci		tty->ops->close(tty, NULL);
2038c2ecf20Sopenharmony_ci	tty_unlock(tty);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	tty_kclose(tty);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return ret;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_civoid spk_ttyio_register_ldisc(void)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops))
2138c2ecf20Sopenharmony_ci		pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_civoid spk_ttyio_unregister_ldisc(void)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	if (tty_unregister_ldisc(N_SPEAKUP))
2198c2ecf20Sopenharmony_ci		pr_warn("speakup: Couldn't unregister ldisc\n");
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
2258c2ecf20Sopenharmony_ci	if (in_synth->alive && speakup_tty && speakup_tty->ops->write) {
2268c2ecf20Sopenharmony_ci		int ret = speakup_tty->ops->write(speakup_tty, &ch, 1);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		mutex_unlock(&speakup_tty_mutex);
2298c2ecf20Sopenharmony_ci		if (ret == 0)
2308c2ecf20Sopenharmony_ci			/* No room */
2318c2ecf20Sopenharmony_ci			return 0;
2328c2ecf20Sopenharmony_ci		if (ret < 0) {
2338c2ecf20Sopenharmony_ci			pr_warn("%s: I/O error, deactivating speakup\n",
2348c2ecf20Sopenharmony_ci				in_synth->long_name);
2358c2ecf20Sopenharmony_ci			/* No synth any more, so nobody will restart TTYs,
2368c2ecf20Sopenharmony_ci			 * and we thus need to do it ourselves.  Now that there
2378c2ecf20Sopenharmony_ci			 * is no synth we can let application flood anyway
2388c2ecf20Sopenharmony_ci			 */
2398c2ecf20Sopenharmony_ci			in_synth->alive = 0;
2408c2ecf20Sopenharmony_ci			speakup_start_ttys();
2418c2ecf20Sopenharmony_ci			return 0;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci		return 1;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	int ret;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (ch < 0x80) {
2558c2ecf20Sopenharmony_ci		ret = spk_ttyio_out(in_synth, ch);
2568c2ecf20Sopenharmony_ci	} else if (ch < 0x800) {
2578c2ecf20Sopenharmony_ci		ret  = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));
2588c2ecf20Sopenharmony_ci		ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
2598c2ecf20Sopenharmony_ci	} else {
2608c2ecf20Sopenharmony_ci		ret  = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));
2618c2ecf20Sopenharmony_ci		ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));
2628c2ecf20Sopenharmony_ci		ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci	return ret;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int check_tty(struct tty_struct *tty)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	if (!tty) {
2708c2ecf20Sopenharmony_ci		pr_warn("%s: I/O error, deactivating speakup\n",
2718c2ecf20Sopenharmony_ci			spk_ttyio_synth->long_name);
2728c2ecf20Sopenharmony_ci		/* No synth any more, so nobody will restart TTYs, and we thus
2738c2ecf20Sopenharmony_ci		 * need to do it ourselves.  Now that there is no synth we can
2748c2ecf20Sopenharmony_ci		 * let application flood anyway
2758c2ecf20Sopenharmony_ci		 */
2768c2ecf20Sopenharmony_ci		spk_ttyio_synth->alive = 0;
2778c2ecf20Sopenharmony_ci		speakup_start_ttys();
2788c2ecf20Sopenharmony_ci		return 1;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic void spk_ttyio_send_xchar(char ch)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
2878c2ecf20Sopenharmony_ci	if (check_tty(speakup_tty)) {
2888c2ecf20Sopenharmony_ci		mutex_unlock(&speakup_tty_mutex);
2898c2ecf20Sopenharmony_ci		return;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (speakup_tty->ops->send_xchar)
2938c2ecf20Sopenharmony_ci		speakup_tty->ops->send_xchar(speakup_tty, ch);
2948c2ecf20Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic void spk_ttyio_tiocmset(unsigned int set, unsigned int clear)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
3008c2ecf20Sopenharmony_ci	if (check_tty(speakup_tty)) {
3018c2ecf20Sopenharmony_ci		mutex_unlock(&speakup_tty_mutex);
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (speakup_tty->ops->tiocmset)
3068c2ecf20Sopenharmony_ci		speakup_tty->ops->tiocmset(speakup_tty, set, clear);
3078c2ecf20Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	return 1;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic unsigned char ttyio_in(int timeout)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data;
3188c2ecf20Sopenharmony_ci	char rv;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (!timeout) {
3218c2ecf20Sopenharmony_ci		if (!try_wait_for_completion(&ldisc_data->completion))
3228c2ecf20Sopenharmony_ci			return 0xff;
3238c2ecf20Sopenharmony_ci	} else if (wait_for_completion_timeout(&ldisc_data->completion,
3248c2ecf20Sopenharmony_ci					usecs_to_jiffies(timeout)) == 0) {
3258c2ecf20Sopenharmony_ci		pr_warn("spk_ttyio: timeout (%d)  while waiting for input\n",
3268c2ecf20Sopenharmony_ci			timeout);
3278c2ecf20Sopenharmony_ci		return 0xff;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	rv = ldisc_data->buf;
3318c2ecf20Sopenharmony_ci	/* Make sure we have read buf before we set buf_free to let
3328c2ecf20Sopenharmony_ci	 * the producer overwrite it
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	mb();
3358c2ecf20Sopenharmony_ci	ldisc_data->buf_free = true;
3368c2ecf20Sopenharmony_ci	/* Let TTY push more characters */
3378c2ecf20Sopenharmony_ci	tty_flip_buffer_push(speakup_tty->port);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return rv;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic unsigned char spk_ttyio_in(void)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	return ttyio_in(SPK_SYNTH_TIMEOUT);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic unsigned char spk_ttyio_in_nowait(void)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	u8 rv = ttyio_in(0);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return (rv == 0xff) ? 0 : rv;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic void spk_ttyio_flush_buffer(void)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
3578c2ecf20Sopenharmony_ci	if (check_tty(speakup_tty)) {
3588c2ecf20Sopenharmony_ci		mutex_unlock(&speakup_tty_mutex);
3598c2ecf20Sopenharmony_ci		return;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (speakup_tty->ops->flush_buffer)
3638c2ecf20Sopenharmony_ci		speakup_tty->ops->flush_buffer(speakup_tty);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ciint spk_ttyio_synth_probe(struct spk_synth *synth)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	int rv = spk_ttyio_initialise_ldisc(synth);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (rv)
3738c2ecf20Sopenharmony_ci		return rv;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	synth->alive = 1;
3768c2ecf20Sopenharmony_ci	spk_ttyio_synth = synth;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_civoid spk_ttyio_release(void)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	if (!speakup_tty)
3858c2ecf20Sopenharmony_ci		return;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	tty_lock(speakup_tty);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (speakup_tty->ops->close)
3908c2ecf20Sopenharmony_ci		speakup_tty->ops->close(speakup_tty, NULL);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	tty_ldisc_flush(speakup_tty);
3938c2ecf20Sopenharmony_ci	tty_unlock(speakup_tty);
3948c2ecf20Sopenharmony_ci	tty_kclose(speakup_tty);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_release);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ciconst char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	u_char ch;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	while ((ch = *buff)) {
4038c2ecf20Sopenharmony_ci		if (ch == '\n')
4048c2ecf20Sopenharmony_ci			ch = synth->procspeech;
4058c2ecf20Sopenharmony_ci		if (tty_write_room(speakup_tty) < 1 ||
4068c2ecf20Sopenharmony_ci		    !synth->io_ops->synth_out(synth, ch))
4078c2ecf20Sopenharmony_ci			return buff;
4088c2ecf20Sopenharmony_ci		buff++;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	return NULL;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);
413