1 // SPDX-License-Identifier: GPL-2.0+
2 /* speakup.c
3 * review functions for the speakup screen review package.
4 * originally written by: Kirk Reiser and Andy Berdan.
5 *
6 * extensively modified by David Borowski.
7 *
8 ** Copyright (C) 1998 Kirk Reiser.
9 * Copyright (C) 2003 David Borowski.
10 */
11
12 #include <linux/kernel.h>
13 #include <linux/vt.h>
14 #include <linux/tty.h>
15 #include <linux/mm.h> /* __get_free_page() and friends */
16 #include <linux/vt_kern.h>
17 #include <linux/ctype.h>
18 #include <linux/selection.h>
19 #include <linux/unistd.h>
20 #include <linux/jiffies.h>
21 #include <linux/kthread.h>
22 #include <linux/keyboard.h> /* for KT_SHIFT */
23 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
24 #include <linux/input.h>
25 #include <linux/kmod.h>
26
27 /* speakup_*_selection */
28 #include <linux/module.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <linux/types.h>
32 #include <linux/consolemap.h>
33
34 #include <linux/spinlock.h>
35 #include <linux/notifier.h>
36
37 #include <linux/uaccess.h> /* copy_from|to|user() and others */
38
39 #include "spk_priv.h"
40 #include "speakup.h"
41
42 #define MAX_DELAY msecs_to_jiffies(500)
43 #define MINECHOCHAR SPACE
44
45 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
46 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
47 MODULE_DESCRIPTION("Speakup console speech");
48 MODULE_LICENSE("GPL");
49 MODULE_VERSION(SPEAKUP_VERSION);
50
51 char *synth_name;
52 module_param_named(synth, synth_name, charp, 0444);
53 module_param_named(quiet, spk_quiet_boot, bool, 0444);
54
55 MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
56 MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
57
58 special_func spk_special_handler;
59
60 short spk_pitch_shift, synth_flags;
61 static u16 buf[256];
62 int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
63 int spk_no_intr, spk_spell_delay;
64 int spk_key_echo, spk_say_word_ctl;
65 int spk_say_ctrl, spk_bell_pos;
66 short spk_punc_mask;
67 int spk_punc_level, spk_reading_punc;
68 int spk_cur_phonetic;
69 char spk_str_caps_start[MAXVARLEN + 1] = "\0";
70 char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
71 char spk_str_pause[MAXVARLEN + 1] = "\0";
72 bool spk_paused;
73 const struct st_bits_data spk_punc_info[] = {
74 {"none", "", 0},
75 {"some", "/$%&@", SOME},
76 {"most", "$%&#()=+*/@^<>|\\", MOST},
77 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
78 {"delimiters", "", B_WDLM},
79 {"repeats", "()", CH_RPT},
80 {"extended numeric", "", B_EXNUM},
81 {"symbols", "", B_SYM},
82 {NULL, NULL}
83 };
84
85 static char mark_cut_flag;
86 #define MAX_KEY 160
87 static u_char *spk_shift_table;
88 u_char *spk_our_keys[MAX_KEY];
89 u_char spk_key_buf[600];
90 const u_char spk_key_defaults[] = {
91 #include "speakupmap.h"
92 };
93
94 /* cursor track modes, must be ordered same as cursor_msgs in enum msg_index_t */
95 enum cursor_track {
96 CT_Off = 0,
97 CT_On,
98 CT_Highlight,
99 CT_Window,
100 CT_Max,
101 read_all_mode = CT_Max,
102 };
103
104 /* Speakup Cursor Track Variables */
105 static enum cursor_track cursor_track = 1, prev_cursor_track = 1;
106
107 static struct tty_struct *tty;
108
109 static void spkup_write(const u16 *in_buf, int count);
110
111 static char *phonetic[] = {
112 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
113 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
114 "papa",
115 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
116 "x ray", "yankee", "zulu"
117 };
118
119 /* array of 256 char pointers (one for each character description)
120 * initialized to default_chars and user selectable via
121 * /proc/speakup/characters
122 */
123 char *spk_characters[256];
124
125 char *spk_default_chars[256] = {
126 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
127 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
128 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
129 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
130 "control",
131 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
132 "tick",
133 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
134 "dot",
135 "slash",
136 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
137 "eight", "nine",
138 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
139 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
140 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
141 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
142 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
143 "caret",
144 "line",
145 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
146 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
147 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
148 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
149 /*127*/ "del", "control", "control", "control", "control", "control",
150 "control", "control", "control", "control", "control",
151 /*138*/ "control", "control", "control", "control", "control",
152 "control", "control", "control", "control", "control",
153 "control", "control",
154 /*150*/ "control", "control", "control", "control", "control",
155 "control", "control", "control", "control", "control",
156 /*160*/ "nbsp", "inverted bang",
157 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
158 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
159 /*172*/ "not", "soft hyphen", "registered", "macron",
160 /*176*/ "degrees", "plus or minus", "super two", "super three",
161 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
162 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
163 /*188*/ "one quarter", "one half", "three quarters",
164 "inverted question",
165 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
166 "A RING",
167 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
168 "E OOMLAUT",
169 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
170 "N TILDE",
171 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
172 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
173 "U CIRCUMFLEX",
174 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
175 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
176 /*230*/ "ae", "c cidella", "e grave", "e acute",
177 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
178 "i circumflex",
179 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
180 "o circumflex",
181 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
182 "u acute",
183 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
184 };
185
186 /* array of 256 u_short (one for each character)
187 * initialized to default_chartab and user selectable via
188 * /sys/module/speakup/parameters/chartab
189 */
190 u_short spk_chartab[256];
191
192 static u_short default_chartab[256] = {
193 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
194 B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
195 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
196 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */
197 WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */
198 PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */
199 NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */
200 NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */
201 PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */
202 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */
203 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */
204 A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */
205 PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */
206 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */
207 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */
208 ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */
209 B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
210 B_SYM, /* 135 */
211 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
212 B_CAPSYM, /* 143 */
213 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
214 B_SYM, /* 151 */
215 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
216 B_SYM, /* 159 */
217 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
218 B_SYM, /* 167 */
219 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */
220 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */
221 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */
222 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */
223 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */
224 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */
225 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */
226 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */
227 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */
228 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */
229 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */
230 };
231
232 struct task_struct *speakup_task;
233 struct bleep spk_unprocessed_sound;
234 static int spk_keydown;
235 static u16 spk_lastkey;
236 static u_char spk_close_press, keymap_flags;
237 static u_char last_keycode, this_speakup_key;
238 static u_long last_spk_jiffy;
239
240 struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
241
242 DEFINE_MUTEX(spk_mutex);
243
244 static int keyboard_notifier_call(struct notifier_block *,
245 unsigned long code, void *param);
246
247 static struct notifier_block keyboard_notifier_block = {
248 .notifier_call = keyboard_notifier_call,
249 };
250
251 static int vt_notifier_call(struct notifier_block *,
252 unsigned long code, void *param);
253
254 static struct notifier_block vt_notifier_block = {
255 .notifier_call = vt_notifier_call,
256 };
257
get_attributes(struct vc_data * vc,u16 * pos)258 static unsigned char get_attributes(struct vc_data *vc, u16 *pos)
259 {
260 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
261 return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
262 }
263
speakup_date(struct vc_data * vc)264 static void speakup_date(struct vc_data *vc)
265 {
266 spk_x = spk_cx = vc->state.x;
267 spk_y = spk_cy = vc->state.y;
268 spk_pos = spk_cp = vc->vc_pos;
269 spk_old_attr = spk_attr;
270 spk_attr = get_attributes(vc, (u_short *)spk_pos);
271 }
272
bleep(u_short val)273 static void bleep(u_short val)
274 {
275 static const short vals[] = {
276 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
277 };
278 short freq;
279 int time = spk_bleep_time;
280
281 freq = vals[val % 12];
282 if (val > 11)
283 freq *= (1 << (val / 12));
284 spk_unprocessed_sound.freq = freq;
285 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
286 spk_unprocessed_sound.active = 1;
287 /* We can only have 1 active sound at a time. */
288 }
289
speakup_shut_up(struct vc_data * vc)290 static void speakup_shut_up(struct vc_data *vc)
291 {
292 if (spk_killed)
293 return;
294 spk_shut_up |= 0x01;
295 spk_parked &= 0xfe;
296 speakup_date(vc);
297 if (synth)
298 spk_do_flush();
299 }
300
speech_kill(struct vc_data * vc)301 static void speech_kill(struct vc_data *vc)
302 {
303 char val = synth->is_alive(synth);
304
305 if (val == 0)
306 return;
307
308 /* re-enables synth, if disabled */
309 if (val == 2 || spk_killed) {
310 /* dead */
311 spk_shut_up &= ~0x40;
312 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
313 } else {
314 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
315 spk_shut_up |= 0x40;
316 }
317 }
318
speakup_off(struct vc_data * vc)319 static void speakup_off(struct vc_data *vc)
320 {
321 if (spk_shut_up & 0x80) {
322 spk_shut_up &= 0x7f;
323 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
324 } else {
325 spk_shut_up |= 0x80;
326 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
327 }
328 speakup_date(vc);
329 }
330
speakup_parked(struct vc_data * vc)331 static void speakup_parked(struct vc_data *vc)
332 {
333 if (spk_parked & 0x80) {
334 spk_parked = 0;
335 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
336 } else {
337 spk_parked |= 0x80;
338 synth_printf("%s\n", spk_msg_get(MSG_PARKED));
339 }
340 }
341
speakup_cut(struct vc_data * vc)342 static void speakup_cut(struct vc_data *vc)
343 {
344 static const char err_buf[] = "set selection failed";
345 int ret;
346
347 if (!mark_cut_flag) {
348 mark_cut_flag = 1;
349 spk_xs = (u_short)spk_x;
350 spk_ys = (u_short)spk_y;
351 spk_sel_cons = vc;
352 synth_printf("%s\n", spk_msg_get(MSG_MARK));
353 return;
354 }
355 spk_xe = (u_short)spk_x;
356 spk_ye = (u_short)spk_y;
357 mark_cut_flag = 0;
358 synth_printf("%s\n", spk_msg_get(MSG_CUT));
359
360 ret = speakup_set_selection(tty);
361
362 switch (ret) {
363 case 0:
364 break; /* no error */
365 case -EFAULT:
366 pr_warn("%sEFAULT\n", err_buf);
367 break;
368 case -EINVAL:
369 pr_warn("%sEINVAL\n", err_buf);
370 break;
371 case -ENOMEM:
372 pr_warn("%sENOMEM\n", err_buf);
373 break;
374 }
375 }
376
speakup_paste(struct vc_data * vc)377 static void speakup_paste(struct vc_data *vc)
378 {
379 if (mark_cut_flag) {
380 mark_cut_flag = 0;
381 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
382 } else {
383 synth_printf("%s\n", spk_msg_get(MSG_PASTE));
384 speakup_paste_selection(tty);
385 }
386 }
387
say_attributes(struct vc_data * vc)388 static void say_attributes(struct vc_data *vc)
389 {
390 int fg = spk_attr & 0x0f;
391 int bg = spk_attr >> 4;
392
393 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
394 if (bg > 7) {
395 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
396 bg -= 8;
397 } else {
398 synth_printf(" %s ", spk_msg_get(MSG_ON));
399 }
400 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
401 }
402
403 /* must be ordered same as edge_msgs in enum msg_index_t */
404 enum edge {
405 edge_none = 0,
406 edge_top,
407 edge_bottom,
408 edge_left,
409 edge_right,
410 edge_quiet
411 };
412
announce_edge(struct vc_data * vc,enum edge msg_id)413 static void announce_edge(struct vc_data *vc, enum edge msg_id)
414 {
415 if (spk_bleeps & 1)
416 bleep(spk_y);
417 if ((spk_bleeps & 2) && (msg_id < edge_quiet))
418 synth_printf("%s\n",
419 spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
420 }
421
speak_char(u16 ch)422 static void speak_char(u16 ch)
423 {
424 char *cp;
425 struct var_t *direct = spk_get_var(DIRECT);
426
427 if (ch >= 0x100 || (direct && direct->u.n.value)) {
428 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
429 spk_pitch_shift++;
430 synth_printf("%s", spk_str_caps_start);
431 }
432 synth_putwc_s(ch);
433 if (ch < 0x100 && IS_CHAR(ch, B_CAP))
434 synth_printf("%s", spk_str_caps_stop);
435 return;
436 }
437
438 cp = spk_characters[ch];
439 if (!cp) {
440 pr_info("%s: cp == NULL!\n", __func__);
441 return;
442 }
443 if (IS_CHAR(ch, B_CAP)) {
444 spk_pitch_shift++;
445 synth_printf("%s %s %s",
446 spk_str_caps_start, cp, spk_str_caps_stop);
447 } else {
448 if (*cp == '^') {
449 cp++;
450 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
451 } else {
452 synth_printf(" %s ", cp);
453 }
454 }
455 }
456
get_char(struct vc_data * vc,u16 * pos,u_char * attribs)457 static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
458 {
459 u16 ch = ' ';
460
461 if (vc && pos) {
462 u16 w;
463 u16 c;
464
465 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
466 w = scr_readw(pos);
467 c = w & 0xff;
468
469 if (w & vc->vc_hi_font_mask) {
470 w &= ~vc->vc_hi_font_mask;
471 c |= 0x100;
472 }
473
474 ch = inverse_translate(vc, c, true);
475 *attribs = (w & 0xff00) >> 8;
476 }
477 return ch;
478 }
479
say_char(struct vc_data * vc)480 static void say_char(struct vc_data *vc)
481 {
482 u16 ch;
483
484 spk_old_attr = spk_attr;
485 ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
486 if (spk_attr != spk_old_attr) {
487 if (spk_attrib_bleep & 1)
488 bleep(spk_y);
489 if (spk_attrib_bleep & 2)
490 say_attributes(vc);
491 }
492 speak_char(ch);
493 }
494
say_phonetic_char(struct vc_data * vc)495 static void say_phonetic_char(struct vc_data *vc)
496 {
497 u16 ch;
498
499 spk_old_attr = spk_attr;
500 ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
501 if (ch <= 0x7f && isalpha(ch)) {
502 ch &= 0x1f;
503 synth_printf("%s\n", phonetic[--ch]);
504 } else {
505 if (ch < 0x100 && IS_CHAR(ch, B_NUM))
506 synth_printf("%s ", spk_msg_get(MSG_NUMBER));
507 speak_char(ch);
508 }
509 }
510
say_prev_char(struct vc_data * vc)511 static void say_prev_char(struct vc_data *vc)
512 {
513 spk_parked |= 0x01;
514 if (spk_x == 0) {
515 announce_edge(vc, edge_left);
516 return;
517 }
518 spk_x--;
519 spk_pos -= 2;
520 say_char(vc);
521 }
522
say_next_char(struct vc_data * vc)523 static void say_next_char(struct vc_data *vc)
524 {
525 spk_parked |= 0x01;
526 if (spk_x == vc->vc_cols - 1) {
527 announce_edge(vc, edge_right);
528 return;
529 }
530 spk_x++;
531 spk_pos += 2;
532 say_char(vc);
533 }
534
535 /* get_word - will first check to see if the character under the
536 * reading cursor is a space and if spk_say_word_ctl is true it will
537 * return the word space. If spk_say_word_ctl is not set it will check to
538 * see if there is a word starting on the next position to the right
539 * and return that word if it exists. If it does not exist it will
540 * move left to the beginning of any previous word on the line or the
541 * beginning off the line whichever comes first..
542 */
543
get_word(struct vc_data * vc)544 static u_long get_word(struct vc_data *vc)
545 {
546 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
547 u16 ch;
548 u16 attr_ch;
549 u_char temp;
550
551 spk_old_attr = spk_attr;
552 ch = get_char(vc, (u_short *)tmp_pos, &temp);
553
554 /* decided to take out the sayword if on a space (mis-information */
555 if (spk_say_word_ctl && ch == SPACE) {
556 *buf = '\0';
557 synth_printf("%s\n", spk_msg_get(MSG_SPACE));
558 return 0;
559 } else if (tmpx < vc->vc_cols - 2 &&
560 (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
561 get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
562 tmp_pos += 2;
563 tmpx++;
564 } else {
565 while (tmpx > 0) {
566 ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
567 if ((ch == SPACE || ch == 0 ||
568 (ch < 0x100 && IS_WDLM(ch))) &&
569 get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
570 break;
571 tmp_pos -= 2;
572 tmpx--;
573 }
574 }
575 attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
576 buf[cnt++] = attr_ch;
577 while (tmpx < vc->vc_cols - 1) {
578 tmp_pos += 2;
579 tmpx++;
580 ch = get_char(vc, (u_short *)tmp_pos, &temp);
581 if (ch == SPACE || ch == 0 ||
582 (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
583 ch > SPACE))
584 break;
585 buf[cnt++] = ch;
586 }
587 buf[cnt] = '\0';
588 return cnt;
589 }
590
say_word(struct vc_data * vc)591 static void say_word(struct vc_data *vc)
592 {
593 u_long cnt = get_word(vc);
594 u_short saved_punc_mask = spk_punc_mask;
595
596 if (cnt == 0)
597 return;
598 spk_punc_mask = PUNC;
599 buf[cnt++] = SPACE;
600 spkup_write(buf, cnt);
601 spk_punc_mask = saved_punc_mask;
602 }
603
say_prev_word(struct vc_data * vc)604 static void say_prev_word(struct vc_data *vc)
605 {
606 u_char temp;
607 u16 ch;
608 enum edge edge_said = edge_none;
609 u_short last_state = 0, state = 0;
610
611 spk_parked |= 0x01;
612
613 if (spk_x == 0) {
614 if (spk_y == 0) {
615 announce_edge(vc, edge_top);
616 return;
617 }
618 spk_y--;
619 spk_x = vc->vc_cols;
620 edge_said = edge_quiet;
621 }
622 while (1) {
623 if (spk_x == 0) {
624 if (spk_y == 0) {
625 edge_said = edge_top;
626 break;
627 }
628 if (edge_said != edge_quiet)
629 edge_said = edge_left;
630 if (state > 0)
631 break;
632 spk_y--;
633 spk_x = vc->vc_cols - 1;
634 } else {
635 spk_x--;
636 }
637 spk_pos -= 2;
638 ch = get_char(vc, (u_short *)spk_pos, &temp);
639 if (ch == SPACE || ch == 0)
640 state = 0;
641 else if (ch < 0x100 && IS_WDLM(ch))
642 state = 1;
643 else
644 state = 2;
645 if (state < last_state) {
646 spk_pos += 2;
647 spk_x++;
648 break;
649 }
650 last_state = state;
651 }
652 if (spk_x == 0 && edge_said == edge_quiet)
653 edge_said = edge_left;
654 if (edge_said > edge_none && edge_said < edge_quiet)
655 announce_edge(vc, edge_said);
656 say_word(vc);
657 }
658
say_next_word(struct vc_data * vc)659 static void say_next_word(struct vc_data *vc)
660 {
661 u_char temp;
662 u16 ch;
663 enum edge edge_said = edge_none;
664 u_short last_state = 2, state = 0;
665
666 spk_parked |= 0x01;
667 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
668 announce_edge(vc, edge_bottom);
669 return;
670 }
671 while (1) {
672 ch = get_char(vc, (u_short *)spk_pos, &temp);
673 if (ch == SPACE || ch == 0)
674 state = 0;
675 else if (ch < 0x100 && IS_WDLM(ch))
676 state = 1;
677 else
678 state = 2;
679 if (state > last_state)
680 break;
681 if (spk_x >= vc->vc_cols - 1) {
682 if (spk_y == vc->vc_rows - 1) {
683 edge_said = edge_bottom;
684 break;
685 }
686 state = 0;
687 spk_y++;
688 spk_x = 0;
689 edge_said = edge_right;
690 } else {
691 spk_x++;
692 }
693 spk_pos += 2;
694 last_state = state;
695 }
696 if (edge_said > edge_none)
697 announce_edge(vc, edge_said);
698 say_word(vc);
699 }
700
spell_word(struct vc_data * vc)701 static void spell_word(struct vc_data *vc)
702 {
703 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
704 u16 *cp = buf;
705 char *cp1;
706 char *str_cap = spk_str_caps_stop;
707 char *last_cap = spk_str_caps_stop;
708 struct var_t *direct = spk_get_var(DIRECT);
709 u16 ch;
710
711 if (!get_word(vc))
712 return;
713 while ((ch = *cp)) {
714 if (cp != buf)
715 synth_printf(" %s ", delay_str[spk_spell_delay]);
716 /* FIXME: Non-latin1 considered as lower case */
717 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
718 str_cap = spk_str_caps_start;
719 if (*spk_str_caps_stop)
720 spk_pitch_shift++;
721 else /* synth has no pitch */
722 last_cap = spk_str_caps_stop;
723 } else {
724 str_cap = spk_str_caps_stop;
725 }
726 if (str_cap != last_cap) {
727 synth_printf("%s", str_cap);
728 last_cap = str_cap;
729 }
730 if (ch >= 0x100 || (direct && direct->u.n.value)) {
731 synth_putwc_s(ch);
732 } else if (this_speakup_key == SPELL_PHONETIC &&
733 ch <= 0x7f && isalpha(ch)) {
734 ch &= 0x1f;
735 cp1 = phonetic[--ch];
736 synth_printf("%s", cp1);
737 } else {
738 cp1 = spk_characters[ch];
739 if (*cp1 == '^') {
740 synth_printf("%s", spk_msg_get(MSG_CTRL));
741 cp1++;
742 }
743 synth_printf("%s", cp1);
744 }
745 cp++;
746 }
747 if (str_cap != spk_str_caps_stop)
748 synth_printf("%s", spk_str_caps_stop);
749 }
750
get_line(struct vc_data * vc)751 static int get_line(struct vc_data *vc)
752 {
753 u_long tmp = spk_pos - (spk_x * 2);
754 int i = 0;
755 u_char tmp2;
756
757 spk_old_attr = spk_attr;
758 spk_attr = get_attributes(vc, (u_short *)spk_pos);
759 for (i = 0; i < vc->vc_cols; i++) {
760 buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
761 tmp += 2;
762 }
763 for (--i; i >= 0; i--)
764 if (buf[i] != SPACE)
765 break;
766 return ++i;
767 }
768
say_line(struct vc_data * vc)769 static void say_line(struct vc_data *vc)
770 {
771 int i = get_line(vc);
772 u16 *cp;
773 u_short saved_punc_mask = spk_punc_mask;
774
775 if (i == 0) {
776 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
777 return;
778 }
779 buf[i++] = '\n';
780 if (this_speakup_key == SAY_LINE_INDENT) {
781 cp = buf;
782 while (*cp == SPACE)
783 cp++;
784 synth_printf("%zd, ", (cp - buf) + 1);
785 }
786 spk_punc_mask = spk_punc_masks[spk_reading_punc];
787 spkup_write(buf, i);
788 spk_punc_mask = saved_punc_mask;
789 }
790
say_prev_line(struct vc_data * vc)791 static void say_prev_line(struct vc_data *vc)
792 {
793 spk_parked |= 0x01;
794 if (spk_y == 0) {
795 announce_edge(vc, edge_top);
796 return;
797 }
798 spk_y--;
799 spk_pos -= vc->vc_size_row;
800 say_line(vc);
801 }
802
say_next_line(struct vc_data * vc)803 static void say_next_line(struct vc_data *vc)
804 {
805 spk_parked |= 0x01;
806 if (spk_y == vc->vc_rows - 1) {
807 announce_edge(vc, edge_bottom);
808 return;
809 }
810 spk_y++;
811 spk_pos += vc->vc_size_row;
812 say_line(vc);
813 }
814
say_from_to(struct vc_data * vc,u_long from,u_long to,int read_punc)815 static int say_from_to(struct vc_data *vc, u_long from, u_long to,
816 int read_punc)
817 {
818 int i = 0;
819 u_char tmp;
820 u_short saved_punc_mask = spk_punc_mask;
821
822 spk_old_attr = spk_attr;
823 spk_attr = get_attributes(vc, (u_short *)from);
824 while (from < to) {
825 buf[i++] = get_char(vc, (u_short *)from, &tmp);
826 from += 2;
827 if (i >= vc->vc_size_row)
828 break;
829 }
830 for (--i; i >= 0; i--)
831 if (buf[i] != SPACE)
832 break;
833 buf[++i] = SPACE;
834 buf[++i] = '\0';
835 if (i < 1)
836 return i;
837 if (read_punc)
838 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
839 spkup_write(buf, i);
840 if (read_punc)
841 spk_punc_mask = saved_punc_mask;
842 return i - 1;
843 }
844
say_line_from_to(struct vc_data * vc,u_long from,u_long to,int read_punc)845 static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
846 int read_punc)
847 {
848 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
849 u_long end = start + (to * 2);
850
851 start += from * 2;
852 if (say_from_to(vc, start, end, read_punc) <= 0)
853 if (cursor_track != read_all_mode)
854 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
855 }
856
857 /* Sentence Reading Commands */
858
859 static int currsentence;
860 static int numsentences[2];
861 static u16 *sentbufend[2];
862 static u16 *sentmarks[2][10];
863 static int currbuf;
864 static int bn;
865 static u16 sentbuf[2][256];
866
say_sentence_num(int num,int prev)867 static int say_sentence_num(int num, int prev)
868 {
869 bn = currbuf;
870 currsentence = num + 1;
871 if (prev && --bn == -1)
872 bn = 1;
873
874 if (num > numsentences[bn])
875 return 0;
876
877 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
878 return 1;
879 }
880
get_sentence_buf(struct vc_data * vc,int read_punc)881 static int get_sentence_buf(struct vc_data *vc, int read_punc)
882 {
883 u_long start, end;
884 int i, bn;
885 u_char tmp;
886
887 currbuf++;
888 if (currbuf == 2)
889 currbuf = 0;
890 bn = currbuf;
891 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
892 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
893
894 numsentences[bn] = 0;
895 sentmarks[bn][0] = &sentbuf[bn][0];
896 i = 0;
897 spk_old_attr = spk_attr;
898 spk_attr = get_attributes(vc, (u_short *)start);
899
900 while (start < end) {
901 sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
902 if (i > 0) {
903 if (sentbuf[bn][i] == SPACE &&
904 sentbuf[bn][i - 1] == '.' &&
905 numsentences[bn] < 9) {
906 /* Sentence Marker */
907 numsentences[bn]++;
908 sentmarks[bn][numsentences[bn]] =
909 &sentbuf[bn][i];
910 }
911 }
912 i++;
913 start += 2;
914 if (i >= vc->vc_size_row)
915 break;
916 }
917
918 for (--i; i >= 0; i--)
919 if (sentbuf[bn][i] != SPACE)
920 break;
921
922 if (i < 1)
923 return -1;
924
925 sentbuf[bn][++i] = SPACE;
926 sentbuf[bn][++i] = '\0';
927
928 sentbufend[bn] = &sentbuf[bn][i];
929 return numsentences[bn];
930 }
931
say_screen_from_to(struct vc_data * vc,u_long from,u_long to)932 static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
933 {
934 u_long start = vc->vc_origin, end;
935
936 if (from > 0)
937 start += from * vc->vc_size_row;
938 if (to > vc->vc_rows)
939 to = vc->vc_rows;
940 end = vc->vc_origin + (to * vc->vc_size_row);
941 for (from = start; from < end; from = to) {
942 to = from + vc->vc_size_row;
943 say_from_to(vc, from, to, 1);
944 }
945 }
946
say_screen(struct vc_data * vc)947 static void say_screen(struct vc_data *vc)
948 {
949 say_screen_from_to(vc, 0, vc->vc_rows);
950 }
951
speakup_win_say(struct vc_data * vc)952 static void speakup_win_say(struct vc_data *vc)
953 {
954 u_long start, end, from, to;
955
956 if (win_start < 2) {
957 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
958 return;
959 }
960 start = vc->vc_origin + (win_top * vc->vc_size_row);
961 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
962 while (start <= end) {
963 from = start + (win_left * 2);
964 to = start + (win_right * 2);
965 say_from_to(vc, from, to, 1);
966 start += vc->vc_size_row;
967 }
968 }
969
top_edge(struct vc_data * vc)970 static void top_edge(struct vc_data *vc)
971 {
972 spk_parked |= 0x01;
973 spk_pos = vc->vc_origin + 2 * spk_x;
974 spk_y = 0;
975 say_line(vc);
976 }
977
bottom_edge(struct vc_data * vc)978 static void bottom_edge(struct vc_data *vc)
979 {
980 spk_parked |= 0x01;
981 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
982 spk_y = vc->vc_rows - 1;
983 say_line(vc);
984 }
985
left_edge(struct vc_data * vc)986 static void left_edge(struct vc_data *vc)
987 {
988 spk_parked |= 0x01;
989 spk_pos -= spk_x * 2;
990 spk_x = 0;
991 say_char(vc);
992 }
993
right_edge(struct vc_data * vc)994 static void right_edge(struct vc_data *vc)
995 {
996 spk_parked |= 0x01;
997 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
998 spk_x = vc->vc_cols - 1;
999 say_char(vc);
1000 }
1001
say_first_char(struct vc_data * vc)1002 static void say_first_char(struct vc_data *vc)
1003 {
1004 int i, len = get_line(vc);
1005 u16 ch;
1006
1007 spk_parked |= 0x01;
1008 if (len == 0) {
1009 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1010 return;
1011 }
1012 for (i = 0; i < len; i++)
1013 if (buf[i] != SPACE)
1014 break;
1015 ch = buf[i];
1016 spk_pos -= (spk_x - i) * 2;
1017 spk_x = i;
1018 synth_printf("%d, ", ++i);
1019 speak_char(ch);
1020 }
1021
say_last_char(struct vc_data * vc)1022 static void say_last_char(struct vc_data *vc)
1023 {
1024 int len = get_line(vc);
1025 u16 ch;
1026
1027 spk_parked |= 0x01;
1028 if (len == 0) {
1029 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1030 return;
1031 }
1032 ch = buf[--len];
1033 spk_pos -= (spk_x - len) * 2;
1034 spk_x = len;
1035 synth_printf("%d, ", ++len);
1036 speak_char(ch);
1037 }
1038
say_position(struct vc_data * vc)1039 static void say_position(struct vc_data *vc)
1040 {
1041 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
1042 vc->vc_num + 1);
1043 synth_printf("\n");
1044 }
1045
1046 /* Added by brianb */
say_char_num(struct vc_data * vc)1047 static void say_char_num(struct vc_data *vc)
1048 {
1049 u_char tmp;
1050 u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
1051
1052 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
1053 }
1054
1055 /* these are stub functions to keep keyboard.c happy. */
1056
say_from_top(struct vc_data * vc)1057 static void say_from_top(struct vc_data *vc)
1058 {
1059 say_screen_from_to(vc, 0, spk_y);
1060 }
1061
say_to_bottom(struct vc_data * vc)1062 static void say_to_bottom(struct vc_data *vc)
1063 {
1064 say_screen_from_to(vc, spk_y, vc->vc_rows);
1065 }
1066
say_from_left(struct vc_data * vc)1067 static void say_from_left(struct vc_data *vc)
1068 {
1069 say_line_from_to(vc, 0, spk_x, 1);
1070 }
1071
say_to_right(struct vc_data * vc)1072 static void say_to_right(struct vc_data *vc)
1073 {
1074 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1075 }
1076
1077 /* end of stub functions. */
1078
spkup_write(const u16 * in_buf,int count)1079 static void spkup_write(const u16 *in_buf, int count)
1080 {
1081 static int rep_count;
1082 static u16 ch = '\0', old_ch = '\0';
1083 static u_short char_type, last_type;
1084 int in_count = count;
1085
1086 spk_keydown = 0;
1087 while (count--) {
1088 if (cursor_track == read_all_mode) {
1089 /* Insert Sentence Index */
1090 if ((in_buf == sentmarks[bn][currsentence]) &&
1091 (currsentence <= numsentences[bn]))
1092 synth_insert_next_index(currsentence++);
1093 }
1094 ch = *in_buf++;
1095 if (ch < 0x100)
1096 char_type = spk_chartab[ch];
1097 else
1098 char_type = ALPHA;
1099 if (ch == old_ch && !(char_type & B_NUM)) {
1100 if (++rep_count > 2)
1101 continue;
1102 } else {
1103 if ((last_type & CH_RPT) && rep_count > 2) {
1104 synth_printf(" ");
1105 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
1106 ++rep_count);
1107 synth_printf(" ");
1108 }
1109 rep_count = 0;
1110 }
1111 if (ch == spk_lastkey) {
1112 rep_count = 0;
1113 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
1114 speak_char(ch);
1115 } else if (char_type & B_ALPHA) {
1116 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1117 synth_buffer_add(SPACE);
1118 synth_putwc_s(ch);
1119 } else if (char_type & B_NUM) {
1120 rep_count = 0;
1121 synth_putwc_s(ch);
1122 } else if (char_type & spk_punc_mask) {
1123 speak_char(ch);
1124 char_type &= ~PUNC; /* for dec nospell processing */
1125 } else if (char_type & SYNTH_OK) {
1126 /* these are usually puncts like . and , which synth
1127 * needs for expression.
1128 * suppress multiple to get rid of long pauses and
1129 * clear repeat count
1130 * so if someone has
1131 * repeats on you don't get nothing repeated count
1132 */
1133 if (ch != old_ch)
1134 synth_putwc_s(ch);
1135 else
1136 rep_count = 0;
1137 } else {
1138 /* send space and record position, if next is num overwrite space */
1139 if (old_ch != ch)
1140 synth_buffer_add(SPACE);
1141 else
1142 rep_count = 0;
1143 }
1144 old_ch = ch;
1145 last_type = char_type;
1146 }
1147 spk_lastkey = 0;
1148 if (in_count > 2 && rep_count > 2) {
1149 if (last_type & CH_RPT) {
1150 synth_printf(" ");
1151 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1152 ++rep_count);
1153 synth_printf(" ");
1154 }
1155 rep_count = 0;
1156 }
1157 }
1158
1159 static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1160
1161 static void read_all_doc(struct vc_data *vc);
1162 static void cursor_done(struct timer_list *unused);
1163 static DEFINE_TIMER(cursor_timer, cursor_done);
1164
do_handle_shift(struct vc_data * vc,u_char value,char up_flag)1165 static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1166 {
1167 unsigned long flags;
1168
1169 if (!synth || up_flag || spk_killed)
1170 return;
1171 spin_lock_irqsave(&speakup_info.spinlock, flags);
1172 if (cursor_track == read_all_mode) {
1173 switch (value) {
1174 case KVAL(K_SHIFT):
1175 del_timer(&cursor_timer);
1176 spk_shut_up &= 0xfe;
1177 spk_do_flush();
1178 read_all_doc(vc);
1179 break;
1180 case KVAL(K_CTRL):
1181 del_timer(&cursor_timer);
1182 cursor_track = prev_cursor_track;
1183 spk_shut_up &= 0xfe;
1184 spk_do_flush();
1185 break;
1186 }
1187 } else {
1188 spk_shut_up &= 0xfe;
1189 spk_do_flush();
1190 }
1191 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1192 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
1193 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1194 }
1195
do_handle_latin(struct vc_data * vc,u_char value,char up_flag)1196 static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1197 {
1198 unsigned long flags;
1199
1200 spin_lock_irqsave(&speakup_info.spinlock, flags);
1201 if (up_flag) {
1202 spk_lastkey = 0;
1203 spk_keydown = 0;
1204 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1205 return;
1206 }
1207 if (!synth || spk_killed) {
1208 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1209 return;
1210 }
1211 spk_shut_up &= 0xfe;
1212 spk_lastkey = value;
1213 spk_keydown++;
1214 spk_parked &= 0xfe;
1215 if (spk_key_echo == 2 && value >= MINECHOCHAR)
1216 speak_char(value);
1217 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1218 }
1219
spk_set_key_info(const u_char * key_info,u_char * k_buffer)1220 int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
1221 {
1222 int i = 0, states, key_data_len;
1223 const u_char *cp = key_info;
1224 u_char *cp1 = k_buffer;
1225 u_char ch, version, num_keys;
1226
1227 version = *cp++;
1228 if (version != KEY_MAP_VER) {
1229 pr_debug("version found %d should be %d\n",
1230 version, KEY_MAP_VER);
1231 return -EINVAL;
1232 }
1233 num_keys = *cp;
1234 states = (int)cp[1];
1235 key_data_len = (states + 1) * (num_keys + 1);
1236 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
1237 pr_debug("too many key_infos (%d over %u)\n",
1238 key_data_len + SHIFT_TBL_SIZE + 4,
1239 (unsigned int)(sizeof(spk_key_buf)));
1240 return -EINVAL;
1241 }
1242 memset(k_buffer, 0, SHIFT_TBL_SIZE);
1243 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1244 spk_shift_table = k_buffer;
1245 spk_our_keys[0] = spk_shift_table;
1246 cp1 += SHIFT_TBL_SIZE;
1247 memcpy(cp1, cp, key_data_len + 3);
1248 /* get num_keys, states and data */
1249 cp1 += 2; /* now pointing at shift states */
1250 for (i = 1; i <= states; i++) {
1251 ch = *cp1++;
1252 if (ch >= SHIFT_TBL_SIZE) {
1253 pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
1254 ch, SHIFT_TBL_SIZE);
1255 return -EINVAL;
1256 }
1257 spk_shift_table[ch] = i;
1258 }
1259 keymap_flags = *cp1++;
1260 while ((ch = *cp1)) {
1261 if (ch >= MAX_KEY) {
1262 pr_debug("(%d), not valid key, (max_allowed = %d)\n",
1263 ch, MAX_KEY);
1264 return -EINVAL;
1265 }
1266 spk_our_keys[ch] = cp1;
1267 cp1 += states + 1;
1268 }
1269 return 0;
1270 }
1271
1272 enum spk_vars_id {
1273 BELL_POS_ID = 0, SPELL_DELAY_ID, ATTRIB_BLEEP_ID,
1274 BLEEPS_ID, BLEEP_TIME_ID, PUNC_LEVEL_ID,
1275 READING_PUNC_ID, CURSOR_TIME_ID, SAY_CONTROL_ID,
1276 SAY_WORD_CTL_ID, NO_INTERRUPT_ID, KEY_ECHO_ID,
1277 CUR_PHONETIC_ID, V_LAST_VAR_ID, NB_ID
1278 };
1279
1280 static struct var_t spk_vars[NB_ID] = {
1281 /* bell must be first to set high limit */
1282 [BELL_POS_ID] = { BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1283 [SPELL_DELAY_ID] = { SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1284 [ATTRIB_BLEEP_ID] = { ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1285 [BLEEPS_ID] = { BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1286 [BLEEP_TIME_ID] = { BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1287 [PUNC_LEVEL_ID] = { PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1288 [READING_PUNC_ID] = { READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1289 [CURSOR_TIME_ID] = { CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1290 [SAY_CONTROL_ID] = { SAY_CONTROL, TOGGLE_0},
1291 [SAY_WORD_CTL_ID] = {SAY_WORD_CTL, TOGGLE_0},
1292 [NO_INTERRUPT_ID] = { NO_INTERRUPT, TOGGLE_0},
1293 [KEY_ECHO_ID] = { KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
1294 [CUR_PHONETIC_ID] = { CUR_PHONETIC, .u.n = {NULL, 0, 0, 1, 0, 0, NULL} },
1295 V_LAST_VAR
1296 };
1297
toggle_cursoring(struct vc_data * vc)1298 static void toggle_cursoring(struct vc_data *vc)
1299 {
1300 if (cursor_track == read_all_mode)
1301 cursor_track = prev_cursor_track;
1302 if (++cursor_track >= CT_Max)
1303 cursor_track = 0;
1304 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
1305 }
1306
spk_reset_default_chars(void)1307 void spk_reset_default_chars(void)
1308 {
1309 int i;
1310
1311 /* First, free any non-default */
1312 for (i = 0; i < 256; i++) {
1313 if (spk_characters[i] &&
1314 (spk_characters[i] != spk_default_chars[i]))
1315 kfree(spk_characters[i]);
1316 }
1317
1318 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
1319 }
1320
spk_reset_default_chartab(void)1321 void spk_reset_default_chartab(void)
1322 {
1323 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1324 }
1325
1326 static const struct st_bits_data *pb_edit;
1327
edit_bits(struct vc_data * vc,u_char type,u_char ch,u_short key)1328 static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1329 {
1330 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
1331
1332 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
1333 return -1;
1334 if (ch == SPACE) {
1335 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1336 spk_special_handler = NULL;
1337 return 1;
1338 }
1339 if (mask < PUNC && !(ch_type & PUNC))
1340 return -1;
1341 spk_chartab[ch] ^= mask;
1342 speak_char(ch);
1343 synth_printf(" %s\n",
1344 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1345 spk_msg_get(MSG_OFF));
1346 return 1;
1347 }
1348
1349 /* Allocation concurrency is protected by the console semaphore */
speakup_allocate(struct vc_data * vc,gfp_t gfp_flags)1350 static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
1351 {
1352 int vc_num;
1353
1354 vc_num = vc->vc_num;
1355 if (!speakup_console[vc_num]) {
1356 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
1357 gfp_flags);
1358 if (!speakup_console[vc_num])
1359 return -ENOMEM;
1360 speakup_date(vc);
1361 } else if (!spk_parked) {
1362 speakup_date(vc);
1363 }
1364
1365 return 0;
1366 }
1367
speakup_deallocate(struct vc_data * vc)1368 static void speakup_deallocate(struct vc_data *vc)
1369 {
1370 int vc_num;
1371
1372 vc_num = vc->vc_num;
1373 kfree(speakup_console[vc_num]);
1374 speakup_console[vc_num] = NULL;
1375 }
1376
1377 enum read_all_command {
1378 RA_NEXT_SENT = KVAL(K_DOWN)+1,
1379 RA_PREV_LINE = KVAL(K_LEFT)+1,
1380 RA_NEXT_LINE = KVAL(K_RIGHT)+1,
1381 RA_PREV_SENT = KVAL(K_UP)+1,
1382 RA_DOWN_ARROW,
1383 RA_TIMER,
1384 RA_FIND_NEXT_SENT,
1385 RA_FIND_PREV_SENT,
1386 };
1387
1388 static u_char is_cursor;
1389 static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1390 static int cursor_con;
1391
1392 static void reset_highlight_buffers(struct vc_data *);
1393
1394 static enum read_all_command read_all_key;
1395
1396 static int in_keyboard_notifier;
1397
1398 static void start_read_all_timer(struct vc_data *vc, enum read_all_command command);
1399
kbd_fakekey2(struct vc_data * vc,enum read_all_command command)1400 static void kbd_fakekey2(struct vc_data *vc, enum read_all_command command)
1401 {
1402 del_timer(&cursor_timer);
1403 speakup_fake_down_arrow();
1404 start_read_all_timer(vc, command);
1405 }
1406
read_all_doc(struct vc_data * vc)1407 static void read_all_doc(struct vc_data *vc)
1408 {
1409 if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
1410 return;
1411 if (!synth_supports_indexing())
1412 return;
1413 if (cursor_track != read_all_mode)
1414 prev_cursor_track = cursor_track;
1415 cursor_track = read_all_mode;
1416 spk_reset_index_count(0);
1417 if (get_sentence_buf(vc, 0) == -1) {
1418 del_timer(&cursor_timer);
1419 if (!in_keyboard_notifier)
1420 speakup_fake_down_arrow();
1421 start_read_all_timer(vc, RA_DOWN_ARROW);
1422 } else {
1423 say_sentence_num(0, 0);
1424 synth_insert_next_index(0);
1425 start_read_all_timer(vc, RA_TIMER);
1426 }
1427 }
1428
stop_read_all(struct vc_data * vc)1429 static void stop_read_all(struct vc_data *vc)
1430 {
1431 del_timer(&cursor_timer);
1432 cursor_track = prev_cursor_track;
1433 spk_shut_up &= 0xfe;
1434 spk_do_flush();
1435 }
1436
start_read_all_timer(struct vc_data * vc,enum read_all_command command)1437 static void start_read_all_timer(struct vc_data *vc, enum read_all_command command)
1438 {
1439 struct var_t *cursor_timeout;
1440
1441 cursor_con = vc->vc_num;
1442 read_all_key = command;
1443 cursor_timeout = spk_get_var(CURSOR_TIME);
1444 mod_timer(&cursor_timer,
1445 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1446 }
1447
handle_cursor_read_all(struct vc_data * vc,enum read_all_command command)1448 static void handle_cursor_read_all(struct vc_data *vc, enum read_all_command command)
1449 {
1450 int indcount, sentcount, rv, sn;
1451
1452 switch (command) {
1453 case RA_NEXT_SENT:
1454 /* Get Current Sentence */
1455 spk_get_index_count(&indcount, &sentcount);
1456 /*printk("%d %d ", indcount, sentcount); */
1457 spk_reset_index_count(sentcount + 1);
1458 if (indcount == 1) {
1459 if (!say_sentence_num(sentcount + 1, 0)) {
1460 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1461 return;
1462 }
1463 synth_insert_next_index(0);
1464 } else {
1465 sn = 0;
1466 if (!say_sentence_num(sentcount + 1, 1)) {
1467 sn = 1;
1468 spk_reset_index_count(sn);
1469 } else {
1470 synth_insert_next_index(0);
1471 }
1472 if (!say_sentence_num(sn, 0)) {
1473 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1474 return;
1475 }
1476 synth_insert_next_index(0);
1477 }
1478 start_read_all_timer(vc, RA_TIMER);
1479 break;
1480 case RA_PREV_SENT:
1481 break;
1482 case RA_NEXT_LINE:
1483 read_all_doc(vc);
1484 break;
1485 case RA_PREV_LINE:
1486 break;
1487 case RA_DOWN_ARROW:
1488 if (get_sentence_buf(vc, 0) == -1) {
1489 kbd_fakekey2(vc, RA_DOWN_ARROW);
1490 } else {
1491 say_sentence_num(0, 0);
1492 synth_insert_next_index(0);
1493 start_read_all_timer(vc, RA_TIMER);
1494 }
1495 break;
1496 case RA_FIND_NEXT_SENT:
1497 rv = get_sentence_buf(vc, 0);
1498 if (rv == -1)
1499 read_all_doc(vc);
1500 if (rv == 0) {
1501 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1502 } else {
1503 say_sentence_num(1, 0);
1504 synth_insert_next_index(0);
1505 start_read_all_timer(vc, RA_TIMER);
1506 }
1507 break;
1508 case RA_FIND_PREV_SENT:
1509 break;
1510 case RA_TIMER:
1511 spk_get_index_count(&indcount, &sentcount);
1512 if (indcount < 2)
1513 kbd_fakekey2(vc, RA_DOWN_ARROW);
1514 else
1515 start_read_all_timer(vc, RA_TIMER);
1516 break;
1517 }
1518 }
1519
pre_handle_cursor(struct vc_data * vc,u_char value,char up_flag)1520 static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1521 {
1522 unsigned long flags;
1523
1524 spin_lock_irqsave(&speakup_info.spinlock, flags);
1525 if (cursor_track == read_all_mode) {
1526 spk_parked &= 0xfe;
1527 if (!synth || up_flag || spk_shut_up) {
1528 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1529 return NOTIFY_STOP;
1530 }
1531 del_timer(&cursor_timer);
1532 spk_shut_up &= 0xfe;
1533 spk_do_flush();
1534 start_read_all_timer(vc, value + 1);
1535 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1536 return NOTIFY_STOP;
1537 }
1538 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1539 return NOTIFY_OK;
1540 }
1541
do_handle_cursor(struct vc_data * vc,u_char value,char up_flag)1542 static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1543 {
1544 unsigned long flags;
1545 struct var_t *cursor_timeout;
1546
1547 spin_lock_irqsave(&speakup_info.spinlock, flags);
1548 spk_parked &= 0xfe;
1549 if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
1550 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1551 return;
1552 }
1553 spk_shut_up &= 0xfe;
1554 if (spk_no_intr)
1555 spk_do_flush();
1556 /* the key press flushes if !no_inter but we want to flush on cursor
1557 * moves regardless of no_inter state
1558 */
1559 is_cursor = value + 1;
1560 old_cursor_pos = vc->vc_pos;
1561 old_cursor_x = vc->state.x;
1562 old_cursor_y = vc->state.y;
1563 speakup_console[vc->vc_num]->ht.cy = vc->state.y;
1564 cursor_con = vc->vc_num;
1565 if (cursor_track == CT_Highlight)
1566 reset_highlight_buffers(vc);
1567 cursor_timeout = spk_get_var(CURSOR_TIME);
1568 mod_timer(&cursor_timer,
1569 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1570 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1571 }
1572
update_color_buffer(struct vc_data * vc,const u16 * ic,int len)1573 static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
1574 {
1575 int i, bi, hi;
1576 int vc_num = vc->vc_num;
1577
1578 bi = (vc->vc_attr & 0x70) >> 4;
1579 hi = speakup_console[vc_num]->ht.highsize[bi];
1580
1581 i = 0;
1582 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1583 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1584 speakup_console[vc_num]->ht.rx[bi] = vc->state.x;
1585 speakup_console[vc_num]->ht.ry[bi] = vc->state.y;
1586 }
1587 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1588 if (ic[i] > 32) {
1589 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1590 hi++;
1591 } else if ((ic[i] == 32) && (hi != 0)) {
1592 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1593 32) {
1594 speakup_console[vc_num]->ht.highbuf[bi][hi] =
1595 ic[i];
1596 hi++;
1597 }
1598 }
1599 i++;
1600 }
1601 speakup_console[vc_num]->ht.highsize[bi] = hi;
1602 }
1603
reset_highlight_buffers(struct vc_data * vc)1604 static void reset_highlight_buffers(struct vc_data *vc)
1605 {
1606 int i;
1607 int vc_num = vc->vc_num;
1608
1609 for (i = 0; i < 8; i++)
1610 speakup_console[vc_num]->ht.highsize[i] = 0;
1611 }
1612
count_highlight_color(struct vc_data * vc)1613 static int count_highlight_color(struct vc_data *vc)
1614 {
1615 int i, bg;
1616 int cc;
1617 int vc_num = vc->vc_num;
1618 u16 ch;
1619 u16 *start = (u16 *)vc->vc_origin;
1620
1621 for (i = 0; i < 8; i++)
1622 speakup_console[vc_num]->ht.bgcount[i] = 0;
1623
1624 for (i = 0; i < vc->vc_rows; i++) {
1625 u16 *end = start + vc->vc_cols * 2;
1626 u16 *ptr;
1627
1628 for (ptr = start; ptr < end; ptr++) {
1629 ch = get_attributes(vc, ptr);
1630 bg = (ch & 0x70) >> 4;
1631 speakup_console[vc_num]->ht.bgcount[bg]++;
1632 }
1633 start += vc->vc_size_row;
1634 }
1635
1636 cc = 0;
1637 for (i = 0; i < 8; i++)
1638 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1639 cc++;
1640 return cc;
1641 }
1642
get_highlight_color(struct vc_data * vc)1643 static int get_highlight_color(struct vc_data *vc)
1644 {
1645 int i, j;
1646 unsigned int cptr[8];
1647 int vc_num = vc->vc_num;
1648
1649 for (i = 0; i < 8; i++)
1650 cptr[i] = i;
1651
1652 for (i = 0; i < 7; i++)
1653 for (j = i + 1; j < 8; j++)
1654 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
1655 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1656 swap(cptr[i], cptr[j]);
1657
1658 for (i = 0; i < 8; i++)
1659 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1660 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1661 return cptr[i];
1662 return -1;
1663 }
1664
speak_highlight(struct vc_data * vc)1665 static int speak_highlight(struct vc_data *vc)
1666 {
1667 int hc, d;
1668 int vc_num = vc->vc_num;
1669
1670 if (count_highlight_color(vc) == 1)
1671 return 0;
1672 hc = get_highlight_color(vc);
1673 if (hc != -1) {
1674 d = vc->state.y - speakup_console[vc_num]->ht.cy;
1675 if ((d == 1) || (d == -1))
1676 if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y)
1677 return 0;
1678 spk_parked |= 0x01;
1679 spk_do_flush();
1680 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
1681 speakup_console[vc_num]->ht.highsize[hc]);
1682 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1683 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1684 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1685 return 1;
1686 }
1687 return 0;
1688 }
1689
cursor_done(struct timer_list * unused)1690 static void cursor_done(struct timer_list *unused)
1691 {
1692 struct vc_data *vc = vc_cons[cursor_con].d;
1693 unsigned long flags;
1694
1695 del_timer(&cursor_timer);
1696 spin_lock_irqsave(&speakup_info.spinlock, flags);
1697 if (cursor_con != fg_console) {
1698 is_cursor = 0;
1699 goto out;
1700 }
1701 speakup_date(vc);
1702 if (win_enabled) {
1703 if (vc->state.x >= win_left && vc->state.x <= win_right &&
1704 vc->state.y >= win_top && vc->state.y <= win_bottom) {
1705 spk_keydown = 0;
1706 is_cursor = 0;
1707 goto out;
1708 }
1709 }
1710 if (cursor_track == read_all_mode) {
1711 handle_cursor_read_all(vc, read_all_key);
1712 goto out;
1713 }
1714 if (cursor_track == CT_Highlight) {
1715 if (speak_highlight(vc)) {
1716 spk_keydown = 0;
1717 is_cursor = 0;
1718 goto out;
1719 }
1720 }
1721 if (cursor_track == CT_Window)
1722 speakup_win_say(vc);
1723 else if (is_cursor == 1 || is_cursor == 4)
1724 say_line_from_to(vc, 0, vc->vc_cols, 0);
1725 else {
1726 if (spk_cur_phonetic == 1)
1727 say_phonetic_char(vc);
1728 else
1729 say_char(vc);
1730 }
1731 spk_keydown = 0;
1732 is_cursor = 0;
1733 out:
1734 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1735 }
1736
1737 /* called by: vt_notifier_call() */
speakup_bs(struct vc_data * vc)1738 static void speakup_bs(struct vc_data *vc)
1739 {
1740 unsigned long flags;
1741
1742 if (!speakup_console[vc->vc_num])
1743 return;
1744 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1745 /* Speakup output, discard */
1746 return;
1747 if (!spk_parked)
1748 speakup_date(vc);
1749 if (spk_shut_up || !synth) {
1750 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1751 return;
1752 }
1753 if (vc->vc_num == fg_console && spk_keydown) {
1754 spk_keydown = 0;
1755 if (!is_cursor)
1756 say_char(vc);
1757 }
1758 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1759 }
1760
1761 /* called by: vt_notifier_call() */
speakup_con_write(struct vc_data * vc,u16 * str,int len)1762 static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
1763 {
1764 unsigned long flags;
1765
1766 if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
1767 return;
1768 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1769 /* Speakup output, discard */
1770 return;
1771 if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1))
1772 bleep(3);
1773 if ((is_cursor) || (cursor_track == read_all_mode)) {
1774 if (cursor_track == CT_Highlight)
1775 update_color_buffer(vc, str, len);
1776 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1777 return;
1778 }
1779 if (win_enabled) {
1780 if (vc->state.x >= win_left && vc->state.x <= win_right &&
1781 vc->state.y >= win_top && vc->state.y <= win_bottom) {
1782 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1783 return;
1784 }
1785 }
1786
1787 spkup_write(str, len);
1788 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1789 }
1790
speakup_con_update(struct vc_data * vc)1791 static void speakup_con_update(struct vc_data *vc)
1792 {
1793 unsigned long flags;
1794
1795 if (!speakup_console[vc->vc_num] || spk_parked || !synth)
1796 return;
1797 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1798 /* Speakup output, discard */
1799 return;
1800 speakup_date(vc);
1801 if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {
1802 synth_printf("%s", spk_str_pause);
1803 spk_paused = true;
1804 }
1805 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1806 }
1807
do_handle_spec(struct vc_data * vc,u_char value,char up_flag)1808 static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1809 {
1810 unsigned long flags;
1811 int on_off = 2;
1812 char *label;
1813
1814 if (!synth || up_flag || spk_killed)
1815 return;
1816 spin_lock_irqsave(&speakup_info.spinlock, flags);
1817 spk_shut_up &= 0xfe;
1818 if (spk_no_intr)
1819 spk_do_flush();
1820 switch (value) {
1821 case KVAL(K_CAPS):
1822 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
1823 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
1824 break;
1825 case KVAL(K_NUM):
1826 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
1827 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
1828 break;
1829 case KVAL(K_HOLD):
1830 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
1831 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
1832 if (speakup_console[vc->vc_num])
1833 speakup_console[vc->vc_num]->tty_stopped = on_off;
1834 break;
1835 default:
1836 spk_parked &= 0xfe;
1837 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1838 return;
1839 }
1840 if (on_off < 2)
1841 synth_printf("%s %s\n",
1842 label, spk_msg_get(MSG_STATUS_START + on_off));
1843 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1844 }
1845
inc_dec_var(u_char value)1846 static int inc_dec_var(u_char value)
1847 {
1848 struct st_var_header *p_header;
1849 struct var_t *var_data;
1850 char num_buf[32];
1851 char *cp = num_buf;
1852 char *pn;
1853 int var_id = (int)value - VAR_START;
1854 int how = (var_id & 1) ? E_INC : E_DEC;
1855
1856 var_id = var_id / 2 + FIRST_SET_VAR;
1857 p_header = spk_get_var_header(var_id);
1858 if (!p_header)
1859 return -1;
1860 if (p_header->var_type != VAR_NUM)
1861 return -1;
1862 var_data = p_header->data;
1863 if (spk_set_num_var(1, p_header, how) != 0)
1864 return -1;
1865 if (!spk_close_press) {
1866 for (pn = p_header->name; *pn; pn++) {
1867 if (*pn == '_')
1868 *cp = SPACE;
1869 else
1870 *cp++ = *pn;
1871 }
1872 }
1873 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
1874 var_data->u.n.value);
1875 synth_printf("%s", num_buf);
1876 return 0;
1877 }
1878
speakup_win_set(struct vc_data * vc)1879 static void speakup_win_set(struct vc_data *vc)
1880 {
1881 char info[40];
1882
1883 if (win_start > 1) {
1884 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
1885 return;
1886 }
1887 if (spk_x < win_left || spk_y < win_top) {
1888 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
1889 return;
1890 }
1891 if (win_start && spk_x == win_left && spk_y == win_top) {
1892 win_left = 0;
1893 win_right = vc->vc_cols - 1;
1894 win_bottom = spk_y;
1895 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
1896 (int)win_top + 1);
1897 } else {
1898 if (!win_start) {
1899 win_top = spk_y;
1900 win_left = spk_x;
1901 } else {
1902 win_bottom = spk_y;
1903 win_right = spk_x;
1904 }
1905 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
1906 (win_start) ?
1907 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
1908 (int)spk_y + 1, (int)spk_x + 1);
1909 }
1910 synth_printf("%s\n", info);
1911 win_start++;
1912 }
1913
speakup_win_clear(struct vc_data * vc)1914 static void speakup_win_clear(struct vc_data *vc)
1915 {
1916 win_top = 0;
1917 win_bottom = 0;
1918 win_left = 0;
1919 win_right = 0;
1920 win_start = 0;
1921 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
1922 }
1923
speakup_win_enable(struct vc_data * vc)1924 static void speakup_win_enable(struct vc_data *vc)
1925 {
1926 if (win_start < 2) {
1927 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
1928 return;
1929 }
1930 win_enabled ^= 1;
1931 if (win_enabled)
1932 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
1933 else
1934 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
1935 }
1936
speakup_bits(struct vc_data * vc)1937 static void speakup_bits(struct vc_data *vc)
1938 {
1939 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
1940
1941 if (spk_special_handler || val < 1 || val > 6) {
1942 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
1943 return;
1944 }
1945 pb_edit = &spk_punc_info[val];
1946 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1947 spk_special_handler = edit_bits;
1948 }
1949
handle_goto(struct vc_data * vc,u_char type,u_char ch,u_short key)1950 static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1951 {
1952 static u_char goto_buf[8];
1953 static int num;
1954 int maxlen;
1955 char *cp;
1956 u16 wch;
1957
1958 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1959 goto do_goto;
1960 if (type == KT_LATIN && ch == '\n')
1961 goto do_goto;
1962 if (type != 0)
1963 goto oops;
1964 if (ch == 8) {
1965 u16 wch;
1966
1967 if (num == 0)
1968 return -1;
1969 wch = goto_buf[--num];
1970 goto_buf[num] = '\0';
1971 spkup_write(&wch, 1);
1972 return 1;
1973 }
1974 if (ch < '+' || ch > 'y')
1975 goto oops;
1976 wch = ch;
1977 goto_buf[num++] = ch;
1978 goto_buf[num] = '\0';
1979 spkup_write(&wch, 1);
1980 maxlen = (*goto_buf >= '0') ? 3 : 4;
1981 if ((ch == '+' || ch == '-') && num == 1)
1982 return 1;
1983 if (ch >= '0' && ch <= '9' && num < maxlen)
1984 return 1;
1985 if (num < maxlen - 1 || num > maxlen)
1986 goto oops;
1987 if (ch < 'x' || ch > 'y') {
1988 oops:
1989 if (!spk_killed)
1990 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
1991 goto_buf[num = 0] = '\0';
1992 spk_special_handler = NULL;
1993 return 1;
1994 }
1995
1996 /* Do not replace with kstrtoul: here we need cp to be updated */
1997 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1998
1999 if (*cp == 'x') {
2000 if (*goto_buf < '0')
2001 goto_pos += spk_x;
2002 else if (goto_pos > 0)
2003 goto_pos--;
2004
2005 if (goto_pos >= vc->vc_cols)
2006 goto_pos = vc->vc_cols - 1;
2007 goto_x = 1;
2008 } else {
2009 if (*goto_buf < '0')
2010 goto_pos += spk_y;
2011 else if (goto_pos > 0)
2012 goto_pos--;
2013
2014 if (goto_pos >= vc->vc_rows)
2015 goto_pos = vc->vc_rows - 1;
2016 goto_x = 0;
2017 }
2018 goto_buf[num = 0] = '\0';
2019 do_goto:
2020 spk_special_handler = NULL;
2021 spk_parked |= 0x01;
2022 if (goto_x) {
2023 spk_pos -= spk_x * 2;
2024 spk_x = goto_pos;
2025 spk_pos += goto_pos * 2;
2026 say_word(vc);
2027 } else {
2028 spk_y = goto_pos;
2029 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
2030 say_line(vc);
2031 }
2032 return 1;
2033 }
2034
speakup_goto(struct vc_data * vc)2035 static void speakup_goto(struct vc_data *vc)
2036 {
2037 if (spk_special_handler) {
2038 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
2039 return;
2040 }
2041 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
2042 spk_special_handler = handle_goto;
2043 }
2044
speakup_help(struct vc_data * vc)2045 static void speakup_help(struct vc_data *vc)
2046 {
2047 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
2048 }
2049
do_nothing(struct vc_data * vc)2050 static void do_nothing(struct vc_data *vc)
2051 {
2052 return; /* flush done in do_spkup */
2053 }
2054
2055 static u_char key_speakup, spk_key_locked;
2056
speakup_lock(struct vc_data * vc)2057 static void speakup_lock(struct vc_data *vc)
2058 {
2059 if (!spk_key_locked) {
2060 spk_key_locked = 16;
2061 key_speakup = 16;
2062 } else {
2063 spk_key_locked = 0;
2064 key_speakup = 0;
2065 }
2066 }
2067
2068 typedef void (*spkup_hand) (struct vc_data *);
2069 static spkup_hand spkup_handler[] = {
2070 /* must be ordered same as defines in speakup.h */
2071 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2072 speakup_cut, speakup_paste, say_first_char, say_last_char,
2073 say_char, say_prev_char, say_next_char,
2074 say_word, say_prev_word, say_next_word,
2075 say_line, say_prev_line, say_next_line,
2076 top_edge, bottom_edge, left_edge, right_edge,
2077 spell_word, spell_word, say_screen,
2078 say_position, say_attributes,
2079 speakup_off, speakup_parked, say_line, /* this is for indent */
2080 say_from_top, say_to_bottom,
2081 say_from_left, say_to_right,
2082 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2083 speakup_bits, speakup_bits, speakup_bits,
2084 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2085 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2086 };
2087
do_spkup(struct vc_data * vc,u_char value)2088 static void do_spkup(struct vc_data *vc, u_char value)
2089 {
2090 if (spk_killed && value != SPEECH_KILL)
2091 return;
2092 spk_keydown = 0;
2093 spk_lastkey = 0;
2094 spk_shut_up &= 0xfe;
2095 this_speakup_key = value;
2096 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
2097 spk_do_flush();
2098 (*spkup_handler[value]) (vc);
2099 } else {
2100 if (inc_dec_var(value) < 0)
2101 bleep(9);
2102 }
2103 }
2104
2105 static const char *pad_chars = "0123456789+-*/\015,.?()";
2106
2107 static int
speakup_key(struct vc_data * vc,int shift_state,int keycode,u_short keysym,int up_flag)2108 speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
2109 int up_flag)
2110 {
2111 unsigned long flags;
2112 int kh;
2113 u_char *key_info;
2114 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2115 u_char shift_info, offset;
2116 int ret = 0;
2117
2118 if (!synth)
2119 return 0;
2120
2121 spin_lock_irqsave(&speakup_info.spinlock, flags);
2122 tty = vc->port.tty;
2123 if (type >= 0xf0)
2124 type -= 0xf0;
2125 if (type == KT_PAD &&
2126 (vt_get_leds(fg_console, VC_NUMLOCK))) {
2127 if (up_flag) {
2128 spk_keydown = 0;
2129 goto out;
2130 }
2131 value = pad_chars[value];
2132 spk_lastkey = value;
2133 spk_keydown++;
2134 spk_parked &= 0xfe;
2135 goto no_map;
2136 }
2137 if (keycode >= MAX_KEY)
2138 goto no_map;
2139 key_info = spk_our_keys[keycode];
2140 if (!key_info)
2141 goto no_map;
2142 /* Check valid read all mode keys */
2143 if ((cursor_track == read_all_mode) && (!up_flag)) {
2144 switch (value) {
2145 case KVAL(K_DOWN):
2146 case KVAL(K_UP):
2147 case KVAL(K_LEFT):
2148 case KVAL(K_RIGHT):
2149 case KVAL(K_PGUP):
2150 case KVAL(K_PGDN):
2151 break;
2152 default:
2153 stop_read_all(vc);
2154 break;
2155 }
2156 }
2157 shift_info = (shift_state & 0x0f) + key_speakup;
2158 offset = spk_shift_table[shift_info];
2159 if (offset) {
2160 new_key = key_info[offset];
2161 if (new_key) {
2162 ret = 1;
2163 if (new_key == SPK_KEY) {
2164 if (!spk_key_locked)
2165 key_speakup = (up_flag) ? 0 : 16;
2166 if (up_flag || spk_killed)
2167 goto out;
2168 spk_shut_up &= 0xfe;
2169 spk_do_flush();
2170 goto out;
2171 }
2172 if (up_flag)
2173 goto out;
2174 if (last_keycode == keycode &&
2175 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
2176 spk_close_press = 1;
2177 offset = spk_shift_table[shift_info + 32];
2178 /* double press? */
2179 if (offset && key_info[offset])
2180 new_key = key_info[offset];
2181 }
2182 last_keycode = keycode;
2183 last_spk_jiffy = jiffies;
2184 type = KT_SPKUP;
2185 value = new_key;
2186 }
2187 }
2188 no_map:
2189 if (type == KT_SPKUP && !spk_special_handler) {
2190 do_spkup(vc, new_key);
2191 spk_close_press = 0;
2192 ret = 1;
2193 goto out;
2194 }
2195 if (up_flag || spk_killed || type == KT_SHIFT)
2196 goto out;
2197 spk_shut_up &= 0xfe;
2198 kh = (value == KVAL(K_DOWN)) ||
2199 (value == KVAL(K_UP)) ||
2200 (value == KVAL(K_LEFT)) ||
2201 (value == KVAL(K_RIGHT));
2202 if ((cursor_track != read_all_mode) || !kh)
2203 if (!spk_no_intr)
2204 spk_do_flush();
2205 if (spk_special_handler) {
2206 if (type == KT_SPEC && value == 1) {
2207 value = '\n';
2208 type = KT_LATIN;
2209 } else if (type == KT_LETTER) {
2210 type = KT_LATIN;
2211 } else if (value == 0x7f) {
2212 value = 8; /* make del = backspace */
2213 }
2214 ret = (*spk_special_handler) (vc, type, value, keycode);
2215 spk_close_press = 0;
2216 if (ret < 0)
2217 bleep(9);
2218 goto out;
2219 }
2220 last_keycode = 0;
2221 out:
2222 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2223 return ret;
2224 }
2225
keyboard_notifier_call(struct notifier_block * nb,unsigned long code,void * _param)2226 static int keyboard_notifier_call(struct notifier_block *nb,
2227 unsigned long code, void *_param)
2228 {
2229 struct keyboard_notifier_param *param = _param;
2230 struct vc_data *vc = param->vc;
2231 int up = !param->down;
2232 int ret = NOTIFY_OK;
2233 static int keycode; /* to hold the current keycode */
2234
2235 in_keyboard_notifier = 1;
2236
2237 if (vc->vc_mode == KD_GRAPHICS)
2238 goto out;
2239
2240 /*
2241 * First, determine whether we are handling a fake keypress on
2242 * the current processor. If we are, then return NOTIFY_OK,
2243 * to pass the keystroke up the chain. This prevents us from
2244 * trying to take the Speakup lock while it is held by the
2245 * processor on which the simulated keystroke was generated.
2246 * Also, the simulated keystrokes should be ignored by Speakup.
2247 */
2248
2249 if (speakup_fake_key_pressed())
2250 goto out;
2251
2252 switch (code) {
2253 case KBD_KEYCODE:
2254 /* speakup requires keycode and keysym currently */
2255 keycode = param->value;
2256 break;
2257 case KBD_UNBOUND_KEYCODE:
2258 /* not used yet */
2259 break;
2260 case KBD_UNICODE:
2261 /* not used yet */
2262 break;
2263 case KBD_KEYSYM:
2264 if (speakup_key(vc, param->shift, keycode, param->value, up))
2265 ret = NOTIFY_STOP;
2266 else if (KTYP(param->value) == KT_CUR)
2267 ret = pre_handle_cursor(vc, KVAL(param->value), up);
2268 break;
2269 case KBD_POST_KEYSYM:{
2270 unsigned char type = KTYP(param->value) - 0xf0;
2271 unsigned char val = KVAL(param->value);
2272
2273 switch (type) {
2274 case KT_SHIFT:
2275 do_handle_shift(vc, val, up);
2276 break;
2277 case KT_LATIN:
2278 case KT_LETTER:
2279 do_handle_latin(vc, val, up);
2280 break;
2281 case KT_CUR:
2282 do_handle_cursor(vc, val, up);
2283 break;
2284 case KT_SPEC:
2285 do_handle_spec(vc, val, up);
2286 break;
2287 }
2288 break;
2289 }
2290 }
2291 out:
2292 in_keyboard_notifier = 0;
2293 return ret;
2294 }
2295
vt_notifier_call(struct notifier_block * nb,unsigned long code,void * _param)2296 static int vt_notifier_call(struct notifier_block *nb,
2297 unsigned long code, void *_param)
2298 {
2299 struct vt_notifier_param *param = _param;
2300 struct vc_data *vc = param->vc;
2301
2302 switch (code) {
2303 case VT_ALLOCATE:
2304 if (vc->vc_mode == KD_TEXT)
2305 speakup_allocate(vc, GFP_ATOMIC);
2306 break;
2307 case VT_DEALLOCATE:
2308 speakup_deallocate(vc);
2309 break;
2310 case VT_WRITE:
2311 if (param->c == '\b') {
2312 speakup_bs(vc);
2313 } else {
2314 u16 d = param->c;
2315
2316 speakup_con_write(vc, &d, 1);
2317 }
2318 break;
2319 case VT_UPDATE:
2320 speakup_con_update(vc);
2321 break;
2322 }
2323 return NOTIFY_OK;
2324 }
2325
2326 /* called by: module_exit() */
speakup_exit(void)2327 static void __exit speakup_exit(void)
2328 {
2329 int i;
2330
2331 unregister_keyboard_notifier(&keyboard_notifier_block);
2332 unregister_vt_notifier(&vt_notifier_block);
2333 speakup_unregister_devsynth();
2334 speakup_cancel_selection();
2335 speakup_cancel_paste();
2336 del_timer_sync(&cursor_timer);
2337 kthread_stop(speakup_task);
2338 speakup_task = NULL;
2339 mutex_lock(&spk_mutex);
2340 synth_release();
2341 mutex_unlock(&spk_mutex);
2342 spk_ttyio_unregister_ldisc();
2343
2344 speakup_kobj_exit();
2345
2346 for (i = 0; i < MAX_NR_CONSOLES; i++)
2347 kfree(speakup_console[i]);
2348
2349 speakup_remove_virtual_keyboard();
2350
2351 for (i = 0; i < MAXVARS; i++)
2352 speakup_unregister_var(i);
2353
2354 for (i = 0; i < 256; i++) {
2355 if (spk_characters[i] != spk_default_chars[i])
2356 kfree(spk_characters[i]);
2357 }
2358
2359 spk_free_user_msgs();
2360 }
2361
2362 /* call by: module_init() */
speakup_init(void)2363 static int __init speakup_init(void)
2364 {
2365 int i;
2366 long err = 0;
2367 struct vc_data *vc = vc_cons[fg_console].d;
2368 struct var_t *var;
2369
2370 /* These first few initializations cannot fail. */
2371 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2372 spk_reset_default_chars();
2373 spk_reset_default_chartab();
2374 spk_strlwr(synth_name);
2375 spk_vars[0].u.n.high = vc->vc_cols;
2376 for (var = spk_vars; var->var_id != MAXVARS; var++)
2377 speakup_register_var(var);
2378 for (var = synth_time_vars;
2379 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
2380 speakup_register_var(var);
2381 for (i = 1; spk_punc_info[i].mask != 0; i++)
2382 spk_set_mask_bits(NULL, i, 2);
2383
2384 spk_set_key_info(spk_key_defaults, spk_key_buf);
2385
2386 /* From here on out, initializations can fail. */
2387 err = speakup_add_virtual_keyboard();
2388 if (err)
2389 goto error_virtkeyboard;
2390
2391 for (i = 0; i < MAX_NR_CONSOLES; i++)
2392 if (vc_cons[i].d) {
2393 err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
2394 if (err)
2395 goto error_kobjects;
2396 }
2397
2398 if (spk_quiet_boot)
2399 spk_shut_up |= 0x01;
2400
2401 err = speakup_kobj_init();
2402 if (err)
2403 goto error_kobjects;
2404
2405 spk_ttyio_register_ldisc();
2406 synth_init(synth_name);
2407 speakup_register_devsynth();
2408 /*
2409 * register_devsynth might fail, but this error is not fatal.
2410 * /dev/synth is an extra feature; the rest of Speakup
2411 * will work fine without it.
2412 */
2413
2414 err = register_keyboard_notifier(&keyboard_notifier_block);
2415 if (err)
2416 goto error_kbdnotifier;
2417 err = register_vt_notifier(&vt_notifier_block);
2418 if (err)
2419 goto error_vtnotifier;
2420
2421 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
2422
2423 if (IS_ERR(speakup_task)) {
2424 err = PTR_ERR(speakup_task);
2425 goto error_task;
2426 }
2427
2428 set_user_nice(speakup_task, 10);
2429 wake_up_process(speakup_task);
2430
2431 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2432 pr_info("synth name on entry is: %s\n", synth_name);
2433 goto out;
2434
2435 error_task:
2436 unregister_vt_notifier(&vt_notifier_block);
2437
2438 error_vtnotifier:
2439 unregister_keyboard_notifier(&keyboard_notifier_block);
2440 del_timer(&cursor_timer);
2441
2442 error_kbdnotifier:
2443 speakup_unregister_devsynth();
2444 mutex_lock(&spk_mutex);
2445 synth_release();
2446 mutex_unlock(&spk_mutex);
2447 speakup_kobj_exit();
2448
2449 error_kobjects:
2450 for (i = 0; i < MAX_NR_CONSOLES; i++)
2451 kfree(speakup_console[i]);
2452
2453 speakup_remove_virtual_keyboard();
2454
2455 error_virtkeyboard:
2456 for (i = 0; i < MAXVARS; i++)
2457 speakup_unregister_var(i);
2458
2459 for (i = 0; i < 256; i++) {
2460 if (spk_characters[i] != spk_default_chars[i])
2461 kfree(spk_characters[i]);
2462 }
2463
2464 spk_free_user_msgs();
2465
2466 out:
2467 return err;
2468 }
2469
2470 module_param_named(bell_pos, spk_vars[BELL_POS_ID].u.n.default_val, int, 0444);
2471 module_param_named(spell_delay, spk_vars[SPELL_DELAY_ID].u.n.default_val, int, 0444);
2472 module_param_named(attrib_bleep, spk_vars[ATTRIB_BLEEP_ID].u.n.default_val, int, 0444);
2473 module_param_named(bleeps, spk_vars[BLEEPS_ID].u.n.default_val, int, 0444);
2474 module_param_named(bleep_time, spk_vars[BLEEP_TIME_ID].u.n.default_val, int, 0444);
2475 module_param_named(punc_level, spk_vars[PUNC_LEVEL_ID].u.n.default_val, int, 0444);
2476 module_param_named(reading_punc, spk_vars[READING_PUNC_ID].u.n.default_val, int, 0444);
2477 module_param_named(cursor_time, spk_vars[CURSOR_TIME_ID].u.n.default_val, int, 0444);
2478 module_param_named(say_control, spk_vars[SAY_CONTROL_ID].u.n.default_val, int, 0444);
2479 module_param_named(say_word_ctl, spk_vars[SAY_WORD_CTL_ID].u.n.default_val, int, 0444);
2480 module_param_named(no_interrupt, spk_vars[NO_INTERRUPT_ID].u.n.default_val, int, 0444);
2481 module_param_named(key_echo, spk_vars[KEY_ECHO_ID].u.n.default_val, int, 0444);
2482 module_param_named(cur_phonetic, spk_vars[CUR_PHONETIC_ID].u.n.default_val, int, 0444);
2483
2484 MODULE_PARM_DESC(bell_pos, "This works much like a typewriter bell. If for example 72 is echoed to bell_pos, it will beep the PC speaker when typing on a line past character 72.");
2485 MODULE_PARM_DESC(spell_delay, "This controls how fast a word is spelled when speakup's spell word review command is pressed.");
2486 MODULE_PARM_DESC(attrib_bleep, "Beeps the PC speaker when there is an attribute change such as background color when using speakup review commands. One = on, zero = off.");
2487 MODULE_PARM_DESC(bleeps, "This controls whether one hears beeps through the PC speaker when using speakup review commands.");
2488 MODULE_PARM_DESC(bleep_time, "This controls the duration of the PC speaker beeps speakup produces.");
2489 MODULE_PARM_DESC(punc_level, "Controls the level of punctuation spoken as the screen is displayed, not reviewed.");
2490 MODULE_PARM_DESC(reading_punc, "It controls the level of punctuation when reviewing the screen with speakup's screen review commands.");
2491 MODULE_PARM_DESC(cursor_time, "This controls cursor delay when using arrow keys.");
2492 MODULE_PARM_DESC(say_control, "This controls if speakup speaks shift, alt and control when those keys are pressed or not.");
2493 MODULE_PARM_DESC(say_word_ctl, "Sets the say_word_ctl on load.");
2494 MODULE_PARM_DESC(no_interrupt, "Controls if typing interrupts output from speakup.");
2495 MODULE_PARM_DESC(key_echo, "Controls if speakup speaks keys when they are typed. One = on zero = off or don't echo keys.");
2496 MODULE_PARM_DESC(cur_phonetic, "Controls if speakup speaks letters phonetically during navigation. One = on zero = off or don't speak phonetically.");
2497
2498 module_init(speakup_init);
2499 module_exit(speakup_exit);
2500