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.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <linux/kernel.h>
27 #include <linux/tty.h>
28 #include <linux/mm.h> /* __get_free_page() and friends */
29 #include <linux/vt_kern.h>
30 #include <linux/ctype.h>
31 #include <linux/selection.h>
32 #include <linux/unistd.h>
33 #include <linux/jiffies.h>
34 #include <linux/kthread.h>
35 #include <linux/keyboard.h> /* for KT_SHIFT */
36 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
37 #include <linux/input.h>
38 #include <linux/kmod.h>
40 /* speakup_*_selection */
41 #include <linux/module.h>
42 #include <linux/sched.h>
43 #include <linux/slab.h>
44 #include <linux/types.h>
45 #include <linux/consolemap.h>
47 #include <linux/spinlock.h>
48 #include <linux/notifier.h>
50 #include <linux/uaccess.h> /* copy_from|to|user() and others */
55 #define MAX_DELAY msecs_to_jiffies(500)
56 #define MINECHOCHAR SPACE
58 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
59 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
60 MODULE_DESCRIPTION("Speakup console speech");
61 MODULE_LICENSE("GPL");
62 MODULE_VERSION(SPEAKUP_VERSION
);
65 module_param_named(synth
, synth_name
, charp
, S_IRUGO
);
66 module_param_named(quiet
, spk_quiet_boot
, bool, S_IRUGO
);
68 MODULE_PARM_DESC(synth
, "Synth to start if speakup is built in.");
69 MODULE_PARM_DESC(quiet
, "Do not announce when the synthesizer is found.");
71 special_func spk_special_handler
;
73 short spk_pitch_shift
, synth_flags
;
75 int spk_attrib_bleep
, spk_bleeps
, spk_bleep_time
= 10;
76 int spk_no_intr
, spk_spell_delay
;
77 int spk_key_echo
, spk_say_word_ctl
;
78 int spk_say_ctrl
, spk_bell_pos
;
80 int spk_punc_level
, spk_reading_punc
;
81 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0";
82 char spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
83 const struct st_bits_data spk_punc_info
[] = {
85 {"some", "/$%&@", SOME
},
86 {"most", "$%&#()=+*/@^<>|\\", MOST
},
87 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
88 {"delimiters", "", B_WDLM
},
89 {"repeats", "()", CH_RPT
},
90 {"extended numeric", "", B_EXNUM
},
91 {"symbols", "", B_SYM
},
95 static char mark_cut_flag
;
97 static u_char
*spk_shift_table
;
98 u_char
*spk_our_keys
[MAX_KEY
];
99 u_char spk_key_buf
[600];
100 const u_char spk_key_defaults
[] = {
101 #include "speakupmap.h"
104 /* Speakup Cursor Track Variables */
105 static int cursor_track
= 1, prev_cursor_track
= 1;
107 /* cursor track modes, must be ordered same as cursor_msgs */
115 #define read_all_mode CT_Max
117 static struct tty_struct
*tty
;
119 static void spkup_write(const char *in_buf
, int count
);
121 static char *phonetic
[] = {
122 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
123 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
125 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
126 "x ray", "yankee", "zulu"
129 /* array of 256 char pointers (one for each character description)
130 * initialized to default_chars and user selectable via
131 * /proc/speakup/characters
133 char *spk_characters
[256];
135 char *spk_default_chars
[256] = {
136 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
137 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
138 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
139 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
141 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
143 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
146 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
148 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
149 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
150 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
151 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
152 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
155 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
156 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
157 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
158 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
159 /*127*/ "del", "control", "control", "control", "control", "control",
160 "control", "control", "control", "control", "control",
161 /*138*/ "control", "control", "control", "control", "control",
162 "control", "control", "control", "control", "control",
163 "control", "control",
164 /*150*/ "control", "control", "control", "control", "control",
165 "control", "control", "control", "control", "control",
166 /*160*/ "nbsp", "inverted bang",
167 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
168 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
169 /*172*/ "not", "soft hyphen", "registered", "macron",
170 /*176*/ "degrees", "plus or minus", "super two", "super three",
171 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
172 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
173 /*188*/ "one quarter", "one half", "three quarters",
175 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
177 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
179 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
181 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
182 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
184 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
185 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
186 /*230*/ "ae", "c cidella", "e grave", "e acute",
187 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
189 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
191 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
193 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
196 /* array of 256 u_short (one for each character)
197 * initialized to default_chartab and user selectable via
198 * /sys/module/speakup/parameters/chartab
200 u_short spk_chartab
[256];
202 static u_short default_chartab
[256] = {
203 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
204 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
205 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
206 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
207 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
208 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
209 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
210 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
211 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
212 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
213 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
214 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
215 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
216 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
217 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
218 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
219 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
221 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
223 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
225 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
227 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
229 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
230 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
231 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
232 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
233 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
234 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
235 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
236 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
237 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
238 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
239 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
242 struct task_struct
*speakup_task
;
243 struct bleep spk_unprocessed_sound
;
244 static int spk_keydown
;
245 static u_char spk_lastkey
, spk_close_press
, keymap_flags
;
246 static u_char last_keycode
, this_speakup_key
;
247 static u_long last_spk_jiffy
;
249 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
251 DEFINE_MUTEX(spk_mutex
);
253 static int keyboard_notifier_call(struct notifier_block
*,
254 unsigned long code
, void *param
);
256 static struct notifier_block keyboard_notifier_block
= {
257 .notifier_call
= keyboard_notifier_call
,
260 static int vt_notifier_call(struct notifier_block
*,
261 unsigned long code
, void *param
);
263 static struct notifier_block vt_notifier_block
= {
264 .notifier_call
= vt_notifier_call
,
267 static unsigned char get_attributes(u16
*pos
)
269 return (u_char
) (scr_readw(pos
) >> 8);
272 static void speakup_date(struct vc_data
*vc
)
274 spk_x
= spk_cx
= vc
->vc_x
;
275 spk_y
= spk_cy
= vc
->vc_y
;
276 spk_pos
= spk_cp
= vc
->vc_pos
;
277 spk_old_attr
= spk_attr
;
278 spk_attr
= get_attributes((u_short
*) spk_pos
);
281 static void bleep(u_short val
)
283 static const short vals
[] = {
284 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
287 int time
= spk_bleep_time
;
289 freq
= vals
[val
% 12];
291 freq
*= (1 << (val
/ 12));
292 spk_unprocessed_sound
.freq
= freq
;
293 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
294 spk_unprocessed_sound
.active
= 1;
295 /* We can only have 1 active sound at a time. */
298 static void speakup_shut_up(struct vc_data
*vc
)
309 static void speech_kill(struct vc_data
*vc
)
311 char val
= synth
->is_alive(synth
);
316 /* re-enables synth, if disabled */
317 if (val
== 2 || spk_killed
) {
319 spk_shut_up
&= ~0x40;
320 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
322 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
327 static void speakup_off(struct vc_data
*vc
)
329 if (spk_shut_up
& 0x80) {
331 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
334 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
339 static void speakup_parked(struct vc_data
*vc
)
341 if (spk_parked
& 0x80) {
343 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
346 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
350 static void speakup_cut(struct vc_data
*vc
)
352 static const char err_buf
[] = "set selection failed";
355 if (!mark_cut_flag
) {
357 spk_xs
= (u_short
) spk_x
;
358 spk_ys
= (u_short
) spk_y
;
360 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
363 spk_xe
= (u_short
) spk_x
;
364 spk_ye
= (u_short
) spk_y
;
366 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
368 speakup_clear_selection();
369 ret
= speakup_set_selection(tty
);
373 break; /* no error */
375 pr_warn("%sEFAULT\n", err_buf
);
378 pr_warn("%sEINVAL\n", err_buf
);
381 pr_warn("%sENOMEM\n", err_buf
);
386 static void speakup_paste(struct vc_data
*vc
)
390 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
392 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
393 speakup_paste_selection(tty
);
397 static void say_attributes(struct vc_data
*vc
)
399 int fg
= spk_attr
& 0x0f;
400 int bg
= spk_attr
>> 4;
403 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
406 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
408 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
411 synth_printf(" %s ", spk_msg_get(MSG_ON
));
412 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
423 static void announce_edge(struct vc_data
*vc
, int msg_id
)
427 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
429 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
432 static void speak_char(u_char ch
)
434 char *cp
= spk_characters
[ch
];
435 struct var_t
*direct
= spk_get_var(DIRECT
);
437 if (direct
&& direct
->u
.n
.value
) {
438 if (IS_CHAR(ch
, B_CAP
)) {
440 synth_printf("%s", spk_str_caps_start
);
442 synth_printf("%c", ch
);
443 if (IS_CHAR(ch
, B_CAP
))
444 synth_printf("%s", spk_str_caps_stop
);
448 pr_info("speak_char: cp == NULL!\n");
451 synth_buffer_add(SPACE
);
452 if (IS_CHAR(ch
, B_CAP
)) {
454 synth_printf("%s", spk_str_caps_start
);
455 synth_printf("%s", cp
);
456 synth_printf("%s", spk_str_caps_stop
);
459 synth_printf("%s", spk_msg_get(MSG_CTRL
));
462 synth_printf("%s", cp
);
464 synth_buffer_add(SPACE
);
467 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
472 u16 w
= scr_readw(pos
);
475 if (w
& vc
->vc_hi_font_mask
)
478 ch
= inverse_translate(vc
, c
, 0);
479 *attribs
= (w
& 0xff00) >> 8;
484 static void say_char(struct vc_data
*vc
)
488 spk_old_attr
= spk_attr
;
489 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
490 if (spk_attr
!= spk_old_attr
) {
491 if (spk_attrib_bleep
& 1)
493 if (spk_attrib_bleep
& 2)
496 speak_char(ch
& 0xff);
499 static void say_phonetic_char(struct vc_data
*vc
)
503 spk_old_attr
= spk_attr
;
504 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
505 if (isascii(ch
) && isalpha(ch
)) {
507 synth_printf("%s\n", phonetic
[--ch
]);
509 if (IS_CHAR(ch
, B_NUM
))
510 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
515 static void say_prev_char(struct vc_data
*vc
)
519 announce_edge(vc
, edge_left
);
527 static void say_next_char(struct vc_data
*vc
)
530 if (spk_x
== vc
->vc_cols
- 1) {
531 announce_edge(vc
, edge_right
);
539 /* get_word - will first check to see if the character under the
540 * reading cursor is a space and if spk_say_word_ctl is true it will
541 * return the word space. If spk_say_word_ctl is not set it will check to
542 * see if there is a word starting on the next position to the right
543 * and return that word if it exists. If it does not exist it will
544 * move left to the beginning of any previous word on the line or the
545 * beginning off the line whichever comes first..
548 static u_long
get_word(struct vc_data
*vc
)
550 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
555 spk_old_attr
= spk_attr
;
556 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
558 /* decided to take out the sayword if on a space (mis-information */
559 if (spk_say_word_ctl
&& ch
== SPACE
) {
561 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
563 } else if ((tmpx
< vc
->vc_cols
- 2)
564 && (ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
565 && ((char)get_char(vc
, (u_short
*) &tmp_pos
+ 1, &temp
) >
571 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
- 1, &temp
);
572 if ((ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
573 && ((char)get_char(vc
, (u_short
*) tmp_pos
, &temp
) >
579 attr_ch
= get_char(vc
, (u_short
*) tmp_pos
, &spk_attr
);
580 buf
[cnt
++] = attr_ch
& 0xff;
581 while (tmpx
< vc
->vc_cols
- 1) {
584 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
585 if ((ch
== SPACE
) || ch
== 0
586 || (IS_WDLM(buf
[cnt
- 1]) && (ch
> SPACE
)))
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;
639 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
640 if (ch
== SPACE
|| ch
== 0)
642 else if (IS_WDLM(ch
))
646 if (state
< last_state
) {
653 if (spk_x
== 0 && edge_said
== edge_quiet
)
654 edge_said
= edge_left
;
655 if (edge_said
> 0 && edge_said
< edge_quiet
)
656 announce_edge(vc
, edge_said
);
660 static void say_next_word(struct vc_data
*vc
)
664 u_short edge_said
= 0, last_state
= 2, state
= 0;
667 if (spk_x
== vc
->vc_cols
- 1 && spk_y
== vc
->vc_rows
- 1) {
668 announce_edge(vc
, edge_bottom
);
672 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
673 if (ch
== SPACE
|| ch
== 0)
675 else if (IS_WDLM(ch
))
679 if (state
> last_state
)
681 if (spk_x
>= vc
->vc_cols
- 1) {
682 if (spk_y
== vc
->vc_rows
- 1) {
683 edge_said
= edge_bottom
;
689 edge_said
= edge_right
;
696 announce_edge(vc
, edge_said
);
700 static void spell_word(struct vc_data
*vc
)
702 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
703 char *cp
= buf
, *str_cap
= spk_str_caps_stop
;
704 char *cp1
, *last_cap
= spk_str_caps_stop
;
709 while ((ch
= (u_char
) *cp
)) {
711 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
712 if (IS_CHAR(ch
, B_CAP
)) {
713 str_cap
= spk_str_caps_start
;
714 if (*spk_str_caps_stop
)
716 else /* synth has no pitch */
717 last_cap
= spk_str_caps_stop
;
719 str_cap
= spk_str_caps_stop
;
720 if (str_cap
!= last_cap
) {
721 synth_printf("%s", str_cap
);
724 if (this_speakup_key
== SPELL_PHONETIC
725 && (isascii(ch
) && isalpha(ch
))) {
727 cp1
= phonetic
[--ch
];
729 cp1
= spk_characters
[ch
];
731 synth_printf("%s", spk_msg_get(MSG_CTRL
));
735 synth_printf("%s", cp1
);
738 if (str_cap
!= spk_str_caps_stop
)
739 synth_printf("%s", spk_str_caps_stop
);
742 static int get_line(struct vc_data
*vc
)
744 u_long tmp
= spk_pos
- (spk_x
* 2);
748 spk_old_attr
= spk_attr
;
749 spk_attr
= get_attributes((u_short
*) spk_pos
);
750 for (i
= 0; i
< vc
->vc_cols
; i
++) {
751 buf
[i
] = (u_char
) get_char(vc
, (u_short
*) tmp
, &tmp2
);
754 for (--i
; i
>= 0; i
--)
760 static void say_line(struct vc_data
*vc
)
762 int i
= get_line(vc
);
764 u_short saved_punc_mask
= spk_punc_mask
;
767 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
771 if (this_speakup_key
== SAY_LINE_INDENT
) {
775 synth_printf("%d, ", (cp
- buf
) + 1);
777 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
779 spk_punc_mask
= saved_punc_mask
;
782 static void say_prev_line(struct vc_data
*vc
)
786 announce_edge(vc
, edge_top
);
790 spk_pos
-= vc
->vc_size_row
;
794 static void say_next_line(struct vc_data
*vc
)
797 if (spk_y
== vc
->vc_rows
- 1) {
798 announce_edge(vc
, edge_bottom
);
802 spk_pos
+= vc
->vc_size_row
;
806 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
811 u_short saved_punc_mask
= spk_punc_mask
;
813 spk_old_attr
= spk_attr
;
814 spk_attr
= get_attributes((u_short
*) from
);
816 buf
[i
++] = (char)get_char(vc
, (u_short
*) from
, &tmp
);
818 if (i
>= vc
->vc_size_row
)
821 for (--i
; i
>= 0; i
--)
829 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
832 spk_punc_mask
= saved_punc_mask
;
836 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
839 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
840 u_long end
= start
+ (to
* 2);
843 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
844 if (cursor_track
!= read_all_mode
)
845 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
848 /* Sentence Reading Commands */
850 static int currsentence
;
851 static int numsentences
[2];
852 static char *sentbufend
[2];
853 static char *sentmarks
[2][10];
856 static char sentbuf
[2][256];
858 static int say_sentence_num(int num
, int prev
)
861 currsentence
= num
+ 1;
862 if (prev
&& --bn
== -1)
865 if (num
> numsentences
[bn
])
868 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
872 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
882 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
883 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
885 numsentences
[bn
] = 0;
886 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
888 spk_old_attr
= spk_attr
;
889 spk_attr
= get_attributes((u_short
*) start
);
891 while (start
< end
) {
892 sentbuf
[bn
][i
] = (char)get_char(vc
, (u_short
*) start
, &tmp
);
894 if (sentbuf
[bn
][i
] == SPACE
&& sentbuf
[bn
][i
- 1] == '.'
895 && numsentences
[bn
] < 9) {
896 /* Sentence Marker */
898 sentmarks
[bn
][numsentences
[bn
]] =
904 if (i
>= vc
->vc_size_row
)
908 for (--i
; i
>= 0; i
--)
909 if (sentbuf
[bn
][i
] != SPACE
)
915 sentbuf
[bn
][++i
] = SPACE
;
916 sentbuf
[bn
][++i
] = '\0';
918 sentbufend
[bn
] = &sentbuf
[bn
][i
];
919 return numsentences
[bn
];
922 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
924 u_long start
= vc
->vc_origin
, end
;
927 start
+= from
* vc
->vc_size_row
;
928 if (to
> vc
->vc_rows
)
930 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
931 for (from
= start
; from
< end
; from
= to
) {
932 to
= from
+ vc
->vc_size_row
;
933 say_from_to(vc
, from
, to
, 1);
937 static void say_screen(struct vc_data
*vc
)
939 say_screen_from_to(vc
, 0, vc
->vc_rows
);
942 static void speakup_win_say(struct vc_data
*vc
)
944 u_long start
, end
, from
, to
;
947 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
950 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
951 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
952 while (start
<= end
) {
953 from
= start
+ (win_left
* 2);
954 to
= start
+ (win_right
* 2);
955 say_from_to(vc
, from
, to
, 1);
956 start
+= vc
->vc_size_row
;
960 static void top_edge(struct vc_data
*vc
)
963 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
968 static void bottom_edge(struct vc_data
*vc
)
971 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
972 spk_y
= vc
->vc_rows
- 1;
976 static void left_edge(struct vc_data
*vc
)
979 spk_pos
-= spk_x
* 2;
984 static void right_edge(struct vc_data
*vc
)
987 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
988 spk_x
= vc
->vc_cols
- 1;
992 static void say_first_char(struct vc_data
*vc
)
994 int i
, len
= get_line(vc
);
999 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1002 for (i
= 0; i
< len
; i
++)
1003 if (buf
[i
] != SPACE
)
1006 spk_pos
-= (spk_x
- i
) * 2;
1008 synth_printf("%d, ", ++i
);
1012 static void say_last_char(struct vc_data
*vc
)
1014 int len
= get_line(vc
);
1019 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1023 spk_pos
-= (spk_x
- len
) * 2;
1025 synth_printf("%d, ", ++len
);
1029 static void say_position(struct vc_data
*vc
)
1031 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1036 /* Added by brianb */
1037 static void say_char_num(struct vc_data
*vc
)
1040 u_short ch
= get_char(vc
, (u_short
*) spk_pos
, &tmp
);
1043 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1046 /* these are stub functions to keep keyboard.c happy. */
1048 static void say_from_top(struct vc_data
*vc
)
1050 say_screen_from_to(vc
, 0, spk_y
);
1053 static void say_to_bottom(struct vc_data
*vc
)
1055 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1058 static void say_from_left(struct vc_data
*vc
)
1060 say_line_from_to(vc
, 0, spk_x
, 1);
1063 static void say_to_right(struct vc_data
*vc
)
1065 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1068 /* end of stub functions. */
1070 static void spkup_write(const char *in_buf
, int count
)
1072 static int rep_count
;
1073 static u_char ch
= '\0', old_ch
= '\0';
1074 static u_short char_type
, last_type
;
1075 int in_count
= count
;
1079 if (cursor_track
== read_all_mode
) {
1080 /* Insert Sentence Index */
1081 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1082 (currsentence
<= numsentences
[bn
]))
1083 synth_insert_next_index(currsentence
++);
1085 ch
= (u_char
) *in_buf
++;
1086 char_type
= spk_chartab
[ch
];
1087 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1088 if (++rep_count
> 2)
1091 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1093 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1099 if (ch
== spk_lastkey
) {
1101 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1103 } else if (char_type
& B_ALPHA
) {
1104 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1105 synth_buffer_add(SPACE
);
1106 synth_printf("%c", ch
);
1107 } else if (char_type
& B_NUM
) {
1109 synth_printf("%c", ch
);
1110 } else if (char_type
& spk_punc_mask
) {
1112 char_type
&= ~PUNC
; /* for dec nospell processing */
1113 } else if (char_type
& SYNTH_OK
) {
1114 /* these are usually puncts like . and , which synth
1115 * needs for expression.
1116 * suppress multiple to get rid of long pauses and
1117 * clear repeat count
1119 * repeats on you don't get nothing repeated count
1122 synth_printf("%c", ch
);
1126 /* send space and record position, if next is num overwrite space */
1128 synth_buffer_add(SPACE
);
1133 last_type
= char_type
;
1136 if (in_count
> 2 && rep_count
> 2) {
1137 if (last_type
& CH_RPT
) {
1139 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1147 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1149 static void read_all_doc(struct vc_data
*vc
);
1150 static void cursor_done(u_long data
);
1151 static DEFINE_TIMER(cursor_timer
, cursor_done
, 0, 0);
1153 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1155 unsigned long flags
;
1157 if (synth
== NULL
|| up_flag
|| spk_killed
)
1159 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1160 if (cursor_track
== read_all_mode
) {
1163 del_timer(&cursor_timer
);
1164 spk_shut_up
&= 0xfe;
1169 del_timer(&cursor_timer
);
1170 cursor_track
= prev_cursor_track
;
1171 spk_shut_up
&= 0xfe;
1176 spk_shut_up
&= 0xfe;
1179 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1180 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1181 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1184 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1186 unsigned long flags
;
1188 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1190 spk_lastkey
= spk_keydown
= 0;
1191 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1194 if (synth
== NULL
|| spk_killed
) {
1195 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1198 spk_shut_up
&= 0xfe;
1199 spk_lastkey
= value
;
1202 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1204 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1207 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1209 int i
= 0, states
, key_data_len
;
1210 const u_char
*cp
= key_info
;
1211 u_char
*cp1
= k_buffer
;
1212 u_char ch
, version
, num_keys
;
1215 if (version
!= KEY_MAP_VER
)
1218 states
= (int)cp
[1];
1219 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1220 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
))
1222 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1223 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1224 spk_shift_table
= k_buffer
;
1225 spk_our_keys
[0] = spk_shift_table
;
1226 cp1
+= SHIFT_TBL_SIZE
;
1227 memcpy(cp1
, cp
, key_data_len
+ 3);
1228 /* get num_keys, states and data */
1229 cp1
+= 2; /* now pointing at shift states */
1230 for (i
= 1; i
<= states
; i
++) {
1232 if (ch
>= SHIFT_TBL_SIZE
)
1234 spk_shift_table
[ch
] = i
;
1236 keymap_flags
= *cp1
++;
1237 while ((ch
= *cp1
)) {
1240 spk_our_keys
[ch
] = cp1
;
1246 static struct var_t spk_vars
[] = {
1247 /* bell must be first to set high limit */
1248 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1249 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1250 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1251 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1252 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1253 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1254 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1255 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1256 {SAY_CONTROL
, TOGGLE_0
},
1257 {SAY_WORD_CTL
, TOGGLE_0
},
1258 {NO_INTERRUPT
, TOGGLE_0
},
1259 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1263 static void toggle_cursoring(struct vc_data
*vc
)
1265 if (cursor_track
== read_all_mode
)
1266 cursor_track
= prev_cursor_track
;
1267 if (++cursor_track
>= CT_Max
)
1269 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1272 void spk_reset_default_chars(void)
1276 /* First, free any non-default */
1277 for (i
= 0; i
< 256; i
++) {
1278 if ((spk_characters
[i
] != NULL
)
1279 && (spk_characters
[i
] != spk_default_chars
[i
]))
1280 kfree(spk_characters
[i
]);
1283 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1286 void spk_reset_default_chartab(void)
1288 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1291 static const struct st_bits_data
*pb_edit
;
1293 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1295 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1297 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1300 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1301 spk_special_handler
= NULL
;
1304 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1306 spk_chartab
[ch
] ^= mask
;
1308 synth_printf(" %s\n",
1309 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1310 spk_msg_get(MSG_OFF
));
1314 /* Allocation concurrency is protected by the console semaphore */
1315 static int speakup_allocate(struct vc_data
*vc
)
1319 vc_num
= vc
->vc_num
;
1320 if (speakup_console
[vc_num
] == NULL
) {
1321 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1323 if (speakup_console
[vc_num
] == NULL
)
1326 } else if (!spk_parked
)
1332 static void speakup_deallocate(struct vc_data
*vc
)
1336 vc_num
= vc
->vc_num
;
1337 kfree(speakup_console
[vc_num
]);
1338 speakup_console
[vc_num
] = NULL
;
1341 static u_char is_cursor
;
1342 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1343 static int cursor_con
;
1345 static void reset_highlight_buffers(struct vc_data
*);
1347 static int read_all_key
;
1349 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1363 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1365 del_timer(&cursor_timer
);
1366 speakup_fake_down_arrow();
1367 start_read_all_timer(vc
, command
);
1370 static void read_all_doc(struct vc_data
*vc
)
1372 if ((vc
->vc_num
!= fg_console
) || synth
== NULL
|| spk_shut_up
)
1374 if (!synth_supports_indexing())
1376 if (cursor_track
!= read_all_mode
)
1377 prev_cursor_track
= cursor_track
;
1378 cursor_track
= read_all_mode
;
1379 spk_reset_index_count(0);
1380 if (get_sentence_buf(vc
, 0) == -1)
1381 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1383 say_sentence_num(0, 0);
1384 synth_insert_next_index(0);
1385 start_read_all_timer(vc
, RA_TIMER
);
1389 static void stop_read_all(struct vc_data
*vc
)
1391 del_timer(&cursor_timer
);
1392 cursor_track
= prev_cursor_track
;
1393 spk_shut_up
&= 0xfe;
1397 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1399 struct var_t
*cursor_timeout
;
1401 cursor_con
= vc
->vc_num
;
1402 read_all_key
= command
;
1403 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1404 mod_timer(&cursor_timer
,
1405 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1408 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1410 int indcount
, sentcount
, rv
, sn
;
1414 /* Get Current Sentence */
1415 spk_get_index_count(&indcount
, &sentcount
);
1416 /*printk("%d %d ", indcount, sentcount); */
1417 spk_reset_index_count(sentcount
+ 1);
1418 if (indcount
== 1) {
1419 if (!say_sentence_num(sentcount
+ 1, 0)) {
1420 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1423 synth_insert_next_index(0);
1426 if (!say_sentence_num(sentcount
+ 1, 1)) {
1428 spk_reset_index_count(sn
);
1430 synth_insert_next_index(0);
1431 if (!say_sentence_num(sn
, 0)) {
1432 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1435 synth_insert_next_index(0);
1437 start_read_all_timer(vc
, RA_TIMER
);
1447 if (get_sentence_buf(vc
, 0) == -1) {
1448 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1450 say_sentence_num(0, 0);
1451 synth_insert_next_index(0);
1452 start_read_all_timer(vc
, RA_TIMER
);
1455 case RA_FIND_NEXT_SENT
:
1456 rv
= get_sentence_buf(vc
, 0);
1460 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1462 say_sentence_num(1, 0);
1463 synth_insert_next_index(0);
1464 start_read_all_timer(vc
, RA_TIMER
);
1467 case RA_FIND_PREV_SENT
:
1470 spk_get_index_count(&indcount
, &sentcount
);
1472 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1474 start_read_all_timer(vc
, RA_TIMER
);
1479 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1481 unsigned long flags
;
1483 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1484 if (cursor_track
== read_all_mode
) {
1486 if (synth
== NULL
|| up_flag
|| spk_shut_up
) {
1487 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1490 del_timer(&cursor_timer
);
1491 spk_shut_up
&= 0xfe;
1493 start_read_all_timer(vc
, value
+ 1);
1494 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1497 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1501 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1503 unsigned long flags
;
1504 struct var_t
*cursor_timeout
;
1506 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1508 if (synth
== NULL
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1509 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1512 spk_shut_up
&= 0xfe;
1515 /* the key press flushes if !no_inter but we want to flush on cursor
1516 * moves regardless of no_inter state
1518 is_cursor
= value
+ 1;
1519 old_cursor_pos
= vc
->vc_pos
;
1520 old_cursor_x
= vc
->vc_x
;
1521 old_cursor_y
= vc
->vc_y
;
1522 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1523 cursor_con
= vc
->vc_num
;
1524 if (cursor_track
== CT_Highlight
)
1525 reset_highlight_buffers(vc
);
1526 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1527 mod_timer(&cursor_timer
,
1528 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1529 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1532 static void update_color_buffer(struct vc_data
*vc
, const char *ic
, int len
)
1535 int vc_num
= vc
->vc_num
;
1537 bi
= (vc
->vc_attr
& 0x70) >> 4;
1538 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1541 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1542 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1543 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1544 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1546 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1547 if ((ic
[i
] > 32) && (ic
[i
] < 127)) {
1548 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1550 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1551 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1553 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1560 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1563 static void reset_highlight_buffers(struct vc_data
*vc
)
1566 int vc_num
= vc
->vc_num
;
1568 for (i
= 0; i
< 8; i
++)
1569 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1572 static int count_highlight_color(struct vc_data
*vc
)
1576 int vc_num
= vc
->vc_num
;
1578 u16
*start
= (u16
*) vc
->vc_origin
;
1580 for (i
= 0; i
< 8; i
++)
1581 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1583 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1584 u16
*end
= start
+ vc
->vc_cols
* 2;
1587 for (ptr
= start
; ptr
< end
; ptr
++) {
1588 ch
= get_attributes(ptr
);
1589 bg
= (ch
& 0x70) >> 4;
1590 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1592 start
+= vc
->vc_size_row
;
1596 for (i
= 0; i
< 8; i
++)
1597 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1602 static int get_highlight_color(struct vc_data
*vc
)
1605 unsigned int cptr
[8];
1606 int vc_num
= vc
->vc_num
;
1608 for (i
= 0; i
< 8; i
++)
1611 for (i
= 0; i
< 7; i
++)
1612 for (j
= i
+ 1; j
< 8; j
++)
1613 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1614 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]])
1615 swap(cptr
[i
], cptr
[j
]);
1617 for (i
= 0; i
< 8; i
++)
1618 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1619 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1624 static int speak_highlight(struct vc_data
*vc
)
1627 int vc_num
= vc
->vc_num
;
1629 if (count_highlight_color(vc
) == 1)
1631 hc
= get_highlight_color(vc
);
1633 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1634 if ((d
== 1) || (d
== -1))
1635 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1639 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1640 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1641 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1642 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1643 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1649 static void cursor_done(u_long data
)
1651 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1652 unsigned long flags
;
1654 del_timer(&cursor_timer
);
1655 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1656 if (cursor_con
!= fg_console
) {
1662 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1663 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1664 spk_keydown
= is_cursor
= 0;
1668 if (cursor_track
== read_all_mode
) {
1669 handle_cursor_read_all(vc
, read_all_key
);
1672 if (cursor_track
== CT_Highlight
) {
1673 if (speak_highlight(vc
)) {
1674 spk_keydown
= is_cursor
= 0;
1678 if (cursor_track
== CT_Window
)
1679 speakup_win_say(vc
);
1680 else if (is_cursor
== 1 || is_cursor
== 4)
1681 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1684 spk_keydown
= is_cursor
= 0;
1686 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1689 /* called by: vt_notifier_call() */
1690 static void speakup_bs(struct vc_data
*vc
)
1692 unsigned long flags
;
1694 if (!speakup_console
[vc
->vc_num
])
1696 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1697 /* Speakup output, discard */
1701 if (spk_shut_up
|| synth
== NULL
) {
1702 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1705 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1710 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1713 /* called by: vt_notifier_call() */
1714 static void speakup_con_write(struct vc_data
*vc
, const char *str
, int len
)
1716 unsigned long flags
;
1718 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| synth
== NULL
)
1720 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1721 /* Speakup output, discard */
1723 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1725 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1726 if (cursor_track
== CT_Highlight
)
1727 update_color_buffer(vc
, str
, len
);
1728 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1732 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1733 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1734 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1739 spkup_write(str
, len
);
1740 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1743 static void speakup_con_update(struct vc_data
*vc
)
1745 unsigned long flags
;
1747 if (speakup_console
[vc
->vc_num
] == NULL
|| spk_parked
)
1749 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1750 /* Speakup output, discard */
1753 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1756 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1758 unsigned long flags
;
1762 if (synth
== NULL
|| up_flag
|| spk_killed
)
1764 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1765 spk_shut_up
&= 0xfe;
1770 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1771 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1774 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1775 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1778 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1779 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1780 if (speakup_console
[vc
->vc_num
])
1781 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1785 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1789 synth_printf("%s %s\n",
1790 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1791 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1794 static int inc_dec_var(u_char value
)
1796 struct st_var_header
*p_header
;
1797 struct var_t
*var_data
;
1801 int var_id
= (int)value
- VAR_START
;
1802 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1804 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1805 p_header
= spk_get_var_header(var_id
);
1806 if (p_header
== NULL
)
1808 if (p_header
->var_type
!= VAR_NUM
)
1810 var_data
= p_header
->data
;
1811 if (spk_set_num_var(1, p_header
, how
) != 0)
1813 if (!spk_close_press
) {
1814 for (pn
= p_header
->name
; *pn
; pn
++) {
1821 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1822 var_data
->u
.n
.value
);
1823 synth_printf("%s", num_buf
);
1827 static void speakup_win_set(struct vc_data
*vc
)
1831 if (win_start
> 1) {
1832 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1835 if (spk_x
< win_left
|| spk_y
< win_top
) {
1836 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1839 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1841 win_right
= vc
->vc_cols
- 1;
1843 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1853 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1855 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1856 (int)spk_y
+ 1, (int)spk_x
+ 1);
1858 synth_printf("%s\n", info
);
1862 static void speakup_win_clear(struct vc_data
*vc
)
1864 win_top
= win_bottom
= 0;
1865 win_left
= win_right
= 0;
1867 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1870 static void speakup_win_enable(struct vc_data
*vc
)
1872 if (win_start
< 2) {
1873 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1878 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1880 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1883 static void speakup_bits(struct vc_data
*vc
)
1885 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1887 if (spk_special_handler
!= NULL
|| val
< 1 || val
> 6) {
1888 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1891 pb_edit
= &spk_punc_info
[val
];
1892 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1893 spk_special_handler
= edit_bits
;
1896 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1898 static u_char goto_buf
[8];
1903 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1905 if (type
== KT_LATIN
&& ch
== '\n')
1912 ch
= goto_buf
[--num
];
1913 goto_buf
[num
] = '\0';
1914 spkup_write(&ch
, 1);
1917 if (ch
< '+' || ch
> 'y')
1919 goto_buf
[num
++] = ch
;
1920 goto_buf
[num
] = '\0';
1921 spkup_write(&ch
, 1);
1922 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1923 if ((ch
== '+' || ch
== '-') && num
== 1)
1925 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1927 if (num
< maxlen
- 1 || num
> maxlen
)
1929 if (ch
< 'x' || ch
> 'y') {
1932 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1933 goto_buf
[num
= 0] = '\0';
1934 spk_special_handler
= NULL
;
1938 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1941 if (*goto_buf
< '0')
1943 else if (goto_pos
> 0)
1946 if (goto_pos
>= vc
->vc_cols
)
1947 goto_pos
= vc
->vc_cols
- 1;
1950 if (*goto_buf
< '0')
1952 else if (goto_pos
> 0)
1955 if (goto_pos
>= vc
->vc_rows
)
1956 goto_pos
= vc
->vc_rows
- 1;
1959 goto_buf
[num
= 0] = '\0';
1961 spk_special_handler
= NULL
;
1964 spk_pos
-= spk_x
* 2;
1966 spk_pos
+= goto_pos
* 2;
1970 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
1976 static void speakup_goto(struct vc_data
*vc
)
1978 if (spk_special_handler
!= NULL
) {
1979 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1982 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
1983 spk_special_handler
= handle_goto
;
1986 static void speakup_help(struct vc_data
*vc
)
1988 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
1991 static void do_nothing(struct vc_data
*vc
)
1993 return; /* flush done in do_spkup */
1996 static u_char key_speakup
, spk_key_locked
;
1998 static void speakup_lock(struct vc_data
*vc
)
2000 if (!spk_key_locked
)
2001 spk_key_locked
= key_speakup
= 16;
2003 spk_key_locked
= key_speakup
= 0;
2006 typedef void (*spkup_hand
) (struct vc_data
*);
2007 static spkup_hand spkup_handler
[] = {
2008 /* must be ordered same as defines in speakup.h */
2009 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2010 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2011 say_char
, say_prev_char
, say_next_char
,
2012 say_word
, say_prev_word
, say_next_word
,
2013 say_line
, say_prev_line
, say_next_line
,
2014 top_edge
, bottom_edge
, left_edge
, right_edge
,
2015 spell_word
, spell_word
, say_screen
,
2016 say_position
, say_attributes
,
2017 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2018 say_from_top
, say_to_bottom
,
2019 say_from_left
, say_to_right
,
2020 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2021 speakup_bits
, speakup_bits
, speakup_bits
,
2022 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2023 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2026 static void do_spkup(struct vc_data
*vc
, u_char value
)
2028 if (spk_killed
&& value
!= SPEECH_KILL
)
2032 spk_shut_up
&= 0xfe;
2033 this_speakup_key
= value
;
2034 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2036 (*spkup_handler
[value
]) (vc
);
2038 if (inc_dec_var(value
) < 0)
2043 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2046 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2049 unsigned long flags
;
2052 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2053 u_char shift_info
, offset
;
2059 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2064 && (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2069 value
= spk_lastkey
= pad_chars
[value
];
2074 if (keycode
>= MAX_KEY
)
2076 key_info
= spk_our_keys
[keycode
];
2079 /* Check valid read all mode keys */
2080 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2094 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2095 offset
= spk_shift_table
[shift_info
];
2097 new_key
= key_info
[offset
];
2100 if (new_key
== SPK_KEY
) {
2101 if (!spk_key_locked
)
2102 key_speakup
= (up_flag
) ? 0 : 16;
2103 if (up_flag
|| spk_killed
)
2105 spk_shut_up
&= 0xfe;
2111 if (last_keycode
== keycode
&&
2112 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2113 spk_close_press
= 1;
2114 offset
= spk_shift_table
[shift_info
+ 32];
2116 if (offset
&& key_info
[offset
])
2117 new_key
= key_info
[offset
];
2119 last_keycode
= keycode
;
2120 last_spk_jiffy
= jiffies
;
2126 if (type
== KT_SPKUP
&& spk_special_handler
== NULL
) {
2127 do_spkup(vc
, new_key
);
2128 spk_close_press
= 0;
2132 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2134 spk_shut_up
&= 0xfe;
2135 kh
= (value
== KVAL(K_DOWN
))
2136 || (value
== KVAL(K_UP
))
2137 || (value
== KVAL(K_LEFT
))
2138 || (value
== KVAL(K_RIGHT
));
2139 if ((cursor_track
!= read_all_mode
) || !kh
)
2142 if (spk_special_handler
) {
2143 if (type
== KT_SPEC
&& value
== 1) {
2146 } else if (type
== KT_LETTER
)
2148 else if (value
== 0x7f)
2149 value
= 8; /* make del = backspace */
2150 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2151 spk_close_press
= 0;
2158 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2162 static int keyboard_notifier_call(struct notifier_block
*nb
,
2163 unsigned long code
, void *_param
)
2165 struct keyboard_notifier_param
*param
= _param
;
2166 struct vc_data
*vc
= param
->vc
;
2167 int up
= !param
->down
;
2168 int ret
= NOTIFY_OK
;
2169 static int keycode
; /* to hold the current keycode */
2171 if (vc
->vc_mode
== KD_GRAPHICS
)
2175 * First, determine whether we are handling a fake keypress on
2176 * the current processor. If we are, then return NOTIFY_OK,
2177 * to pass the keystroke up the chain. This prevents us from
2178 * trying to take the Speakup lock while it is held by the
2179 * processor on which the simulated keystroke was generated.
2180 * Also, the simulated keystrokes should be ignored by Speakup.
2183 if (speakup_fake_key_pressed())
2188 /* speakup requires keycode and keysym currently */
2189 keycode
= param
->value
;
2191 case KBD_UNBOUND_KEYCODE
:
2198 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2200 else if (KTYP(param
->value
) == KT_CUR
)
2201 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2203 case KBD_POST_KEYSYM
:{
2204 unsigned char type
= KTYP(param
->value
) - 0xf0;
2205 unsigned char val
= KVAL(param
->value
);
2209 do_handle_shift(vc
, val
, up
);
2213 do_handle_latin(vc
, val
, up
);
2216 do_handle_cursor(vc
, val
, up
);
2219 do_handle_spec(vc
, val
, up
);
2228 static int vt_notifier_call(struct notifier_block
*nb
,
2229 unsigned long code
, void *_param
)
2231 struct vt_notifier_param
*param
= _param
;
2232 struct vc_data
*vc
= param
->vc
;
2236 if (vc
->vc_mode
== KD_TEXT
)
2237 speakup_allocate(vc
);
2240 speakup_deallocate(vc
);
2243 if (param
->c
== '\b')
2245 else if (param
->c
< 0x100) {
2248 speakup_con_write(vc
, &d
, 1);
2252 speakup_con_update(vc
);
2258 /* called by: module_exit() */
2259 static void __exit
speakup_exit(void)
2263 unregister_keyboard_notifier(&keyboard_notifier_block
);
2264 unregister_vt_notifier(&vt_notifier_block
);
2265 speakup_unregister_devsynth();
2266 speakup_cancel_paste();
2267 del_timer(&cursor_timer
);
2268 kthread_stop(speakup_task
);
2269 speakup_task
= NULL
;
2270 mutex_lock(&spk_mutex
);
2272 mutex_unlock(&spk_mutex
);
2274 speakup_kobj_exit();
2276 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2277 kfree(speakup_console
[i
]);
2279 speakup_remove_virtual_keyboard();
2281 for (i
= 0; i
< MAXVARS
; i
++)
2282 speakup_unregister_var(i
);
2284 for (i
= 0; i
< 256; i
++) {
2285 if (spk_characters
[i
] != spk_default_chars
[i
])
2286 kfree(spk_characters
[i
]);
2289 spk_free_user_msgs();
2292 /* call by: module_init() */
2293 static int __init
speakup_init(void)
2297 struct st_spk_t
*first_console
;
2298 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2301 /* These first few initializations cannot fail. */
2302 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2303 spk_reset_default_chars();
2304 spk_reset_default_chartab();
2305 spk_strlwr(synth_name
);
2306 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2307 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2308 speakup_register_var(var
);
2309 for (var
= synth_time_vars
;
2310 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2311 speakup_register_var(var
);
2312 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2313 spk_set_mask_bits(NULL
, i
, 2);
2315 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2317 /* From here on out, initializations can fail. */
2318 err
= speakup_add_virtual_keyboard();
2320 goto error_virtkeyboard
;
2322 first_console
= kzalloc(sizeof(*first_console
), GFP_KERNEL
);
2323 if (!first_console
) {
2328 speakup_console
[vc
->vc_num
] = first_console
;
2331 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2333 err
= speakup_allocate(vc_cons
[i
].d
);
2335 goto error_kobjects
;
2339 spk_shut_up
|= 0x01;
2341 err
= speakup_kobj_init();
2343 goto error_kobjects
;
2345 synth_init(synth_name
);
2346 speakup_register_devsynth();
2348 * register_devsynth might fail, but this error is not fatal.
2349 * /dev/synth is an extra feature; the rest of Speakup
2350 * will work fine without it.
2353 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2355 goto error_kbdnotifier
;
2356 err
= register_vt_notifier(&vt_notifier_block
);
2358 goto error_vtnotifier
;
2360 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2362 if (IS_ERR(speakup_task
)) {
2363 err
= PTR_ERR(speakup_task
);
2367 set_user_nice(speakup_task
, 10);
2368 wake_up_process(speakup_task
);
2370 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2371 pr_info("synth name on entry is: %s\n", synth_name
);
2375 unregister_vt_notifier(&vt_notifier_block
);
2378 unregister_keyboard_notifier(&keyboard_notifier_block
);
2379 del_timer(&cursor_timer
);
2382 speakup_unregister_devsynth();
2383 mutex_lock(&spk_mutex
);
2385 mutex_unlock(&spk_mutex
);
2386 speakup_kobj_exit();
2389 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2390 kfree(speakup_console
[i
]);
2393 speakup_remove_virtual_keyboard();
2396 for (i
= 0; i
< MAXVARS
; i
++)
2397 speakup_unregister_var(i
);
2399 for (i
= 0; i
< 256; i
++) {
2400 if (spk_characters
[i
] != spk_default_chars
[i
])
2401 kfree(spk_characters
[i
]);
2404 spk_free_user_msgs();
2410 module_init(speakup_init
);
2411 module_exit(speakup_exit
);