1 // SPDX-License-Identifier: GPL-2.0+
3 * review functions for the speakup screen review package.
4 * originally written by: Kirk Reiser and Andy Berdan.
6 * extensively modified by David Borowski.
8 ** Copyright (C) 1998 Kirk Reiser.
9 * Copyright (C) 2003 David Borowski.
12 #include <linux/kernel.h>
14 #include <linux/tty.h>
15 #include <linux/mm.h> /* __get_free_page() and friends */
16 #include <linux/vt_kern.h>
17 #include <linux/ctype.h>
18 #include <linux/selection.h>
19 #include <linux/unistd.h>
20 #include <linux/jiffies.h>
21 #include <linux/kthread.h>
22 #include <linux/keyboard.h> /* for KT_SHIFT */
23 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
24 #include <linux/input.h>
25 #include <linux/kmod.h>
27 /* speakup_*_selection */
28 #include <linux/module.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <linux/types.h>
32 #include <linux/consolemap.h>
34 #include <linux/spinlock.h>
35 #include <linux/notifier.h>
37 #include <linux/uaccess.h> /* copy_from|to|user() and others */
42 #define MAX_DELAY msecs_to_jiffies(500)
43 #define MINECHOCHAR SPACE
45 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
46 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
47 MODULE_DESCRIPTION("Speakup console speech");
48 MODULE_LICENSE("GPL");
49 MODULE_VERSION(SPEAKUP_VERSION
);
52 module_param_named(synth
, synth_name
, charp
, 0444);
53 module_param_named(quiet
, spk_quiet_boot
, bool, 0444);
55 MODULE_PARM_DESC(synth
, "Synth to start if speakup is built in.");
56 MODULE_PARM_DESC(quiet
, "Do not announce when the synthesizer is found.");
58 special_func spk_special_handler
;
60 short spk_pitch_shift
, synth_flags
;
62 int spk_attrib_bleep
, spk_bleeps
, spk_bleep_time
= 10;
63 int spk_no_intr
, spk_spell_delay
;
64 int spk_key_echo
, spk_say_word_ctl
;
65 int spk_say_ctrl
, spk_bell_pos
;
67 int spk_punc_level
, spk_reading_punc
;
68 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0";
69 char spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
70 char spk_str_pause
[MAXVARLEN
+ 1] = "\0";
72 const struct st_bits_data spk_punc_info
[] = {
74 {"some", "/$%&@", SOME
},
75 {"most", "$%&#()=+*/@^<>|\\", MOST
},
76 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
77 {"delimiters", "", B_WDLM
},
78 {"repeats", "()", CH_RPT
},
79 {"extended numeric", "", B_EXNUM
},
80 {"symbols", "", B_SYM
},
84 static char mark_cut_flag
;
86 static u_char
*spk_shift_table
;
87 u_char
*spk_our_keys
[MAX_KEY
];
88 u_char spk_key_buf
[600];
89 const u_char spk_key_defaults
[] = {
90 #include "speakupmap.h"
93 /* cursor track modes, must be ordered same as cursor_msgs in enum msg_index_t */
100 read_all_mode
= CT_Max
,
103 /* Speakup Cursor Track Variables */
104 static enum cursor_track cursor_track
= 1, prev_cursor_track
= 1;
106 static struct tty_struct
*tty
;
108 static void spkup_write(const u16
*in_buf
, int count
);
110 static char *phonetic
[] = {
111 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
112 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
114 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
115 "x ray", "yankee", "zulu"
118 /* array of 256 char pointers (one for each character description)
119 * initialized to default_chars and user selectable via
120 * /proc/speakup/characters
122 char *spk_characters
[256];
124 char *spk_default_chars
[256] = {
125 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
126 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
127 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
128 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
130 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
132 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
135 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
137 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
138 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
139 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
140 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
141 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
144 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
145 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
146 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
147 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
148 /*127*/ "del", "control", "control", "control", "control", "control",
149 "control", "control", "control", "control", "control",
150 /*138*/ "control", "control", "control", "control", "control",
151 "control", "control", "control", "control", "control",
152 "control", "control",
153 /*150*/ "control", "control", "control", "control", "control",
154 "control", "control", "control", "control", "control",
155 /*160*/ "nbsp", "inverted bang",
156 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
157 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
158 /*172*/ "not", "soft hyphen", "registered", "macron",
159 /*176*/ "degrees", "plus or minus", "super two", "super three",
160 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
161 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
162 /*188*/ "one quarter", "one half", "three quarters",
164 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
166 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
168 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
170 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
171 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
173 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
174 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
175 /*230*/ "ae", "c cidella", "e grave", "e acute",
176 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
178 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
180 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
182 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
185 /* array of 256 u_short (one for each character)
186 * initialized to default_chartab and user selectable via
187 * /sys/module/speakup/parameters/chartab
189 u_short spk_chartab
[256];
191 static u_short default_chartab
[256] = {
192 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
193 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
194 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
195 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
196 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
197 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
198 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
199 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
200 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
201 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
202 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
203 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
204 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
205 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
206 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
207 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
208 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
210 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
212 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
214 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
216 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
218 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
219 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
220 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
221 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
222 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
223 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
224 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
225 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
226 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
227 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
228 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
231 struct task_struct
*speakup_task
;
232 struct bleep spk_unprocessed_sound
;
233 static int spk_keydown
;
234 static u16 spk_lastkey
;
235 static u_char spk_close_press
, keymap_flags
;
236 static u_char last_keycode
, this_speakup_key
;
237 static u_long last_spk_jiffy
;
239 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
241 DEFINE_MUTEX(spk_mutex
);
243 static int keyboard_notifier_call(struct notifier_block
*,
244 unsigned long code
, void *param
);
246 static struct notifier_block keyboard_notifier_block
= {
247 .notifier_call
= keyboard_notifier_call
,
250 static int vt_notifier_call(struct notifier_block
*,
251 unsigned long code
, void *param
);
253 static struct notifier_block vt_notifier_block
= {
254 .notifier_call
= vt_notifier_call
,
257 static unsigned char get_attributes(struct vc_data
*vc
, u16
*pos
)
259 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, true);
260 return (scr_readw(pos
) & ~vc
->vc_hi_font_mask
) >> 8;
263 static void speakup_date(struct vc_data
*vc
)
265 spk_x
= spk_cx
= vc
->state
.x
;
266 spk_y
= spk_cy
= vc
->state
.y
;
267 spk_pos
= spk_cp
= vc
->vc_pos
;
268 spk_old_attr
= spk_attr
;
269 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
272 static void bleep(u_short val
)
274 static const short vals
[] = {
275 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
278 int time
= spk_bleep_time
;
280 freq
= vals
[val
% 12];
282 freq
*= (1 << (val
/ 12));
283 spk_unprocessed_sound
.freq
= freq
;
284 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
285 spk_unprocessed_sound
.active
= 1;
286 /* We can only have 1 active sound at a time. */
289 static void speakup_shut_up(struct vc_data
*vc
)
300 static void speech_kill(struct vc_data
*vc
)
302 char val
= synth
->is_alive(synth
);
307 /* re-enables synth, if disabled */
308 if (val
== 2 || spk_killed
) {
310 spk_shut_up
&= ~0x40;
311 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
313 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
318 static void speakup_off(struct vc_data
*vc
)
320 if (spk_shut_up
& 0x80) {
322 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
325 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
330 static void speakup_parked(struct vc_data
*vc
)
332 if (spk_parked
& 0x80) {
334 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
337 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
341 static void speakup_cut(struct vc_data
*vc
)
343 static const char err_buf
[] = "set selection failed";
346 if (!mark_cut_flag
) {
348 spk_xs
= (u_short
)spk_x
;
349 spk_ys
= (u_short
)spk_y
;
351 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
354 spk_xe
= (u_short
)spk_x
;
355 spk_ye
= (u_short
)spk_y
;
357 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
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
));
406 /* must be ordered same as edge_msgs in enum msg_index_t */
416 static void announce_edge(struct vc_data
*vc
, enum edge msg_id
)
420 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
422 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
425 static void speak_char(u16 ch
)
428 struct var_t
*direct
= spk_get_var(DIRECT
);
430 if (ch
>= 0x100 || (direct
&& direct
->u
.n
.value
)) {
431 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
)) {
433 synth_printf("%s", spk_str_caps_start
);
436 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
))
437 synth_printf("%s", spk_str_caps_stop
);
441 cp
= spk_characters
[ch
];
443 pr_info("%s: cp == NULL!\n", __func__
);
446 if (IS_CHAR(ch
, B_CAP
)) {
448 synth_printf("%s %s %s",
449 spk_str_caps_start
, cp
, spk_str_caps_stop
);
453 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL
), cp
);
455 synth_printf(" %s ", cp
);
460 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
468 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, true);
472 if (w
& vc
->vc_hi_font_mask
) {
473 w
&= ~vc
->vc_hi_font_mask
;
477 ch
= inverse_translate(vc
, c
, 1);
478 *attribs
= (w
& 0xff00) >> 8;
483 static void say_char(struct vc_data
*vc
)
487 spk_old_attr
= spk_attr
;
488 ch
= get_char(vc
, (u_short
*)spk_pos
, &spk_attr
);
489 if (spk_attr
!= spk_old_attr
) {
490 if (spk_attrib_bleep
& 1)
492 if (spk_attrib_bleep
& 2)
498 static void say_phonetic_char(struct vc_data
*vc
)
502 spk_old_attr
= spk_attr
;
503 ch
= get_char(vc
, (u_short
*)spk_pos
, &spk_attr
);
504 if (ch
<= 0x7f && isalpha(ch
)) {
506 synth_printf("%s\n", phonetic
[--ch
]);
508 if (ch
< 0x100 && IS_CHAR(ch
, B_NUM
))
509 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
514 static void say_prev_char(struct vc_data
*vc
)
518 announce_edge(vc
, edge_left
);
526 static void say_next_char(struct vc_data
*vc
)
529 if (spk_x
== vc
->vc_cols
- 1) {
530 announce_edge(vc
, edge_right
);
538 /* get_word - will first check to see if the character under the
539 * reading cursor is a space and if spk_say_word_ctl is true it will
540 * return the word space. If spk_say_word_ctl is not set it will check to
541 * see if there is a word starting on the next position to the right
542 * and return that word if it exists. If it does not exist it will
543 * move left to the beginning of any previous word on the line or the
544 * beginning off the line whichever comes first..
547 static u_long
get_word(struct vc_data
*vc
)
549 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
554 spk_old_attr
= spk_attr
;
555 ch
= get_char(vc
, (u_short
*)tmp_pos
, &temp
);
557 /* decided to take out the sayword if on a space (mis-information */
558 if (spk_say_word_ctl
&& ch
== SPACE
) {
560 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
562 } else if (tmpx
< vc
->vc_cols
- 2 &&
563 (ch
== SPACE
|| ch
== 0 || (ch
< 0x100 && IS_WDLM(ch
))) &&
564 get_char(vc
, (u_short
*)tmp_pos
+ 1, &temp
) > SPACE
) {
569 ch
= get_char(vc
, (u_short
*)tmp_pos
- 1, &temp
);
570 if ((ch
== SPACE
|| ch
== 0 ||
571 (ch
< 0x100 && IS_WDLM(ch
))) &&
572 get_char(vc
, (u_short
*)tmp_pos
, &temp
) > SPACE
)
578 attr_ch
= get_char(vc
, (u_short
*)tmp_pos
, &spk_attr
);
579 buf
[cnt
++] = attr_ch
;
580 while (tmpx
< vc
->vc_cols
- 1) {
583 ch
= get_char(vc
, (u_short
*)tmp_pos
, &temp
);
584 if (ch
== SPACE
|| ch
== 0 ||
585 (buf
[cnt
- 1] < 0x100 && IS_WDLM(buf
[cnt
- 1]) &&
594 static void say_word(struct vc_data
*vc
)
596 u_long cnt
= get_word(vc
);
597 u_short saved_punc_mask
= spk_punc_mask
;
601 spk_punc_mask
= PUNC
;
603 spkup_write(buf
, cnt
);
604 spk_punc_mask
= saved_punc_mask
;
607 static void say_prev_word(struct vc_data
*vc
)
611 enum edge edge_said
= edge_none
;
612 u_short last_state
= 0, state
= 0;
618 announce_edge(vc
, edge_top
);
623 edge_said
= edge_quiet
;
628 edge_said
= edge_top
;
631 if (edge_said
!= edge_quiet
)
632 edge_said
= edge_left
;
636 spk_x
= vc
->vc_cols
- 1;
641 ch
= get_char(vc
, (u_short
*)spk_pos
, &temp
);
642 if (ch
== SPACE
|| ch
== 0)
644 else if (ch
< 0x100 && IS_WDLM(ch
))
648 if (state
< last_state
) {
655 if (spk_x
== 0 && edge_said
== edge_quiet
)
656 edge_said
= edge_left
;
657 if (edge_said
> edge_none
&& edge_said
< edge_quiet
)
658 announce_edge(vc
, edge_said
);
662 static void say_next_word(struct vc_data
*vc
)
666 enum edge edge_said
= edge_none
;
667 u_short last_state
= 2, state
= 0;
670 if (spk_x
== vc
->vc_cols
- 1 && spk_y
== vc
->vc_rows
- 1) {
671 announce_edge(vc
, edge_bottom
);
675 ch
= get_char(vc
, (u_short
*)spk_pos
, &temp
);
676 if (ch
== SPACE
|| ch
== 0)
678 else if (ch
< 0x100 && IS_WDLM(ch
))
682 if (state
> last_state
)
684 if (spk_x
>= vc
->vc_cols
- 1) {
685 if (spk_y
== vc
->vc_rows
- 1) {
686 edge_said
= edge_bottom
;
692 edge_said
= edge_right
;
699 if (edge_said
> edge_none
)
700 announce_edge(vc
, edge_said
);
704 static void spell_word(struct vc_data
*vc
)
706 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
709 char *str_cap
= spk_str_caps_stop
;
710 char *last_cap
= spk_str_caps_stop
;
711 struct var_t
*direct
= spk_get_var(DIRECT
);
718 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
719 /* FIXME: Non-latin1 considered as lower case */
720 if (ch
< 0x100 && IS_CHAR(ch
, B_CAP
)) {
721 str_cap
= spk_str_caps_start
;
722 if (*spk_str_caps_stop
)
724 else /* synth has no pitch */
725 last_cap
= spk_str_caps_stop
;
727 str_cap
= spk_str_caps_stop
;
729 if (str_cap
!= last_cap
) {
730 synth_printf("%s", str_cap
);
733 if (ch
>= 0x100 || (direct
&& direct
->u
.n
.value
)) {
735 } else if (this_speakup_key
== SPELL_PHONETIC
&&
736 ch
<= 0x7f && isalpha(ch
)) {
738 cp1
= phonetic
[--ch
];
739 synth_printf("%s", cp1
);
741 cp1
= spk_characters
[ch
];
743 synth_printf("%s", spk_msg_get(MSG_CTRL
));
746 synth_printf("%s", cp1
);
750 if (str_cap
!= spk_str_caps_stop
)
751 synth_printf("%s", spk_str_caps_stop
);
754 static int get_line(struct vc_data
*vc
)
756 u_long tmp
= spk_pos
- (spk_x
* 2);
760 spk_old_attr
= spk_attr
;
761 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
762 for (i
= 0; i
< vc
->vc_cols
; i
++) {
763 buf
[i
] = get_char(vc
, (u_short
*)tmp
, &tmp2
);
766 for (--i
; i
>= 0; i
--)
772 static void say_line(struct vc_data
*vc
)
774 int i
= get_line(vc
);
776 u_short saved_punc_mask
= spk_punc_mask
;
779 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
783 if (this_speakup_key
== SAY_LINE_INDENT
) {
787 synth_printf("%zd, ", (cp
- buf
) + 1);
789 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
791 spk_punc_mask
= saved_punc_mask
;
794 static void say_prev_line(struct vc_data
*vc
)
798 announce_edge(vc
, edge_top
);
802 spk_pos
-= vc
->vc_size_row
;
806 static void say_next_line(struct vc_data
*vc
)
809 if (spk_y
== vc
->vc_rows
- 1) {
810 announce_edge(vc
, edge_bottom
);
814 spk_pos
+= vc
->vc_size_row
;
818 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
823 u_short saved_punc_mask
= spk_punc_mask
;
825 spk_old_attr
= spk_attr
;
826 spk_attr
= get_attributes(vc
, (u_short
*)from
);
828 buf
[i
++] = get_char(vc
, (u_short
*)from
, &tmp
);
830 if (i
>= vc
->vc_size_row
)
833 for (--i
; i
>= 0; i
--)
841 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
844 spk_punc_mask
= saved_punc_mask
;
848 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
851 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
852 u_long end
= start
+ (to
* 2);
855 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
856 if (cursor_track
!= read_all_mode
)
857 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
860 /* Sentence Reading Commands */
862 static int currsentence
;
863 static int numsentences
[2];
864 static u16
*sentbufend
[2];
865 static u16
*sentmarks
[2][10];
868 static u16 sentbuf
[2][256];
870 static int say_sentence_num(int num
, int prev
)
873 currsentence
= num
+ 1;
874 if (prev
&& --bn
== -1)
877 if (num
> numsentences
[bn
])
880 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
884 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
894 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
895 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
897 numsentences
[bn
] = 0;
898 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
900 spk_old_attr
= spk_attr
;
901 spk_attr
= get_attributes(vc
, (u_short
*)start
);
903 while (start
< end
) {
904 sentbuf
[bn
][i
] = get_char(vc
, (u_short
*)start
, &tmp
);
906 if (sentbuf
[bn
][i
] == SPACE
&&
907 sentbuf
[bn
][i
- 1] == '.' &&
908 numsentences
[bn
] < 9) {
909 /* Sentence Marker */
911 sentmarks
[bn
][numsentences
[bn
]] =
917 if (i
>= vc
->vc_size_row
)
921 for (--i
; i
>= 0; i
--)
922 if (sentbuf
[bn
][i
] != SPACE
)
928 sentbuf
[bn
][++i
] = SPACE
;
929 sentbuf
[bn
][++i
] = '\0';
931 sentbufend
[bn
] = &sentbuf
[bn
][i
];
932 return numsentences
[bn
];
935 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
937 u_long start
= vc
->vc_origin
, end
;
940 start
+= from
* vc
->vc_size_row
;
941 if (to
> vc
->vc_rows
)
943 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
944 for (from
= start
; from
< end
; from
= to
) {
945 to
= from
+ vc
->vc_size_row
;
946 say_from_to(vc
, from
, to
, 1);
950 static void say_screen(struct vc_data
*vc
)
952 say_screen_from_to(vc
, 0, vc
->vc_rows
);
955 static void speakup_win_say(struct vc_data
*vc
)
957 u_long start
, end
, from
, to
;
960 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
963 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
964 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
965 while (start
<= end
) {
966 from
= start
+ (win_left
* 2);
967 to
= start
+ (win_right
* 2);
968 say_from_to(vc
, from
, to
, 1);
969 start
+= vc
->vc_size_row
;
973 static void top_edge(struct vc_data
*vc
)
976 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
981 static void bottom_edge(struct vc_data
*vc
)
984 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
985 spk_y
= vc
->vc_rows
- 1;
989 static void left_edge(struct vc_data
*vc
)
992 spk_pos
-= spk_x
* 2;
997 static void right_edge(struct vc_data
*vc
)
1000 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
1001 spk_x
= vc
->vc_cols
- 1;
1005 static void say_first_char(struct vc_data
*vc
)
1007 int i
, len
= get_line(vc
);
1012 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1015 for (i
= 0; i
< len
; i
++)
1016 if (buf
[i
] != SPACE
)
1019 spk_pos
-= (spk_x
- i
) * 2;
1021 synth_printf("%d, ", ++i
);
1025 static void say_last_char(struct vc_data
*vc
)
1027 int len
= get_line(vc
);
1032 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1036 spk_pos
-= (spk_x
- len
) * 2;
1038 synth_printf("%d, ", ++len
);
1042 static void say_position(struct vc_data
*vc
)
1044 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1049 /* Added by brianb */
1050 static void say_char_num(struct vc_data
*vc
)
1053 u16 ch
= get_char(vc
, (u_short
*)spk_pos
, &tmp
);
1055 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1058 /* these are stub functions to keep keyboard.c happy. */
1060 static void say_from_top(struct vc_data
*vc
)
1062 say_screen_from_to(vc
, 0, spk_y
);
1065 static void say_to_bottom(struct vc_data
*vc
)
1067 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1070 static void say_from_left(struct vc_data
*vc
)
1072 say_line_from_to(vc
, 0, spk_x
, 1);
1075 static void say_to_right(struct vc_data
*vc
)
1077 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1080 /* end of stub functions. */
1082 static void spkup_write(const u16
*in_buf
, int count
)
1084 static int rep_count
;
1085 static u16 ch
= '\0', old_ch
= '\0';
1086 static u_short char_type
, last_type
;
1087 int in_count
= count
;
1091 if (cursor_track
== read_all_mode
) {
1092 /* Insert Sentence Index */
1093 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1094 (currsentence
<= numsentences
[bn
]))
1095 synth_insert_next_index(currsentence
++);
1099 char_type
= spk_chartab
[ch
];
1102 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1103 if (++rep_count
> 2)
1106 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1108 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1114 if (ch
== spk_lastkey
) {
1116 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1118 } else if (char_type
& B_ALPHA
) {
1119 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1120 synth_buffer_add(SPACE
);
1122 } else if (char_type
& B_NUM
) {
1125 } else if (char_type
& spk_punc_mask
) {
1127 char_type
&= ~PUNC
; /* for dec nospell processing */
1128 } else if (char_type
& SYNTH_OK
) {
1129 /* these are usually puncts like . and , which synth
1130 * needs for expression.
1131 * suppress multiple to get rid of long pauses and
1132 * clear repeat count
1134 * repeats on you don't get nothing repeated count
1141 /* send space and record position, if next is num overwrite space */
1143 synth_buffer_add(SPACE
);
1148 last_type
= char_type
;
1151 if (in_count
> 2 && rep_count
> 2) {
1152 if (last_type
& CH_RPT
) {
1154 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1162 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1164 static void read_all_doc(struct vc_data
*vc
);
1165 static void cursor_done(struct timer_list
*unused
);
1166 static DEFINE_TIMER(cursor_timer
, cursor_done
);
1168 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1170 unsigned long flags
;
1172 if (!synth
|| up_flag
|| spk_killed
)
1174 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1175 if (cursor_track
== read_all_mode
) {
1178 del_timer(&cursor_timer
);
1179 spk_shut_up
&= 0xfe;
1184 del_timer(&cursor_timer
);
1185 cursor_track
= prev_cursor_track
;
1186 spk_shut_up
&= 0xfe;
1191 spk_shut_up
&= 0xfe;
1194 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1195 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1196 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1199 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1201 unsigned long flags
;
1203 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1207 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1210 if (!synth
|| spk_killed
) {
1211 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1214 spk_shut_up
&= 0xfe;
1215 spk_lastkey
= value
;
1218 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1220 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1223 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1225 int i
= 0, states
, key_data_len
;
1226 const u_char
*cp
= key_info
;
1227 u_char
*cp1
= k_buffer
;
1228 u_char ch
, version
, num_keys
;
1231 if (version
!= KEY_MAP_VER
) {
1232 pr_debug("version found %d should be %d\n",
1233 version
, KEY_MAP_VER
);
1237 states
= (int)cp
[1];
1238 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1239 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
)) {
1240 pr_debug("too many key_infos (%d over %u)\n",
1241 key_data_len
+ SHIFT_TBL_SIZE
+ 4,
1242 (unsigned int)(sizeof(spk_key_buf
)));
1245 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1246 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1247 spk_shift_table
= k_buffer
;
1248 spk_our_keys
[0] = spk_shift_table
;
1249 cp1
+= SHIFT_TBL_SIZE
;
1250 memcpy(cp1
, cp
, key_data_len
+ 3);
1251 /* get num_keys, states and data */
1252 cp1
+= 2; /* now pointing at shift states */
1253 for (i
= 1; i
<= states
; i
++) {
1255 if (ch
>= SHIFT_TBL_SIZE
) {
1256 pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
1257 ch
, SHIFT_TBL_SIZE
);
1260 spk_shift_table
[ch
] = i
;
1262 keymap_flags
= *cp1
++;
1263 while ((ch
= *cp1
)) {
1264 if (ch
>= MAX_KEY
) {
1265 pr_debug("(%d), not valid key, (max_allowed = %d)\n",
1269 spk_our_keys
[ch
] = cp1
;
1275 static struct var_t spk_vars
[] = {
1276 /* bell must be first to set high limit */
1277 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1278 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1279 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1280 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1281 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1282 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1283 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1284 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1285 {SAY_CONTROL
, TOGGLE_0
},
1286 {SAY_WORD_CTL
, TOGGLE_0
},
1287 {NO_INTERRUPT
, TOGGLE_0
},
1288 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1292 static void toggle_cursoring(struct vc_data
*vc
)
1294 if (cursor_track
== read_all_mode
)
1295 cursor_track
= prev_cursor_track
;
1296 if (++cursor_track
>= CT_Max
)
1298 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1301 void spk_reset_default_chars(void)
1305 /* First, free any non-default */
1306 for (i
= 0; i
< 256; i
++) {
1307 if (spk_characters
[i
] &&
1308 (spk_characters
[i
] != spk_default_chars
[i
]))
1309 kfree(spk_characters
[i
]);
1312 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1315 void spk_reset_default_chartab(void)
1317 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1320 static const struct st_bits_data
*pb_edit
;
1322 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1324 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1326 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1329 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1330 spk_special_handler
= NULL
;
1333 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1335 spk_chartab
[ch
] ^= mask
;
1337 synth_printf(" %s\n",
1338 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1339 spk_msg_get(MSG_OFF
));
1343 /* Allocation concurrency is protected by the console semaphore */
1344 static int speakup_allocate(struct vc_data
*vc
, gfp_t gfp_flags
)
1348 vc_num
= vc
->vc_num
;
1349 if (!speakup_console
[vc_num
]) {
1350 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1352 if (!speakup_console
[vc_num
])
1355 } else if (!spk_parked
) {
1362 static void speakup_deallocate(struct vc_data
*vc
)
1366 vc_num
= vc
->vc_num
;
1367 kfree(speakup_console
[vc_num
]);
1368 speakup_console
[vc_num
] = NULL
;
1371 enum read_all_command
{
1372 RA_NEXT_SENT
= KVAL(K_DOWN
)+1,
1373 RA_PREV_LINE
= KVAL(K_LEFT
)+1,
1374 RA_NEXT_LINE
= KVAL(K_RIGHT
)+1,
1375 RA_PREV_SENT
= KVAL(K_UP
)+1,
1382 static u_char is_cursor
;
1383 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1384 static int cursor_con
;
1386 static void reset_highlight_buffers(struct vc_data
*);
1388 static enum read_all_command read_all_key
;
1390 static int in_keyboard_notifier
;
1392 static void start_read_all_timer(struct vc_data
*vc
, enum read_all_command command
);
1394 static void kbd_fakekey2(struct vc_data
*vc
, enum read_all_command command
)
1396 del_timer(&cursor_timer
);
1397 speakup_fake_down_arrow();
1398 start_read_all_timer(vc
, command
);
1401 static void read_all_doc(struct vc_data
*vc
)
1403 if ((vc
->vc_num
!= fg_console
) || !synth
|| spk_shut_up
)
1405 if (!synth_supports_indexing())
1407 if (cursor_track
!= read_all_mode
)
1408 prev_cursor_track
= cursor_track
;
1409 cursor_track
= read_all_mode
;
1410 spk_reset_index_count(0);
1411 if (get_sentence_buf(vc
, 0) == -1) {
1412 del_timer(&cursor_timer
);
1413 if (!in_keyboard_notifier
)
1414 speakup_fake_down_arrow();
1415 start_read_all_timer(vc
, RA_DOWN_ARROW
);
1417 say_sentence_num(0, 0);
1418 synth_insert_next_index(0);
1419 start_read_all_timer(vc
, RA_TIMER
);
1423 static void stop_read_all(struct vc_data
*vc
)
1425 del_timer(&cursor_timer
);
1426 cursor_track
= prev_cursor_track
;
1427 spk_shut_up
&= 0xfe;
1431 static void start_read_all_timer(struct vc_data
*vc
, enum read_all_command command
)
1433 struct var_t
*cursor_timeout
;
1435 cursor_con
= vc
->vc_num
;
1436 read_all_key
= command
;
1437 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1438 mod_timer(&cursor_timer
,
1439 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1442 static void handle_cursor_read_all(struct vc_data
*vc
, enum read_all_command command
)
1444 int indcount
, sentcount
, rv
, sn
;
1448 /* Get Current Sentence */
1449 spk_get_index_count(&indcount
, &sentcount
);
1450 /*printk("%d %d ", indcount, sentcount); */
1451 spk_reset_index_count(sentcount
+ 1);
1452 if (indcount
== 1) {
1453 if (!say_sentence_num(sentcount
+ 1, 0)) {
1454 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1457 synth_insert_next_index(0);
1460 if (!say_sentence_num(sentcount
+ 1, 1)) {
1462 spk_reset_index_count(sn
);
1464 synth_insert_next_index(0);
1466 if (!say_sentence_num(sn
, 0)) {
1467 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1470 synth_insert_next_index(0);
1472 start_read_all_timer(vc
, RA_TIMER
);
1482 if (get_sentence_buf(vc
, 0) == -1) {
1483 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1485 say_sentence_num(0, 0);
1486 synth_insert_next_index(0);
1487 start_read_all_timer(vc
, RA_TIMER
);
1490 case RA_FIND_NEXT_SENT
:
1491 rv
= get_sentence_buf(vc
, 0);
1495 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1497 say_sentence_num(1, 0);
1498 synth_insert_next_index(0);
1499 start_read_all_timer(vc
, RA_TIMER
);
1502 case RA_FIND_PREV_SENT
:
1505 spk_get_index_count(&indcount
, &sentcount
);
1507 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1509 start_read_all_timer(vc
, RA_TIMER
);
1514 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1516 unsigned long flags
;
1518 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1519 if (cursor_track
== read_all_mode
) {
1521 if (!synth
|| up_flag
|| spk_shut_up
) {
1522 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1525 del_timer(&cursor_timer
);
1526 spk_shut_up
&= 0xfe;
1528 start_read_all_timer(vc
, value
+ 1);
1529 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1532 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1536 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1538 unsigned long flags
;
1539 struct var_t
*cursor_timeout
;
1541 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1543 if (!synth
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1544 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1547 spk_shut_up
&= 0xfe;
1550 /* the key press flushes if !no_inter but we want to flush on cursor
1551 * moves regardless of no_inter state
1553 is_cursor
= value
+ 1;
1554 old_cursor_pos
= vc
->vc_pos
;
1555 old_cursor_x
= vc
->state
.x
;
1556 old_cursor_y
= vc
->state
.y
;
1557 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->state
.y
;
1558 cursor_con
= vc
->vc_num
;
1559 if (cursor_track
== CT_Highlight
)
1560 reset_highlight_buffers(vc
);
1561 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1562 mod_timer(&cursor_timer
,
1563 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1564 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1567 static void update_color_buffer(struct vc_data
*vc
, const u16
*ic
, int len
)
1570 int vc_num
= vc
->vc_num
;
1572 bi
= (vc
->vc_attr
& 0x70) >> 4;
1573 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1576 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1577 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1578 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->state
.x
;
1579 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->state
.y
;
1581 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1583 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1585 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1586 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1588 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1595 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1598 static void reset_highlight_buffers(struct vc_data
*vc
)
1601 int vc_num
= vc
->vc_num
;
1603 for (i
= 0; i
< 8; i
++)
1604 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1607 static int count_highlight_color(struct vc_data
*vc
)
1611 int vc_num
= vc
->vc_num
;
1613 u16
*start
= (u16
*)vc
->vc_origin
;
1615 for (i
= 0; i
< 8; i
++)
1616 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1618 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1619 u16
*end
= start
+ vc
->vc_cols
* 2;
1622 for (ptr
= start
; ptr
< end
; ptr
++) {
1623 ch
= get_attributes(vc
, ptr
);
1624 bg
= (ch
& 0x70) >> 4;
1625 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1627 start
+= vc
->vc_size_row
;
1631 for (i
= 0; i
< 8; i
++)
1632 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1637 static int get_highlight_color(struct vc_data
*vc
)
1640 unsigned int cptr
[8];
1641 int vc_num
= vc
->vc_num
;
1643 for (i
= 0; i
< 8; i
++)
1646 for (i
= 0; i
< 7; i
++)
1647 for (j
= i
+ 1; j
< 8; j
++)
1648 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1649 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]])
1650 swap(cptr
[i
], cptr
[j
]);
1652 for (i
= 0; i
< 8; i
++)
1653 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1654 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1659 static int speak_highlight(struct vc_data
*vc
)
1662 int vc_num
= vc
->vc_num
;
1664 if (count_highlight_color(vc
) == 1)
1666 hc
= get_highlight_color(vc
);
1668 d
= vc
->state
.y
- speakup_console
[vc_num
]->ht
.cy
;
1669 if ((d
== 1) || (d
== -1))
1670 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->state
.y
)
1674 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1675 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1676 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1677 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1678 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1684 static void cursor_done(struct timer_list
*unused
)
1686 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1687 unsigned long flags
;
1689 del_timer(&cursor_timer
);
1690 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1691 if (cursor_con
!= fg_console
) {
1697 if (vc
->state
.x
>= win_left
&& vc
->state
.x
<= win_right
&&
1698 vc
->state
.y
>= win_top
&& vc
->state
.y
<= win_bottom
) {
1704 if (cursor_track
== read_all_mode
) {
1705 handle_cursor_read_all(vc
, read_all_key
);
1708 if (cursor_track
== CT_Highlight
) {
1709 if (speak_highlight(vc
)) {
1715 if (cursor_track
== CT_Window
)
1716 speakup_win_say(vc
);
1717 else if (is_cursor
== 1 || is_cursor
== 4)
1718 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1724 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1727 /* called by: vt_notifier_call() */
1728 static void speakup_bs(struct vc_data
*vc
)
1730 unsigned long flags
;
1732 if (!speakup_console
[vc
->vc_num
])
1734 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1735 /* Speakup output, discard */
1739 if (spk_shut_up
|| !synth
) {
1740 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1743 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1748 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1751 /* called by: vt_notifier_call() */
1752 static void speakup_con_write(struct vc_data
*vc
, u16
*str
, int len
)
1754 unsigned long flags
;
1756 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| !synth
)
1758 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1759 /* Speakup output, discard */
1761 if (spk_bell_pos
&& spk_keydown
&& (vc
->state
.x
== spk_bell_pos
- 1))
1763 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1764 if (cursor_track
== CT_Highlight
)
1765 update_color_buffer(vc
, str
, len
);
1766 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1770 if (vc
->state
.x
>= win_left
&& vc
->state
.x
<= win_right
&&
1771 vc
->state
.y
>= win_top
&& vc
->state
.y
<= win_bottom
) {
1772 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1777 spkup_write(str
, len
);
1778 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1781 static void speakup_con_update(struct vc_data
*vc
)
1783 unsigned long flags
;
1785 if (!speakup_console
[vc
->vc_num
] || spk_parked
)
1787 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1788 /* Speakup output, discard */
1791 if (vc
->vc_mode
== KD_GRAPHICS
&& !spk_paused
&& spk_str_pause
[0]) {
1792 synth_printf("%s", spk_str_pause
);
1795 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1798 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1800 unsigned long flags
;
1804 if (!synth
|| up_flag
|| spk_killed
)
1806 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1807 spk_shut_up
&= 0xfe;
1812 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1813 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1816 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1817 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1820 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1821 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1822 if (speakup_console
[vc
->vc_num
])
1823 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1827 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1831 synth_printf("%s %s\n",
1832 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1833 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1836 static int inc_dec_var(u_char value
)
1838 struct st_var_header
*p_header
;
1839 struct var_t
*var_data
;
1843 int var_id
= (int)value
- VAR_START
;
1844 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1846 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1847 p_header
= spk_get_var_header(var_id
);
1850 if (p_header
->var_type
!= VAR_NUM
)
1852 var_data
= p_header
->data
;
1853 if (spk_set_num_var(1, p_header
, how
) != 0)
1855 if (!spk_close_press
) {
1856 for (pn
= p_header
->name
; *pn
; pn
++) {
1863 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1864 var_data
->u
.n
.value
);
1865 synth_printf("%s", num_buf
);
1869 static void speakup_win_set(struct vc_data
*vc
)
1873 if (win_start
> 1) {
1874 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1877 if (spk_x
< win_left
|| spk_y
< win_top
) {
1878 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1881 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1883 win_right
= vc
->vc_cols
- 1;
1885 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1895 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1897 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1898 (int)spk_y
+ 1, (int)spk_x
+ 1);
1900 synth_printf("%s\n", info
);
1904 static void speakup_win_clear(struct vc_data
*vc
)
1911 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1914 static void speakup_win_enable(struct vc_data
*vc
)
1916 if (win_start
< 2) {
1917 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1922 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1924 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1927 static void speakup_bits(struct vc_data
*vc
)
1929 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1931 if (spk_special_handler
|| val
< 1 || val
> 6) {
1932 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1935 pb_edit
= &spk_punc_info
[val
];
1936 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1937 spk_special_handler
= edit_bits
;
1940 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1942 static u_char goto_buf
[8];
1948 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1950 if (type
== KT_LATIN
&& ch
== '\n')
1959 wch
= goto_buf
[--num
];
1960 goto_buf
[num
] = '\0';
1961 spkup_write(&wch
, 1);
1964 if (ch
< '+' || ch
> 'y')
1967 goto_buf
[num
++] = ch
;
1968 goto_buf
[num
] = '\0';
1969 spkup_write(&wch
, 1);
1970 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1971 if ((ch
== '+' || ch
== '-') && num
== 1)
1973 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1975 if (num
< maxlen
- 1 || num
> maxlen
)
1977 if (ch
< 'x' || ch
> 'y') {
1980 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1981 goto_buf
[num
= 0] = '\0';
1982 spk_special_handler
= NULL
;
1986 /* Do not replace with kstrtoul: here we need cp to be updated */
1987 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1990 if (*goto_buf
< '0')
1992 else if (goto_pos
> 0)
1995 if (goto_pos
>= vc
->vc_cols
)
1996 goto_pos
= vc
->vc_cols
- 1;
1999 if (*goto_buf
< '0')
2001 else if (goto_pos
> 0)
2004 if (goto_pos
>= vc
->vc_rows
)
2005 goto_pos
= vc
->vc_rows
- 1;
2008 goto_buf
[num
= 0] = '\0';
2010 spk_special_handler
= NULL
;
2013 spk_pos
-= spk_x
* 2;
2015 spk_pos
+= goto_pos
* 2;
2019 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
2025 static void speakup_goto(struct vc_data
*vc
)
2027 if (spk_special_handler
) {
2028 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
2031 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
2032 spk_special_handler
= handle_goto
;
2035 static void speakup_help(struct vc_data
*vc
)
2037 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
2040 static void do_nothing(struct vc_data
*vc
)
2042 return; /* flush done in do_spkup */
2045 static u_char key_speakup
, spk_key_locked
;
2047 static void speakup_lock(struct vc_data
*vc
)
2049 if (!spk_key_locked
) {
2050 spk_key_locked
= 16;
2058 typedef void (*spkup_hand
) (struct vc_data
*);
2059 static spkup_hand spkup_handler
[] = {
2060 /* must be ordered same as defines in speakup.h */
2061 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2062 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2063 say_char
, say_prev_char
, say_next_char
,
2064 say_word
, say_prev_word
, say_next_word
,
2065 say_line
, say_prev_line
, say_next_line
,
2066 top_edge
, bottom_edge
, left_edge
, right_edge
,
2067 spell_word
, spell_word
, say_screen
,
2068 say_position
, say_attributes
,
2069 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2070 say_from_top
, say_to_bottom
,
2071 say_from_left
, say_to_right
,
2072 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2073 speakup_bits
, speakup_bits
, speakup_bits
,
2074 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2075 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2078 static void do_spkup(struct vc_data
*vc
, u_char value
)
2080 if (spk_killed
&& value
!= SPEECH_KILL
)
2084 spk_shut_up
&= 0xfe;
2085 this_speakup_key
= value
;
2086 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2088 (*spkup_handler
[value
]) (vc
);
2090 if (inc_dec_var(value
) < 0)
2095 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2098 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2101 unsigned long flags
;
2104 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2105 u_char shift_info
, offset
;
2111 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2115 if (type
== KT_PAD
&&
2116 (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2121 value
= pad_chars
[value
];
2122 spk_lastkey
= value
;
2127 if (keycode
>= MAX_KEY
)
2129 key_info
= spk_our_keys
[keycode
];
2132 /* Check valid read all mode keys */
2133 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2147 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2148 offset
= spk_shift_table
[shift_info
];
2150 new_key
= key_info
[offset
];
2153 if (new_key
== SPK_KEY
) {
2154 if (!spk_key_locked
)
2155 key_speakup
= (up_flag
) ? 0 : 16;
2156 if (up_flag
|| spk_killed
)
2158 spk_shut_up
&= 0xfe;
2164 if (last_keycode
== keycode
&&
2165 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2166 spk_close_press
= 1;
2167 offset
= spk_shift_table
[shift_info
+ 32];
2169 if (offset
&& key_info
[offset
])
2170 new_key
= key_info
[offset
];
2172 last_keycode
= keycode
;
2173 last_spk_jiffy
= jiffies
;
2179 if (type
== KT_SPKUP
&& !spk_special_handler
) {
2180 do_spkup(vc
, new_key
);
2181 spk_close_press
= 0;
2185 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2187 spk_shut_up
&= 0xfe;
2188 kh
= (value
== KVAL(K_DOWN
)) ||
2189 (value
== KVAL(K_UP
)) ||
2190 (value
== KVAL(K_LEFT
)) ||
2191 (value
== KVAL(K_RIGHT
));
2192 if ((cursor_track
!= read_all_mode
) || !kh
)
2195 if (spk_special_handler
) {
2196 if (type
== KT_SPEC
&& value
== 1) {
2199 } else if (type
== KT_LETTER
) {
2201 } else if (value
== 0x7f) {
2202 value
= 8; /* make del = backspace */
2204 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2205 spk_close_press
= 0;
2212 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2216 static int keyboard_notifier_call(struct notifier_block
*nb
,
2217 unsigned long code
, void *_param
)
2219 struct keyboard_notifier_param
*param
= _param
;
2220 struct vc_data
*vc
= param
->vc
;
2221 int up
= !param
->down
;
2222 int ret
= NOTIFY_OK
;
2223 static int keycode
; /* to hold the current keycode */
2225 in_keyboard_notifier
= 1;
2227 if (vc
->vc_mode
== KD_GRAPHICS
)
2231 * First, determine whether we are handling a fake keypress on
2232 * the current processor. If we are, then return NOTIFY_OK,
2233 * to pass the keystroke up the chain. This prevents us from
2234 * trying to take the Speakup lock while it is held by the
2235 * processor on which the simulated keystroke was generated.
2236 * Also, the simulated keystrokes should be ignored by Speakup.
2239 if (speakup_fake_key_pressed())
2244 /* speakup requires keycode and keysym currently */
2245 keycode
= param
->value
;
2247 case KBD_UNBOUND_KEYCODE
:
2254 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2256 else if (KTYP(param
->value
) == KT_CUR
)
2257 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2259 case KBD_POST_KEYSYM
:{
2260 unsigned char type
= KTYP(param
->value
) - 0xf0;
2261 unsigned char val
= KVAL(param
->value
);
2265 do_handle_shift(vc
, val
, up
);
2269 do_handle_latin(vc
, val
, up
);
2272 do_handle_cursor(vc
, val
, up
);
2275 do_handle_spec(vc
, val
, up
);
2282 in_keyboard_notifier
= 0;
2286 static int vt_notifier_call(struct notifier_block
*nb
,
2287 unsigned long code
, void *_param
)
2289 struct vt_notifier_param
*param
= _param
;
2290 struct vc_data
*vc
= param
->vc
;
2294 if (vc
->vc_mode
== KD_TEXT
)
2295 speakup_allocate(vc
, GFP_ATOMIC
);
2298 speakup_deallocate(vc
);
2301 if (param
->c
== '\b') {
2306 speakup_con_write(vc
, &d
, 1);
2310 speakup_con_update(vc
);
2316 /* called by: module_exit() */
2317 static void __exit
speakup_exit(void)
2321 unregister_keyboard_notifier(&keyboard_notifier_block
);
2322 unregister_vt_notifier(&vt_notifier_block
);
2323 speakup_unregister_devsynth();
2324 speakup_cancel_selection();
2325 speakup_cancel_paste();
2326 del_timer_sync(&cursor_timer
);
2327 kthread_stop(speakup_task
);
2328 speakup_task
= NULL
;
2329 mutex_lock(&spk_mutex
);
2331 mutex_unlock(&spk_mutex
);
2332 spk_ttyio_unregister_ldisc();
2334 speakup_kobj_exit();
2336 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2337 kfree(speakup_console
[i
]);
2339 speakup_remove_virtual_keyboard();
2341 for (i
= 0; i
< MAXVARS
; i
++)
2342 speakup_unregister_var(i
);
2344 for (i
= 0; i
< 256; i
++) {
2345 if (spk_characters
[i
] != spk_default_chars
[i
])
2346 kfree(spk_characters
[i
]);
2349 spk_free_user_msgs();
2352 /* call by: module_init() */
2353 static int __init
speakup_init(void)
2357 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2360 /* These first few initializations cannot fail. */
2361 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2362 spk_reset_default_chars();
2363 spk_reset_default_chartab();
2364 spk_strlwr(synth_name
);
2365 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2366 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2367 speakup_register_var(var
);
2368 for (var
= synth_time_vars
;
2369 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2370 speakup_register_var(var
);
2371 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2372 spk_set_mask_bits(NULL
, i
, 2);
2374 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2376 /* From here on out, initializations can fail. */
2377 err
= speakup_add_virtual_keyboard();
2379 goto error_virtkeyboard
;
2381 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2383 err
= speakup_allocate(vc_cons
[i
].d
, GFP_KERNEL
);
2385 goto error_kobjects
;
2389 spk_shut_up
|= 0x01;
2391 err
= speakup_kobj_init();
2393 goto error_kobjects
;
2395 spk_ttyio_register_ldisc();
2396 synth_init(synth_name
);
2397 speakup_register_devsynth();
2399 * register_devsynth might fail, but this error is not fatal.
2400 * /dev/synth is an extra feature; the rest of Speakup
2401 * will work fine without it.
2404 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2406 goto error_kbdnotifier
;
2407 err
= register_vt_notifier(&vt_notifier_block
);
2409 goto error_vtnotifier
;
2411 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2413 if (IS_ERR(speakup_task
)) {
2414 err
= PTR_ERR(speakup_task
);
2418 set_user_nice(speakup_task
, 10);
2419 wake_up_process(speakup_task
);
2421 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2422 pr_info("synth name on entry is: %s\n", synth_name
);
2426 unregister_vt_notifier(&vt_notifier_block
);
2429 unregister_keyboard_notifier(&keyboard_notifier_block
);
2430 del_timer(&cursor_timer
);
2433 speakup_unregister_devsynth();
2434 mutex_lock(&spk_mutex
);
2436 mutex_unlock(&spk_mutex
);
2437 speakup_kobj_exit();
2440 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2441 kfree(speakup_console
[i
]);
2443 speakup_remove_virtual_keyboard();
2446 for (i
= 0; i
< MAXVARS
; i
++)
2447 speakup_unregister_var(i
);
2449 for (i
= 0; i
< 256; i
++) {
2450 if (spk_characters
[i
] != spk_default_chars
[i
])
2451 kfree(spk_characters
[i
]);
2454 spk_free_user_msgs();
2460 module_init(speakup_init
);
2461 module_exit(speakup_exit
);