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