1c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs/*
2c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * This is the DECtalk PC speakup driver
3c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
4c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Some constants from DEC's DOS driver:
5c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *      Copyright (c) by Digital Equipment Corp.
6c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
7c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * 386BSD DECtalk PC driver:
8c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *      Copyright (c) 1996 Brian Buhrow <buhrow@lothlorien.nfbcal.org>
9c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
10c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Linux DECtalk PC driver:
11c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *      Copyright (c) 1997 Nicolas Pitre <nico@cam.org>
12c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
13c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * speakup DECtalk PC Internal driver:
14c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *      Copyright (c) 2003 David Borowski <david575@golden.net>
15c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
16c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * All rights reserved.
17c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
18c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * This program is free software; you can redistribute it and/or modify
19c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * it under the terms of the GNU General Public License as published by
20c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * the Free Software Foundation; either version 2 of the License, or
21c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * (at your option) any later version.
22c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
23c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * This program is distributed in the hope that it will be useful,
24c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * but WITHOUT ANY WARRANTY; without even the implied warranty of
25c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * GNU General Public License for more details.
27c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs *
28c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * You should have received a copy of the GNU General Public License
29c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * along with this program; if not, write to the Free Software
30c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs */
32c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/jiffies.h>
33c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/sched.h>
34c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/timer.h>
35c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include <linux/kthread.h>
36c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
37c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include "spk_priv.h"
38c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#include "speakup.h"
39c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
40c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODULE_init		0x0dec		/* module in boot code */
41c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODULE_self_test	0x8800		/* module in self-test */
42c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODULE_reset		0xffff		/* reinit the whole module */
43c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
44c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODE_mask		0xf000		/* mode bits in high nibble */
45c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODE_null		0x0000
46c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODE_test		0x2000		/* in testing mode */
47c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODE_status		0x8000
48c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_int		0x0001		/* running in interrupt mode */
49c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_tr_char		0x0002	/* character data to transmit */
50c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_rr_char		0x0004	/* ready to receive char data */
51c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_cmd_ready		0x0008	/* ready to accept commands */
52c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_dma_ready		0x0010	/* dma command ready */
53c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_digitized		0x0020	/* spc in digitized mode */
54c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_new_index		0x0040	/* new last index ready */
55c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_new_status		0x0080	/* new status posted */
56c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_dma_state		0x0100	/* dma state toggle */
57c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_index_valid	0x0200	/* indexs are valid */
58c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_flushing		0x0400	/* flush in progress */
59c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	STAT_self_test		0x0800	/* module in self test */
60c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODE_ready		0xc000	/* module ready for next phase */
61c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	READY_boot		0x0000
62c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	READY_kernel		0x0001
63c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	MODE_error		0xf000
64c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
65c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_mask		0xf000	/* mask for command nibble */
66c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_null		0x0000	/* post status */
67c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_control		0x1000	/* hard control command */
68c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_mask		0x0F00	/*   mask off control nibble */
6983414d528d2f97d6574c14afb892879c11bbcc7cJustin P. Mattock#define	CTRL_data		0x00FF	/*   mask to get data byte */
70c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_null		0x0000	/*   null control */
71c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_vol_up		0x0100	/*   increase volume */
72c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_vol_down		0x0200	/*   decrease volume */
73c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_vol_set		0x0300	/*   set volume */
74c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_pause		0x0400	/*   pause spc */
75c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_resume		0x0500	/*   resume spc clock */
76c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_resume_spc		0x0001	/*   resume spc soft pause */
77c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_flush		0x0600	/*   flush all buffers */
78c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_int_enable	0x0700	/*   enable status change ints */
79c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_buff_free		0x0800	/*   buffer remain count */
80c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_buff_used		0x0900	/*   buffer in use */
81c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_speech		0x0a00	/*   immediate speech change */
82c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	   CTRL_SP_voice	0x0001	/*       voice change */
83c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	   CTRL_SP_rate		0x0002	/*       rate change */
84c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	   CTRL_SP_comma	0x0003	/*       comma pause change */
85c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	   CTRL_SP_period	0x0004	/*       period pause change */
86c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	   CTRL_SP_rate_delta	0x0005	/*       delta rate change */
87c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	   CTRL_SP_get_param	0x0006	/*       return the desired parameter */
88c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_last_index		0x0b00	/*   get last index spoken */
89c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_io_priority	0x0c00	/*   change i/o priority */
90c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_free_mem		0x0d00	/*   get free paragraphs on module */
91c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CTRL_get_lang		0x0e00	/*   return bit mask of loaded
92c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs					 *   languages */
93c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_test			0x2000		/* self-test request */
94c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_mask		0x0F00	/* isolate test field */
95c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_null		0x0000	/* no test requested */
96c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_isa_int		0x0100	/* assert isa irq */
97c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_echo		0x0200	/* make data in == data out */
98c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_seg		0x0300	/* set peek/poke segment */
99c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_off		0x0400	/* set peek/poke offset */
100c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_peek		0x0500	/* data out == *peek */
101c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_poke		0x0600	/* *peek == data in */
102c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	TEST_sub_code		0x00FF	/* user defined test sub codes */
103c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_id			0x3000	/* return software id */
104c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	ID_null			0x0000	/* null id */
105c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	ID_kernel		0x0100	/* kernel code executing */
106c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	ID_boot			0x0200	/* boot code executing */
107c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_dma			0x4000		/* force a dma start */
108c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_reset		0x5000		/* reset module status */
109c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_sync		0x6000		/* kernel sync command */
110c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_char_in		0x7000		/* single character send */
111c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_char_out		0x8000		/* single character get */
112c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CHAR_count_1		0x0100	/*    one char in cmd_low */
113c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CHAR_count_2		0x0200	/*	the second in data_low */
114c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CHAR_count_3		0x0300	/*	the third in data_high */
115c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_spc_mode		0x9000		/* change spc mode */
116c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_spc_to_text		0x0100	/*   set to text mode */
117c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_spc_to_digit	0x0200	/*   set to digital mode */
118c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_spc_rate		0x0400	/*   change spc data rate */
119c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	CMD_error		0xf000		/* severe error */
120c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
121c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsenum {	PRIMARY_DIC	= 0, USER_DIC, COMMAND_DIC, ABBREV_DIC };
122c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
123c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_single_in		0x01
124c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_single_out		0x02
125c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_buff_in		0x03
126c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_buff_out		0x04
127c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_control		0x05
128c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DT_MEM_ALLOC		0x03
129c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DT_SET_DIC		0x04
130c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DT_START_TASK		0x05
131c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DT_LOAD_MEM		0x06
132c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DT_READ_MEM		0x07
133c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DT_DIGITAL_IN		0x08
134c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_sync		0x06
135c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define	DMA_sync_char		0x07
136c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
137c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define DRV_VERSION "2.12"
138c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define PROCSPEECH 0x0b
139c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs#define SYNTH_IO_EXTENT 8
140c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
141c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int synth_probe(struct spk_synth *synth);
142c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void dtpc_release(void);
143c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic const char *synth_immediate(struct spk_synth *synth, const char *buf);
144c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void do_catch_up(struct spk_synth *synth);
145c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void synth_flush(struct spk_synth *synth);
146c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
147c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 };
148c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int in_escape, is_flushing;
149c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int dt_stat, dma_state;
150c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
151c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct var_t vars[] = {
152227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ CAPS_START, .u.s = {"[:dv ap 200]" } },
153227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ CAPS_STOP, .u.s = {"[:dv ap 100]" } },
154227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } },
155227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } },
156227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } },
157227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
158227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
159227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	{ DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
160c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	V_LAST_VAR
161c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs};
162c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
163c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs/*
164c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * These attributes will appear in /sys/accessibility/speakup/decpc.
165c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs */
166c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute caps_start_attribute =
167d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
168c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute caps_stop_attribute =
169d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
170c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute pitch_attribute =
171d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
172c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute punct_attribute =
173d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
174c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute rate_attribute =
175d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
176c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute voice_attribute =
177d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
178c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute vol_attribute =
179d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
180c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
181c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute delay_time_attribute =
18222c9bcad859d5c969289b3b37084a96c621f8f2cRusty Russell	__ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
183c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute direct_attribute =
184d901aaa723a7ea4601b0984534dde70adc81a38cRusty Russell	__ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
185c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute full_time_attribute =
18622c9bcad859d5c969289b3b37084a96c621f8f2cRusty Russell	__ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
187c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute jiffy_delta_attribute =
18822c9bcad859d5c969289b3b37084a96c621f8f2cRusty Russell	__ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
189c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct kobj_attribute trigger_time_attribute =
19022c9bcad859d5c969289b3b37084a96c621f8f2cRusty Russell	__ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
191c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
192c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs/*
193c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * Create a group of attributes so that we can create and destroy them all
194c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs * at once.
195c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs */
196c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct attribute *synth_attrs[] = {
197c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&caps_start_attribute.attr,
198c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&caps_stop_attribute.attr,
199c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&pitch_attribute.attr,
200c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&punct_attribute.attr,
201c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&rate_attribute.attr,
202c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&voice_attribute.attr,
203c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&vol_attribute.attr,
204c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&delay_time_attribute.attr,
205c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&direct_attribute.attr,
206c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&full_time_attribute.attr,
207c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&jiffy_delta_attribute.attr,
208c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	&trigger_time_attribute.attr,
209c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	NULL,	/* need to NULL terminate the list of attributes */
210c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs};
211c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
212c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic struct spk_synth synth_dec_pc = {
213c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.name = "decpc",
214c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.version = DRV_VERSION,
215c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.long_name = "Dectalk PC",
216c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.init = "[:pe -380]",
217c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.procspeech = PROCSPEECH,
218c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.delay = 500,
219c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.trigger = 50,
220c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.jiffies = 50,
221c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.full = 1000,
222c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.flags = SF_DEC,
223c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.startup = SYNTH_START,
224c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.checkval = SYNTH_CHECK,
225c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.vars = vars,
226c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.probe = synth_probe,
227c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.release = dtpc_release,
228c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.synth_immediate = synth_immediate,
229c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.catch_up = do_catch_up,
230c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.flush = synth_flush,
231c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.is_alive = spk_synth_is_alive_nop,
232c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.synth_adjust = NULL,
233c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.read_buff_add = NULL,
234c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.get_index = NULL,
235c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.indexing = {
236c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.command = NULL,
237c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.lowindex = 0,
238c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.highindex = 0,
239c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.currindex = 0,
240c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	},
241c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	.attributes = {
242c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.attrs = synth_attrs,
243c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		.name = "decpc",
244c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	},
245c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs};
246c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
247c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int dt_getstatus(void)
248c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
249c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dt_stat = inb_p(speakup_info.port_tts) |
250c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		 (inb_p(speakup_info.port_tts + 1) << 8);
251c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return dt_stat;
252c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
253c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
254c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void dt_sendcmd(u_int cmd)
255c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
256c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(cmd & 0xFF, speakup_info.port_tts);
257c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p((cmd >> 8) & 0xFF, speakup_info.port_tts+1);
258c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
259c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
260c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int dt_waitbit(int bit)
261c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
262c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int timeout = 100;
2638e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
264c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (--timeout > 0) {
265c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if ((dt_getstatus() & bit) == bit)
266c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			return 1;
267c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		udelay(50);
268c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
269c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 0;
270c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
271c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
272c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int dt_wait_dma(void)
273c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
274c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int timeout = 100, state = dma_state;
2758e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
276c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (!dt_waitbit(STAT_dma_ready))
277c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return 0;
278c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (--timeout > 0) {
279c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if ((dt_getstatus()&STAT_dma_state) == state)
280c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			return 1;
281c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		udelay(50);
282c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
283c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dma_state = dt_getstatus() & STAT_dma_state;
284c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 1;
285c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
286c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
287c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int dt_ctrl(u_int cmd)
288c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
289c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int timeout = 10;
2908e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
291c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (!dt_waitbit(STAT_cmd_ready))
292c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return -1;
293c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(0, speakup_info.port_tts+2);
294c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(0, speakup_info.port_tts+3);
295c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dt_getstatus();
296c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dt_sendcmd(CMD_control|cmd);
297c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(0, speakup_info.port_tts+6);
298c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (dt_getstatus() & STAT_cmd_ready) {
299c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		udelay(20);
300c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (--timeout == 0)
301c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
302c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
303c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dt_sendcmd(CMD_null);
304c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 0;
305c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
306c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
307c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void synth_flush(struct spk_synth *synth)
308c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
309c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int timeout = 10;
3108e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
311c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (is_flushing)
312c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return;
313c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	is_flushing = 4;
314c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	in_escape = 0;
315c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (dt_ctrl(CTRL_flush)) {
316c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (--timeout == 0)
317c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
318c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsudelay(50);
319c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
320c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	for (timeout = 0; timeout < 10; timeout++) {
321c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (dt_waitbit(STAT_dma_ready))
322c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
323c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsudelay(50);
324c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
325c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(DMA_sync, speakup_info.port_tts+4);
326c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(0, speakup_info.port_tts+4);
327c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	udelay(100);
328c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	for (timeout = 0; timeout < 10; timeout++) {
329c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (!(dt_getstatus() & STAT_flushing))
330c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
331c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsudelay(50);
332c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
333c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dma_state = dt_getstatus() & STAT_dma_state;
334c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dma_state ^= STAT_dma_state;
335c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	is_flushing = 0;
336c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
337c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
338c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int dt_sendchar(char ch)
339c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
340c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (!dt_wait_dma())
341c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return -1;
342c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (!(dt_stat & STAT_rr_char))
343c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return -2;
344c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(DMA_single_in, speakup_info.port_tts+4);
345c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	outb_p(ch, speakup_info.port_tts+4);
346c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dma_state ^= STAT_dma_state;
347c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 0;
348c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
349c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
350c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int testkernel(void)
351c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
352c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int status = 0;
3538e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
354c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (dt_getstatus() == 0xffff) {
355c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		status = -1;
356c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		goto oops;
357c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
358c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	dt_sendcmd(CMD_sync);
359c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (!dt_waitbit(STAT_cmd_ready))
360c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		status = -2;
361c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	else if (dt_stat&0x8000)
362c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return 0;
363c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	else if (dt_stat == 0x0dec)
364c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		pr_warn("dec_pc at 0x%x, software not loaded\n",
365c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				speakup_info.port_tts);
366c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	status = -3;
367c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsoops:	synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
368c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	speakup_info.port_tts = 0;
369c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return status;
370c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
371c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
372c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void do_catch_up(struct spk_synth *synth)
373c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
374c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	u_char ch;
375227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon	static u_char last;
376c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	unsigned long flags;
377c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	unsigned long jiff_max;
378c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	struct var_t *jiffy_delta;
379c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	struct var_t *delay_time;
380c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int jiffy_delta_val;
381c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int delay_time_val;
382c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
383ca2beaf84d9678c12b17d92623f0e90829d6ca13Samuel Thibault	jiffy_delta = spk_get_var(JIFFY);
384ca2beaf84d9678c12b17d92623f0e90829d6ca13Samuel Thibault	delay_time = spk_get_var(DELAY);
3854d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs	spin_lock_irqsave(&speakup_info.spinlock, flags);
386c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	jiffy_delta_val = jiffy_delta->u.n.value;
3874d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
388c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	jiff_max = jiffies + jiffy_delta_val;
389c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
390c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while (!kthread_should_stop()) {
3914d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs		spin_lock_irqsave(&speakup_info.spinlock, flags);
392c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (speakup_info.flushing) {
393c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			speakup_info.flushing = 0;
3944d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
395c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			synth->flush(synth);
396c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			continue;
397c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
398c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (synth_buffer_empty()) {
3994d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
400c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
401c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
402c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		ch = synth_buffer_peek();
403c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		set_current_state(TASK_INTERRUPTIBLE);
404c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		delay_time_val = delay_time->u.n.value;
4054d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
406c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (ch == '\n')
407c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			ch = 0x0D;
408c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (dt_sendchar(ch)) {
409c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			schedule_timeout(msecs_to_jiffies(delay_time_val));
410c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			continue;
411c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
412c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		set_current_state(TASK_RUNNING);
4134d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs		spin_lock_irqsave(&speakup_info.spinlock, flags);
414c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_buffer_getc();
4154d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
416c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (ch == '[')
417c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			in_escape = 1;
418c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		else if (ch == ']')
419c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			in_escape = 0;
420c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		else if (ch <= SPACE) {
421c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			if (!in_escape && strchr(",.!?;:", last))
422c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				dt_sendchar(PROCSPEECH);
42389021ecc8364e727a24b0683a74515c8ddf308dbTapasweni Pathak			if (time_after_eq(jiffies, jiff_max)) {
424c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				if (!in_escape)
425c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs					dt_sendchar(PROCSPEECH);
4264d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs				spin_lock_irqsave(&speakup_info.spinlock, flags);
427c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				jiffy_delta_val = jiffy_delta->u.n.value;
428c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				delay_time_val = delay_time->u.n.value;
4294d888b292a6a6e249f3960b3f9673e178c87aacaWilliam Hubbs				spin_unlock_irqrestore(&speakup_info.spinlock, flags);
430227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon				schedule_timeout(msecs_to_jiffies
431227e18d6b62550dafb822de2f3d673e3791a13e0Christopher Brannon						 (delay_time_val));
432c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				jiff_max = jiffies + jiffy_delta_val;
433c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			}
434c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
435c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		last = ch;
436c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		ch = 0;
437c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
438c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (!in_escape)
439c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		dt_sendchar(PROCSPEECH);
440c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
441c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
442c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic const char *synth_immediate(struct spk_synth *synth, const char *buf)
443c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
444c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	u_char ch;
4458e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
446c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	while ((ch = *buf)) {
447c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (ch == '\n')
448c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			ch = PROCSPEECH;
449c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (dt_sendchar(ch))
450c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			return buf;
451c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		buf++;
452c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
4536eb178cebb5b380377033a972a9803adb5a58edeSachin Kamat	return NULL;
454c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
455c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
456c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int synth_probe(struct spk_synth *synth)
457c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
458c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	int i = 0, failed = 0;
4598e69a8110686572a4b88d006faa8c3c759c4c261Domagoj Trsan
460c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	pr_info("Probing for %s.\n", synth->long_name);
461c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	for (i = 0; synth_portlist[i]; i++) {
462c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (synth_request_region(synth_portlist[i], SYNTH_IO_EXTENT)) {
463c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			pr_warn("request_region: failed with 0x%x, %d\n",
464c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs				synth_portlist[i], SYNTH_IO_EXTENT);
465c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			continue;
466c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		}
467c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		speakup_info.port_tts = synth_portlist[i];
468c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		failed = testkernel();
469c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		if (failed == 0)
470c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs			break;
471c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
472c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (failed) {
473c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		pr_info("%s: not found\n", synth->long_name);
474c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		return -ENODEV;
475c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	}
476c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	pr_info("%s: %03x-%03x, Driver Version %s,\n", synth->long_name,
477c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		speakup_info.port_tts, speakup_info.port_tts + 7,
478c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth->version);
479c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	synth->alive = 1;
480c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return 0;
481c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
482c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
483c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void dtpc_release(void)
484c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
485c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	if (speakup_info.port_tts)
486c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs		synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
487c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	speakup_info.port_tts = 0;
488c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
489c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
490c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_param_named(start, synth_dec_pc.startup, short, S_IRUGO);
491c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
492c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
493c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
494c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic int __init decpc_init(void)
495c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
496c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	return synth_add(&synth_dec_pc);
497c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
498c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
499c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsstatic void __exit decpc_exit(void)
500c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs{
501c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs	synth_remove(&synth_dec_pc);
502c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs}
503c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
504c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_init(decpc_init);
505c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbsmodule_exit(decpc_exit);
506c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
507c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_AUTHOR("David Borowski");
508c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_DESCRIPTION("Speakup support for DECtalk PC synthesizers");
509c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_LICENSE("GPL");
510c6e3fd22cd538365bfeb82997d5b89562e077d42William HubbsMODULE_VERSION(DRV_VERSION);
511c6e3fd22cd538365bfeb82997d5b89562e077d42William Hubbs
512