12067fd92SSamuel Thibault // SPDX-License-Identifier: GPL-2.0 22067fd92SSamuel Thibault #include <linux/types.h> 32067fd92SSamuel Thibault #include <linux/tty.h> 42067fd92SSamuel Thibault #include <linux/tty_flip.h> 52067fd92SSamuel Thibault #include <linux/slab.h> 62067fd92SSamuel Thibault 72067fd92SSamuel Thibault #include "speakup.h" 82067fd92SSamuel Thibault #include "spk_types.h" 92067fd92SSamuel Thibault #include "spk_priv.h" 102067fd92SSamuel Thibault 112067fd92SSamuel Thibault struct spk_ldisc_data { 122067fd92SSamuel Thibault char buf; 132067fd92SSamuel Thibault struct completion completion; 142067fd92SSamuel Thibault bool buf_free; 152067fd92SSamuel Thibault }; 162067fd92SSamuel Thibault 172067fd92SSamuel Thibault static struct spk_synth *spk_ttyio_synth; 182067fd92SSamuel Thibault static struct tty_struct *speakup_tty; 192067fd92SSamuel Thibault /* mutex to protect against speakup_tty disappearing from underneath us while 202067fd92SSamuel Thibault * we are using it. this can happen when the device physically unplugged, 212067fd92SSamuel Thibault * while in use. it also serialises access to speakup_tty. 222067fd92SSamuel Thibault */ 232067fd92SSamuel Thibault static DEFINE_MUTEX(speakup_tty_mutex); 242067fd92SSamuel Thibault 252067fd92SSamuel Thibault static int ser_to_dev(int ser, dev_t *dev_no) 262067fd92SSamuel Thibault { 272067fd92SSamuel Thibault if (ser < 0 || ser > (255 - 64)) { 282067fd92SSamuel Thibault pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n"); 292067fd92SSamuel Thibault return -EINVAL; 302067fd92SSamuel Thibault } 312067fd92SSamuel Thibault 322067fd92SSamuel Thibault *dev_no = MKDEV(4, (64 + ser)); 332067fd92SSamuel Thibault return 0; 342067fd92SSamuel Thibault } 352067fd92SSamuel Thibault 362067fd92SSamuel Thibault static int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no) 372067fd92SSamuel Thibault { 382067fd92SSamuel Thibault /* use ser only when dev is not specified */ 392067fd92SSamuel Thibault if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) || 402067fd92SSamuel Thibault synth->ser == SYNTH_DEFAULT_SER) 412067fd92SSamuel Thibault return tty_dev_name_to_number(synth->dev_name, dev_no); 422067fd92SSamuel Thibault 432067fd92SSamuel Thibault return ser_to_dev(synth->ser, dev_no); 442067fd92SSamuel Thibault } 452067fd92SSamuel Thibault 462067fd92SSamuel Thibault static int spk_ttyio_ldisc_open(struct tty_struct *tty) 472067fd92SSamuel Thibault { 482067fd92SSamuel Thibault struct spk_ldisc_data *ldisc_data; 492067fd92SSamuel Thibault 50f0992098SSamuel Thibault if (tty != speakup_tty) 51f0992098SSamuel Thibault /* Somebody tried to use this line discipline outside speakup */ 52f0992098SSamuel Thibault return -ENODEV; 53f0992098SSamuel Thibault 542067fd92SSamuel Thibault if (!tty->ops->write) 552067fd92SSamuel Thibault return -EOPNOTSUPP; 56d4122754SSamuel Thibault 572067fd92SSamuel Thibault ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL); 58f0992098SSamuel Thibault if (!ldisc_data) 592067fd92SSamuel Thibault return -ENOMEM; 602067fd92SSamuel Thibault 612067fd92SSamuel Thibault init_completion(&ldisc_data->completion); 622067fd92SSamuel Thibault ldisc_data->buf_free = true; 63f0992098SSamuel Thibault tty->disc_data = ldisc_data; 642067fd92SSamuel Thibault 652067fd92SSamuel Thibault return 0; 662067fd92SSamuel Thibault } 672067fd92SSamuel Thibault 682067fd92SSamuel Thibault static void spk_ttyio_ldisc_close(struct tty_struct *tty) 692067fd92SSamuel Thibault { 702067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 712067fd92SSamuel Thibault kfree(speakup_tty->disc_data); 722067fd92SSamuel Thibault speakup_tty = NULL; 732067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 742067fd92SSamuel Thibault } 752067fd92SSamuel Thibault 762067fd92SSamuel Thibault static int spk_ttyio_receive_buf2(struct tty_struct *tty, 772067fd92SSamuel Thibault const unsigned char *cp, char *fp, int count) 782067fd92SSamuel Thibault { 792067fd92SSamuel Thibault struct spk_ldisc_data *ldisc_data = tty->disc_data; 802067fd92SSamuel Thibault 812067fd92SSamuel Thibault if (spk_ttyio_synth->read_buff_add) { 822067fd92SSamuel Thibault int i; 832067fd92SSamuel Thibault 842067fd92SSamuel Thibault for (i = 0; i < count; i++) 852067fd92SSamuel Thibault spk_ttyio_synth->read_buff_add(cp[i]); 862067fd92SSamuel Thibault 872067fd92SSamuel Thibault return count; 882067fd92SSamuel Thibault } 892067fd92SSamuel Thibault 902067fd92SSamuel Thibault if (!ldisc_data->buf_free) 912067fd92SSamuel Thibault /* ttyio_in will tty_schedule_flip */ 922067fd92SSamuel Thibault return 0; 932067fd92SSamuel Thibault 942067fd92SSamuel Thibault /* Make sure the consumer has read buf before we have seen 952067fd92SSamuel Thibault * buf_free == true and overwrite buf 962067fd92SSamuel Thibault */ 972067fd92SSamuel Thibault mb(); 982067fd92SSamuel Thibault 992067fd92SSamuel Thibault ldisc_data->buf = cp[0]; 1002067fd92SSamuel Thibault ldisc_data->buf_free = false; 1012067fd92SSamuel Thibault complete(&ldisc_data->completion); 1022067fd92SSamuel Thibault 1032067fd92SSamuel Thibault return 1; 1042067fd92SSamuel Thibault } 1052067fd92SSamuel Thibault 1062067fd92SSamuel Thibault static struct tty_ldisc_ops spk_ttyio_ldisc_ops = { 1072067fd92SSamuel Thibault .owner = THIS_MODULE, 1082067fd92SSamuel Thibault .magic = TTY_LDISC_MAGIC, 1092067fd92SSamuel Thibault .name = "speakup_ldisc", 1102067fd92SSamuel Thibault .open = spk_ttyio_ldisc_open, 1112067fd92SSamuel Thibault .close = spk_ttyio_ldisc_close, 1122067fd92SSamuel Thibault .receive_buf2 = spk_ttyio_receive_buf2, 1132067fd92SSamuel Thibault }; 1142067fd92SSamuel Thibault 1152067fd92SSamuel Thibault static int spk_ttyio_out(struct spk_synth *in_synth, const char ch); 1162067fd92SSamuel Thibault static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch); 117*1941ab1dSSamuel Thibault static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch); 118*1941ab1dSSamuel Thibault static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear); 119*1941ab1dSSamuel Thibault static unsigned char spk_ttyio_in(struct spk_synth *in_synth); 120*1941ab1dSSamuel Thibault static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth); 121*1941ab1dSSamuel Thibault static void spk_ttyio_flush_buffer(struct spk_synth *in_synth); 1222b86d9b8SSamuel Thibault static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth); 1232067fd92SSamuel Thibault 1242067fd92SSamuel Thibault struct spk_io_ops spk_ttyio_ops = { 1252067fd92SSamuel Thibault .synth_out = spk_ttyio_out, 1262067fd92SSamuel Thibault .synth_out_unicode = spk_ttyio_out_unicode, 1272067fd92SSamuel Thibault .send_xchar = spk_ttyio_send_xchar, 1282067fd92SSamuel Thibault .tiocmset = spk_ttyio_tiocmset, 1292067fd92SSamuel Thibault .synth_in = spk_ttyio_in, 1302067fd92SSamuel Thibault .synth_in_nowait = spk_ttyio_in_nowait, 1312067fd92SSamuel Thibault .flush_buffer = spk_ttyio_flush_buffer, 1322b86d9b8SSamuel Thibault .wait_for_xmitr = spk_ttyio_wait_for_xmitr, 1332067fd92SSamuel Thibault }; 1342067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_ops); 1352067fd92SSamuel Thibault 1362067fd92SSamuel Thibault static inline void get_termios(struct tty_struct *tty, 1372067fd92SSamuel Thibault struct ktermios *out_termios) 1382067fd92SSamuel Thibault { 1392067fd92SSamuel Thibault down_read(&tty->termios_rwsem); 1402067fd92SSamuel Thibault *out_termios = tty->termios; 1412067fd92SSamuel Thibault up_read(&tty->termios_rwsem); 1422067fd92SSamuel Thibault } 1432067fd92SSamuel Thibault 1442067fd92SSamuel Thibault static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) 1452067fd92SSamuel Thibault { 1462067fd92SSamuel Thibault int ret = 0; 1472067fd92SSamuel Thibault struct tty_struct *tty; 1482067fd92SSamuel Thibault struct ktermios tmp_termios; 1492067fd92SSamuel Thibault dev_t dev; 1502067fd92SSamuel Thibault 1512067fd92SSamuel Thibault ret = get_dev_to_use(synth, &dev); 1522067fd92SSamuel Thibault if (ret) 1532067fd92SSamuel Thibault return ret; 1542067fd92SSamuel Thibault 1552067fd92SSamuel Thibault tty = tty_kopen(dev); 1562067fd92SSamuel Thibault if (IS_ERR(tty)) 1572067fd92SSamuel Thibault return PTR_ERR(tty); 1582067fd92SSamuel Thibault 1592067fd92SSamuel Thibault if (tty->ops->open) 1602067fd92SSamuel Thibault ret = tty->ops->open(tty, NULL); 1612067fd92SSamuel Thibault else 1622067fd92SSamuel Thibault ret = -ENODEV; 1632067fd92SSamuel Thibault 1642067fd92SSamuel Thibault if (ret) { 1652067fd92SSamuel Thibault tty_unlock(tty); 1662067fd92SSamuel Thibault return ret; 1672067fd92SSamuel Thibault } 1682067fd92SSamuel Thibault 1692067fd92SSamuel Thibault clear_bit(TTY_HUPPED, &tty->flags); 1702067fd92SSamuel Thibault /* ensure hardware flow control is enabled */ 1712067fd92SSamuel Thibault get_termios(tty, &tmp_termios); 1722067fd92SSamuel Thibault if (!(tmp_termios.c_cflag & CRTSCTS)) { 1732067fd92SSamuel Thibault tmp_termios.c_cflag |= CRTSCTS; 1742067fd92SSamuel Thibault tty_set_termios(tty, &tmp_termios); 1752067fd92SSamuel Thibault /* 1762067fd92SSamuel Thibault * check c_cflag to see if it's updated as tty_set_termios 1772067fd92SSamuel Thibault * may not return error even when no tty bits are 1782067fd92SSamuel Thibault * changed by the request. 1792067fd92SSamuel Thibault */ 1802067fd92SSamuel Thibault get_termios(tty, &tmp_termios); 1812067fd92SSamuel Thibault if (!(tmp_termios.c_cflag & CRTSCTS)) 1822067fd92SSamuel Thibault pr_warn("speakup: Failed to set hardware flow control\n"); 1832067fd92SSamuel Thibault } 1842067fd92SSamuel Thibault 1852067fd92SSamuel Thibault tty_unlock(tty); 1862067fd92SSamuel Thibault 187f0992098SSamuel Thibault mutex_lock(&speakup_tty_mutex); 188f0992098SSamuel Thibault speakup_tty = tty; 1892067fd92SSamuel Thibault ret = tty_set_ldisc(tty, N_SPEAKUP); 1902067fd92SSamuel Thibault if (ret) 191f0992098SSamuel Thibault speakup_tty = NULL; 192f0992098SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 193f0992098SSamuel Thibault 194f0992098SSamuel Thibault if (!ret) 195f0992098SSamuel Thibault /* Success */ 196f0992098SSamuel Thibault return 0; 197f0992098SSamuel Thibault 1982067fd92SSamuel Thibault pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); 1992067fd92SSamuel Thibault 200f0992098SSamuel Thibault tty_lock(tty); 201f0992098SSamuel Thibault if (tty->ops->close) 202f0992098SSamuel Thibault tty->ops->close(tty, NULL); 203f0992098SSamuel Thibault tty_unlock(tty); 204f0992098SSamuel Thibault 205f0992098SSamuel Thibault tty_kclose(tty); 206f0992098SSamuel Thibault 2072067fd92SSamuel Thibault return ret; 2082067fd92SSamuel Thibault } 2092067fd92SSamuel Thibault 2102067fd92SSamuel Thibault void spk_ttyio_register_ldisc(void) 2112067fd92SSamuel Thibault { 2122067fd92SSamuel Thibault if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops)) 2132067fd92SSamuel Thibault pr_warn("speakup: Error registering line discipline. Most synths won't work.\n"); 2142067fd92SSamuel Thibault } 2152067fd92SSamuel Thibault 2162067fd92SSamuel Thibault void spk_ttyio_unregister_ldisc(void) 2172067fd92SSamuel Thibault { 2182067fd92SSamuel Thibault if (tty_unregister_ldisc(N_SPEAKUP)) 2192067fd92SSamuel Thibault pr_warn("speakup: Couldn't unregister ldisc\n"); 2202067fd92SSamuel Thibault } 2212067fd92SSamuel Thibault 2222067fd92SSamuel Thibault static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) 2232067fd92SSamuel Thibault { 2242067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 2252067fd92SSamuel Thibault if (in_synth->alive && speakup_tty && speakup_tty->ops->write) { 2262067fd92SSamuel Thibault int ret = speakup_tty->ops->write(speakup_tty, &ch, 1); 2272067fd92SSamuel Thibault 2282067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2292067fd92SSamuel Thibault if (ret == 0) 2302067fd92SSamuel Thibault /* No room */ 2312067fd92SSamuel Thibault return 0; 2322067fd92SSamuel Thibault if (ret < 0) { 2332067fd92SSamuel Thibault pr_warn("%s: I/O error, deactivating speakup\n", 2342067fd92SSamuel Thibault in_synth->long_name); 2352067fd92SSamuel Thibault /* No synth any more, so nobody will restart TTYs, 2362067fd92SSamuel Thibault * and we thus need to do it ourselves. Now that there 2372067fd92SSamuel Thibault * is no synth we can let application flood anyway 2382067fd92SSamuel Thibault */ 2392067fd92SSamuel Thibault in_synth->alive = 0; 2402067fd92SSamuel Thibault speakup_start_ttys(); 2412067fd92SSamuel Thibault return 0; 2422067fd92SSamuel Thibault } 2432067fd92SSamuel Thibault return 1; 2442067fd92SSamuel Thibault } 2452067fd92SSamuel Thibault 2462067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2472067fd92SSamuel Thibault return 0; 2482067fd92SSamuel Thibault } 2492067fd92SSamuel Thibault 2502067fd92SSamuel Thibault static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch) 2512067fd92SSamuel Thibault { 2522067fd92SSamuel Thibault int ret; 2532067fd92SSamuel Thibault 2542067fd92SSamuel Thibault if (ch < 0x80) { 2552067fd92SSamuel Thibault ret = spk_ttyio_out(in_synth, ch); 2562067fd92SSamuel Thibault } else if (ch < 0x800) { 2572067fd92SSamuel Thibault ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6)); 2582067fd92SSamuel Thibault ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); 2592067fd92SSamuel Thibault } else { 2602067fd92SSamuel Thibault ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12)); 2612067fd92SSamuel Thibault ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f)); 2622067fd92SSamuel Thibault ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); 2632067fd92SSamuel Thibault } 2642067fd92SSamuel Thibault return ret; 2652067fd92SSamuel Thibault } 2662067fd92SSamuel Thibault 2672067fd92SSamuel Thibault static int check_tty(struct tty_struct *tty) 2682067fd92SSamuel Thibault { 2692067fd92SSamuel Thibault if (!tty) { 2702067fd92SSamuel Thibault pr_warn("%s: I/O error, deactivating speakup\n", 2712067fd92SSamuel Thibault spk_ttyio_synth->long_name); 2722067fd92SSamuel Thibault /* No synth any more, so nobody will restart TTYs, and we thus 2732067fd92SSamuel Thibault * need to do it ourselves. Now that there is no synth we can 2742067fd92SSamuel Thibault * let application flood anyway 2752067fd92SSamuel Thibault */ 2762067fd92SSamuel Thibault spk_ttyio_synth->alive = 0; 2772067fd92SSamuel Thibault speakup_start_ttys(); 2782067fd92SSamuel Thibault return 1; 2792067fd92SSamuel Thibault } 2802067fd92SSamuel Thibault 2812067fd92SSamuel Thibault return 0; 2822067fd92SSamuel Thibault } 2832067fd92SSamuel Thibault 284*1941ab1dSSamuel Thibault static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch) 2852067fd92SSamuel Thibault { 2862067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 2872067fd92SSamuel Thibault if (check_tty(speakup_tty)) { 2882067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2892067fd92SSamuel Thibault return; 2902067fd92SSamuel Thibault } 2912067fd92SSamuel Thibault 2922067fd92SSamuel Thibault if (speakup_tty->ops->send_xchar) 2932067fd92SSamuel Thibault speakup_tty->ops->send_xchar(speakup_tty, ch); 2942067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2952067fd92SSamuel Thibault } 2962067fd92SSamuel Thibault 297*1941ab1dSSamuel Thibault static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear) 2982067fd92SSamuel Thibault { 2992067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 3002067fd92SSamuel Thibault if (check_tty(speakup_tty)) { 3012067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 3022067fd92SSamuel Thibault return; 3032067fd92SSamuel Thibault } 3042067fd92SSamuel Thibault 3052067fd92SSamuel Thibault if (speakup_tty->ops->tiocmset) 3062067fd92SSamuel Thibault speakup_tty->ops->tiocmset(speakup_tty, set, clear); 3072067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 3082067fd92SSamuel Thibault } 3092067fd92SSamuel Thibault 3102b86d9b8SSamuel Thibault static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth) 3112b86d9b8SSamuel Thibault { 3122b86d9b8SSamuel Thibault return 1; 3132b86d9b8SSamuel Thibault } 3142b86d9b8SSamuel Thibault 315*1941ab1dSSamuel Thibault static unsigned char ttyio_in(struct spk_synth *in_synth, int timeout) 3162067fd92SSamuel Thibault { 3172067fd92SSamuel Thibault struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data; 3182067fd92SSamuel Thibault char rv; 3192067fd92SSamuel Thibault 3203ed1cfb2SSamuel Thibault if (!timeout) { 3213ed1cfb2SSamuel Thibault if (!try_wait_for_completion(&ldisc_data->completion)) 3223ed1cfb2SSamuel Thibault return 0xff; 3233ed1cfb2SSamuel Thibault } else if (wait_for_completion_timeout(&ldisc_data->completion, 3242067fd92SSamuel Thibault usecs_to_jiffies(timeout)) == 0) { 3252067fd92SSamuel Thibault pr_warn("spk_ttyio: timeout (%d) while waiting for input\n", 3262067fd92SSamuel Thibault timeout); 3272067fd92SSamuel Thibault return 0xff; 3282067fd92SSamuel Thibault } 3292067fd92SSamuel Thibault 3302067fd92SSamuel Thibault rv = ldisc_data->buf; 3312067fd92SSamuel Thibault /* Make sure we have read buf before we set buf_free to let 3322067fd92SSamuel Thibault * the producer overwrite it 3332067fd92SSamuel Thibault */ 3342067fd92SSamuel Thibault mb(); 3352067fd92SSamuel Thibault ldisc_data->buf_free = true; 3362067fd92SSamuel Thibault /* Let TTY push more characters */ 3372067fd92SSamuel Thibault tty_schedule_flip(speakup_tty->port); 3382067fd92SSamuel Thibault 3392067fd92SSamuel Thibault return rv; 3402067fd92SSamuel Thibault } 3412067fd92SSamuel Thibault 342*1941ab1dSSamuel Thibault static unsigned char spk_ttyio_in(struct spk_synth *in_synth) 3432067fd92SSamuel Thibault { 344*1941ab1dSSamuel Thibault return ttyio_in(in_synth, SPK_SYNTH_TIMEOUT); 3452067fd92SSamuel Thibault } 3462067fd92SSamuel Thibault 347*1941ab1dSSamuel Thibault static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth) 3482067fd92SSamuel Thibault { 349*1941ab1dSSamuel Thibault u8 rv = ttyio_in(in_synth, 0); 3502067fd92SSamuel Thibault 3512067fd92SSamuel Thibault return (rv == 0xff) ? 0 : rv; 3522067fd92SSamuel Thibault } 3532067fd92SSamuel Thibault 354*1941ab1dSSamuel Thibault static void spk_ttyio_flush_buffer(struct spk_synth *in_synth) 3552067fd92SSamuel Thibault { 3562067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 3572067fd92SSamuel Thibault if (check_tty(speakup_tty)) { 3582067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 3592067fd92SSamuel Thibault return; 3602067fd92SSamuel Thibault } 3612067fd92SSamuel Thibault 3622067fd92SSamuel Thibault if (speakup_tty->ops->flush_buffer) 3632067fd92SSamuel Thibault speakup_tty->ops->flush_buffer(speakup_tty); 3642067fd92SSamuel Thibault 3652067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 3662067fd92SSamuel Thibault } 3672067fd92SSamuel Thibault 3682067fd92SSamuel Thibault int spk_ttyio_synth_probe(struct spk_synth *synth) 3692067fd92SSamuel Thibault { 3702067fd92SSamuel Thibault int rv = spk_ttyio_initialise_ldisc(synth); 3712067fd92SSamuel Thibault 3722067fd92SSamuel Thibault if (rv) 3732067fd92SSamuel Thibault return rv; 3742067fd92SSamuel Thibault 3752067fd92SSamuel Thibault synth->alive = 1; 3762067fd92SSamuel Thibault spk_ttyio_synth = synth; 3772067fd92SSamuel Thibault 3782067fd92SSamuel Thibault return 0; 3792067fd92SSamuel Thibault } 3802067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe); 3812067fd92SSamuel Thibault 382*1941ab1dSSamuel Thibault void spk_ttyio_release(struct spk_synth *in_synth) 3832067fd92SSamuel Thibault { 3842067fd92SSamuel Thibault if (!speakup_tty) 3852067fd92SSamuel Thibault return; 3862067fd92SSamuel Thibault 3872067fd92SSamuel Thibault tty_lock(speakup_tty); 3882067fd92SSamuel Thibault 3892067fd92SSamuel Thibault if (speakup_tty->ops->close) 3902067fd92SSamuel Thibault speakup_tty->ops->close(speakup_tty, NULL); 3912067fd92SSamuel Thibault 3922067fd92SSamuel Thibault tty_ldisc_flush(speakup_tty); 3932067fd92SSamuel Thibault tty_unlock(speakup_tty); 3942067fd92SSamuel Thibault tty_kclose(speakup_tty); 3952067fd92SSamuel Thibault } 3962067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_release); 3972067fd92SSamuel Thibault 398*1941ab1dSSamuel Thibault const char *spk_ttyio_synth_immediate(struct spk_synth *in_synth, const char *buff) 3992067fd92SSamuel Thibault { 4002067fd92SSamuel Thibault u_char ch; 4012067fd92SSamuel Thibault 4022067fd92SSamuel Thibault while ((ch = *buff)) { 4032067fd92SSamuel Thibault if (ch == '\n') 404*1941ab1dSSamuel Thibault ch = in_synth->procspeech; 4052067fd92SSamuel Thibault if (tty_write_room(speakup_tty) < 1 || 406*1941ab1dSSamuel Thibault !in_synth->io_ops->synth_out(in_synth, ch)) 4072067fd92SSamuel Thibault return buff; 4082067fd92SSamuel Thibault buff++; 4092067fd92SSamuel Thibault } 4102067fd92SSamuel Thibault return NULL; 4112067fd92SSamuel Thibault } 4122067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate); 413