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