162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/types.h>
362306a36Sopenharmony_ci#include <linux/ctype.h>	/* for isdigit() and friends */
462306a36Sopenharmony_ci#include <linux/fs.h>
562306a36Sopenharmony_ci#include <linux/mm.h>		/* for verify_area */
662306a36Sopenharmony_ci#include <linux/errno.h>	/* for -EBUSY */
762306a36Sopenharmony_ci#include <linux/ioport.h>	/* for check_region, request_region */
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/delay.h>	/* for loops_per_sec */
1062306a36Sopenharmony_ci#include <linux/kmod.h>
1162306a36Sopenharmony_ci#include <linux/jiffies.h>
1262306a36Sopenharmony_ci#include <linux/uaccess.h>	/* for copy_from_user */
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/timer.h>
1562306a36Sopenharmony_ci#include <linux/kthread.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "spk_priv.h"
1862306a36Sopenharmony_ci#include "speakup.h"
1962306a36Sopenharmony_ci#include "serialio.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic LIST_HEAD(synths);
2262306a36Sopenharmony_cistruct spk_synth *synth;
2362306a36Sopenharmony_cichar spk_pitch_buff[32] = "";
2462306a36Sopenharmony_cistatic int module_status;
2562306a36Sopenharmony_cibool spk_quiet_boot;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct speakup_info_t speakup_info = {
2862306a36Sopenharmony_ci	/*
2962306a36Sopenharmony_ci	 * This spinlock is used to protect the entire speakup machinery, and
3062306a36Sopenharmony_ci	 * must be taken at each kernel->speakup transition and released at
3162306a36Sopenharmony_ci	 * each corresponding speakup->kernel transition.
3262306a36Sopenharmony_ci	 *
3362306a36Sopenharmony_ci	 * The progression thread only interferes with the speakup machinery
3462306a36Sopenharmony_ci	 * through the synth buffer, so only needs to take the lock
3562306a36Sopenharmony_ci	 * while tinkering with the buffer.
3662306a36Sopenharmony_ci	 *
3762306a36Sopenharmony_ci	 * We use spin_lock/trylock_irqsave and spin_unlock_irqrestore with this
3862306a36Sopenharmony_ci	 * spinlock because speakup needs to disable the keyboard IRQ.
3962306a36Sopenharmony_ci	 */
4062306a36Sopenharmony_ci	.spinlock = __SPIN_LOCK_UNLOCKED(speakup_info.spinlock),
4162306a36Sopenharmony_ci	.flushing = 0,
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(speakup_info);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int do_synth_init(struct spk_synth *in_synth);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Main loop of the progression thread: keep eating from the buffer
4962306a36Sopenharmony_ci * and push to the serial port, waiting as needed
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * For devices that have a "full" notification mechanism, the driver can
5262306a36Sopenharmony_ci * adapt the loop the way they prefer.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic void _spk_do_catch_up(struct spk_synth *synth, int unicode)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	u16 ch;
5762306a36Sopenharmony_ci	unsigned long flags;
5862306a36Sopenharmony_ci	unsigned long jiff_max;
5962306a36Sopenharmony_ci	struct var_t *delay_time;
6062306a36Sopenharmony_ci	struct var_t *full_time;
6162306a36Sopenharmony_ci	struct var_t *jiffy_delta;
6262306a36Sopenharmony_ci	int jiffy_delta_val;
6362306a36Sopenharmony_ci	int delay_time_val;
6462306a36Sopenharmony_ci	int full_time_val;
6562306a36Sopenharmony_ci	int ret;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	jiffy_delta = spk_get_var(JIFFY);
6862306a36Sopenharmony_ci	full_time = spk_get_var(FULL);
6962306a36Sopenharmony_ci	delay_time = spk_get_var(DELAY);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
7262306a36Sopenharmony_ci	jiffy_delta_val = jiffy_delta->u.n.value;
7362306a36Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	jiff_max = jiffies + jiffy_delta_val;
7662306a36Sopenharmony_ci	while (!kthread_should_stop()) {
7762306a36Sopenharmony_ci		spin_lock_irqsave(&speakup_info.spinlock, flags);
7862306a36Sopenharmony_ci		if (speakup_info.flushing) {
7962306a36Sopenharmony_ci			speakup_info.flushing = 0;
8062306a36Sopenharmony_ci			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
8162306a36Sopenharmony_ci			synth->flush(synth);
8262306a36Sopenharmony_ci			continue;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci		if (!unicode)
8562306a36Sopenharmony_ci			synth_buffer_skip_nonlatin1();
8662306a36Sopenharmony_ci		if (synth_buffer_empty()) {
8762306a36Sopenharmony_ci			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
8862306a36Sopenharmony_ci			break;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci		ch = synth_buffer_peek();
9162306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
9262306a36Sopenharmony_ci		full_time_val = full_time->u.n.value;
9362306a36Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
9462306a36Sopenharmony_ci		if (ch == '\n')
9562306a36Sopenharmony_ci			ch = synth->procspeech;
9662306a36Sopenharmony_ci		if (unicode)
9762306a36Sopenharmony_ci			ret = synth->io_ops->synth_out_unicode(synth, ch);
9862306a36Sopenharmony_ci		else
9962306a36Sopenharmony_ci			ret = synth->io_ops->synth_out(synth, ch);
10062306a36Sopenharmony_ci		if (!ret) {
10162306a36Sopenharmony_ci			schedule_timeout(msecs_to_jiffies(full_time_val));
10262306a36Sopenharmony_ci			continue;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci		if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
10562306a36Sopenharmony_ci			spin_lock_irqsave(&speakup_info.spinlock, flags);
10662306a36Sopenharmony_ci			jiffy_delta_val = jiffy_delta->u.n.value;
10762306a36Sopenharmony_ci			delay_time_val = delay_time->u.n.value;
10862306a36Sopenharmony_ci			full_time_val = full_time->u.n.value;
10962306a36Sopenharmony_ci			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
11062306a36Sopenharmony_ci			if (synth->io_ops->synth_out(synth, synth->procspeech))
11162306a36Sopenharmony_ci				schedule_timeout(
11262306a36Sopenharmony_ci					msecs_to_jiffies(delay_time_val));
11362306a36Sopenharmony_ci			else
11462306a36Sopenharmony_ci				schedule_timeout(
11562306a36Sopenharmony_ci					msecs_to_jiffies(full_time_val));
11662306a36Sopenharmony_ci			jiff_max = jiffies + jiffy_delta_val;
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci		set_current_state(TASK_RUNNING);
11962306a36Sopenharmony_ci		spin_lock_irqsave(&speakup_info.spinlock, flags);
12062306a36Sopenharmony_ci		synth_buffer_getc();
12162306a36Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	synth->io_ops->synth_out(synth, synth->procspeech);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_civoid spk_do_catch_up(struct spk_synth *synth)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	_spk_do_catch_up(synth, 0);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_do_catch_up);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_civoid spk_do_catch_up_unicode(struct spk_synth *synth)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	_spk_do_catch_up(synth, 1);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_do_catch_up_unicode);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_civoid spk_synth_flush(struct spk_synth *synth)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	synth->io_ops->flush_buffer(synth);
14162306a36Sopenharmony_ci	synth->io_ops->synth_out(synth, synth->clear);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_flush);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciunsigned char spk_synth_get_index(struct spk_synth *synth)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return synth->io_ops->synth_in_nowait(synth);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_get_index);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciint spk_synth_is_alive_nop(struct spk_synth *synth)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	synth->alive = 1;
15462306a36Sopenharmony_ci	return 1;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_is_alive_nop);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciint spk_synth_is_alive_restart(struct spk_synth *synth)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	if (synth->alive)
16162306a36Sopenharmony_ci		return 1;
16262306a36Sopenharmony_ci	if (synth->io_ops->wait_for_xmitr(synth) > 0) {
16362306a36Sopenharmony_ci		/* restart */
16462306a36Sopenharmony_ci		synth->alive = 1;
16562306a36Sopenharmony_ci		synth_printf("%s", synth->init);
16662306a36Sopenharmony_ci		return 2; /* reenabled */
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci	pr_warn("%s: can't restart synth\n", synth->long_name);
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_is_alive_restart);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void thread_wake_up(struct timer_list *unused)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	wake_up_interruptible_all(&speakup_event);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic DEFINE_TIMER(thread_timer, thread_wake_up);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_civoid synth_start(void)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct var_t *trigger_time;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!synth->alive) {
18562306a36Sopenharmony_ci		synth_buffer_clear();
18662306a36Sopenharmony_ci		return;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	trigger_time = spk_get_var(TRIGGER);
18962306a36Sopenharmony_ci	if (!timer_pending(&thread_timer))
19062306a36Sopenharmony_ci		mod_timer(&thread_timer, jiffies +
19162306a36Sopenharmony_ci			msecs_to_jiffies(trigger_time->u.n.value));
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_civoid spk_do_flush(void)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	if (!synth)
19762306a36Sopenharmony_ci		return;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	speakup_info.flushing = 1;
20062306a36Sopenharmony_ci	synth_buffer_clear();
20162306a36Sopenharmony_ci	if (synth->alive) {
20262306a36Sopenharmony_ci		if (spk_pitch_shift) {
20362306a36Sopenharmony_ci			synth_printf("%s", spk_pitch_buff);
20462306a36Sopenharmony_ci			spk_pitch_shift = 0;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	wake_up_interruptible_all(&speakup_event);
20862306a36Sopenharmony_ci	wake_up_process(speakup_task);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_civoid synth_write(const char *_buf, size_t count)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	const unsigned char *buf = (const unsigned char *) _buf;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	while (count--)
21662306a36Sopenharmony_ci		synth_buffer_add(*buf++);
21762306a36Sopenharmony_ci	synth_start();
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_civoid synth_printf(const char *fmt, ...)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	va_list args;
22362306a36Sopenharmony_ci	unsigned char buf[160], *p;
22462306a36Sopenharmony_ci	int r;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	va_start(args, fmt);
22762306a36Sopenharmony_ci	r = vsnprintf(buf, sizeof(buf), fmt, args);
22862306a36Sopenharmony_ci	va_end(args);
22962306a36Sopenharmony_ci	if (r > sizeof(buf) - 1)
23062306a36Sopenharmony_ci		r = sizeof(buf) - 1;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	p = buf;
23362306a36Sopenharmony_ci	while (r--)
23462306a36Sopenharmony_ci		synth_buffer_add(*p++);
23562306a36Sopenharmony_ci	synth_start();
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_printf);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_civoid synth_putwc(u16 wc)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	synth_buffer_add(wc);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putwc);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_civoid synth_putwc_s(u16 wc)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	synth_buffer_add(wc);
24862306a36Sopenharmony_ci	synth_start();
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putwc_s);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_civoid synth_putws(const u16 *buf)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	const u16 *p;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	for (p = buf; *p; p++)
25762306a36Sopenharmony_ci		synth_buffer_add(*p);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putws);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_civoid synth_putws_s(const u16 *buf)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	synth_putws(buf);
26462306a36Sopenharmony_ci	synth_start();
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putws_s);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int index_count;
26962306a36Sopenharmony_cistatic int sentence_count;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_civoid spk_reset_index_count(int sc)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	static int first = 1;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (first)
27662306a36Sopenharmony_ci		first = 0;
27762306a36Sopenharmony_ci	else
27862306a36Sopenharmony_ci		synth->get_index(synth);
27962306a36Sopenharmony_ci	index_count = 0;
28062306a36Sopenharmony_ci	sentence_count = sc;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint synth_supports_indexing(void)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	if (synth->get_index)
28662306a36Sopenharmony_ci		return 1;
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_civoid synth_insert_next_index(int sent_num)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	int out;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (synth->alive) {
29562306a36Sopenharmony_ci		if (sent_num == 0) {
29662306a36Sopenharmony_ci			synth->indexing.currindex++;
29762306a36Sopenharmony_ci			index_count++;
29862306a36Sopenharmony_ci			if (synth->indexing.currindex >
29962306a36Sopenharmony_ci					synth->indexing.highindex)
30062306a36Sopenharmony_ci				synth->indexing.currindex =
30162306a36Sopenharmony_ci					synth->indexing.lowindex;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		out = synth->indexing.currindex * 10 + sent_num;
30562306a36Sopenharmony_ci		synth_printf(synth->indexing.command, out, out);
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_civoid spk_get_index_count(int *linecount, int *sentcount)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	int ind = synth->get_index(synth);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (ind) {
31462306a36Sopenharmony_ci		sentence_count = ind % 10;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if ((ind / 10) <= synth->indexing.currindex)
31762306a36Sopenharmony_ci			index_count = synth->indexing.currindex - (ind / 10);
31862306a36Sopenharmony_ci		else
31962306a36Sopenharmony_ci			index_count = synth->indexing.currindex
32062306a36Sopenharmony_ci				- synth->indexing.lowindex
32162306a36Sopenharmony_ci				+ synth->indexing.highindex - (ind / 10) + 1;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	*sentcount = sentence_count;
32462306a36Sopenharmony_ci	*linecount = index_count;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic struct resource synth_res;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ciint synth_request_region(unsigned long start, unsigned long n)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct resource *parent = &ioport_resource;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	memset(&synth_res, 0, sizeof(synth_res));
33462306a36Sopenharmony_ci	synth_res.name = synth->name;
33562306a36Sopenharmony_ci	synth_res.start = start;
33662306a36Sopenharmony_ci	synth_res.end = start + n - 1;
33762306a36Sopenharmony_ci	synth_res.flags = IORESOURCE_BUSY;
33862306a36Sopenharmony_ci	return request_resource(parent, &synth_res);
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_request_region);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciint synth_release_region(unsigned long start, unsigned long n)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	return release_resource(&synth_res);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_release_region);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistruct var_t synth_time_vars[] = {
34962306a36Sopenharmony_ci	{ DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL } },
35062306a36Sopenharmony_ci	{ TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } },
35162306a36Sopenharmony_ci	{ JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } },
35262306a36Sopenharmony_ci	{ FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } },
35362306a36Sopenharmony_ci	{ FLUSH, .u.n = {NULL, 4000, 10, 4000, 0, 0, NULL } },
35462306a36Sopenharmony_ci	V_LAST_VAR
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/* called by: speakup_init() */
35862306a36Sopenharmony_ciint synth_init(char *synth_name)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	int ret = 0;
36162306a36Sopenharmony_ci	struct spk_synth *tmp, *synth = NULL;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (!synth_name)
36462306a36Sopenharmony_ci		return 0;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (strcmp(synth_name, "none") == 0) {
36762306a36Sopenharmony_ci		mutex_lock(&spk_mutex);
36862306a36Sopenharmony_ci		synth_release();
36962306a36Sopenharmony_ci		mutex_unlock(&spk_mutex);
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	mutex_lock(&spk_mutex);
37462306a36Sopenharmony_ci	/* First, check if we already have it loaded. */
37562306a36Sopenharmony_ci	list_for_each_entry(tmp, &synths, node) {
37662306a36Sopenharmony_ci		if (strcmp(tmp->name, synth_name) == 0)
37762306a36Sopenharmony_ci			synth = tmp;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* If we got one, initialize it now. */
38162306a36Sopenharmony_ci	if (synth)
38262306a36Sopenharmony_ci		ret = do_synth_init(synth);
38362306a36Sopenharmony_ci	else
38462306a36Sopenharmony_ci		ret = -ENODEV;
38562306a36Sopenharmony_ci	mutex_unlock(&spk_mutex);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return ret;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/* called by: synth_add() */
39162306a36Sopenharmony_cistatic int do_synth_init(struct spk_synth *in_synth)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct var_t *var;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	synth_release();
39662306a36Sopenharmony_ci	if (in_synth->checkval != SYNTH_CHECK)
39762306a36Sopenharmony_ci		return -EINVAL;
39862306a36Sopenharmony_ci	synth = in_synth;
39962306a36Sopenharmony_ci	synth->alive = 0;
40062306a36Sopenharmony_ci	pr_warn("synth probe\n");
40162306a36Sopenharmony_ci	if (synth->probe(synth) < 0) {
40262306a36Sopenharmony_ci		pr_warn("%s: device probe failed\n", in_synth->name);
40362306a36Sopenharmony_ci		synth = NULL;
40462306a36Sopenharmony_ci		return -ENODEV;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	synth_time_vars[0].u.n.value =
40762306a36Sopenharmony_ci		synth_time_vars[0].u.n.default_val = synth->delay;
40862306a36Sopenharmony_ci	synth_time_vars[1].u.n.value =
40962306a36Sopenharmony_ci		synth_time_vars[1].u.n.default_val = synth->trigger;
41062306a36Sopenharmony_ci	synth_time_vars[2].u.n.value =
41162306a36Sopenharmony_ci		synth_time_vars[2].u.n.default_val = synth->jiffies;
41262306a36Sopenharmony_ci	synth_time_vars[3].u.n.value =
41362306a36Sopenharmony_ci		synth_time_vars[3].u.n.default_val = synth->full;
41462306a36Sopenharmony_ci	synth_time_vars[4].u.n.value =
41562306a36Sopenharmony_ci		synth_time_vars[4].u.n.default_val = synth->flush_time;
41662306a36Sopenharmony_ci	synth_printf("%s", synth->init);
41762306a36Sopenharmony_ci	for (var = synth->vars;
41862306a36Sopenharmony_ci		(var->var_id >= 0) && (var->var_id < MAXVARS); var++)
41962306a36Sopenharmony_ci		speakup_register_var(var);
42062306a36Sopenharmony_ci	if (!spk_quiet_boot)
42162306a36Sopenharmony_ci		synth_printf("%s found\n", synth->long_name);
42262306a36Sopenharmony_ci	if (synth->attributes.name &&
42362306a36Sopenharmony_ci	    sysfs_create_group(speakup_kobj, &synth->attributes) < 0)
42462306a36Sopenharmony_ci		return -ENOMEM;
42562306a36Sopenharmony_ci	synth_flags = synth->flags;
42662306a36Sopenharmony_ci	wake_up_interruptible_all(&speakup_event);
42762306a36Sopenharmony_ci	if (speakup_task)
42862306a36Sopenharmony_ci		wake_up_process(speakup_task);
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_civoid synth_release(void)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct var_t *var;
43562306a36Sopenharmony_ci	unsigned long flags;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (!synth)
43862306a36Sopenharmony_ci		return;
43962306a36Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
44062306a36Sopenharmony_ci	pr_info("releasing synth %s\n", synth->name);
44162306a36Sopenharmony_ci	synth->alive = 0;
44262306a36Sopenharmony_ci	del_timer(&thread_timer);
44362306a36Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
44462306a36Sopenharmony_ci	if (synth->attributes.name)
44562306a36Sopenharmony_ci		sysfs_remove_group(speakup_kobj, &synth->attributes);
44662306a36Sopenharmony_ci	for (var = synth->vars; var->var_id != MAXVARS; var++)
44762306a36Sopenharmony_ci		speakup_unregister_var(var->var_id);
44862306a36Sopenharmony_ci	synth->release(synth);
44962306a36Sopenharmony_ci	synth = NULL;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci/* called by: all_driver_init() */
45362306a36Sopenharmony_ciint synth_add(struct spk_synth *in_synth)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	int status = 0;
45662306a36Sopenharmony_ci	struct spk_synth *tmp;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	mutex_lock(&spk_mutex);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	list_for_each_entry(tmp, &synths, node) {
46162306a36Sopenharmony_ci		if (tmp == in_synth) {
46262306a36Sopenharmony_ci			mutex_unlock(&spk_mutex);
46362306a36Sopenharmony_ci			return 0;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (in_synth->startup)
46862306a36Sopenharmony_ci		status = do_synth_init(in_synth);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (!status)
47162306a36Sopenharmony_ci		list_add_tail(&in_synth->node, &synths);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	mutex_unlock(&spk_mutex);
47462306a36Sopenharmony_ci	return status;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_add);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_civoid synth_remove(struct spk_synth *in_synth)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	mutex_lock(&spk_mutex);
48162306a36Sopenharmony_ci	if (synth == in_synth)
48262306a36Sopenharmony_ci		synth_release();
48362306a36Sopenharmony_ci	list_del(&in_synth->node);
48462306a36Sopenharmony_ci	module_status = 0;
48562306a36Sopenharmony_ci	mutex_unlock(&spk_mutex);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_remove);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistruct spk_synth *synth_current(void)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	return synth;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_current);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cishort spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM };
496