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