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 502067fd92SSamuel Thibault if (!tty->ops->write) 512067fd92SSamuel Thibault return -EOPNOTSUPP; 52d4122754SSamuel Thibault 53d4122754SSamuel Thibault mutex_lock(&speakup_tty_mutex); 54d4122754SSamuel Thibault if (speakup_tty) { 55d4122754SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 56d4122754SSamuel Thibault return -EBUSY; 57d4122754SSamuel Thibault } 582067fd92SSamuel Thibault speakup_tty = tty; 592067fd92SSamuel Thibault 602067fd92SSamuel Thibault ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL); 61d4122754SSamuel Thibault if (!ldisc_data) { 62d4122754SSamuel Thibault speakup_tty = NULL; 63d4122754SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 642067fd92SSamuel Thibault return -ENOMEM; 65d4122754SSamuel Thibault } 662067fd92SSamuel Thibault 672067fd92SSamuel Thibault init_completion(&ldisc_data->completion); 682067fd92SSamuel Thibault ldisc_data->buf_free = true; 692067fd92SSamuel Thibault speakup_tty->disc_data = ldisc_data; 70d4122754SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 712067fd92SSamuel Thibault 722067fd92SSamuel Thibault return 0; 732067fd92SSamuel Thibault } 742067fd92SSamuel Thibault 752067fd92SSamuel Thibault static void spk_ttyio_ldisc_close(struct tty_struct *tty) 762067fd92SSamuel Thibault { 772067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 782067fd92SSamuel Thibault kfree(speakup_tty->disc_data); 792067fd92SSamuel Thibault speakup_tty = NULL; 802067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 812067fd92SSamuel Thibault } 822067fd92SSamuel Thibault 832067fd92SSamuel Thibault static int spk_ttyio_receive_buf2(struct tty_struct *tty, 842067fd92SSamuel Thibault const unsigned char *cp, char *fp, int count) 852067fd92SSamuel Thibault { 862067fd92SSamuel Thibault struct spk_ldisc_data *ldisc_data = tty->disc_data; 872067fd92SSamuel Thibault 882067fd92SSamuel Thibault if (spk_ttyio_synth->read_buff_add) { 892067fd92SSamuel Thibault int i; 902067fd92SSamuel Thibault 912067fd92SSamuel Thibault for (i = 0; i < count; i++) 922067fd92SSamuel Thibault spk_ttyio_synth->read_buff_add(cp[i]); 932067fd92SSamuel Thibault 942067fd92SSamuel Thibault return count; 952067fd92SSamuel Thibault } 962067fd92SSamuel Thibault 972067fd92SSamuel Thibault if (!ldisc_data->buf_free) 982067fd92SSamuel Thibault /* ttyio_in will tty_schedule_flip */ 992067fd92SSamuel Thibault return 0; 1002067fd92SSamuel Thibault 1012067fd92SSamuel Thibault /* Make sure the consumer has read buf before we have seen 1022067fd92SSamuel Thibault * buf_free == true and overwrite buf 1032067fd92SSamuel Thibault */ 1042067fd92SSamuel Thibault mb(); 1052067fd92SSamuel Thibault 1062067fd92SSamuel Thibault ldisc_data->buf = cp[0]; 1072067fd92SSamuel Thibault ldisc_data->buf_free = false; 1082067fd92SSamuel Thibault complete(&ldisc_data->completion); 1092067fd92SSamuel Thibault 1102067fd92SSamuel Thibault return 1; 1112067fd92SSamuel Thibault } 1122067fd92SSamuel Thibault 1132067fd92SSamuel Thibault static struct tty_ldisc_ops spk_ttyio_ldisc_ops = { 1142067fd92SSamuel Thibault .owner = THIS_MODULE, 1152067fd92SSamuel Thibault .magic = TTY_LDISC_MAGIC, 1162067fd92SSamuel Thibault .name = "speakup_ldisc", 1172067fd92SSamuel Thibault .open = spk_ttyio_ldisc_open, 1182067fd92SSamuel Thibault .close = spk_ttyio_ldisc_close, 1192067fd92SSamuel Thibault .receive_buf2 = spk_ttyio_receive_buf2, 1202067fd92SSamuel Thibault }; 1212067fd92SSamuel Thibault 1222067fd92SSamuel Thibault static int spk_ttyio_out(struct spk_synth *in_synth, const char ch); 1232067fd92SSamuel Thibault static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch); 1242067fd92SSamuel Thibault static void spk_ttyio_send_xchar(char ch); 1252067fd92SSamuel Thibault static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear); 1262067fd92SSamuel Thibault static unsigned char spk_ttyio_in(void); 1272067fd92SSamuel Thibault static unsigned char spk_ttyio_in_nowait(void); 1282067fd92SSamuel Thibault static void spk_ttyio_flush_buffer(void); 1292b86d9b8SSamuel Thibault static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth); 1302067fd92SSamuel Thibault 1312067fd92SSamuel Thibault struct spk_io_ops spk_ttyio_ops = { 1322067fd92SSamuel Thibault .synth_out = spk_ttyio_out, 1332067fd92SSamuel Thibault .synth_out_unicode = spk_ttyio_out_unicode, 1342067fd92SSamuel Thibault .send_xchar = spk_ttyio_send_xchar, 1352067fd92SSamuel Thibault .tiocmset = spk_ttyio_tiocmset, 1362067fd92SSamuel Thibault .synth_in = spk_ttyio_in, 1372067fd92SSamuel Thibault .synth_in_nowait = spk_ttyio_in_nowait, 1382067fd92SSamuel Thibault .flush_buffer = spk_ttyio_flush_buffer, 1392b86d9b8SSamuel Thibault .wait_for_xmitr = spk_ttyio_wait_for_xmitr, 1402067fd92SSamuel Thibault }; 1412067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_ops); 1422067fd92SSamuel Thibault 1432067fd92SSamuel Thibault static inline void get_termios(struct tty_struct *tty, 1442067fd92SSamuel Thibault struct ktermios *out_termios) 1452067fd92SSamuel Thibault { 1462067fd92SSamuel Thibault down_read(&tty->termios_rwsem); 1472067fd92SSamuel Thibault *out_termios = tty->termios; 1482067fd92SSamuel Thibault up_read(&tty->termios_rwsem); 1492067fd92SSamuel Thibault } 1502067fd92SSamuel Thibault 1512067fd92SSamuel Thibault static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) 1522067fd92SSamuel Thibault { 1532067fd92SSamuel Thibault int ret = 0; 1542067fd92SSamuel Thibault struct tty_struct *tty; 1552067fd92SSamuel Thibault struct ktermios tmp_termios; 1562067fd92SSamuel Thibault dev_t dev; 1572067fd92SSamuel Thibault 1582067fd92SSamuel Thibault ret = get_dev_to_use(synth, &dev); 1592067fd92SSamuel Thibault if (ret) 1602067fd92SSamuel Thibault return ret; 1612067fd92SSamuel Thibault 1622067fd92SSamuel Thibault tty = tty_kopen(dev); 1632067fd92SSamuel Thibault if (IS_ERR(tty)) 1642067fd92SSamuel Thibault return PTR_ERR(tty); 1652067fd92SSamuel Thibault 1662067fd92SSamuel Thibault if (tty->ops->open) 1672067fd92SSamuel Thibault ret = tty->ops->open(tty, NULL); 1682067fd92SSamuel Thibault else 1692067fd92SSamuel Thibault ret = -ENODEV; 1702067fd92SSamuel Thibault 1712067fd92SSamuel Thibault if (ret) { 1722067fd92SSamuel Thibault tty_unlock(tty); 1732067fd92SSamuel Thibault return ret; 1742067fd92SSamuel Thibault } 1752067fd92SSamuel Thibault 1762067fd92SSamuel Thibault clear_bit(TTY_HUPPED, &tty->flags); 1772067fd92SSamuel Thibault /* ensure hardware flow control is enabled */ 1782067fd92SSamuel Thibault get_termios(tty, &tmp_termios); 1792067fd92SSamuel Thibault if (!(tmp_termios.c_cflag & CRTSCTS)) { 1802067fd92SSamuel Thibault tmp_termios.c_cflag |= CRTSCTS; 1812067fd92SSamuel Thibault tty_set_termios(tty, &tmp_termios); 1822067fd92SSamuel Thibault /* 1832067fd92SSamuel Thibault * check c_cflag to see if it's updated as tty_set_termios 1842067fd92SSamuel Thibault * may not return error even when no tty bits are 1852067fd92SSamuel Thibault * changed by the request. 1862067fd92SSamuel Thibault */ 1872067fd92SSamuel Thibault get_termios(tty, &tmp_termios); 1882067fd92SSamuel Thibault if (!(tmp_termios.c_cflag & CRTSCTS)) 1892067fd92SSamuel Thibault pr_warn("speakup: Failed to set hardware flow control\n"); 1902067fd92SSamuel Thibault } 1912067fd92SSamuel Thibault 1922067fd92SSamuel Thibault tty_unlock(tty); 1932067fd92SSamuel Thibault 1942067fd92SSamuel Thibault ret = tty_set_ldisc(tty, N_SPEAKUP); 1952067fd92SSamuel Thibault if (ret) 1962067fd92SSamuel Thibault pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); 1972067fd92SSamuel Thibault 1982067fd92SSamuel Thibault return ret; 1992067fd92SSamuel Thibault } 2002067fd92SSamuel Thibault 2012067fd92SSamuel Thibault void spk_ttyio_register_ldisc(void) 2022067fd92SSamuel Thibault { 2032067fd92SSamuel Thibault if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops)) 2042067fd92SSamuel Thibault pr_warn("speakup: Error registering line discipline. Most synths won't work.\n"); 2052067fd92SSamuel Thibault } 2062067fd92SSamuel Thibault 2072067fd92SSamuel Thibault void spk_ttyio_unregister_ldisc(void) 2082067fd92SSamuel Thibault { 2092067fd92SSamuel Thibault if (tty_unregister_ldisc(N_SPEAKUP)) 2102067fd92SSamuel Thibault pr_warn("speakup: Couldn't unregister ldisc\n"); 2112067fd92SSamuel Thibault } 2122067fd92SSamuel Thibault 2132067fd92SSamuel Thibault static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) 2142067fd92SSamuel Thibault { 2152067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 2162067fd92SSamuel Thibault if (in_synth->alive && speakup_tty && speakup_tty->ops->write) { 2172067fd92SSamuel Thibault int ret = speakup_tty->ops->write(speakup_tty, &ch, 1); 2182067fd92SSamuel Thibault 2192067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2202067fd92SSamuel Thibault if (ret == 0) 2212067fd92SSamuel Thibault /* No room */ 2222067fd92SSamuel Thibault return 0; 2232067fd92SSamuel Thibault if (ret < 0) { 2242067fd92SSamuel Thibault pr_warn("%s: I/O error, deactivating speakup\n", 2252067fd92SSamuel Thibault in_synth->long_name); 2262067fd92SSamuel Thibault /* No synth any more, so nobody will restart TTYs, 2272067fd92SSamuel Thibault * and we thus need to do it ourselves. Now that there 2282067fd92SSamuel Thibault * is no synth we can let application flood anyway 2292067fd92SSamuel Thibault */ 2302067fd92SSamuel Thibault in_synth->alive = 0; 2312067fd92SSamuel Thibault speakup_start_ttys(); 2322067fd92SSamuel Thibault return 0; 2332067fd92SSamuel Thibault } 2342067fd92SSamuel Thibault return 1; 2352067fd92SSamuel Thibault } 2362067fd92SSamuel Thibault 2372067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2382067fd92SSamuel Thibault return 0; 2392067fd92SSamuel Thibault } 2402067fd92SSamuel Thibault 2412067fd92SSamuel Thibault static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch) 2422067fd92SSamuel Thibault { 2432067fd92SSamuel Thibault int ret; 2442067fd92SSamuel Thibault 2452067fd92SSamuel Thibault if (ch < 0x80) { 2462067fd92SSamuel Thibault ret = spk_ttyio_out(in_synth, ch); 2472067fd92SSamuel Thibault } else if (ch < 0x800) { 2482067fd92SSamuel Thibault ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6)); 2492067fd92SSamuel Thibault ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); 2502067fd92SSamuel Thibault } else { 2512067fd92SSamuel Thibault ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12)); 2522067fd92SSamuel Thibault ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f)); 2532067fd92SSamuel Thibault ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f)); 2542067fd92SSamuel Thibault } 2552067fd92SSamuel Thibault return ret; 2562067fd92SSamuel Thibault } 2572067fd92SSamuel Thibault 2582067fd92SSamuel Thibault static int check_tty(struct tty_struct *tty) 2592067fd92SSamuel Thibault { 2602067fd92SSamuel Thibault if (!tty) { 2612067fd92SSamuel Thibault pr_warn("%s: I/O error, deactivating speakup\n", 2622067fd92SSamuel Thibault spk_ttyio_synth->long_name); 2632067fd92SSamuel Thibault /* No synth any more, so nobody will restart TTYs, and we thus 2642067fd92SSamuel Thibault * need to do it ourselves. Now that there is no synth we can 2652067fd92SSamuel Thibault * let application flood anyway 2662067fd92SSamuel Thibault */ 2672067fd92SSamuel Thibault spk_ttyio_synth->alive = 0; 2682067fd92SSamuel Thibault speakup_start_ttys(); 2692067fd92SSamuel Thibault return 1; 2702067fd92SSamuel Thibault } 2712067fd92SSamuel Thibault 2722067fd92SSamuel Thibault return 0; 2732067fd92SSamuel Thibault } 2742067fd92SSamuel Thibault 2752067fd92SSamuel Thibault static void spk_ttyio_send_xchar(char ch) 2762067fd92SSamuel Thibault { 2772067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 2782067fd92SSamuel Thibault if (check_tty(speakup_tty)) { 2792067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2802067fd92SSamuel Thibault return; 2812067fd92SSamuel Thibault } 2822067fd92SSamuel Thibault 2832067fd92SSamuel Thibault if (speakup_tty->ops->send_xchar) 2842067fd92SSamuel Thibault speakup_tty->ops->send_xchar(speakup_tty, ch); 2852067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2862067fd92SSamuel Thibault } 2872067fd92SSamuel Thibault 2882067fd92SSamuel Thibault static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear) 2892067fd92SSamuel Thibault { 2902067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 2912067fd92SSamuel Thibault if (check_tty(speakup_tty)) { 2922067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2932067fd92SSamuel Thibault return; 2942067fd92SSamuel Thibault } 2952067fd92SSamuel Thibault 2962067fd92SSamuel Thibault if (speakup_tty->ops->tiocmset) 2972067fd92SSamuel Thibault speakup_tty->ops->tiocmset(speakup_tty, set, clear); 2982067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 2992067fd92SSamuel Thibault } 3002067fd92SSamuel Thibault 3012b86d9b8SSamuel Thibault static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth) 3022b86d9b8SSamuel Thibault { 3032b86d9b8SSamuel Thibault return 1; 3042b86d9b8SSamuel Thibault } 3052b86d9b8SSamuel Thibault 3062067fd92SSamuel Thibault static unsigned char ttyio_in(int timeout) 3072067fd92SSamuel Thibault { 3082067fd92SSamuel Thibault struct spk_ldisc_data *ldisc_data = speakup_tty->disc_data; 3092067fd92SSamuel Thibault char rv; 3102067fd92SSamuel Thibault 3112067fd92SSamuel Thibault if (wait_for_completion_timeout(&ldisc_data->completion, 3122067fd92SSamuel Thibault usecs_to_jiffies(timeout)) == 0) { 3132067fd92SSamuel Thibault if (timeout) 3142067fd92SSamuel Thibault pr_warn("spk_ttyio: timeout (%d) while waiting for input\n", 3152067fd92SSamuel Thibault timeout); 3162067fd92SSamuel Thibault return 0xff; 3172067fd92SSamuel Thibault } 3182067fd92SSamuel Thibault 3192067fd92SSamuel Thibault rv = ldisc_data->buf; 3202067fd92SSamuel Thibault /* Make sure we have read buf before we set buf_free to let 3212067fd92SSamuel Thibault * the producer overwrite it 3222067fd92SSamuel Thibault */ 3232067fd92SSamuel Thibault mb(); 3242067fd92SSamuel Thibault ldisc_data->buf_free = true; 3252067fd92SSamuel Thibault /* Let TTY push more characters */ 3262067fd92SSamuel Thibault tty_schedule_flip(speakup_tty->port); 3272067fd92SSamuel Thibault 3282067fd92SSamuel Thibault return rv; 3292067fd92SSamuel Thibault } 3302067fd92SSamuel Thibault 3312067fd92SSamuel Thibault static unsigned char spk_ttyio_in(void) 3322067fd92SSamuel Thibault { 3332067fd92SSamuel Thibault return ttyio_in(SPK_SYNTH_TIMEOUT); 3342067fd92SSamuel Thibault } 3352067fd92SSamuel Thibault 3362067fd92SSamuel Thibault static unsigned char spk_ttyio_in_nowait(void) 3372067fd92SSamuel Thibault { 3382067fd92SSamuel Thibault u8 rv = ttyio_in(0); 3392067fd92SSamuel Thibault 3402067fd92SSamuel Thibault return (rv == 0xff) ? 0 : rv; 3412067fd92SSamuel Thibault } 3422067fd92SSamuel Thibault 3432067fd92SSamuel Thibault static void spk_ttyio_flush_buffer(void) 3442067fd92SSamuel Thibault { 3452067fd92SSamuel Thibault mutex_lock(&speakup_tty_mutex); 3462067fd92SSamuel Thibault if (check_tty(speakup_tty)) { 3472067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 3482067fd92SSamuel Thibault return; 3492067fd92SSamuel Thibault } 3502067fd92SSamuel Thibault 3512067fd92SSamuel Thibault if (speakup_tty->ops->flush_buffer) 3522067fd92SSamuel Thibault speakup_tty->ops->flush_buffer(speakup_tty); 3532067fd92SSamuel Thibault 3542067fd92SSamuel Thibault mutex_unlock(&speakup_tty_mutex); 3552067fd92SSamuel Thibault } 3562067fd92SSamuel Thibault 3572067fd92SSamuel Thibault int spk_ttyio_synth_probe(struct spk_synth *synth) 3582067fd92SSamuel Thibault { 3592067fd92SSamuel Thibault int rv = spk_ttyio_initialise_ldisc(synth); 3602067fd92SSamuel Thibault 3612067fd92SSamuel Thibault if (rv) 3622067fd92SSamuel Thibault return rv; 3632067fd92SSamuel Thibault 3642067fd92SSamuel Thibault synth->alive = 1; 3652067fd92SSamuel Thibault spk_ttyio_synth = synth; 3662067fd92SSamuel Thibault 3672067fd92SSamuel Thibault return 0; 3682067fd92SSamuel Thibault } 3692067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe); 3702067fd92SSamuel Thibault 3712067fd92SSamuel Thibault void spk_ttyio_release(void) 3722067fd92SSamuel Thibault { 3732067fd92SSamuel Thibault if (!speakup_tty) 3742067fd92SSamuel Thibault return; 3752067fd92SSamuel Thibault 3762067fd92SSamuel Thibault tty_lock(speakup_tty); 3772067fd92SSamuel Thibault 3782067fd92SSamuel Thibault if (speakup_tty->ops->close) 3792067fd92SSamuel Thibault speakup_tty->ops->close(speakup_tty, NULL); 3802067fd92SSamuel Thibault 3812067fd92SSamuel Thibault tty_ldisc_flush(speakup_tty); 3822067fd92SSamuel Thibault tty_unlock(speakup_tty); 3832067fd92SSamuel Thibault tty_kclose(speakup_tty); 3842067fd92SSamuel Thibault } 3852067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_release); 3862067fd92SSamuel Thibault 3872067fd92SSamuel Thibault const char *spk_ttyio_synth_immediate(struct spk_synth *synth, const char *buff) 3882067fd92SSamuel Thibault { 3892067fd92SSamuel Thibault u_char ch; 3902067fd92SSamuel Thibault 3912067fd92SSamuel Thibault while ((ch = *buff)) { 3922067fd92SSamuel Thibault if (ch == '\n') 3932067fd92SSamuel Thibault ch = synth->procspeech; 3942067fd92SSamuel Thibault if (tty_write_room(speakup_tty) < 1 || 3952067fd92SSamuel Thibault !synth->io_ops->synth_out(synth, ch)) 3962067fd92SSamuel Thibault return buff; 3972067fd92SSamuel Thibault buff++; 3982067fd92SSamuel Thibault } 3992067fd92SSamuel Thibault return NULL; 4002067fd92SSamuel Thibault } 4012067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate); 402