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