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 */
132 char *spk_characters
[256];
134 char *spk_default_chars
[256] = {
135 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
136 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
137 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
138 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
140 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
142 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
145 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
147 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
148 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
149 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
150 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
151 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
154 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
155 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
156 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
157 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
158 /*127*/ "del", "control", "control", "control", "control", "control",
159 "control", "control", "control", "control", "control",
160 /*138*/ "control", "control", "control", "control", "control",
161 "control", "control", "control", "control", "control",
162 "control", "control",
163 /*150*/ "control", "control", "control", "control", "control",
164 "control", "control", "control", "control", "control",
165 /*160*/ "nbsp", "inverted bang",
166 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
167 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
168 /*172*/ "not", "soft hyphen", "registered", "macron",
169 /*176*/ "degrees", "plus or minus", "super two", "super three",
170 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
171 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
172 /*188*/ "one quarter", "one half", "three quarters",
174 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
176 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
178 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
180 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
181 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
183 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
184 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
185 /*230*/ "ae", "c cidella", "e grave", "e acute",
186 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
188 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
190 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
192 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
195 /* array of 256 u_short (one for each character)
196 * initialized to default_chartab and user selectable via
197 * /sys/module/speakup/parameters/chartab */
198 u_short spk_chartab
[256];
200 static u_short default_chartab
[256] = {
201 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
202 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
203 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
204 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
205 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
206 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
207 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
208 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
209 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
210 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
211 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
212 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
213 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
214 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
215 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
216 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
217 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
219 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
221 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
223 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
225 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
227 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
228 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
229 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
230 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
231 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
232 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
233 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
234 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
235 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
236 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
237 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
240 struct task_struct
*speakup_task
;
241 struct bleep spk_unprocessed_sound
;
242 static int spk_keydown
;
243 static u_char spk_lastkey
, spk_close_press
, keymap_flags
;
244 static u_char last_keycode
, this_speakup_key
;
245 static u_long last_spk_jiffy
;
247 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
249 DEFINE_MUTEX(spk_mutex
);
251 static int keyboard_notifier_call(struct notifier_block
*,
252 unsigned long code
, void *param
);
254 static struct notifier_block keyboard_notifier_block
= {
255 .notifier_call
= keyboard_notifier_call
,
258 static int vt_notifier_call(struct notifier_block
*,
259 unsigned long code
, void *param
);
261 static struct notifier_block vt_notifier_block
= {
262 .notifier_call
= vt_notifier_call
,
265 static unsigned char get_attributes(u16
*pos
)
267 return (u_char
) (scr_readw(pos
) >> 8);
270 static void speakup_date(struct vc_data
*vc
)
272 spk_x
= spk_cx
= vc
->vc_x
;
273 spk_y
= spk_cy
= vc
->vc_y
;
274 spk_pos
= spk_cp
= vc
->vc_pos
;
275 spk_old_attr
= spk_attr
;
276 spk_attr
= get_attributes((u_short
*) spk_pos
);
279 static void bleep(u_short val
)
281 static const short vals
[] = {
282 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
285 int time
= spk_bleep_time
;
287 freq
= vals
[val
% 12];
289 freq
*= (1 << (val
/ 12));
290 spk_unprocessed_sound
.freq
= freq
;
291 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
292 spk_unprocessed_sound
.active
= 1;
293 /* We can only have 1 active sound at a time. */
296 static void speakup_shut_up(struct vc_data
*vc
)
307 static void speech_kill(struct vc_data
*vc
)
309 char val
= synth
->is_alive(synth
);
314 /* re-enables synth, if disabled */
315 if (val
== 2 || spk_killed
) {
317 spk_shut_up
&= ~0x40;
318 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
320 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
325 static void speakup_off(struct vc_data
*vc
)
327 if (spk_shut_up
& 0x80) {
329 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
332 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
337 static void speakup_parked(struct vc_data
*vc
)
339 if (spk_parked
& 0x80) {
341 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
344 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
348 static void speakup_cut(struct vc_data
*vc
)
350 static const char err_buf
[] = "set selection failed";
353 if (!mark_cut_flag
) {
355 spk_xs
= (u_short
) spk_x
;
356 spk_ys
= (u_short
) spk_y
;
358 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
361 spk_xe
= (u_short
) spk_x
;
362 spk_ye
= (u_short
) spk_y
;
364 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
366 speakup_clear_selection();
367 ret
= speakup_set_selection(tty
);
371 break; /* no error */
373 pr_warn("%sEFAULT\n", err_buf
);
376 pr_warn("%sEINVAL\n", err_buf
);
379 pr_warn("%sENOMEM\n", err_buf
);
384 static void speakup_paste(struct vc_data
*vc
)
388 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
390 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
391 speakup_paste_selection(tty
);
395 static void say_attributes(struct vc_data
*vc
)
397 int fg
= spk_attr
& 0x0f;
398 int bg
= spk_attr
>> 4;
401 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
404 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
406 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
409 synth_printf(" %s ", spk_msg_get(MSG_ON
));
410 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
421 static void announce_edge(struct vc_data
*vc
, int msg_id
)
425 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
427 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
430 static void speak_char(u_char ch
)
432 char *cp
= spk_characters
[ch
];
433 struct var_t
*direct
= spk_get_var(DIRECT
);
435 if (direct
&& direct
->u
.n
.value
) {
436 if (IS_CHAR(ch
, B_CAP
)) {
438 synth_printf("%s", spk_str_caps_start
);
440 synth_printf("%c", ch
);
441 if (IS_CHAR(ch
, B_CAP
))
442 synth_printf("%s", spk_str_caps_stop
);
446 pr_info("speak_char: cp == NULL!\n");
449 synth_buffer_add(SPACE
);
450 if (IS_CHAR(ch
, B_CAP
)) {
452 synth_printf("%s", spk_str_caps_start
);
453 synth_printf("%s", cp
);
454 synth_printf("%s", spk_str_caps_stop
);
457 synth_printf("%s", spk_msg_get(MSG_CTRL
));
460 synth_printf("%s", cp
);
462 synth_buffer_add(SPACE
);
465 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
470 u16 w
= scr_readw(pos
);
473 if (w
& vc
->vc_hi_font_mask
)
476 ch
= inverse_translate(vc
, c
, 0);
477 *attribs
= (w
& 0xff00) >> 8;
482 static void say_char(struct vc_data
*vc
)
486 spk_old_attr
= spk_attr
;
487 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
488 if (spk_attr
!= spk_old_attr
) {
489 if (spk_attrib_bleep
& 1)
491 if (spk_attrib_bleep
& 2)
494 speak_char(ch
& 0xff);
497 static void say_phonetic_char(struct vc_data
*vc
)
501 spk_old_attr
= spk_attr
;
502 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
503 if (isascii(ch
) && isalpha(ch
)) {
505 synth_printf("%s\n", phonetic
[--ch
]);
507 if (IS_CHAR(ch
, B_NUM
))
508 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
513 static void say_prev_char(struct vc_data
*vc
)
517 announce_edge(vc
, edge_left
);
525 static void say_next_char(struct vc_data
*vc
)
528 if (spk_x
== vc
->vc_cols
- 1) {
529 announce_edge(vc
, edge_right
);
537 /* get_word - will first check to see if the character under the
538 * reading cursor is a space and if spk_say_word_ctl is true it will
539 * return the word space. If spk_say_word_ctl is not set it will check to
540 * see if there is a word starting on the next position to the right
541 * and return that word if it exists. If it does not exist it will
542 * move left to the beginning of any previous word on the line or the
543 * beginning off the line whichever comes first.. */
545 static u_long
get_word(struct vc_data
*vc
)
547 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
552 spk_old_attr
= spk_attr
;
553 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
555 /* decided to take out the sayword if on a space (mis-information */
556 if (spk_say_word_ctl
&& ch
== SPACE
) {
558 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
560 } else if ((tmpx
< vc
->vc_cols
- 2)
561 && (ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
562 && ((char)get_char(vc
, (u_short
*) &tmp_pos
+ 1, &temp
) >
568 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
- 1, &temp
);
569 if ((ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
570 && ((char)get_char(vc
, (u_short
*) tmp_pos
, &temp
) >
576 attr_ch
= get_char(vc
, (u_short
*) tmp_pos
, &spk_attr
);
577 buf
[cnt
++] = attr_ch
& 0xff;
578 while (tmpx
< vc
->vc_cols
- 1) {
581 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
582 if ((ch
== SPACE
) || ch
== 0
583 || (IS_WDLM(buf
[cnt
- 1]) && (ch
> SPACE
)))
591 static void say_word(struct vc_data
*vc
)
593 u_long cnt
= get_word(vc
);
594 u_short saved_punc_mask
= spk_punc_mask
;
598 spk_punc_mask
= PUNC
;
600 spkup_write(buf
, cnt
);
601 spk_punc_mask
= saved_punc_mask
;
604 static void say_prev_word(struct vc_data
*vc
)
608 u_short edge_said
= 0, last_state
= 0, state
= 0;
614 announce_edge(vc
, edge_top
);
619 edge_said
= edge_quiet
;
624 edge_said
= edge_top
;
627 if (edge_said
!= edge_quiet
)
628 edge_said
= edge_left
;
632 spk_x
= vc
->vc_cols
- 1;
636 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
637 if (ch
== SPACE
|| ch
== 0)
639 else if (IS_WDLM(ch
))
643 if (state
< last_state
) {
650 if (spk_x
== 0 && edge_said
== edge_quiet
)
651 edge_said
= edge_left
;
652 if (edge_said
> 0 && edge_said
< edge_quiet
)
653 announce_edge(vc
, edge_said
);
657 static void say_next_word(struct vc_data
*vc
)
661 u_short edge_said
= 0, last_state
= 2, state
= 0;
664 if (spk_x
== vc
->vc_cols
- 1 && spk_y
== vc
->vc_rows
- 1) {
665 announce_edge(vc
, edge_bottom
);
669 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
670 if (ch
== SPACE
|| ch
== 0)
672 else if (IS_WDLM(ch
))
676 if (state
> last_state
)
678 if (spk_x
>= vc
->vc_cols
- 1) {
679 if (spk_y
== vc
->vc_rows
- 1) {
680 edge_said
= edge_bottom
;
686 edge_said
= edge_right
;
693 announce_edge(vc
, edge_said
);
697 static void spell_word(struct vc_data
*vc
)
699 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
700 char *cp
= buf
, *str_cap
= spk_str_caps_stop
;
701 char *cp1
, *last_cap
= spk_str_caps_stop
;
706 while ((ch
= (u_char
) *cp
)) {
708 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
709 if (IS_CHAR(ch
, B_CAP
)) {
710 str_cap
= spk_str_caps_start
;
711 if (*spk_str_caps_stop
)
713 else /* synth has no pitch */
714 last_cap
= spk_str_caps_stop
;
716 str_cap
= spk_str_caps_stop
;
717 if (str_cap
!= last_cap
) {
718 synth_printf("%s", str_cap
);
721 if (this_speakup_key
== SPELL_PHONETIC
722 && (isascii(ch
) && isalpha(ch
))) {
724 cp1
= phonetic
[--ch
];
726 cp1
= spk_characters
[ch
];
728 synth_printf("%s", spk_msg_get(MSG_CTRL
));
732 synth_printf("%s", cp1
);
735 if (str_cap
!= spk_str_caps_stop
)
736 synth_printf("%s", spk_str_caps_stop
);
739 static int get_line(struct vc_data
*vc
)
741 u_long tmp
= spk_pos
- (spk_x
* 2);
745 spk_old_attr
= spk_attr
;
746 spk_attr
= get_attributes((u_short
*) spk_pos
);
747 for (i
= 0; i
< vc
->vc_cols
; i
++) {
748 buf
[i
] = (u_char
) get_char(vc
, (u_short
*) tmp
, &tmp2
);
751 for (--i
; i
>= 0; i
--)
757 static void say_line(struct vc_data
*vc
)
759 int i
= get_line(vc
);
761 u_short saved_punc_mask
= spk_punc_mask
;
764 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
768 if (this_speakup_key
== SAY_LINE_INDENT
) {
772 synth_printf("%d, ", (cp
- buf
) + 1);
774 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
776 spk_punc_mask
= saved_punc_mask
;
779 static void say_prev_line(struct vc_data
*vc
)
783 announce_edge(vc
, edge_top
);
787 spk_pos
-= vc
->vc_size_row
;
791 static void say_next_line(struct vc_data
*vc
)
794 if (spk_y
== vc
->vc_rows
- 1) {
795 announce_edge(vc
, edge_bottom
);
799 spk_pos
+= vc
->vc_size_row
;
803 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
808 u_short saved_punc_mask
= spk_punc_mask
;
810 spk_old_attr
= spk_attr
;
811 spk_attr
= get_attributes((u_short
*) from
);
813 buf
[i
++] = (char)get_char(vc
, (u_short
*) from
, &tmp
);
815 if (i
>= vc
->vc_size_row
)
818 for (--i
; i
>= 0; i
--)
826 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
829 spk_punc_mask
= saved_punc_mask
;
833 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
836 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
837 u_long end
= start
+ (to
* 2);
840 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
841 if (cursor_track
!= read_all_mode
)
842 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
845 /* Sentence Reading Commands */
847 static int currsentence
;
848 static int numsentences
[2];
849 static char *sentbufend
[2];
850 static char *sentmarks
[2][10];
853 static char sentbuf
[2][256];
855 static int say_sentence_num(int num
, int prev
)
858 currsentence
= num
+ 1;
859 if (prev
&& --bn
== -1)
862 if (num
> numsentences
[bn
])
865 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
869 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
879 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
880 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
882 numsentences
[bn
] = 0;
883 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
885 spk_old_attr
= spk_attr
;
886 spk_attr
= get_attributes((u_short
*) start
);
888 while (start
< end
) {
889 sentbuf
[bn
][i
] = (char)get_char(vc
, (u_short
*) start
, &tmp
);
891 if (sentbuf
[bn
][i
] == SPACE
&& sentbuf
[bn
][i
- 1] == '.'
892 && numsentences
[bn
] < 9) {
893 /* Sentence Marker */
895 sentmarks
[bn
][numsentences
[bn
]] =
901 if (i
>= vc
->vc_size_row
)
905 for (--i
; i
>= 0; i
--)
906 if (sentbuf
[bn
][i
] != SPACE
)
912 sentbuf
[bn
][++i
] = SPACE
;
913 sentbuf
[bn
][++i
] = '\0';
915 sentbufend
[bn
] = &sentbuf
[bn
][i
];
916 return numsentences
[bn
];
919 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
921 u_long start
= vc
->vc_origin
, end
;
924 start
+= from
* vc
->vc_size_row
;
925 if (to
> vc
->vc_rows
)
927 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
928 for (from
= start
; from
< end
; from
= to
) {
929 to
= from
+ vc
->vc_size_row
;
930 say_from_to(vc
, from
, to
, 1);
934 static void say_screen(struct vc_data
*vc
)
936 say_screen_from_to(vc
, 0, vc
->vc_rows
);
939 static void speakup_win_say(struct vc_data
*vc
)
941 u_long start
, end
, from
, to
;
944 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
947 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
948 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
949 while (start
<= end
) {
950 from
= start
+ (win_left
* 2);
951 to
= start
+ (win_right
* 2);
952 say_from_to(vc
, from
, to
, 1);
953 start
+= vc
->vc_size_row
;
957 static void top_edge(struct vc_data
*vc
)
960 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
965 static void bottom_edge(struct vc_data
*vc
)
968 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
969 spk_y
= vc
->vc_rows
- 1;
973 static void left_edge(struct vc_data
*vc
)
976 spk_pos
-= spk_x
* 2;
981 static void right_edge(struct vc_data
*vc
)
984 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
985 spk_x
= vc
->vc_cols
- 1;
989 static void say_first_char(struct vc_data
*vc
)
991 int i
, len
= get_line(vc
);
996 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
999 for (i
= 0; i
< len
; i
++)
1000 if (buf
[i
] != SPACE
)
1003 spk_pos
-= (spk_x
- i
) * 2;
1005 synth_printf("%d, ", ++i
);
1009 static void say_last_char(struct vc_data
*vc
)
1011 int len
= get_line(vc
);
1016 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1020 spk_pos
-= (spk_x
- len
) * 2;
1022 synth_printf("%d, ", ++len
);
1026 static void say_position(struct vc_data
*vc
)
1028 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1033 /* Added by brianb */
1034 static void say_char_num(struct vc_data
*vc
)
1037 u_short ch
= get_char(vc
, (u_short
*) spk_pos
, &tmp
);
1040 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1043 /* these are stub functions to keep keyboard.c happy. */
1045 static void say_from_top(struct vc_data
*vc
)
1047 say_screen_from_to(vc
, 0, spk_y
);
1050 static void say_to_bottom(struct vc_data
*vc
)
1052 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1055 static void say_from_left(struct vc_data
*vc
)
1057 say_line_from_to(vc
, 0, spk_x
, 1);
1060 static void say_to_right(struct vc_data
*vc
)
1062 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1065 /* end of stub functions. */
1067 static void spkup_write(const char *in_buf
, int count
)
1069 static int rep_count
;
1070 static u_char ch
= '\0', old_ch
= '\0';
1071 static u_short char_type
, last_type
;
1072 int in_count
= count
;
1076 if (cursor_track
== read_all_mode
) {
1077 /* Insert Sentence Index */
1078 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1079 (currsentence
<= numsentences
[bn
]))
1080 synth_insert_next_index(currsentence
++);
1082 ch
= (u_char
) *in_buf
++;
1083 char_type
= spk_chartab
[ch
];
1084 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1085 if (++rep_count
> 2)
1088 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1090 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1096 if (ch
== spk_lastkey
) {
1098 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1100 } else if (char_type
& B_ALPHA
) {
1101 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1102 synth_buffer_add(SPACE
);
1103 synth_printf("%c", ch
);
1104 } else if (char_type
& B_NUM
) {
1106 synth_printf("%c", ch
);
1107 } else if (char_type
& spk_punc_mask
) {
1109 char_type
&= ~PUNC
; /* for dec nospell processing */
1110 } else if (char_type
& SYNTH_OK
) {
1111 /* these are usually puncts like . and , which synth
1112 * needs for expression.
1113 * suppress multiple to get rid of long pauses and
1114 * clear repeat count
1116 * repeats on you don't get nothing repeated count */
1118 synth_printf("%c", ch
);
1122 /* send space and record position, if next is num overwrite space */
1124 synth_buffer_add(SPACE
);
1129 last_type
= char_type
;
1132 if (in_count
> 2 && rep_count
> 2) {
1133 if (last_type
& CH_RPT
) {
1135 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1143 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1145 static void read_all_doc(struct vc_data
*vc
);
1146 static void cursor_done(u_long data
);
1147 static DEFINE_TIMER(cursor_timer
, cursor_done
, 0, 0);
1149 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1151 unsigned long flags
;
1153 if (synth
== NULL
|| up_flag
|| spk_killed
)
1155 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1156 if (cursor_track
== read_all_mode
) {
1159 del_timer(&cursor_timer
);
1160 spk_shut_up
&= 0xfe;
1165 del_timer(&cursor_timer
);
1166 cursor_track
= prev_cursor_track
;
1167 spk_shut_up
&= 0xfe;
1172 spk_shut_up
&= 0xfe;
1175 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1176 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1177 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1180 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1182 unsigned long flags
;
1184 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1186 spk_lastkey
= spk_keydown
= 0;
1187 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1190 if (synth
== NULL
|| spk_killed
) {
1191 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1194 spk_shut_up
&= 0xfe;
1195 spk_lastkey
= value
;
1198 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1200 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1203 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1205 int i
= 0, states
, key_data_len
;
1206 const u_char
*cp
= key_info
;
1207 u_char
*cp1
= k_buffer
;
1208 u_char ch
, version
, num_keys
;
1211 if (version
!= KEY_MAP_VER
)
1214 states
= (int)cp
[1];
1215 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1216 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
))
1218 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1219 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1220 spk_shift_table
= k_buffer
;
1221 spk_our_keys
[0] = spk_shift_table
;
1222 cp1
+= SHIFT_TBL_SIZE
;
1223 memcpy(cp1
, cp
, key_data_len
+ 3);
1224 /* get num_keys, states and data */
1225 cp1
+= 2; /* now pointing at shift states */
1226 for (i
= 1; i
<= states
; i
++) {
1228 if (ch
>= SHIFT_TBL_SIZE
)
1230 spk_shift_table
[ch
] = i
;
1232 keymap_flags
= *cp1
++;
1233 while ((ch
= *cp1
)) {
1236 spk_our_keys
[ch
] = cp1
;
1242 static struct var_t spk_vars
[] = {
1243 /* bell must be first to set high limit */
1244 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1245 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1246 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1247 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1248 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1249 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1250 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1251 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1252 {SAY_CONTROL
, TOGGLE_0
},
1253 {SAY_WORD_CTL
, TOGGLE_0
},
1254 {NO_INTERRUPT
, TOGGLE_0
},
1255 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1259 static void toggle_cursoring(struct vc_data
*vc
)
1261 if (cursor_track
== read_all_mode
)
1262 cursor_track
= prev_cursor_track
;
1263 if (++cursor_track
>= CT_Max
)
1265 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1268 void spk_reset_default_chars(void)
1272 /* First, free any non-default */
1273 for (i
= 0; i
< 256; i
++) {
1274 if ((spk_characters
[i
] != NULL
)
1275 && (spk_characters
[i
] != spk_default_chars
[i
]))
1276 kfree(spk_characters
[i
]);
1279 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1282 void spk_reset_default_chartab(void)
1284 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1287 static const struct st_bits_data
*pb_edit
;
1289 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1291 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1293 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1296 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1297 spk_special_handler
= NULL
;
1300 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1302 spk_chartab
[ch
] ^= mask
;
1304 synth_printf(" %s\n",
1305 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1306 spk_msg_get(MSG_OFF
));
1310 /* Allocation concurrency is protected by the console semaphore */
1311 static int speakup_allocate(struct vc_data
*vc
)
1315 vc_num
= vc
->vc_num
;
1316 if (speakup_console
[vc_num
] == NULL
) {
1317 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1319 if (speakup_console
[vc_num
] == NULL
)
1322 } else if (!spk_parked
)
1328 static void speakup_deallocate(struct vc_data
*vc
)
1332 vc_num
= vc
->vc_num
;
1333 kfree(speakup_console
[vc_num
]);
1334 speakup_console
[vc_num
] = NULL
;
1337 static u_char is_cursor
;
1338 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1339 static int cursor_con
;
1341 static void reset_highlight_buffers(struct vc_data
*);
1343 static int read_all_key
;
1345 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1359 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1361 del_timer(&cursor_timer
);
1362 speakup_fake_down_arrow();
1363 start_read_all_timer(vc
, command
);
1366 static void read_all_doc(struct vc_data
*vc
)
1368 if ((vc
->vc_num
!= fg_console
) || synth
== NULL
|| spk_shut_up
)
1370 if (!synth_supports_indexing())
1372 if (cursor_track
!= read_all_mode
)
1373 prev_cursor_track
= cursor_track
;
1374 cursor_track
= read_all_mode
;
1375 spk_reset_index_count(0);
1376 if (get_sentence_buf(vc
, 0) == -1)
1377 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1379 say_sentence_num(0, 0);
1380 synth_insert_next_index(0);
1381 start_read_all_timer(vc
, RA_TIMER
);
1385 static void stop_read_all(struct vc_data
*vc
)
1387 del_timer(&cursor_timer
);
1388 cursor_track
= prev_cursor_track
;
1389 spk_shut_up
&= 0xfe;
1393 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1395 struct var_t
*cursor_timeout
;
1397 cursor_con
= vc
->vc_num
;
1398 read_all_key
= command
;
1399 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1400 mod_timer(&cursor_timer
,
1401 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1404 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1406 int indcount
, sentcount
, rv
, sn
;
1410 /* Get Current Sentence */
1411 spk_get_index_count(&indcount
, &sentcount
);
1412 /*printk("%d %d ", indcount, sentcount); */
1413 spk_reset_index_count(sentcount
+ 1);
1414 if (indcount
== 1) {
1415 if (!say_sentence_num(sentcount
+ 1, 0)) {
1416 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1419 synth_insert_next_index(0);
1422 if (!say_sentence_num(sentcount
+ 1, 1)) {
1424 spk_reset_index_count(sn
);
1426 synth_insert_next_index(0);
1427 if (!say_sentence_num(sn
, 0)) {
1428 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1431 synth_insert_next_index(0);
1433 start_read_all_timer(vc
, RA_TIMER
);
1443 if (get_sentence_buf(vc
, 0) == -1) {
1444 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1446 say_sentence_num(0, 0);
1447 synth_insert_next_index(0);
1448 start_read_all_timer(vc
, RA_TIMER
);
1451 case RA_FIND_NEXT_SENT
:
1452 rv
= get_sentence_buf(vc
, 0);
1456 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1458 say_sentence_num(1, 0);
1459 synth_insert_next_index(0);
1460 start_read_all_timer(vc
, RA_TIMER
);
1463 case RA_FIND_PREV_SENT
:
1466 spk_get_index_count(&indcount
, &sentcount
);
1468 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1470 start_read_all_timer(vc
, RA_TIMER
);
1475 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1477 unsigned long flags
;
1479 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1480 if (cursor_track
== read_all_mode
) {
1482 if (synth
== NULL
|| up_flag
|| spk_shut_up
) {
1483 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1486 del_timer(&cursor_timer
);
1487 spk_shut_up
&= 0xfe;
1489 start_read_all_timer(vc
, value
+ 1);
1490 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1493 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1497 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1499 unsigned long flags
;
1500 struct var_t
*cursor_timeout
;
1502 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1504 if (synth
== NULL
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1505 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1508 spk_shut_up
&= 0xfe;
1511 /* the key press flushes if !no_inter but we want to flush on cursor
1512 * moves regardless of no_inter state */
1513 is_cursor
= value
+ 1;
1514 old_cursor_pos
= vc
->vc_pos
;
1515 old_cursor_x
= vc
->vc_x
;
1516 old_cursor_y
= vc
->vc_y
;
1517 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1518 cursor_con
= vc
->vc_num
;
1519 if (cursor_track
== CT_Highlight
)
1520 reset_highlight_buffers(vc
);
1521 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1522 mod_timer(&cursor_timer
,
1523 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1524 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1527 static void update_color_buffer(struct vc_data
*vc
, const char *ic
, int len
)
1530 int vc_num
= vc
->vc_num
;
1532 bi
= (vc
->vc_attr
& 0x70) >> 4;
1533 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1536 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1537 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1538 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1539 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1541 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1542 if ((ic
[i
] > 32) && (ic
[i
] < 127)) {
1543 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1545 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1546 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1548 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1555 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1558 static void reset_highlight_buffers(struct vc_data
*vc
)
1561 int vc_num
= vc
->vc_num
;
1563 for (i
= 0; i
< 8; i
++)
1564 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1567 static int count_highlight_color(struct vc_data
*vc
)
1571 int vc_num
= vc
->vc_num
;
1573 u16
*start
= (u16
*) vc
->vc_origin
;
1575 for (i
= 0; i
< 8; i
++)
1576 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1578 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1579 u16
*end
= start
+ vc
->vc_cols
* 2;
1582 for (ptr
= start
; ptr
< end
; ptr
++) {
1583 ch
= get_attributes(ptr
);
1584 bg
= (ch
& 0x70) >> 4;
1585 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1587 start
+= vc
->vc_size_row
;
1591 for (i
= 0; i
< 8; i
++)
1592 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1597 static int get_highlight_color(struct vc_data
*vc
)
1600 unsigned int cptr
[8], tmp
;
1601 int vc_num
= vc
->vc_num
;
1603 for (i
= 0; i
< 8; i
++)
1606 for (i
= 0; i
< 7; i
++)
1607 for (j
= i
+ 1; j
< 8; j
++)
1608 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1609 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]]) {
1615 for (i
= 0; i
< 8; i
++)
1616 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1617 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1622 static int speak_highlight(struct vc_data
*vc
)
1625 int vc_num
= vc
->vc_num
;
1627 if (count_highlight_color(vc
) == 1)
1629 hc
= get_highlight_color(vc
);
1631 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1632 if ((d
== 1) || (d
== -1))
1633 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1637 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1638 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1639 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1640 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1641 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1647 static void cursor_done(u_long data
)
1649 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1650 unsigned long flags
;
1652 del_timer(&cursor_timer
);
1653 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1654 if (cursor_con
!= fg_console
) {
1660 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1661 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1662 spk_keydown
= is_cursor
= 0;
1666 if (cursor_track
== read_all_mode
) {
1667 handle_cursor_read_all(vc
, read_all_key
);
1670 if (cursor_track
== CT_Highlight
) {
1671 if (speak_highlight(vc
)) {
1672 spk_keydown
= is_cursor
= 0;
1676 if (cursor_track
== CT_Window
)
1677 speakup_win_say(vc
);
1678 else if (is_cursor
== 1 || is_cursor
== 4)
1679 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1682 spk_keydown
= is_cursor
= 0;
1684 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1687 /* called by: vt_notifier_call() */
1688 static void speakup_bs(struct vc_data
*vc
)
1690 unsigned long flags
;
1692 if (!speakup_console
[vc
->vc_num
])
1694 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1695 /* Speakup output, discard */
1699 if (spk_shut_up
|| synth
== NULL
) {
1700 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1703 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1708 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1711 /* called by: vt_notifier_call() */
1712 static void speakup_con_write(struct vc_data
*vc
, const char *str
, int len
)
1714 unsigned long flags
;
1716 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| synth
== NULL
)
1718 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1719 /* Speakup output, discard */
1721 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1723 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1724 if (cursor_track
== CT_Highlight
)
1725 update_color_buffer(vc
, str
, len
);
1726 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1730 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1731 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1732 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1737 spkup_write(str
, len
);
1738 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1741 static void speakup_con_update(struct vc_data
*vc
)
1743 unsigned long flags
;
1745 if (speakup_console
[vc
->vc_num
] == NULL
|| spk_parked
)
1747 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1748 /* Speakup output, discard */
1751 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1754 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1756 unsigned long flags
;
1760 if (synth
== NULL
|| up_flag
|| spk_killed
)
1762 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1763 spk_shut_up
&= 0xfe;
1768 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1769 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1772 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1773 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1776 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1777 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1778 if (speakup_console
[vc
->vc_num
])
1779 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1783 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1787 synth_printf("%s %s\n",
1788 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1789 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1792 static int inc_dec_var(u_char value
)
1794 struct st_var_header
*p_header
;
1795 struct var_t
*var_data
;
1799 int var_id
= (int)value
- VAR_START
;
1800 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1802 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1803 p_header
= spk_get_var_header(var_id
);
1804 if (p_header
== NULL
)
1806 if (p_header
->var_type
!= VAR_NUM
)
1808 var_data
= p_header
->data
;
1809 if (spk_set_num_var(1, p_header
, how
) != 0)
1811 if (!spk_close_press
) {
1812 for (pn
= p_header
->name
; *pn
; pn
++) {
1819 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1820 var_data
->u
.n
.value
);
1821 synth_printf("%s", num_buf
);
1825 static void speakup_win_set(struct vc_data
*vc
)
1829 if (win_start
> 1) {
1830 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1833 if (spk_x
< win_left
|| spk_y
< win_top
) {
1834 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1837 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1839 win_right
= vc
->vc_cols
- 1;
1841 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1851 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1853 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1854 (int)spk_y
+ 1, (int)spk_x
+ 1);
1856 synth_printf("%s\n", info
);
1860 static void speakup_win_clear(struct vc_data
*vc
)
1862 win_top
= win_bottom
= 0;
1863 win_left
= win_right
= 0;
1865 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1868 static void speakup_win_enable(struct vc_data
*vc
)
1870 if (win_start
< 2) {
1871 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1876 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1878 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1881 static void speakup_bits(struct vc_data
*vc
)
1883 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1885 if (spk_special_handler
!= NULL
|| val
< 1 || val
> 6) {
1886 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1889 pb_edit
= &spk_punc_info
[val
];
1890 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1891 spk_special_handler
= edit_bits
;
1894 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1896 static u_char goto_buf
[8];
1901 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1903 if (type
== KT_LATIN
&& ch
== '\n')
1910 ch
= goto_buf
[--num
];
1911 goto_buf
[num
] = '\0';
1912 spkup_write(&ch
, 1);
1915 if (ch
< '+' || ch
> 'y')
1917 goto_buf
[num
++] = ch
;
1918 goto_buf
[num
] = '\0';
1919 spkup_write(&ch
, 1);
1920 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1921 if ((ch
== '+' || ch
== '-') && num
== 1)
1923 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1925 if (num
< maxlen
- 1 || num
> maxlen
)
1927 if (ch
< 'x' || ch
> 'y') {
1930 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1931 goto_buf
[num
= 0] = '\0';
1932 spk_special_handler
= NULL
;
1936 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1939 if (*goto_buf
< '0')
1941 else if (goto_pos
> 0)
1944 if (goto_pos
>= vc
->vc_cols
)
1945 goto_pos
= vc
->vc_cols
- 1;
1948 if (*goto_buf
< '0')
1950 else if (goto_pos
> 0)
1953 if (goto_pos
>= vc
->vc_rows
)
1954 goto_pos
= vc
->vc_rows
- 1;
1957 goto_buf
[num
= 0] = '\0';
1959 spk_special_handler
= NULL
;
1962 spk_pos
-= spk_x
* 2;
1964 spk_pos
+= goto_pos
* 2;
1968 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
1974 static void speakup_goto(struct vc_data
*vc
)
1976 if (spk_special_handler
!= NULL
) {
1977 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1980 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
1981 spk_special_handler
= handle_goto
;
1984 static void speakup_help(struct vc_data
*vc
)
1986 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
1989 static void do_nothing(struct vc_data
*vc
)
1991 return; /* flush done in do_spkup */
1994 static u_char key_speakup
, spk_key_locked
;
1996 static void speakup_lock(struct vc_data
*vc
)
1998 if (!spk_key_locked
)
1999 spk_key_locked
= key_speakup
= 16;
2001 spk_key_locked
= key_speakup
= 0;
2004 typedef void (*spkup_hand
) (struct vc_data
*);
2005 static spkup_hand spkup_handler
[] = {
2006 /* must be ordered same as defines in speakup.h */
2007 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2008 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2009 say_char
, say_prev_char
, say_next_char
,
2010 say_word
, say_prev_word
, say_next_word
,
2011 say_line
, say_prev_line
, say_next_line
,
2012 top_edge
, bottom_edge
, left_edge
, right_edge
,
2013 spell_word
, spell_word
, say_screen
,
2014 say_position
, say_attributes
,
2015 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2016 say_from_top
, say_to_bottom
,
2017 say_from_left
, say_to_right
,
2018 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2019 speakup_bits
, speakup_bits
, speakup_bits
,
2020 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2021 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2024 static void do_spkup(struct vc_data
*vc
, u_char value
)
2026 if (spk_killed
&& value
!= SPEECH_KILL
)
2030 spk_shut_up
&= 0xfe;
2031 this_speakup_key
= value
;
2032 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2034 (*spkup_handler
[value
]) (vc
);
2036 if (inc_dec_var(value
) < 0)
2041 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2044 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2047 unsigned long flags
;
2050 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2051 u_char shift_info
, offset
;
2057 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2062 && (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2067 value
= spk_lastkey
= pad_chars
[value
];
2072 if (keycode
>= MAX_KEY
)
2074 key_info
= spk_our_keys
[keycode
];
2077 /* Check valid read all mode keys */
2078 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2092 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2093 offset
= spk_shift_table
[shift_info
];
2095 new_key
= key_info
[offset
];
2098 if (new_key
== SPK_KEY
) {
2099 if (!spk_key_locked
)
2100 key_speakup
= (up_flag
) ? 0 : 16;
2101 if (up_flag
|| spk_killed
)
2103 spk_shut_up
&= 0xfe;
2109 if (last_keycode
== keycode
&&
2110 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2111 spk_close_press
= 1;
2112 offset
= spk_shift_table
[shift_info
+ 32];
2114 if (offset
&& key_info
[offset
])
2115 new_key
= key_info
[offset
];
2117 last_keycode
= keycode
;
2118 last_spk_jiffy
= jiffies
;
2124 if (type
== KT_SPKUP
&& spk_special_handler
== NULL
) {
2125 do_spkup(vc
, new_key
);
2126 spk_close_press
= 0;
2130 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2132 spk_shut_up
&= 0xfe;
2133 kh
= (value
== KVAL(K_DOWN
))
2134 || (value
== KVAL(K_UP
))
2135 || (value
== KVAL(K_LEFT
))
2136 || (value
== KVAL(K_RIGHT
));
2137 if ((cursor_track
!= read_all_mode
) || !kh
)
2140 if (spk_special_handler
) {
2141 if (type
== KT_SPEC
&& value
== 1) {
2144 } else if (type
== KT_LETTER
)
2146 else if (value
== 0x7f)
2147 value
= 8; /* make del = backspace */
2148 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2149 spk_close_press
= 0;
2156 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2160 static int keyboard_notifier_call(struct notifier_block
*nb
,
2161 unsigned long code
, void *_param
)
2163 struct keyboard_notifier_param
*param
= _param
;
2164 struct vc_data
*vc
= param
->vc
;
2165 int up
= !param
->down
;
2166 int ret
= NOTIFY_OK
;
2167 static int keycode
; /* to hold the current keycode */
2169 if (vc
->vc_mode
== KD_GRAPHICS
)
2173 * First, determine whether we are handling a fake keypress on
2174 * the current processor. If we are, then return NOTIFY_OK,
2175 * to pass the keystroke up the chain. This prevents us from
2176 * trying to take the Speakup lock while it is held by the
2177 * processor on which the simulated keystroke was generated.
2178 * Also, the simulated keystrokes should be ignored by Speakup.
2181 if (speakup_fake_key_pressed())
2186 /* speakup requires keycode and keysym currently */
2187 keycode
= param
->value
;
2189 case KBD_UNBOUND_KEYCODE
:
2196 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2198 else if (KTYP(param
->value
) == KT_CUR
)
2199 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2201 case KBD_POST_KEYSYM
:{
2202 unsigned char type
= KTYP(param
->value
) - 0xf0;
2203 unsigned char val
= KVAL(param
->value
);
2207 do_handle_shift(vc
, val
, up
);
2211 do_handle_latin(vc
, val
, up
);
2214 do_handle_cursor(vc
, val
, up
);
2217 do_handle_spec(vc
, val
, up
);
2226 static int vt_notifier_call(struct notifier_block
*nb
,
2227 unsigned long code
, void *_param
)
2229 struct vt_notifier_param
*param
= _param
;
2230 struct vc_data
*vc
= param
->vc
;
2234 if (vc
->vc_mode
== KD_TEXT
)
2235 speakup_allocate(vc
);
2238 speakup_deallocate(vc
);
2241 if (param
->c
== '\b')
2243 else if (param
->c
< 0x100) {
2246 speakup_con_write(vc
, &d
, 1);
2250 speakup_con_update(vc
);
2256 /* called by: module_exit() */
2257 static void __exit
speakup_exit(void)
2261 unregister_keyboard_notifier(&keyboard_notifier_block
);
2262 unregister_vt_notifier(&vt_notifier_block
);
2263 speakup_unregister_devsynth();
2264 speakup_cancel_paste();
2265 del_timer(&cursor_timer
);
2266 kthread_stop(speakup_task
);
2267 speakup_task
= NULL
;
2268 mutex_lock(&spk_mutex
);
2270 mutex_unlock(&spk_mutex
);
2272 speakup_kobj_exit();
2274 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2275 kfree(speakup_console
[i
]);
2277 speakup_remove_virtual_keyboard();
2279 for (i
= 0; i
< MAXVARS
; i
++)
2280 speakup_unregister_var(i
);
2282 for (i
= 0; i
< 256; i
++) {
2283 if (spk_characters
[i
] != spk_default_chars
[i
])
2284 kfree(spk_characters
[i
]);
2287 spk_free_user_msgs();
2290 /* call by: module_init() */
2291 static int __init
speakup_init(void)
2295 struct st_spk_t
*first_console
;
2296 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2299 /* These first few initializations cannot fail. */
2300 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2301 spk_reset_default_chars();
2302 spk_reset_default_chartab();
2303 spk_strlwr(synth_name
);
2304 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2305 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2306 speakup_register_var(var
);
2307 for (var
= synth_time_vars
;
2308 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2309 speakup_register_var(var
);
2310 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2311 spk_set_mask_bits(NULL
, i
, 2);
2313 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2315 /* From here on out, initializations can fail. */
2316 err
= speakup_add_virtual_keyboard();
2318 goto error_virtkeyboard
;
2320 first_console
= kzalloc(sizeof(*first_console
), GFP_KERNEL
);
2321 if (!first_console
) {
2326 speakup_console
[vc
->vc_num
] = first_console
;
2329 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2331 err
= speakup_allocate(vc_cons
[i
].d
);
2333 goto error_kobjects
;
2337 spk_shut_up
|= 0x01;
2339 err
= speakup_kobj_init();
2341 goto error_kobjects
;
2343 synth_init(synth_name
);
2344 speakup_register_devsynth();
2346 * register_devsynth might fail, but this error is not fatal.
2347 * /dev/synth is an extra feature; the rest of Speakup
2348 * will work fine without it.
2351 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2353 goto error_kbdnotifier
;
2354 err
= register_vt_notifier(&vt_notifier_block
);
2356 goto error_vtnotifier
;
2358 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2360 if (IS_ERR(speakup_task
)) {
2361 err
= PTR_ERR(speakup_task
);
2365 set_user_nice(speakup_task
, 10);
2366 wake_up_process(speakup_task
);
2368 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2369 pr_info("synth name on entry is: %s\n", synth_name
);
2373 unregister_vt_notifier(&vt_notifier_block
);
2376 unregister_keyboard_notifier(&keyboard_notifier_block
);
2377 del_timer(&cursor_timer
);
2380 speakup_unregister_devsynth();
2381 mutex_lock(&spk_mutex
);
2383 mutex_unlock(&spk_mutex
);
2384 speakup_kobj_exit();
2387 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2388 kfree(speakup_console
[i
]);
2391 speakup_remove_virtual_keyboard();
2394 for (i
= 0; i
< MAXVARS
; i
++)
2395 speakup_unregister_var(i
);
2397 for (i
= 0; i
< 256; i
++) {
2398 if (spk_characters
[i
] != spk_default_chars
[i
])
2399 kfree(spk_characters
[i
]);
2402 spk_free_user_msgs();
2408 module_init(speakup_init
);
2409 module_exit(speakup_exit
);