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