linux/drivers/staging/speakup/speakup_dectlk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
   4 * this version considerably modified by David Borowski, david575@rogers.com
   5 *
   6 * Copyright (C) 1998-99  Kirk Reiser.
   7 * Copyright (C) 2003 David Borowski.
   8 *
   9 * specificly written as a driver for the speakup screenreview
  10 * s not a general device driver.
  11 */
  12#include <linux/unistd.h>
  13#include <linux/proc_fs.h>
  14#include <linux/jiffies.h>
  15#include <linux/spinlock.h>
  16#include <linux/sched.h>
  17#include <linux/timer.h>
  18#include <linux/kthread.h>
  19#include "speakup.h"
  20#include "spk_priv.h"
  21
  22#define DRV_VERSION "2.20"
  23#define SYNTH_CLEAR 0x03
  24#define PROCSPEECH 0x0b
  25static int xoff;
  26
  27static inline int synth_full(void)
  28{
  29        return xoff;
  30}
  31
  32static void do_catch_up(struct spk_synth *synth);
  33static void synth_flush(struct spk_synth *synth);
  34static void read_buff_add(u_char c);
  35static unsigned char get_index(struct spk_synth *synth);
  36
  37static int in_escape;
  38static int is_flushing;
  39
  40static spinlock_t flush_lock;
  41static DECLARE_WAIT_QUEUE_HEAD(flush);
  42
  43static struct var_t vars[] = {
  44        { CAPS_START, .u.s = {"[:dv ap 160] " } },
  45        { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } },
  46        { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } },
  47        { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL } },
  48        { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } },
  49        { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } },
  50        { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } },
  51        { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
  52        V_LAST_VAR
  53};
  54
  55/*
  56 * These attributes will appear in /sys/accessibility/speakup/dectlk.
  57 */
  58static struct kobj_attribute caps_start_attribute =
  59        __ATTR(caps_start, 0644, spk_var_show, spk_var_store);
  60static struct kobj_attribute caps_stop_attribute =
  61        __ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
  62static struct kobj_attribute pitch_attribute =
  63        __ATTR(pitch, 0644, spk_var_show, spk_var_store);
  64static struct kobj_attribute punct_attribute =
  65        __ATTR(punct, 0644, spk_var_show, spk_var_store);
  66static struct kobj_attribute rate_attribute =
  67        __ATTR(rate, 0644, spk_var_show, spk_var_store);
  68static struct kobj_attribute voice_attribute =
  69        __ATTR(voice, 0644, spk_var_show, spk_var_store);
  70static struct kobj_attribute vol_attribute =
  71        __ATTR(vol, 0644, spk_var_show, spk_var_store);
  72
  73static struct kobj_attribute delay_time_attribute =
  74        __ATTR(delay_time, 0644, spk_var_show, spk_var_store);
  75static struct kobj_attribute direct_attribute =
  76        __ATTR(direct, 0644, spk_var_show, spk_var_store);
  77static struct kobj_attribute full_time_attribute =
  78        __ATTR(full_time, 0644, spk_var_show, spk_var_store);
  79static struct kobj_attribute jiffy_delta_attribute =
  80        __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
  81static struct kobj_attribute trigger_time_attribute =
  82        __ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
  83
  84/*
  85 * Create a group of attributes so that we can create and destroy them all
  86 * at once.
  87 */
  88static struct attribute *synth_attrs[] = {
  89        &caps_start_attribute.attr,
  90        &caps_stop_attribute.attr,
  91        &pitch_attribute.attr,
  92        &punct_attribute.attr,
  93        &rate_attribute.attr,
  94        &voice_attribute.attr,
  95        &vol_attribute.attr,
  96        &delay_time_attribute.attr,
  97        &direct_attribute.attr,
  98        &full_time_attribute.attr,
  99        &jiffy_delta_attribute.attr,
 100        &trigger_time_attribute.attr,
 101        NULL,   /* need to NULL terminate the list of attributes */
 102};
 103
 104static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306};
 105static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73};
 106
 107static struct spk_synth synth_dectlk = {
 108        .name = "dectlk",
 109        .version = DRV_VERSION,
 110        .long_name = "Dectalk Express",
 111        .init = "[:error sp :name paul :rate 180 :tsr off] ",
 112        .procspeech = PROCSPEECH,
 113        .clear = SYNTH_CLEAR,
 114        .delay = 500,
 115        .trigger = 50,
 116        .jiffies = 50,
 117        .full = 40000,
 118        .dev_name = SYNTH_DEFAULT_DEV,
 119        .startup = SYNTH_START,
 120        .checkval = SYNTH_CHECK,
 121        .vars = vars,
 122        .default_pitch = ap_defaults,
 123        .default_vol = g5_defaults,
 124        .io_ops = &spk_ttyio_ops,
 125        .probe = spk_ttyio_synth_probe,
 126        .release = spk_ttyio_release,
 127        .synth_immediate = spk_ttyio_synth_immediate,
 128        .catch_up = do_catch_up,
 129        .flush = synth_flush,
 130        .is_alive = spk_synth_is_alive_restart,
 131        .synth_adjust = NULL,
 132        .read_buff_add = read_buff_add,
 133        .get_index = get_index,
 134        .indexing = {
 135                .command = "[:in re %d ] ",
 136                .lowindex = 1,
 137                .highindex = 8,
 138                .currindex = 1,
 139        },
 140        .attributes = {
 141                .attrs = synth_attrs,
 142                .name = "dectlk",
 143        },
 144};
 145
 146static int is_indnum(u_char *ch)
 147{
 148        if ((*ch >= '0') && (*ch <= '9')) {
 149                *ch = *ch - '0';
 150                return 1;
 151        }
 152        return 0;
 153}
 154
 155static u_char lastind;
 156
 157static unsigned char get_index(struct spk_synth *synth)
 158{
 159        u_char rv;
 160
 161        rv = lastind;
 162        lastind = 0;
 163        return rv;
 164}
 165
 166static void read_buff_add(u_char c)
 167{
 168        static int ind = -1;
 169
 170        if (c == 0x01) {
 171                unsigned long flags;
 172
 173                spin_lock_irqsave(&flush_lock, flags);
 174                is_flushing = 0;
 175                wake_up_interruptible(&flush);
 176                spin_unlock_irqrestore(&flush_lock, flags);
 177        } else if (c == 0x13) {
 178                xoff = 1;
 179        } else if (c == 0x11) {
 180                xoff = 0;
 181        } else if (is_indnum(&c)) {
 182                if (ind == -1)
 183                        ind = c;
 184                else
 185                        ind = ind * 10 + c;
 186        } else if ((c > 31) && (c < 127)) {
 187                if (ind != -1)
 188                        lastind = (u_char)ind;
 189                ind = -1;
 190        }
 191}
 192
 193static void do_catch_up(struct spk_synth *synth)
 194{
 195        int synth_full_val = 0;
 196        static u_char ch;
 197        static u_char last = '\0';
 198        unsigned long flags;
 199        unsigned long jiff_max;
 200        unsigned long timeout = msecs_to_jiffies(4000);
 201        DEFINE_WAIT(wait);
 202        struct var_t *jiffy_delta;
 203        struct var_t *delay_time;
 204        int jiffy_delta_val;
 205        int delay_time_val;
 206
 207        jiffy_delta = spk_get_var(JIFFY);
 208        delay_time = spk_get_var(DELAY);
 209        spin_lock_irqsave(&speakup_info.spinlock, flags);
 210        jiffy_delta_val = jiffy_delta->u.n.value;
 211        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 212        jiff_max = jiffies + jiffy_delta_val;
 213
 214        while (!kthread_should_stop()) {
 215                /* if no ctl-a in 4, send data anyway */
 216                spin_lock_irqsave(&flush_lock, flags);
 217                while (is_flushing && timeout) {
 218                        prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE);
 219                        spin_unlock_irqrestore(&flush_lock, flags);
 220                        timeout = schedule_timeout(timeout);
 221                        spin_lock_irqsave(&flush_lock, flags);
 222                }
 223                finish_wait(&flush, &wait);
 224                is_flushing = 0;
 225                spin_unlock_irqrestore(&flush_lock, flags);
 226
 227                spin_lock_irqsave(&speakup_info.spinlock, flags);
 228                if (speakup_info.flushing) {
 229                        speakup_info.flushing = 0;
 230                        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 231                        synth->flush(synth);
 232                        continue;
 233                }
 234                synth_buffer_skip_nonlatin1();
 235                if (synth_buffer_empty()) {
 236                        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 237                        break;
 238                }
 239                ch = synth_buffer_peek();
 240                set_current_state(TASK_INTERRUPTIBLE);
 241                delay_time_val = delay_time->u.n.value;
 242                synth_full_val = synth_full();
 243                spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 244                if (ch == '\n')
 245                        ch = 0x0D;
 246                if (synth_full_val || !synth->io_ops->synth_out(synth, ch)) {
 247                        schedule_timeout(msecs_to_jiffies(delay_time_val));
 248                        continue;
 249                }
 250                set_current_state(TASK_RUNNING);
 251                spin_lock_irqsave(&speakup_info.spinlock, flags);
 252                synth_buffer_getc();
 253                spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 254                if (ch == '[') {
 255                        in_escape = 1;
 256                } else if (ch == ']') {
 257                        in_escape = 0;
 258                } else if (ch <= SPACE) {
 259                        if (!in_escape && strchr(",.!?;:", last))
 260                                synth->io_ops->synth_out(synth, PROCSPEECH);
 261                        if (time_after_eq(jiffies, jiff_max)) {
 262                                if (!in_escape)
 263                                        synth->io_ops->synth_out(synth, PROCSPEECH);
 264                                spin_lock_irqsave(&speakup_info.spinlock,
 265                                                  flags);
 266                                jiffy_delta_val = jiffy_delta->u.n.value;
 267                                delay_time_val = delay_time->u.n.value;
 268                                spin_unlock_irqrestore(&speakup_info.spinlock,
 269                                                       flags);
 270                                schedule_timeout(msecs_to_jiffies
 271                                                 (delay_time_val));
 272                                jiff_max = jiffies + jiffy_delta_val;
 273                        }
 274                }
 275                last = ch;
 276        }
 277        if (!in_escape)
 278                synth->io_ops->synth_out(synth, PROCSPEECH);
 279}
 280
 281static void synth_flush(struct spk_synth *synth)
 282{
 283        if (in_escape)
 284                /* if in command output ']' so we don't get an error */
 285                synth->io_ops->synth_out(synth, ']');
 286        in_escape = 0;
 287        is_flushing = 1;
 288        synth->io_ops->flush_buffer();
 289        synth->io_ops->synth_out(synth, SYNTH_CLEAR);
 290}
 291
 292module_param_named(ser, synth_dectlk.ser, int, 0444);
 293module_param_named(dev, synth_dectlk.dev_name, charp, 0444);
 294module_param_named(start, synth_dectlk.startup, short, 0444);
 295
 296MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
 297MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer.");
 298MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
 299
 300module_spk_synth(synth_dectlk);
 301
 302MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
 303MODULE_AUTHOR("David Borowski");
 304MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers");
 305MODULE_LICENSE("GPL");
 306MODULE_VERSION(DRV_VERSION);
 307
 308