#include <linux/kernel.h>
#include <linux/vt.h>
#include <linux/tty.h>
#include <linux/mm.h> /* __get_free_page() and friends */
#include <linux/vt_kern.h>
#include <linux/ctype.h>
#include <linux/selection.h>
#include <linux/unistd.h>
#include <linux/jiffies.h>
#include <linux/kthread.h>
#include <linux/keyboard.h> /* for KT_SHIFT */
#include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
#include <linux/input.h>
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/consolemap.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/uaccess.h> /* copy_from|to|user() and others */
#include "spk_priv.h"
#include "speakup.h"
#define MAX_DELAY msecs_to_jiffies(500)
#define MINECHOCHAR SPACE
MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
MODULE_DESCRIPTION("Speakup console speech");
MODULE_LICENSE("GPL");
MODULE_VERSION(SPEAKUP_VERSION);
char *synth_name;
module_param_named(synth, synth_name, charp, 0444);
module_param_named(quiet, spk_quiet_boot, bool, 0444);
MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
special_func spk_special_handler;
short spk_pitch_shift, synth_flags;
static u16 buf[256];
int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
int spk_no_intr, spk_spell_delay;
int spk_key_echo, spk_say_word_ctl;
int spk_say_ctrl, spk_bell_pos;
short spk_punc_mask;
int spk_punc_level, spk_reading_punc;
int spk_cur_phonetic;
char spk_str_caps_start[MAXVARLEN + 1] = "\0";
char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
char spk_str_pause[MAXVARLEN + 1] = "\0";
bool spk_paused;
const struct st_bits_data spk_punc_info[] = {
{"none", "", 0},
{"some", "/$%&@", SOME},
{"most", "$%&#()=+*/@^<>|\\", MOST},
{"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
{"delimiters", "", B_WDLM},
{"repeats", "()", CH_RPT},
{"extended numeric", "", B_EXNUM},
{"symbols", "", B_SYM},
{NULL, NULL}
};
static char mark_cut_flag;
#define MAX_KEY 160
static u_char *spk_shift_table;
u_char *spk_our_keys[MAX_KEY];
u_char spk_key_buf[600];
const u_char spk_key_defaults[] = {
#include "speakupmap.h"
};
enum cursor_track {
CT_Off = 0,
CT_On,
CT_Highlight,
CT_Window,
CT_Max,
read_all_mode = CT_Max,
};
static enum cursor_track cursor_track = 1, prev_cursor_track = 1;
static struct tty_struct *tty;
static void spkup_write(const u16 *in_buf, int count);
static char *phonetic[] = {
"alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
"india", "juliett", "keelo", "leema", "mike", "november", "oscar",
"papa",
"keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
"x ray", "yankee", "zulu"
};
char *spk_characters[256];
char *spk_default_chars[256] = {
"null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
"^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
"^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
"^x", "^y", "^z", "control", "control", "control", "control",
"control",
"space", "bang!", "quote", "number", "dollar", "percent", "and",
"tick",
"left paren", "right paren", "star", "plus", "comma", "dash",
"dot",
"slash",
"zero", "one", "two", "three", "four", "five", "six", "seven",
"eight", "nine",
"colon", "semmy", "less", "equals", "greater", "question", "at",
"EIGH", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X",
"Y", "ZED", "left bracket", "backslash", "right bracket",
"caret",
"line",
"accent", "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
"del", "control", "control", "control", "control", "control",
"control", "control", "control", "control", "control",
"control", "control", "control", "control", "control",
"control", "control", "control", "control", "control",
"control", "control",
"control", "control", "control", "control", "control",
"control", "control", "control", "control", "control",
"nbsp", "inverted bang",
"cents", "pounds", "currency", "yen", "broken bar", "section",
"diaeresis", "copyright", "female ordinal", "double left angle",
"not", "soft hyphen", "registered", "macron",
"degrees", "plus or minus", "super two", "super three",
"acute accent", "micro", "pilcrow", "middle dot",
"cedilla", "super one", "male ordinal", "double right angle",
"one quarter", "one half", "three quarters",
"inverted question",
"A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
"A RING",
"AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
"E OOMLAUT",
"I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
"N TILDE",
"O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
"multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
"U CIRCUMFLEX",
"U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
"a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
"ae", "c cidella", "e grave", "e acute",
"e circumflex", "e oomlaut", "i grave", "i acute",
"i circumflex",
"i oomlaut", "eth", "n tilde", "o grave", "o acute",
"o circumflex",
"o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
"u acute",
"u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
};
u_short spk_chartab[256];
static u_short default_chartab[256] = {
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,
B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,
B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,
WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,
PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC,
NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM,
NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,
PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,
A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,
A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,
A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC,
PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,
ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0,
B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,
B_SYM,
B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,
B_CAPSYM,
B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM,
B_SYM,
B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM,
B_SYM,
WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM,
B_SYM,
B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,
B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,
B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,
A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,
A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,
A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM,
A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA,
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM,
ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA
};
struct task_struct *speakup_task;
struct bleep spk_unprocessed_sound;
static int spk_keydown;
static u16 spk_lastkey;
static u_char spk_close_press, keymap_flags;
static u_char last_keycode, this_speakup_key;
static u_long last_spk_jiffy;
struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
DEFINE_MUTEX(spk_mutex);
static int keyboard_notifier_call(struct notifier_block *,
unsigned long code, void *param);
static struct notifier_block keyboard_notifier_block = {
.notifier_call = keyboard_notifier_call,
};
static int vt_notifier_call(struct notifier_block *,
unsigned long code, void *param);
static struct notifier_block vt_notifier_block = {
.notifier_call = vt_notifier_call,
};
static unsigned char get_attributes(struct vc_data *vc, u16 *pos)
{
pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
}
static void speakup_date(struct vc_data *vc)
{
spk_x = spk_cx = vc->state.x;
spk_y = spk_cy = vc->state.y;
spk_pos = spk_cp = vc->vc_pos;
spk_old_attr = spk_attr;
spk_attr = get_attributes(vc, (u_short *)spk_pos);
}
static void bleep(u_short val)
{
static const short vals[] = {
350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
};
short freq;
int time = spk_bleep_time;
freq = vals[val % 12];
if (val > 11)
freq *= (1 << (val / 12));
spk_unprocessed_sound.freq = freq;
spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
spk_unprocessed_sound.active = 1;
}
static void speakup_shut_up(struct vc_data *vc)
{
if (spk_killed)
return;
spk_shut_up |= 0x01;
spk_parked &= 0xfe;
speakup_date(vc);
if (synth)
spk_do_flush();
}
static void speech_kill(struct vc_data *vc)
{
char val = synth->is_alive(synth);
if (val == 0)
return;
if (val == 2 || spk_killed) {
spk_shut_up &= ~0x40;
synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
} else {
synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
spk_shut_up |= 0x40;
}
}
static void speakup_off(struct vc_data *vc)
{
if (spk_shut_up & 0x80) {
spk_shut_up &= 0x7f;
synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
} else {
spk_shut_up |= 0x80;
synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
}
speakup_date(vc);
}
static void speakup_parked(struct vc_data *vc)
{
if (spk_parked & 0x80) {
spk_parked = 0;
synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
} else {
spk_parked |= 0x80;
synth_printf("%s\n", spk_msg_get(MSG_PARKED));
}
}
static void speakup_cut(struct vc_data *vc)
{
static const char err_buf[] = "set selection failed";
int ret;
if (!mark_cut_flag) {
mark_cut_flag = 1;
spk_xs = (u_short)spk_x;
spk_ys = (u_short)spk_y;
spk_sel_cons = vc;
synth_printf("%s\n", spk_msg_get(MSG_MARK));
return;
}
spk_xe = (u_short)spk_x;
spk_ye = (u_short)spk_y;
mark_cut_flag = 0;
synth_printf("%s\n", spk_msg_get(MSG_CUT));
ret = speakup_set_selection(tty);
switch (ret) {
case 0:
break;
case -EFAULT:
pr_warn("%sEFAULT\n", err_buf);
break;
case -EINVAL:
pr_warn("%sEINVAL\n", err_buf);
break;
case -ENOMEM:
pr_warn("%sENOMEM\n", err_buf);
break;
}
}
static void speakup_paste(struct vc_data *vc)
{
if (mark_cut_flag) {
mark_cut_flag = 0;
synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
} else {
synth_printf("%s\n", spk_msg_get(MSG_PASTE));
speakup_paste_selection(tty);
}
}
static void say_attributes(struct vc_data *vc)
{
int fg = spk_attr & 0x0f;
int bg = spk_attr >> 4;
synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
if (bg > 7) {
synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
bg -= 8;
} else {
synth_printf(" %s ", spk_msg_get(MSG_ON));
}
synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
}
enum edge {
edge_none = 0,
edge_top,
edge_bottom,
edge_left,
edge_right,
edge_quiet
};
static void announce_edge(struct vc_data *vc, enum edge msg_id)
{
if (spk_bleeps & 1)
bleep(spk_y);
if ((spk_bleeps & 2) && (msg_id < edge_quiet))
synth_printf("%s\n",
spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
}
static void speak_char(u16 ch)
{
char *cp;
struct var_t *direct = spk_get_var(DIRECT);
if (ch >= 0x100 || (direct && direct->u.n.value)) {
if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
spk_pitch_shift++;
synth_printf("%s", spk_str_caps_start);
}
synth_putwc_s(ch);
if (ch < 0x100 && IS_CHAR(ch, B_CAP))
synth_printf("%s", spk_str_caps_stop);
return;
}
cp = spk_characters[ch];
if (!cp) {
pr_info("%s: cp == NULL!\n", __func__);
return;
}
if (IS_CHAR(ch, B_CAP)) {
spk_pitch_shift++;
synth_printf("%s %s %s",
spk_str_caps_start, cp, spk_str_caps_stop);
} else {
if (*cp == '^') {
cp++;
synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
} else {
synth_printf(" %s ", cp);
}
}
}
static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
{
u16 ch = ' ';
if (vc && pos) {
u16 w;
u16 c;
pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
w = scr_readw(pos);
c = w & 0xff;
if (w & vc->vc_hi_font_mask) {
w &= ~vc->vc_hi_font_mask;
c |= 0x100;
}
ch = inverse_translate(vc, c, true);
*attribs = (w & 0xff00) >> 8;
}
return ch;
}
static void say_char(struct vc_data *vc)
{
u16 ch;
spk_old_attr = spk_attr;
ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
if (spk_attr != spk_old_attr) {
if (spk_attrib_bleep & 1)
bleep(spk_y);
if (spk_attrib_bleep & 2)
say_attributes(vc);
}
speak_char(ch);
}
static void say_phonetic_char(struct vc_data *vc)
{
u16 ch;
spk_old_attr = spk_attr;
ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
if (ch <= 0x7f && isalpha(ch)) {
ch &= 0x1f;
synth_printf("%s\n", phonetic[--ch]);
} else {
if (ch < 0x100 && IS_CHAR(ch, B_NUM))
synth_printf("%s ", spk_msg_get(MSG_NUMBER));
speak_char(ch);
}
}
static void say_prev_char(struct vc_data *vc)
{
spk_parked |= 0x01;
if (spk_x == 0) {
announce_edge(vc, edge_left);
return;
}
spk_x--;
spk_pos -= 2;
say_char(vc);
}
static void say_next_char(struct vc_data *vc)
{
spk_parked |= 0x01;
if (spk_x == vc->vc_cols - 1) {
announce_edge(vc, edge_right);
return;
}
spk_x++;
spk_pos += 2;
say_char(vc);
}
static u_long get_word(struct vc_data *vc)
{
u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
u16 ch;
u16 attr_ch;
u_char temp;
spk_old_attr = spk_attr;
ch = get_char(vc, (u_short *)tmp_pos, &temp);
if (spk_say_word_ctl && ch == SPACE) {
*buf = '\0';
synth_printf("%s\n", spk_msg_get(MSG_SPACE));
return 0;
} else if (tmpx < vc->vc_cols - 2 &&
(ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
tmp_pos += 2;
tmpx++;
} else {
while (tmpx > 0) {
ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
if ((ch == SPACE || ch == 0 ||
(ch < 0x100 && IS_WDLM(ch))) &&
get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
break;
tmp_pos -= 2;
tmpx--;
}
}
attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
buf[cnt++] = attr_ch;
while (tmpx < vc->vc_cols - 1) {
tmp_pos += 2;
tmpx++;
ch = get_char(vc, (u_short *)tmp_pos, &temp);
if (ch == SPACE || ch == 0 ||
(buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
ch > SPACE))
break;
buf[cnt++] = ch;
}
buf[cnt] = '\0';
return cnt;
}
static void say_word(struct vc_data *vc)
{
u_long cnt = get_word(vc);
u_short saved_punc_mask = spk_punc_mask;
if (cnt == 0)
return;
spk_punc_mask = PUNC;
buf[cnt++] = SPACE;
spkup_write(buf, cnt);
spk_punc_mask = saved_punc_mask;
}
static void say_prev_word(struct vc_data *vc)
{
u_char temp;
u16 ch;
enum edge edge_said = edge_none;
u_short last_state = 0, state = 0;
spk_parked |= 0x01;
if (spk_x == 0) {
if (spk_y == 0) {
announce_edge(vc, edge_top);
return;
}
spk_y--;
spk_x = vc->vc_cols;
edge_said = edge_quiet;
}
while (1) {
if (spk_x == 0) {
if (spk_y == 0) {
edge_said = edge_top;
break;
}
if (edge_said != edge_quiet)
edge_said = edge_left;
if (state > 0)
break;
spk_y--;
spk_x = vc->vc_cols - 1;
} else {
spk_x--;
}
spk_pos -= 2;
ch = get_char(vc, (u_short *)spk_pos, &temp);
if (ch == SPACE || ch == 0)
state = 0;
else if (ch < 0x100 && IS_WDLM(ch))
state = 1;
else
state = 2;
if (state < last_state) {
spk_pos += 2;
spk_x++;
break;
}
last_state = state;
}
if (spk_x == 0 && edge_said == edge_quiet)
edge_said = edge_left;
if (edge_said > edge_none && edge_said < edge_quiet)
announce_edge(vc, edge_said);
say_word(vc);
}
static void say_next_word(struct vc_data *vc)
{
u_char temp;
u16 ch;
enum edge edge_said = edge_none;
u_short last_state = 2, state = 0;
spk_parked |= 0x01;
if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
announce_edge(vc, edge_bottom);
return;
}
while (1) {
ch = get_char(vc, (u_short *)spk_pos, &temp);
if (ch == SPACE || ch == 0)
state = 0;
else if (ch < 0x100 && IS_WDLM(ch))
state = 1;
else
state = 2;
if (state > last_state)
break;
if (spk_x >= vc->vc_cols - 1) {
if (spk_y == vc->vc_rows - 1) {
edge_said = edge_bottom;
break;
}
state = 0;
spk_y++;
spk_x = 0;
edge_said = edge_right;
} else {
spk_x++;
}
spk_pos += 2;
last_state = state;
}
if (edge_said > edge_none)
announce_edge(vc, edge_said);
say_word(vc);
}
static void spell_word(struct vc_data *vc)
{
static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
u16 *cp = buf;
char *cp1;
char *str_cap = spk_str_caps_stop;
char *last_cap = spk_str_caps_stop;
struct var_t *direct = spk_get_var(DIRECT);
u16 ch;
if (!get_word(vc))
return;
while ((ch = *cp)) {
if (cp != buf)
synth_printf(" %s ", delay_str[spk_spell_delay]);
if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
str_cap = spk_str_caps_start;
if (*spk_str_caps_stop)
spk_pitch_shift++;
else
last_cap = spk_str_caps_stop;
} else {
str_cap = spk_str_caps_stop;
}
if (str_cap != last_cap) {
synth_printf("%s", str_cap);
last_cap = str_cap;
}
if (ch >= 0x100 || (direct && direct->u.n.value)) {
synth_putwc_s(ch);
} else if (this_speakup_key == SPELL_PHONETIC &&
ch <= 0x7f && isalpha(ch)) {
ch &= 0x1f;
cp1 = phonetic[--ch];
synth_printf("%s", cp1);
} else {
cp1 = spk_characters[ch];
if (*cp1 == '^') {
synth_printf("%s", spk_msg_get(MSG_CTRL));
cp1++;
}
synth_printf("%s", cp1);
}
cp++;
}
if (str_cap != spk_str_caps_stop)
synth_printf("%s", spk_str_caps_stop);
}
static int get_line(struct vc_data *vc)
{
u_long tmp = spk_pos - (spk_x * 2);
int i = 0;
u_char tmp2;
spk_old_attr = spk_attr;
spk_attr = get_attributes(vc, (u_short *)spk_pos);
for (i = 0; i < vc->vc_cols; i++) {
buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
tmp += 2;
}
for (--i; i >= 0; i--)
if (buf[i] != SPACE)
break;
return ++i;
}
static void say_line(struct vc_data *vc)
{
int i = get_line(vc);
u16 *cp;
u_short saved_punc_mask = spk_punc_mask;
if (i == 0) {
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
return;
}
buf[i++] = '\n';
if (this_speakup_key == SAY_LINE_INDENT) {
cp = buf;
while (*cp == SPACE)
cp++;
synth_printf("%zd, ", (cp - buf) + 1);
}
spk_punc_mask = spk_punc_masks[spk_reading_punc];
spkup_write(buf, i);
spk_punc_mask = saved_punc_mask;
}
static void say_prev_line(struct vc_data *vc)
{
spk_parked |= 0x01;
if (spk_y == 0) {
announce_edge(vc, edge_top);
return;
}
spk_y--;
spk_pos -= vc->vc_size_row;
say_line(vc);
}
static void say_next_line(struct vc_data *vc)
{
spk_parked |= 0x01;
if (spk_y == vc->vc_rows - 1) {
announce_edge(vc, edge_bottom);
return;
}
spk_y++;
spk_pos += vc->vc_size_row;
say_line(vc);
}
static int say_from_to(struct vc_data *vc, u_long from, u_long to,
int read_punc)
{
int i = 0;
u_char tmp;
u_short saved_punc_mask = spk_punc_mask;
spk_old_attr = spk_attr;
spk_attr = get_attributes(vc, (u_short *)from);
while (from < to) {
buf[i++] = get_char(vc, (u_short *)from, &tmp);
from += 2;
if (i >= vc->vc_size_row)
break;
}
for (--i; i >= 0; i--)
if (buf[i] != SPACE)
break;
buf[++i] = SPACE;
buf[++i] = '\0';
if (i < 1)
return i;
if (read_punc)
spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
spkup_write(buf, i);
if (read_punc)
spk_punc_mask = saved_punc_mask;
return i - 1;
}
static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
int read_punc)
{
u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
u_long end = start + (to * 2);
start += from * 2;
if (say_from_to(vc, start, end, read_punc) <= 0)
if (cursor_track != read_all_mode)
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
}
static int currsentence;
static int numsentences[2];
static u16 *sentbufend[2];
static u16 *sentmarks[2][10];
static int currbuf;
static int bn;
static u16 sentbuf[2][256];
static int say_sentence_num(int num, int prev)
{
bn = currbuf;
currsentence = num + 1;
if (prev && --bn == -1)
bn = 1;
if (num > numsentences[bn])
return 0;
spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
return 1;
}
static int get_sentence_buf(struct vc_data *vc, int read_punc)
{
u_long start, end;
int i, bn;
u_char tmp;
currbuf++;
if (currbuf == 2)
currbuf = 0;
bn = currbuf;
start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
numsentences[bn] = 0;
sentmarks[bn][0] = &sentbuf[bn][0];
i = 0;
spk_old_attr = spk_attr;
spk_attr = get_attributes(vc, (u_short *)start);
while (start < end) {
sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
if (i > 0) {
if (sentbuf[bn][i] == SPACE &&
sentbuf[bn][i - 1] == '.' &&
numsentences[bn] < 9) {
numsentences[bn]++;
sentmarks[bn][numsentences[bn]] =
&sentbuf[bn][i];
}
}
i++;
start += 2;
if (i >= vc->vc_size_row)
break;
}
for (--i; i >= 0; i--)
if (sentbuf[bn][i] != SPACE)
break;
if (i < 1)
return -1;
sentbuf[bn][++i] = SPACE;
sentbuf[bn][++i] = '\0';
sentbufend[bn] = &sentbuf[bn][i];
return numsentences[bn];
}
static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
{
u_long start = vc->vc_origin, end;
if (from > 0)
start += from * vc->vc_size_row;
if (to > vc->vc_rows)
to = vc->vc_rows;
end = vc->vc_origin + (to * vc->vc_size_row);
for (from = start; from < end; from = to) {
to = from + vc->vc_size_row;
say_from_to(vc, from, to, 1);
}
}
static void say_screen(struct vc_data *vc)
{
say_screen_from_to(vc, 0, vc->vc_rows);
}
static void speakup_win_say(struct vc_data *vc)
{
u_long start, end, from, to;
if (win_start < 2) {
synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
return;
}
start = vc->vc_origin + (win_top * vc->vc_size_row);
end = vc->vc_origin + (win_bottom * vc->vc_size_row);
while (start <= end) {
from = start + (win_left * 2);
to = start + (win_right * 2);
say_from_to(vc, from, to, 1);
start += vc->vc_size_row;
}
}
static void top_edge(struct vc_data *vc)
{
spk_parked |= 0x01;
spk_pos = vc->vc_origin + 2 * spk_x;
spk_y = 0;
say_line(vc);
}
static void bottom_edge(struct vc_data *vc)
{
spk_parked |= 0x01;
spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
spk_y = vc->vc_rows - 1;
say_line(vc);
}
static void left_edge(struct vc_data *vc)
{
spk_parked |= 0x01;
spk_pos -= spk_x * 2;
spk_x = 0;
say_char(vc);
}
static void right_edge(struct vc_data *vc)
{
spk_parked |= 0x01;
spk_pos += (vc->vc_cols - spk_x - 1) * 2;
spk_x = vc->vc_cols - 1;
say_char(vc);
}
static void say_first_char(struct vc_data *vc)
{
int i, len = get_line(vc);
u16 ch;
spk_parked |= 0x01;
if (len == 0) {
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
return;
}
for (i = 0; i < len; i++)
if (buf[i] != SPACE)
break;
ch = buf[i];
spk_pos -= (spk_x - i) * 2;
spk_x = i;
synth_printf("%d, ", ++i);
speak_char(ch);
}
static void say_last_char(struct vc_data *vc)
{
int len = get_line(vc);
u16 ch;
spk_parked |= 0x01;
if (len == 0) {
synth_printf("%s\n", spk_msg_get(MSG_BLANK));
return;
}
ch = buf[--len];
spk_pos -= (spk_x - len) * 2;
spk_x = len;
synth_printf("%d, ", ++len);
speak_char(ch);
}
static void say_position(struct vc_data *vc)
{
synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
vc->vc_num + 1);
synth_printf("\n");
}
static void say_char_num(struct vc_data *vc)
{
u_char tmp;
u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
}
static void say_from_top(struct vc_data *vc)
{
say_screen_from_to(vc, 0, spk_y);
}
static void say_to_bottom(struct vc_data *vc)
{
say_screen_from_to(vc, spk_y, vc->vc_rows);
}
static void say_from_left(struct vc_data *vc)
{
say_line_from_to(vc, 0, spk_x, 1);
}
static void say_to_right(struct vc_data *vc)
{
say_line_from_to(vc, spk_x, vc->vc_cols, 1);
}
static void spkup_write(const u16 *in_buf, int count)
{
static int rep_count;
static u16 ch = '\0', old_ch = '\0';
static u_short char_type, last_type;
int in_count = count;
spk_keydown = 0;
while (count--) {
if (cursor_track == read_all_mode) {
if ((in_buf == sentmarks[bn][currsentence]) &&
(currsentence <= numsentences[bn]))
synth_insert_next_index(currsentence++);
}
ch = *in_buf++;
if (ch < 0x100)
char_type = spk_chartab[ch];
else
char_type = ALPHA;
if (ch == old_ch && !(char_type & B_NUM)) {
if (++rep_count > 2)
continue;
} else {
if ((last_type & CH_RPT) && rep_count > 2) {
synth_printf(" ");
synth_printf(spk_msg_get(MSG_REPEAT_DESC),
++rep_count);
synth_printf(" ");
}
rep_count = 0;
}
if (ch == spk_lastkey) {
rep_count = 0;
if (spk_key_echo == 1 && ch >= MINECHOCHAR)
speak_char(ch);
} else if (char_type & B_ALPHA) {
if ((synth_flags & SF_DEC) && (last_type & PUNC))
synth_buffer_add(SPACE);
synth_putwc_s(ch);
} else if (char_type & B_NUM) {
rep_count = 0;
synth_putwc_s(ch);
} else if (char_type & spk_punc_mask) {
speak_char(ch);
char_type &= ~PUNC;
} else if (char_type & SYNTH_OK) {
if (ch != old_ch)
synth_putwc_s(ch);
else
rep_count = 0;
} else {
if (old_ch != ch)
synth_buffer_add(SPACE);
else
rep_count = 0;
}
old_ch = ch;
last_type = char_type;
}
spk_lastkey = 0;
if (in_count > 2 && rep_count > 2) {
if (last_type & CH_RPT) {
synth_printf(" ");
synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
++rep_count);
synth_printf(" ");
}
rep_count = 0;
}
}
static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
static void read_all_doc(struct vc_data *vc);
static void cursor_done(struct timer_list *unused);
static DEFINE_TIMER(cursor_timer, cursor_done);
static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
{
unsigned long flags;
if (!synth || up_flag || spk_killed)
return;
spin_lock_irqsave(&speakup_info.spinlock, flags);
if (cursor_track == read_all_mode) {
switch (value) {
case KVAL(K_SHIFT):
del_timer(&cursor_timer);
spk_shut_up &= 0xfe;
spk_do_flush();
read_all_doc(vc);
break;
case KVAL(K_CTRL):
del_timer(&cursor_timer);
cursor_track = prev_cursor_track;
spk_shut_up &= 0xfe;
spk_do_flush();
break;
}
} else {
spk_shut_up &= 0xfe;
spk_do_flush();
}
if (spk_say_ctrl && value < NUM_CTL_LABELS)
synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
{
unsigned long flags;
spin_lock_irqsave(&speakup_info.spinlock, flags);
if (up_flag) {
spk_lastkey = 0;
spk_keydown = 0;
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
if (!synth || spk_killed) {
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
spk_shut_up &= 0xfe;
spk_lastkey = value;
spk_keydown++;
spk_parked &= 0xfe;
if (spk_key_echo == 2 && value >= MINECHOCHAR)
speak_char(value);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
{
int i = 0, states, key_data_len;
const u_char *cp = key_info;
u_char *cp1 = k_buffer;
u_char ch, version, num_keys;
version = *cp++;
if (version != KEY_MAP_VER) {
pr_debug("version found %d should be %d\n",
version, KEY_MAP_VER);
return -EINVAL;
}
num_keys = *cp;
states = (int)cp[1];
key_data_len = (states + 1) * (num_keys + 1);
if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
pr_debug("too many key_infos (%d over %u)\n",
key_data_len + SHIFT_TBL_SIZE + 4,
(unsigned int)(sizeof(spk_key_buf)));
return -EINVAL;
}
memset(k_buffer, 0, SHIFT_TBL_SIZE);
memset(spk_our_keys, 0, sizeof(spk_our_keys));
spk_shift_table = k_buffer;
spk_our_keys[0] = spk_shift_table;
cp1 += SHIFT_TBL_SIZE;
memcpy(cp1, cp, key_data_len + 3);
cp1 += 2;
for (i = 1; i <= states; i++) {
ch = *cp1++;
if (ch >= SHIFT_TBL_SIZE) {
pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
ch, SHIFT_TBL_SIZE);
return -EINVAL;
}
spk_shift_table[ch] = i;
}
keymap_flags = *cp1++;
while ((ch = *cp1)) {
if (ch >= MAX_KEY) {
pr_debug("(%d), not valid key, (max_allowed = %d)\n",
ch, MAX_KEY);
return -EINVAL;
}
spk_our_keys[ch] = cp1;
cp1 += states + 1;
}
return 0;
}
enum spk_vars_id {
BELL_POS_ID = 0, SPELL_DELAY_ID, ATTRIB_BLEEP_ID,
BLEEPS_ID, BLEEP_TIME_ID, PUNC_LEVEL_ID,
READING_PUNC_ID, CURSOR_TIME_ID, SAY_CONTROL_ID,
SAY_WORD_CTL_ID, NO_INTERRUPT_ID, KEY_ECHO_ID,
CUR_PHONETIC_ID, V_LAST_VAR_ID, NB_ID
};
static struct var_t spk_vars[NB_ID] = {
[BELL_POS_ID] = { BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
[SPELL_DELAY_ID] = { SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
[ATTRIB_BLEEP_ID] = { ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
[BLEEPS_ID] = { BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
[BLEEP_TIME_ID] = { BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
[PUNC_LEVEL_ID] = { PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
[READING_PUNC_ID] = { READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
[CURSOR_TIME_ID] = { CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
[SAY_CONTROL_ID] = { SAY_CONTROL, TOGGLE_0},
[SAY_WORD_CTL_ID] = {SAY_WORD_CTL, TOGGLE_0},
[NO_INTERRUPT_ID] = { NO_INTERRUPT, TOGGLE_0},
[KEY_ECHO_ID] = { KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
[CUR_PHONETIC_ID] = { CUR_PHONETIC, .u.n = {NULL, 0, 0, 1, 0, 0, NULL} },
V_LAST_VAR
};
static void toggle_cursoring(struct vc_data *vc)
{
if (cursor_track == read_all_mode)
cursor_track = prev_cursor_track;
if (++cursor_track >= CT_Max)
cursor_track = 0;
synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
}
void spk_reset_default_chars(void)
{
int i;
for (i = 0; i < 256; i++) {
if (spk_characters[i] &&
(spk_characters[i] != spk_default_chars[i]))
kfree(spk_characters[i]);
}
memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
}
void spk_reset_default_chartab(void)
{
memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
}
static const struct st_bits_data *pb_edit;
static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
{
short mask = pb_edit->mask, ch_type = spk_chartab[ch];
if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
return -1;
if (ch == SPACE) {
synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
spk_special_handler = NULL;
return 1;
}
if (mask < PUNC && !(ch_type & PUNC))
return -1;
spk_chartab[ch] ^= mask;
speak_char(ch);
synth_printf(" %s\n",
(spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
spk_msg_get(MSG_OFF));
return 1;
}
static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
{
int vc_num;
vc_num = vc->vc_num;
if (!speakup_console[vc_num]) {
speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
gfp_flags);
if (!speakup_console[vc_num])
return -ENOMEM;
speakup_date(vc);
} else if (!spk_parked) {
speakup_date(vc);
}
return 0;
}
static void speakup_deallocate(struct vc_data *vc)
{
int vc_num;
vc_num = vc->vc_num;
kfree(speakup_console[vc_num]);
speakup_console[vc_num] = NULL;
}
enum read_all_command {
RA_NEXT_SENT = KVAL(K_DOWN)+1,
RA_PREV_LINE = KVAL(K_LEFT)+1,
RA_NEXT_LINE = KVAL(K_RIGHT)+1,
RA_PREV_SENT = KVAL(K_UP)+1,
RA_DOWN_ARROW,
RA_TIMER,
RA_FIND_NEXT_SENT,
RA_FIND_PREV_SENT,
};
static u_char is_cursor;
static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
static int cursor_con;
static void reset_highlight_buffers(struct vc_data *);
static enum read_all_command read_all_key;
static int in_keyboard_notifier;
static void start_read_all_timer(struct vc_data *vc, enum read_all_command command);
static void kbd_fakekey2(struct vc_data *vc, enum read_all_command command)
{
del_timer(&cursor_timer);
speakup_fake_down_arrow();
start_read_all_timer(vc, command);
}
static void read_all_doc(struct vc_data *vc)
{
if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
return;
if (!synth_supports_indexing())
return;
if (cursor_track != read_all_mode)
prev_cursor_track = cursor_track;
cursor_track = read_all_mode;
spk_reset_index_count(0);
if (get_sentence_buf(vc, 0) == -1) {
del_timer(&cursor_timer);
if (!in_keyboard_notifier)
speakup_fake_down_arrow();
start_read_all_timer(vc, RA_DOWN_ARROW);
} else {
say_sentence_num(0, 0);
synth_insert_next_index(0);
start_read_all_timer(vc, RA_TIMER);
}
}
static void stop_read_all(struct vc_data *vc)
{
del_timer(&cursor_timer);
cursor_track = prev_cursor_track;
spk_shut_up &= 0xfe;
spk_do_flush();
}
static void start_read_all_timer(struct vc_data *vc, enum read_all_command command)
{
struct var_t *cursor_timeout;
cursor_con = vc->vc_num;
read_all_key = command;
cursor_timeout = spk_get_var(CURSOR_TIME);
mod_timer(&cursor_timer,
jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
}
static void handle_cursor_read_all(struct vc_data *vc, enum read_all_command command)
{
int indcount, sentcount, rv, sn;
switch (command) {
case RA_NEXT_SENT:
spk_get_index_count(&indcount, &sentcount);
spk_reset_index_count(sentcount + 1);
if (indcount == 1) {
if (!say_sentence_num(sentcount + 1, 0)) {
kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
return;
}
synth_insert_next_index(0);
} else {
sn = 0;
if (!say_sentence_num(sentcount + 1, 1)) {
sn = 1;
spk_reset_index_count(sn);
} else {
synth_insert_next_index(0);
}
if (!say_sentence_num(sn, 0)) {
kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
return;
}
synth_insert_next_index(0);
}
start_read_all_timer(vc, RA_TIMER);
break;
case RA_PREV_SENT:
break;
case RA_NEXT_LINE:
read_all_doc(vc);
break;
case RA_PREV_LINE:
break;
case RA_DOWN_ARROW:
if (get_sentence_buf(vc, 0) == -1) {
kbd_fakekey2(vc, RA_DOWN_ARROW);
} else {
say_sentence_num(0, 0);
synth_insert_next_index(0);
start_read_all_timer(vc, RA_TIMER);
}
break;
case RA_FIND_NEXT_SENT:
rv = get_sentence_buf(vc, 0);
if (rv == -1)
read_all_doc(vc);
if (rv == 0) {
kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
} else {
say_sentence_num(1, 0);
synth_insert_next_index(0);
start_read_all_timer(vc, RA_TIMER);
}
break;
case RA_FIND_PREV_SENT:
break;
case RA_TIMER:
spk_get_index_count(&indcount, &sentcount);
if (indcount < 2)
kbd_fakekey2(vc, RA_DOWN_ARROW);
else
start_read_all_timer(vc, RA_TIMER);
break;
}
}
static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
{
unsigned long flags;
spin_lock_irqsave(&speakup_info.spinlock, flags);
if (cursor_track == read_all_mode) {
spk_parked &= 0xfe;
if (!synth || up_flag || spk_shut_up) {
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return NOTIFY_STOP;
}
del_timer(&cursor_timer);
spk_shut_up &= 0xfe;
spk_do_flush();
start_read_all_timer(vc, value + 1);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return NOTIFY_STOP;
}
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return NOTIFY_OK;
}
static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
{
unsigned long flags;
struct var_t *cursor_timeout;
spin_lock_irqsave(&speakup_info.spinlock, flags);
spk_parked &= 0xfe;
if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
spk_shut_up &= 0xfe;
if (spk_no_intr)
spk_do_flush();
is_cursor = value + 1;
old_cursor_pos = vc->vc_pos;
old_cursor_x = vc->state.x;
old_cursor_y = vc->state.y;
speakup_console[vc->vc_num]->ht.cy = vc->state.y;
cursor_con = vc->vc_num;
if (cursor_track == CT_Highlight)
reset_highlight_buffers(vc);
cursor_timeout = spk_get_var(CURSOR_TIME);
mod_timer(&cursor_timer,
jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
{
int i, bi, hi;
int vc_num = vc->vc_num;
bi = (vc->vc_attr & 0x70) >> 4;
hi = speakup_console[vc_num]->ht.highsize[bi];
i = 0;
if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
speakup_console[vc_num]->ht.rx[bi] = vc->state.x;
speakup_console[vc_num]->ht.ry[bi] = vc->state.y;
}
while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
if (ic[i] > 32) {
speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
hi++;
} else if ((ic[i] == 32) && (hi != 0)) {
if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
32) {
speakup_console[vc_num]->ht.highbuf[bi][hi] =
ic[i];
hi++;
}
}
i++;
}
speakup_console[vc_num]->ht.highsize[bi] = hi;
}
static void reset_highlight_buffers(struct vc_data *vc)
{
int i;
int vc_num = vc->vc_num;
for (i = 0; i < 8; i++)
speakup_console[vc_num]->ht.highsize[i] = 0;
}
static int count_highlight_color(struct vc_data *vc)
{
int i, bg;
int cc;
int vc_num = vc->vc_num;
u16 ch;
u16 *start = (u16 *)vc->vc_origin;
for (i = 0; i < 8; i++)
speakup_console[vc_num]->ht.bgcount[i] = 0;
for (i = 0; i < vc->vc_rows; i++) {
u16 *end = start + vc->vc_cols * 2;
u16 *ptr;
for (ptr = start; ptr < end; ptr++) {
ch = get_attributes(vc, ptr);
bg = (ch & 0x70) >> 4;
speakup_console[vc_num]->ht.bgcount[bg]++;
}
start += vc->vc_size_row;
}
cc = 0;
for (i = 0; i < 8; i++)
if (speakup_console[vc_num]->ht.bgcount[i] > 0)
cc++;
return cc;
}
static int get_highlight_color(struct vc_data *vc)
{
int i, j;
unsigned int cptr[8];
int vc_num = vc->vc_num;
for (i = 0; i < 8; i++)
cptr[i] = i;
for (i = 0; i < 7; i++)
for (j = i + 1; j < 8; j++)
if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
speakup_console[vc_num]->ht.bgcount[cptr[j]])
swap(cptr[i], cptr[j]);
for (i = 0; i < 8; i++)
if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
return cptr[i];
return -1;
}
static int speak_highlight(struct vc_data *vc)
{
int hc, d;
int vc_num = vc->vc_num;
if (count_highlight_color(vc) == 1)
return 0;
hc = get_highlight_color(vc);
if (hc != -1) {
d = vc->state.y - speakup_console[vc_num]->ht.cy;
if ((d == 1) || (d == -1))
if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y)
return 0;
spk_parked |= 0x01;
spk_do_flush();
spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
speakup_console[vc_num]->ht.highsize[hc]);
spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
return 1;
}
return 0;
}
static void cursor_done(struct timer_list *unused)
{
struct vc_data *vc = vc_cons[cursor_con].d;
unsigned long flags;
del_timer(&cursor_timer);
spin_lock_irqsave(&speakup_info.spinlock, flags);
if (cursor_con != fg_console) {
is_cursor = 0;
goto out;
}
speakup_date(vc);
if (win_enabled) {
if (vc->state.x >= win_left && vc->state.x <= win_right &&
vc->state.y >= win_top && vc->state.y <= win_bottom) {
spk_keydown = 0;
is_cursor = 0;
goto out;
}
}
if (cursor_track == read_all_mode) {
handle_cursor_read_all(vc, read_all_key);
goto out;
}
if (cursor_track == CT_Highlight) {
if (speak_highlight(vc)) {
spk_keydown = 0;
is_cursor = 0;
goto out;
}
}
if (cursor_track == CT_Window)
speakup_win_say(vc);
else if (is_cursor == 1 || is_cursor == 4)
say_line_from_to(vc, 0, vc->vc_cols, 0);
else {
if (spk_cur_phonetic == 1)
say_phonetic_char(vc);
else
say_char(vc);
}
spk_keydown = 0;
is_cursor = 0;
out:
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static void speakup_bs(struct vc_data *vc)
{
unsigned long flags;
if (!speakup_console[vc->vc_num])
return;
if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
return;
if (!spk_parked)
speakup_date(vc);
if (spk_shut_up || !synth) {
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
if (vc->vc_num == fg_console && spk_keydown) {
spk_keydown = 0;
if (!is_cursor)
say_char(vc);
}
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
{
unsigned long flags;
if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
return;
if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
return;
if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1))
bleep(3);
if ((is_cursor) || (cursor_track == read_all_mode)) {
if (cursor_track == CT_Highlight)
update_color_buffer(vc, str, len);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
if (win_enabled) {
if (vc->state.x >= win_left && vc->state.x <= win_right &&
vc->state.y >= win_top && vc->state.y <= win_bottom) {
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
}
spkup_write(str, len);
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static void speakup_con_update(struct vc_data *vc)
{
unsigned long flags;
if (!speakup_console[vc->vc_num] || spk_parked || !synth)
return;
if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
return;
speakup_date(vc);
if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {
synth_printf("%s", spk_str_pause);
spk_paused = true;
}
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
{
unsigned long flags;
int on_off = 2;
char *label;
if (!synth || up_flag || spk_killed)
return;
spin_lock_irqsave(&speakup_info.spinlock, flags);
spk_shut_up &= 0xfe;
if (spk_no_intr)
spk_do_flush();
switch (value) {
case KVAL(K_CAPS):
label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
break;
case KVAL(K_NUM):
label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
on_off = vt_get_leds(fg_console, VC_NUMLOCK);
break;
case KVAL(K_HOLD):
label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
if (speakup_console[vc->vc_num])
speakup_console[vc->vc_num]->tty_stopped = on_off;
break;
default:
spk_parked &= 0xfe;
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return;
}
if (on_off < 2)
synth_printf("%s %s\n",
label, spk_msg_get(MSG_STATUS_START + on_off));
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
static int inc_dec_var(u_char value)
{
struct st_var_header *p_header;
struct var_t *var_data;
char num_buf[32];
char *cp = num_buf;
char *pn;
int var_id = (int)value - VAR_START;
int how = (var_id & 1) ? E_INC : E_DEC;
var_id = var_id / 2 + FIRST_SET_VAR;
p_header = spk_get_var_header(var_id);
if (!p_header)
return -1;
if (p_header->var_type != VAR_NUM)
return -1;
var_data = p_header->data;
if (spk_set_num_var(1, p_header, how) != 0)
return -1;
if (!spk_close_press) {
for (pn = p_header->name; *pn; pn++) {
if (*pn == '_')
*cp = SPACE;
else
*cp++ = *pn;
}
}
snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
var_data->u.n.value);
synth_printf("%s", num_buf);
return 0;
}
static void speakup_win_set(struct vc_data *vc)
{
char info[40];
if (win_start > 1) {
synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
return;
}
if (spk_x < win_left || spk_y < win_top) {
synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
return;
}
if (win_start && spk_x == win_left && spk_y == win_top) {
win_left = 0;
win_right = vc->vc_cols - 1;
win_bottom = spk_y;
snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
(int)win_top + 1);
} else {
if (!win_start) {
win_top = spk_y;
win_left = spk_x;
} else {
win_bottom = spk_y;
win_right = spk_x;
}
snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
(win_start) ?
spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
(int)spk_y + 1, (int)spk_x + 1);
}
synth_printf("%s\n", info);
win_start++;
}
static void speakup_win_clear(struct vc_data *vc)
{
win_top = 0;
win_bottom = 0;
win_left = 0;
win_right = 0;
win_start = 0;
synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
}
static void speakup_win_enable(struct vc_data *vc)
{
if (win_start < 2) {
synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
return;
}
win_enabled ^= 1;
if (win_enabled)
synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
else
synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
}
static void speakup_bits(struct vc_data *vc)
{
int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
if (spk_special_handler || val < 1 || val > 6) {
synth_printf("%s\n", spk_msg_get(MSG_ERROR));
return;
}
pb_edit = &spk_punc_info[val];
synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
spk_special_handler = edit_bits;
}
static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
{
static u_char goto_buf[8];
static int num;
int maxlen;
char *cp;
u16 wch;
if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
goto do_goto;
if (type == KT_LATIN && ch == '\n')
goto do_goto;
if (type != 0)
goto oops;
if (ch == 8) {
u16 wch;
if (num == 0)
return -1;
wch = goto_buf[--num];
goto_buf[num] = '\0';
spkup_write(&wch, 1);
return 1;
}
if (ch < '+' || ch > 'y')
goto oops;
wch = ch;
goto_buf[num++] = ch;
goto_buf[num] = '\0';
spkup_write(&wch, 1);
maxlen = (*goto_buf >= '0') ? 3 : 4;
if ((ch == '+' || ch == '-') && num == 1)
return 1;
if (ch >= '0' && ch <= '9' && num < maxlen)
return 1;
if (num < maxlen - 1 || num > maxlen)
goto oops;
if (ch < 'x' || ch > 'y') {
oops:
if (!spk_killed)
synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
goto_buf[num = 0] = '\0';
spk_special_handler = NULL;
return 1;
}
goto_pos = simple_strtoul(goto_buf, &cp, 10);
if (*cp == 'x') {
if (*goto_buf < '0')
goto_pos += spk_x;
else if (goto_pos > 0)
goto_pos--;
if (goto_pos >= vc->vc_cols)
goto_pos = vc->vc_cols - 1;
goto_x = 1;
} else {
if (*goto_buf < '0')
goto_pos += spk_y;
else if (goto_pos > 0)
goto_pos--;
if (goto_pos >= vc->vc_rows)
goto_pos = vc->vc_rows - 1;
goto_x = 0;
}
goto_buf[num = 0] = '\0';
do_goto:
spk_special_handler = NULL;
spk_parked |= 0x01;
if (goto_x) {
spk_pos -= spk_x * 2;
spk_x = goto_pos;
spk_pos += goto_pos * 2;
say_word(vc);
} else {
spk_y = goto_pos;
spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
say_line(vc);
}
return 1;
}
static void speakup_goto(struct vc_data *vc)
{
if (spk_special_handler) {
synth_printf("%s\n", spk_msg_get(MSG_ERROR));
return;
}
synth_printf("%s\n", spk_msg_get(MSG_GOTO));
spk_special_handler = handle_goto;
}
static void speakup_help(struct vc_data *vc)
{
spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
}
static void do_nothing(struct vc_data *vc)
{
return;
}
static u_char key_speakup, spk_key_locked;
static void speakup_lock(struct vc_data *vc)
{
if (!spk_key_locked) {
spk_key_locked = 16;
key_speakup = 16;
} else {
spk_key_locked = 0;
key_speakup = 0;
}
}
typedef void (*spkup_hand) (struct vc_data *);
static spkup_hand spkup_handler[] = {
do_nothing, speakup_goto, speech_kill, speakup_shut_up,
speakup_cut, speakup_paste, say_first_char, say_last_char,
say_char, say_prev_char, say_next_char,
say_word, say_prev_word, say_next_word,
say_line, say_prev_line, say_next_line,
top_edge, bottom_edge, left_edge, right_edge,
spell_word, spell_word, say_screen,
say_position, say_attributes,
speakup_off, speakup_parked, say_line,
say_from_top, say_to_bottom,
say_from_left, say_to_right,
say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
speakup_bits, speakup_bits, speakup_bits,
speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
};
static void do_spkup(struct vc_data *vc, u_char value)
{
if (spk_killed && value != SPEECH_KILL)
return;
spk_keydown = 0;
spk_lastkey = 0;
spk_shut_up &= 0xfe;
this_speakup_key = value;
if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
spk_do_flush();
(*spkup_handler[value]) (vc);
} else {
if (inc_dec_var(value) < 0)
bleep(9);
}
}
static const char *pad_chars = "0123456789+-*/\015,.?()";
static int
speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
int up_flag)
{
unsigned long flags;
int kh;
u_char *key_info;
u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
u_char shift_info, offset;
int ret = 0;
if (!synth)
return 0;
spin_lock_irqsave(&speakup_info.spinlock, flags);
tty = vc->port.tty;
if (type >= 0xf0)
type -= 0xf0;
if (type == KT_PAD &&
(vt_get_leds(fg_console, VC_NUMLOCK))) {
if (up_flag) {
spk_keydown = 0;
goto out;
}
value = pad_chars[value];
spk_lastkey = value;
spk_keydown++;
spk_parked &= 0xfe;
goto no_map;
}
if (keycode >= MAX_KEY)
goto no_map;
key_info = spk_our_keys[keycode];
if (!key_info)
goto no_map;
if ((cursor_track == read_all_mode) && (!up_flag)) {
switch (value) {
case KVAL(K_DOWN):
case KVAL(K_UP):
case KVAL(K_LEFT):
case KVAL(K_RIGHT):
case KVAL(K_PGUP):
case KVAL(K_PGDN):
break;
default:
stop_read_all(vc);
break;
}
}
shift_info = (shift_state & 0x0f) + key_speakup;
offset = spk_shift_table[shift_info];
if (offset) {
new_key = key_info[offset];
if (new_key) {
ret = 1;
if (new_key == SPK_KEY) {
if (!spk_key_locked)
key_speakup = (up_flag) ? 0 : 16;
if (up_flag || spk_killed)
goto out;
spk_shut_up &= 0xfe;
spk_do_flush();
goto out;
}
if (up_flag)
goto out;
if (last_keycode == keycode &&
time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
spk_close_press = 1;
offset = spk_shift_table[shift_info + 32];
if (offset && key_info[offset])
new_key = key_info[offset];
}
last_keycode = keycode;
last_spk_jiffy = jiffies;
type = KT_SPKUP;
value = new_key;
}
}
no_map:
if (type == KT_SPKUP && !spk_special_handler) {
do_spkup(vc, new_key);
spk_close_press = 0;
ret = 1;
goto out;
}
if (up_flag || spk_killed || type == KT_SHIFT)
goto out;
spk_shut_up &= 0xfe;
kh = (value == KVAL(K_DOWN)) ||
(value == KVAL(K_UP)) ||
(value == KVAL(K_LEFT)) ||
(value == KVAL(K_RIGHT));
if ((cursor_track != read_all_mode) || !kh)
if (!spk_no_intr)
spk_do_flush();
if (spk_special_handler) {
if (type == KT_SPEC && value == 1) {
value = '\n';
type = KT_LATIN;
} else if (type == KT_LETTER) {
type = KT_LATIN;
} else if (value == 0x7f) {
value = 8;
}
ret = (*spk_special_handler) (vc, type, value, keycode);
spk_close_press = 0;
if (ret < 0)
bleep(9);
goto out;
}
last_keycode = 0;
out:
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
return ret;
}
static int keyboard_notifier_call(struct notifier_block *nb,
unsigned long code, void *_param)
{
struct keyboard_notifier_param *param = _param;
struct vc_data *vc = param->vc;
int up = !param->down;
int ret = NOTIFY_OK;
static int keycode;
in_keyboard_notifier = 1;
if (vc->vc_mode == KD_GRAPHICS)
goto out;
if (speakup_fake_key_pressed())
goto out;
switch (code) {
case KBD_KEYCODE:
keycode = param->value;
break;
case KBD_UNBOUND_KEYCODE:
break;
case KBD_UNICODE:
break;
case KBD_KEYSYM:
if (speakup_key(vc, param->shift, keycode, param->value, up))
ret = NOTIFY_STOP;
else if (KTYP(param->value) == KT_CUR)
ret = pre_handle_cursor(vc, KVAL(param->value), up);
break;
case KBD_POST_KEYSYM:{
unsigned char type = KTYP(param->value) - 0xf0;
unsigned char val = KVAL(param->value);
switch (type) {
case KT_SHIFT:
do_handle_shift(vc, val, up);
break;
case KT_LATIN:
case KT_LETTER:
do_handle_latin(vc, val, up);
break;
case KT_CUR:
do_handle_cursor(vc, val, up);
break;
case KT_SPEC:
do_handle_spec(vc, val, up);
break;
}
break;
}
}
out:
in_keyboard_notifier = 0;
return ret;
}
static int vt_notifier_call(struct notifier_block *nb,
unsigned long code, void *_param)
{
struct vt_notifier_param *param = _param;
struct vc_data *vc = param->vc;
switch (code) {
case VT_ALLOCATE:
if (vc->vc_mode == KD_TEXT)
speakup_allocate(vc, GFP_ATOMIC);
break;
case VT_DEALLOCATE:
speakup_deallocate(vc);
break;
case VT_WRITE:
if (param->c == '\b') {
speakup_bs(vc);
} else {
u16 d = param->c;
speakup_con_write(vc, &d, 1);
}
break;
case VT_UPDATE:
speakup_con_update(vc);
break;
}
return NOTIFY_OK;
}
static void __exit speakup_exit(void)
{
int i;
unregister_keyboard_notifier(&keyboard_notifier_block);
unregister_vt_notifier(&vt_notifier_block);
speakup_unregister_devsynth();
speakup_cancel_selection();
speakup_cancel_paste();
del_timer_sync(&cursor_timer);
kthread_stop(speakup_task);
speakup_task = NULL;
mutex_lock(&spk_mutex);
synth_release();
mutex_unlock(&spk_mutex);
spk_ttyio_unregister_ldisc();
speakup_kobj_exit();
for (i = 0; i < MAX_NR_CONSOLES; i++)
kfree(speakup_console[i]);
speakup_remove_virtual_keyboard();
for (i = 0; i < MAXVARS; i++)
speakup_unregister_var(i);
for (i = 0; i < 256; i++) {
if (spk_characters[i] != spk_default_chars[i])
kfree(spk_characters[i]);
}
spk_free_user_msgs();
}
static int __init speakup_init(void)
{
int i;
long err = 0;
struct vc_data *vc = vc_cons[fg_console].d;
struct var_t *var;
spk_initialize_msgs();
spk_reset_default_chars();
spk_reset_default_chartab();
spk_strlwr(synth_name);
spk_vars[0].u.n.high = vc->vc_cols;
for (var = spk_vars; var->var_id != MAXVARS; var++)
speakup_register_var(var);
for (var = synth_time_vars;
(var->var_id >= 0) && (var->var_id < MAXVARS); var++)
speakup_register_var(var);
for (i = 1; spk_punc_info[i].mask != 0; i++)
spk_set_mask_bits(NULL, i, 2);
spk_set_key_info(spk_key_defaults, spk_key_buf);
err = speakup_add_virtual_keyboard();
if (err)
goto error_virtkeyboard;
for (i = 0; i < MAX_NR_CONSOLES; i++)
if (vc_cons[i].d) {
err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
if (err)
goto error_kobjects;
}
if (spk_quiet_boot)
spk_shut_up |= 0x01;
err = speakup_kobj_init();
if (err)
goto error_kobjects;
spk_ttyio_register_ldisc();
synth_init(synth_name);
speakup_register_devsynth();
err = register_keyboard_notifier(&keyboard_notifier_block);
if (err)
goto error_kbdnotifier;
err = register_vt_notifier(&vt_notifier_block);
if (err)
goto error_vtnotifier;
speakup_task = kthread_create(speakup_thread, NULL, "speakup");
if (IS_ERR(speakup_task)) {
err = PTR_ERR(speakup_task);
goto error_task;
}
set_user_nice(speakup_task, 10);
wake_up_process(speakup_task);
pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
pr_info("synth name on entry is: %s\n", synth_name);
goto out;
error_task:
unregister_vt_notifier(&vt_notifier_block);
error_vtnotifier:
unregister_keyboard_notifier(&keyboard_notifier_block);
del_timer(&cursor_timer);
error_kbdnotifier:
speakup_unregister_devsynth();
mutex_lock(&spk_mutex);
synth_release();
mutex_unlock(&spk_mutex);
speakup_kobj_exit();
error_kobjects:
for (i = 0; i < MAX_NR_CONSOLES; i++)
kfree(speakup_console[i]);
speakup_remove_virtual_keyboard();
error_virtkeyboard:
for (i = 0; i < MAXVARS; i++)
speakup_unregister_var(i);
for (i = 0; i < 256; i++) {
if (spk_characters[i] != spk_default_chars[i])
kfree(spk_characters[i]);
}
spk_free_user_msgs();
out:
return err;
}
module_param_named(bell_pos, spk_vars[BELL_POS_ID].u.n.default_val, int, 0444);
module_param_named(spell_delay, spk_vars[SPELL_DELAY_ID].u.n.default_val, int, 0444);
module_param_named(attrib_bleep, spk_vars[ATTRIB_BLEEP_ID].u.n.default_val, int, 0444);
module_param_named(bleeps, spk_vars[BLEEPS_ID].u.n.default_val, int, 0444);
module_param_named(bleep_time, spk_vars[BLEEP_TIME_ID].u.n.default_val, int, 0444);
module_param_named(punc_level, spk_vars[PUNC_LEVEL_ID].u.n.default_val, int, 0444);
module_param_named(reading_punc, spk_vars[READING_PUNC_ID].u.n.default_val, int, 0444);
module_param_named(cursor_time, spk_vars[CURSOR_TIME_ID].u.n.default_val, int, 0444);
module_param_named(say_control, spk_vars[SAY_CONTROL_ID].u.n.default_val, int, 0444);
module_param_named(say_word_ctl, spk_vars[SAY_WORD_CTL_ID].u.n.default_val, int, 0444);
module_param_named(no_interrupt, spk_vars[NO_INTERRUPT_ID].u.n.default_val, int, 0444);
module_param_named(key_echo, spk_vars[KEY_ECHO_ID].u.n.default_val, int, 0444);
module_param_named(cur_phonetic, spk_vars[CUR_PHONETIC_ID].u.n.default_val, int, 0444);
MODULE_PARM_DESC(bell_pos, "This works much like a typewriter bell. If for example 72 is echoed to bell_pos, it will beep the PC speaker when typing on a line past character 72.");
MODULE_PARM_DESC(spell_delay, "This controls how fast a word is spelled when speakup's spell word review command is pressed.");
MODULE_PARM_DESC(attrib_bleep, "Beeps the PC speaker when there is an attribute change such as background color when using speakup review commands. One = on, zero = off.");
MODULE_PARM_DESC(bleeps, "This controls whether one hears beeps through the PC speaker when using speakup review commands.");
MODULE_PARM_DESC(bleep_time, "This controls the duration of the PC speaker beeps speakup produces.");
MODULE_PARM_DESC(punc_level, "Controls the level of punctuation spoken as the screen is displayed, not reviewed.");
MODULE_PARM_DESC(reading_punc, "It controls the level of punctuation when reviewing the screen with speakup's screen review commands.");
MODULE_PARM_DESC(cursor_time, "This controls cursor delay when using arrow keys.");
MODULE_PARM_DESC(say_control, "This controls if speakup speaks shift, alt and control when those keys are pressed or not.");
MODULE_PARM_DESC(say_word_ctl, "Sets the say_word_ctl on load.");
MODULE_PARM_DESC(no_interrupt, "Controls if typing interrupts output from speakup.");
MODULE_PARM_DESC(key_echo, "Controls if speakup speaks keys when they are typed. One = on zero = off or don't echo keys.");
MODULE_PARM_DESC(cur_phonetic, "Controls if speakup speaks letters phonetically during navigation. One = on zero = off or don't speak phonetically.");
module_init(speakup_init);
module_exit