]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/staging/speakup/main.c
Staging: speakup: Fix getting port information
[mirror_ubuntu-artful-kernel.git] / drivers / staging / speakup / main.c
CommitLineData
c6e3fd22 1/* speakup.c
16d35515
WH
2 * review functions for the speakup screen review package.
3 * originally written by: Kirk Reiser and Andy Berdan.
4 *
5 * extensively modified by David Borowski.
6 *
7 ** Copyright (C) 1998 Kirk Reiser.
8 * Copyright (C) 2003 David Borowski.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
c6e3fd22
WH
23*/
24
25#include <linux/kernel.h>
c6e3fd22
WH
26#include <linux/vt.h>
27#include <linux/tty.h>
16d35515 28#include <linux/mm.h> /* __get_free_page() and friends */
c6e3fd22
WH
29#include <linux/vt_kern.h>
30#include <linux/ctype.h>
31#include <linux/selection.h>
32#include <linux/unistd.h>
33#include <linux/jiffies.h>
34#include <linux/kthread.h>
35#include <linux/keyboard.h> /* for KT_SHIFT */
16d35515 36#include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
c6e3fd22
WH
37#include <linux/input.h>
38#include <linux/kmod.h>
39
c6e3fd22
WH
40/* speakup_*_selection */
41#include <linux/module.h>
42#include <linux/sched.h>
43#include <linux/slab.h>
44#include <linux/types.h>
45#include <linux/consolemap.h>
46
47#include <linux/spinlock.h>
48#include <linux/notifier.h>
49
16d35515 50#include <linux/uaccess.h> /* copy_from|to|user() and others */
c6e3fd22
WH
51
52#include "spk_priv.h"
53#include "speakup.h"
54
55#define MAX_DELAY msecs_to_jiffies(500)
56#define MINECHOCHAR SPACE
57
58MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
59MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
60MODULE_DESCRIPTION("Speakup console speech");
61MODULE_LICENSE("GPL");
62MODULE_VERSION(SPEAKUP_VERSION);
63
64char *synth_name;
65module_param_named(synth, synth_name, charp, S_IRUGO);
ca2beaf8 66module_param_named(quiet, spk_quiet_boot, bool, S_IRUGO);
c6e3fd22
WH
67
68MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
69MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
70
ca2beaf8 71special_func spk_special_handler;
c6e3fd22 72
ca2beaf8 73short spk_pitch_shift, synth_flags;
c6e3fd22 74static char buf[256];
ca2beaf8
ST
75int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
76int spk_no_intr, spk_spell_delay;
77int spk_key_echo, spk_say_word_ctl;
78int spk_say_ctrl, spk_bell_pos;
79short spk_punc_mask;
80int spk_punc_level, spk_reading_punc;
e7027b9b
DT
81char spk_str_caps_start[MAXVARLEN + 1] = "\0";
82char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
ca2beaf8 83const struct st_bits_data spk_punc_info[] = {
16d35515
WH
84 {"none", "", 0},
85 {"some", "/$%&@", SOME},
86 {"most", "$%&#()=+*/@^<>|\\", MOST},
87 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
88 {"delimiters", "", B_WDLM},
89 {"repeats", "()", CH_RPT},
90 {"extended numeric", "", B_EXNUM},
91 {"symbols", "", B_SYM},
ab06e0f2 92 {NULL, NULL}
c6e3fd22 93};
16d35515 94
c6e3fd22
WH
95static char mark_cut_flag;
96#define MAX_KEY 160
0012196c
SK
97static u_char *spk_shift_table;
98u_char *spk_our_keys[MAX_KEY];
ca2beaf8
ST
99u_char spk_key_buf[600];
100const u_char spk_key_defaults[] = {
c6e3fd22
WH
101#include "speakupmap.h"
102};
103
104/* Speakup Cursor Track Variables */
105static int cursor_track = 1, prev_cursor_track = 1;
106
107/* cursor track modes, must be ordered same as cursor_msgs */
108enum {
109 CT_Off = 0,
110 CT_On,
111 CT_Highlight,
112 CT_Window,
113 CT_Max
114};
115#define read_all_mode CT_Max
116
117static struct tty_struct *tty;
118
119static void spkup_write(const char *in_buf, int count);
120
c6e3fd22
WH
121static char *phonetic[] = {
122 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
16d35515
WH
123 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
124 "papa",
c6e3fd22
WH
125 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
126 "x ray", "yankee", "zulu"
127};
128
129/* array of 256 char pointers (one for each character description)
130 * initialized to default_chars and user selectable via
13d825ed
AF
131 * /proc/speakup/characters
132 */
ca2beaf8 133char *spk_characters[256];
c6e3fd22 134
ca2beaf8 135char *spk_default_chars[256] = {
16d35515 136/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
c6e3fd22
WH
137/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
138/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
16d35515
WH
139/*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
140 "control",
141/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
142 "tick",
143/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
144 "dot",
c6e3fd22
WH
145 "slash",
146/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
147 "eight", "nine",
148/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
149/*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
150/*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
151/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
16d35515
WH
152/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
153 "caret",
c6e3fd22
WH
154 "line",
155/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
156/*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
157/*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
158/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
16d35515
WH
159/*127*/ "del", "control", "control", "control", "control", "control",
160 "control", "control", "control", "control", "control",
161/*138*/ "control", "control", "control", "control", "control",
162 "control", "control", "control", "control", "control",
163 "control", "control",
164/*150*/ "control", "control", "control", "control", "control",
165 "control", "control", "control", "control", "control",
c6e3fd22 166/*160*/ "nbsp", "inverted bang",
16d35515
WH
167/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
168/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
c6e3fd22 169/*172*/ "not", "soft hyphen", "registered", "macron",
16d35515
WH
170/*176*/ "degrees", "plus or minus", "super two", "super three",
171/*180*/ "acute accent", "micro", "pilcrow", "middle dot",
c6e3fd22 172/*184*/ "cedilla", "super one", "male ordinal", "double right angle",
16d35515
WH
173/*188*/ "one quarter", "one half", "three quarters",
174 "inverted question",
175/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
176 "A RING",
177/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
178 "E OOMLAUT",
179/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
180 "N TILDE",
c6e3fd22 181/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
16d35515
WH
182/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
183 "U CIRCUMFLEX",
c6e3fd22
WH
184/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
185/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
186/*230*/ "ae", "c cidella", "e grave", "e acute",
16d35515
WH
187/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
188 "i circumflex",
189/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
190 "o circumflex",
191/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
192 "u acute",
c6e3fd22
WH
193/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
194};
195
196/* array of 256 u_short (one for each character)
197 * initialized to default_chartab and user selectable via
13d825ed
AF
198 * /sys/module/speakup/parameters/chartab
199 */
c6e3fd22
WH
200u_short spk_chartab[256];
201
202static u_short default_chartab[256] = {
16d35515
WH
203 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
204 B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
205 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
206 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */
207 WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */
208 PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */
209 NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */
210 NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */
211 PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */
212 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */
213 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */
214 A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */
215 PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */
216 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */
217 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */
218 ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */
219 B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
220 B_SYM, /* 135 */
221 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
222 B_CAPSYM, /* 143 */
223 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
224 B_SYM, /* 151 */
225 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
226 B_SYM, /* 159 */
227 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
228 B_SYM, /* 167 */
229 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */
230 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */
231 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */
232 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */
233 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */
234 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */
235 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */
236 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */
237 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */
238 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */
239 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */
c6e3fd22
WH
240};
241
242struct task_struct *speakup_task;
ca2beaf8 243struct bleep spk_unprocessed_sound;
c6e3fd22
WH
244static int spk_keydown;
245static u_char spk_lastkey, spk_close_press, keymap_flags;
246static u_char last_keycode, this_speakup_key;
247static u_long last_spk_jiffy;
248
249struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
250
251DEFINE_MUTEX(spk_mutex);
252
253static int keyboard_notifier_call(struct notifier_block *,
254 unsigned long code, void *param);
255
d9f54202 256static struct notifier_block keyboard_notifier_block = {
c6e3fd22
WH
257 .notifier_call = keyboard_notifier_call,
258};
259
260static int vt_notifier_call(struct notifier_block *,
261 unsigned long code, void *param);
262
d9f54202 263static struct notifier_block vt_notifier_block = {
c6e3fd22
WH
264 .notifier_call = vt_notifier_call,
265};
266
267static unsigned char get_attributes(u16 *pos)
268{
16d35515 269 return (u_char) (scr_readw(pos) >> 8);
c6e3fd22
WH
270}
271
272static void speakup_date(struct vc_data *vc)
273{
274 spk_x = spk_cx = vc->vc_x;
275 spk_y = spk_cy = vc->vc_y;
276 spk_pos = spk_cp = vc->vc_pos;
277 spk_old_attr = spk_attr;
278 spk_attr = get_attributes((u_short *) spk_pos);
279}
280
281static void bleep(u_short val)
282{
283 static const short vals[] = {
284 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
285 };
286 short freq;
ca2beaf8 287 int time = spk_bleep_time;
8e69a811 288
16d35515 289 freq = vals[val % 12];
c6e3fd22 290 if (val > 11)
16d35515 291 freq *= (1 << (val / 12));
ca2beaf8
ST
292 spk_unprocessed_sound.freq = freq;
293 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
294 spk_unprocessed_sound.active = 1;
c6e3fd22
WH
295 /* We can only have 1 active sound at a time. */
296}
297
298static void speakup_shut_up(struct vc_data *vc)
299{
300 if (spk_killed)
301 return;
302 spk_shut_up |= 0x01;
303 spk_parked &= 0xfe;
304 speakup_date(vc);
305 if (synth != NULL)
ca2beaf8 306 spk_do_flush();
c6e3fd22
WH
307}
308
309static void speech_kill(struct vc_data *vc)
310{
311 char val = synth->is_alive(synth);
8e69a811 312
c6e3fd22
WH
313 if (val == 0)
314 return;
315
316 /* re-enables synth, if disabled */
317 if (val == 2 || spk_killed) {
318 /* dead */
319 spk_shut_up &= ~0x40;
ca2beaf8 320 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
c6e3fd22 321 } else {
ca2beaf8 322 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
c6e3fd22
WH
323 spk_shut_up |= 0x40;
324 }
325}
326
327static void speakup_off(struct vc_data *vc)
328{
329 if (spk_shut_up & 0x80) {
330 spk_shut_up &= 0x7f;
ca2beaf8 331 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
c6e3fd22
WH
332 } else {
333 spk_shut_up |= 0x80;
ca2beaf8 334 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
c6e3fd22
WH
335 }
336 speakup_date(vc);
337}
338
339static void speakup_parked(struct vc_data *vc)
340{
341 if (spk_parked & 0x80) {
342 spk_parked = 0;
ca2beaf8 343 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
c6e3fd22
WH
344 } else {
345 spk_parked |= 0x80;
ca2beaf8 346 synth_printf("%s\n", spk_msg_get(MSG_PARKED));
c6e3fd22
WH
347 }
348}
349
350static void speakup_cut(struct vc_data *vc)
351{
352 static const char err_buf[] = "set selection failed";
353 int ret;
354
355 if (!mark_cut_flag) {
356 mark_cut_flag = 1;
ca2beaf8
ST
357 spk_xs = (u_short) spk_x;
358 spk_ys = (u_short) spk_y;
c6e3fd22 359 spk_sel_cons = vc;
ca2beaf8 360 synth_printf("%s\n", spk_msg_get(MSG_MARK));
c6e3fd22
WH
361 return;
362 }
ca2beaf8
ST
363 spk_xe = (u_short) spk_x;
364 spk_ye = (u_short) spk_y;
c6e3fd22 365 mark_cut_flag = 0;
ca2beaf8 366 synth_printf("%s\n", spk_msg_get(MSG_CUT));
c6e3fd22
WH
367
368 speakup_clear_selection();
369 ret = speakup_set_selection(tty);
370
371 switch (ret) {
372 case 0:
16d35515
WH
373 break; /* no error */
374 case -EFAULT:
c6e3fd22
WH
375 pr_warn("%sEFAULT\n", err_buf);
376 break;
16d35515 377 case -EINVAL:
c6e3fd22
WH
378 pr_warn("%sEINVAL\n", err_buf);
379 break;
16d35515 380 case -ENOMEM:
c6e3fd22
WH
381 pr_warn("%sENOMEM\n", err_buf);
382 break;
383 }
384}
385
386static void speakup_paste(struct vc_data *vc)
387{
388 if (mark_cut_flag) {
389 mark_cut_flag = 0;
ca2beaf8 390 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
c6e3fd22 391 } else {
ca2beaf8 392 synth_printf("%s\n", spk_msg_get(MSG_PASTE));
c6e3fd22
WH
393 speakup_paste_selection(tty);
394 }
395}
396
397static void say_attributes(struct vc_data *vc)
398{
399 int fg = spk_attr & 0x0f;
400 int bg = spk_attr >> 4;
8e69a811 401
c6e3fd22 402 if (fg > 8) {
ca2beaf8 403 synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
c6e3fd22
WH
404 fg -= 8;
405 }
ca2beaf8 406 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
c6e3fd22 407 if (bg > 7) {
ca2beaf8 408 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
c6e3fd22
WH
409 bg -= 8;
410 } else
ca2beaf8
ST
411 synth_printf(" %s ", spk_msg_get(MSG_ON));
412 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
c6e3fd22
WH
413}
414
415enum {
416 edge_top = 1,
417 edge_bottom,
418 edge_left,
419 edge_right,
420 edge_quiet
421};
422
423static void announce_edge(struct vc_data *vc, int msg_id)
424{
ca2beaf8 425 if (spk_bleeps & 1)
c6e3fd22 426 bleep(spk_y);
ca2beaf8 427 if ((spk_bleeps & 2) && (msg_id < edge_quiet))
63b8ebe4
SG
428 synth_printf("%s\n",
429 spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
c6e3fd22
WH
430}
431
432static void speak_char(u_char ch)
433{
ca2beaf8
ST
434 char *cp = spk_characters[ch];
435 struct var_t *direct = spk_get_var(DIRECT);
8e69a811 436
c6e3fd22
WH
437 if (direct && direct->u.n.value) {
438 if (IS_CHAR(ch, B_CAP)) {
ca2beaf8
ST
439 spk_pitch_shift++;
440 synth_printf("%s", spk_str_caps_start);
c6e3fd22
WH
441 }
442 synth_printf("%c", ch);
443 if (IS_CHAR(ch, B_CAP))
ca2beaf8 444 synth_printf("%s", spk_str_caps_stop);
c6e3fd22
WH
445 return;
446 }
447 if (cp == NULL) {
448 pr_info("speak_char: cp == NULL!\n");
449 return;
450 }
451 synth_buffer_add(SPACE);
452 if (IS_CHAR(ch, B_CAP)) {
ca2beaf8
ST
453 spk_pitch_shift++;
454 synth_printf("%s", spk_str_caps_start);
c6e3fd22 455 synth_printf("%s", cp);
ca2beaf8 456 synth_printf("%s", spk_str_caps_stop);
c6e3fd22
WH
457 } else {
458 if (*cp == '^') {
ca2beaf8 459 synth_printf("%s", spk_msg_get(MSG_CTRL));
c6e3fd22
WH
460 cp++;
461 }
462 synth_printf("%s", cp);
463 }
464 synth_buffer_add(SPACE);
465}
466
69d8ba56 467static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
c6e3fd22
WH
468{
469 u16 ch = ' ';
8e69a811 470
c6e3fd22
WH
471 if (vc && pos) {
472 u16 w = scr_readw(pos);
473 u16 c = w & 0xff;
474
475 if (w & vc->vc_hi_font_mask)
476 c |= 0x100;
477
478 ch = inverse_translate(vc, c, 0);
479 *attribs = (w & 0xff00) >> 8;
480 }
481 return ch;
482}
483
484static void say_char(struct vc_data *vc)
485{
486 u_short ch;
8e69a811 487
c6e3fd22
WH
488 spk_old_attr = spk_attr;
489 ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
490 if (spk_attr != spk_old_attr) {
ca2beaf8 491 if (spk_attrib_bleep & 1)
c6e3fd22 492 bleep(spk_y);
ca2beaf8 493 if (spk_attrib_bleep & 2)
c6e3fd22
WH
494 say_attributes(vc);
495 }
496 speak_char(ch & 0xff);
497}
498
499static void say_phonetic_char(struct vc_data *vc)
500{
501 u_short ch;
8e69a811 502
c6e3fd22
WH
503 spk_old_attr = spk_attr;
504 ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
505 if (isascii(ch) && isalpha(ch)) {
506 ch &= 0x1f;
507 synth_printf("%s\n", phonetic[--ch]);
508 } else {
509 if (IS_CHAR(ch, B_NUM))
ca2beaf8 510 synth_printf("%s ", spk_msg_get(MSG_NUMBER));
c6e3fd22
WH
511 speak_char(ch);
512 }
513}
514
515static void say_prev_char(struct vc_data *vc)
516{
517 spk_parked |= 0x01;
518 if (spk_x == 0) {
519 announce_edge(vc, edge_left);
520 return;
521 }
522 spk_x--;
523 spk_pos -= 2;
524 say_char(vc);
525}
526
527static void say_next_char(struct vc_data *vc)
528{
529 spk_parked |= 0x01;
530 if (spk_x == vc->vc_cols - 1) {
531 announce_edge(vc, edge_right);
532 return;
533 }
534 spk_x++;
535 spk_pos += 2;
536 say_char(vc);
537}
538
539/* get_word - will first check to see if the character under the
ca2beaf8
ST
540 * reading cursor is a space and if spk_say_word_ctl is true it will
541 * return the word space. If spk_say_word_ctl is not set it will check to
16d35515
WH
542 * see if there is a word starting on the next position to the right
543 * and return that word if it exists. If it does not exist it will
544 * move left to the beginning of any previous word on the line or the
13d825ed
AF
545 * beginning off the line whichever comes first..
546 */
c6e3fd22
WH
547
548static u_long get_word(struct vc_data *vc)
549{
550 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
551 char ch;
552 u_short attr_ch;
553 u_char temp;
8e69a811 554
c6e3fd22 555 spk_old_attr = spk_attr;
16d35515 556 ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
c6e3fd22
WH
557
558/* decided to take out the sayword if on a space (mis-information */
ca2beaf8 559 if (spk_say_word_ctl && ch == SPACE) {
c6e3fd22 560 *buf = '\0';
ca2beaf8 561 synth_printf("%s\n", spk_msg_get(MSG_SPACE));
c6e3fd22
WH
562 return 0;
563 } else if ((tmpx < vc->vc_cols - 2)
564 && (ch == SPACE || ch == 0 || IS_WDLM(ch))
16d35515
WH
565 && ((char)get_char(vc, (u_short *) &tmp_pos + 1, &temp) >
566 SPACE)) {
c6e3fd22
WH
567 tmp_pos += 2;
568 tmpx++;
569 } else
570 while (tmpx > 0) {
16d35515 571 ch = (char)get_char(vc, (u_short *) tmp_pos - 1, &temp);
c6e3fd22 572 if ((ch == SPACE || ch == 0 || IS_WDLM(ch))
16d35515
WH
573 && ((char)get_char(vc, (u_short *) tmp_pos, &temp) >
574 SPACE))
c6e3fd22
WH
575 break;
576 tmp_pos -= 2;
577 tmpx--;
578 }
579 attr_ch = get_char(vc, (u_short *) tmp_pos, &spk_attr);
580 buf[cnt++] = attr_ch & 0xff;
581 while (tmpx < vc->vc_cols - 1) {
582 tmp_pos += 2;
583 tmpx++;
16d35515
WH
584 ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
585 if ((ch == SPACE) || ch == 0
586 || (IS_WDLM(buf[cnt - 1]) && (ch > SPACE)))
c6e3fd22
WH
587 break;
588 buf[cnt++] = ch;
589 }
590 buf[cnt] = '\0';
591 return cnt;
592}
593
594static void say_word(struct vc_data *vc)
595{
596 u_long cnt = get_word(vc);
ca2beaf8 597 u_short saved_punc_mask = spk_punc_mask;
8e69a811 598
c6e3fd22
WH
599 if (cnt == 0)
600 return;
ca2beaf8 601 spk_punc_mask = PUNC;
c6e3fd22
WH
602 buf[cnt++] = SPACE;
603 spkup_write(buf, cnt);
ca2beaf8 604 spk_punc_mask = saved_punc_mask;
c6e3fd22
WH
605}
606
607static void say_prev_word(struct vc_data *vc)
608{
609 u_char temp;
610 char ch;
611 u_short edge_said = 0, last_state = 0, state = 0;
8e69a811 612
c6e3fd22
WH
613 spk_parked |= 0x01;
614
615 if (spk_x == 0) {
616 if (spk_y == 0) {
617 announce_edge(vc, edge_top);
618 return;
619 }
620 spk_y--;
621 spk_x = vc->vc_cols;
622 edge_said = edge_quiet;
623 }
624 while (1) {
625 if (spk_x == 0) {
626 if (spk_y == 0) {
627 edge_said = edge_top;
628 break;
629 }
630 if (edge_said != edge_quiet)
631 edge_said = edge_left;
632 if (state > 0)
633 break;
634 spk_y--;
635 spk_x = vc->vc_cols - 1;
636 } else
637 spk_x--;
16d35515
WH
638 spk_pos -= 2;
639 ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
c6e3fd22
WH
640 if (ch == SPACE || ch == 0)
641 state = 0;
642 else if (IS_WDLM(ch))
643 state = 1;
644 else
645 state = 2;
646 if (state < last_state) {
647 spk_pos += 2;
648 spk_x++;
649 break;
650 }
651 last_state = state;
652 }
653 if (spk_x == 0 && edge_said == edge_quiet)
654 edge_said = edge_left;
655 if (edge_said > 0 && edge_said < edge_quiet)
656 announce_edge(vc, edge_said);
657 say_word(vc);
658}
659
660static void say_next_word(struct vc_data *vc)
661{
662 u_char temp;
663 char ch;
664 u_short edge_said = 0, last_state = 2, state = 0;
c6e3fd22 665
8e69a811 666 spk_parked |= 0x01;
c6e3fd22
WH
667 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
668 announce_edge(vc, edge_bottom);
669 return;
670 }
671 while (1) {
16d35515 672 ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
c6e3fd22
WH
673 if (ch == SPACE || ch == 0)
674 state = 0;
675 else if (IS_WDLM(ch))
676 state = 1;
677 else
678 state = 2;
679 if (state > last_state)
680 break;
681 if (spk_x >= vc->vc_cols - 1) {
682 if (spk_y == vc->vc_rows - 1) {
683 edge_said = edge_bottom;
684 break;
685 }
686 state = 0;
687 spk_y++;
688 spk_x = 0;
689 edge_said = edge_right;
690 } else
691 spk_x++;
692 spk_pos += 2;
693 last_state = state;
694 }
695 if (edge_said > 0)
696 announce_edge(vc, edge_said);
697 say_word(vc);
698}
699
700static void spell_word(struct vc_data *vc)
701{
0f709488 702 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
ca2beaf8
ST
703 char *cp = buf, *str_cap = spk_str_caps_stop;
704 char *cp1, *last_cap = spk_str_caps_stop;
c6e3fd22 705 u_char ch;
8e69a811 706
c6e3fd22
WH
707 if (!get_word(vc))
708 return;
709 while ((ch = (u_char) *cp)) {
710 if (cp != buf)
ca2beaf8 711 synth_printf(" %s ", delay_str[spk_spell_delay]);
c6e3fd22 712 if (IS_CHAR(ch, B_CAP)) {
ca2beaf8
ST
713 str_cap = spk_str_caps_start;
714 if (*spk_str_caps_stop)
715 spk_pitch_shift++;
16d35515 716 else /* synth has no pitch */
ca2beaf8 717 last_cap = spk_str_caps_stop;
c6e3fd22 718 } else
ca2beaf8 719 str_cap = spk_str_caps_stop;
c6e3fd22
WH
720 if (str_cap != last_cap) {
721 synth_printf("%s", str_cap);
722 last_cap = str_cap;
723 }
724 if (this_speakup_key == SPELL_PHONETIC
725 && (isascii(ch) && isalpha(ch))) {
726 ch &= 31;
727 cp1 = phonetic[--ch];
728 } else {
ca2beaf8 729 cp1 = spk_characters[ch];
c6e3fd22 730 if (*cp1 == '^') {
ca2beaf8 731 synth_printf("%s", spk_msg_get(MSG_CTRL));
c6e3fd22
WH
732 cp1++;
733 }
734 }
735 synth_printf("%s", cp1);
736 cp++;
737 }
ca2beaf8
ST
738 if (str_cap != spk_str_caps_stop)
739 synth_printf("%s", spk_str_caps_stop);
c6e3fd22
WH
740}
741
742static int get_line(struct vc_data *vc)
743{
744 u_long tmp = spk_pos - (spk_x * 2);
745 int i = 0;
746 u_char tmp2;
747
748 spk_old_attr = spk_attr;
749 spk_attr = get_attributes((u_short *) spk_pos);
750 for (i = 0; i < vc->vc_cols; i++) {
751 buf[i] = (u_char) get_char(vc, (u_short *) tmp, &tmp2);
752 tmp += 2;
753 }
754 for (--i; i >= 0; i--)
755 if (buf[i] != SPACE)
756 break;
757 return ++i;
758}
759
760static void say_line(struct vc_data *vc)
761{
762 int i = get_line(vc);
763 char *cp;
ca2beaf8 764 u_short saved_punc_mask = spk_punc_mask;
8e69a811 765
c6e3fd22 766 if (i == 0) {
ca2beaf8 767 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
768 return;
769 }
770 buf[i++] = '\n';
771 if (this_speakup_key == SAY_LINE_INDENT) {
16d35515
WH
772 cp = buf;
773 while (*cp == SPACE)
774 cp++;
c6e3fd22
WH
775 synth_printf("%d, ", (cp - buf) + 1);
776 }
ca2beaf8 777 spk_punc_mask = spk_punc_masks[spk_reading_punc];
c6e3fd22 778 spkup_write(buf, i);
ca2beaf8 779 spk_punc_mask = saved_punc_mask;
c6e3fd22
WH
780}
781
782static void say_prev_line(struct vc_data *vc)
783{
784 spk_parked |= 0x01;
785 if (spk_y == 0) {
786 announce_edge(vc, edge_top);
787 return;
788 }
789 spk_y--;
790 spk_pos -= vc->vc_size_row;
791 say_line(vc);
792}
793
794static void say_next_line(struct vc_data *vc)
795{
796 spk_parked |= 0x01;
797 if (spk_y == vc->vc_rows - 1) {
798 announce_edge(vc, edge_bottom);
799 return;
800 }
801 spk_y++;
802 spk_pos += vc->vc_size_row;
803 say_line(vc);
804}
805
806static int say_from_to(struct vc_data *vc, u_long from, u_long to,
807 int read_punc)
808{
809 int i = 0;
810 u_char tmp;
ca2beaf8 811 u_short saved_punc_mask = spk_punc_mask;
8e69a811 812
c6e3fd22
WH
813 spk_old_attr = spk_attr;
814 spk_attr = get_attributes((u_short *) from);
815 while (from < to) {
16d35515 816 buf[i++] = (char)get_char(vc, (u_short *) from, &tmp);
c6e3fd22
WH
817 from += 2;
818 if (i >= vc->vc_size_row)
819 break;
820 }
821 for (--i; i >= 0; i--)
822 if (buf[i] != SPACE)
823 break;
824 buf[++i] = SPACE;
825 buf[++i] = '\0';
826 if (i < 1)
827 return i;
828 if (read_punc)
ca2beaf8 829 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
c6e3fd22
WH
830 spkup_write(buf, i);
831 if (read_punc)
ca2beaf8 832 spk_punc_mask = saved_punc_mask;
c6e3fd22
WH
833 return i - 1;
834}
835
836static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
837 int read_punc)
838{
839 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
840 u_long end = start + (to * 2);
8e69a811 841
c6e3fd22
WH
842 start += from * 2;
843 if (say_from_to(vc, start, end, read_punc) <= 0)
844 if (cursor_track != read_all_mode)
ca2beaf8 845 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
846}
847
848/* Sentence Reading Commands */
849
c6e3fd22
WH
850static int currsentence;
851static int numsentences[2];
852static char *sentbufend[2];
853static char *sentmarks[2][10];
854static int currbuf;
855static int bn;
856static char sentbuf[2][256];
857
16d35515 858static int say_sentence_num(int num, int prev)
c6e3fd22
WH
859{
860 bn = currbuf;
861 currsentence = num + 1;
862 if (prev && --bn == -1)
863 bn = 1;
864
865 if (num > numsentences[bn])
866 return 0;
867
868 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
869 return 1;
870}
871
872static int get_sentence_buf(struct vc_data *vc, int read_punc)
873{
874 u_long start, end;
875 int i, bn;
876 u_char tmp;
877
878 currbuf++;
879 if (currbuf == 2)
880 currbuf = 0;
881 bn = currbuf;
882 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
16d35515 883 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
c6e3fd22
WH
884
885 numsentences[bn] = 0;
886 sentmarks[bn][0] = &sentbuf[bn][0];
887 i = 0;
888 spk_old_attr = spk_attr;
889 spk_attr = get_attributes((u_short *) start);
890
891 while (start < end) {
16d35515 892 sentbuf[bn][i] = (char)get_char(vc, (u_short *) start, &tmp);
c6e3fd22 893 if (i > 0) {
16d35515 894 if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.'
c6e3fd22
WH
895 && numsentences[bn] < 9) {
896 /* Sentence Marker */
897 numsentences[bn]++;
898 sentmarks[bn][numsentences[bn]] =
16d35515 899 &sentbuf[bn][i];
c6e3fd22
WH
900 }
901 }
902 i++;
903 start += 2;
904 if (i >= vc->vc_size_row)
905 break;
906 }
907
908 for (--i; i >= 0; i--)
909 if (sentbuf[bn][i] != SPACE)
910 break;
911
912 if (i < 1)
913 return -1;
914
915 sentbuf[bn][++i] = SPACE;
916 sentbuf[bn][++i] = '\0';
917
918 sentbufend[bn] = &sentbuf[bn][i];
919 return numsentences[bn];
920}
921
922static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
923{
924 u_long start = vc->vc_origin, end;
8e69a811 925
c6e3fd22
WH
926 if (from > 0)
927 start += from * vc->vc_size_row;
928 if (to > vc->vc_rows)
929 to = vc->vc_rows;
930 end = vc->vc_origin + (to * vc->vc_size_row);
931 for (from = start; from < end; from = to) {
932 to = from + vc->vc_size_row;
933 say_from_to(vc, from, to, 1);
934 }
935}
936
937static void say_screen(struct vc_data *vc)
938{
939 say_screen_from_to(vc, 0, vc->vc_rows);
940}
941
942static void speakup_win_say(struct vc_data *vc)
943{
944 u_long start, end, from, to;
8e69a811 945
c6e3fd22 946 if (win_start < 2) {
ca2beaf8 947 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
c6e3fd22
WH
948 return;
949 }
950 start = vc->vc_origin + (win_top * vc->vc_size_row);
951 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
952 while (start <= end) {
953 from = start + (win_left * 2);
954 to = start + (win_right * 2);
955 say_from_to(vc, from, to, 1);
956 start += vc->vc_size_row;
957 }
958}
959
960static void top_edge(struct vc_data *vc)
961{
962 spk_parked |= 0x01;
963 spk_pos = vc->vc_origin + 2 * spk_x;
964 spk_y = 0;
965 say_line(vc);
966}
967
968static void bottom_edge(struct vc_data *vc)
969{
970 spk_parked |= 0x01;
971 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
972 spk_y = vc->vc_rows - 1;
973 say_line(vc);
974}
975
976static void left_edge(struct vc_data *vc)
977{
978 spk_parked |= 0x01;
979 spk_pos -= spk_x * 2;
980 spk_x = 0;
981 say_char(vc);
982}
983
984static void right_edge(struct vc_data *vc)
985{
986 spk_parked |= 0x01;
987 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
988 spk_x = vc->vc_cols - 1;
989 say_char(vc);
990}
991
992static void say_first_char(struct vc_data *vc)
993{
994 int i, len = get_line(vc);
995 u_char ch;
8e69a811 996
c6e3fd22
WH
997 spk_parked |= 0x01;
998 if (len == 0) {
ca2beaf8 999 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
1000 return;
1001 }
1002 for (i = 0; i < len; i++)
1003 if (buf[i] != SPACE)
1004 break;
1005 ch = buf[i];
1006 spk_pos -= (spk_x - i) * 2;
1007 spk_x = i;
1008 synth_printf("%d, ", ++i);
1009 speak_char(ch);
1010}
1011
1012static void say_last_char(struct vc_data *vc)
1013{
1014 int len = get_line(vc);
1015 u_char ch;
8e69a811 1016
c6e3fd22
WH
1017 spk_parked |= 0x01;
1018 if (len == 0) {
ca2beaf8 1019 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
1020 return;
1021 }
1022 ch = buf[--len];
1023 spk_pos -= (spk_x - len) * 2;
1024 spk_x = len;
1025 synth_printf("%d, ", ++len);
1026 speak_char(ch);
1027}
1028
1029static void say_position(struct vc_data *vc)
1030{
ca2beaf8 1031 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
16d35515 1032 vc->vc_num + 1);
c6e3fd22
WH
1033 synth_printf("\n");
1034}
1035
1036/* Added by brianb */
1037static void say_char_num(struct vc_data *vc)
1038{
1039 u_char tmp;
1040 u_short ch = get_char(vc, (u_short *) spk_pos, &tmp);
8e69a811 1041
c6e3fd22 1042 ch &= 0xff;
ca2beaf8 1043 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
c6e3fd22
WH
1044}
1045
1046/* these are stub functions to keep keyboard.c happy. */
1047
1048static void say_from_top(struct vc_data *vc)
1049{
1050 say_screen_from_to(vc, 0, spk_y);
1051}
1052
1053static void say_to_bottom(struct vc_data *vc)
1054{
1055 say_screen_from_to(vc, spk_y, vc->vc_rows);
1056}
1057
1058static void say_from_left(struct vc_data *vc)
1059{
1060 say_line_from_to(vc, 0, spk_x, 1);
1061}
1062
1063static void say_to_right(struct vc_data *vc)
1064{
1065 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1066}
1067
1068/* end of stub functions. */
1069
1070static void spkup_write(const char *in_buf, int count)
1071{
16d35515 1072 static int rep_count;
c6e3fd22 1073 static u_char ch = '\0', old_ch = '\0';
16d35515 1074 static u_short char_type, last_type;
c6e3fd22 1075 int in_count = count;
8e69a811 1076
c6e3fd22
WH
1077 spk_keydown = 0;
1078 while (count--) {
1079 if (cursor_track == read_all_mode) {
1080 /* Insert Sentence Index */
1081 if ((in_buf == sentmarks[bn][currsentence]) &&
16d35515 1082 (currsentence <= numsentences[bn]))
c6e3fd22
WH
1083 synth_insert_next_index(currsentence++);
1084 }
16d35515 1085 ch = (u_char) *in_buf++;
c6e3fd22 1086 char_type = spk_chartab[ch];
16d35515 1087 if (ch == old_ch && !(char_type & B_NUM)) {
c6e3fd22
WH
1088 if (++rep_count > 2)
1089 continue;
1090 } else {
16d35515 1091 if ((last_type & CH_RPT) && rep_count > 2) {
c6e3fd22 1092 synth_printf(" ");
ca2beaf8 1093 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
16d35515 1094 ++rep_count);
c6e3fd22
WH
1095 synth_printf(" ");
1096 }
1097 rep_count = 0;
1098 }
1099 if (ch == spk_lastkey) {
1100 rep_count = 0;
ca2beaf8 1101 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
c6e3fd22
WH
1102 speak_char(ch);
1103 } else if (char_type & B_ALPHA) {
1104 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1105 synth_buffer_add(SPACE);
1106 synth_printf("%c", ch);
1107 } else if (char_type & B_NUM) {
1108 rep_count = 0;
1109 synth_printf("%c", ch);
ca2beaf8 1110 } else if (char_type & spk_punc_mask) {
c6e3fd22 1111 speak_char(ch);
16d35515
WH
1112 char_type &= ~PUNC; /* for dec nospell processing */
1113 } else if (char_type & SYNTH_OK) {
1114 /* these are usually puncts like . and , which synth
1115 * needs for expression.
1116 * suppress multiple to get rid of long pauses and
1117 * clear repeat count
1118 * so if someone has
13d825ed
AF
1119 * repeats on you don't get nothing repeated count
1120 */
c6e3fd22
WH
1121 if (ch != old_ch)
1122 synth_printf("%c", ch);
1123 else
1124 rep_count = 0;
1125 } else {
1126/* send space and record position, if next is num overwrite space */
1127 if (old_ch != ch)
1128 synth_buffer_add(SPACE);
1129 else
1130 rep_count = 0;
1131 }
1132 old_ch = ch;
1133 last_type = char_type;
1134 }
1135 spk_lastkey = 0;
1136 if (in_count > 2 && rep_count > 2) {
16d35515 1137 if (last_type & CH_RPT) {
c6e3fd22 1138 synth_printf(" ");
63b8ebe4
SG
1139 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1140 ++rep_count);
c6e3fd22
WH
1141 synth_printf(" ");
1142 }
1143 rep_count = 0;
1144 }
1145}
1146
1147static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1148
1149static void read_all_doc(struct vc_data *vc);
1150static void cursor_done(u_long data);
1151static DEFINE_TIMER(cursor_timer, cursor_done, 0, 0);
1152
1153static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1154{
1155 unsigned long flags;
8e69a811 1156
c6e3fd22
WH
1157 if (synth == NULL || up_flag || spk_killed)
1158 return;
3efe810f 1159 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1160 if (cursor_track == read_all_mode) {
1161 switch (value) {
1162 case KVAL(K_SHIFT):
1163 del_timer(&cursor_timer);
1164 spk_shut_up &= 0xfe;
ca2beaf8 1165 spk_do_flush();
c6e3fd22
WH
1166 read_all_doc(vc);
1167 break;
1168 case KVAL(K_CTRL):
1169 del_timer(&cursor_timer);
1170 cursor_track = prev_cursor_track;
1171 spk_shut_up &= 0xfe;
ca2beaf8 1172 spk_do_flush();
c6e3fd22
WH
1173 break;
1174 }
1175 } else {
1176 spk_shut_up &= 0xfe;
ca2beaf8 1177 spk_do_flush();
c6e3fd22 1178 }
ca2beaf8
ST
1179 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1180 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
3efe810f 1181 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1182}
1183
1184static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1185{
1186 unsigned long flags;
8e69a811 1187
3efe810f 1188 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1189 if (up_flag) {
1190 spk_lastkey = spk_keydown = 0;
3efe810f 1191 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1192 return;
1193 }
1194 if (synth == NULL || spk_killed) {
3efe810f 1195 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1196 return;
1197 }
1198 spk_shut_up &= 0xfe;
1199 spk_lastkey = value;
1200 spk_keydown++;
1201 spk_parked &= 0xfe;
ca2beaf8 1202 if (spk_key_echo == 2 && value >= MINECHOCHAR)
c6e3fd22 1203 speak_char(value);
3efe810f 1204 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1205}
1206
ca2beaf8 1207int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
c6e3fd22
WH
1208{
1209 int i = 0, states, key_data_len;
1210 const u_char *cp = key_info;
1211 u_char *cp1 = k_buffer;
1212 u_char ch, version, num_keys;
8e69a811 1213
c6e3fd22
WH
1214 version = *cp++;
1215 if (version != KEY_MAP_VER)
1216 return -1;
1217 num_keys = *cp;
16d35515 1218 states = (int)cp[1];
c6e3fd22 1219 key_data_len = (states + 1) * (num_keys + 1);
ca2beaf8 1220 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf))
c6e3fd22
WH
1221 return -2;
1222 memset(k_buffer, 0, SHIFT_TBL_SIZE);
ca2beaf8
ST
1223 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1224 spk_shift_table = k_buffer;
1225 spk_our_keys[0] = spk_shift_table;
c6e3fd22
WH
1226 cp1 += SHIFT_TBL_SIZE;
1227 memcpy(cp1, cp, key_data_len + 3);
16d35515
WH
1228 /* get num_keys, states and data */
1229 cp1 += 2; /* now pointing at shift states */
c6e3fd22
WH
1230 for (i = 1; i <= states; i++) {
1231 ch = *cp1++;
1232 if (ch >= SHIFT_TBL_SIZE)
1233 return -3;
ca2beaf8 1234 spk_shift_table[ch] = i;
c6e3fd22
WH
1235 }
1236 keymap_flags = *cp1++;
1237 while ((ch = *cp1)) {
1238 if (ch >= MAX_KEY)
1239 return -4;
ca2beaf8 1240 spk_our_keys[ch] = cp1;
c6e3fd22
WH
1241 cp1 += states + 1;
1242 }
1243 return 0;
1244}
1245
1246static struct var_t spk_vars[] = {
1247 /* bell must be first to set high limit */
16d35515
WH
1248 {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1249 {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1250 {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1251 {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1252 {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1253 {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1254 {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1255 {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1256 {SAY_CONTROL, TOGGLE_0},
1257 {SAY_WORD_CTL, TOGGLE_0},
1258 {NO_INTERRUPT, TOGGLE_0},
1259 {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
c6e3fd22
WH
1260 V_LAST_VAR
1261};
1262
c6e3fd22
WH
1263static void toggle_cursoring(struct vc_data *vc)
1264{
1265 if (cursor_track == read_all_mode)
1266 cursor_track = prev_cursor_track;
1267 if (++cursor_track >= CT_Max)
1268 cursor_track = 0;
ca2beaf8 1269 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
c6e3fd22
WH
1270}
1271
ca2beaf8 1272void spk_reset_default_chars(void)
c6e3fd22
WH
1273{
1274 int i;
1275
1276 /* First, free any non-default */
1277 for (i = 0; i < 256; i++) {
ca2beaf8
ST
1278 if ((spk_characters[i] != NULL)
1279 && (spk_characters[i] != spk_default_chars[i]))
1280 kfree(spk_characters[i]);
c6e3fd22
WH
1281 }
1282
ca2beaf8 1283 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
c6e3fd22
WH
1284}
1285
ca2beaf8 1286void spk_reset_default_chartab(void)
c6e3fd22
WH
1287{
1288 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1289}
1290
16d35515 1291static const struct st_bits_data *pb_edit;
c6e3fd22
WH
1292
1293static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1294{
1295 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
8e69a811 1296
16d35515 1297 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
c6e3fd22
WH
1298 return -1;
1299 if (ch == SPACE) {
ca2beaf8
ST
1300 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1301 spk_special_handler = NULL;
c6e3fd22
WH
1302 return 1;
1303 }
16d35515 1304 if (mask < PUNC && !(ch_type & PUNC))
c6e3fd22
WH
1305 return -1;
1306 spk_chartab[ch] ^= mask;
1307 speak_char(ch);
1308 synth_printf(" %s\n",
ca2beaf8
ST
1309 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1310 spk_msg_get(MSG_OFF));
c6e3fd22
WH
1311 return 1;
1312}
1313
1314/* Allocation concurrency is protected by the console semaphore */
0012196c 1315static int speakup_allocate(struct vc_data *vc)
c6e3fd22
WH
1316{
1317 int vc_num;
1318
1319 vc_num = vc->vc_num;
1320 if (speakup_console[vc_num] == NULL) {
1321 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
16d35515 1322 GFP_ATOMIC);
c6e3fd22 1323 if (speakup_console[vc_num] == NULL)
628f3428 1324 return -ENOMEM;
c6e3fd22
WH
1325 speakup_date(vc);
1326 } else if (!spk_parked)
1327 speakup_date(vc);
628f3428
CB
1328
1329 return 0;
c6e3fd22
WH
1330}
1331
0012196c 1332static void speakup_deallocate(struct vc_data *vc)
c6e3fd22
WH
1333{
1334 int vc_num;
1335
1336 vc_num = vc->vc_num;
39dd3e5d
IM
1337 kfree(speakup_console[vc_num]);
1338 speakup_console[vc_num] = NULL;
c6e3fd22
WH
1339}
1340
1341static u_char is_cursor;
1342static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1343static int cursor_con;
1344
1345static void reset_highlight_buffers(struct vc_data *);
1346
1347static int read_all_key;
1348
c6e3fd22
WH
1349static void start_read_all_timer(struct vc_data *vc, int command);
1350
1351enum {
1352 RA_NOTHING,
1353 RA_NEXT_SENT,
1354 RA_PREV_LINE,
1355 RA_NEXT_LINE,
1356 RA_PREV_SENT,
1357 RA_DOWN_ARROW,
1358 RA_TIMER,
1359 RA_FIND_NEXT_SENT,
1360 RA_FIND_PREV_SENT,
1361};
1362
16d35515 1363static void kbd_fakekey2(struct vc_data *vc, int command)
c6e3fd22
WH
1364{
1365 del_timer(&cursor_timer);
1366 speakup_fake_down_arrow();
1367 start_read_all_timer(vc, command);
1368}
1369
16d35515 1370static void read_all_doc(struct vc_data *vc)
c6e3fd22
WH
1371{
1372 if ((vc->vc_num != fg_console) || synth == NULL || spk_shut_up)
1373 return;
1374 if (!synth_supports_indexing())
1375 return;
1376 if (cursor_track != read_all_mode)
1377 prev_cursor_track = cursor_track;
1378 cursor_track = read_all_mode;
ca2beaf8 1379 spk_reset_index_count(0);
c6e3fd22
WH
1380 if (get_sentence_buf(vc, 0) == -1)
1381 kbd_fakekey2(vc, RA_DOWN_ARROW);
1382 else {
1383 say_sentence_num(0, 0);
1384 synth_insert_next_index(0);
1385 start_read_all_timer(vc, RA_TIMER);
1386 }
1387}
1388
16d35515 1389static void stop_read_all(struct vc_data *vc)
c6e3fd22
WH
1390{
1391 del_timer(&cursor_timer);
1392 cursor_track = prev_cursor_track;
1393 spk_shut_up &= 0xfe;
ca2beaf8 1394 spk_do_flush();
c6e3fd22
WH
1395}
1396
16d35515 1397static void start_read_all_timer(struct vc_data *vc, int command)
c6e3fd22
WH
1398{
1399 struct var_t *cursor_timeout;
1400
1401 cursor_con = vc->vc_num;
1402 read_all_key = command;
ca2beaf8 1403 cursor_timeout = spk_get_var(CURSOR_TIME);
16d35515
WH
1404 mod_timer(&cursor_timer,
1405 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
c6e3fd22
WH
1406}
1407
16d35515 1408static void handle_cursor_read_all(struct vc_data *vc, int command)
c6e3fd22
WH
1409{
1410 int indcount, sentcount, rv, sn;
1411
1412 switch (command) {
1413 case RA_NEXT_SENT:
1414 /* Get Current Sentence */
ca2beaf8 1415 spk_get_index_count(&indcount, &sentcount);
c6e3fd22 1416 /*printk("%d %d ", indcount, sentcount); */
ca2beaf8 1417 spk_reset_index_count(sentcount + 1);
c6e3fd22 1418 if (indcount == 1) {
16d35515 1419 if (!say_sentence_num(sentcount + 1, 0)) {
c6e3fd22
WH
1420 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1421 return;
1422 }
1423 synth_insert_next_index(0);
1424 } else {
1425 sn = 0;
16d35515 1426 if (!say_sentence_num(sentcount + 1, 1)) {
c6e3fd22 1427 sn = 1;
ca2beaf8 1428 spk_reset_index_count(sn);
c6e3fd22
WH
1429 } else
1430 synth_insert_next_index(0);
1431 if (!say_sentence_num(sn, 0)) {
1432 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1433 return;
1434 }
1435 synth_insert_next_index(0);
1436 }
1437 start_read_all_timer(vc, RA_TIMER);
1438 break;
1439 case RA_PREV_SENT:
1440 break;
1441 case RA_NEXT_LINE:
1442 read_all_doc(vc);
1443 break;
1444 case RA_PREV_LINE:
1445 break;
1446 case RA_DOWN_ARROW:
1447 if (get_sentence_buf(vc, 0) == -1) {
1448 kbd_fakekey2(vc, RA_DOWN_ARROW);
1449 } else {
1450 say_sentence_num(0, 0);
1451 synth_insert_next_index(0);
1452 start_read_all_timer(vc, RA_TIMER);
1453 }
1454 break;
1455 case RA_FIND_NEXT_SENT:
1456 rv = get_sentence_buf(vc, 0);
1457 if (rv == -1)
1458 read_all_doc(vc);
1459 if (rv == 0)
1460 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1461 else {
1462 say_sentence_num(1, 0);
1463 synth_insert_next_index(0);
1464 start_read_all_timer(vc, RA_TIMER);
1465 }
1466 break;
1467 case RA_FIND_PREV_SENT:
1468 break;
1469 case RA_TIMER:
ca2beaf8 1470 spk_get_index_count(&indcount, &sentcount);
c6e3fd22
WH
1471 if (indcount < 2)
1472 kbd_fakekey2(vc, RA_DOWN_ARROW);
1473 else
1474 start_read_all_timer(vc, RA_TIMER);
1475 break;
1476 }
1477}
1478
1479static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1480{
1481 unsigned long flags;
8e69a811 1482
3efe810f 1483 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1484 if (cursor_track == read_all_mode) {
1485 spk_parked &= 0xfe;
1486 if (synth == NULL || up_flag || spk_shut_up) {
3efe810f 1487 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1488 return NOTIFY_STOP;
1489 }
1490 del_timer(&cursor_timer);
1491 spk_shut_up &= 0xfe;
ca2beaf8 1492 spk_do_flush();
16d35515 1493 start_read_all_timer(vc, value + 1);
3efe810f 1494 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1495 return NOTIFY_STOP;
1496 }
3efe810f 1497 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1498 return NOTIFY_OK;
1499}
1500
1501static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1502{
1503 unsigned long flags;
1504 struct var_t *cursor_timeout;
1505
3efe810f 1506 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1507 spk_parked &= 0xfe;
1508 if (synth == NULL || up_flag || spk_shut_up || cursor_track == CT_Off) {
3efe810f 1509 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1510 return;
1511 }
1512 spk_shut_up &= 0xfe;
ca2beaf8
ST
1513 if (spk_no_intr)
1514 spk_do_flush();
c6e3fd22 1515/* the key press flushes if !no_inter but we want to flush on cursor
13d825ed
AF
1516 * moves regardless of no_inter state
1517 */
c6e3fd22
WH
1518 is_cursor = value + 1;
1519 old_cursor_pos = vc->vc_pos;
1520 old_cursor_x = vc->vc_x;
1521 old_cursor_y = vc->vc_y;
1522 speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
1523 cursor_con = vc->vc_num;
1524 if (cursor_track == CT_Highlight)
1525 reset_highlight_buffers(vc);
ca2beaf8 1526 cursor_timeout = spk_get_var(CURSOR_TIME);
16d35515
WH
1527 mod_timer(&cursor_timer,
1528 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
3efe810f 1529 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1530}
1531
16d35515 1532static void update_color_buffer(struct vc_data *vc, const char *ic, int len)
c6e3fd22
WH
1533{
1534 int i, bi, hi;
1535 int vc_num = vc->vc_num;
1536
6de3f58b 1537 bi = (vc->vc_attr & 0x70) >> 4;
c6e3fd22
WH
1538 hi = speakup_console[vc_num]->ht.highsize[bi];
1539
1540 i = 0;
1541 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1542 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1543 speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
1544 speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
1545 }
1546 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1547 if ((ic[i] > 32) && (ic[i] < 127)) {
1548 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1549 hi++;
1550 } else if ((ic[i] == 32) && (hi != 0)) {
16d35515
WH
1551 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1552 32) {
c6e3fd22 1553 speakup_console[vc_num]->ht.highbuf[bi][hi] =
16d35515 1554 ic[i];
c6e3fd22
WH
1555 hi++;
1556 }
1557 }
1558 i++;
1559 }
1560 speakup_console[vc_num]->ht.highsize[bi] = hi;
1561}
1562
16d35515 1563static void reset_highlight_buffers(struct vc_data *vc)
c6e3fd22
WH
1564{
1565 int i;
1566 int vc_num = vc->vc_num;
8e69a811 1567
16d35515 1568 for (i = 0; i < 8; i++)
c6e3fd22
WH
1569 speakup_console[vc_num]->ht.highsize[i] = 0;
1570}
1571
16d35515 1572static int count_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1573{
1574 int i, bg;
1575 int cc;
1576 int vc_num = vc->vc_num;
1577 u16 ch;
1578 u16 *start = (u16 *) vc->vc_origin;
1579
1580 for (i = 0; i < 8; i++)
1581 speakup_console[vc_num]->ht.bgcount[i] = 0;
1582
1583 for (i = 0; i < vc->vc_rows; i++) {
16d35515 1584 u16 *end = start + vc->vc_cols * 2;
c6e3fd22 1585 u16 *ptr;
8e69a811 1586
c6e3fd22
WH
1587 for (ptr = start; ptr < end; ptr++) {
1588 ch = get_attributes(ptr);
1589 bg = (ch & 0x70) >> 4;
1590 speakup_console[vc_num]->ht.bgcount[bg]++;
1591 }
1592 start += vc->vc_size_row;
1593 }
1594
1595 cc = 0;
1596 for (i = 0; i < 8; i++)
1597 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1598 cc++;
1599 return cc;
1600}
1601
16d35515 1602static int get_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1603{
1604 int i, j;
590aeb17 1605 unsigned int cptr[8];
c6e3fd22
WH
1606 int vc_num = vc->vc_num;
1607
1608 for (i = 0; i < 8; i++)
1609 cptr[i] = i;
1610
1611 for (i = 0; i < 7; i++)
1612 for (j = i + 1; j < 8; j++)
1613 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
590aeb17
FF
1614 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1615 swap(cptr[i], cptr[j]);
c6e3fd22
WH
1616
1617 for (i = 0; i < 8; i++)
1618 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1619 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1620 return cptr[i];
1621 return -1;
1622}
1623
16d35515 1624static int speak_highlight(struct vc_data *vc)
c6e3fd22
WH
1625{
1626 int hc, d;
1627 int vc_num = vc->vc_num;
8e69a811 1628
c6e3fd22
WH
1629 if (count_highlight_color(vc) == 1)
1630 return 0;
1631 hc = get_highlight_color(vc);
1632 if (hc != -1) {
16d35515 1633 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
c6e3fd22
WH
1634 if ((d == 1) || (d == -1))
1635 if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
1636 return 0;
1637 spk_parked |= 0x01;
ca2beaf8 1638 spk_do_flush();
c6e3fd22 1639 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
16d35515 1640 speakup_console[vc_num]->ht.highsize[hc]);
c6e3fd22
WH
1641 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1642 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1643 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1644 return 1;
1645 }
1646 return 0;
1647}
1648
16d35515 1649static void cursor_done(u_long data)
c6e3fd22
WH
1650{
1651 struct vc_data *vc = vc_cons[cursor_con].d;
1652 unsigned long flags;
8e69a811 1653
c6e3fd22 1654 del_timer(&cursor_timer);
3efe810f 1655 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1656 if (cursor_con != fg_console) {
1657 is_cursor = 0;
1658 goto out;
1659 }
1660 speakup_date(vc);
1661 if (win_enabled) {
1662 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1663 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
c6e3fd22
WH
1664 spk_keydown = is_cursor = 0;
1665 goto out;
1666 }
1667 }
1668 if (cursor_track == read_all_mode) {
1669 handle_cursor_read_all(vc, read_all_key);
1670 goto out;
1671 }
1672 if (cursor_track == CT_Highlight) {
1673 if (speak_highlight(vc)) {
1674 spk_keydown = is_cursor = 0;
1675 goto out;
1676 }
1677 }
1678 if (cursor_track == CT_Window)
1679 speakup_win_say(vc);
1680 else if (is_cursor == 1 || is_cursor == 4)
1681 say_line_from_to(vc, 0, vc->vc_cols, 0);
1682 else
1683 say_char(vc);
1684 spk_keydown = is_cursor = 0;
1685out:
3efe810f 1686 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1687}
1688
1689/* called by: vt_notifier_call() */
1690static void speakup_bs(struct vc_data *vc)
1691{
1692 unsigned long flags;
8e69a811 1693
c6e3fd22
WH
1694 if (!speakup_console[vc->vc_num])
1695 return;
3efe810f 1696 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1697 /* Speakup output, discard */
1698 return;
1699 if (!spk_parked)
1700 speakup_date(vc);
1701 if (spk_shut_up || synth == NULL) {
3efe810f 1702 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1703 return;
1704 }
1705 if (vc->vc_num == fg_console && spk_keydown) {
1706 spk_keydown = 0;
1707 if (!is_cursor)
1708 say_char(vc);
1709 }
3efe810f 1710 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1711}
1712
1713/* called by: vt_notifier_call() */
1714static void speakup_con_write(struct vc_data *vc, const char *str, int len)
1715{
1716 unsigned long flags;
8e69a811 1717
c6e3fd22
WH
1718 if ((vc->vc_num != fg_console) || spk_shut_up || synth == NULL)
1719 return;
3efe810f 1720 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1721 /* Speakup output, discard */
1722 return;
ca2beaf8 1723 if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
c6e3fd22
WH
1724 bleep(3);
1725 if ((is_cursor) || (cursor_track == read_all_mode)) {
1726 if (cursor_track == CT_Highlight)
1727 update_color_buffer(vc, str, len);
3efe810f 1728 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1729 return;
1730 }
1731 if (win_enabled) {
1732 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1733 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
3efe810f 1734 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1735 return;
1736 }
1737 }
1738
1739 spkup_write(str, len);
3efe810f 1740 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1741}
1742
0012196c 1743static void speakup_con_update(struct vc_data *vc)
c6e3fd22
WH
1744{
1745 unsigned long flags;
8e69a811 1746
c6e3fd22
WH
1747 if (speakup_console[vc->vc_num] == NULL || spk_parked)
1748 return;
3efe810f 1749 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1750 /* Speakup output, discard */
1751 return;
1752 speakup_date(vc);
3efe810f 1753 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1754}
1755
1756static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1757{
1758 unsigned long flags;
1759 int on_off = 2;
1760 char *label;
8e69a811 1761
c6e3fd22
WH
1762 if (synth == NULL || up_flag || spk_killed)
1763 return;
3efe810f 1764 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22 1765 spk_shut_up &= 0xfe;
ca2beaf8
ST
1766 if (spk_no_intr)
1767 spk_do_flush();
c6e3fd22
WH
1768 switch (value) {
1769 case KVAL(K_CAPS):
ca2beaf8 1770 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
079c9534 1771 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
c6e3fd22
WH
1772 break;
1773 case KVAL(K_NUM):
ca2beaf8 1774 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
079c9534 1775 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
c6e3fd22
WH
1776 break;
1777 case KVAL(K_HOLD):
ca2beaf8 1778 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
079c9534 1779 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
c6e3fd22
WH
1780 if (speakup_console[vc->vc_num])
1781 speakup_console[vc->vc_num]->tty_stopped = on_off;
1782 break;
1783 default:
1784 spk_parked &= 0xfe;
3efe810f 1785 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1786 return;
1787 }
1788 if (on_off < 2)
1789 synth_printf("%s %s\n",
ca2beaf8 1790 label, spk_msg_get(MSG_STATUS_START + on_off));
3efe810f 1791 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1792}
1793
16d35515 1794static int inc_dec_var(u_char value)
c6e3fd22
WH
1795{
1796 struct st_var_header *p_header;
1797 struct var_t *var_data;
1798 char num_buf[32];
1799 char *cp = num_buf;
1800 char *pn;
1801 int var_id = (int)value - VAR_START;
16d35515 1802 int how = (var_id & 1) ? E_INC : E_DEC;
8e69a811 1803
16d35515 1804 var_id = var_id / 2 + FIRST_SET_VAR;
ca2beaf8 1805 p_header = spk_get_var_header(var_id);
c6e3fd22
WH
1806 if (p_header == NULL)
1807 return -1;
1808 if (p_header->var_type != VAR_NUM)
1809 return -1;
1810 var_data = p_header->data;
ca2beaf8 1811 if (spk_set_num_var(1, p_header, how) != 0)
c6e3fd22
WH
1812 return -1;
1813 if (!spk_close_press) {
1814 for (pn = p_header->name; *pn; pn++) {
1815 if (*pn == '_')
1816 *cp = SPACE;
1817 else
1818 *cp++ = *pn;
1819 }
1820 }
1821 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
16d35515 1822 var_data->u.n.value);
c6e3fd22
WH
1823 synth_printf("%s", num_buf);
1824 return 0;
1825}
1826
16d35515 1827static void speakup_win_set(struct vc_data *vc)
c6e3fd22
WH
1828{
1829 char info[40];
8e69a811 1830
c6e3fd22 1831 if (win_start > 1) {
ca2beaf8 1832 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
c6e3fd22
WH
1833 return;
1834 }
1835 if (spk_x < win_left || spk_y < win_top) {
ca2beaf8 1836 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
c6e3fd22
WH
1837 return;
1838 }
1839 if (win_start && spk_x == win_left && spk_y == win_top) {
1840 win_left = 0;
16d35515 1841 win_right = vc->vc_cols - 1;
c6e3fd22 1842 win_bottom = spk_y;
ca2beaf8 1843 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
16d35515 1844 (int)win_top + 1);
c6e3fd22
WH
1845 } else {
1846 if (!win_start) {
1847 win_top = spk_y;
1848 win_left = spk_x;
1849 } else {
1850 win_bottom = spk_y;
1851 win_right = spk_x;
1852 }
ca2beaf8 1853 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
63b8ebe4
SG
1854 (win_start) ?
1855 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
16d35515 1856 (int)spk_y + 1, (int)spk_x + 1);
c6e3fd22
WH
1857 }
1858 synth_printf("%s\n", info);
1859 win_start++;
1860}
1861
16d35515 1862static void speakup_win_clear(struct vc_data *vc)
c6e3fd22
WH
1863{
1864 win_top = win_bottom = 0;
1865 win_left = win_right = 0;
1866 win_start = 0;
ca2beaf8 1867 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
c6e3fd22
WH
1868}
1869
16d35515 1870static void speakup_win_enable(struct vc_data *vc)
c6e3fd22
WH
1871{
1872 if (win_start < 2) {
ca2beaf8 1873 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
c6e3fd22
WH
1874 return;
1875 }
1876 win_enabled ^= 1;
1877 if (win_enabled)
ca2beaf8 1878 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
c6e3fd22 1879 else
ca2beaf8 1880 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
c6e3fd22
WH
1881}
1882
16d35515 1883static void speakup_bits(struct vc_data *vc)
c6e3fd22
WH
1884{
1885 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
8e69a811 1886
ca2beaf8
ST
1887 if (spk_special_handler != NULL || val < 1 || val > 6) {
1888 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
c6e3fd22
WH
1889 return;
1890 }
ca2beaf8
ST
1891 pb_edit = &spk_punc_info[val];
1892 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1893 spk_special_handler = edit_bits;
c6e3fd22
WH
1894}
1895
1896static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1897{
4ea418b8 1898 static u_char goto_buf[8];
16d35515 1899 static int num;
ef35a4f4 1900 int maxlen;
c6e3fd22 1901 char *cp;
ef35a4f4 1902
c6e3fd22
WH
1903 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1904 goto do_goto;
1905 if (type == KT_LATIN && ch == '\n')
1906 goto do_goto;
1907 if (type != 0)
1908 goto oops;
1909 if (ch == 8) {
1910 if (num == 0)
1911 return -1;
1912 ch = goto_buf[--num];
1913 goto_buf[num] = '\0';
1914 spkup_write(&ch, 1);
1915 return 1;
1916 }
1917 if (ch < '+' || ch > 'y')
1918 goto oops;
1919 goto_buf[num++] = ch;
1920 goto_buf[num] = '\0';
1921 spkup_write(&ch, 1);
1922 maxlen = (*goto_buf >= '0') ? 3 : 4;
1923 if ((ch == '+' || ch == '-') && num == 1)
1924 return 1;
1925 if (ch >= '0' && ch <= '9' && num < maxlen)
1926 return 1;
16d35515 1927 if (num < maxlen - 1 || num > maxlen)
c6e3fd22
WH
1928 goto oops;
1929 if (ch < 'x' || ch > 'y') {
1930oops:
1931 if (!spk_killed)
ca2beaf8 1932 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
c6e3fd22 1933 goto_buf[num = 0] = '\0';
ca2beaf8 1934 spk_special_handler = NULL;
c6e3fd22
WH
1935 return 1;
1936 }
ef35a4f4
DY
1937
1938 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1939
c6e3fd22
WH
1940 if (*cp == 'x') {
1941 if (*goto_buf < '0')
1942 goto_pos += spk_x;
ef35a4f4 1943 else if (goto_pos > 0)
c6e3fd22 1944 goto_pos--;
ef35a4f4 1945
c6e3fd22 1946 if (goto_pos >= vc->vc_cols)
16d35515 1947 goto_pos = vc->vc_cols - 1;
c6e3fd22
WH
1948 goto_x = 1;
1949 } else {
1950 if (*goto_buf < '0')
1951 goto_pos += spk_y;
ef35a4f4 1952 else if (goto_pos > 0)
c6e3fd22 1953 goto_pos--;
ef35a4f4 1954
c6e3fd22 1955 if (goto_pos >= vc->vc_rows)
16d35515 1956 goto_pos = vc->vc_rows - 1;
c6e3fd22
WH
1957 goto_x = 0;
1958 }
16d35515 1959 goto_buf[num = 0] = '\0';
c6e3fd22 1960do_goto:
ca2beaf8 1961 spk_special_handler = NULL;
c6e3fd22
WH
1962 spk_parked |= 0x01;
1963 if (goto_x) {
1964 spk_pos -= spk_x * 2;
1965 spk_x = goto_pos;
1966 spk_pos += goto_pos * 2;
1967 say_word(vc);
1968 } else {
1969 spk_y = goto_pos;
1970 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
1971 say_line(vc);
1972 }
1973 return 1;
1974}
1975
16d35515 1976static void speakup_goto(struct vc_data *vc)
c6e3fd22 1977{
ca2beaf8
ST
1978 if (spk_special_handler != NULL) {
1979 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
c6e3fd22
WH
1980 return;
1981 }
ca2beaf8
ST
1982 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
1983 spk_special_handler = handle_goto;
c6e3fd22
WH
1984}
1985
1986static void speakup_help(struct vc_data *vc)
1987{
ca2beaf8 1988 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
c6e3fd22
WH
1989}
1990
16d35515 1991static void do_nothing(struct vc_data *vc)
c6e3fd22 1992{
16d35515 1993 return; /* flush done in do_spkup */
c6e3fd22 1994}
16d35515 1995
c6e3fd22
WH
1996static u_char key_speakup, spk_key_locked;
1997
16d35515 1998static void speakup_lock(struct vc_data *vc)
c6e3fd22
WH
1999{
2000 if (!spk_key_locked)
2001 spk_key_locked = key_speakup = 16;
2002 else
2003 spk_key_locked = key_speakup = 0;
2004}
2005
16d35515 2006typedef void (*spkup_hand) (struct vc_data *);
0012196c 2007static spkup_hand spkup_handler[] = {
c6e3fd22
WH
2008 /* must be ordered same as defines in speakup.h */
2009 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2010 speakup_cut, speakup_paste, say_first_char, say_last_char,
2011 say_char, say_prev_char, say_next_char,
2012 say_word, say_prev_word, say_next_word,
2013 say_line, say_prev_line, say_next_line,
2014 top_edge, bottom_edge, left_edge, right_edge,
2015 spell_word, spell_word, say_screen,
2016 say_position, say_attributes,
16d35515 2017 speakup_off, speakup_parked, say_line, /* this is for indent */
c6e3fd22
WH
2018 say_from_top, say_to_bottom,
2019 say_from_left, say_to_right,
2020 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2021 speakup_bits, speakup_bits, speakup_bits,
2022 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2023 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2024};
2025
2026static void do_spkup(struct vc_data *vc, u_char value)
2027{
2028 if (spk_killed && value != SPEECH_KILL)
2029 return;
2030 spk_keydown = 0;
2031 spk_lastkey = 0;
2032 spk_shut_up &= 0xfe;
2033 this_speakup_key = value;
2034 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
ca2beaf8 2035 spk_do_flush();
16d35515 2036 (*spkup_handler[value]) (vc);
c6e3fd22
WH
2037 } else {
2038 if (inc_dec_var(value) < 0)
2039 bleep(9);
2040 }
2041}
2042
2043static const char *pad_chars = "0123456789+-*/\015,.?()";
2044
0012196c 2045static int
c6e3fd22 2046speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
16d35515 2047 int up_flag)
c6e3fd22
WH
2048{
2049 unsigned long flags;
2050 int kh;
2051 u_char *key_info;
2052 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2053 u_char shift_info, offset;
2054 int ret = 0;
8e69a811 2055
c6e3fd22
WH
2056 if (synth == NULL)
2057 return 0;
2058
3efe810f 2059 spin_lock_irqsave(&speakup_info.spinlock, flags);
5b19208a 2060 tty = vc->port.tty;
c6e3fd22
WH
2061 if (type >= 0xf0)
2062 type -= 0xf0;
16d35515 2063 if (type == KT_PAD
079c9534 2064 && (vt_get_leds(fg_console, VC_NUMLOCK))) {
c6e3fd22
WH
2065 if (up_flag) {
2066 spk_keydown = 0;
2067 goto out;
2068 }
2069 value = spk_lastkey = pad_chars[value];
2070 spk_keydown++;
2071 spk_parked &= 0xfe;
2072 goto no_map;
2073 }
2074 if (keycode >= MAX_KEY)
2075 goto no_map;
ca2beaf8 2076 key_info = spk_our_keys[keycode];
ff471ea8 2077 if (!key_info)
c6e3fd22
WH
2078 goto no_map;
2079 /* Check valid read all mode keys */
2080 if ((cursor_track == read_all_mode) && (!up_flag)) {
2081 switch (value) {
2082 case KVAL(K_DOWN):
2083 case KVAL(K_UP):
2084 case KVAL(K_LEFT):
2085 case KVAL(K_RIGHT):
2086 case KVAL(K_PGUP):
2087 case KVAL(K_PGDN):
2088 break;
2089 default:
2090 stop_read_all(vc);
2091 break;
2092 }
2093 }
16d35515 2094 shift_info = (shift_state & 0x0f) + key_speakup;
ca2beaf8 2095 offset = spk_shift_table[shift_info];
c6e3fd22
WH
2096 if (offset) {
2097 new_key = key_info[offset];
2098 if (new_key) {
2099 ret = 1;
2100 if (new_key == SPK_KEY) {
2101 if (!spk_key_locked)
2102 key_speakup = (up_flag) ? 0 : 16;
2103 if (up_flag || spk_killed)
2104 goto out;
2105 spk_shut_up &= 0xfe;
ca2beaf8 2106 spk_do_flush();
c6e3fd22
WH
2107 goto out;
2108 }
2109 if (up_flag)
2110 goto out;
2111 if (last_keycode == keycode &&
3d3cb1bf 2112 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
c6e3fd22 2113 spk_close_press = 1;
ca2beaf8 2114 offset = spk_shift_table[shift_info + 32];
16d35515 2115 /* double press? */
c6e3fd22
WH
2116 if (offset && key_info[offset])
2117 new_key = key_info[offset];
2118 }
2119 last_keycode = keycode;
2120 last_spk_jiffy = jiffies;
2121 type = KT_SPKUP;
2122 value = new_key;
2123 }
2124 }
2125no_map:
ca2beaf8 2126 if (type == KT_SPKUP && spk_special_handler == NULL) {
c6e3fd22
WH
2127 do_spkup(vc, new_key);
2128 spk_close_press = 0;
2129 ret = 1;
2130 goto out;
2131 }
2132 if (up_flag || spk_killed || type == KT_SHIFT)
2133 goto out;
2134 spk_shut_up &= 0xfe;
2135 kh = (value == KVAL(K_DOWN))
16d35515
WH
2136 || (value == KVAL(K_UP))
2137 || (value == KVAL(K_LEFT))
2138 || (value == KVAL(K_RIGHT));
c6e3fd22 2139 if ((cursor_track != read_all_mode) || !kh)
ca2beaf8
ST
2140 if (!spk_no_intr)
2141 spk_do_flush();
2142 if (spk_special_handler) {
c6e3fd22
WH
2143 if (type == KT_SPEC && value == 1) {
2144 value = '\n';
2145 type = KT_LATIN;
2146 } else if (type == KT_LETTER)
2147 type = KT_LATIN;
2148 else if (value == 0x7f)
16d35515 2149 value = 8; /* make del = backspace */
ca2beaf8 2150 ret = (*spk_special_handler) (vc, type, value, keycode);
c6e3fd22
WH
2151 spk_close_press = 0;
2152 if (ret < 0)
2153 bleep(9);
2154 goto out;
2155 }
2156 last_keycode = 0;
2157out:
3efe810f 2158 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
2159 return ret;
2160}
2161
2162static int keyboard_notifier_call(struct notifier_block *nb,
16d35515 2163 unsigned long code, void *_param)
c6e3fd22
WH
2164{
2165 struct keyboard_notifier_param *param = _param;
2166 struct vc_data *vc = param->vc;
2167 int up = !param->down;
2168 int ret = NOTIFY_OK;
16d35515 2169 static int keycode; /* to hold the current keycode */
c6e3fd22
WH
2170
2171 if (vc->vc_mode == KD_GRAPHICS)
2172 return ret;
2173
2174 /*
2175 * First, determine whether we are handling a fake keypress on
2176 * the current processor. If we are, then return NOTIFY_OK,
2177 * to pass the keystroke up the chain. This prevents us from
2178 * trying to take the Speakup lock while it is held by the
2179 * processor on which the simulated keystroke was generated.
2180 * Also, the simulated keystrokes should be ignored by Speakup.
2181 */
2182
2183 if (speakup_fake_key_pressed())
2184 return ret;
2185
2186 switch (code) {
2187 case KBD_KEYCODE:
2188 /* speakup requires keycode and keysym currently */
2189 keycode = param->value;
2190 break;
2191 case KBD_UNBOUND_KEYCODE:
2192 /* not used yet */
2193 break;
2194 case KBD_UNICODE:
2195 /* not used yet */
2196 break;
2197 case KBD_KEYSYM:
2198 if (speakup_key(vc, param->shift, keycode, param->value, up))
2199 ret = NOTIFY_STOP;
16d35515
WH
2200 else if (KTYP(param->value) == KT_CUR)
2201 ret = pre_handle_cursor(vc, KVAL(param->value), up);
c6e3fd22 2202 break;
16d35515
WH
2203 case KBD_POST_KEYSYM:{
2204 unsigned char type = KTYP(param->value) - 0xf0;
2205 unsigned char val = KVAL(param->value);
8e69a811 2206
16d35515
WH
2207 switch (type) {
2208 case KT_SHIFT:
2209 do_handle_shift(vc, val, up);
2210 break;
2211 case KT_LATIN:
2212 case KT_LETTER:
2213 do_handle_latin(vc, val, up);
2214 break;
2215 case KT_CUR:
2216 do_handle_cursor(vc, val, up);
2217 break;
2218 case KT_SPEC:
2219 do_handle_spec(vc, val, up);
2220 break;
2221 }
c6e3fd22
WH
2222 break;
2223 }
c6e3fd22
WH
2224 }
2225 return ret;
2226}
2227
2228static int vt_notifier_call(struct notifier_block *nb,
16d35515 2229 unsigned long code, void *_param)
c6e3fd22
WH
2230{
2231 struct vt_notifier_param *param = _param;
2232 struct vc_data *vc = param->vc;
8e69a811 2233
c6e3fd22
WH
2234 switch (code) {
2235 case VT_ALLOCATE:
2236 if (vc->vc_mode == KD_TEXT)
2237 speakup_allocate(vc);
2238 break;
2239 case VT_DEALLOCATE:
2240 speakup_deallocate(vc);
2241 break;
2242 case VT_WRITE:
2243 if (param->c == '\b')
2244 speakup_bs(vc);
16d35515
WH
2245 else if (param->c < 0x100) {
2246 char d = param->c;
0a3a725a 2247
16d35515
WH
2248 speakup_con_write(vc, &d, 1);
2249 }
c6e3fd22
WH
2250 break;
2251 case VT_UPDATE:
2252 speakup_con_update(vc);
2253 break;
2254 }
2255 return NOTIFY_OK;
2256}
2257
2258/* called by: module_exit() */
2259static void __exit speakup_exit(void)
2260{
2261 int i;
2262
c6e3fd22
WH
2263 unregister_keyboard_notifier(&keyboard_notifier_block);
2264 unregister_vt_notifier(&vt_notifier_block);
2265 speakup_unregister_devsynth();
d7500135 2266 speakup_cancel_paste();
c6e3fd22 2267 del_timer(&cursor_timer);
c6e3fd22
WH
2268 kthread_stop(speakup_task);
2269 speakup_task = NULL;
2270 mutex_lock(&spk_mutex);
2271 synth_release();
2272 mutex_unlock(&spk_mutex);
2273
628f3428
CB
2274 speakup_kobj_exit();
2275
2276 for (i = 0; i < MAX_NR_CONSOLES; i++)
2277 kfree(speakup_console[i]);
2278
2279 speakup_remove_virtual_keyboard();
2280
c6e3fd22
WH
2281 for (i = 0; i < MAXVARS; i++)
2282 speakup_unregister_var(i);
2283
2284 for (i = 0; i < 256; i++) {
ca2beaf8
ST
2285 if (spk_characters[i] != spk_default_chars[i])
2286 kfree(spk_characters[i]);
c6e3fd22 2287 }
628f3428 2288
ca2beaf8 2289 spk_free_user_msgs();
c6e3fd22
WH
2290}
2291
2292/* call by: module_init() */
2293static int __init speakup_init(void)
2294{
2295 int i;
628f3428 2296 long err = 0;
c6e3fd22
WH
2297 struct st_spk_t *first_console;
2298 struct vc_data *vc = vc_cons[fg_console].d;
2299 struct var_t *var;
2300
628f3428 2301 /* These first few initializations cannot fail. */
ca2beaf8
ST
2302 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2303 spk_reset_default_chars();
2304 spk_reset_default_chartab();
2305 spk_strlwr(synth_name);
c6e3fd22 2306 spk_vars[0].u.n.high = vc->vc_cols;
16d35515 2307 for (var = spk_vars; var->var_id != MAXVARS; var++)
c6e3fd22 2308 speakup_register_var(var);
16d35515
WH
2309 for (var = synth_time_vars;
2310 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
c6e3fd22 2311 speakup_register_var(var);
ca2beaf8 2312 for (i = 1; spk_punc_info[i].mask != 0; i++)
ff471ea8 2313 spk_set_mask_bits(NULL, i, 2);
c6e3fd22 2314
ca2beaf8 2315 spk_set_key_info(spk_key_defaults, spk_key_buf);
c6e3fd22 2316
628f3428
CB
2317 /* From here on out, initializations can fail. */
2318 err = speakup_add_virtual_keyboard();
2319 if (err)
2320 goto error_virtkeyboard;
2321
2322 first_console = kzalloc(sizeof(*first_console), GFP_KERNEL);
2323 if (!first_console) {
2324 err = -ENOMEM;
2325 goto error_alloc;
2326 }
2327
2328 speakup_console[vc->vc_num] = first_console;
2329 speakup_date(vc);
2330
c6e3fd22 2331 for (i = 0; i < MAX_NR_CONSOLES; i++)
628f3428
CB
2332 if (vc_cons[i].d) {
2333 err = speakup_allocate(vc_cons[i].d);
2334 if (err)
2335 goto error_kobjects;
2336 }
2337
ca2beaf8 2338 if (spk_quiet_boot)
4afaee15
CB
2339 spk_shut_up |= 0x01;
2340
628f3428
CB
2341 err = speakup_kobj_init();
2342 if (err)
2343 goto error_kobjects;
c6e3fd22 2344
c6e3fd22
WH
2345 synth_init(synth_name);
2346 speakup_register_devsynth();
628f3428
CB
2347 /*
2348 * register_devsynth might fail, but this error is not fatal.
2349 * /dev/synth is an extra feature; the rest of Speakup
2350 * will work fine without it.
2351 */
c6e3fd22 2352
628f3428
CB
2353 err = register_keyboard_notifier(&keyboard_notifier_block);
2354 if (err)
2355 goto error_kbdnotifier;
2356 err = register_vt_notifier(&vt_notifier_block);
2357 if (err)
2358 goto error_vtnotifier;
c6e3fd22
WH
2359
2360 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
628f3428 2361
7959d556 2362 if (IS_ERR(speakup_task)) {
628f3428
CB
2363 err = PTR_ERR(speakup_task);
2364 goto error_task;
7959d556 2365 }
628f3428
CB
2366
2367 set_user_nice(speakup_task, 10);
7959d556 2368 wake_up_process(speakup_task);
628f3428
CB
2369
2370 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2371 pr_info("synth name on entry is: %s\n", synth_name);
7959d556
WH
2372 goto out;
2373
628f3428
CB
2374error_task:
2375 unregister_vt_notifier(&vt_notifier_block);
2376
2377error_vtnotifier:
2378 unregister_keyboard_notifier(&keyboard_notifier_block);
2379 del_timer(&cursor_timer);
2380
2381error_kbdnotifier:
2382 speakup_unregister_devsynth();
2383 mutex_lock(&spk_mutex);
2384 synth_release();
2385 mutex_unlock(&spk_mutex);
2386 speakup_kobj_exit();
2387
2388error_kobjects:
2389 for (i = 0; i < MAX_NR_CONSOLES; i++)
2390 kfree(speakup_console[i]);
2391
2392error_alloc:
7959d556 2393 speakup_remove_virtual_keyboard();
628f3428
CB
2394
2395error_virtkeyboard:
2396 for (i = 0; i < MAXVARS; i++)
2397 speakup_unregister_var(i);
2398
2399 for (i = 0; i < 256; i++) {
ca2beaf8
ST
2400 if (spk_characters[i] != spk_default_chars[i])
2401 kfree(spk_characters[i]);
628f3428
CB
2402 }
2403
ca2beaf8 2404 spk_free_user_msgs();
628f3428 2405
7959d556
WH
2406out:
2407 return err;
c6e3fd22
WH
2408}
2409
c6e3fd22
WH
2410module_init(speakup_init);
2411module_exit(speakup_exit);