root/drivers/accessibility/speakup/buffers.c
// SPDX-License-Identifier: GPL-2.0
#include <linux/console.h>
#include <linux/types.h>
#include <linux/wait.h>

#include "speakup.h"
#include "spk_priv.h"

#define SYNTH_BUF_SIZE 8192     /* currently 8K bytes */

static u16 synth_buffer[SYNTH_BUF_SIZE];        /* guess what this is for! */
static u16 *buff_in = synth_buffer;
static u16 *buff_out = synth_buffer;
static u16 *buffer_end = synth_buffer + SYNTH_BUF_SIZE - 1;

/* These try to throttle applications by stopping the TTYs
 * Note: we need to make sure that we will restart them eventually, which is
 * usually not possible to do from the notifiers. TODO: it should be possible
 * starting from linux 2.6.26.
 *
 * So we only stop when we know alive == 1 (else we discard the data anyway),
 * and the alive synth will eventually call start_ttys from the thread context.
 */
void speakup_start_ttys(void)
{
        int i;

        for (i = 0; i < MAX_NR_CONSOLES; i++) {
                if (speakup_console[i] && speakup_console[i]->tty_stopped)
                        continue;
                if (vc_cons[i].d && vc_cons[i].d->port.tty)
                        start_tty(vc_cons[i].d->port.tty);
        }
}
EXPORT_SYMBOL_GPL(speakup_start_ttys);

static void speakup_stop_ttys(void)
{
        int i;

        for (i = 0; i < MAX_NR_CONSOLES; i++)
                if (vc_cons[i].d && vc_cons[i].d->port.tty)
                        stop_tty(vc_cons[i].d->port.tty);
}

static int synth_buffer_free(void)
{
        int chars_free;

        if (buff_in >= buff_out)
                chars_free = SYNTH_BUF_SIZE - (buff_in - buff_out);
        else
                chars_free = buff_out - buff_in;
        return chars_free;
}

int synth_buffer_empty(void)
{
        return (buff_in == buff_out);
}
EXPORT_SYMBOL_GPL(synth_buffer_empty);

void synth_buffer_add(u16 ch)
{
        if (!synth->alive) {
                /* This makes sure that we won't stop TTYs if there is no synth
                 * to restart them
                 */
                return;
        }
        if (synth_buffer_free() <= 100) {
                synth_start();
                speakup_stop_ttys();
        }
        if (synth_buffer_free() <= 1)
                return;
        *buff_in++ = ch;
        if (buff_in > buffer_end)
                buff_in = synth_buffer;
        /* We have written something to the speech synthesis, so we are not
         * paused any more.
         */
        spk_paused = false;
}

u16 synth_buffer_getc(void)
{
        u16 ch;

        if (buff_out == buff_in)
                return 0;
        ch = *buff_out++;
        if (buff_out > buffer_end)
                buff_out = synth_buffer;
        return ch;
}
EXPORT_SYMBOL_GPL(synth_buffer_getc);

u16 synth_buffer_peek(void)
{
        if (buff_out == buff_in)
                return 0;
        return *buff_out;
}
EXPORT_SYMBOL_GPL(synth_buffer_peek);

void synth_buffer_skip_nonlatin1(void)
{
        while (buff_out != buff_in) {
                if (*buff_out < 0x100)
                        return;
                buff_out++;
                if (buff_out > buffer_end)
                        buff_out = synth_buffer;
        }
}
EXPORT_SYMBOL_GPL(synth_buffer_skip_nonlatin1);

void synth_buffer_clear(void)
{
        buff_in = synth_buffer;
        buff_out = synth_buffer;
}
EXPORT_SYMBOL_GPL(synth_buffer_clear);