1 // SPDX-License-Identifier: GPL-2.0+
3 * review functions for the speakup screen review package.
4 * originally written by: Kirk Reiser and Andy Berdan.
6 * extensively modified by David Borowski.
8 ** Copyright (C) 1998 Kirk Reiser.
9 * Copyright (C) 2003 David Borowski.
12 #include <linux/kernel.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>
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>
34 #include <linux/spinlock.h>
35 #include <linux/notifier.h>
37 #include <linux/uaccess.h> /* copy_from|to|user() and others */
42 #define MAX_DELAY msecs_to_jiffies(500)
43 #define MINECHOCHAR SPACE
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
);
52 module_param_named(synth
, synth_name
, charp
, 0444);
53 module_param_named(quiet
, spk_quiet_boot
, bool, 0444);
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.");
58 special_func spk_special_handler
;
60 short spk_pitch_shift
, synth_flags
;
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
;
67 int spk_punc_level
, spk_reading_punc
;
68 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0";
69 char spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
70 char spk_str_pause
[MAXVARLEN
+ 1] = "\0";
72 const struct st_bits_data spk_punc_info
[] = {
74 {"some", "/$%&@", SOME
},
75 {"most", "$%&#()=+*/@^<>|\\", MOST
},
76 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
77 {"delimiters", "", B_WDLM
},
78 {"repeats", "()", CH_RPT
},
79 {"extended numeric", "", B_EXNUM
},
80 {"symbols", "", B_SYM
},
84 static char mark_cut_flag
;
86 static u_char
*spk_shift_table
;
87 u_char
*spk_our_keys
[MAX_KEY
];
88 u_char spk_key_buf
[600];
89 const u_char spk_key_defaults
[] = {
90 #include "speakupmap.h"
93 /* Speakup Cursor Track Variables */
94 static int cursor_track
= 1, prev_cursor_track
= 1;
96 /* cursor track modes, must be ordered same as cursor_msgs */
105 #define read_all_mode CT_Max
107 static struct tty_struct
*tty
;
109 static void spkup_write(const u16
*in_buf
, int count
);
111 static char *phonetic
[] = {
112 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
113 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
115 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
116 "x ray", "yankee", "zulu"
119 /* array of 256 char pointers (one for each character description)
120 * initialized to default_chars and user selectable via
121 * /proc/speakup/characters
123 char *spk_characters
[256];
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",
131 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
133 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
136 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
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",
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",
165 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
167 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
169 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
171 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
172 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
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",
179 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
181 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
183 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
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
190 u_short spk_chartab
[256];
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 */
211 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
213 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
215 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
217 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
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 */
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
;
240 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
242 DEFINE_MUTEX(spk_mutex
);
244 static int keyboard_notifier_call(struct notifier_block
*,
245 unsigned long code
, void *param
);
247 static struct notifier_block keyboard_notifier_block
= {
248 .notifier_call
= keyboard_notifier_call
,
251 static int vt_notifier_call(struct notifier_block
*,
252 unsigned long code
, void *param
);
254 static struct notifier_block vt_notifier_block
= {
255 .notifier_call
= vt_notifier_call
,
258 static unsigned char get_attributes(struct vc_data
*vc
, u16
*pos
)
260 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
261 return (scr_readw(pos
) & ~vc
->vc_hi_font_mask
) >> 8;
264 static void speakup_date(struct vc_data
*vc
)
266 spk_x
= spk_cx
= vc
->vc_x
;
267 spk_y
= spk_cy
= vc
->vc_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
);
273 static void bleep(u_short val
)
275 static const short vals
[] = {
276 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
279 int time
= spk_bleep_time
;
281 freq
= vals
[val
% 12];
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. */
290 static void speakup_shut_up(struct vc_data
*vc
)
301 static void speech_kill(struct vc_data
*vc
)
303 char val
= synth
->is_alive(synth
);
308 /* re-enables synth, if disabled */
309 if (val
== 2 || spk_killed
) {
311 spk_shut_up
&= ~0x40;
312 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
314 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
319 static void speakup_off(struct vc_data
*vc
)
321 if (spk_shut_up
& 0x80) {
323 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
326 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
331 static void speakup_parked(struct vc_data
*vc
)
333 if (spk_parked
& 0x80) {
335 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
338 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
342 static void speakup_cut(struct vc_data
*vc
)
344 static const char err_buf
[] = "set selection failed";
347 if (!mark_cut_flag
) {
349 spk_xs
= (u_short
)spk_x
;
350 spk_ys
= (u_short
)spk_y
;
352 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
355 spk_xe
= (u_short
)spk_x
;
356 spk_ye
= (u_short
)spk_y
;
358 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
360 speakup_clear_selection();
361 ret
= speakup_set_selection(tty
);
365 break; /* no error */
367 pr_warn("%sEFAULT\n", err_buf
);
370 pr_warn("%sEINVAL\n", err_buf
);
373 pr_warn("%sENOMEM\n", err_buf
);
378 static void speakup_paste(struct vc_data
*vc
)
382 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
384 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
385 speakup_paste_selection(tty
);
389 static void say_attributes(struct vc_data
*vc
)
391 int fg
= spk_attr
& 0x0f;
392 int bg
= spk_attr
>> 4;
395 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
398 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
400 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
403 synth_printf(" %s ", spk_msg_get(MSG_ON
));
405 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
416 static void announce_edge(struct vc_data
*vc
, int msg_id
)
420 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
422 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
425 static void speak_char(u16 ch
)
428 struct var_t
*direct
= spk_get_var(DIRECT
);
430 if (ch
>= 0x100 || (direct
&& direct
->u
.n
.value
)) {
431 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
)) {
433 synth_printf("%s", spk_str_caps_start
);
436 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
))
437 synth_printf("%s", spk_str_caps_stop
);
441 cp
= spk_characters
[ch
];
443 pr_info("%s: cp == NULL!\n", __func__
);
446 if (IS_CHAR(ch
, B_CAP
)) {
448 synth_printf("%s %s %s",
449 spk_str_caps_start
, cp
, spk_str_caps_stop
);
453 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL
), cp
);
455 synth_printf(" %s ", cp
);
460 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
468 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
472 if (w
& vc
->vc_hi_font_mask
) {
473 w
&= ~vc
->vc_hi_font_mask
;
477 ch
= inverse_translate(vc
, c
, 1);
478 *attribs
= (w
& 0xff00) >> 8;
483 static void say_char(struct vc_data
*vc
)
487 spk_old_attr
= spk_attr
;
488 ch
= get_char(vc
, (u_short
*)spk_pos
, &spk_attr
);
489 if (spk_attr
!= spk_old_attr
) {
490 if (spk_attrib_bleep
& 1)
492 if (spk_attrib_bleep
& 2)
498 static void say_phonetic_char(struct vc_data
*vc
)
502 spk_old_attr
= spk_attr
;
503 ch
= get_char(vc
, (u_short
*)spk_pos
, &spk_attr
);
504 if (ch
<= 0x7f && isalpha(ch
)) {
506 synth_printf("%s\n", phonetic
[--ch
]);
508 if (ch
< 0x100 && IS_CHAR(ch
, B_NUM
))
509 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
514 static void say_prev_char(struct vc_data
*vc
)
518 announce_edge(vc
, edge_left
);
526 static void say_next_char(struct vc_data
*vc
)
529 if (spk_x
== vc
->vc_cols
- 1) {
530 announce_edge(vc
, edge_right
);
538 /* get_word - will first check to see if the character under the
539 * reading cursor is a space and if spk_say_word_ctl is true it will
540 * return the word space. If spk_say_word_ctl is not set it will check to
541 * see if there is a word starting on the next position to the right
542 * and return that word if it exists. If it does not exist it will
543 * move left to the beginning of any previous word on the line or the
544 * beginning off the line whichever comes first..
547 static u_long
get_word(struct vc_data
*vc
)
549 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
554 spk_old_attr
= spk_attr
;
555 ch
= get_char(vc
, (u_short
*)tmp_pos
, &temp
);
557 /* decided to take out the sayword if on a space (mis-information */
558 if (spk_say_word_ctl
&& ch
== SPACE
) {
560 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
562 } else if (tmpx
< vc
->vc_cols
- 2 &&
563 (ch
== SPACE
|| ch
== 0 || (ch
< 0x100 && IS_WDLM(ch
))) &&
564 get_char(vc
, (u_short
*)&tmp_pos
+ 1, &temp
) > SPACE
) {
569 ch
= get_char(vc
, (u_short
*)tmp_pos
- 1, &temp
);
570 if ((ch
== SPACE
|| ch
== 0 ||
571 (ch
< 0x100 && IS_WDLM(ch
))) &&
572 get_char(vc
, (u_short
*)tmp_pos
, &temp
) > SPACE
)
578 attr_ch
= get_char(vc
, (u_short
*)tmp_pos
, &spk_attr
);
579 buf
[cnt
++] = attr_ch
;
580 while (tmpx
< vc
->vc_cols
- 1) {
583 ch
= get_char(vc
, (u_short
*)tmp_pos
, &temp
);
584 if (ch
== SPACE
|| ch
== 0 ||
585 (buf
[cnt
- 1] < 0x100 && IS_WDLM(buf
[cnt
- 1]) &&
594 static void say_word(struct vc_data
*vc
)
596 u_long cnt
= get_word(vc
);
597 u_short saved_punc_mask
= spk_punc_mask
;
601 spk_punc_mask
= PUNC
;
603 spkup_write(buf
, cnt
);
604 spk_punc_mask
= saved_punc_mask
;
607 static void say_prev_word(struct vc_data
*vc
)
611 u_short edge_said
= 0, last_state
= 0, state
= 0;
617 announce_edge(vc
, edge_top
);
622 edge_said
= edge_quiet
;
627 edge_said
= edge_top
;
630 if (edge_said
!= edge_quiet
)
631 edge_said
= edge_left
;
635 spk_x
= vc
->vc_cols
- 1;
640 ch
= get_char(vc
, (u_short
*)spk_pos
, &temp
);
641 if (ch
== SPACE
|| ch
== 0)
643 else if (ch
< 0x100 && IS_WDLM(ch
))
647 if (state
< last_state
) {
654 if (spk_x
== 0 && edge_said
== edge_quiet
)
655 edge_said
= edge_left
;
656 if (edge_said
> 0 && edge_said
< edge_quiet
)
657 announce_edge(vc
, edge_said
);
661 static void say_next_word(struct vc_data
*vc
)
665 u_short edge_said
= 0, last_state
= 2, state
= 0;
668 if (spk_x
== vc
->vc_cols
- 1 && spk_y
== vc
->vc_rows
- 1) {
669 announce_edge(vc
, edge_bottom
);
673 ch
= get_char(vc
, (u_short
*)spk_pos
, &temp
);
674 if (ch
== SPACE
|| ch
== 0)
676 else if (ch
< 0x100 && IS_WDLM(ch
))
680 if (state
> last_state
)
682 if (spk_x
>= vc
->vc_cols
- 1) {
683 if (spk_y
== vc
->vc_rows
- 1) {
684 edge_said
= edge_bottom
;
690 edge_said
= edge_right
;
698 announce_edge(vc
, edge_said
);
702 static void spell_word(struct vc_data
*vc
)
704 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
707 char *str_cap
= spk_str_caps_stop
;
708 char *last_cap
= spk_str_caps_stop
;
709 struct var_t
*direct
= spk_get_var(DIRECT
);
716 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
717 /* FIXME: Non-latin1 considered as lower case */
718 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
)) {
719 str_cap
= spk_str_caps_start
;
720 if (*spk_str_caps_stop
)
722 else /* synth has no pitch */
723 last_cap
= spk_str_caps_stop
;
725 str_cap
= spk_str_caps_stop
;
727 if (str_cap
!= last_cap
) {
728 synth_printf("%s", str_cap
);
731 if (ch
>= 0x100 || (direct
&& direct
->u
.n
.value
)) {
733 } else if (this_speakup_key
== SPELL_PHONETIC
&&
734 ch
<= 0x7f && isalpha(ch
)) {
736 cp1
= phonetic
[--ch
];
737 synth_printf("%s", cp1
);
739 cp1
= spk_characters
[ch
];
741 synth_printf("%s", spk_msg_get(MSG_CTRL
));
744 synth_printf("%s", cp1
);
748 if (str_cap
!= spk_str_caps_stop
)
749 synth_printf("%s", spk_str_caps_stop
);
752 static int get_line(struct vc_data
*vc
)
754 u_long tmp
= spk_pos
- (spk_x
* 2);
758 spk_old_attr
= spk_attr
;
759 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
760 for (i
= 0; i
< vc
->vc_cols
; i
++) {
761 buf
[i
] = get_char(vc
, (u_short
*)tmp
, &tmp2
);
764 for (--i
; i
>= 0; i
--)
770 static void say_line(struct vc_data
*vc
)
772 int i
= get_line(vc
);
774 u_short saved_punc_mask
= spk_punc_mask
;
777 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
781 if (this_speakup_key
== SAY_LINE_INDENT
) {
785 synth_printf("%zd, ", (cp
- buf
) + 1);
787 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
789 spk_punc_mask
= saved_punc_mask
;
792 static void say_prev_line(struct vc_data
*vc
)
796 announce_edge(vc
, edge_top
);
800 spk_pos
-= vc
->vc_size_row
;
804 static void say_next_line(struct vc_data
*vc
)
807 if (spk_y
== vc
->vc_rows
- 1) {
808 announce_edge(vc
, edge_bottom
);
812 spk_pos
+= vc
->vc_size_row
;
816 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
821 u_short saved_punc_mask
= spk_punc_mask
;
823 spk_old_attr
= spk_attr
;
824 spk_attr
= get_attributes(vc
, (u_short
*)from
);
826 buf
[i
++] = get_char(vc
, (u_short
*)from
, &tmp
);
828 if (i
>= vc
->vc_size_row
)
831 for (--i
; i
>= 0; i
--)
839 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
842 spk_punc_mask
= saved_punc_mask
;
846 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
849 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
850 u_long end
= start
+ (to
* 2);
853 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
854 if (cursor_track
!= read_all_mode
)
855 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
858 /* Sentence Reading Commands */
860 static int currsentence
;
861 static int numsentences
[2];
862 static u16
*sentbufend
[2];
863 static u16
*sentmarks
[2][10];
866 static u16 sentbuf
[2][256];
868 static int say_sentence_num(int num
, int prev
)
871 currsentence
= num
+ 1;
872 if (prev
&& --bn
== -1)
875 if (num
> numsentences
[bn
])
878 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
882 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
892 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
893 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
895 numsentences
[bn
] = 0;
896 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
898 spk_old_attr
= spk_attr
;
899 spk_attr
= get_attributes(vc
, (u_short
*)start
);
901 while (start
< end
) {
902 sentbuf
[bn
][i
] = get_char(vc
, (u_short
*)start
, &tmp
);
904 if (sentbuf
[bn
][i
] == SPACE
&&
905 sentbuf
[bn
][i
- 1] == '.' &&
906 numsentences
[bn
] < 9) {
907 /* Sentence Marker */
909 sentmarks
[bn
][numsentences
[bn
]] =
915 if (i
>= vc
->vc_size_row
)
919 for (--i
; i
>= 0; i
--)
920 if (sentbuf
[bn
][i
] != SPACE
)
926 sentbuf
[bn
][++i
] = SPACE
;
927 sentbuf
[bn
][++i
] = '\0';
929 sentbufend
[bn
] = &sentbuf
[bn
][i
];
930 return numsentences
[bn
];
933 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
935 u_long start
= vc
->vc_origin
, end
;
938 start
+= from
* vc
->vc_size_row
;
939 if (to
> vc
->vc_rows
)
941 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
942 for (from
= start
; from
< end
; from
= to
) {
943 to
= from
+ vc
->vc_size_row
;
944 say_from_to(vc
, from
, to
, 1);
948 static void say_screen(struct vc_data
*vc
)
950 say_screen_from_to(vc
, 0, vc
->vc_rows
);
953 static void speakup_win_say(struct vc_data
*vc
)
955 u_long start
, end
, from
, to
;
958 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
961 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
962 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
963 while (start
<= end
) {
964 from
= start
+ (win_left
* 2);
965 to
= start
+ (win_right
* 2);
966 say_from_to(vc
, from
, to
, 1);
967 start
+= vc
->vc_size_row
;
971 static void top_edge(struct vc_data
*vc
)
974 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
979 static void bottom_edge(struct vc_data
*vc
)
982 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
983 spk_y
= vc
->vc_rows
- 1;
987 static void left_edge(struct vc_data
*vc
)
990 spk_pos
-= spk_x
* 2;
995 static void right_edge(struct vc_data
*vc
)
998 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
999 spk_x
= vc
->vc_cols
- 1;
1003 static void say_first_char(struct vc_data
*vc
)
1005 int i
, len
= get_line(vc
);
1010 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1013 for (i
= 0; i
< len
; i
++)
1014 if (buf
[i
] != SPACE
)
1017 spk_pos
-= (spk_x
- i
) * 2;
1019 synth_printf("%d, ", ++i
);
1023 static void say_last_char(struct vc_data
*vc
)
1025 int len
= get_line(vc
);
1030 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1034 spk_pos
-= (spk_x
- len
) * 2;
1036 synth_printf("%d, ", ++len
);
1040 static void say_position(struct vc_data
*vc
)
1042 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1047 /* Added by brianb */
1048 static void say_char_num(struct vc_data
*vc
)
1051 u16 ch
= get_char(vc
, (u_short
*)spk_pos
, &tmp
);
1053 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1056 /* these are stub functions to keep keyboard.c happy. */
1058 static void say_from_top(struct vc_data
*vc
)
1060 say_screen_from_to(vc
, 0, spk_y
);
1063 static void say_to_bottom(struct vc_data
*vc
)
1065 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1068 static void say_from_left(struct vc_data
*vc
)
1070 say_line_from_to(vc
, 0, spk_x
, 1);
1073 static void say_to_right(struct vc_data
*vc
)
1075 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1078 /* end of stub functions. */
1080 static void spkup_write(const u16
*in_buf
, int count
)
1082 static int rep_count
;
1083 static u16 ch
= '\0', old_ch
= '\0';
1084 static u_short char_type
, last_type
;
1085 int in_count
= count
;
1089 if (cursor_track
== read_all_mode
) {
1090 /* Insert Sentence Index */
1091 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1092 (currsentence
<= numsentences
[bn
]))
1093 synth_insert_next_index(currsentence
++);
1097 char_type
= spk_chartab
[ch
];
1100 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1101 if (++rep_count
> 2)
1104 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1106 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1112 if (ch
== spk_lastkey
) {
1114 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1116 } else if (char_type
& B_ALPHA
) {
1117 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1118 synth_buffer_add(SPACE
);
1120 } else if (char_type
& B_NUM
) {
1123 } else if (char_type
& spk_punc_mask
) {
1125 char_type
&= ~PUNC
; /* for dec nospell processing */
1126 } else if (char_type
& SYNTH_OK
) {
1127 /* these are usually puncts like . and , which synth
1128 * needs for expression.
1129 * suppress multiple to get rid of long pauses and
1130 * clear repeat count
1132 * repeats on you don't get nothing repeated count
1139 /* send space and record position, if next is num overwrite space */
1141 synth_buffer_add(SPACE
);
1146 last_type
= char_type
;
1149 if (in_count
> 2 && rep_count
> 2) {
1150 if (last_type
& CH_RPT
) {
1152 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1160 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1162 static void read_all_doc(struct vc_data
*vc
);
1163 static void cursor_done(struct timer_list
*unused
);
1164 static DEFINE_TIMER(cursor_timer
, cursor_done
);
1166 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1168 unsigned long flags
;
1170 if (!synth
|| up_flag
|| spk_killed
)
1172 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1173 if (cursor_track
== read_all_mode
) {
1176 del_timer(&cursor_timer
);
1177 spk_shut_up
&= 0xfe;
1182 del_timer(&cursor_timer
);
1183 cursor_track
= prev_cursor_track
;
1184 spk_shut_up
&= 0xfe;
1189 spk_shut_up
&= 0xfe;
1192 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1193 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1194 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1197 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1199 unsigned long flags
;
1201 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1205 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1208 if (!synth
|| spk_killed
) {
1209 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1212 spk_shut_up
&= 0xfe;
1213 spk_lastkey
= value
;
1216 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1218 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1221 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1223 int i
= 0, states
, key_data_len
;
1224 const u_char
*cp
= key_info
;
1225 u_char
*cp1
= k_buffer
;
1226 u_char ch
, version
, num_keys
;
1229 if (version
!= KEY_MAP_VER
) {
1230 pr_debug("version found %d should be %d\n",
1231 version
, KEY_MAP_VER
);
1235 states
= (int)cp
[1];
1236 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1237 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
)) {
1238 pr_debug("too many key_infos (%d over %u)\n",
1239 key_data_len
+ SHIFT_TBL_SIZE
+ 4,
1240 (unsigned int)(sizeof(spk_key_buf
)));
1243 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1244 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1245 spk_shift_table
= k_buffer
;
1246 spk_our_keys
[0] = spk_shift_table
;
1247 cp1
+= SHIFT_TBL_SIZE
;
1248 memcpy(cp1
, cp
, key_data_len
+ 3);
1249 /* get num_keys, states and data */
1250 cp1
+= 2; /* now pointing at shift states */
1251 for (i
= 1; i
<= states
; i
++) {
1253 if (ch
>= SHIFT_TBL_SIZE
) {
1254 pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
1255 ch
, SHIFT_TBL_SIZE
);
1258 spk_shift_table
[ch
] = i
;
1260 keymap_flags
= *cp1
++;
1261 while ((ch
= *cp1
)) {
1262 if (ch
>= MAX_KEY
) {
1263 pr_debug("(%d), not valid key, (max_allowed = %d)\n",
1267 spk_our_keys
[ch
] = cp1
;
1273 static struct var_t spk_vars
[] = {
1274 /* bell must be first to set high limit */
1275 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1276 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1277 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1278 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1279 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1280 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1281 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1282 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1283 {SAY_CONTROL
, TOGGLE_0
},
1284 {SAY_WORD_CTL
, TOGGLE_0
},
1285 {NO_INTERRUPT
, TOGGLE_0
},
1286 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1290 static void toggle_cursoring(struct vc_data
*vc
)
1292 if (cursor_track
== read_all_mode
)
1293 cursor_track
= prev_cursor_track
;
1294 if (++cursor_track
>= CT_Max
)
1296 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1299 void spk_reset_default_chars(void)
1303 /* First, free any non-default */
1304 for (i
= 0; i
< 256; i
++) {
1305 if (spk_characters
[i
] &&
1306 (spk_characters
[i
] != spk_default_chars
[i
]))
1307 kfree(spk_characters
[i
]);
1310 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1313 void spk_reset_default_chartab(void)
1315 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1318 static const struct st_bits_data
*pb_edit
;
1320 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1322 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1324 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1327 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1328 spk_special_handler
= NULL
;
1331 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1333 spk_chartab
[ch
] ^= mask
;
1335 synth_printf(" %s\n",
1336 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1337 spk_msg_get(MSG_OFF
));
1341 /* Allocation concurrency is protected by the console semaphore */
1342 static int speakup_allocate(struct vc_data
*vc
, gfp_t gfp_flags
)
1346 vc_num
= vc
->vc_num
;
1347 if (!speakup_console
[vc_num
]) {
1348 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1350 if (!speakup_console
[vc_num
])
1353 } else if (!spk_parked
) {
1360 static void speakup_deallocate(struct vc_data
*vc
)
1364 vc_num
= vc
->vc_num
;
1365 kfree(speakup_console
[vc_num
]);
1366 speakup_console
[vc_num
] = NULL
;
1369 static u_char is_cursor
;
1370 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1371 static int cursor_con
;
1373 static void reset_highlight_buffers(struct vc_data
*);
1375 static int read_all_key
;
1377 static int in_keyboard_notifier
;
1379 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1393 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1395 del_timer(&cursor_timer
);
1396 speakup_fake_down_arrow();
1397 start_read_all_timer(vc
, command
);
1400 static void read_all_doc(struct vc_data
*vc
)
1402 if ((vc
->vc_num
!= fg_console
) || !synth
|| spk_shut_up
)
1404 if (!synth_supports_indexing())
1406 if (cursor_track
!= read_all_mode
)
1407 prev_cursor_track
= cursor_track
;
1408 cursor_track
= read_all_mode
;
1409 spk_reset_index_count(0);
1410 if (get_sentence_buf(vc
, 0) == -1) {
1411 del_timer(&cursor_timer
);
1412 if (!in_keyboard_notifier
)
1413 speakup_fake_down_arrow();
1414 start_read_all_timer(vc
, RA_DOWN_ARROW
);
1416 say_sentence_num(0, 0);
1417 synth_insert_next_index(0);
1418 start_read_all_timer(vc
, RA_TIMER
);
1422 static void stop_read_all(struct vc_data
*vc
)
1424 del_timer(&cursor_timer
);
1425 cursor_track
= prev_cursor_track
;
1426 spk_shut_up
&= 0xfe;
1430 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1432 struct var_t
*cursor_timeout
;
1434 cursor_con
= vc
->vc_num
;
1435 read_all_key
= command
;
1436 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1437 mod_timer(&cursor_timer
,
1438 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1441 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1443 int indcount
, sentcount
, rv
, sn
;
1447 /* Get Current Sentence */
1448 spk_get_index_count(&indcount
, &sentcount
);
1449 /*printk("%d %d ", indcount, sentcount); */
1450 spk_reset_index_count(sentcount
+ 1);
1451 if (indcount
== 1) {
1452 if (!say_sentence_num(sentcount
+ 1, 0)) {
1453 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1456 synth_insert_next_index(0);
1459 if (!say_sentence_num(sentcount
+ 1, 1)) {
1461 spk_reset_index_count(sn
);
1463 synth_insert_next_index(0);
1465 if (!say_sentence_num(sn
, 0)) {
1466 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1469 synth_insert_next_index(0);
1471 start_read_all_timer(vc
, RA_TIMER
);
1481 if (get_sentence_buf(vc
, 0) == -1) {
1482 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1484 say_sentence_num(0, 0);
1485 synth_insert_next_index(0);
1486 start_read_all_timer(vc
, RA_TIMER
);
1489 case RA_FIND_NEXT_SENT
:
1490 rv
= get_sentence_buf(vc
, 0);
1494 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1496 say_sentence_num(1, 0);
1497 synth_insert_next_index(0);
1498 start_read_all_timer(vc
, RA_TIMER
);
1501 case RA_FIND_PREV_SENT
:
1504 spk_get_index_count(&indcount
, &sentcount
);
1506 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1508 start_read_all_timer(vc
, RA_TIMER
);
1513 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1515 unsigned long flags
;
1517 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1518 if (cursor_track
== read_all_mode
) {
1520 if (!synth
|| up_flag
|| spk_shut_up
) {
1521 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1524 del_timer(&cursor_timer
);
1525 spk_shut_up
&= 0xfe;
1527 start_read_all_timer(vc
, value
+ 1);
1528 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1531 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1535 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1537 unsigned long flags
;
1538 struct var_t
*cursor_timeout
;
1540 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1542 if (!synth
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1543 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1546 spk_shut_up
&= 0xfe;
1549 /* the key press flushes if !no_inter but we want to flush on cursor
1550 * moves regardless of no_inter state
1552 is_cursor
= value
+ 1;
1553 old_cursor_pos
= vc
->vc_pos
;
1554 old_cursor_x
= vc
->vc_x
;
1555 old_cursor_y
= vc
->vc_y
;
1556 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1557 cursor_con
= vc
->vc_num
;
1558 if (cursor_track
== CT_Highlight
)
1559 reset_highlight_buffers(vc
);
1560 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1561 mod_timer(&cursor_timer
,
1562 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1563 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1566 static void update_color_buffer(struct vc_data
*vc
, const u16
*ic
, int len
)
1569 int vc_num
= vc
->vc_num
;
1571 bi
= (vc
->vc_attr
& 0x70) >> 4;
1572 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1575 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1576 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1577 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1578 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1580 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1582 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1584 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1585 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1587 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1594 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1597 static void reset_highlight_buffers(struct vc_data
*vc
)
1600 int vc_num
= vc
->vc_num
;
1602 for (i
= 0; i
< 8; i
++)
1603 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1606 static int count_highlight_color(struct vc_data
*vc
)
1610 int vc_num
= vc
->vc_num
;
1612 u16
*start
= (u16
*)vc
->vc_origin
;
1614 for (i
= 0; i
< 8; i
++)
1615 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1617 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1618 u16
*end
= start
+ vc
->vc_cols
* 2;
1621 for (ptr
= start
; ptr
< end
; ptr
++) {
1622 ch
= get_attributes(vc
, ptr
);
1623 bg
= (ch
& 0x70) >> 4;
1624 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1626 start
+= vc
->vc_size_row
;
1630 for (i
= 0; i
< 8; i
++)
1631 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1636 static int get_highlight_color(struct vc_data
*vc
)
1639 unsigned int cptr
[8];
1640 int vc_num
= vc
->vc_num
;
1642 for (i
= 0; i
< 8; i
++)
1645 for (i
= 0; i
< 7; i
++)
1646 for (j
= i
+ 1; j
< 8; j
++)
1647 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1648 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]])
1649 swap(cptr
[i
], cptr
[j
]);
1651 for (i
= 0; i
< 8; i
++)
1652 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1653 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1658 static int speak_highlight(struct vc_data
*vc
)
1661 int vc_num
= vc
->vc_num
;
1663 if (count_highlight_color(vc
) == 1)
1665 hc
= get_highlight_color(vc
);
1667 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1668 if ((d
== 1) || (d
== -1))
1669 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1673 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1674 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1675 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1676 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1677 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1683 static void cursor_done(struct timer_list
*unused
)
1685 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1686 unsigned long flags
;
1688 del_timer(&cursor_timer
);
1689 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1690 if (cursor_con
!= fg_console
) {
1696 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1697 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1703 if (cursor_track
== read_all_mode
) {
1704 handle_cursor_read_all(vc
, read_all_key
);
1707 if (cursor_track
== CT_Highlight
) {
1708 if (speak_highlight(vc
)) {
1714 if (cursor_track
== CT_Window
)
1715 speakup_win_say(vc
);
1716 else if (is_cursor
== 1 || is_cursor
== 4)
1717 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1723 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1726 /* called by: vt_notifier_call() */
1727 static void speakup_bs(struct vc_data
*vc
)
1729 unsigned long flags
;
1731 if (!speakup_console
[vc
->vc_num
])
1733 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1734 /* Speakup output, discard */
1738 if (spk_shut_up
|| !synth
) {
1739 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1742 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1747 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1750 /* called by: vt_notifier_call() */
1751 static void speakup_con_write(struct vc_data
*vc
, u16
*str
, int len
)
1753 unsigned long flags
;
1755 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| !synth
)
1757 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1758 /* Speakup output, discard */
1760 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1762 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1763 if (cursor_track
== CT_Highlight
)
1764 update_color_buffer(vc
, str
, len
);
1765 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1769 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1770 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1771 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1776 spkup_write(str
, len
);
1777 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1780 static void speakup_con_update(struct vc_data
*vc
)
1782 unsigned long flags
;
1784 if (!speakup_console
[vc
->vc_num
] || spk_parked
)
1786 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1787 /* Speakup output, discard */
1790 if (vc
->vc_mode
== KD_GRAPHICS
&& !spk_paused
&& spk_str_pause
[0]) {
1791 synth_printf("%s", spk_str_pause
);
1794 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1797 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1799 unsigned long flags
;
1803 if (!synth
|| up_flag
|| spk_killed
)
1805 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1806 spk_shut_up
&= 0xfe;
1811 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1812 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1815 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1816 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1819 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1820 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1821 if (speakup_console
[vc
->vc_num
])
1822 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1826 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1830 synth_printf("%s %s\n",
1831 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1832 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1835 static int inc_dec_var(u_char value
)
1837 struct st_var_header
*p_header
;
1838 struct var_t
*var_data
;
1842 int var_id
= (int)value
- VAR_START
;
1843 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1845 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1846 p_header
= spk_get_var_header(var_id
);
1849 if (p_header
->var_type
!= VAR_NUM
)
1851 var_data
= p_header
->data
;
1852 if (spk_set_num_var(1, p_header
, how
) != 0)
1854 if (!spk_close_press
) {
1855 for (pn
= p_header
->name
; *pn
; pn
++) {
1862 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1863 var_data
->u
.n
.value
);
1864 synth_printf("%s", num_buf
);
1868 static void speakup_win_set(struct vc_data
*vc
)
1872 if (win_start
> 1) {
1873 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1876 if (spk_x
< win_left
|| spk_y
< win_top
) {
1877 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1880 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1882 win_right
= vc
->vc_cols
- 1;
1884 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1894 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1896 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1897 (int)spk_y
+ 1, (int)spk_x
+ 1);
1899 synth_printf("%s\n", info
);
1903 static void speakup_win_clear(struct vc_data
*vc
)
1910 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1913 static void speakup_win_enable(struct vc_data
*vc
)
1915 if (win_start
< 2) {
1916 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1921 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1923 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1926 static void speakup_bits(struct vc_data
*vc
)
1928 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1930 if (spk_special_handler
|| val
< 1 || val
> 6) {
1931 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1934 pb_edit
= &spk_punc_info
[val
];
1935 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1936 spk_special_handler
= edit_bits
;
1939 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1941 static u_char goto_buf
[8];
1947 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1949 if (type
== KT_LATIN
&& ch
== '\n')
1958 wch
= goto_buf
[--num
];
1959 goto_buf
[num
] = '\0';
1960 spkup_write(&wch
, 1);
1963 if (ch
< '+' || ch
> 'y')
1966 goto_buf
[num
++] = ch
;
1967 goto_buf
[num
] = '\0';
1968 spkup_write(&wch
, 1);
1969 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1970 if ((ch
== '+' || ch
== '-') && num
== 1)
1972 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1974 if (num
< maxlen
- 1 || num
> maxlen
)
1976 if (ch
< 'x' || ch
> 'y') {
1979 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1980 goto_buf
[num
= 0] = '\0';
1981 spk_special_handler
= NULL
;
1985 /* Do not replace with kstrtoul: here we need cp to be updated */
1986 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1989 if (*goto_buf
< '0')
1991 else if (goto_pos
> 0)
1994 if (goto_pos
>= vc
->vc_cols
)
1995 goto_pos
= vc
->vc_cols
- 1;
1998 if (*goto_buf
< '0')
2000 else if (goto_pos
> 0)
2003 if (goto_pos
>= vc
->vc_rows
)
2004 goto_pos
= vc
->vc_rows
- 1;
2007 goto_buf
[num
= 0] = '\0';
2009 spk_special_handler
= NULL
;
2012 spk_pos
-= spk_x
* 2;
2014 spk_pos
+= goto_pos
* 2;
2018 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
2024 static void speakup_goto(struct vc_data
*vc
)
2026 if (spk_special_handler
) {
2027 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
2030 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
2031 spk_special_handler
= handle_goto
;
2034 static void speakup_help(struct vc_data
*vc
)
2036 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
2039 static void do_nothing(struct vc_data
*vc
)
2041 return; /* flush done in do_spkup */
2044 static u_char key_speakup
, spk_key_locked
;
2046 static void speakup_lock(struct vc_data
*vc
)
2048 if (!spk_key_locked
) {
2049 spk_key_locked
= 16;
2057 typedef void (*spkup_hand
) (struct vc_data
*);
2058 static spkup_hand spkup_handler
[] = {
2059 /* must be ordered same as defines in speakup.h */
2060 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2061 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2062 say_char
, say_prev_char
, say_next_char
,
2063 say_word
, say_prev_word
, say_next_word
,
2064 say_line
, say_prev_line
, say_next_line
,
2065 top_edge
, bottom_edge
, left_edge
, right_edge
,
2066 spell_word
, spell_word
, say_screen
,
2067 say_position
, say_attributes
,
2068 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2069 say_from_top
, say_to_bottom
,
2070 say_from_left
, say_to_right
,
2071 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2072 speakup_bits
, speakup_bits
, speakup_bits
,
2073 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2074 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2077 static void do_spkup(struct vc_data
*vc
, u_char value
)
2079 if (spk_killed
&& value
!= SPEECH_KILL
)
2083 spk_shut_up
&= 0xfe;
2084 this_speakup_key
= value
;
2085 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2087 (*spkup_handler
[value
]) (vc
);
2089 if (inc_dec_var(value
) < 0)
2094 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2097 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2100 unsigned long flags
;
2103 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2104 u_char shift_info
, offset
;
2110 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2114 if (type
== KT_PAD
&&
2115 (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2120 value
= spk_lastkey
= pad_chars
[value
];
2125 if (keycode
>= MAX_KEY
)
2127 key_info
= spk_our_keys
[keycode
];
2130 /* Check valid read all mode keys */
2131 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2145 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2146 offset
= spk_shift_table
[shift_info
];
2148 new_key
= key_info
[offset
];
2151 if (new_key
== SPK_KEY
) {
2152 if (!spk_key_locked
)
2153 key_speakup
= (up_flag
) ? 0 : 16;
2154 if (up_flag
|| spk_killed
)
2156 spk_shut_up
&= 0xfe;
2162 if (last_keycode
== keycode
&&
2163 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2164 spk_close_press
= 1;
2165 offset
= spk_shift_table
[shift_info
+ 32];
2167 if (offset
&& key_info
[offset
])
2168 new_key
= key_info
[offset
];
2170 last_keycode
= keycode
;
2171 last_spk_jiffy
= jiffies
;
2177 if (type
== KT_SPKUP
&& !spk_special_handler
) {
2178 do_spkup(vc
, new_key
);
2179 spk_close_press
= 0;
2183 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2185 spk_shut_up
&= 0xfe;
2186 kh
= (value
== KVAL(K_DOWN
)) ||
2187 (value
== KVAL(K_UP
)) ||
2188 (value
== KVAL(K_LEFT
)) ||
2189 (value
== KVAL(K_RIGHT
));
2190 if ((cursor_track
!= read_all_mode
) || !kh
)
2193 if (spk_special_handler
) {
2194 if (type
== KT_SPEC
&& value
== 1) {
2197 } else if (type
== KT_LETTER
) {
2199 } else if (value
== 0x7f) {
2200 value
= 8; /* make del = backspace */
2202 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2203 spk_close_press
= 0;
2210 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2214 static int keyboard_notifier_call(struct notifier_block
*nb
,
2215 unsigned long code
, void *_param
)
2217 struct keyboard_notifier_param
*param
= _param
;
2218 struct vc_data
*vc
= param
->vc
;
2219 int up
= !param
->down
;
2220 int ret
= NOTIFY_OK
;
2221 static int keycode
; /* to hold the current keycode */
2223 in_keyboard_notifier
= 1;
2225 if (vc
->vc_mode
== KD_GRAPHICS
)
2229 * First, determine whether we are handling a fake keypress on
2230 * the current processor. If we are, then return NOTIFY_OK,
2231 * to pass the keystroke up the chain. This prevents us from
2232 * trying to take the Speakup lock while it is held by the
2233 * processor on which the simulated keystroke was generated.
2234 * Also, the simulated keystrokes should be ignored by Speakup.
2237 if (speakup_fake_key_pressed())
2242 /* speakup requires keycode and keysym currently */
2243 keycode
= param
->value
;
2245 case KBD_UNBOUND_KEYCODE
:
2252 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2254 else if (KTYP(param
->value
) == KT_CUR
)
2255 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2257 case KBD_POST_KEYSYM
:{
2258 unsigned char type
= KTYP(param
->value
) - 0xf0;
2259 unsigned char val
= KVAL(param
->value
);
2263 do_handle_shift(vc
, val
, up
);
2267 do_handle_latin(vc
, val
, up
);
2270 do_handle_cursor(vc
, val
, up
);
2273 do_handle_spec(vc
, val
, up
);
2280 in_keyboard_notifier
= 0;
2284 static int vt_notifier_call(struct notifier_block
*nb
,
2285 unsigned long code
, void *_param
)
2287 struct vt_notifier_param
*param
= _param
;
2288 struct vc_data
*vc
= param
->vc
;
2292 if (vc
->vc_mode
== KD_TEXT
)
2293 speakup_allocate(vc
, GFP_ATOMIC
);
2296 speakup_deallocate(vc
);
2299 if (param
->c
== '\b') {
2304 speakup_con_write(vc
, &d
, 1);
2308 speakup_con_update(vc
);
2314 /* called by: module_exit() */
2315 static void __exit
speakup_exit(void)
2319 unregister_keyboard_notifier(&keyboard_notifier_block
);
2320 unregister_vt_notifier(&vt_notifier_block
);
2321 speakup_unregister_devsynth();
2322 speakup_cancel_paste();
2323 del_timer_sync(&cursor_timer
);
2324 kthread_stop(speakup_task
);
2325 speakup_task
= NULL
;
2326 mutex_lock(&spk_mutex
);
2328 mutex_unlock(&spk_mutex
);
2329 spk_ttyio_unregister_ldisc();
2331 speakup_kobj_exit();
2333 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2334 kfree(speakup_console
[i
]);
2336 speakup_remove_virtual_keyboard();
2338 for (i
= 0; i
< MAXVARS
; i
++)
2339 speakup_unregister_var(i
);
2341 for (i
= 0; i
< 256; i
++) {
2342 if (spk_characters
[i
] != spk_default_chars
[i
])
2343 kfree(spk_characters
[i
]);
2346 spk_free_user_msgs();
2349 /* call by: module_init() */
2350 static int __init
speakup_init(void)
2354 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2357 /* These first few initializations cannot fail. */
2358 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2359 spk_reset_default_chars();
2360 spk_reset_default_chartab();
2361 spk_strlwr(synth_name
);
2362 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2363 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2364 speakup_register_var(var
);
2365 for (var
= synth_time_vars
;
2366 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2367 speakup_register_var(var
);
2368 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2369 spk_set_mask_bits(NULL
, i
, 2);
2371 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2373 /* From here on out, initializations can fail. */
2374 err
= speakup_add_virtual_keyboard();
2376 goto error_virtkeyboard
;
2378 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2380 err
= speakup_allocate(vc_cons
[i
].d
, GFP_KERNEL
);
2382 goto error_kobjects
;
2386 spk_shut_up
|= 0x01;
2388 err
= speakup_kobj_init();
2390 goto error_kobjects
;
2392 spk_ttyio_register_ldisc();
2393 synth_init(synth_name
);
2394 speakup_register_devsynth();
2396 * register_devsynth might fail, but this error is not fatal.
2397 * /dev/synth is an extra feature; the rest of Speakup
2398 * will work fine without it.
2401 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2403 goto error_kbdnotifier
;
2404 err
= register_vt_notifier(&vt_notifier_block
);
2406 goto error_vtnotifier
;
2408 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2410 if (IS_ERR(speakup_task
)) {
2411 err
= PTR_ERR(speakup_task
);
2415 set_user_nice(speakup_task
, 10);
2416 wake_up_process(speakup_task
);
2418 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2419 pr_info("synth name on entry is: %s\n", synth_name
);
2423 unregister_vt_notifier(&vt_notifier_block
);
2426 unregister_keyboard_notifier(&keyboard_notifier_block
);
2427 del_timer(&cursor_timer
);
2430 speakup_unregister_devsynth();
2431 mutex_lock(&spk_mutex
);
2433 mutex_unlock(&spk_mutex
);
2434 speakup_kobj_exit();
2437 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2438 kfree(speakup_console
[i
]);
2440 speakup_remove_virtual_keyboard();
2443 for (i
= 0; i
< MAXVARS
; i
++)
2444 speakup_unregister_var(i
);
2446 for (i
= 0; i
< 256; i
++) {
2447 if (spk_characters
[i
] != spk_default_chars
[i
])
2448 kfree(spk_characters
[i
]);
2451 spk_free_user_msgs();
2457 module_init(speakup_init
);
2458 module_exit(speakup_exit
);