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