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