388 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/types.h>3#include <linux/tty.h>4#include <linux/tty_flip.h>5#include <linux/slab.h>6 7#include "speakup.h"8#include "spk_types.h"9#include "spk_priv.h"10 11struct spk_ldisc_data {12 char buf;13 struct completion completion;14 bool buf_free;15 struct spk_synth *synth;16};17 18/*19 * This allows to catch within spk_ttyio_ldisc_open whether it is getting set20 * on for a speakup-driven device.21 */22static struct tty_struct *speakup_tty;23/* This mutex serializes the use of such global speakup_tty variable */24static DEFINE_MUTEX(speakup_tty_mutex);25 26static int ser_to_dev(int ser, dev_t *dev_no)27{28 if (ser < 0 || ser > (255 - 64)) {29 pr_err("speakup: Invalid ser param. Must be between 0 and 191 inclusive.\n");30 return -EINVAL;31 }32 33 *dev_no = MKDEV(4, (64 + ser));34 return 0;35}36 37static int get_dev_to_use(struct spk_synth *synth, dev_t *dev_no)38{39 /* use ser only when dev is not specified */40 if (strcmp(synth->dev_name, SYNTH_DEFAULT_DEV) ||41 synth->ser == SYNTH_DEFAULT_SER)42 return tty_dev_name_to_number(synth->dev_name, dev_no);43 44 return ser_to_dev(synth->ser, dev_no);45}46 47static int spk_ttyio_ldisc_open(struct tty_struct *tty)48{49 struct spk_ldisc_data *ldisc_data;50 51 if (tty != speakup_tty)52 /* Somebody tried to use this line discipline outside speakup */53 return -ENODEV;54 55 if (!tty->ops->write)56 return -EOPNOTSUPP;57 58 ldisc_data = kmalloc(sizeof(*ldisc_data), GFP_KERNEL);59 if (!ldisc_data)60 return -ENOMEM;61 62 init_completion(&ldisc_data->completion);63 ldisc_data->buf_free = true;64 tty->disc_data = ldisc_data;65 66 return 0;67}68 69static void spk_ttyio_ldisc_close(struct tty_struct *tty)70{71 kfree(tty->disc_data);72}73 74static size_t spk_ttyio_receive_buf2(struct tty_struct *tty, const u8 *cp,75 const u8 *fp, size_t count)76{77 struct spk_ldisc_data *ldisc_data = tty->disc_data;78 struct spk_synth *synth = ldisc_data->synth;79 80 if (synth->read_buff_add) {81 unsigned int i;82 83 for (i = 0; i < count; i++)84 synth->read_buff_add(cp[i]);85 86 return count;87 }88 89 if (!ldisc_data->buf_free)90 /* ttyio_in will tty_flip_buffer_push */91 return 0;92 93 /* Make sure the consumer has read buf before we have seen94 * buf_free == true and overwrite buf95 */96 mb();97 98 ldisc_data->buf = cp[0];99 ldisc_data->buf_free = false;100 complete(&ldisc_data->completion);101 102 return 1;103}104 105static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {106 .owner = THIS_MODULE,107 .num = N_SPEAKUP,108 .name = "speakup_ldisc",109 .open = spk_ttyio_ldisc_open,110 .close = spk_ttyio_ldisc_close,111 .receive_buf2 = spk_ttyio_receive_buf2,112};113 114static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);115static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);116static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch);117static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear);118static unsigned char spk_ttyio_in(struct spk_synth *in_synth);119static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth);120static void spk_ttyio_flush_buffer(struct spk_synth *in_synth);121static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth);122 123struct spk_io_ops spk_ttyio_ops = {124 .synth_out = spk_ttyio_out,125 .synth_out_unicode = spk_ttyio_out_unicode,126 .send_xchar = spk_ttyio_send_xchar,127 .tiocmset = spk_ttyio_tiocmset,128 .synth_in = spk_ttyio_in,129 .synth_in_nowait = spk_ttyio_in_nowait,130 .flush_buffer = spk_ttyio_flush_buffer,131 .wait_for_xmitr = spk_ttyio_wait_for_xmitr,132};133EXPORT_SYMBOL_GPL(spk_ttyio_ops);134 135static inline void get_termios(struct tty_struct *tty,136 struct ktermios *out_termios)137{138 down_read(&tty->termios_rwsem);139 *out_termios = tty->termios;140 up_read(&tty->termios_rwsem);141}142 143static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)144{145 int ret = 0;146 struct tty_struct *tty;147 struct ktermios tmp_termios;148 dev_t dev;149 150 ret = get_dev_to_use(synth, &dev);151 if (ret)152 return ret;153 154 tty = tty_kopen_exclusive(dev);155 if (IS_ERR(tty))156 return PTR_ERR(tty);157 158 if (tty->ops->open)159 ret = tty->ops->open(tty, NULL);160 else161 ret = -ENODEV;162 163 if (ret) {164 tty_unlock(tty);165 return ret;166 }167 168 clear_bit(TTY_HUPPED, &tty->flags);169 /* ensure hardware flow control is enabled */170 get_termios(tty, &tmp_termios);171 if (!(tmp_termios.c_cflag & CRTSCTS)) {172 tmp_termios.c_cflag |= CRTSCTS;173 tty_set_termios(tty, &tmp_termios);174 /*175 * check c_cflag to see if it's updated as tty_set_termios176 * may not return error even when no tty bits are177 * changed by the request.178 */179 get_termios(tty, &tmp_termios);180 if (!(tmp_termios.c_cflag & CRTSCTS))181 pr_warn("speakup: Failed to set hardware flow control\n");182 }183 184 tty_unlock(tty);185 186 mutex_lock(&speakup_tty_mutex);187 speakup_tty = tty;188 ret = tty_set_ldisc(tty, N_SPEAKUP);189 speakup_tty = NULL;190 mutex_unlock(&speakup_tty_mutex);191 192 if (!ret) {193 /* Success */194 struct spk_ldisc_data *ldisc_data = tty->disc_data;195 196 ldisc_data->synth = synth;197 synth->dev = tty;198 return 0;199 }200 201 pr_err("speakup: Failed to set N_SPEAKUP on tty\n");202 203 tty_lock(tty);204 if (tty->ops->close)205 tty->ops->close(tty, NULL);206 tty_unlock(tty);207 208 tty_kclose(tty);209 210 return ret;211}212 213void spk_ttyio_register_ldisc(void)214{215 if (tty_register_ldisc(&spk_ttyio_ldisc_ops))216 pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");217}218 219void spk_ttyio_unregister_ldisc(void)220{221 tty_unregister_ldisc(&spk_ttyio_ldisc_ops);222}223 224static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)225{226 struct tty_struct *tty = in_synth->dev;227 int ret;228 229 if (!in_synth->alive || !tty->ops->write)230 return 0;231 232 ret = tty->ops->write(tty, &ch, 1);233 234 if (ret == 0)235 /* No room */236 return 0;237 238 if (ret > 0)239 /* Success */240 return 1;241 242 pr_warn("%s: I/O error, deactivating speakup\n",243 in_synth->long_name);244 /* No synth any more, so nobody will restart TTYs,245 * and we thus need to do it ourselves. Now that there246 * is no synth we can let application flood anyway247 */248 in_synth->alive = 0;249 speakup_start_ttys();250 return 0;251}252 253static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)254{255 int ret;256 257 if (ch < 0x80) {258 ret = spk_ttyio_out(in_synth, ch);259 } else if (ch < 0x800) {260 ret = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));261 ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));262 } else {263 ret = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));264 ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));265 ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));266 }267 return ret;268}269 270static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch)271{272 struct tty_struct *tty = in_synth->dev;273 274 if (tty->ops->send_xchar)275 tty->ops->send_xchar(tty, ch);276}277 278static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear)279{280 struct tty_struct *tty = in_synth->dev;281 282 if (tty->ops->tiocmset)283 tty->ops->tiocmset(tty, set, clear);284}285 286static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth)287{288 return 1;289}290 291static unsigned char ttyio_in(struct spk_synth *in_synth, int timeout)292{293 struct tty_struct *tty = in_synth->dev;294 struct spk_ldisc_data *ldisc_data = tty->disc_data;295 char rv;296 297 if (!timeout) {298 if (!try_wait_for_completion(&ldisc_data->completion))299 return 0xff;300 } else if (wait_for_completion_timeout(&ldisc_data->completion,301 usecs_to_jiffies(timeout)) == 0) {302 pr_warn("spk_ttyio: timeout (%d) while waiting for input\n",303 timeout);304 return 0xff;305 }306 307 rv = ldisc_data->buf;308 /* Make sure we have read buf before we set buf_free to let309 * the producer overwrite it310 */311 mb();312 ldisc_data->buf_free = true;313 /* Let TTY push more characters */314 tty_flip_buffer_push(tty->port);315 316 return rv;317}318 319static unsigned char spk_ttyio_in(struct spk_synth *in_synth)320{321 return ttyio_in(in_synth, SPK_SYNTH_TIMEOUT);322}323 324static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth)325{326 u8 rv = ttyio_in(in_synth, 0);327 328 return (rv == 0xff) ? 0 : rv;329}330 331static void spk_ttyio_flush_buffer(struct spk_synth *in_synth)332{333 struct tty_struct *tty = in_synth->dev;334 335 if (tty->ops->flush_buffer)336 tty->ops->flush_buffer(tty);337}338 339int spk_ttyio_synth_probe(struct spk_synth *synth)340{341 int rv = spk_ttyio_initialise_ldisc(synth);342 343 if (rv)344 return rv;345 346 synth->alive = 1;347 348 return 0;349}350EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);351 352void spk_ttyio_release(struct spk_synth *in_synth)353{354 struct tty_struct *tty = in_synth->dev;355 356 if (tty == NULL)357 return;358 359 tty_lock(tty);360 361 if (tty->ops->close)362 tty->ops->close(tty, NULL);363 364 tty_ldisc_flush(tty);365 tty_unlock(tty);366 tty_kclose(tty);367 368 in_synth->dev = NULL;369}370EXPORT_SYMBOL_GPL(spk_ttyio_release);371 372const char *spk_ttyio_synth_immediate(struct spk_synth *in_synth, const char *buff)373{374 struct tty_struct *tty = in_synth->dev;375 u_char ch;376 377 while ((ch = *buff)) {378 if (ch == '\n')379 ch = in_synth->procspeech;380 if (tty_write_room(tty) < 1 ||381 !in_synth->io_ops->synth_out(in_synth, ch))382 return buff;383 buff++;384 }385 return NULL;386}387EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);388