linux/drivers/accessibility/speakup/spk_ttyio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#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 set
  20 * 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 int spk_ttyio_receive_buf2(struct tty_struct *tty,
  75                                  const unsigned char *cp,
  76                                  const char *fp, int count)
  77{
  78        struct spk_ldisc_data *ldisc_data = tty->disc_data;
  79        struct spk_synth *synth = ldisc_data->synth;
  80
  81        if (synth->read_buff_add) {
  82                int i;
  83
  84                for (i = 0; i < count; i++)
  85                        synth->read_buff_add(cp[i]);
  86
  87                return count;
  88        }
  89
  90        if (!ldisc_data->buf_free)
  91                /* ttyio_in will tty_schedule_flip */
  92                return 0;
  93
  94        /* Make sure the consumer has read buf before we have seen
  95         * buf_free == true and overwrite buf
  96         */
  97        mb();
  98
  99        ldisc_data->buf = cp[0];
 100        ldisc_data->buf_free = false;
 101        complete(&ldisc_data->completion);
 102
 103        return 1;
 104}
 105
 106static struct tty_ldisc_ops spk_ttyio_ldisc_ops = {
 107        .owner          = THIS_MODULE,
 108        .num            = N_SPEAKUP,
 109        .name           = "speakup_ldisc",
 110        .open           = spk_ttyio_ldisc_open,
 111        .close          = spk_ttyio_ldisc_close,
 112        .receive_buf2   = spk_ttyio_receive_buf2,
 113};
 114
 115static int spk_ttyio_out(struct spk_synth *in_synth, const char ch);
 116static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch);
 117static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch);
 118static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear);
 119static unsigned char spk_ttyio_in(struct spk_synth *in_synth);
 120static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth);
 121static void spk_ttyio_flush_buffer(struct spk_synth *in_synth);
 122static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth);
 123
 124struct spk_io_ops spk_ttyio_ops = {
 125        .synth_out = spk_ttyio_out,
 126        .synth_out_unicode = spk_ttyio_out_unicode,
 127        .send_xchar = spk_ttyio_send_xchar,
 128        .tiocmset = spk_ttyio_tiocmset,
 129        .synth_in = spk_ttyio_in,
 130        .synth_in_nowait = spk_ttyio_in_nowait,
 131        .flush_buffer = spk_ttyio_flush_buffer,
 132        .wait_for_xmitr = spk_ttyio_wait_for_xmitr,
 133};
 134EXPORT_SYMBOL_GPL(spk_ttyio_ops);
 135
 136static inline void get_termios(struct tty_struct *tty,
 137                               struct ktermios *out_termios)
 138{
 139        down_read(&tty->termios_rwsem);
 140        *out_termios = tty->termios;
 141        up_read(&tty->termios_rwsem);
 142}
 143
 144static int spk_ttyio_initialise_ldisc(struct spk_synth *synth)
 145{
 146        int ret = 0;
 147        struct tty_struct *tty;
 148        struct ktermios tmp_termios;
 149        dev_t dev;
 150
 151        ret = get_dev_to_use(synth, &dev);
 152        if (ret)
 153                return ret;
 154
 155        tty = tty_kopen_exclusive(dev);
 156        if (IS_ERR(tty))
 157                return PTR_ERR(tty);
 158
 159        if (tty->ops->open)
 160                ret = tty->ops->open(tty, NULL);
 161        else
 162                ret = -ENODEV;
 163
 164        if (ret) {
 165                tty_unlock(tty);
 166                return ret;
 167        }
 168
 169        clear_bit(TTY_HUPPED, &tty->flags);
 170        /* ensure hardware flow control is enabled */
 171        get_termios(tty, &tmp_termios);
 172        if (!(tmp_termios.c_cflag & CRTSCTS)) {
 173                tmp_termios.c_cflag |= CRTSCTS;
 174                tty_set_termios(tty, &tmp_termios);
 175                /*
 176                 * check c_cflag to see if it's updated as tty_set_termios
 177                 * may not return error even when no tty bits are
 178                 * changed by the request.
 179                 */
 180                get_termios(tty, &tmp_termios);
 181                if (!(tmp_termios.c_cflag & CRTSCTS))
 182                        pr_warn("speakup: Failed to set hardware flow control\n");
 183        }
 184
 185        tty_unlock(tty);
 186
 187        mutex_lock(&speakup_tty_mutex);
 188        speakup_tty = tty;
 189        ret = tty_set_ldisc(tty, N_SPEAKUP);
 190        speakup_tty = NULL;
 191        mutex_unlock(&speakup_tty_mutex);
 192
 193        if (!ret) {
 194                /* Success */
 195                struct spk_ldisc_data *ldisc_data = tty->disc_data;
 196
 197                ldisc_data->synth = synth;
 198                synth->dev = tty;
 199                return 0;
 200        }
 201
 202        pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
 203
 204        tty_lock(tty);
 205        if (tty->ops->close)
 206                tty->ops->close(tty, NULL);
 207        tty_unlock(tty);
 208
 209        tty_kclose(tty);
 210
 211        return ret;
 212}
 213
 214void spk_ttyio_register_ldisc(void)
 215{
 216        if (tty_register_ldisc(&spk_ttyio_ldisc_ops))
 217                pr_warn("speakup: Error registering line discipline. Most synths won't work.\n");
 218}
 219
 220void spk_ttyio_unregister_ldisc(void)
 221{
 222        tty_unregister_ldisc(&spk_ttyio_ldisc_ops);
 223}
 224
 225static int spk_ttyio_out(struct spk_synth *in_synth, const char ch)
 226{
 227        struct tty_struct *tty = in_synth->dev;
 228        int ret;
 229
 230        if (!in_synth->alive || !tty->ops->write)
 231                return 0;
 232
 233        ret = tty->ops->write(tty, &ch, 1);
 234
 235        if (ret == 0)
 236                /* No room */
 237                return 0;
 238
 239        if (ret > 0)
 240                /* Success */
 241                return 1;
 242
 243        pr_warn("%s: I/O error, deactivating speakup\n",
 244                in_synth->long_name);
 245        /* No synth any more, so nobody will restart TTYs,
 246         * and we thus need to do it ourselves.  Now that there
 247         * is no synth we can let application flood anyway
 248         */
 249        in_synth->alive = 0;
 250        speakup_start_ttys();
 251        return 0;
 252}
 253
 254static int spk_ttyio_out_unicode(struct spk_synth *in_synth, u16 ch)
 255{
 256        int ret;
 257
 258        if (ch < 0x80) {
 259                ret = spk_ttyio_out(in_synth, ch);
 260        } else if (ch < 0x800) {
 261                ret  = spk_ttyio_out(in_synth, 0xc0 | (ch >> 6));
 262                ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
 263        } else {
 264                ret  = spk_ttyio_out(in_synth, 0xe0 | (ch >> 12));
 265                ret &= spk_ttyio_out(in_synth, 0x80 | ((ch >> 6) & 0x3f));
 266                ret &= spk_ttyio_out(in_synth, 0x80 | (ch & 0x3f));
 267        }
 268        return ret;
 269}
 270
 271static void spk_ttyio_send_xchar(struct spk_synth *in_synth, char ch)
 272{
 273        struct tty_struct *tty = in_synth->dev;
 274
 275        if (tty->ops->send_xchar)
 276                tty->ops->send_xchar(tty, ch);
 277}
 278
 279static void spk_ttyio_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear)
 280{
 281        struct tty_struct *tty = in_synth->dev;
 282
 283        if (tty->ops->tiocmset)
 284                tty->ops->tiocmset(tty, set, clear);
 285}
 286
 287static int spk_ttyio_wait_for_xmitr(struct spk_synth *in_synth)
 288{
 289        return 1;
 290}
 291
 292static unsigned char ttyio_in(struct spk_synth *in_synth, int timeout)
 293{
 294        struct tty_struct *tty = in_synth->dev;
 295        struct spk_ldisc_data *ldisc_data = tty->disc_data;
 296        char rv;
 297
 298        if (!timeout) {
 299                if (!try_wait_for_completion(&ldisc_data->completion))
 300                        return 0xff;
 301        } else if (wait_for_completion_timeout(&ldisc_data->completion,
 302                                        usecs_to_jiffies(timeout)) == 0) {
 303                pr_warn("spk_ttyio: timeout (%d)  while waiting for input\n",
 304                        timeout);
 305                return 0xff;
 306        }
 307
 308        rv = ldisc_data->buf;
 309        /* Make sure we have read buf before we set buf_free to let
 310         * the producer overwrite it
 311         */
 312        mb();
 313        ldisc_data->buf_free = true;
 314        /* Let TTY push more characters */
 315        tty_schedule_flip(tty->port);
 316
 317        return rv;
 318}
 319
 320static unsigned char spk_ttyio_in(struct spk_synth *in_synth)
 321{
 322        return ttyio_in(in_synth, SPK_SYNTH_TIMEOUT);
 323}
 324
 325static unsigned char spk_ttyio_in_nowait(struct spk_synth *in_synth)
 326{
 327        u8 rv = ttyio_in(in_synth, 0);
 328
 329        return (rv == 0xff) ? 0 : rv;
 330}
 331
 332static void spk_ttyio_flush_buffer(struct spk_synth *in_synth)
 333{
 334        struct tty_struct *tty = in_synth->dev;
 335
 336        if (tty->ops->flush_buffer)
 337                tty->ops->flush_buffer(tty);
 338}
 339
 340int spk_ttyio_synth_probe(struct spk_synth *synth)
 341{
 342        int rv = spk_ttyio_initialise_ldisc(synth);
 343
 344        if (rv)
 345                return rv;
 346
 347        synth->alive = 1;
 348
 349        return 0;
 350}
 351EXPORT_SYMBOL_GPL(spk_ttyio_synth_probe);
 352
 353void spk_ttyio_release(struct spk_synth *in_synth)
 354{
 355        struct tty_struct *tty = in_synth->dev;
 356
 357        tty_lock(tty);
 358
 359        if (tty->ops->close)
 360                tty->ops->close(tty, NULL);
 361
 362        tty_ldisc_flush(tty);
 363        tty_unlock(tty);
 364        tty_kclose(tty);
 365
 366        in_synth->dev = NULL;
 367}
 368EXPORT_SYMBOL_GPL(spk_ttyio_release);
 369
 370const char *spk_ttyio_synth_immediate(struct spk_synth *in_synth, const char *buff)
 371{
 372        struct tty_struct *tty = in_synth->dev;
 373        u_char ch;
 374
 375        while ((ch = *buff)) {
 376                if (ch == '\n')
 377                        ch = in_synth->procspeech;
 378                if (tty_write_room(tty) < 1 ||
 379                    !in_synth->io_ops->synth_out(in_synth, ch))
 380                        return buff;
 381                buff++;
 382        }
 383        return NULL;
 384}
 385EXPORT_SYMBOL_GPL(spk_ttyio_synth_immediate);
 386