1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/types.h> |
3 | #include <linux/ctype.h> /* for isdigit() and friends */ |
4 | #include <linux/fs.h> |
5 | #include <linux/mm.h> /* for verify_area */ |
6 | #include <linux/errno.h> /* for -EBUSY */ |
7 | #include <linux/ioport.h> /* for check_region, request_region */ |
8 | #include <linux/interrupt.h> |
9 | #include <linux/delay.h> /* for loops_per_sec */ |
10 | #include <linux/kmod.h> |
11 | #include <linux/jiffies.h> |
12 | #include <linux/uaccess.h> /* for copy_from_user */ |
13 | #include <linux/sched.h> |
14 | #include <linux/timer.h> |
15 | #include <linux/kthread.h> |
16 | |
17 | #include "spk_priv.h" |
18 | #include "speakup.h" |
19 | #include "serialio.h" |
20 | |
21 | static LIST_HEAD(synths); |
22 | struct spk_synth *synth; |
23 | char spk_pitch_buff[32] = "" ; |
24 | static int module_status; |
25 | bool spk_quiet_boot; |
26 | |
27 | struct speakup_info_t speakup_info = { |
28 | /* |
29 | * This spinlock is used to protect the entire speakup machinery, and |
30 | * must be taken at each kernel->speakup transition and released at |
31 | * each corresponding speakup->kernel transition. |
32 | * |
33 | * The progression thread only interferes with the speakup machinery |
34 | * through the synth buffer, so only needs to take the lock |
35 | * while tinkering with the buffer. |
36 | * |
37 | * We use spin_lock/trylock_irqsave and spin_unlock_irqrestore with this |
38 | * spinlock because speakup needs to disable the keyboard IRQ. |
39 | */ |
40 | .spinlock = __SPIN_LOCK_UNLOCKED(speakup_info.spinlock), |
41 | .flushing = 0, |
42 | }; |
43 | EXPORT_SYMBOL_GPL(speakup_info); |
44 | |
45 | static int do_synth_init(struct spk_synth *in_synth); |
46 | |
47 | /* |
48 | * Main loop of the progression thread: keep eating from the buffer |
49 | * and push to the serial port, waiting as needed |
50 | * |
51 | * For devices that have a "full" notification mechanism, the driver can |
52 | * adapt the loop the way they prefer. |
53 | */ |
54 | static void _spk_do_catch_up(struct spk_synth *synth, int unicode) |
55 | { |
56 | u16 ch; |
57 | unsigned long flags; |
58 | unsigned long jiff_max; |
59 | struct var_t *delay_time; |
60 | struct var_t *full_time; |
61 | struct var_t *jiffy_delta; |
62 | int jiffy_delta_val; |
63 | int delay_time_val; |
64 | int full_time_val; |
65 | int ret; |
66 | |
67 | jiffy_delta = spk_get_var(var_id: JIFFY); |
68 | full_time = spk_get_var(var_id: FULL); |
69 | delay_time = spk_get_var(var_id: DELAY); |
70 | |
71 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
72 | jiffy_delta_val = jiffy_delta->u.n.value; |
73 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
74 | |
75 | jiff_max = jiffies + jiffy_delta_val; |
76 | while (!kthread_should_stop()) { |
77 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
78 | if (speakup_info.flushing) { |
79 | speakup_info.flushing = 0; |
80 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
81 | synth->flush(synth); |
82 | continue; |
83 | } |
84 | if (!unicode) |
85 | synth_buffer_skip_nonlatin1(); |
86 | if (synth_buffer_empty()) { |
87 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
88 | break; |
89 | } |
90 | ch = synth_buffer_peek(); |
91 | set_current_state(TASK_INTERRUPTIBLE); |
92 | full_time_val = full_time->u.n.value; |
93 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
94 | if (ch == '\n') |
95 | ch = synth->procspeech; |
96 | if (unicode) |
97 | ret = synth->io_ops->synth_out_unicode(synth, ch); |
98 | else |
99 | ret = synth->io_ops->synth_out(synth, ch); |
100 | if (!ret) { |
101 | schedule_timeout(timeout: msecs_to_jiffies(m: full_time_val)); |
102 | continue; |
103 | } |
104 | if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { |
105 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
106 | jiffy_delta_val = jiffy_delta->u.n.value; |
107 | delay_time_val = delay_time->u.n.value; |
108 | full_time_val = full_time->u.n.value; |
109 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
110 | if (synth->io_ops->synth_out(synth, synth->procspeech)) |
111 | schedule_timeout( |
112 | timeout: msecs_to_jiffies(m: delay_time_val)); |
113 | else |
114 | schedule_timeout( |
115 | timeout: msecs_to_jiffies(m: full_time_val)); |
116 | jiff_max = jiffies + jiffy_delta_val; |
117 | } |
118 | set_current_state(TASK_RUNNING); |
119 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
120 | synth_buffer_getc(); |
121 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
122 | } |
123 | synth->io_ops->synth_out(synth, synth->procspeech); |
124 | } |
125 | |
126 | void spk_do_catch_up(struct spk_synth *synth) |
127 | { |
128 | _spk_do_catch_up(synth, unicode: 0); |
129 | } |
130 | EXPORT_SYMBOL_GPL(spk_do_catch_up); |
131 | |
132 | void spk_do_catch_up_unicode(struct spk_synth *synth) |
133 | { |
134 | _spk_do_catch_up(synth, unicode: 1); |
135 | } |
136 | EXPORT_SYMBOL_GPL(spk_do_catch_up_unicode); |
137 | |
138 | void spk_synth_flush(struct spk_synth *synth) |
139 | { |
140 | synth->io_ops->flush_buffer(synth); |
141 | synth->io_ops->synth_out(synth, synth->clear); |
142 | } |
143 | EXPORT_SYMBOL_GPL(spk_synth_flush); |
144 | |
145 | unsigned char spk_synth_get_index(struct spk_synth *synth) |
146 | { |
147 | return synth->io_ops->synth_in_nowait(synth); |
148 | } |
149 | EXPORT_SYMBOL_GPL(spk_synth_get_index); |
150 | |
151 | int spk_synth_is_alive_nop(struct spk_synth *synth) |
152 | { |
153 | synth->alive = 1; |
154 | return 1; |
155 | } |
156 | EXPORT_SYMBOL_GPL(spk_synth_is_alive_nop); |
157 | |
158 | int spk_synth_is_alive_restart(struct spk_synth *synth) |
159 | { |
160 | if (synth->alive) |
161 | return 1; |
162 | if (synth->io_ops->wait_for_xmitr(synth) > 0) { |
163 | /* restart */ |
164 | synth->alive = 1; |
165 | synth_printf(buf: "%s" , synth->init); |
166 | return 2; /* reenabled */ |
167 | } |
168 | pr_warn("%s: can't restart synth\n" , synth->long_name); |
169 | return 0; |
170 | } |
171 | EXPORT_SYMBOL_GPL(spk_synth_is_alive_restart); |
172 | |
173 | static void thread_wake_up(struct timer_list *unused) |
174 | { |
175 | wake_up_interruptible_all(&speakup_event); |
176 | } |
177 | |
178 | static DEFINE_TIMER(thread_timer, thread_wake_up); |
179 | |
180 | void synth_start(void) |
181 | { |
182 | struct var_t *trigger_time; |
183 | |
184 | if (!synth->alive) { |
185 | synth_buffer_clear(); |
186 | return; |
187 | } |
188 | trigger_time = spk_get_var(var_id: TRIGGER); |
189 | if (!timer_pending(timer: &thread_timer)) |
190 | mod_timer(timer: &thread_timer, expires: jiffies + |
191 | msecs_to_jiffies(m: trigger_time->u.n.value)); |
192 | } |
193 | |
194 | void spk_do_flush(void) |
195 | { |
196 | if (!synth) |
197 | return; |
198 | |
199 | speakup_info.flushing = 1; |
200 | synth_buffer_clear(); |
201 | if (synth->alive) { |
202 | if (spk_pitch_shift) { |
203 | synth_printf(buf: "%s" , spk_pitch_buff); |
204 | spk_pitch_shift = 0; |
205 | } |
206 | } |
207 | wake_up_interruptible_all(&speakup_event); |
208 | wake_up_process(tsk: speakup_task); |
209 | } |
210 | |
211 | void synth_write(const char *_buf, size_t count) |
212 | { |
213 | const unsigned char *buf = (const unsigned char *) _buf; |
214 | |
215 | while (count--) |
216 | synth_buffer_add(ch: *buf++); |
217 | synth_start(); |
218 | } |
219 | |
220 | void synth_printf(const char *fmt, ...) |
221 | { |
222 | va_list args; |
223 | unsigned char buf[160], *p; |
224 | int r; |
225 | |
226 | va_start(args, fmt); |
227 | r = vsnprintf(buf, size: sizeof(buf), fmt, args); |
228 | va_end(args); |
229 | if (r > sizeof(buf) - 1) |
230 | r = sizeof(buf) - 1; |
231 | |
232 | p = buf; |
233 | while (r--) |
234 | synth_buffer_add(ch: *p++); |
235 | synth_start(); |
236 | } |
237 | EXPORT_SYMBOL_GPL(synth_printf); |
238 | |
239 | void synth_putwc(u16 wc) |
240 | { |
241 | synth_buffer_add(ch: wc); |
242 | } |
243 | EXPORT_SYMBOL_GPL(synth_putwc); |
244 | |
245 | void synth_putwc_s(u16 wc) |
246 | { |
247 | synth_buffer_add(ch: wc); |
248 | synth_start(); |
249 | } |
250 | EXPORT_SYMBOL_GPL(synth_putwc_s); |
251 | |
252 | void synth_putws(const u16 *buf) |
253 | { |
254 | const u16 *p; |
255 | |
256 | for (p = buf; *p; p++) |
257 | synth_buffer_add(ch: *p); |
258 | } |
259 | EXPORT_SYMBOL_GPL(synth_putws); |
260 | |
261 | void synth_putws_s(const u16 *buf) |
262 | { |
263 | synth_putws(buf); |
264 | synth_start(); |
265 | } |
266 | EXPORT_SYMBOL_GPL(synth_putws_s); |
267 | |
268 | static int index_count; |
269 | static int sentence_count; |
270 | |
271 | void spk_reset_index_count(int sc) |
272 | { |
273 | static int first = 1; |
274 | |
275 | if (first) |
276 | first = 0; |
277 | else |
278 | synth->get_index(synth); |
279 | index_count = 0; |
280 | sentence_count = sc; |
281 | } |
282 | |
283 | int synth_supports_indexing(void) |
284 | { |
285 | if (synth->get_index) |
286 | return 1; |
287 | return 0; |
288 | } |
289 | |
290 | void synth_insert_next_index(int sent_num) |
291 | { |
292 | int out; |
293 | |
294 | if (synth->alive) { |
295 | if (sent_num == 0) { |
296 | synth->indexing.currindex++; |
297 | index_count++; |
298 | if (synth->indexing.currindex > |
299 | synth->indexing.highindex) |
300 | synth->indexing.currindex = |
301 | synth->indexing.lowindex; |
302 | } |
303 | |
304 | out = synth->indexing.currindex * 10 + sent_num; |
305 | synth_printf(synth->indexing.command, out, out); |
306 | } |
307 | } |
308 | |
309 | void spk_get_index_count(int *linecount, int *sentcount) |
310 | { |
311 | int ind = synth->get_index(synth); |
312 | |
313 | if (ind) { |
314 | sentence_count = ind % 10; |
315 | |
316 | if ((ind / 10) <= synth->indexing.currindex) |
317 | index_count = synth->indexing.currindex - (ind / 10); |
318 | else |
319 | index_count = synth->indexing.currindex |
320 | - synth->indexing.lowindex |
321 | + synth->indexing.highindex - (ind / 10) + 1; |
322 | } |
323 | *sentcount = sentence_count; |
324 | *linecount = index_count; |
325 | } |
326 | |
327 | static struct resource synth_res; |
328 | |
329 | int synth_request_region(unsigned long start, unsigned long n) |
330 | { |
331 | struct resource *parent = &ioport_resource; |
332 | |
333 | memset(&synth_res, 0, sizeof(synth_res)); |
334 | synth_res.name = synth->name; |
335 | synth_res.start = start; |
336 | synth_res.end = start + n - 1; |
337 | synth_res.flags = IORESOURCE_BUSY; |
338 | return request_resource(root: parent, new: &synth_res); |
339 | } |
340 | EXPORT_SYMBOL_GPL(synth_request_region); |
341 | |
342 | int synth_release_region(unsigned long start, unsigned long n) |
343 | { |
344 | return release_resource(new: &synth_res); |
345 | } |
346 | EXPORT_SYMBOL_GPL(synth_release_region); |
347 | |
348 | struct var_t synth_time_vars[] = { |
349 | { DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL } }, |
350 | { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } }, |
351 | { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } }, |
352 | { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } }, |
353 | { FLUSH, .u.n = {NULL, 4000, 10, 4000, 0, 0, NULL } }, |
354 | V_LAST_VAR |
355 | }; |
356 | |
357 | /* called by: speakup_init() */ |
358 | int synth_init(char *synth_name) |
359 | { |
360 | int ret = 0; |
361 | struct spk_synth *tmp, *synth = NULL; |
362 | |
363 | if (!synth_name) |
364 | return 0; |
365 | |
366 | if (strcmp(synth_name, "none" ) == 0) { |
367 | mutex_lock(&spk_mutex); |
368 | synth_release(); |
369 | mutex_unlock(lock: &spk_mutex); |
370 | return 0; |
371 | } |
372 | |
373 | mutex_lock(&spk_mutex); |
374 | /* First, check if we already have it loaded. */ |
375 | list_for_each_entry(tmp, &synths, node) { |
376 | if (strcmp(tmp->name, synth_name) == 0) |
377 | synth = tmp; |
378 | } |
379 | |
380 | /* If we got one, initialize it now. */ |
381 | if (synth) |
382 | ret = do_synth_init(in_synth: synth); |
383 | else |
384 | ret = -ENODEV; |
385 | mutex_unlock(lock: &spk_mutex); |
386 | |
387 | return ret; |
388 | } |
389 | |
390 | /* called by: synth_add() */ |
391 | static int do_synth_init(struct spk_synth *in_synth) |
392 | { |
393 | struct var_t *var; |
394 | |
395 | synth_release(); |
396 | if (in_synth->checkval != SYNTH_CHECK) |
397 | return -EINVAL; |
398 | synth = in_synth; |
399 | synth->alive = 0; |
400 | pr_warn("synth probe\n" ); |
401 | if (synth->probe(synth) < 0) { |
402 | pr_warn("%s: device probe failed\n" , in_synth->name); |
403 | synth = NULL; |
404 | return -ENODEV; |
405 | } |
406 | synth_time_vars[0].u.n.value = |
407 | synth_time_vars[0].u.n.default_val = synth->delay; |
408 | synth_time_vars[1].u.n.value = |
409 | synth_time_vars[1].u.n.default_val = synth->trigger; |
410 | synth_time_vars[2].u.n.value = |
411 | synth_time_vars[2].u.n.default_val = synth->jiffies; |
412 | synth_time_vars[3].u.n.value = |
413 | synth_time_vars[3].u.n.default_val = synth->full; |
414 | synth_time_vars[4].u.n.value = |
415 | synth_time_vars[4].u.n.default_val = synth->flush_time; |
416 | synth_printf("%s" , synth->init); |
417 | for (var = synth->vars; |
418 | (var->var_id >= 0) && (var->var_id < MAXVARS); var++) |
419 | speakup_register_var(var); |
420 | if (!spk_quiet_boot) |
421 | synth_printf("%s found\n" , synth->long_name); |
422 | if (synth->attributes.name && |
423 | sysfs_create_group(kobj: speakup_kobj, grp: &synth->attributes) < 0) |
424 | return -ENOMEM; |
425 | synth_flags = synth->flags; |
426 | wake_up_interruptible_all(&speakup_event); |
427 | if (speakup_task) |
428 | wake_up_process(tsk: speakup_task); |
429 | return 0; |
430 | } |
431 | |
432 | void synth_release(void) |
433 | { |
434 | struct var_t *var; |
435 | unsigned long flags; |
436 | |
437 | if (!synth) |
438 | return; |
439 | spin_lock_irqsave(&speakup_info.spinlock, flags); |
440 | pr_info("releasing synth %s\n" , synth->name); |
441 | synth->alive = 0; |
442 | del_timer(timer: &thread_timer); |
443 | spin_unlock_irqrestore(lock: &speakup_info.spinlock, flags); |
444 | if (synth->attributes.name) |
445 | sysfs_remove_group(kobj: speakup_kobj, grp: &synth->attributes); |
446 | for (var = synth->vars; var->var_id != MAXVARS; var++) |
447 | speakup_unregister_var(var_id: var->var_id); |
448 | synth->release(synth); |
449 | synth = NULL; |
450 | } |
451 | |
452 | /* called by: all_driver_init() */ |
453 | int synth_add(struct spk_synth *in_synth) |
454 | { |
455 | int status = 0; |
456 | struct spk_synth *tmp; |
457 | |
458 | mutex_lock(&spk_mutex); |
459 | |
460 | list_for_each_entry(tmp, &synths, node) { |
461 | if (tmp == in_synth) { |
462 | mutex_unlock(lock: &spk_mutex); |
463 | return 0; |
464 | } |
465 | } |
466 | |
467 | if (in_synth->startup) |
468 | status = do_synth_init(in_synth); |
469 | |
470 | if (!status) |
471 | list_add_tail(new: &in_synth->node, head: &synths); |
472 | |
473 | mutex_unlock(lock: &spk_mutex); |
474 | return status; |
475 | } |
476 | EXPORT_SYMBOL_GPL(synth_add); |
477 | |
478 | void synth_remove(struct spk_synth *in_synth) |
479 | { |
480 | mutex_lock(&spk_mutex); |
481 | if (synth == in_synth) |
482 | synth_release(); |
483 | list_del(entry: &in_synth->node); |
484 | module_status = 0; |
485 | mutex_unlock(lock: &spk_mutex); |
486 | } |
487 | EXPORT_SYMBOL_GPL(synth_remove); |
488 | |
489 | struct spk_synth *synth_current(void) |
490 | { |
491 | return synth; |
492 | } |
493 | EXPORT_SYMBOL_GPL(synth_current); |
494 | |
495 | short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; |
496 | |