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