2 * review functions for the speakup screen review package.
3 * originally written by: Kirk Reiser and Andy Berdan.
5 * extensively modified by David Borowski.
7 ** Copyright (C) 1998 Kirk Reiser.
8 * Copyright (C) 2003 David Borowski.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <linux/kernel.h>
23 #include <linux/tty.h>
24 #include <linux/mm.h> /* __get_free_page() and friends */
25 #include <linux/vt_kern.h>
26 #include <linux/ctype.h>
27 #include <linux/selection.h>
28 #include <linux/unistd.h>
29 #include <linux/jiffies.h>
30 #include <linux/kthread.h>
31 #include <linux/keyboard.h> /* for KT_SHIFT */
32 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
33 #include <linux/input.h>
34 #include <linux/kmod.h>
36 /* speakup_*_selection */
37 #include <linux/module.h>
38 #include <linux/sched.h>
39 #include <linux/slab.h>
40 #include <linux/types.h>
41 #include <linux/consolemap.h>
43 #include <linux/spinlock.h>
44 #include <linux/notifier.h>
46 #include <linux/uaccess.h> /* copy_from|to|user() and others */
51 #define MAX_DELAY msecs_to_jiffies(500)
52 #define MINECHOCHAR SPACE
54 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
55 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
56 MODULE_DESCRIPTION("Speakup console speech");
57 MODULE_LICENSE("GPL");
58 MODULE_VERSION(SPEAKUP_VERSION
);
61 module_param_named(synth
, synth_name
, charp
, S_IRUGO
);
62 module_param_named(quiet
, spk_quiet_boot
, bool, S_IRUGO
);
64 MODULE_PARM_DESC(synth
, "Synth to start if speakup is built in.");
65 MODULE_PARM_DESC(quiet
, "Do not announce when the synthesizer is found.");
67 special_func spk_special_handler
;
69 short spk_pitch_shift
, synth_flags
;
71 int spk_attrib_bleep
, spk_bleeps
, spk_bleep_time
= 10;
72 int spk_no_intr
, spk_spell_delay
;
73 int spk_key_echo
, spk_say_word_ctl
;
74 int spk_say_ctrl
, spk_bell_pos
;
76 int spk_punc_level
, spk_reading_punc
;
77 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0";
78 char spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
79 const struct st_bits_data spk_punc_info
[] = {
81 {"some", "/$%&@", SOME
},
82 {"most", "$%&#()=+*/@^<>|\\", MOST
},
83 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
84 {"delimiters", "", B_WDLM
},
85 {"repeats", "()", CH_RPT
},
86 {"extended numeric", "", B_EXNUM
},
87 {"symbols", "", B_SYM
},
91 static char mark_cut_flag
;
93 static u_char
*spk_shift_table
;
94 u_char
*spk_our_keys
[MAX_KEY
];
95 u_char spk_key_buf
[600];
96 const u_char spk_key_defaults
[] = {
97 #include "speakupmap.h"
100 /* Speakup Cursor Track Variables */
101 static int cursor_track
= 1, prev_cursor_track
= 1;
103 /* cursor track modes, must be ordered same as cursor_msgs */
111 #define read_all_mode CT_Max
113 static struct tty_struct
*tty
;
115 static void spkup_write(const char *in_buf
, int count
);
117 static char *phonetic
[] = {
118 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
119 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
121 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
122 "x ray", "yankee", "zulu"
125 /* array of 256 char pointers (one for each character description)
126 * initialized to default_chars and user selectable via
127 * /proc/speakup/characters
129 char *spk_characters
[256];
131 char *spk_default_chars
[256] = {
132 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
133 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
134 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
135 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
137 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
139 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
142 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
144 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
145 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
146 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
147 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
148 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
151 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
152 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
153 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
154 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
155 /*127*/ "del", "control", "control", "control", "control", "control",
156 "control", "control", "control", "control", "control",
157 /*138*/ "control", "control", "control", "control", "control",
158 "control", "control", "control", "control", "control",
159 "control", "control",
160 /*150*/ "control", "control", "control", "control", "control",
161 "control", "control", "control", "control", "control",
162 /*160*/ "nbsp", "inverted bang",
163 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
164 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
165 /*172*/ "not", "soft hyphen", "registered", "macron",
166 /*176*/ "degrees", "plus or minus", "super two", "super three",
167 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
168 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
169 /*188*/ "one quarter", "one half", "three quarters",
171 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
173 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
175 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
177 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
178 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
180 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
181 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
182 /*230*/ "ae", "c cidella", "e grave", "e acute",
183 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
185 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
187 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
189 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
192 /* array of 256 u_short (one for each character)
193 * initialized to default_chartab and user selectable via
194 * /sys/module/speakup/parameters/chartab
196 u_short spk_chartab
[256];
198 static u_short default_chartab
[256] = {
199 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
200 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
201 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
202 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
203 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
204 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
205 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
206 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
207 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
208 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
209 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
210 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
211 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
212 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
213 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
214 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
215 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
217 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
219 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
221 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
223 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
225 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
226 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
227 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
228 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
229 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
230 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
231 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
232 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
233 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
234 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
235 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
238 struct task_struct
*speakup_task
;
239 struct bleep spk_unprocessed_sound
;
240 static int spk_keydown
;
241 static u_char spk_lastkey
, spk_close_press
, keymap_flags
;
242 static u_char last_keycode
, this_speakup_key
;
243 static u_long last_spk_jiffy
;
245 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
247 DEFINE_MUTEX(spk_mutex
);
249 static int keyboard_notifier_call(struct notifier_block
*,
250 unsigned long code
, void *param
);
252 static struct notifier_block keyboard_notifier_block
= {
253 .notifier_call
= keyboard_notifier_call
,
256 static int vt_notifier_call(struct notifier_block
*,
257 unsigned long code
, void *param
);
259 static struct notifier_block vt_notifier_block
= {
260 .notifier_call
= vt_notifier_call
,
263 static unsigned char get_attributes(struct vc_data
*vc
, u16
*pos
)
265 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
266 return (u_char
) (scr_readw(pos
) >> 8);
269 static void speakup_date(struct vc_data
*vc
)
271 spk_x
= spk_cx
= vc
->vc_x
;
272 spk_y
= spk_cy
= vc
->vc_y
;
273 spk_pos
= spk_cp
= vc
->vc_pos
;
274 spk_old_attr
= spk_attr
;
275 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
278 static void bleep(u_short val
)
280 static const short vals
[] = {
281 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
284 int time
= spk_bleep_time
;
286 freq
= vals
[val
% 12];
288 freq
*= (1 << (val
/ 12));
289 spk_unprocessed_sound
.freq
= freq
;
290 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
291 spk_unprocessed_sound
.active
= 1;
292 /* We can only have 1 active sound at a time. */
295 static void speakup_shut_up(struct vc_data
*vc
)
306 static void speech_kill(struct vc_data
*vc
)
308 char val
= synth
->is_alive(synth
);
313 /* re-enables synth, if disabled */
314 if (val
== 2 || spk_killed
) {
316 spk_shut_up
&= ~0x40;
317 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
319 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
324 static void speakup_off(struct vc_data
*vc
)
326 if (spk_shut_up
& 0x80) {
328 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
331 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
336 static void speakup_parked(struct vc_data
*vc
)
338 if (spk_parked
& 0x80) {
340 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
343 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
347 static void speakup_cut(struct vc_data
*vc
)
349 static const char err_buf
[] = "set selection failed";
352 if (!mark_cut_flag
) {
354 spk_xs
= (u_short
) spk_x
;
355 spk_ys
= (u_short
) spk_y
;
357 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
360 spk_xe
= (u_short
) spk_x
;
361 spk_ye
= (u_short
) spk_y
;
363 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
365 speakup_clear_selection();
366 ret
= speakup_set_selection(tty
);
370 break; /* no error */
372 pr_warn("%sEFAULT\n", err_buf
);
375 pr_warn("%sEINVAL\n", err_buf
);
378 pr_warn("%sENOMEM\n", err_buf
);
383 static void speakup_paste(struct vc_data
*vc
)
387 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
389 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
390 speakup_paste_selection(tty
);
394 static void say_attributes(struct vc_data
*vc
)
396 int fg
= spk_attr
& 0x0f;
397 int bg
= spk_attr
>> 4;
400 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
403 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
405 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
408 synth_printf(" %s ", spk_msg_get(MSG_ON
));
409 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
420 static void announce_edge(struct vc_data
*vc
, int msg_id
)
424 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
426 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
429 static void speak_char(u_char ch
)
431 char *cp
= spk_characters
[ch
];
432 struct var_t
*direct
= spk_get_var(DIRECT
);
434 if (direct
&& direct
->u
.n
.value
) {
435 if (IS_CHAR(ch
, B_CAP
)) {
437 synth_printf("%s", spk_str_caps_start
);
439 synth_printf("%c", ch
);
440 if (IS_CHAR(ch
, B_CAP
))
441 synth_printf("%s", spk_str_caps_stop
);
445 pr_info("speak_char: cp == NULL!\n");
448 synth_buffer_add(SPACE
);
449 if (IS_CHAR(ch
, B_CAP
)) {
451 synth_printf("%s", spk_str_caps_start
);
452 synth_printf("%s", cp
);
453 synth_printf("%s", spk_str_caps_stop
);
456 synth_printf("%s", spk_msg_get(MSG_CTRL
));
459 synth_printf("%s", cp
);
461 synth_buffer_add(SPACE
);
464 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
472 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
476 if (w
& vc
->vc_hi_font_mask
)
479 ch
= inverse_translate(vc
, c
, 0);
480 *attribs
= (w
& 0xff00) >> 8;
485 static void say_char(struct vc_data
*vc
)
489 spk_old_attr
= spk_attr
;
490 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
491 if (spk_attr
!= spk_old_attr
) {
492 if (spk_attrib_bleep
& 1)
494 if (spk_attrib_bleep
& 2)
497 speak_char(ch
& 0xff);
500 static void say_phonetic_char(struct vc_data
*vc
)
504 spk_old_attr
= spk_attr
;
505 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
506 if (isascii(ch
) && isalpha(ch
)) {
508 synth_printf("%s\n", phonetic
[--ch
]);
510 if (IS_CHAR(ch
, B_NUM
))
511 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
516 static void say_prev_char(struct vc_data
*vc
)
520 announce_edge(vc
, edge_left
);
528 static void say_next_char(struct vc_data
*vc
)
531 if (spk_x
== vc
->vc_cols
- 1) {
532 announce_edge(vc
, edge_right
);
540 /* get_word - will first check to see if the character under the
541 * reading cursor is a space and if spk_say_word_ctl is true it will
542 * return the word space. If spk_say_word_ctl is not set it will check to
543 * see if there is a word starting on the next position to the right
544 * and return that word if it exists. If it does not exist it will
545 * move left to the beginning of any previous word on the line or the
546 * beginning off the line whichever comes first..
549 static u_long
get_word(struct vc_data
*vc
)
551 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
556 spk_old_attr
= spk_attr
;
557 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
559 /* decided to take out the sayword if on a space (mis-information */
560 if (spk_say_word_ctl
&& ch
== SPACE
) {
562 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
564 } else if ((tmpx
< vc
->vc_cols
- 2)
565 && (ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
566 && ((char)get_char(vc
, (u_short
*) &tmp_pos
+ 1, &temp
) >
572 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
- 1, &temp
);
573 if ((ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
574 && ((char)get_char(vc
, (u_short
*) tmp_pos
, &temp
) >
580 attr_ch
= get_char(vc
, (u_short
*) tmp_pos
, &spk_attr
);
581 buf
[cnt
++] = attr_ch
& 0xff;
582 while (tmpx
< vc
->vc_cols
- 1) {
585 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
586 if ((ch
== SPACE
) || ch
== 0
587 || (IS_WDLM(buf
[cnt
- 1]) && (ch
> SPACE
)))
595 static void say_word(struct vc_data
*vc
)
597 u_long cnt
= get_word(vc
);
598 u_short saved_punc_mask
= spk_punc_mask
;
602 spk_punc_mask
= PUNC
;
604 spkup_write(buf
, cnt
);
605 spk_punc_mask
= saved_punc_mask
;
608 static void say_prev_word(struct vc_data
*vc
)
612 u_short edge_said
= 0, last_state
= 0, state
= 0;
618 announce_edge(vc
, edge_top
);
623 edge_said
= edge_quiet
;
628 edge_said
= edge_top
;
631 if (edge_said
!= edge_quiet
)
632 edge_said
= edge_left
;
636 spk_x
= vc
->vc_cols
- 1;
640 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
641 if (ch
== SPACE
|| ch
== 0)
643 else if (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
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
674 if (ch
== SPACE
|| ch
== 0)
676 else if (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
;
697 announce_edge(vc
, edge_said
);
701 static void spell_word(struct vc_data
*vc
)
703 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
704 char *cp
= buf
, *str_cap
= spk_str_caps_stop
;
705 char *cp1
, *last_cap
= spk_str_caps_stop
;
710 while ((ch
= (u_char
) *cp
)) {
712 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
713 if (IS_CHAR(ch
, B_CAP
)) {
714 str_cap
= spk_str_caps_start
;
715 if (*spk_str_caps_stop
)
717 else /* synth has no pitch */
718 last_cap
= spk_str_caps_stop
;
720 str_cap
= spk_str_caps_stop
;
721 if (str_cap
!= last_cap
) {
722 synth_printf("%s", str_cap
);
725 if (this_speakup_key
== SPELL_PHONETIC
726 && (isascii(ch
) && isalpha(ch
))) {
728 cp1
= phonetic
[--ch
];
730 cp1
= spk_characters
[ch
];
732 synth_printf("%s", spk_msg_get(MSG_CTRL
));
736 synth_printf("%s", cp1
);
739 if (str_cap
!= spk_str_caps_stop
)
740 synth_printf("%s", spk_str_caps_stop
);
743 static int get_line(struct vc_data
*vc
)
745 u_long tmp
= spk_pos
- (spk_x
* 2);
749 spk_old_attr
= spk_attr
;
750 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
751 for (i
= 0; i
< vc
->vc_cols
; i
++) {
752 buf
[i
] = (u_char
) get_char(vc
, (u_short
*) tmp
, &tmp2
);
755 for (--i
; i
>= 0; i
--)
761 static void say_line(struct vc_data
*vc
)
763 int i
= get_line(vc
);
765 u_short saved_punc_mask
= spk_punc_mask
;
768 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
772 if (this_speakup_key
== SAY_LINE_INDENT
) {
776 synth_printf("%d, ", (cp
- buf
) + 1);
778 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
780 spk_punc_mask
= saved_punc_mask
;
783 static void say_prev_line(struct vc_data
*vc
)
787 announce_edge(vc
, edge_top
);
791 spk_pos
-= vc
->vc_size_row
;
795 static void say_next_line(struct vc_data
*vc
)
798 if (spk_y
== vc
->vc_rows
- 1) {
799 announce_edge(vc
, edge_bottom
);
803 spk_pos
+= vc
->vc_size_row
;
807 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
812 u_short saved_punc_mask
= spk_punc_mask
;
814 spk_old_attr
= spk_attr
;
815 spk_attr
= get_attributes(vc
, (u_short
*)from
);
817 buf
[i
++] = (char)get_char(vc
, (u_short
*) from
, &tmp
);
819 if (i
>= vc
->vc_size_row
)
822 for (--i
; i
>= 0; i
--)
830 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
833 spk_punc_mask
= saved_punc_mask
;
837 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
840 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
841 u_long end
= start
+ (to
* 2);
844 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
845 if (cursor_track
!= read_all_mode
)
846 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
849 /* Sentence Reading Commands */
851 static int currsentence
;
852 static int numsentences
[2];
853 static char *sentbufend
[2];
854 static char *sentmarks
[2][10];
857 static char sentbuf
[2][256];
859 static int say_sentence_num(int num
, int prev
)
862 currsentence
= num
+ 1;
863 if (prev
&& --bn
== -1)
866 if (num
> numsentences
[bn
])
869 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
873 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
883 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
884 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
886 numsentences
[bn
] = 0;
887 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
889 spk_old_attr
= spk_attr
;
890 spk_attr
= get_attributes(vc
, (u_short
*)start
);
892 while (start
< end
) {
893 sentbuf
[bn
][i
] = (char)get_char(vc
, (u_short
*) start
, &tmp
);
895 if (sentbuf
[bn
][i
] == SPACE
&& sentbuf
[bn
][i
- 1] == '.'
896 && numsentences
[bn
] < 9) {
897 /* Sentence Marker */
899 sentmarks
[bn
][numsentences
[bn
]] =
905 if (i
>= vc
->vc_size_row
)
909 for (--i
; i
>= 0; i
--)
910 if (sentbuf
[bn
][i
] != SPACE
)
916 sentbuf
[bn
][++i
] = SPACE
;
917 sentbuf
[bn
][++i
] = '\0';
919 sentbufend
[bn
] = &sentbuf
[bn
][i
];
920 return numsentences
[bn
];
923 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
925 u_long start
= vc
->vc_origin
, end
;
928 start
+= from
* vc
->vc_size_row
;
929 if (to
> vc
->vc_rows
)
931 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
932 for (from
= start
; from
< end
; from
= to
) {
933 to
= from
+ vc
->vc_size_row
;
934 say_from_to(vc
, from
, to
, 1);
938 static void say_screen(struct vc_data
*vc
)
940 say_screen_from_to(vc
, 0, vc
->vc_rows
);
943 static void speakup_win_say(struct vc_data
*vc
)
945 u_long start
, end
, from
, to
;
948 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
951 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
952 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
953 while (start
<= end
) {
954 from
= start
+ (win_left
* 2);
955 to
= start
+ (win_right
* 2);
956 say_from_to(vc
, from
, to
, 1);
957 start
+= vc
->vc_size_row
;
961 static void top_edge(struct vc_data
*vc
)
964 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
969 static void bottom_edge(struct vc_data
*vc
)
972 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
973 spk_y
= vc
->vc_rows
- 1;
977 static void left_edge(struct vc_data
*vc
)
980 spk_pos
-= spk_x
* 2;
985 static void right_edge(struct vc_data
*vc
)
988 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
989 spk_x
= vc
->vc_cols
- 1;
993 static void say_first_char(struct vc_data
*vc
)
995 int i
, len
= get_line(vc
);
1000 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1003 for (i
= 0; i
< len
; i
++)
1004 if (buf
[i
] != SPACE
)
1007 spk_pos
-= (spk_x
- i
) * 2;
1009 synth_printf("%d, ", ++i
);
1013 static void say_last_char(struct vc_data
*vc
)
1015 int len
= get_line(vc
);
1020 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1024 spk_pos
-= (spk_x
- len
) * 2;
1026 synth_printf("%d, ", ++len
);
1030 static void say_position(struct vc_data
*vc
)
1032 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1037 /* Added by brianb */
1038 static void say_char_num(struct vc_data
*vc
)
1041 u_short ch
= get_char(vc
, (u_short
*) spk_pos
, &tmp
);
1044 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1047 /* these are stub functions to keep keyboard.c happy. */
1049 static void say_from_top(struct vc_data
*vc
)
1051 say_screen_from_to(vc
, 0, spk_y
);
1054 static void say_to_bottom(struct vc_data
*vc
)
1056 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1059 static void say_from_left(struct vc_data
*vc
)
1061 say_line_from_to(vc
, 0, spk_x
, 1);
1064 static void say_to_right(struct vc_data
*vc
)
1066 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1069 /* end of stub functions. */
1071 static void spkup_write(const char *in_buf
, int count
)
1073 static int rep_count
;
1074 static u_char ch
= '\0', old_ch
= '\0';
1075 static u_short char_type
, last_type
;
1076 int in_count
= count
;
1080 if (cursor_track
== read_all_mode
) {
1081 /* Insert Sentence Index */
1082 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1083 (currsentence
<= numsentences
[bn
]))
1084 synth_insert_next_index(currsentence
++);
1086 ch
= (u_char
) *in_buf
++;
1087 char_type
= spk_chartab
[ch
];
1088 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1089 if (++rep_count
> 2)
1092 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1094 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1100 if (ch
== spk_lastkey
) {
1102 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1104 } else if (char_type
& B_ALPHA
) {
1105 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1106 synth_buffer_add(SPACE
);
1107 synth_printf("%c", ch
);
1108 } else if (char_type
& B_NUM
) {
1110 synth_printf("%c", ch
);
1111 } else if (char_type
& spk_punc_mask
) {
1113 char_type
&= ~PUNC
; /* for dec nospell processing */
1114 } else if (char_type
& SYNTH_OK
) {
1115 /* these are usually puncts like . and , which synth
1116 * needs for expression.
1117 * suppress multiple to get rid of long pauses and
1118 * clear repeat count
1120 * repeats on you don't get nothing repeated count
1123 synth_printf("%c", ch
);
1127 /* send space and record position, if next is num overwrite space */
1129 synth_buffer_add(SPACE
);
1134 last_type
= char_type
;
1137 if (in_count
> 2 && rep_count
> 2) {
1138 if (last_type
& CH_RPT
) {
1140 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1148 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1150 static void read_all_doc(struct vc_data
*vc
);
1151 static void cursor_done(u_long data
);
1152 static DEFINE_TIMER(cursor_timer
, cursor_done
, 0, 0);
1154 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1156 unsigned long flags
;
1158 if (synth
== NULL
|| up_flag
|| spk_killed
)
1160 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1161 if (cursor_track
== read_all_mode
) {
1164 del_timer(&cursor_timer
);
1165 spk_shut_up
&= 0xfe;
1170 del_timer(&cursor_timer
);
1171 cursor_track
= prev_cursor_track
;
1172 spk_shut_up
&= 0xfe;
1177 spk_shut_up
&= 0xfe;
1180 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1181 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1182 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1185 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1187 unsigned long flags
;
1189 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1191 spk_lastkey
= spk_keydown
= 0;
1192 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1195 if (synth
== NULL
|| spk_killed
) {
1196 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1199 spk_shut_up
&= 0xfe;
1200 spk_lastkey
= value
;
1203 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1205 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1208 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1210 int i
= 0, states
, key_data_len
;
1211 const u_char
*cp
= key_info
;
1212 u_char
*cp1
= k_buffer
;
1213 u_char ch
, version
, num_keys
;
1216 if (version
!= KEY_MAP_VER
)
1219 states
= (int)cp
[1];
1220 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1221 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
))
1223 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1224 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1225 spk_shift_table
= k_buffer
;
1226 spk_our_keys
[0] = spk_shift_table
;
1227 cp1
+= SHIFT_TBL_SIZE
;
1228 memcpy(cp1
, cp
, key_data_len
+ 3);
1229 /* get num_keys, states and data */
1230 cp1
+= 2; /* now pointing at shift states */
1231 for (i
= 1; i
<= states
; i
++) {
1233 if (ch
>= SHIFT_TBL_SIZE
)
1235 spk_shift_table
[ch
] = i
;
1237 keymap_flags
= *cp1
++;
1238 while ((ch
= *cp1
)) {
1241 spk_our_keys
[ch
] = cp1
;
1247 static struct var_t spk_vars
[] = {
1248 /* bell must be first to set high limit */
1249 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1250 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1251 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1252 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1253 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1254 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1255 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1256 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1257 {SAY_CONTROL
, TOGGLE_0
},
1258 {SAY_WORD_CTL
, TOGGLE_0
},
1259 {NO_INTERRUPT
, TOGGLE_0
},
1260 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1264 static void toggle_cursoring(struct vc_data
*vc
)
1266 if (cursor_track
== read_all_mode
)
1267 cursor_track
= prev_cursor_track
;
1268 if (++cursor_track
>= CT_Max
)
1270 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1273 void spk_reset_default_chars(void)
1277 /* First, free any non-default */
1278 for (i
= 0; i
< 256; i
++) {
1279 if ((spk_characters
[i
] != NULL
)
1280 && (spk_characters
[i
] != spk_default_chars
[i
]))
1281 kfree(spk_characters
[i
]);
1284 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1287 void spk_reset_default_chartab(void)
1289 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1292 static const struct st_bits_data
*pb_edit
;
1294 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1296 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1298 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1301 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1302 spk_special_handler
= NULL
;
1305 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1307 spk_chartab
[ch
] ^= mask
;
1309 synth_printf(" %s\n",
1310 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1311 spk_msg_get(MSG_OFF
));
1315 /* Allocation concurrency is protected by the console semaphore */
1316 static int speakup_allocate(struct vc_data
*vc
)
1320 vc_num
= vc
->vc_num
;
1321 if (speakup_console
[vc_num
] == NULL
) {
1322 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1324 if (speakup_console
[vc_num
] == NULL
)
1327 } else if (!spk_parked
)
1333 static void speakup_deallocate(struct vc_data
*vc
)
1337 vc_num
= vc
->vc_num
;
1338 kfree(speakup_console
[vc_num
]);
1339 speakup_console
[vc_num
] = NULL
;
1342 static u_char is_cursor
;
1343 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1344 static int cursor_con
;
1346 static void reset_highlight_buffers(struct vc_data
*);
1348 static int read_all_key
;
1350 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1364 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1366 del_timer(&cursor_timer
);
1367 speakup_fake_down_arrow();
1368 start_read_all_timer(vc
, command
);
1371 static void read_all_doc(struct vc_data
*vc
)
1373 if ((vc
->vc_num
!= fg_console
) || synth
== NULL
|| spk_shut_up
)
1375 if (!synth_supports_indexing())
1377 if (cursor_track
!= read_all_mode
)
1378 prev_cursor_track
= cursor_track
;
1379 cursor_track
= read_all_mode
;
1380 spk_reset_index_count(0);
1381 if (get_sentence_buf(vc
, 0) == -1)
1382 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1384 say_sentence_num(0, 0);
1385 synth_insert_next_index(0);
1386 start_read_all_timer(vc
, RA_TIMER
);
1390 static void stop_read_all(struct vc_data
*vc
)
1392 del_timer(&cursor_timer
);
1393 cursor_track
= prev_cursor_track
;
1394 spk_shut_up
&= 0xfe;
1398 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1400 struct var_t
*cursor_timeout
;
1402 cursor_con
= vc
->vc_num
;
1403 read_all_key
= command
;
1404 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1405 mod_timer(&cursor_timer
,
1406 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1409 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1411 int indcount
, sentcount
, rv
, sn
;
1415 /* Get Current Sentence */
1416 spk_get_index_count(&indcount
, &sentcount
);
1417 /*printk("%d %d ", indcount, sentcount); */
1418 spk_reset_index_count(sentcount
+ 1);
1419 if (indcount
== 1) {
1420 if (!say_sentence_num(sentcount
+ 1, 0)) {
1421 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1424 synth_insert_next_index(0);
1427 if (!say_sentence_num(sentcount
+ 1, 1)) {
1429 spk_reset_index_count(sn
);
1431 synth_insert_next_index(0);
1432 if (!say_sentence_num(sn
, 0)) {
1433 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1436 synth_insert_next_index(0);
1438 start_read_all_timer(vc
, RA_TIMER
);
1448 if (get_sentence_buf(vc
, 0) == -1) {
1449 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1451 say_sentence_num(0, 0);
1452 synth_insert_next_index(0);
1453 start_read_all_timer(vc
, RA_TIMER
);
1456 case RA_FIND_NEXT_SENT
:
1457 rv
= get_sentence_buf(vc
, 0);
1461 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1463 say_sentence_num(1, 0);
1464 synth_insert_next_index(0);
1465 start_read_all_timer(vc
, RA_TIMER
);
1468 case RA_FIND_PREV_SENT
:
1471 spk_get_index_count(&indcount
, &sentcount
);
1473 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1475 start_read_all_timer(vc
, RA_TIMER
);
1480 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1482 unsigned long flags
;
1484 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1485 if (cursor_track
== read_all_mode
) {
1487 if (synth
== NULL
|| up_flag
|| spk_shut_up
) {
1488 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1491 del_timer(&cursor_timer
);
1492 spk_shut_up
&= 0xfe;
1494 start_read_all_timer(vc
, value
+ 1);
1495 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1498 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1502 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1504 unsigned long flags
;
1505 struct var_t
*cursor_timeout
;
1507 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1509 if (synth
== NULL
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1510 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1513 spk_shut_up
&= 0xfe;
1516 /* the key press flushes if !no_inter but we want to flush on cursor
1517 * moves regardless of no_inter state
1519 is_cursor
= value
+ 1;
1520 old_cursor_pos
= vc
->vc_pos
;
1521 old_cursor_x
= vc
->vc_x
;
1522 old_cursor_y
= vc
->vc_y
;
1523 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1524 cursor_con
= vc
->vc_num
;
1525 if (cursor_track
== CT_Highlight
)
1526 reset_highlight_buffers(vc
);
1527 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1528 mod_timer(&cursor_timer
,
1529 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1530 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1533 static void update_color_buffer(struct vc_data
*vc
, const char *ic
, int len
)
1536 int vc_num
= vc
->vc_num
;
1538 bi
= (vc
->vc_attr
& 0x70) >> 4;
1539 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1542 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1543 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1544 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1545 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1547 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1548 if ((ic
[i
] > 32) && (ic
[i
] < 127)) {
1549 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1551 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1552 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1554 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1561 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1564 static void reset_highlight_buffers(struct vc_data
*vc
)
1567 int vc_num
= vc
->vc_num
;
1569 for (i
= 0; i
< 8; i
++)
1570 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1573 static int count_highlight_color(struct vc_data
*vc
)
1577 int vc_num
= vc
->vc_num
;
1579 u16
*start
= (u16
*) vc
->vc_origin
;
1581 for (i
= 0; i
< 8; i
++)
1582 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1584 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1585 u16
*end
= start
+ vc
->vc_cols
* 2;
1588 for (ptr
= start
; ptr
< end
; ptr
++) {
1589 ch
= get_attributes(vc
, ptr
);
1590 bg
= (ch
& 0x70) >> 4;
1591 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1593 start
+= vc
->vc_size_row
;
1597 for (i
= 0; i
< 8; i
++)
1598 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1603 static int get_highlight_color(struct vc_data
*vc
)
1606 unsigned int cptr
[8];
1607 int vc_num
= vc
->vc_num
;
1609 for (i
= 0; i
< 8; i
++)
1612 for (i
= 0; i
< 7; i
++)
1613 for (j
= i
+ 1; j
< 8; j
++)
1614 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1615 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]])
1616 swap(cptr
[i
], cptr
[j
]);
1618 for (i
= 0; i
< 8; i
++)
1619 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1620 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1625 static int speak_highlight(struct vc_data
*vc
)
1628 int vc_num
= vc
->vc_num
;
1630 if (count_highlight_color(vc
) == 1)
1632 hc
= get_highlight_color(vc
);
1634 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1635 if ((d
== 1) || (d
== -1))
1636 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1640 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1641 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1642 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1643 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1644 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1650 static void cursor_done(u_long data
)
1652 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1653 unsigned long flags
;
1655 del_timer(&cursor_timer
);
1656 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1657 if (cursor_con
!= fg_console
) {
1663 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1664 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1665 spk_keydown
= is_cursor
= 0;
1669 if (cursor_track
== read_all_mode
) {
1670 handle_cursor_read_all(vc
, read_all_key
);
1673 if (cursor_track
== CT_Highlight
) {
1674 if (speak_highlight(vc
)) {
1675 spk_keydown
= is_cursor
= 0;
1679 if (cursor_track
== CT_Window
)
1680 speakup_win_say(vc
);
1681 else if (is_cursor
== 1 || is_cursor
== 4)
1682 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1685 spk_keydown
= is_cursor
= 0;
1687 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1690 /* called by: vt_notifier_call() */
1691 static void speakup_bs(struct vc_data
*vc
)
1693 unsigned long flags
;
1695 if (!speakup_console
[vc
->vc_num
])
1697 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1698 /* Speakup output, discard */
1702 if (spk_shut_up
|| synth
== NULL
) {
1703 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1706 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1711 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1714 /* called by: vt_notifier_call() */
1715 static void speakup_con_write(struct vc_data
*vc
, const char *str
, int len
)
1717 unsigned long flags
;
1719 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| synth
== NULL
)
1721 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1722 /* Speakup output, discard */
1724 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1726 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1727 if (cursor_track
== CT_Highlight
)
1728 update_color_buffer(vc
, str
, len
);
1729 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1733 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1734 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1735 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1740 spkup_write(str
, len
);
1741 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1744 static void speakup_con_update(struct vc_data
*vc
)
1746 unsigned long flags
;
1748 if (speakup_console
[vc
->vc_num
] == NULL
|| spk_parked
)
1750 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1751 /* Speakup output, discard */
1754 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1757 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1759 unsigned long flags
;
1763 if (synth
== NULL
|| up_flag
|| spk_killed
)
1765 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1766 spk_shut_up
&= 0xfe;
1771 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1772 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1775 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1776 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1779 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1780 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1781 if (speakup_console
[vc
->vc_num
])
1782 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1786 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1790 synth_printf("%s %s\n",
1791 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1792 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1795 static int inc_dec_var(u_char value
)
1797 struct st_var_header
*p_header
;
1798 struct var_t
*var_data
;
1802 int var_id
= (int)value
- VAR_START
;
1803 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1805 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1806 p_header
= spk_get_var_header(var_id
);
1807 if (p_header
== NULL
)
1809 if (p_header
->var_type
!= VAR_NUM
)
1811 var_data
= p_header
->data
;
1812 if (spk_set_num_var(1, p_header
, how
) != 0)
1814 if (!spk_close_press
) {
1815 for (pn
= p_header
->name
; *pn
; pn
++) {
1822 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1823 var_data
->u
.n
.value
);
1824 synth_printf("%s", num_buf
);
1828 static void speakup_win_set(struct vc_data
*vc
)
1832 if (win_start
> 1) {
1833 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1836 if (spk_x
< win_left
|| spk_y
< win_top
) {
1837 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1840 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1842 win_right
= vc
->vc_cols
- 1;
1844 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1854 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1856 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1857 (int)spk_y
+ 1, (int)spk_x
+ 1);
1859 synth_printf("%s\n", info
);
1863 static void speakup_win_clear(struct vc_data
*vc
)
1865 win_top
= win_bottom
= 0;
1866 win_left
= win_right
= 0;
1868 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1871 static void speakup_win_enable(struct vc_data
*vc
)
1873 if (win_start
< 2) {
1874 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1879 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1881 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1884 static void speakup_bits(struct vc_data
*vc
)
1886 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1888 if (spk_special_handler
!= NULL
|| val
< 1 || val
> 6) {
1889 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1892 pb_edit
= &spk_punc_info
[val
];
1893 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1894 spk_special_handler
= edit_bits
;
1897 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1899 static u_char goto_buf
[8];
1904 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1906 if (type
== KT_LATIN
&& ch
== '\n')
1913 ch
= goto_buf
[--num
];
1914 goto_buf
[num
] = '\0';
1915 spkup_write(&ch
, 1);
1918 if (ch
< '+' || ch
> 'y')
1920 goto_buf
[num
++] = ch
;
1921 goto_buf
[num
] = '\0';
1922 spkup_write(&ch
, 1);
1923 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1924 if ((ch
== '+' || ch
== '-') && num
== 1)
1926 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1928 if (num
< maxlen
- 1 || num
> maxlen
)
1930 if (ch
< 'x' || ch
> 'y') {
1933 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1934 goto_buf
[num
= 0] = '\0';
1935 spk_special_handler
= NULL
;
1939 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1942 if (*goto_buf
< '0')
1944 else if (goto_pos
> 0)
1947 if (goto_pos
>= vc
->vc_cols
)
1948 goto_pos
= vc
->vc_cols
- 1;
1951 if (*goto_buf
< '0')
1953 else if (goto_pos
> 0)
1956 if (goto_pos
>= vc
->vc_rows
)
1957 goto_pos
= vc
->vc_rows
- 1;
1960 goto_buf
[num
= 0] = '\0';
1962 spk_special_handler
= NULL
;
1965 spk_pos
-= spk_x
* 2;
1967 spk_pos
+= goto_pos
* 2;
1971 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
1977 static void speakup_goto(struct vc_data
*vc
)
1979 if (spk_special_handler
!= NULL
) {
1980 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1983 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
1984 spk_special_handler
= handle_goto
;
1987 static void speakup_help(struct vc_data
*vc
)
1989 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
1992 static void do_nothing(struct vc_data
*vc
)
1994 return; /* flush done in do_spkup */
1997 static u_char key_speakup
, spk_key_locked
;
1999 static void speakup_lock(struct vc_data
*vc
)
2001 if (!spk_key_locked
)
2002 spk_key_locked
= key_speakup
= 16;
2004 spk_key_locked
= key_speakup
= 0;
2007 typedef void (*spkup_hand
) (struct vc_data
*);
2008 static spkup_hand spkup_handler
[] = {
2009 /* must be ordered same as defines in speakup.h */
2010 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2011 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2012 say_char
, say_prev_char
, say_next_char
,
2013 say_word
, say_prev_word
, say_next_word
,
2014 say_line
, say_prev_line
, say_next_line
,
2015 top_edge
, bottom_edge
, left_edge
, right_edge
,
2016 spell_word
, spell_word
, say_screen
,
2017 say_position
, say_attributes
,
2018 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2019 say_from_top
, say_to_bottom
,
2020 say_from_left
, say_to_right
,
2021 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2022 speakup_bits
, speakup_bits
, speakup_bits
,
2023 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2024 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2027 static void do_spkup(struct vc_data
*vc
, u_char value
)
2029 if (spk_killed
&& value
!= SPEECH_KILL
)
2033 spk_shut_up
&= 0xfe;
2034 this_speakup_key
= value
;
2035 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2037 (*spkup_handler
[value
]) (vc
);
2039 if (inc_dec_var(value
) < 0)
2044 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2047 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2050 unsigned long flags
;
2053 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2054 u_char shift_info
, offset
;
2060 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2065 && (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2070 value
= spk_lastkey
= pad_chars
[value
];
2075 if (keycode
>= MAX_KEY
)
2077 key_info
= spk_our_keys
[keycode
];
2080 /* Check valid read all mode keys */
2081 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2095 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2096 offset
= spk_shift_table
[shift_info
];
2098 new_key
= key_info
[offset
];
2101 if (new_key
== SPK_KEY
) {
2102 if (!spk_key_locked
)
2103 key_speakup
= (up_flag
) ? 0 : 16;
2104 if (up_flag
|| spk_killed
)
2106 spk_shut_up
&= 0xfe;
2112 if (last_keycode
== keycode
&&
2113 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2114 spk_close_press
= 1;
2115 offset
= spk_shift_table
[shift_info
+ 32];
2117 if (offset
&& key_info
[offset
])
2118 new_key
= key_info
[offset
];
2120 last_keycode
= keycode
;
2121 last_spk_jiffy
= jiffies
;
2127 if (type
== KT_SPKUP
&& spk_special_handler
== NULL
) {
2128 do_spkup(vc
, new_key
);
2129 spk_close_press
= 0;
2133 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2135 spk_shut_up
&= 0xfe;
2136 kh
= (value
== KVAL(K_DOWN
))
2137 || (value
== KVAL(K_UP
))
2138 || (value
== KVAL(K_LEFT
))
2139 || (value
== KVAL(K_RIGHT
));
2140 if ((cursor_track
!= read_all_mode
) || !kh
)
2143 if (spk_special_handler
) {
2144 if (type
== KT_SPEC
&& value
== 1) {
2147 } else if (type
== KT_LETTER
)
2149 else if (value
== 0x7f)
2150 value
= 8; /* make del = backspace */
2151 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2152 spk_close_press
= 0;
2159 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2163 static int keyboard_notifier_call(struct notifier_block
*nb
,
2164 unsigned long code
, void *_param
)
2166 struct keyboard_notifier_param
*param
= _param
;
2167 struct vc_data
*vc
= param
->vc
;
2168 int up
= !param
->down
;
2169 int ret
= NOTIFY_OK
;
2170 static int keycode
; /* to hold the current keycode */
2172 if (vc
->vc_mode
== KD_GRAPHICS
)
2176 * First, determine whether we are handling a fake keypress on
2177 * the current processor. If we are, then return NOTIFY_OK,
2178 * to pass the keystroke up the chain. This prevents us from
2179 * trying to take the Speakup lock while it is held by the
2180 * processor on which the simulated keystroke was generated.
2181 * Also, the simulated keystrokes should be ignored by Speakup.
2184 if (speakup_fake_key_pressed())
2189 /* speakup requires keycode and keysym currently */
2190 keycode
= param
->value
;
2192 case KBD_UNBOUND_KEYCODE
:
2199 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2201 else if (KTYP(param
->value
) == KT_CUR
)
2202 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2204 case KBD_POST_KEYSYM
:{
2205 unsigned char type
= KTYP(param
->value
) - 0xf0;
2206 unsigned char val
= KVAL(param
->value
);
2210 do_handle_shift(vc
, val
, up
);
2214 do_handle_latin(vc
, val
, up
);
2217 do_handle_cursor(vc
, val
, up
);
2220 do_handle_spec(vc
, val
, up
);
2229 static int vt_notifier_call(struct notifier_block
*nb
,
2230 unsigned long code
, void *_param
)
2232 struct vt_notifier_param
*param
= _param
;
2233 struct vc_data
*vc
= param
->vc
;
2237 if (vc
->vc_mode
== KD_TEXT
)
2238 speakup_allocate(vc
);
2241 speakup_deallocate(vc
);
2244 if (param
->c
== '\b')
2246 else if (param
->c
< 0x100) {
2249 speakup_con_write(vc
, &d
, 1);
2253 speakup_con_update(vc
);
2259 /* called by: module_exit() */
2260 static void __exit
speakup_exit(void)
2264 unregister_keyboard_notifier(&keyboard_notifier_block
);
2265 unregister_vt_notifier(&vt_notifier_block
);
2266 speakup_unregister_devsynth();
2267 speakup_cancel_paste();
2268 del_timer(&cursor_timer
);
2269 kthread_stop(speakup_task
);
2270 speakup_task
= NULL
;
2271 mutex_lock(&spk_mutex
);
2273 mutex_unlock(&spk_mutex
);
2275 speakup_kobj_exit();
2277 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2278 kfree(speakup_console
[i
]);
2280 speakup_remove_virtual_keyboard();
2282 for (i
= 0; i
< MAXVARS
; i
++)
2283 speakup_unregister_var(i
);
2285 for (i
= 0; i
< 256; i
++) {
2286 if (spk_characters
[i
] != spk_default_chars
[i
])
2287 kfree(spk_characters
[i
]);
2290 spk_free_user_msgs();
2293 /* call by: module_init() */
2294 static int __init
speakup_init(void)
2298 struct st_spk_t
*first_console
;
2299 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2302 /* These first few initializations cannot fail. */
2303 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2304 spk_reset_default_chars();
2305 spk_reset_default_chartab();
2306 spk_strlwr(synth_name
);
2307 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2308 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2309 speakup_register_var(var
);
2310 for (var
= synth_time_vars
;
2311 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2312 speakup_register_var(var
);
2313 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2314 spk_set_mask_bits(NULL
, i
, 2);
2316 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2318 /* From here on out, initializations can fail. */
2319 err
= speakup_add_virtual_keyboard();
2321 goto error_virtkeyboard
;
2323 first_console
= kzalloc(sizeof(*first_console
), GFP_KERNEL
);
2324 if (!first_console
) {
2329 speakup_console
[vc
->vc_num
] = first_console
;
2332 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2334 err
= speakup_allocate(vc_cons
[i
].d
);
2336 goto error_kobjects
;
2340 spk_shut_up
|= 0x01;
2342 err
= speakup_kobj_init();
2344 goto error_kobjects
;
2346 synth_init(synth_name
);
2347 speakup_register_devsynth();
2349 * register_devsynth might fail, but this error is not fatal.
2350 * /dev/synth is an extra feature; the rest of Speakup
2351 * will work fine without it.
2354 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2356 goto error_kbdnotifier
;
2357 err
= register_vt_notifier(&vt_notifier_block
);
2359 goto error_vtnotifier
;
2361 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2363 if (IS_ERR(speakup_task
)) {
2364 err
= PTR_ERR(speakup_task
);
2368 set_user_nice(speakup_task
, 10);
2369 wake_up_process(speakup_task
);
2371 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2372 pr_info("synth name on entry is: %s\n", synth_name
);
2376 unregister_vt_notifier(&vt_notifier_block
);
2379 unregister_keyboard_notifier(&keyboard_notifier_block
);
2380 del_timer(&cursor_timer
);
2383 speakup_unregister_devsynth();
2384 mutex_lock(&spk_mutex
);
2386 mutex_unlock(&spk_mutex
);
2387 speakup_kobj_exit();
2390 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2391 kfree(speakup_console
[i
]);
2394 speakup_remove_virtual_keyboard();
2397 for (i
= 0; i
< MAXVARS
; i
++)
2398 speakup_unregister_var(i
);
2400 for (i
= 0; i
< 256; i
++) {
2401 if (spk_characters
[i
] != spk_default_chars
[i
])
2402 kfree(spk_characters
[i
]);
2405 spk_free_user_msgs();
2411 module_init(speakup_init
);
2412 module_exit(speakup_exit
);