162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/types.h>
362306a36Sopenharmony_ci#include <linux/tty.h>
462306a36Sopenharmony_ci#include <linux/tty_flip.h>
562306a36Sopenharmony_ci#include <linux/slab.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "speakup.h"
862306a36Sopenharmony_ci#include "spk_types.h"
962306a36Sopenharmony_ci#include "spk_priv.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct spk_ldisc_data {
1262306a36Sopenharmony_ci	char buf;
1362306a36Sopenharmony_ci	struct completion completion;
1462306a36Sopenharmony_ci	bool buf_free;
1562306a36Sopenharmony_ci	struct spk_synth *synth;
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * This allows to catch within spk_ttyio_ldisc_open whether it is getting set
2062306a36Sopenharmony_ci * on for a speakup-driven device.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic struct tty_struct *speakup_tty;
2362306a36Sopenharmony_ci/* This mutex serializes the use of such global speakup_tty variable */
2462306a36Sopenharmony_cistatic DEFINE_MUTEX(speakup_tty_mutex);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int ser_to_dev(int ser, dev_t *dev_no)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	if (ser < 0 || ser > (255 - 64)) {
2962306a36Sopenharmony_ci		pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n");
3062306a36Sopenharmony_ci		return -EINVAL;
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	*dev_no = MKDEV(4, (64 + ser));
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	/* use ser only when dev is not specified */
4062306a36Sopenharmony_ci	if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) ||
4162306a36Sopenharmony_ci	    synth->ser == SYNTH_DEFAULT_SER)
4262306a36Sopenharmony_ci		return tty_dev_name_to_number(synth->dev_name, dev_no);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return ser_to_dev(synth->ser, dev_no);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int spk_ttyio_ldisc_open(struct tty_struct *tty)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct spk_ldisc_data *ldisc_data;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (tty != speakup_tty)
5262306a36Sopenharmony_ci		/* Somebody tried to use this line discipline outside speakup */
5362306a36Sopenharmony_ci		return -ENODEV;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (!tty->ops->write)
5662306a36Sopenharmony_ci		return -EOPNOTSUPP;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);
5962306a36Sopenharmony_ci	if (!ldisc_data)
6062306a36Sopenharmony_ci		return -ENOMEM;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	init_completion(&ldisc_data->completion);
6362306a36Sopenharmony_ci	ldisc_data->buf_free = true;
6462306a36Sopenharmony_ci	tty->disc_data = ldisc_data;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void spk_ttyio_ldisc_close(struct tty_struct *tty)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	kfree(tty->disc_data);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic size_t spk_ttyio_receive_buf2(struct tty_struct *tty, const u8 *cp,
7562306a36Sopenharmony_ci				     const u8 *fp, size_t count)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct spk_ldisc_data *ldisc_data = tty->disc_data;
7862306a36Sopenharmony_ci	struct spk_synth *synth = ldisc_data->synth;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (synth->read_buff_add) {
8162306a36Sopenharmony_ci		unsigned int i;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		for (i = 0; i < count; i++)
8462306a36Sopenharmony_ci			synth->read_buff_add(cp[i]);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		return count;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!ldisc_data->buf_free)
9062306a36Sopenharmony_ci		/* ttyio_in will tty_flip_buffer_push */
9162306a36Sopenharmony_ci		return 0;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Make sure the consumer has read buf before we have seen
9462306a36Sopenharmony_ci	 * buf_free == true and overwrite buf
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	mb();
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	ldisc_data->buf = cp[0];
9962306a36Sopenharmony_ci	ldisc_data->buf_free = false;
10062306a36Sopenharmony_ci	complete(&ldisc_data->completion);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 1;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
10662306a36Sopenharmony_ci	.owner          = THIS_MODULE,
10762306a36Sopenharmony_ci	.num		= N_SPEAKUP,
10862306a36Sopenharmony_ci	.name           = "speakup_ldisc",
10962306a36Sopenharmony_ci	.open           = spk_ttyio_ldisc_open,
11062306a36Sopenharmony_ci	.close          = spk_ttyio_ldisc_close,
11162306a36Sopenharmony_ci	.receive_buf2	= spk_ttyio_receive_buf2,
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
11562306a36Sopenharmony_cistatic int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);
11662306a36Sopenharmony_cistatic void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch);
11762306a36Sopenharmony_cistatic void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear);
11862306a36Sopenharmony_cistatic unsigned char spk_ttyio_in(struct spk_synth *in_synth);
11962306a36Sopenharmony_cistatic unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth);
12062306a36Sopenharmony_cistatic void spk_ttyio_flush_buffer(struct spk_synth *in_synth);
12162306a36Sopenharmony_cistatic int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistruct spk_io_ops spk_ttyio_ops = {
12462306a36Sopenharmony_ci	.synth_out = spk_ttyio_out,
12562306a36Sopenharmony_ci	.synth_out_unicode = spk_ttyio_out_unicode,
12662306a36Sopenharmony_ci	.send_xchar = spk_ttyio_send_xchar,
12762306a36Sopenharmony_ci	.tiocmset = spk_ttyio_tiocmset,
12862306a36Sopenharmony_ci	.synth_in = spk_ttyio_in,
12962306a36Sopenharmony_ci	.synth_in_nowait = spk_ttyio_in_nowait,
13062306a36Sopenharmony_ci	.flush_buffer = spk_ttyio_flush_buffer,
13162306a36Sopenharmony_ci	.wait_for_xmitr = spk_ttyio_wait_for_xmitr,
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_ops);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic inline void get_termios(struct tty_struct *tty,
13662306a36Sopenharmony_ci			       struct ktermios *out_termios)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	down_read(&tty->termios_rwsem);
13962306a36Sopenharmony_ci	*out_termios = tty->termios;
14062306a36Sopenharmony_ci	up_read(&tty->termios_rwsem);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	int ret = 0;
14662306a36Sopenharmony_ci	struct tty_struct *tty;
14762306a36Sopenharmony_ci	struct ktermios tmp_termios;
14862306a36Sopenharmony_ci	dev_t dev;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = get_dev_to_use(synth, &dev);
15162306a36Sopenharmony_ci	if (ret)
15262306a36Sopenharmony_ci		return ret;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	tty = tty_kopen_exclusive(dev);
15562306a36Sopenharmony_ci	if (IS_ERR(tty))
15662306a36Sopenharmony_ci		return PTR_ERR(tty);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (tty->ops->open)
15962306a36Sopenharmony_ci		ret = tty->ops->open(tty, NULL);
16062306a36Sopenharmony_ci	else
16162306a36Sopenharmony_ci		ret = -ENODEV;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (ret) {
16462306a36Sopenharmony_ci		tty_unlock(tty);
16562306a36Sopenharmony_ci		return ret;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	clear_bit(TTY_HUPPED, &tty->flags);
16962306a36Sopenharmony_ci	/* ensure hardware flow control is enabled */
17062306a36Sopenharmony_ci	get_termios(tty, &tmp_termios);
17162306a36Sopenharmony_ci	if (!(tmp_termios.c_cflag & CRTSCTS)) {
17262306a36Sopenharmony_ci		tmp_termios.c_cflag |= CRTSCTS;
17362306a36Sopenharmony_ci		tty_set_termios(tty, &tmp_termios);
17462306a36Sopenharmony_ci		/*
17562306a36Sopenharmony_ci		 * check c_cflag to see if it's updated as tty_set_termios
17662306a36Sopenharmony_ci		 * may not return error even when no tty bits are
17762306a36Sopenharmony_ci		 * changed by the request.
17862306a36Sopenharmony_ci		 */
17962306a36Sopenharmony_ci		get_termios(tty, &tmp_termios);
18062306a36Sopenharmony_ci		if (!(tmp_termios.c_cflag & CRTSCTS))
18162306a36Sopenharmony_ci			pr_warn("speakup: Failed to set hardware flow control\n");
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	tty_unlock(tty);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	mutex_lock(&speakup_tty_mutex);
18762306a36Sopenharmony_ci	speakup_tty = tty;
18862306a36Sopenharmony_ci	ret = tty_set_ldisc(tty, N_SPEAKUP);
18962306a36Sopenharmony_ci	speakup_tty = NULL;
19062306a36Sopenharmony_ci	mutex_unlock(&speakup_tty_mutex);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (!ret) {
19362306a36Sopenharmony_ci		/* Success */
19462306a36Sopenharmony_ci		struct spk_ldisc_data *ldisc_data = tty->disc_data;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		ldisc_data->synth = synth;
19762306a36Sopenharmony_ci		synth->dev = tty;
19862306a36Sopenharmony_ci		return 0;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	tty_lock(tty);
20462306a36Sopenharmony_ci	if (tty->ops->close)
20562306a36Sopenharmony_ci		tty->ops->close(tty, NULL);
20662306a36Sopenharmony_ci	tty_unlock(tty);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	tty_kclose(tty);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return ret;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_civoid spk_ttyio_register_ldisc(void)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (tty_register_ldisc(&spk_ttyio_ldisc_ops))
21662306a36Sopenharmony_ci		pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_civoid spk_ttyio_unregister_ldisc(void)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	tty_unregister_ldisc(&spk_ttyio_ldisc_ops);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
22762306a36Sopenharmony_ci	int ret;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (!in_synth->alive || !tty->ops->write)
23062306a36Sopenharmony_ci		return 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = tty->ops->write(tty, &ch, 1);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (ret == 0)
23562306a36Sopenharmony_ci		/* No room */
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (ret > 0)
23962306a36Sopenharmony_ci		/* Success */
24062306a36Sopenharmony_ci		return 1;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	pr_warn("%s: I/O error, deactivating speakup\n",
24362306a36Sopenharmony_ci		in_synth->long_name);
24462306a36Sopenharmony_ci	/* No synth any more, so nobody will restart TTYs,
24562306a36Sopenharmony_ci	 * and we thus need to do it ourselves.  Now that there
24662306a36Sopenharmony_ci	 * is no synth we can let application flood anyway
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	in_synth->alive = 0;
24962306a36Sopenharmony_ci	speakup_start_ttys();
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int ret;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (ch < 0x80) {
25862306a36Sopenharmony_ci		ret = spk_ttyio_out(in_synth, ch);
25962306a36Sopenharmony_ci	} else if (ch < 0x800) {
26062306a36Sopenharmony_ci		ret  = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));
26162306a36Sopenharmony_ci		ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
26262306a36Sopenharmony_ci	} else {
26362306a36Sopenharmony_ci		ret  = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));
26462306a36Sopenharmony_ci		ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));
26562306a36Sopenharmony_ci		ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci	return ret;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (tty->ops->send_xchar)
27562306a36Sopenharmony_ci		tty->ops->send_xchar(tty, ch);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (tty->ops->tiocmset)
28362306a36Sopenharmony_ci		tty->ops->tiocmset(tty, set, clear);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	return 1;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic unsigned char ttyio_in(struct spk_synth *in_synth, int timeout)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
29462306a36Sopenharmony_ci	struct spk_ldisc_data *ldisc_data = tty->disc_data;
29562306a36Sopenharmony_ci	char rv;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (!timeout) {
29862306a36Sopenharmony_ci		if (!try_wait_for_completion(&ldisc_data->completion))
29962306a36Sopenharmony_ci			return 0xff;
30062306a36Sopenharmony_ci	} else if (wait_for_completion_timeout(&ldisc_data->completion,
30162306a36Sopenharmony_ci					usecs_to_jiffies(timeout)) == 0) {
30262306a36Sopenharmony_ci		pr_warn("spk_ttyio: timeout (%d)  while waiting for input\n",
30362306a36Sopenharmony_ci			timeout);
30462306a36Sopenharmony_ci		return 0xff;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	rv = ldisc_data->buf;
30862306a36Sopenharmony_ci	/* Make sure we have read buf before we set buf_free to let
30962306a36Sopenharmony_ci	 * the producer overwrite it
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	mb();
31262306a36Sopenharmony_ci	ldisc_data->buf_free = true;
31362306a36Sopenharmony_ci	/* Let TTY push more characters */
31462306a36Sopenharmony_ci	tty_flip_buffer_push(tty->port);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return rv;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic unsigned char spk_ttyio_in(struct spk_synth *in_synth)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	return ttyio_in(in_synth, SPK_SYNTH_TIMEOUT);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	u8 rv = ttyio_in(in_synth, 0);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return (rv == 0xff) ? 0 : rv;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void spk_ttyio_flush_buffer(struct spk_synth *in_synth)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (tty->ops->flush_buffer)
33662306a36Sopenharmony_ci		tty->ops->flush_buffer(tty);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ciint spk_ttyio_synth_probe(struct spk_synth *synth)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int rv = spk_ttyio_initialise_ldisc(synth);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (rv)
34462306a36Sopenharmony_ci		return rv;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	synth->alive = 1;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_civoid spk_ttyio_release(struct spk_synth *in_synth)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (tty == NULL)
35762306a36Sopenharmony_ci		return;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	tty_lock(tty);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (tty->ops->close)
36262306a36Sopenharmony_ci		tty->ops->close(tty, NULL);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	tty_ldisc_flush(tty);
36562306a36Sopenharmony_ci	tty_unlock(tty);
36662306a36Sopenharmony_ci	tty_kclose(tty);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	in_synth->dev = NULL;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_release);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ciconst char *spk_ttyio_synth_immediate(struct spk_synth *in_synth, const char *buff)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct tty_struct *tty = in_synth->dev;
37562306a36Sopenharmony_ci	u_char ch;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	while ((ch = *buff)) {
37862306a36Sopenharmony_ci		if (ch == '\n')
37962306a36Sopenharmony_ci			ch = in_synth->procspeech;
38062306a36Sopenharmony_ci		if (tty_write_room(tty) < 1 ||
38162306a36Sopenharmony_ci		    !in_synth->io_ops->synth_out(in_synth, ch))
38262306a36Sopenharmony_ci			return buff;
38362306a36Sopenharmony_ci		buff++;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	return NULL;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);
388