brintos

brintos / linux-shallow public Read only

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