root/drivers/staging/speakup/main.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_attributes
  2. speakup_date
  3. bleep
  4. speakup_shut_up
  5. speech_kill
  6. speakup_off
  7. speakup_parked
  8. speakup_cut
  9. speakup_paste
  10. say_attributes
  11. announce_edge
  12. speak_char
  13. get_char
  14. say_char
  15. say_phonetic_char
  16. say_prev_char
  17. say_next_char
  18. get_word
  19. say_word
  20. say_prev_word
  21. say_next_word
  22. spell_word
  23. get_line
  24. say_line
  25. say_prev_line
  26. say_next_line
  27. say_from_to
  28. say_line_from_to
  29. say_sentence_num
  30. get_sentence_buf
  31. say_screen_from_to
  32. say_screen
  33. speakup_win_say
  34. top_edge
  35. bottom_edge
  36. left_edge
  37. right_edge
  38. say_first_char
  39. say_last_char
  40. say_position
  41. say_char_num
  42. say_from_top
  43. say_to_bottom
  44. say_from_left
  45. say_to_right
  46. spkup_write
  47. do_handle_shift
  48. do_handle_latin
  49. spk_set_key_info
  50. toggle_cursoring
  51. spk_reset_default_chars
  52. spk_reset_default_chartab
  53. edit_bits
  54. speakup_allocate
  55. speakup_deallocate
  56. kbd_fakekey2
  57. read_all_doc
  58. stop_read_all
  59. start_read_all_timer
  60. handle_cursor_read_all
  61. pre_handle_cursor
  62. do_handle_cursor
  63. update_color_buffer
  64. reset_highlight_buffers
  65. count_highlight_color
  66. get_highlight_color
  67. speak_highlight
  68. cursor_done
  69. speakup_bs
  70. speakup_con_write
  71. speakup_con_update
  72. do_handle_spec
  73. inc_dec_var
  74. speakup_win_set
  75. speakup_win_clear
  76. speakup_win_enable
  77. speakup_bits
  78. handle_goto
  79. speakup_goto
  80. speakup_help
  81. do_nothing
  82. speakup_lock
  83. do_spkup
  84. speakup_key
  85. keyboard_notifier_call
  86. vt_notifier_call
  87. speakup_exit
  88. speakup_init

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

/* [<][>][^][v][top][bottom][index][help] */