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 * specifically written as a driver for the speakup screenreview 10 * s not a general device driver. 11 */ 12 #include <linux/jiffies.h> 13 #include <linux/sched.h> 14 #include <linux/timer.h> 15 #include <linux/kthread.h> 16 17 #include "spk_priv.h" 18 #include "speakup.h" 19 20 #define DRV_VERSION "2.14" 21 #define SYNTH_CLEAR 0x03 22 #define PROCSPEECH 0x0b 23 24 static volatile unsigned char last_char; 25 26 static void read_buff_add(u_char ch) 27 { 28 last_char = ch; 29 } 30 31 static inline bool synth_full(void) 32 { 33 return last_char == 0x13; 34 } 35 36 static void do_catch_up(struct spk_synth *synth); 37 static void synth_flush(struct spk_synth *synth); 38 39 static int in_escape; 40 41 42 enum default_vars_id { 43 CAPS_START_ID = 0, CAPS_STOP_ID, 44 RATE_ID, PITCH_ID, INFLECTION_ID, 45 VOL_ID, PUNCT_ID, VOICE_ID, 46 DIRECT_ID, V_LAST_ID, 47 NB_ID, 48 }; 49 50 static struct var_t vars[NB_ID] = { 51 [CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 222]" } }, 52 [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[:dv ap 100]" } }, 53 [RATE_ID] = { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL } }, 54 [PITCH_ID] = { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL } }, 55 [INFLECTION_ID] = { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, 56 [VOL_ID] = { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL } }, 57 [PUNCT_ID] = { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } }, 58 [VOICE_ID] = { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } }, 59 [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 60 V_LAST_VAR 61 }; 62 63 /* 64 * These attributes will appear in /sys/accessibility/speakup/decext. 65 */ 66 static struct kobj_attribute caps_start_attribute = 67 __ATTR(caps_start, 0644, spk_var_show, spk_var_store); 68 static struct kobj_attribute caps_stop_attribute = 69 __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); 70 static struct kobj_attribute pitch_attribute = 71 __ATTR(pitch, 0644, spk_var_show, spk_var_store); 72 static struct kobj_attribute inflection_attribute = 73 __ATTR(inflection, 0644, spk_var_show, spk_var_store); 74 static struct kobj_attribute punct_attribute = 75 __ATTR(punct, 0644, spk_var_show, spk_var_store); 76 static struct kobj_attribute rate_attribute = 77 __ATTR(rate, 0644, spk_var_show, spk_var_store); 78 static struct kobj_attribute voice_attribute = 79 __ATTR(voice, 0644, spk_var_show, spk_var_store); 80 static struct kobj_attribute vol_attribute = 81 __ATTR(vol, 0644, spk_var_show, spk_var_store); 82 83 static struct kobj_attribute delay_time_attribute = 84 __ATTR(delay_time, 0644, spk_var_show, spk_var_store); 85 static struct kobj_attribute direct_attribute = 86 __ATTR(direct, 0644, spk_var_show, spk_var_store); 87 static struct kobj_attribute full_time_attribute = 88 __ATTR(full_time, 0644, spk_var_show, spk_var_store); 89 static struct kobj_attribute jiffy_delta_attribute = 90 __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); 91 static struct kobj_attribute trigger_time_attribute = 92 __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); 93 94 /* 95 * Create a group of attributes so that we can create and destroy them all 96 * at once. 97 */ 98 static struct attribute *synth_attrs[] = { 99 &caps_start_attribute.attr, 100 &caps_stop_attribute.attr, 101 &pitch_attribute.attr, 102 &inflection_attribute.attr, 103 &punct_attribute.attr, 104 &rate_attribute.attr, 105 &voice_attribute.attr, 106 &vol_attribute.attr, 107 &delay_time_attribute.attr, 108 &direct_attribute.attr, 109 &full_time_attribute.attr, 110 &jiffy_delta_attribute.attr, 111 &trigger_time_attribute.attr, 112 NULL, /* need to NULL terminate the list of attributes */ 113 }; 114 115 static struct spk_synth synth_decext = { 116 .name = "decext", 117 .version = DRV_VERSION, 118 .long_name = "Dectalk External", 119 .init = "[:pe -380]", 120 .procspeech = PROCSPEECH, 121 .clear = SYNTH_CLEAR, 122 .delay = 500, 123 .trigger = 50, 124 .jiffies = 50, 125 .full = 40000, 126 .flags = SF_DEC, 127 .dev_name = SYNTH_DEFAULT_DEV, 128 .startup = SYNTH_START, 129 .checkval = SYNTH_CHECK, 130 .vars = vars, 131 .io_ops = &spk_ttyio_ops, 132 .probe = spk_ttyio_synth_probe, 133 .release = spk_ttyio_release, 134 .synth_immediate = spk_ttyio_synth_immediate, 135 .catch_up = do_catch_up, 136 .flush = synth_flush, 137 .is_alive = spk_synth_is_alive_restart, 138 .synth_adjust = NULL, 139 .read_buff_add = read_buff_add, 140 .get_index = NULL, 141 .indexing = { 142 .command = NULL, 143 .lowindex = 0, 144 .highindex = 0, 145 .currindex = 0, 146 }, 147 .attributes = { 148 .attrs = synth_attrs, 149 .name = "decext", 150 }, 151 }; 152 153 static void do_catch_up(struct spk_synth *synth) 154 { 155 u_char ch; 156 static u_char last = '\0'; 157 unsigned long flags; 158 unsigned long jiff_max; 159 struct var_t *jiffy_delta; 160 struct var_t *delay_time; 161 int jiffy_delta_val = 0; 162 int delay_time_val = 0; 163 164 jiffy_delta = spk_get_var(JIFFY); 165 delay_time = spk_get_var(DELAY); 166 167 spin_lock_irqsave(&speakup_info.spinlock, flags); 168 jiffy_delta_val = jiffy_delta->u.n.value; 169 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 170 jiff_max = jiffies + jiffy_delta_val; 171 172 while (!kthread_should_stop()) { 173 spin_lock_irqsave(&speakup_info.spinlock, flags); 174 if (speakup_info.flushing) { 175 speakup_info.flushing = 0; 176 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 177 synth->flush(synth); 178 continue; 179 } 180 synth_buffer_skip_nonlatin1(); 181 if (synth_buffer_empty()) { 182 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 183 break; 184 } 185 ch = synth_buffer_peek(); 186 set_current_state(TASK_INTERRUPTIBLE); 187 delay_time_val = delay_time->u.n.value; 188 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 189 if (ch == '\n') 190 ch = 0x0D; 191 if (synth_full() || !synth->io_ops->synth_out(synth, ch)) { 192 schedule_timeout(msecs_to_jiffies(delay_time_val)); 193 continue; 194 } 195 set_current_state(TASK_RUNNING); 196 spin_lock_irqsave(&speakup_info.spinlock, flags); 197 synth_buffer_getc(); 198 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 199 if (ch == '[') { 200 in_escape = 1; 201 } else if (ch == ']') { 202 in_escape = 0; 203 } else if (ch <= SPACE) { 204 if (!in_escape && strchr(",.!?;:", last)) 205 synth->io_ops->synth_out(synth, PROCSPEECH); 206 if (time_after_eq(jiffies, jiff_max)) { 207 if (!in_escape) 208 synth->io_ops->synth_out(synth, 209 PROCSPEECH); 210 spin_lock_irqsave(&speakup_info.spinlock, 211 flags); 212 jiffy_delta_val = jiffy_delta->u.n.value; 213 delay_time_val = delay_time->u.n.value; 214 spin_unlock_irqrestore(&speakup_info.spinlock, 215 flags); 216 schedule_timeout(msecs_to_jiffies 217 (delay_time_val)); 218 jiff_max = jiffies + jiffy_delta_val; 219 } 220 } 221 last = ch; 222 } 223 if (!in_escape) 224 synth->io_ops->synth_out(synth, PROCSPEECH); 225 } 226 227 static void synth_flush(struct spk_synth *synth) 228 { 229 in_escape = 0; 230 synth->io_ops->flush_buffer(synth); 231 synth->synth_immediate(synth, "\033P;10z\033\\"); 232 } 233 234 module_param_named(ser, synth_decext.ser, int, 0444); 235 module_param_named(dev, synth_decext.dev_name, charp, 0444); 236 module_param_named(start, synth_decext.startup, short, 0444); 237 module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444); 238 module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444); 239 module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444); 240 module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444); 241 module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444); 242 module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444); 243 module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444); 244 245 246 MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); 247 MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); 248 MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 249 MODULE_PARM_DESC(rate, "Set the rate variable on load."); 250 MODULE_PARM_DESC(pitch, "Set the pitch variable on load."); 251 MODULE_PARM_DESC(inflection, "Set the inflection variable on load."); 252 MODULE_PARM_DESC(vol, "Set the vol variable on load."); 253 MODULE_PARM_DESC(punct, "Set the punct variable on load."); 254 MODULE_PARM_DESC(voice, "Set the voice variable on load."); 255 MODULE_PARM_DESC(direct, "Set the direct variable on load."); 256 257 module_spk_synth(synth_decext); 258 259 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 260 MODULE_AUTHOR("David Borowski"); 261 MODULE_DESCRIPTION("Speakup support for DECtalk External synthesizers"); 262 MODULE_LICENSE("GPL"); 263 MODULE_VERSION(DRV_VERSION); 264 265