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, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 74static struct kobj_attribute caps_stop_attribute = 75 __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 76static struct kobj_attribute pitch_attribute = 77 __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 78static struct kobj_attribute punct_attribute = 79 __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 80static struct kobj_attribute rate_attribute = 81 __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 82static struct kobj_attribute voice_attribute = 83 __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 84static struct kobj_attribute vol_attribute = 85 __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 86 87static struct kobj_attribute delay_time_attribute = 88 __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 89static struct kobj_attribute direct_attribute = 90 __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 91static struct kobj_attribute full_time_attribute = 92 __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 93static struct kobj_attribute jiffy_delta_attribute = 94 __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 95static struct kobj_attribute trigger_time_attribute = 96 __ATTR(trigger_time, S_IWUSR|S_IRUGO, 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 = spk_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 173 rv = lastind; 174 lastind = 0; 175 return rv; 176} 177 178static void read_buff_add(u_char c) 179{ 180 static int ind = -1; 181 182 if (c == 0x01) { 183 unsigned long flags; 184 185 spin_lock_irqsave(&flush_lock, flags); 186 is_flushing = 0; 187 wake_up_interruptible(&flush); 188 spin_unlock_irqrestore(&flush_lock, flags); 189 } else if (c == 0x13) { 190 xoff = 1; 191 } else if (c == 0x11) { 192 xoff = 0; 193 } else if (is_indnum(&c)) { 194 if (ind == -1) 195 ind = c; 196 else 197 ind = ind * 10 + c; 198 } else if ((c > 31) && (c < 127)) { 199 if (ind != -1) 200 lastind = (u_char)ind; 201 ind = -1; 202 } 203} 204 205static void do_catch_up(struct spk_synth *synth) 206{ 207 int synth_full_val = 0; 208 static u_char ch; 209 static u_char last = '\0'; 210 unsigned long flags; 211 unsigned long jiff_max; 212 unsigned long timeout = msecs_to_jiffies(4000); 213 DEFINE_WAIT(wait); 214 struct var_t *jiffy_delta; 215 struct var_t *delay_time; 216 int jiffy_delta_val; 217 int delay_time_val; 218 219 jiffy_delta = spk_get_var(JIFFY); 220 delay_time = spk_get_var(DELAY); 221 spin_lock_irqsave(&speakup_info.spinlock, flags); 222 jiffy_delta_val = jiffy_delta->u.n.value; 223 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 224 jiff_max = jiffies + jiffy_delta_val; 225 226 while (!kthread_should_stop()) { 227 /* if no ctl-a in 4, send data anyway */ 228 spin_lock_irqsave(&flush_lock, flags); 229 while (is_flushing && timeout) { 230 prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE); 231 spin_unlock_irqrestore(&flush_lock, flags); 232 timeout = schedule_timeout(timeout); 233 spin_lock_irqsave(&flush_lock, flags); 234 } 235 finish_wait(&flush, &wait); 236 is_flushing = 0; 237 spin_unlock_irqrestore(&flush_lock, flags); 238 239 spin_lock_irqsave(&speakup_info.spinlock, flags); 240 if (speakup_info.flushing) { 241 speakup_info.flushing = 0; 242 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 243 synth->flush(synth); 244 continue; 245 } 246 if (synth_buffer_empty()) { 247 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 248 break; 249 } 250 ch = synth_buffer_peek(); 251 set_current_state(TASK_INTERRUPTIBLE); 252 delay_time_val = delay_time->u.n.value; 253 synth_full_val = synth_full(); 254 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 255 if (ch == '\n') 256 ch = 0x0D; 257 if (synth_full_val || !spk_serial_out(ch)) { 258 schedule_timeout(msecs_to_jiffies(delay_time_val)); 259 continue; 260 } 261 set_current_state(TASK_RUNNING); 262 spin_lock_irqsave(&speakup_info.spinlock, flags); 263 synth_buffer_getc(); 264 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 265 if (ch == '[') 266 in_escape = 1; 267 else if (ch == ']') 268 in_escape = 0; 269 else if (ch <= SPACE) { 270 if (!in_escape && strchr(",.!?;:", last)) 271 spk_serial_out(PROCSPEECH); 272 if (time_after_eq(jiffies, jiff_max)) { 273 if (!in_escape) 274 spk_serial_out(PROCSPEECH); 275 spin_lock_irqsave(&speakup_info.spinlock, 276 flags); 277 jiffy_delta_val = jiffy_delta->u.n.value; 278 delay_time_val = delay_time->u.n.value; 279 spin_unlock_irqrestore(&speakup_info.spinlock, 280 flags); 281 schedule_timeout(msecs_to_jiffies 282 (delay_time_val)); 283 jiff_max = jiffies + jiffy_delta_val; 284 } 285 } 286 last = ch; 287 } 288 if (!in_escape) 289 spk_serial_out(PROCSPEECH); 290} 291 292static void synth_flush(struct spk_synth *synth) 293{ 294 if (in_escape) { 295 /* if in command output ']' so we don't get an error */ 296 spk_serial_out(']'); 297 } 298 in_escape = 0; 299 is_flushing = 1; 300 spk_serial_out(SYNTH_CLEAR); 301} 302 303module_param_named(ser, synth_dectlk.ser, int, S_IRUGO); 304module_param_named(start, synth_dectlk.startup, short, S_IRUGO); 305 306MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); 307MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 308 309static int __init dectlk_init(void) 310{ 311 return synth_add(&synth_dectlk); 312} 313 314static void __exit dectlk_exit(void) 315{ 316 synth_remove(&synth_dectlk); 317} 318 319module_init(dectlk_init); 320module_exit(dectlk_exit); 321MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 322MODULE_AUTHOR("David Borowski"); 323MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers"); 324MODULE_LICENSE("GPL"); 325MODULE_VERSION(DRV_VERSION); 326 327