1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * originally written by: Kirk Reiser <kirk@braille.uwo.ca> |
4 | * this version considerably modified by David Borowski, david575@rogers.com |
5 | * |
6 | * Copyright (C) 1998-99 Kirk Reiser. |
7 | * Copyright (C) 2003 David Borowski. |
8 | * |
9 | * specifically written as a driver for the speakup screenreview |
10 | * s not a general device driver. |
11 | */ |
12 | #include <linux/unistd.h> |
13 | #include <linux/proc_fs.h> |
14 | #include <linux/jiffies.h> |
15 | #include <linux/spinlock.h> |
16 | #include <linux/sched.h> |
17 | #include <linux/timer.h> |
18 | #include <linux/kthread.h> |
19 | #include "speakup.h" |
20 | #include "spk_priv.h" |
21 | |
22 | #define DRV_VERSION "2.20" |
23 | #define SYNTH_CLEAR 0x03 |
24 | #define PROCSPEECH 0x0b |
25 | static int xoff; |
26 | |
27 | static inline int synth_full(void) |
28 | { |
29 | return xoff; |
30 | } |
31 | |
32 | static void do_catch_up(struct spk_synth *synth); |
33 | static void synth_flush(struct spk_synth *synth); |
34 | static void read_buff_add(u_char c); |
35 | static unsigned char get_index(struct spk_synth *synth); |
36 | |
37 | static int in_escape; |
38 | static int is_flushing; |
39 | |
40 | static DEFINE_SPINLOCK(flush_lock); |
41 | static DECLARE_WAIT_QUEUE_HEAD(flush); |
42 | |
43 | enum default_vars_id { |
44 | CAPS_START_ID = 0, CAPS_STOP_ID, |
45 | RATE_ID, PITCH_ID, INFLECTION_ID, |
46 | VOL_ID, PUNCT_ID, VOICE_ID, |
47 | DIRECT_ID, V_LAST_VAR_ID, |
48 | NB_ID, |
49 | }; |
50 | |
51 | static struct var_t vars[NB_ID] = { |
52 | [CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 160] " } }, |
53 | [CAPS_STOP_ID] = { .var_id: CAPS_STOP, .u.s = {"[:dv ap 100 ] " } }, |
54 | [RATE_ID] = { .var_id: RATE, .u.n = {"[:ra %d] " , 180, 75, 650, 0, 0, NULL } }, |
55 | [PITCH_ID] = { .var_id: PITCH, .u.n = {"[:dv ap %d] " , 122, 50, 350, 0, 0, NULL } }, |
56 | [INFLECTION_ID] = { .var_id: INFLECTION, .u.n = {"[:dv pr %d] " , 100, 0, 10000, 0, 0, NULL } }, |
57 | [VOL_ID] = { .var_id: VOL, .u.n = {"[:dv g5 %d] " , 86, 60, 86, 0, 0, NULL } }, |
58 | [PUNCT_ID] = { .var_id: PUNCT, .u.n = {"[:pu %c] " , 0, 0, 2, 0, 0, "nsa" } }, |
59 | [VOICE_ID] = { .var_id: VOICE, .u.n = {"[:n%c] " , 0, 0, 9, 0, 0, "phfdburwkv" } }, |
60 | [DIRECT_ID] = { .var_id: DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, |
61 | V_LAST_VAR |
62 | }; |
63 | |
64 | /* |
65 | * These attributes will appear in /sys/accessibility/speakup/dectlk. |
66 | */ |
67 | static struct kobj_attribute caps_start_attribute = |
68 | __ATTR(caps_start, 0644, spk_var_show, spk_var_store); |
69 | static struct kobj_attribute caps_stop_attribute = |
70 | __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); |
71 | static struct kobj_attribute pitch_attribute = |
72 | __ATTR(pitch, 0644, spk_var_show, spk_var_store); |
73 | static struct kobj_attribute inflection_attribute = |
74 | __ATTR(inflection, 0644, spk_var_show, spk_var_store); |
75 | static struct kobj_attribute punct_attribute = |
76 | __ATTR(punct, 0644, spk_var_show, spk_var_store); |
77 | static struct kobj_attribute rate_attribute = |
78 | __ATTR(rate, 0644, spk_var_show, spk_var_store); |
79 | static struct kobj_attribute voice_attribute = |
80 | __ATTR(voice, 0644, spk_var_show, spk_var_store); |
81 | static struct kobj_attribute vol_attribute = |
82 | __ATTR(vol, 0644, spk_var_show, spk_var_store); |
83 | |
84 | static struct kobj_attribute delay_time_attribute = |
85 | __ATTR(delay_time, 0644, spk_var_show, spk_var_store); |
86 | static struct kobj_attribute direct_attribute = |
87 | __ATTR(direct, 0644, spk_var_show, spk_var_store); |
88 | static struct kobj_attribute full_time_attribute = |
89 | __ATTR(full_time, 0644, spk_var_show, spk_var_store); |
90 | static struct kobj_attribute flush_time_attribute = |
91 | __ATTR(flush_time, 0644, spk_var_show, spk_var_store); |
92 | static struct kobj_attribute jiffy_delta_attribute = |
93 | __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); |
94 | static struct kobj_attribute trigger_time_attribute = |
95 | __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); |
96 | |
97 | /* |
98 | * Create a group of attributes so that we can create and destroy them all |
99 | * at once. |
100 | */ |
101 | static struct attribute *synth_attrs[] = { |
102 | &caps_start_attribute.attr, |
103 | &caps_stop_attribute.attr, |
104 | &pitch_attribute.attr, |
105 | &inflection_attribute.attr, |
106 | &punct_attribute.attr, |
107 | &rate_attribute.attr, |
108 | &voice_attribute.attr, |
109 | &vol_attribute.attr, |
110 | &delay_time_attribute.attr, |
111 | &direct_attribute.attr, |
112 | &full_time_attribute.attr, |
113 | &flush_time_attribute.attr, |
114 | &jiffy_delta_attribute.attr, |
115 | &trigger_time_attribute.attr, |
116 | NULL, /* need to NULL terminate the list of attributes */ |
117 | }; |
118 | |
119 | static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306}; |
120 | static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73}; |
121 | |
122 | static struct spk_synth synth_dectlk = { |
123 | .name = "dectlk" , |
124 | .version = DRV_VERSION, |
125 | .long_name = "Dectalk Express" , |
126 | .init = "[:error sp :name paul :rate 180 :tsr off] " , |
127 | .procspeech = PROCSPEECH, |
128 | .clear = SYNTH_CLEAR, |
129 | .delay = 500, |
130 | .trigger = 50, |
131 | .jiffies = 50, |
132 | .full = 40000, |
133 | .flush_time = 4000, |
134 | .dev_name = SYNTH_DEFAULT_DEV, |
135 | .startup = SYNTH_START, |
136 | .checkval = SYNTH_CHECK, |
137 | .vars = vars, |
138 | .default_pitch = ap_defaults, |
139 | .default_vol = g5_defaults, |
140 | .io_ops = &spk_ttyio_ops, |
141 | .probe = spk_ttyio_synth_probe, |
142 | .release = spk_ttyio_release, |
143 | .synth_immediate = spk_ttyio_synth_immediate, |
144 | .catch_up = do_catch_up, |
145 | .flush = synth_flush, |
146 | .is_alive = spk_synth_is_alive_restart, |
147 | .synth_adjust = NULL, |
148 | .read_buff_add = read_buff_add, |
149 | .get_index = get_index, |
150 | .indexing = { |
151 | .command = "[:in re %d ] " , |
152 | .lowindex = 1, |
153 | .highindex = 8, |
154 | .currindex = 1, |
155 | }, |
156 | .attributes = { |
157 | .attrs = synth_attrs, |
158 | .name = "dectlk" , |
159 | }, |
160 | }; |
161 | |
162 | static int is_indnum(u_char *ch) |
163 | { |
164 | if ((*ch >= '0') && (*ch <= '9')) { |
165 | *ch = *ch - '0'; |
166 | return 1; |
167 | } |
168 | return 0; |
169 | } |
170 | |
171 | static u_char lastind; |
172 | |
173 | static unsigned char get_index(struct spk_synth *synth) |
174 | { |
175 | u_char rv; |
176 | |
177 | rv = lastind; |
178 | lastind = 0; |
179 | return rv; |
180 | } |
181 | |
182 | static void read_buff_add(u_char c) |
183 | { |
184 | static int ind = -1; |
185 | |
186 | if (c == 0x01) { |
187 | unsigned long flags; |
188 | |
189 | spin_lock_irqsave(&flush_lock, flags); |
190 | is_flushing = 0; |
191 | wake_up_interruptible(&flush); |
192 | spin_unlock_irqrestore(lock: &flush_lock, flags); |
193 | } else if (c == 0x13) { |
194 | xoff = 1; |
195 | } else if (c == 0x11) { |
196 | xoff = 0; |
197 | } else if (is_indnum(ch: &c)) { |
198 | if (ind == -1) |
199 | ind = c; |
200 | else |
201 | ind = ind * 10 + c; |
202 | } else if ((c > 31) && (c < 127)) { |
203 | if (ind != -1) |
204 | lastind = (u_char)ind; |
205 | ind = -1; |
206 | } |
207 | } |
208 | |
209 | static void do_catch_up(struct spk_synth *synth) |
210 | { |
211 | int synth_full_val = 0; |
212 | static u_char ch; |
213 | static u_char last = '\0'; |
214 | unsigned long flags; |
215 | unsigned long jiff_max; |
216 | unsigned long timeout; |
217 | DEFINE_WAIT(wait); |
218 | struct var_t *jiffy_delta; |
219 | struct var_t *delay_time; |
220 | struct var_t *flush_time; |
221 | int jiffy_delta_val; |
222 | int delay_time_val; |
223 | int timeout_val; |
224 | |
225 | jiffy_delta = spk_get_var(var_id: JIFFY); |
226 | delay_time = spk_get_var(var_id: DELAY); |
227 | flush_time = spk_get_var(var_id: FLUSH); |
228 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
229 | jiffy_delta_val = jiffy_delta->u.n.value; |
230 | timeout_val = flush_time->u.n.value; |
231 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
232 | timeout = msecs_to_jiffies(m: timeout_val); |
233 | jiff_max = jiffies + jiffy_delta_val; |
234 | |
235 | while (!kthread_should_stop()) { |
236 | /* if no ctl-a in 4, send data anyway */ |
237 | spin_lock_irqsave(&flush_lock, flags); |
238 | while (is_flushing && timeout) { |
239 | prepare_to_wait(wq_head: &flush, wq_entry: &wait, TASK_INTERRUPTIBLE); |
240 | spin_unlock_irqrestore(lock: &flush_lock, flags); |
241 | timeout = schedule_timeout(timeout); |
242 | spin_lock_irqsave(&flush_lock, flags); |
243 | } |
244 | finish_wait(wq_head: &flush, wq_entry: &wait); |
245 | is_flushing = 0; |
246 | spin_unlock_irqrestore(lock: &flush_lock, flags); |
247 | |
248 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
249 | if (speakup_info.flushing) { |
250 | speakup_info.flushing = 0; |
251 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
252 | synth->flush(synth); |
253 | continue; |
254 | } |
255 | synth_buffer_skip_nonlatin1(); |
256 | if (synth_buffer_empty()) { |
257 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
258 | break; |
259 | } |
260 | ch = synth_buffer_peek(); |
261 | set_current_state(TASK_INTERRUPTIBLE); |
262 | delay_time_val = delay_time->u.n.value; |
263 | synth_full_val = synth_full(); |
264 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
265 | if (ch == '\n') |
266 | ch = 0x0D; |
267 | if (synth_full_val || !synth->io_ops->synth_out(synth, ch)) { |
268 | schedule_timeout(timeout: msecs_to_jiffies(m: delay_time_val)); |
269 | continue; |
270 | } |
271 | set_current_state(TASK_RUNNING); |
272 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
273 | synth_buffer_getc(); |
274 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
275 | if (ch == '[') { |
276 | in_escape = 1; |
277 | } else if (ch == ']') { |
278 | in_escape = 0; |
279 | } else if (ch <= SPACE) { |
280 | if (!in_escape && strchr(",.!?;:" , last)) |
281 | synth->io_ops->synth_out(synth, PROCSPEECH); |
282 | if (time_after_eq(jiffies, jiff_max)) { |
283 | if (!in_escape) |
284 | synth->io_ops->synth_out(synth, |
285 | PROCSPEECH); |
286 | spin_lock_irqsave(&speakup_info.spinlock, |
287 | flags); |
288 | jiffy_delta_val = jiffy_delta->u.n.value; |
289 | delay_time_val = delay_time->u.n.value; |
290 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, |
291 | flags); |
292 | schedule_timeout(timeout: msecs_to_jiffies |
293 | (m: delay_time_val)); |
294 | jiff_max = jiffies + jiffy_delta_val; |
295 | } |
296 | } |
297 | last = ch; |
298 | } |
299 | if (!in_escape) |
300 | synth->io_ops->synth_out(synth, PROCSPEECH); |
301 | } |
302 | |
303 | static void synth_flush(struct spk_synth *synth) |
304 | { |
305 | if (in_escape) |
306 | /* if in command output ']' so we don't get an error */ |
307 | synth->io_ops->synth_out(synth, ']'); |
308 | in_escape = 0; |
309 | is_flushing = 1; |
310 | synth->io_ops->flush_buffer(synth); |
311 | synth->io_ops->synth_out(synth, SYNTH_CLEAR); |
312 | } |
313 | |
314 | module_param_named(ser, synth_dectlk.ser, int, 0444); |
315 | module_param_named(dev, synth_dectlk.dev_name, charp, 0444); |
316 | module_param_named(start, synth_dectlk.startup, short, 0444); |
317 | module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444); |
318 | module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444); |
319 | module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444); |
320 | module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444); |
321 | module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444); |
322 | module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444); |
323 | module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444); |
324 | |
325 | |
326 | |
327 | MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)." ); |
328 | MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer." ); |
329 | MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded." ); |
330 | MODULE_PARM_DESC(rate, "Set the rate variable on load." ); |
331 | MODULE_PARM_DESC(pitch, "Set the pitch variable on load." ); |
332 | MODULE_PARM_DESC(inflection, "Set the inflection variable on load." ); |
333 | MODULE_PARM_DESC(vol, "Set the vol variable on load." ); |
334 | MODULE_PARM_DESC(punct, "Set the punct variable on load." ); |
335 | MODULE_PARM_DESC(voice, "Set the voice variable on load." ); |
336 | MODULE_PARM_DESC(direct, "Set the direct variable on load." ); |
337 | |
338 | |
339 | module_spk_synth(synth_dectlk); |
340 | |
341 | MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>" ); |
342 | MODULE_AUTHOR("David Borowski" ); |
343 | MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers" ); |
344 | MODULE_LICENSE("GPL" ); |
345 | MODULE_VERSION(DRV_VERSION); |
346 | |
347 | |