1 // SPDX-License-Identifier: GPL-2.0+
3 * review functions for the speakup screen review package.
4 * originally written by: Kirk Reiser and Andy Berdan.
6 * extensively modified by David Borowski.
8 ** Copyright (C) 1998 Kirk Reiser.
9 * Copyright (C) 2003 David Borowski.
12 #include <linux/kernel.h>
14 #include <linux/tty.h>
15 #include <linux/mm.h> /* __get_free_page() and friends */
16 #include <linux/vt_kern.h>
17 #include <linux/ctype.h>
18 #include <linux/selection.h>
19 #include <linux/unistd.h>
20 #include <linux/jiffies.h>
21 #include <linux/kthread.h>
22 #include <linux/keyboard.h> /* for KT_SHIFT */
23 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
24 #include <linux/input.h>
25 #include <linux/kmod.h>
27 /* speakup_*_selection */
28 #include <linux/module.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <linux/types.h>
32 #include <linux/consolemap.h>
34 #include <linux/spinlock.h>
35 #include <linux/notifier.h>
37 #include <linux/uaccess.h> /* copy_from|to|user() and others */
42 #define MAX_DELAY msecs_to_jiffies(500)
43 #define MINECHOCHAR SPACE
45 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
46 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
47 MODULE_DESCRIPTION("Speakup console speech");
48 MODULE_LICENSE("GPL");
49 MODULE_VERSION(SPEAKUP_VERSION
);
52 module_param_named(synth
, synth_name
, charp
, 0444);
53 module_param_named(quiet
, spk_quiet_boot
, bool, 0444);
55 MODULE_PARM_DESC(synth
, "Synth to start if speakup is built in.");
56 MODULE_PARM_DESC(quiet
, "Do not announce when the synthesizer is found.");
58 special_func spk_special_handler
;
60 short spk_pitch_shift
, synth_flags
;
62 int spk_attrib_bleep
, spk_bleeps
, spk_bleep_time
= 10;
63 int spk_no_intr
, spk_spell_delay
;
64 int spk_key_echo
, spk_say_word_ctl
;
65 int spk_say_ctrl
, spk_bell_pos
;
67 int spk_punc_level
, spk_reading_punc
;
68 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0";
69 char spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
70 const struct st_bits_data spk_punc_info
[] = {
72 {"some", "/$%&@", SOME
},
73 {"most", "$%&#()=+*/@^<>|\\", MOST
},
74 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
75 {"delimiters", "", B_WDLM
},
76 {"repeats", "()", CH_RPT
},
77 {"extended numeric", "", B_EXNUM
},
78 {"symbols", "", B_SYM
},
82 static char mark_cut_flag
;
84 static u_char
*spk_shift_table
;
85 u_char
*spk_our_keys
[MAX_KEY
];
86 u_char spk_key_buf
[600];
87 const u_char spk_key_defaults
[] = {
88 #include "speakupmap.h"
91 /* Speakup Cursor Track Variables */
92 static int cursor_track
= 1, prev_cursor_track
= 1;
94 /* cursor track modes, must be ordered same as cursor_msgs */
103 #define read_all_mode CT_Max
105 static struct tty_struct
*tty
;
107 static void spkup_write(const u16
*in_buf
, int count
);
109 static char *phonetic
[] = {
110 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
111 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
113 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
114 "x ray", "yankee", "zulu"
117 /* array of 256 char pointers (one for each character description)
118 * initialized to default_chars and user selectable via
119 * /proc/speakup/characters
121 char *spk_characters
[256];
123 char *spk_default_chars
[256] = {
124 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
125 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
126 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
127 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
129 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
131 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
134 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
136 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
137 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
138 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
139 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
140 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
143 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
144 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
145 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
146 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
147 /*127*/ "del", "control", "control", "control", "control", "control",
148 "control", "control", "control", "control", "control",
149 /*138*/ "control", "control", "control", "control", "control",
150 "control", "control", "control", "control", "control",
151 "control", "control",
152 /*150*/ "control", "control", "control", "control", "control",
153 "control", "control", "control", "control", "control",
154 /*160*/ "nbsp", "inverted bang",
155 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
156 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
157 /*172*/ "not", "soft hyphen", "registered", "macron",
158 /*176*/ "degrees", "plus or minus", "super two", "super three",
159 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
160 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
161 /*188*/ "one quarter", "one half", "three quarters",
163 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
165 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
167 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
169 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
170 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
172 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
173 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
174 /*230*/ "ae", "c cidella", "e grave", "e acute",
175 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
177 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
179 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
181 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
184 /* array of 256 u_short (one for each character)
185 * initialized to default_chartab and user selectable via
186 * /sys/module/speakup/parameters/chartab
188 u_short spk_chartab
[256];
190 static u_short default_chartab
[256] = {
191 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
192 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
193 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
194 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
195 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
196 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
197 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
198 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
199 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
200 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
201 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
202 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
203 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
204 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
205 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
206 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
207 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
209 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
211 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
213 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
215 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
217 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
218 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
219 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
220 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
221 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
222 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
223 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
224 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
225 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
226 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
227 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
230 struct task_struct
*speakup_task
;
231 struct bleep spk_unprocessed_sound
;
232 static int spk_keydown
;
233 static u16 spk_lastkey
;
234 static u_char spk_close_press
, keymap_flags
;
235 static u_char last_keycode
, this_speakup_key
;
236 static u_long last_spk_jiffy
;
238 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
240 DEFINE_MUTEX(spk_mutex
);
242 static int keyboard_notifier_call(struct notifier_block
*,
243 unsigned long code
, void *param
);
245 static struct notifier_block keyboard_notifier_block
= {
246 .notifier_call
= keyboard_notifier_call
,
249 static int vt_notifier_call(struct notifier_block
*,
250 unsigned long code
, void *param
);
252 static struct notifier_block vt_notifier_block
= {
253 .notifier_call
= vt_notifier_call
,
256 static unsigned char get_attributes(struct vc_data
*vc
, u16
*pos
)
258 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
259 return (scr_readw(pos
) & ~vc
->vc_hi_font_mask
) >> 8;
262 static void speakup_date(struct vc_data
*vc
)
264 spk_x
= spk_cx
= vc
->vc_x
;
265 spk_y
= spk_cy
= vc
->vc_y
;
266 spk_pos
= spk_cp
= vc
->vc_pos
;
267 spk_old_attr
= spk_attr
;
268 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
271 static void bleep(u_short val
)
273 static const short vals
[] = {
274 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
277 int time
= spk_bleep_time
;
279 freq
= vals
[val
% 12];
281 freq
*= (1 << (val
/ 12));
282 spk_unprocessed_sound
.freq
= freq
;
283 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
284 spk_unprocessed_sound
.active
= 1;
285 /* We can only have 1 active sound at a time. */
288 static void speakup_shut_up(struct vc_data
*vc
)
299 static void speech_kill(struct vc_data
*vc
)
301 char val
= synth
->is_alive(synth
);
306 /* re-enables synth, if disabled */
307 if (val
== 2 || spk_killed
) {
309 spk_shut_up
&= ~0x40;
310 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
312 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
317 static void speakup_off(struct vc_data
*vc
)
319 if (spk_shut_up
& 0x80) {
321 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
324 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
329 static void speakup_parked(struct vc_data
*vc
)
331 if (spk_parked
& 0x80) {
333 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
336 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
340 static void speakup_cut(struct vc_data
*vc
)
342 static const char err_buf
[] = "set selection failed";
345 if (!mark_cut_flag
) {
347 spk_xs
= (u_short
)spk_x
;
348 spk_ys
= (u_short
)spk_y
;
350 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
353 spk_xe
= (u_short
)spk_x
;
354 spk_ye
= (u_short
)spk_y
;
356 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
358 speakup_clear_selection();
359 ret
= speakup_set_selection(tty
);
363 break; /* no error */
365 pr_warn("%sEFAULT\n", err_buf
);
368 pr_warn("%sEINVAL\n", err_buf
);
371 pr_warn("%sENOMEM\n", err_buf
);
376 static void speakup_paste(struct vc_data
*vc
)
380 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
382 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
383 speakup_paste_selection(tty
);
387 static void say_attributes(struct vc_data
*vc
)
389 int fg
= spk_attr
& 0x0f;
390 int bg
= spk_attr
>> 4;
393 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
396 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
398 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
401 synth_printf(" %s ", spk_msg_get(MSG_ON
));
403 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
414 static void announce_edge(struct vc_data
*vc
, int msg_id
)
418 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
420 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
423 static void speak_char(u16 ch
)
426 struct var_t
*direct
= spk_get_var(DIRECT
);
428 if (ch
>= 0x100 || (direct
&& direct
->u
.n
.value
)) {
429 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
)) {
431 synth_printf("%s", spk_str_caps_start
);
434 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
))
435 synth_printf("%s", spk_str_caps_stop
);
439 cp
= spk_characters
[ch
];
441 pr_info("%s: cp == NULL!\n", __func__
);
444 if (IS_CHAR(ch
, B_CAP
)) {
446 synth_printf("%s %s %s",
447 spk_str_caps_start
, cp
, spk_str_caps_stop
);
451 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL
), cp
);
453 synth_printf(" %s ", cp
);
457 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
465 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
469 if (w
& vc
->vc_hi_font_mask
) {
470 w
&= ~vc
->vc_hi_font_mask
;
474 ch
= inverse_translate(vc
, c
, 1);
475 *attribs
= (w
& 0xff00) >> 8;
480 static void say_char(struct vc_data
*vc
)
484 spk_old_attr
= spk_attr
;
485 ch
= get_char(vc
, (u_short
*)spk_pos
, &spk_attr
);
486 if (spk_attr
!= spk_old_attr
) {
487 if (spk_attrib_bleep
& 1)
489 if (spk_attrib_bleep
& 2)
495 static void say_phonetic_char(struct vc_data
*vc
)
499 spk_old_attr
= spk_attr
;
500 ch
= get_char(vc
, (u_short
*)spk_pos
, &spk_attr
);
501 if (ch
<= 0x7f && isalpha(ch
)) {
503 synth_printf("%s\n", phonetic
[--ch
]);
505 if (ch
< 0x100 && IS_CHAR(ch
, B_NUM
))
506 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
511 static void say_prev_char(struct vc_data
*vc
)
515 announce_edge(vc
, edge_left
);
523 static void say_next_char(struct vc_data
*vc
)
526 if (spk_x
== vc
->vc_cols
- 1) {
527 announce_edge(vc
, edge_right
);
535 /* get_word - will first check to see if the character under the
536 * reading cursor is a space and if spk_say_word_ctl is true it will
537 * return the word space. If spk_say_word_ctl is not set it will check to
538 * see if there is a word starting on the next position to the right
539 * and return that word if it exists. If it does not exist it will
540 * move left to the beginning of any previous word on the line or the
541 * beginning off the line whichever comes first..
544 static u_long
get_word(struct vc_data
*vc
)
546 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
551 spk_old_attr
= spk_attr
;
552 ch
= get_char(vc
, (u_short
*)tmp_pos
, &temp
);
554 /* decided to take out the sayword if on a space (mis-information */
555 if (spk_say_word_ctl
&& ch
== SPACE
) {
557 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
559 } else if (tmpx
< vc
->vc_cols
- 2 &&
560 (ch
== SPACE
|| ch
== 0 || (ch
< 0x100 && IS_WDLM(ch
))) &&
561 get_char(vc
, (u_short
*)&tmp_pos
+ 1, &temp
) > SPACE
) {
566 ch
= get_char(vc
, (u_short
*)tmp_pos
- 1, &temp
);
567 if ((ch
== SPACE
|| ch
== 0 ||
568 (ch
< 0x100 && IS_WDLM(ch
))) &&
569 get_char(vc
, (u_short
*)tmp_pos
, &temp
) > SPACE
)
574 attr_ch
= get_char(vc
, (u_short
*)tmp_pos
, &spk_attr
);
575 buf
[cnt
++] = attr_ch
;
576 while (tmpx
< vc
->vc_cols
- 1) {
579 ch
= get_char(vc
, (u_short
*)tmp_pos
, &temp
);
580 if (ch
== SPACE
|| ch
== 0 ||
581 (buf
[cnt
- 1] < 0x100 && IS_WDLM(buf
[cnt
- 1]) &&
590 static void say_word(struct vc_data
*vc
)
592 u_long cnt
= get_word(vc
);
593 u_short saved_punc_mask
= spk_punc_mask
;
597 spk_punc_mask
= PUNC
;
599 spkup_write(buf
, cnt
);
600 spk_punc_mask
= saved_punc_mask
;
603 static void say_prev_word(struct vc_data
*vc
)
607 u_short edge_said
= 0, last_state
= 0, state
= 0;
613 announce_edge(vc
, edge_top
);
618 edge_said
= edge_quiet
;
623 edge_said
= edge_top
;
626 if (edge_said
!= edge_quiet
)
627 edge_said
= edge_left
;
631 spk_x
= vc
->vc_cols
- 1;
636 ch
= get_char(vc
, (u_short
*)spk_pos
, &temp
);
637 if (ch
== SPACE
|| ch
== 0)
639 else if (ch
< 0x100 && 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
= get_char(vc
, (u_short
*)spk_pos
, &temp
);
670 if (ch
== SPACE
|| ch
== 0)
672 else if (ch
< 0x100 && 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
;
694 announce_edge(vc
, edge_said
);
698 static void spell_word(struct vc_data
*vc
)
700 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
703 char *str_cap
= spk_str_caps_stop
;
704 char *last_cap
= spk_str_caps_stop
;
705 struct var_t
*direct
= spk_get_var(DIRECT
);
712 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
713 /* FIXME: Non-latin1 considered as lower case */
714 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
)) {
715 str_cap
= spk_str_caps_start
;
716 if (*spk_str_caps_stop
)
718 else /* synth has no pitch */
719 last_cap
= spk_str_caps_stop
;
721 str_cap
= spk_str_caps_stop
;
723 if (str_cap
!= last_cap
) {
724 synth_printf("%s", str_cap
);
727 if (ch
>= 0x100 || (direct
&& direct
->u
.n
.value
)) {
729 } else if (this_speakup_key
== SPELL_PHONETIC
&&
730 ch
<= 0x7f && isalpha(ch
)) {
732 cp1
= phonetic
[--ch
];
733 synth_printf("%s", cp1
);
735 cp1
= spk_characters
[ch
];
737 synth_printf("%s", spk_msg_get(MSG_CTRL
));
740 synth_printf("%s", cp1
);
744 if (str_cap
!= spk_str_caps_stop
)
745 synth_printf("%s", spk_str_caps_stop
);
748 static int get_line(struct vc_data
*vc
)
750 u_long tmp
= spk_pos
- (spk_x
* 2);
754 spk_old_attr
= spk_attr
;
755 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
756 for (i
= 0; i
< vc
->vc_cols
; i
++) {
757 buf
[i
] = get_char(vc
, (u_short
*)tmp
, &tmp2
);
760 for (--i
; i
>= 0; i
--)
766 static void say_line(struct vc_data
*vc
)
768 int i
= get_line(vc
);
770 u_short saved_punc_mask
= spk_punc_mask
;
773 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
777 if (this_speakup_key
== SAY_LINE_INDENT
) {
781 synth_printf("%zd, ", (cp
- buf
) + 1);
783 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
785 spk_punc_mask
= saved_punc_mask
;
788 static void say_prev_line(struct vc_data
*vc
)
792 announce_edge(vc
, edge_top
);
796 spk_pos
-= vc
->vc_size_row
;
800 static void say_next_line(struct vc_data
*vc
)
803 if (spk_y
== vc
->vc_rows
- 1) {
804 announce_edge(vc
, edge_bottom
);
808 spk_pos
+= vc
->vc_size_row
;
812 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
817 u_short saved_punc_mask
= spk_punc_mask
;
819 spk_old_attr
= spk_attr
;
820 spk_attr
= get_attributes(vc
, (u_short
*)from
);
822 buf
[i
++] = get_char(vc
, (u_short
*)from
, &tmp
);
824 if (i
>= vc
->vc_size_row
)
827 for (--i
; i
>= 0; i
--)
835 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
838 spk_punc_mask
= saved_punc_mask
;
842 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
845 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
846 u_long end
= start
+ (to
* 2);
849 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
850 if (cursor_track
!= read_all_mode
)
851 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
854 /* Sentence Reading Commands */
856 static int currsentence
;
857 static int numsentences
[2];
858 static u16
*sentbufend
[2];
859 static u16
*sentmarks
[2][10];
862 static u16 sentbuf
[2][256];
864 static int say_sentence_num(int num
, int prev
)
867 currsentence
= num
+ 1;
868 if (prev
&& --bn
== -1)
871 if (num
> numsentences
[bn
])
874 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
878 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
888 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
889 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
891 numsentences
[bn
] = 0;
892 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
894 spk_old_attr
= spk_attr
;
895 spk_attr
= get_attributes(vc
, (u_short
*)start
);
897 while (start
< end
) {
898 sentbuf
[bn
][i
] = get_char(vc
, (u_short
*)start
, &tmp
);
900 if (sentbuf
[bn
][i
] == SPACE
&& sentbuf
[bn
][i
- 1] == '.' &&
901 numsentences
[bn
] < 9) {
902 /* Sentence Marker */
904 sentmarks
[bn
][numsentences
[bn
]] =
910 if (i
>= vc
->vc_size_row
)
914 for (--i
; i
>= 0; i
--)
915 if (sentbuf
[bn
][i
] != SPACE
)
921 sentbuf
[bn
][++i
] = SPACE
;
922 sentbuf
[bn
][++i
] = '\0';
924 sentbufend
[bn
] = &sentbuf
[bn
][i
];
925 return numsentences
[bn
];
928 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
930 u_long start
= vc
->vc_origin
, end
;
933 start
+= from
* vc
->vc_size_row
;
934 if (to
> vc
->vc_rows
)
936 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
937 for (from
= start
; from
< end
; from
= to
) {
938 to
= from
+ vc
->vc_size_row
;
939 say_from_to(vc
, from
, to
, 1);
943 static void say_screen(struct vc_data
*vc
)
945 say_screen_from_to(vc
, 0, vc
->vc_rows
);
948 static void speakup_win_say(struct vc_data
*vc
)
950 u_long start
, end
, from
, to
;
953 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
956 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
957 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
958 while (start
<= end
) {
959 from
= start
+ (win_left
* 2);
960 to
= start
+ (win_right
* 2);
961 say_from_to(vc
, from
, to
, 1);
962 start
+= vc
->vc_size_row
;
966 static void top_edge(struct vc_data
*vc
)
969 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
974 static void bottom_edge(struct vc_data
*vc
)
977 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
978 spk_y
= vc
->vc_rows
- 1;
982 static void left_edge(struct vc_data
*vc
)
985 spk_pos
-= spk_x
* 2;
990 static void right_edge(struct vc_data
*vc
)
993 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
994 spk_x
= vc
->vc_cols
- 1;
998 static void say_first_char(struct vc_data
*vc
)
1000 int i
, len
= get_line(vc
);
1005 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1008 for (i
= 0; i
< len
; i
++)
1009 if (buf
[i
] != SPACE
)
1012 spk_pos
-= (spk_x
- i
) * 2;
1014 synth_printf("%d, ", ++i
);
1018 static void say_last_char(struct vc_data
*vc
)
1020 int len
= get_line(vc
);
1025 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1029 spk_pos
-= (spk_x
- len
) * 2;
1031 synth_printf("%d, ", ++len
);
1035 static void say_position(struct vc_data
*vc
)
1037 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1042 /* Added by brianb */
1043 static void say_char_num(struct vc_data
*vc
)
1046 u16 ch
= get_char(vc
, (u_short
*)spk_pos
, &tmp
);
1048 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1051 /* these are stub functions to keep keyboard.c happy. */
1053 static void say_from_top(struct vc_data
*vc
)
1055 say_screen_from_to(vc
, 0, spk_y
);
1058 static void say_to_bottom(struct vc_data
*vc
)
1060 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1063 static void say_from_left(struct vc_data
*vc
)
1065 say_line_from_to(vc
, 0, spk_x
, 1);
1068 static void say_to_right(struct vc_data
*vc
)
1070 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1073 /* end of stub functions. */
1075 static void spkup_write(const u16
*in_buf
, int count
)
1077 static int rep_count
;
1078 static u16 ch
= '\0', old_ch
= '\0';
1079 static u_short char_type
, last_type
;
1080 int in_count
= count
;
1084 if (cursor_track
== read_all_mode
) {
1085 /* Insert Sentence Index */
1086 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1087 (currsentence
<= numsentences
[bn
]))
1088 synth_insert_next_index(currsentence
++);
1092 char_type
= spk_chartab
[ch
];
1095 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1096 if (++rep_count
> 2)
1099 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1101 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1107 if (ch
== spk_lastkey
) {
1109 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1111 } else if (char_type
& B_ALPHA
) {
1112 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1113 synth_buffer_add(SPACE
);
1115 } else if (char_type
& B_NUM
) {
1118 } else if (char_type
& spk_punc_mask
) {
1120 char_type
&= ~PUNC
; /* for dec nospell processing */
1121 } else if (char_type
& SYNTH_OK
) {
1122 /* these are usually puncts like . and , which synth
1123 * needs for expression.
1124 * suppress multiple to get rid of long pauses and
1125 * clear repeat count
1127 * repeats on you don't get nothing repeated count
1134 /* send space and record position, if next is num overwrite space */
1136 synth_buffer_add(SPACE
);
1141 last_type
= char_type
;
1144 if (in_count
> 2 && rep_count
> 2) {
1145 if (last_type
& CH_RPT
) {
1147 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1155 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1157 static void read_all_doc(struct vc_data
*vc
);
1158 static void cursor_done(struct timer_list
*unused
);
1159 static DEFINE_TIMER(cursor_timer
, cursor_done
);
1161 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1163 unsigned long flags
;
1165 if (!synth
|| up_flag
|| spk_killed
)
1167 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1168 if (cursor_track
== read_all_mode
) {
1171 del_timer(&cursor_timer
);
1172 spk_shut_up
&= 0xfe;
1177 del_timer(&cursor_timer
);
1178 cursor_track
= prev_cursor_track
;
1179 spk_shut_up
&= 0xfe;
1184 spk_shut_up
&= 0xfe;
1187 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1188 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1189 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1192 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1194 unsigned long flags
;
1196 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1200 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1203 if (!synth
|| spk_killed
) {
1204 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1207 spk_shut_up
&= 0xfe;
1208 spk_lastkey
= value
;
1211 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1213 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1216 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1218 int i
= 0, states
, key_data_len
;
1219 const u_char
*cp
= key_info
;
1220 u_char
*cp1
= k_buffer
;
1221 u_char ch
, version
, num_keys
;
1224 if (version
!= KEY_MAP_VER
) {
1225 pr_debug("version found %d should be %d\n",
1226 version
, KEY_MAP_VER
);
1230 states
= (int)cp
[1];
1231 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1232 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
)) {
1233 pr_debug("too many key_infos (%d over %u)\n",
1234 key_data_len
+ SHIFT_TBL_SIZE
+ 4, (unsigned int)(sizeof(spk_key_buf
)));
1237 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1238 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1239 spk_shift_table
= k_buffer
;
1240 spk_our_keys
[0] = spk_shift_table
;
1241 cp1
+= SHIFT_TBL_SIZE
;
1242 memcpy(cp1
, cp
, key_data_len
+ 3);
1243 /* get num_keys, states and data */
1244 cp1
+= 2; /* now pointing at shift states */
1245 for (i
= 1; i
<= states
; i
++) {
1247 if (ch
>= SHIFT_TBL_SIZE
) {
1248 pr_debug("(%d) not valid shift state (max_allowed = %d)\n", ch
,
1252 spk_shift_table
[ch
] = i
;
1254 keymap_flags
= *cp1
++;
1255 while ((ch
= *cp1
)) {
1256 if (ch
>= MAX_KEY
) {
1257 pr_debug("(%d), not valid key, (max_allowed = %d)\n", ch
, MAX_KEY
);
1260 spk_our_keys
[ch
] = cp1
;
1266 static struct var_t spk_vars
[] = {
1267 /* bell must be first to set high limit */
1268 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1269 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1270 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1271 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1272 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1273 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1274 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1275 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1276 {SAY_CONTROL
, TOGGLE_0
},
1277 {SAY_WORD_CTL
, TOGGLE_0
},
1278 {NO_INTERRUPT
, TOGGLE_0
},
1279 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1283 static void toggle_cursoring(struct vc_data
*vc
)
1285 if (cursor_track
== read_all_mode
)
1286 cursor_track
= prev_cursor_track
;
1287 if (++cursor_track
>= CT_Max
)
1289 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1292 void spk_reset_default_chars(void)
1296 /* First, free any non-default */
1297 for (i
= 0; i
< 256; i
++) {
1298 if (spk_characters
[i
] &&
1299 (spk_characters
[i
] != spk_default_chars
[i
]))
1300 kfree(spk_characters
[i
]);
1303 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1306 void spk_reset_default_chartab(void)
1308 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1311 static const struct st_bits_data
*pb_edit
;
1313 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1315 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1317 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1320 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1321 spk_special_handler
= NULL
;
1324 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1326 spk_chartab
[ch
] ^= mask
;
1328 synth_printf(" %s\n",
1329 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1330 spk_msg_get(MSG_OFF
));
1334 /* Allocation concurrency is protected by the console semaphore */
1335 static int speakup_allocate(struct vc_data
*vc
, gfp_t gfp_flags
)
1339 vc_num
= vc
->vc_num
;
1340 if (!speakup_console
[vc_num
]) {
1341 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1343 if (!speakup_console
[vc_num
])
1346 } else if (!spk_parked
) {
1353 static void speakup_deallocate(struct vc_data
*vc
)
1357 vc_num
= vc
->vc_num
;
1358 kfree(speakup_console
[vc_num
]);
1359 speakup_console
[vc_num
] = NULL
;
1362 static u_char is_cursor
;
1363 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1364 static int cursor_con
;
1366 static void reset_highlight_buffers(struct vc_data
*);
1368 static int read_all_key
;
1370 static int in_keyboard_notifier
;
1372 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1386 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1388 del_timer(&cursor_timer
);
1389 speakup_fake_down_arrow();
1390 start_read_all_timer(vc
, command
);
1393 static void read_all_doc(struct vc_data
*vc
)
1395 if ((vc
->vc_num
!= fg_console
) || !synth
|| spk_shut_up
)
1397 if (!synth_supports_indexing())
1399 if (cursor_track
!= read_all_mode
)
1400 prev_cursor_track
= cursor_track
;
1401 cursor_track
= read_all_mode
;
1402 spk_reset_index_count(0);
1403 if (get_sentence_buf(vc
, 0) == -1) {
1404 del_timer(&cursor_timer
);
1405 if (!in_keyboard_notifier
)
1406 speakup_fake_down_arrow();
1407 start_read_all_timer(vc
, RA_DOWN_ARROW
);
1409 say_sentence_num(0, 0);
1410 synth_insert_next_index(0);
1411 start_read_all_timer(vc
, RA_TIMER
);
1415 static void stop_read_all(struct vc_data
*vc
)
1417 del_timer(&cursor_timer
);
1418 cursor_track
= prev_cursor_track
;
1419 spk_shut_up
&= 0xfe;
1423 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1425 struct var_t
*cursor_timeout
;
1427 cursor_con
= vc
->vc_num
;
1428 read_all_key
= command
;
1429 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1430 mod_timer(&cursor_timer
,
1431 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1434 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1436 int indcount
, sentcount
, rv
, sn
;
1440 /* Get Current Sentence */
1441 spk_get_index_count(&indcount
, &sentcount
);
1442 /*printk("%d %d ", indcount, sentcount); */
1443 spk_reset_index_count(sentcount
+ 1);
1444 if (indcount
== 1) {
1445 if (!say_sentence_num(sentcount
+ 1, 0)) {
1446 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1449 synth_insert_next_index(0);
1452 if (!say_sentence_num(sentcount
+ 1, 1)) {
1454 spk_reset_index_count(sn
);
1456 synth_insert_next_index(0);
1458 if (!say_sentence_num(sn
, 0)) {
1459 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1462 synth_insert_next_index(0);
1464 start_read_all_timer(vc
, RA_TIMER
);
1474 if (get_sentence_buf(vc
, 0) == -1) {
1475 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1477 say_sentence_num(0, 0);
1478 synth_insert_next_index(0);
1479 start_read_all_timer(vc
, RA_TIMER
);
1482 case RA_FIND_NEXT_SENT
:
1483 rv
= get_sentence_buf(vc
, 0);
1487 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1489 say_sentence_num(1, 0);
1490 synth_insert_next_index(0);
1491 start_read_all_timer(vc
, RA_TIMER
);
1494 case RA_FIND_PREV_SENT
:
1497 spk_get_index_count(&indcount
, &sentcount
);
1499 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1501 start_read_all_timer(vc
, RA_TIMER
);
1506 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1508 unsigned long flags
;
1510 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1511 if (cursor_track
== read_all_mode
) {
1513 if (!synth
|| up_flag
|| spk_shut_up
) {
1514 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1517 del_timer(&cursor_timer
);
1518 spk_shut_up
&= 0xfe;
1520 start_read_all_timer(vc
, value
+ 1);
1521 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1524 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1528 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1530 unsigned long flags
;
1531 struct var_t
*cursor_timeout
;
1533 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1535 if (!synth
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1536 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1539 spk_shut_up
&= 0xfe;
1542 /* the key press flushes if !no_inter but we want to flush on cursor
1543 * moves regardless of no_inter state
1545 is_cursor
= value
+ 1;
1546 old_cursor_pos
= vc
->vc_pos
;
1547 old_cursor_x
= vc
->vc_x
;
1548 old_cursor_y
= vc
->vc_y
;
1549 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1550 cursor_con
= vc
->vc_num
;
1551 if (cursor_track
== CT_Highlight
)
1552 reset_highlight_buffers(vc
);
1553 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1554 mod_timer(&cursor_timer
,
1555 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1556 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1559 static void update_color_buffer(struct vc_data
*vc
, const u16
*ic
, int len
)
1562 int vc_num
= vc
->vc_num
;
1564 bi
= (vc
->vc_attr
& 0x70) >> 4;
1565 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1568 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1569 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1570 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1571 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1573 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1575 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1577 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1578 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1580 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1587 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1590 static void reset_highlight_buffers(struct vc_data
*vc
)
1593 int vc_num
= vc
->vc_num
;
1595 for (i
= 0; i
< 8; i
++)
1596 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1599 static int count_highlight_color(struct vc_data
*vc
)
1603 int vc_num
= vc
->vc_num
;
1605 u16
*start
= (u16
*)vc
->vc_origin
;
1607 for (i
= 0; i
< 8; i
++)
1608 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1610 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1611 u16
*end
= start
+ vc
->vc_cols
* 2;
1614 for (ptr
= start
; ptr
< end
; ptr
++) {
1615 ch
= get_attributes(vc
, ptr
);
1616 bg
= (ch
& 0x70) >> 4;
1617 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1619 start
+= vc
->vc_size_row
;
1623 for (i
= 0; i
< 8; i
++)
1624 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1629 static int get_highlight_color(struct vc_data
*vc
)
1632 unsigned int cptr
[8];
1633 int vc_num
= vc
->vc_num
;
1635 for (i
= 0; i
< 8; i
++)
1638 for (i
= 0; i
< 7; i
++)
1639 for (j
= i
+ 1; j
< 8; j
++)
1640 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1641 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]])
1642 swap(cptr
[i
], cptr
[j
]);
1644 for (i
= 0; i
< 8; i
++)
1645 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1646 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1651 static int speak_highlight(struct vc_data
*vc
)
1654 int vc_num
= vc
->vc_num
;
1656 if (count_highlight_color(vc
) == 1)
1658 hc
= get_highlight_color(vc
);
1660 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1661 if ((d
== 1) || (d
== -1))
1662 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1666 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1667 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1668 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1669 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1670 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1676 static void cursor_done(struct timer_list
*unused
)
1678 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1679 unsigned long flags
;
1681 del_timer(&cursor_timer
);
1682 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1683 if (cursor_con
!= fg_console
) {
1689 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1690 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1696 if (cursor_track
== read_all_mode
) {
1697 handle_cursor_read_all(vc
, read_all_key
);
1700 if (cursor_track
== CT_Highlight
) {
1701 if (speak_highlight(vc
)) {
1707 if (cursor_track
== CT_Window
)
1708 speakup_win_say(vc
);
1709 else if (is_cursor
== 1 || is_cursor
== 4)
1710 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1716 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1719 /* called by: vt_notifier_call() */
1720 static void speakup_bs(struct vc_data
*vc
)
1722 unsigned long flags
;
1724 if (!speakup_console
[vc
->vc_num
])
1726 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1727 /* Speakup output, discard */
1731 if (spk_shut_up
|| !synth
) {
1732 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1735 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1740 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1743 /* called by: vt_notifier_call() */
1744 static void speakup_con_write(struct vc_data
*vc
, u16
*str
, int len
)
1746 unsigned long flags
;
1748 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| !synth
)
1750 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1751 /* Speakup output, discard */
1753 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1755 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1756 if (cursor_track
== CT_Highlight
)
1757 update_color_buffer(vc
, str
, len
);
1758 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1762 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1763 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1764 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1769 spkup_write(str
, len
);
1770 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1773 static void speakup_con_update(struct vc_data
*vc
)
1775 unsigned long flags
;
1777 if (!speakup_console
[vc
->vc_num
] || spk_parked
)
1779 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1780 /* Speakup output, discard */
1783 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1786 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1788 unsigned long flags
;
1792 if (!synth
|| up_flag
|| spk_killed
)
1794 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1795 spk_shut_up
&= 0xfe;
1800 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1801 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1804 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1805 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1808 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1809 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1810 if (speakup_console
[vc
->vc_num
])
1811 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1815 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1819 synth_printf("%s %s\n",
1820 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1821 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1824 static int inc_dec_var(u_char value
)
1826 struct st_var_header
*p_header
;
1827 struct var_t
*var_data
;
1831 int var_id
= (int)value
- VAR_START
;
1832 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1834 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1835 p_header
= spk_get_var_header(var_id
);
1838 if (p_header
->var_type
!= VAR_NUM
)
1840 var_data
= p_header
->data
;
1841 if (spk_set_num_var(1, p_header
, how
) != 0)
1843 if (!spk_close_press
) {
1844 for (pn
= p_header
->name
; *pn
; pn
++) {
1851 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1852 var_data
->u
.n
.value
);
1853 synth_printf("%s", num_buf
);
1857 static void speakup_win_set(struct vc_data
*vc
)
1861 if (win_start
> 1) {
1862 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1865 if (spk_x
< win_left
|| spk_y
< win_top
) {
1866 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1869 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1871 win_right
= vc
->vc_cols
- 1;
1873 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1883 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1885 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1886 (int)spk_y
+ 1, (int)spk_x
+ 1);
1888 synth_printf("%s\n", info
);
1892 static void speakup_win_clear(struct vc_data
*vc
)
1899 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1902 static void speakup_win_enable(struct vc_data
*vc
)
1904 if (win_start
< 2) {
1905 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1910 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1912 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1915 static void speakup_bits(struct vc_data
*vc
)
1917 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1919 if (spk_special_handler
|| val
< 1 || val
> 6) {
1920 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1923 pb_edit
= &spk_punc_info
[val
];
1924 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1925 spk_special_handler
= edit_bits
;
1928 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1930 static u_char goto_buf
[8];
1936 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1938 if (type
== KT_LATIN
&& ch
== '\n')
1947 wch
= goto_buf
[--num
];
1948 goto_buf
[num
] = '\0';
1949 spkup_write(&wch
, 1);
1952 if (ch
< '+' || ch
> 'y')
1955 goto_buf
[num
++] = ch
;
1956 goto_buf
[num
] = '\0';
1957 spkup_write(&wch
, 1);
1958 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1959 if ((ch
== '+' || ch
== '-') && num
== 1)
1961 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1963 if (num
< maxlen
- 1 || num
> maxlen
)
1965 if (ch
< 'x' || ch
> 'y') {
1968 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1969 goto_buf
[num
= 0] = '\0';
1970 spk_special_handler
= NULL
;
1974 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1977 if (*goto_buf
< '0')
1979 else if (goto_pos
> 0)
1982 if (goto_pos
>= vc
->vc_cols
)
1983 goto_pos
= vc
->vc_cols
- 1;
1986 if (*goto_buf
< '0')
1988 else if (goto_pos
> 0)
1991 if (goto_pos
>= vc
->vc_rows
)
1992 goto_pos
= vc
->vc_rows
- 1;
1995 goto_buf
[num
= 0] = '\0';
1997 spk_special_handler
= NULL
;
2000 spk_pos
-= spk_x
* 2;
2002 spk_pos
+= goto_pos
* 2;
2006 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
2012 static void speakup_goto(struct vc_data
*vc
)
2014 if (spk_special_handler
) {
2015 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
2018 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
2019 spk_special_handler
= handle_goto
;
2022 static void speakup_help(struct vc_data
*vc
)
2024 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
2027 static void do_nothing(struct vc_data
*vc
)
2029 return; /* flush done in do_spkup */
2032 static u_char key_speakup
, spk_key_locked
;
2034 static void speakup_lock(struct vc_data
*vc
)
2036 if (!spk_key_locked
) {
2037 spk_key_locked
= 16;
2045 typedef void (*spkup_hand
) (struct vc_data
*);
2046 static spkup_hand spkup_handler
[] = {
2047 /* must be ordered same as defines in speakup.h */
2048 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2049 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2050 say_char
, say_prev_char
, say_next_char
,
2051 say_word
, say_prev_word
, say_next_word
,
2052 say_line
, say_prev_line
, say_next_line
,
2053 top_edge
, bottom_edge
, left_edge
, right_edge
,
2054 spell_word
, spell_word
, say_screen
,
2055 say_position
, say_attributes
,
2056 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2057 say_from_top
, say_to_bottom
,
2058 say_from_left
, say_to_right
,
2059 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2060 speakup_bits
, speakup_bits
, speakup_bits
,
2061 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2062 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2065 static void do_spkup(struct vc_data
*vc
, u_char value
)
2067 if (spk_killed
&& value
!= SPEECH_KILL
)
2071 spk_shut_up
&= 0xfe;
2072 this_speakup_key
= value
;
2073 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2075 (*spkup_handler
[value
]) (vc
);
2077 if (inc_dec_var(value
) < 0)
2082 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2085 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2088 unsigned long flags
;
2091 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2092 u_char shift_info
, offset
;
2098 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2102 if (type
== KT_PAD
&&
2103 (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2108 value
= spk_lastkey
= pad_chars
[value
];
2113 if (keycode
>= MAX_KEY
)
2115 key_info
= spk_our_keys
[keycode
];
2118 /* Check valid read all mode keys */
2119 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2133 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2134 offset
= spk_shift_table
[shift_info
];
2136 new_key
= key_info
[offset
];
2139 if (new_key
== SPK_KEY
) {
2140 if (!spk_key_locked
)
2141 key_speakup
= (up_flag
) ? 0 : 16;
2142 if (up_flag
|| spk_killed
)
2144 spk_shut_up
&= 0xfe;
2150 if (last_keycode
== keycode
&&
2151 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2152 spk_close_press
= 1;
2153 offset
= spk_shift_table
[shift_info
+ 32];
2155 if (offset
&& key_info
[offset
])
2156 new_key
= key_info
[offset
];
2158 last_keycode
= keycode
;
2159 last_spk_jiffy
= jiffies
;
2165 if (type
== KT_SPKUP
&& !spk_special_handler
) {
2166 do_spkup(vc
, new_key
);
2167 spk_close_press
= 0;
2171 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2173 spk_shut_up
&= 0xfe;
2174 kh
= (value
== KVAL(K_DOWN
)) ||
2175 (value
== KVAL(K_UP
)) ||
2176 (value
== KVAL(K_LEFT
)) ||
2177 (value
== KVAL(K_RIGHT
));
2178 if ((cursor_track
!= read_all_mode
) || !kh
)
2181 if (spk_special_handler
) {
2182 if (type
== KT_SPEC
&& value
== 1) {
2185 } else if (type
== KT_LETTER
) {
2187 } else if (value
== 0x7f) {
2188 value
= 8; /* make del = backspace */
2190 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2191 spk_close_press
= 0;
2198 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2202 static int keyboard_notifier_call(struct notifier_block
*nb
,
2203 unsigned long code
, void *_param
)
2205 struct keyboard_notifier_param
*param
= _param
;
2206 struct vc_data
*vc
= param
->vc
;
2207 int up
= !param
->down
;
2208 int ret
= NOTIFY_OK
;
2209 static int keycode
; /* to hold the current keycode */
2211 in_keyboard_notifier
= 1;
2213 if (vc
->vc_mode
== KD_GRAPHICS
)
2217 * First, determine whether we are handling a fake keypress on
2218 * the current processor. If we are, then return NOTIFY_OK,
2219 * to pass the keystroke up the chain. This prevents us from
2220 * trying to take the Speakup lock while it is held by the
2221 * processor on which the simulated keystroke was generated.
2222 * Also, the simulated keystrokes should be ignored by Speakup.
2225 if (speakup_fake_key_pressed())
2230 /* speakup requires keycode and keysym currently */
2231 keycode
= param
->value
;
2233 case KBD_UNBOUND_KEYCODE
:
2240 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2242 else if (KTYP(param
->value
) == KT_CUR
)
2243 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2245 case KBD_POST_KEYSYM
:{
2246 unsigned char type
= KTYP(param
->value
) - 0xf0;
2247 unsigned char val
= KVAL(param
->value
);
2251 do_handle_shift(vc
, val
, up
);
2255 do_handle_latin(vc
, val
, up
);
2258 do_handle_cursor(vc
, val
, up
);
2261 do_handle_spec(vc
, val
, up
);
2268 in_keyboard_notifier
= 0;
2272 static int vt_notifier_call(struct notifier_block
*nb
,
2273 unsigned long code
, void *_param
)
2275 struct vt_notifier_param
*param
= _param
;
2276 struct vc_data
*vc
= param
->vc
;
2280 if (vc
->vc_mode
== KD_TEXT
)
2281 speakup_allocate(vc
, GFP_ATOMIC
);
2284 speakup_deallocate(vc
);
2287 if (param
->c
== '\b') {
2292 speakup_con_write(vc
, &d
, 1);
2296 speakup_con_update(vc
);
2302 /* called by: module_exit() */
2303 static void __exit
speakup_exit(void)
2307 unregister_keyboard_notifier(&keyboard_notifier_block
);
2308 unregister_vt_notifier(&vt_notifier_block
);
2309 speakup_unregister_devsynth();
2310 speakup_cancel_paste();
2311 del_timer_sync(&cursor_timer
);
2312 kthread_stop(speakup_task
);
2313 speakup_task
= NULL
;
2314 mutex_lock(&spk_mutex
);
2316 mutex_unlock(&spk_mutex
);
2317 spk_ttyio_unregister_ldisc();
2319 speakup_kobj_exit();
2321 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2322 kfree(speakup_console
[i
]);
2324 speakup_remove_virtual_keyboard();
2326 for (i
= 0; i
< MAXVARS
; i
++)
2327 speakup_unregister_var(i
);
2329 for (i
= 0; i
< 256; i
++) {
2330 if (spk_characters
[i
] != spk_default_chars
[i
])
2331 kfree(spk_characters
[i
]);
2334 spk_free_user_msgs();
2337 /* call by: module_init() */
2338 static int __init
speakup_init(void)
2342 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2345 /* These first few initializations cannot fail. */
2346 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2347 spk_reset_default_chars();
2348 spk_reset_default_chartab();
2349 spk_strlwr(synth_name
);
2350 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2351 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2352 speakup_register_var(var
);
2353 for (var
= synth_time_vars
;
2354 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2355 speakup_register_var(var
);
2356 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2357 spk_set_mask_bits(NULL
, i
, 2);
2359 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2361 /* From here on out, initializations can fail. */
2362 err
= speakup_add_virtual_keyboard();
2364 goto error_virtkeyboard
;
2366 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2368 err
= speakup_allocate(vc_cons
[i
].d
, GFP_KERNEL
);
2370 goto error_kobjects
;
2374 spk_shut_up
|= 0x01;
2376 err
= speakup_kobj_init();
2378 goto error_kobjects
;
2380 spk_ttyio_register_ldisc();
2381 synth_init(synth_name
);
2382 speakup_register_devsynth();
2384 * register_devsynth might fail, but this error is not fatal.
2385 * /dev/synth is an extra feature; the rest of Speakup
2386 * will work fine without it.
2389 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2391 goto error_kbdnotifier
;
2392 err
= register_vt_notifier(&vt_notifier_block
);
2394 goto error_vtnotifier
;
2396 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2398 if (IS_ERR(speakup_task
)) {
2399 err
= PTR_ERR(speakup_task
);
2403 set_user_nice(speakup_task
, 10);
2404 wake_up_process(speakup_task
);
2406 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2407 pr_info("synth name on entry is: %s\n", synth_name
);
2411 unregister_vt_notifier(&vt_notifier_block
);
2414 unregister_keyboard_notifier(&keyboard_notifier_block
);
2415 del_timer(&cursor_timer
);
2418 speakup_unregister_devsynth();
2419 mutex_lock(&spk_mutex
);
2421 mutex_unlock(&spk_mutex
);
2422 speakup_kobj_exit();
2425 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2426 kfree(speakup_console
[i
]);
2428 speakup_remove_virtual_keyboard();
2431 for (i
= 0; i
< MAXVARS
; i
++)
2432 speakup_unregister_var(i
);
2434 for (i
= 0; i
< 256; i
++) {
2435 if (spk_characters
[i
] != spk_default_chars
[i
])
2436 kfree(spk_characters
[i
]);
2439 spk_free_user_msgs();
2445 module_init(speakup_init
);
2446 module_exit(speakup_exit
);