18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/types.h> 38c2ecf20Sopenharmony_ci#include <linux/ctype.h> /* for isdigit() and friends */ 48c2ecf20Sopenharmony_ci#include <linux/fs.h> 58c2ecf20Sopenharmony_ci#include <linux/mm.h> /* for verify_area */ 68c2ecf20Sopenharmony_ci#include <linux/errno.h> /* for -EBUSY */ 78c2ecf20Sopenharmony_ci#include <linux/ioport.h> /* for check_region, request_region */ 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> /* for loops_per_sec */ 108c2ecf20Sopenharmony_ci#include <linux/kmod.h> 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> /* for copy_from_user */ 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/timer.h> 158c2ecf20Sopenharmony_ci#include <linux/kthread.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "spk_priv.h" 188c2ecf20Sopenharmony_ci#include "speakup.h" 198c2ecf20Sopenharmony_ci#include "serialio.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic LIST_HEAD(synths); 228c2ecf20Sopenharmony_cistruct spk_synth *synth; 238c2ecf20Sopenharmony_cichar spk_pitch_buff[32] = ""; 248c2ecf20Sopenharmony_cistatic int module_status; 258c2ecf20Sopenharmony_cibool spk_quiet_boot; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct speakup_info_t speakup_info = { 288c2ecf20Sopenharmony_ci /* 298c2ecf20Sopenharmony_ci * This spinlock is used to protect the entire speakup machinery, and 308c2ecf20Sopenharmony_ci * must be taken at each kernel->speakup transition and released at 318c2ecf20Sopenharmony_ci * each corresponding speakup->kernel transition. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * The progression thread only interferes with the speakup machinery 348c2ecf20Sopenharmony_ci * through the synth buffer, so only needs to take the lock 358c2ecf20Sopenharmony_ci * while tinkering with the buffer. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * We use spin_lock/trylock_irqsave and spin_unlock_irqrestore with this 388c2ecf20Sopenharmony_ci * spinlock because speakup needs to disable the keyboard IRQ. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci .spinlock = __SPIN_LOCK_UNLOCKED(speakup_info.spinlock), 418c2ecf20Sopenharmony_ci .flushing = 0, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(speakup_info); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int do_synth_init(struct spk_synth *in_synth); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Main loop of the progression thread: keep eating from the buffer 498c2ecf20Sopenharmony_ci * and push to the serial port, waiting as needed 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * For devices that have a "full" notification mechanism, the driver can 528c2ecf20Sopenharmony_ci * adapt the loop the way they prefer. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic void _spk_do_catch_up(struct spk_synth *synth, int unicode) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci u16 ch; 578c2ecf20Sopenharmony_ci unsigned long flags; 588c2ecf20Sopenharmony_ci unsigned long jiff_max; 598c2ecf20Sopenharmony_ci struct var_t *delay_time; 608c2ecf20Sopenharmony_ci struct var_t *full_time; 618c2ecf20Sopenharmony_ci struct var_t *jiffy_delta; 628c2ecf20Sopenharmony_ci int jiffy_delta_val; 638c2ecf20Sopenharmony_ci int delay_time_val; 648c2ecf20Sopenharmony_ci int full_time_val; 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci jiffy_delta = spk_get_var(JIFFY); 688c2ecf20Sopenharmony_ci full_time = spk_get_var(FULL); 698c2ecf20Sopenharmony_ci delay_time = spk_get_var(DELAY); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 728c2ecf20Sopenharmony_ci jiffy_delta_val = jiffy_delta->u.n.value; 738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci jiff_max = jiffies + jiffy_delta_val; 768c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 778c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 788c2ecf20Sopenharmony_ci if (speakup_info.flushing) { 798c2ecf20Sopenharmony_ci speakup_info.flushing = 0; 808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 818c2ecf20Sopenharmony_ci synth->flush(synth); 828c2ecf20Sopenharmony_ci continue; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci if (!unicode) 858c2ecf20Sopenharmony_ci synth_buffer_skip_nonlatin1(); 868c2ecf20Sopenharmony_ci if (synth_buffer_empty()) { 878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci ch = synth_buffer_peek(); 918c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 928c2ecf20Sopenharmony_ci full_time_val = full_time->u.n.value; 938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 948c2ecf20Sopenharmony_ci if (ch == '\n') 958c2ecf20Sopenharmony_ci ch = synth->procspeech; 968c2ecf20Sopenharmony_ci if (unicode) 978c2ecf20Sopenharmony_ci ret = synth->io_ops->synth_out_unicode(synth, ch); 988c2ecf20Sopenharmony_ci else 998c2ecf20Sopenharmony_ci ret = synth->io_ops->synth_out(synth, ch); 1008c2ecf20Sopenharmony_ci if (!ret) { 1018c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(full_time_val)); 1028c2ecf20Sopenharmony_ci continue; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { 1058c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 1068c2ecf20Sopenharmony_ci jiffy_delta_val = jiffy_delta->u.n.value; 1078c2ecf20Sopenharmony_ci delay_time_val = delay_time->u.n.value; 1088c2ecf20Sopenharmony_ci full_time_val = full_time->u.n.value; 1098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 1108c2ecf20Sopenharmony_ci if (synth->io_ops->synth_out(synth, synth->procspeech)) 1118c2ecf20Sopenharmony_ci schedule_timeout( 1128c2ecf20Sopenharmony_ci msecs_to_jiffies(delay_time_val)); 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci schedule_timeout( 1158c2ecf20Sopenharmony_ci msecs_to_jiffies(full_time_val)); 1168c2ecf20Sopenharmony_ci jiff_max = jiffies + jiffy_delta_val; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 1198c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 1208c2ecf20Sopenharmony_ci synth_buffer_getc(); 1218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci synth->io_ops->synth_out(synth, synth->procspeech); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_civoid spk_do_catch_up(struct spk_synth *synth) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci _spk_do_catch_up(synth, 0); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_do_catch_up); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid spk_do_catch_up_unicode(struct spk_synth *synth) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci _spk_do_catch_up(synth, 1); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_do_catch_up_unicode); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_civoid spk_synth_flush(struct spk_synth *synth) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci synth->io_ops->flush_buffer(); 1418c2ecf20Sopenharmony_ci synth->io_ops->synth_out(synth, synth->clear); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_flush); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciunsigned char spk_synth_get_index(struct spk_synth *synth) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return synth->io_ops->synth_in_nowait(); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_get_index); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciint spk_synth_is_alive_nop(struct spk_synth *synth) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci synth->alive = 1; 1548c2ecf20Sopenharmony_ci return 1; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_is_alive_nop); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciint spk_synth_is_alive_restart(struct spk_synth *synth) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci if (synth->alive) 1618c2ecf20Sopenharmony_ci return 1; 1628c2ecf20Sopenharmony_ci if (synth->io_ops->wait_for_xmitr(synth) > 0) { 1638c2ecf20Sopenharmony_ci /* restart */ 1648c2ecf20Sopenharmony_ci synth->alive = 1; 1658c2ecf20Sopenharmony_ci synth_printf("%s", synth->init); 1668c2ecf20Sopenharmony_ci return 2; /* reenabled */ 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci pr_warn("%s: can't restart synth\n", synth->long_name); 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_synth_is_alive_restart); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void thread_wake_up(struct timer_list *unused) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci wake_up_interruptible_all(&speakup_event); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic DEFINE_TIMER(thread_timer, thread_wake_up); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_civoid synth_start(void) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct var_t *trigger_time; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!synth->alive) { 1858c2ecf20Sopenharmony_ci synth_buffer_clear(); 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci trigger_time = spk_get_var(TRIGGER); 1898c2ecf20Sopenharmony_ci if (!timer_pending(&thread_timer)) 1908c2ecf20Sopenharmony_ci mod_timer(&thread_timer, jiffies + 1918c2ecf20Sopenharmony_ci msecs_to_jiffies(trigger_time->u.n.value)); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid spk_do_flush(void) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci if (!synth) 1978c2ecf20Sopenharmony_ci return; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci speakup_info.flushing = 1; 2008c2ecf20Sopenharmony_ci synth_buffer_clear(); 2018c2ecf20Sopenharmony_ci if (synth->alive) { 2028c2ecf20Sopenharmony_ci if (spk_pitch_shift) { 2038c2ecf20Sopenharmony_ci synth_printf("%s", spk_pitch_buff); 2048c2ecf20Sopenharmony_ci spk_pitch_shift = 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci wake_up_interruptible_all(&speakup_event); 2088c2ecf20Sopenharmony_ci wake_up_process(speakup_task); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_civoid synth_write(const char *buf, size_t count) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci while (count--) 2148c2ecf20Sopenharmony_ci synth_buffer_add(*buf++); 2158c2ecf20Sopenharmony_ci synth_start(); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_civoid synth_printf(const char *fmt, ...) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci va_list args; 2218c2ecf20Sopenharmony_ci unsigned char buf[160], *p; 2228c2ecf20Sopenharmony_ci int r; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci va_start(args, fmt); 2258c2ecf20Sopenharmony_ci r = vsnprintf(buf, sizeof(buf), fmt, args); 2268c2ecf20Sopenharmony_ci va_end(args); 2278c2ecf20Sopenharmony_ci if (r > sizeof(buf) - 1) 2288c2ecf20Sopenharmony_ci r = sizeof(buf) - 1; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci p = buf; 2318c2ecf20Sopenharmony_ci while (r--) 2328c2ecf20Sopenharmony_ci synth_buffer_add(*p++); 2338c2ecf20Sopenharmony_ci synth_start(); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_printf); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_civoid synth_putwc(u16 wc) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci synth_buffer_add(wc); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putwc); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_civoid synth_putwc_s(u16 wc) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci synth_buffer_add(wc); 2468c2ecf20Sopenharmony_ci synth_start(); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putwc_s); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_civoid synth_putws(const u16 *buf) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci const u16 *p; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci for (p = buf; *p; p++) 2558c2ecf20Sopenharmony_ci synth_buffer_add(*p); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putws); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_civoid synth_putws_s(const u16 *buf) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci synth_putws(buf); 2628c2ecf20Sopenharmony_ci synth_start(); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_putws_s); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int index_count; 2678c2ecf20Sopenharmony_cistatic int sentence_count; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid spk_reset_index_count(int sc) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci static int first = 1; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (first) 2748c2ecf20Sopenharmony_ci first = 0; 2758c2ecf20Sopenharmony_ci else 2768c2ecf20Sopenharmony_ci synth->get_index(synth); 2778c2ecf20Sopenharmony_ci index_count = 0; 2788c2ecf20Sopenharmony_ci sentence_count = sc; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ciint synth_supports_indexing(void) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci if (synth->get_index) 2848c2ecf20Sopenharmony_ci return 1; 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid synth_insert_next_index(int sent_num) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int out; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (synth->alive) { 2938c2ecf20Sopenharmony_ci if (sent_num == 0) { 2948c2ecf20Sopenharmony_ci synth->indexing.currindex++; 2958c2ecf20Sopenharmony_ci index_count++; 2968c2ecf20Sopenharmony_ci if (synth->indexing.currindex > 2978c2ecf20Sopenharmony_ci synth->indexing.highindex) 2988c2ecf20Sopenharmony_ci synth->indexing.currindex = 2998c2ecf20Sopenharmony_ci synth->indexing.lowindex; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci out = synth->indexing.currindex * 10 + sent_num; 3038c2ecf20Sopenharmony_ci synth_printf(synth->indexing.command, out, out); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_civoid spk_get_index_count(int *linecount, int *sentcount) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci int ind = synth->get_index(synth); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (ind) { 3128c2ecf20Sopenharmony_ci sentence_count = ind % 10; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if ((ind / 10) <= synth->indexing.currindex) 3158c2ecf20Sopenharmony_ci index_count = synth->indexing.currindex - (ind / 10); 3168c2ecf20Sopenharmony_ci else 3178c2ecf20Sopenharmony_ci index_count = synth->indexing.currindex 3188c2ecf20Sopenharmony_ci - synth->indexing.lowindex 3198c2ecf20Sopenharmony_ci + synth->indexing.highindex - (ind / 10) + 1; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci *sentcount = sentence_count; 3228c2ecf20Sopenharmony_ci *linecount = index_count; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic struct resource synth_res; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ciint synth_request_region(unsigned long start, unsigned long n) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct resource *parent = &ioport_resource; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci memset(&synth_res, 0, sizeof(synth_res)); 3328c2ecf20Sopenharmony_ci synth_res.name = synth->name; 3338c2ecf20Sopenharmony_ci synth_res.start = start; 3348c2ecf20Sopenharmony_ci synth_res.end = start + n - 1; 3358c2ecf20Sopenharmony_ci synth_res.flags = IORESOURCE_BUSY; 3368c2ecf20Sopenharmony_ci return request_resource(parent, &synth_res); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_request_region); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciint synth_release_region(unsigned long start, unsigned long n) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci return release_resource(&synth_res); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_release_region); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistruct var_t synth_time_vars[] = { 3478c2ecf20Sopenharmony_ci { DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL } }, 3488c2ecf20Sopenharmony_ci { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } }, 3498c2ecf20Sopenharmony_ci { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } }, 3508c2ecf20Sopenharmony_ci { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } }, 3518c2ecf20Sopenharmony_ci V_LAST_VAR 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* called by: speakup_init() */ 3558c2ecf20Sopenharmony_ciint synth_init(char *synth_name) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int ret = 0; 3588c2ecf20Sopenharmony_ci struct spk_synth *tmp, *synth = NULL; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!synth_name) 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (strcmp(synth_name, "none") == 0) { 3648c2ecf20Sopenharmony_ci mutex_lock(&spk_mutex); 3658c2ecf20Sopenharmony_ci synth_release(); 3668c2ecf20Sopenharmony_ci mutex_unlock(&spk_mutex); 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mutex_lock(&spk_mutex); 3718c2ecf20Sopenharmony_ci /* First, check if we already have it loaded. */ 3728c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &synths, node) { 3738c2ecf20Sopenharmony_ci if (strcmp(tmp->name, synth_name) == 0) 3748c2ecf20Sopenharmony_ci synth = tmp; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* If we got one, initialize it now. */ 3788c2ecf20Sopenharmony_ci if (synth) 3798c2ecf20Sopenharmony_ci ret = do_synth_init(synth); 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci ret = -ENODEV; 3828c2ecf20Sopenharmony_ci mutex_unlock(&spk_mutex); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* called by: synth_add() */ 3888c2ecf20Sopenharmony_cistatic int do_synth_init(struct spk_synth *in_synth) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct var_t *var; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci synth_release(); 3938c2ecf20Sopenharmony_ci if (in_synth->checkval != SYNTH_CHECK) 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci synth = in_synth; 3968c2ecf20Sopenharmony_ci synth->alive = 0; 3978c2ecf20Sopenharmony_ci pr_warn("synth probe\n"); 3988c2ecf20Sopenharmony_ci if (synth->probe(synth) < 0) { 3998c2ecf20Sopenharmony_ci pr_warn("%s: device probe failed\n", in_synth->name); 4008c2ecf20Sopenharmony_ci synth = NULL; 4018c2ecf20Sopenharmony_ci return -ENODEV; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci synth_time_vars[0].u.n.value = 4048c2ecf20Sopenharmony_ci synth_time_vars[0].u.n.default_val = synth->delay; 4058c2ecf20Sopenharmony_ci synth_time_vars[1].u.n.value = 4068c2ecf20Sopenharmony_ci synth_time_vars[1].u.n.default_val = synth->trigger; 4078c2ecf20Sopenharmony_ci synth_time_vars[2].u.n.value = 4088c2ecf20Sopenharmony_ci synth_time_vars[2].u.n.default_val = synth->jiffies; 4098c2ecf20Sopenharmony_ci synth_time_vars[3].u.n.value = 4108c2ecf20Sopenharmony_ci synth_time_vars[3].u.n.default_val = synth->full; 4118c2ecf20Sopenharmony_ci synth_printf("%s", synth->init); 4128c2ecf20Sopenharmony_ci for (var = synth->vars; 4138c2ecf20Sopenharmony_ci (var->var_id >= 0) && (var->var_id < MAXVARS); var++) 4148c2ecf20Sopenharmony_ci speakup_register_var(var); 4158c2ecf20Sopenharmony_ci if (!spk_quiet_boot) 4168c2ecf20Sopenharmony_ci synth_printf("%s found\n", synth->long_name); 4178c2ecf20Sopenharmony_ci if (synth->attributes.name && 4188c2ecf20Sopenharmony_ci sysfs_create_group(speakup_kobj, &synth->attributes) < 0) 4198c2ecf20Sopenharmony_ci return -ENOMEM; 4208c2ecf20Sopenharmony_ci synth_flags = synth->flags; 4218c2ecf20Sopenharmony_ci wake_up_interruptible_all(&speakup_event); 4228c2ecf20Sopenharmony_ci if (speakup_task) 4238c2ecf20Sopenharmony_ci wake_up_process(speakup_task); 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_civoid synth_release(void) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct var_t *var; 4308c2ecf20Sopenharmony_ci unsigned long flags; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!synth) 4338c2ecf20Sopenharmony_ci return; 4348c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 4358c2ecf20Sopenharmony_ci pr_info("releasing synth %s\n", synth->name); 4368c2ecf20Sopenharmony_ci synth->alive = 0; 4378c2ecf20Sopenharmony_ci del_timer(&thread_timer); 4388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 4398c2ecf20Sopenharmony_ci if (synth->attributes.name) 4408c2ecf20Sopenharmony_ci sysfs_remove_group(speakup_kobj, &synth->attributes); 4418c2ecf20Sopenharmony_ci for (var = synth->vars; var->var_id != MAXVARS; var++) 4428c2ecf20Sopenharmony_ci speakup_unregister_var(var->var_id); 4438c2ecf20Sopenharmony_ci synth->release(); 4448c2ecf20Sopenharmony_ci synth = NULL; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* called by: all_driver_init() */ 4488c2ecf20Sopenharmony_ciint synth_add(struct spk_synth *in_synth) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int status = 0; 4518c2ecf20Sopenharmony_ci struct spk_synth *tmp; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mutex_lock(&spk_mutex); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &synths, node) { 4568c2ecf20Sopenharmony_ci if (tmp == in_synth) { 4578c2ecf20Sopenharmony_ci mutex_unlock(&spk_mutex); 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (in_synth->startup) 4638c2ecf20Sopenharmony_ci status = do_synth_init(in_synth); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (!status) 4668c2ecf20Sopenharmony_ci list_add_tail(&in_synth->node, &synths); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci mutex_unlock(&spk_mutex); 4698c2ecf20Sopenharmony_ci return status; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_add); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_civoid synth_remove(struct spk_synth *in_synth) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci mutex_lock(&spk_mutex); 4768c2ecf20Sopenharmony_ci if (synth == in_synth) 4778c2ecf20Sopenharmony_ci synth_release(); 4788c2ecf20Sopenharmony_ci list_del(&in_synth->node); 4798c2ecf20Sopenharmony_ci module_status = 0; 4808c2ecf20Sopenharmony_ci mutex_unlock(&spk_mutex); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_remove); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistruct spk_synth *synth_current(void) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci return synth; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(synth_current); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cishort spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC | B_SYM }; 491