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