18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * originally written by: Kirk Reiser <kirk@braille.uwo.ca> 48c2ecf20Sopenharmony_ci * this version considerably modified by David Borowski, david575@rogers.com 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 1998-99 Kirk Reiser. 78c2ecf20Sopenharmony_ci * Copyright (C) 2003 David Borowski. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * specificly written as a driver for the speakup screenreview 108c2ecf20Sopenharmony_ci * package it's not a general device driver. 118c2ecf20Sopenharmony_ci * This driver is for the RC Systems DoubleTalk PC internal synthesizer. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <linux/timer.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "spk_priv.h" 198c2ecf20Sopenharmony_ci#include "serialio.h" 208c2ecf20Sopenharmony_ci#include "speakup_dtlk.h" /* local header file for DoubleTalk values */ 218c2ecf20Sopenharmony_ci#include "speakup.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRV_VERSION "2.10" 248c2ecf20Sopenharmony_ci#define PROCSPEECH 0x00 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int synth_probe(struct spk_synth *synth); 278c2ecf20Sopenharmony_cistatic void dtlk_release(void); 288c2ecf20Sopenharmony_cistatic const char *synth_immediate(struct spk_synth *synth, const char *buf); 298c2ecf20Sopenharmony_cistatic void do_catch_up(struct spk_synth *synth); 308c2ecf20Sopenharmony_cistatic void synth_flush(struct spk_synth *synth); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int synth_lpc; 338c2ecf20Sopenharmony_cistatic int port_forced; 348c2ecf20Sopenharmony_cistatic unsigned int synth_portlist[] = { 358c2ecf20Sopenharmony_ci 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u_char synth_status; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct var_t vars[] = { 418c2ecf20Sopenharmony_ci { CAPS_START, .u.s = {"\x01+35p" } }, 428c2ecf20Sopenharmony_ci { CAPS_STOP, .u.s = {"\x01-35p" } }, 438c2ecf20Sopenharmony_ci { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } }, 448c2ecf20Sopenharmony_ci { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } }, 458c2ecf20Sopenharmony_ci { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, 468c2ecf20Sopenharmony_ci { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, 478c2ecf20Sopenharmony_ci { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } }, 488c2ecf20Sopenharmony_ci { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, 498c2ecf20Sopenharmony_ci { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, 508c2ecf20Sopenharmony_ci { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 518c2ecf20Sopenharmony_ci V_LAST_VAR 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * These attributes will appear in /sys/accessibility/speakup/dtlk. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic struct kobj_attribute caps_start_attribute = 588c2ecf20Sopenharmony_ci __ATTR(caps_start, 0644, spk_var_show, spk_var_store); 598c2ecf20Sopenharmony_cistatic struct kobj_attribute caps_stop_attribute = 608c2ecf20Sopenharmony_ci __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); 618c2ecf20Sopenharmony_cistatic struct kobj_attribute freq_attribute = 628c2ecf20Sopenharmony_ci __ATTR(freq, 0644, spk_var_show, spk_var_store); 638c2ecf20Sopenharmony_cistatic struct kobj_attribute pitch_attribute = 648c2ecf20Sopenharmony_ci __ATTR(pitch, 0644, spk_var_show, spk_var_store); 658c2ecf20Sopenharmony_cistatic struct kobj_attribute punct_attribute = 668c2ecf20Sopenharmony_ci __ATTR(punct, 0644, spk_var_show, spk_var_store); 678c2ecf20Sopenharmony_cistatic struct kobj_attribute rate_attribute = 688c2ecf20Sopenharmony_ci __ATTR(rate, 0644, spk_var_show, spk_var_store); 698c2ecf20Sopenharmony_cistatic struct kobj_attribute tone_attribute = 708c2ecf20Sopenharmony_ci __ATTR(tone, 0644, spk_var_show, spk_var_store); 718c2ecf20Sopenharmony_cistatic struct kobj_attribute voice_attribute = 728c2ecf20Sopenharmony_ci __ATTR(voice, 0644, spk_var_show, spk_var_store); 738c2ecf20Sopenharmony_cistatic struct kobj_attribute vol_attribute = 748c2ecf20Sopenharmony_ci __ATTR(vol, 0644, spk_var_show, spk_var_store); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct kobj_attribute delay_time_attribute = 778c2ecf20Sopenharmony_ci __ATTR(delay_time, 0644, spk_var_show, spk_var_store); 788c2ecf20Sopenharmony_cistatic struct kobj_attribute direct_attribute = 798c2ecf20Sopenharmony_ci __ATTR(direct, 0644, spk_var_show, spk_var_store); 808c2ecf20Sopenharmony_cistatic struct kobj_attribute full_time_attribute = 818c2ecf20Sopenharmony_ci __ATTR(full_time, 0644, spk_var_show, spk_var_store); 828c2ecf20Sopenharmony_cistatic struct kobj_attribute jiffy_delta_attribute = 838c2ecf20Sopenharmony_ci __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); 848c2ecf20Sopenharmony_cistatic struct kobj_attribute trigger_time_attribute = 858c2ecf20Sopenharmony_ci __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * Create a group of attributes so that we can create and destroy them all 898c2ecf20Sopenharmony_ci * at once. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic struct attribute *synth_attrs[] = { 928c2ecf20Sopenharmony_ci &caps_start_attribute.attr, 938c2ecf20Sopenharmony_ci &caps_stop_attribute.attr, 948c2ecf20Sopenharmony_ci &freq_attribute.attr, 958c2ecf20Sopenharmony_ci &pitch_attribute.attr, 968c2ecf20Sopenharmony_ci &punct_attribute.attr, 978c2ecf20Sopenharmony_ci &rate_attribute.attr, 988c2ecf20Sopenharmony_ci &tone_attribute.attr, 998c2ecf20Sopenharmony_ci &voice_attribute.attr, 1008c2ecf20Sopenharmony_ci &vol_attribute.attr, 1018c2ecf20Sopenharmony_ci &delay_time_attribute.attr, 1028c2ecf20Sopenharmony_ci &direct_attribute.attr, 1038c2ecf20Sopenharmony_ci &full_time_attribute.attr, 1048c2ecf20Sopenharmony_ci &jiffy_delta_attribute.attr, 1058c2ecf20Sopenharmony_ci &trigger_time_attribute.attr, 1068c2ecf20Sopenharmony_ci NULL, /* need to NULL terminate the list of attributes */ 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct spk_synth synth_dtlk = { 1108c2ecf20Sopenharmony_ci .name = "dtlk", 1118c2ecf20Sopenharmony_ci .version = DRV_VERSION, 1128c2ecf20Sopenharmony_ci .long_name = "DoubleTalk PC", 1138c2ecf20Sopenharmony_ci .init = "\x01@\x01\x31y", 1148c2ecf20Sopenharmony_ci .procspeech = PROCSPEECH, 1158c2ecf20Sopenharmony_ci .clear = SYNTH_CLEAR, 1168c2ecf20Sopenharmony_ci .delay = 500, 1178c2ecf20Sopenharmony_ci .trigger = 30, 1188c2ecf20Sopenharmony_ci .jiffies = 50, 1198c2ecf20Sopenharmony_ci .full = 1000, 1208c2ecf20Sopenharmony_ci .startup = SYNTH_START, 1218c2ecf20Sopenharmony_ci .checkval = SYNTH_CHECK, 1228c2ecf20Sopenharmony_ci .vars = vars, 1238c2ecf20Sopenharmony_ci .io_ops = &spk_serial_io_ops, 1248c2ecf20Sopenharmony_ci .probe = synth_probe, 1258c2ecf20Sopenharmony_ci .release = dtlk_release, 1268c2ecf20Sopenharmony_ci .synth_immediate = synth_immediate, 1278c2ecf20Sopenharmony_ci .catch_up = do_catch_up, 1288c2ecf20Sopenharmony_ci .flush = synth_flush, 1298c2ecf20Sopenharmony_ci .is_alive = spk_synth_is_alive_nop, 1308c2ecf20Sopenharmony_ci .synth_adjust = NULL, 1318c2ecf20Sopenharmony_ci .read_buff_add = NULL, 1328c2ecf20Sopenharmony_ci .get_index = spk_synth_get_index, 1338c2ecf20Sopenharmony_ci .indexing = { 1348c2ecf20Sopenharmony_ci .command = "\x01%di", 1358c2ecf20Sopenharmony_ci .lowindex = 1, 1368c2ecf20Sopenharmony_ci .highindex = 5, 1378c2ecf20Sopenharmony_ci .currindex = 1, 1388c2ecf20Sopenharmony_ci }, 1398c2ecf20Sopenharmony_ci .attributes = { 1408c2ecf20Sopenharmony_ci .attrs = synth_attrs, 1418c2ecf20Sopenharmony_ci .name = "dtlk", 1428c2ecf20Sopenharmony_ci }, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic inline bool synth_readable(void) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci synth_status = inb_p(speakup_info.port_tts + UART_RX); 1488c2ecf20Sopenharmony_ci return (synth_status & TTS_READABLE) != 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic inline bool synth_writable(void) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci synth_status = inb_p(speakup_info.port_tts + UART_RX); 1548c2ecf20Sopenharmony_ci return (synth_status & TTS_WRITABLE) != 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic inline bool synth_full(void) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci synth_status = inb_p(speakup_info.port_tts + UART_RX); 1608c2ecf20Sopenharmony_ci return (synth_status & TTS_ALMOST_FULL) != 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void spk_out(const char ch) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int timeout = SPK_XMITR_TIMEOUT; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci while (!synth_writable()) { 1688c2ecf20Sopenharmony_ci if (!--timeout) 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci udelay(1); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci outb_p(ch, speakup_info.port_tts); 1738c2ecf20Sopenharmony_ci timeout = SPK_XMITR_TIMEOUT; 1748c2ecf20Sopenharmony_ci while (synth_writable()) { 1758c2ecf20Sopenharmony_ci if (!--timeout) 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci udelay(1); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void do_catch_up(struct spk_synth *synth) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci u_char ch; 1848c2ecf20Sopenharmony_ci unsigned long flags; 1858c2ecf20Sopenharmony_ci unsigned long jiff_max; 1868c2ecf20Sopenharmony_ci struct var_t *jiffy_delta; 1878c2ecf20Sopenharmony_ci struct var_t *delay_time; 1888c2ecf20Sopenharmony_ci int jiffy_delta_val; 1898c2ecf20Sopenharmony_ci int delay_time_val; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci jiffy_delta = spk_get_var(JIFFY); 1928c2ecf20Sopenharmony_ci delay_time = spk_get_var(DELAY); 1938c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 1948c2ecf20Sopenharmony_ci jiffy_delta_val = jiffy_delta->u.n.value; 1958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 1968c2ecf20Sopenharmony_ci jiff_max = jiffies + jiffy_delta_val; 1978c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 1988c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 1998c2ecf20Sopenharmony_ci if (speakup_info.flushing) { 2008c2ecf20Sopenharmony_ci speakup_info.flushing = 0; 2018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2028c2ecf20Sopenharmony_ci synth->flush(synth); 2038c2ecf20Sopenharmony_ci continue; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci synth_buffer_skip_nonlatin1(); 2068c2ecf20Sopenharmony_ci if (synth_buffer_empty()) { 2078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 2118c2ecf20Sopenharmony_ci delay_time_val = delay_time->u.n.value; 2128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2138c2ecf20Sopenharmony_ci if (synth_full()) { 2148c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(delay_time_val)); 2158c2ecf20Sopenharmony_ci continue; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 2198c2ecf20Sopenharmony_ci ch = synth_buffer_getc(); 2208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2218c2ecf20Sopenharmony_ci if (ch == '\n') 2228c2ecf20Sopenharmony_ci ch = PROCSPEECH; 2238c2ecf20Sopenharmony_ci spk_out(ch); 2248c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { 2258c2ecf20Sopenharmony_ci spk_out(PROCSPEECH); 2268c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 2278c2ecf20Sopenharmony_ci delay_time_val = delay_time->u.n.value; 2288c2ecf20Sopenharmony_ci jiffy_delta_val = jiffy_delta->u.n.value; 2298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2308c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(delay_time_val)); 2318c2ecf20Sopenharmony_ci jiff_max = jiffies + jiffy_delta_val; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci spk_out(PROCSPEECH); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic const char *synth_immediate(struct spk_synth *synth, const char *buf) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci u_char ch; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci while ((ch = (u_char)*buf)) { 2428c2ecf20Sopenharmony_ci if (synth_full()) 2438c2ecf20Sopenharmony_ci return buf; 2448c2ecf20Sopenharmony_ci if (ch == '\n') 2458c2ecf20Sopenharmony_ci ch = PROCSPEECH; 2468c2ecf20Sopenharmony_ci spk_out(ch); 2478c2ecf20Sopenharmony_ci buf++; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci return NULL; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void synth_flush(struct spk_synth *synth) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci outb_p(SYNTH_CLEAR, speakup_info.port_tts); 2558c2ecf20Sopenharmony_ci while (synth_writable()) 2568c2ecf20Sopenharmony_ci cpu_relax(); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic char synth_read_tts(void) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci u_char ch; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci while (!synth_readable()) 2648c2ecf20Sopenharmony_ci cpu_relax(); 2658c2ecf20Sopenharmony_ci ch = synth_status & 0x7f; 2668c2ecf20Sopenharmony_ci outb_p(ch, speakup_info.port_tts); 2678c2ecf20Sopenharmony_ci while (synth_readable()) 2688c2ecf20Sopenharmony_ci cpu_relax(); 2698c2ecf20Sopenharmony_ci return (char)ch; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* interrogate the DoubleTalk PC and return its settings */ 2738c2ecf20Sopenharmony_cistatic struct synth_settings *synth_interrogate(struct spk_synth *synth) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci u_char *t; 2768c2ecf20Sopenharmony_ci static char buf[sizeof(struct synth_settings) + 1]; 2778c2ecf20Sopenharmony_ci int total, i; 2788c2ecf20Sopenharmony_ci static struct synth_settings status; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci synth_immediate(synth, "\x18\x01?"); 2818c2ecf20Sopenharmony_ci for (total = 0, i = 0; i < 50; i++) { 2828c2ecf20Sopenharmony_ci buf[total] = synth_read_tts(); 2838c2ecf20Sopenharmony_ci if (total > 2 && buf[total] == 0x7f) 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci if (total < sizeof(struct synth_settings)) 2868c2ecf20Sopenharmony_ci total++; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci t = buf; 2898c2ecf20Sopenharmony_ci /* serial number is little endian */ 2908c2ecf20Sopenharmony_ci status.serial_number = t[0] + t[1] * 256; 2918c2ecf20Sopenharmony_ci t += 2; 2928c2ecf20Sopenharmony_ci for (i = 0; *t != '\r'; t++) { 2938c2ecf20Sopenharmony_ci status.rom_version[i] = *t; 2948c2ecf20Sopenharmony_ci if (i < sizeof(status.rom_version) - 1) 2958c2ecf20Sopenharmony_ci i++; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci status.rom_version[i] = 0; 2988c2ecf20Sopenharmony_ci t++; 2998c2ecf20Sopenharmony_ci status.mode = *t++; 3008c2ecf20Sopenharmony_ci status.punc_level = *t++; 3018c2ecf20Sopenharmony_ci status.formant_freq = *t++; 3028c2ecf20Sopenharmony_ci status.pitch = *t++; 3038c2ecf20Sopenharmony_ci status.speed = *t++; 3048c2ecf20Sopenharmony_ci status.volume = *t++; 3058c2ecf20Sopenharmony_ci status.tone = *t++; 3068c2ecf20Sopenharmony_ci status.expression = *t++; 3078c2ecf20Sopenharmony_ci status.ext_dict_loaded = *t++; 3088c2ecf20Sopenharmony_ci status.ext_dict_status = *t++; 3098c2ecf20Sopenharmony_ci status.free_ram = *t++; 3108c2ecf20Sopenharmony_ci status.articulation = *t++; 3118c2ecf20Sopenharmony_ci status.reverb = *t++; 3128c2ecf20Sopenharmony_ci status.eob = *t++; 3138c2ecf20Sopenharmony_ci return &status; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int synth_probe(struct spk_synth *synth) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci unsigned int port_val = 0; 3198c2ecf20Sopenharmony_ci int i = 0; 3208c2ecf20Sopenharmony_ci struct synth_settings *sp; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci pr_info("Probing for DoubleTalk.\n"); 3238c2ecf20Sopenharmony_ci if (port_forced) { 3248c2ecf20Sopenharmony_ci speakup_info.port_tts = port_forced; 3258c2ecf20Sopenharmony_ci pr_info("probe forced to %x by kernel command line\n", 3268c2ecf20Sopenharmony_ci speakup_info.port_tts); 3278c2ecf20Sopenharmony_ci if ((port_forced & 0xf) != 0xf) 3288c2ecf20Sopenharmony_ci pr_info("warning: port base should probably end with f\n"); 3298c2ecf20Sopenharmony_ci if (synth_request_region(speakup_info.port_tts - 1, 3308c2ecf20Sopenharmony_ci SYNTH_IO_EXTENT)) { 3318c2ecf20Sopenharmony_ci pr_warn("sorry, port already reserved\n"); 3328c2ecf20Sopenharmony_ci return -EBUSY; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci port_val = inw(speakup_info.port_tts - 1); 3358c2ecf20Sopenharmony_ci synth_lpc = speakup_info.port_tts - 1; 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci for (i = 0; synth_portlist[i]; i++) { 3388c2ecf20Sopenharmony_ci if (synth_request_region(synth_portlist[i], 3398c2ecf20Sopenharmony_ci SYNTH_IO_EXTENT)) 3408c2ecf20Sopenharmony_ci continue; 3418c2ecf20Sopenharmony_ci port_val = inw(synth_portlist[i]) & 0xfbff; 3428c2ecf20Sopenharmony_ci if (port_val == 0x107f) { 3438c2ecf20Sopenharmony_ci synth_lpc = synth_portlist[i]; 3448c2ecf20Sopenharmony_ci speakup_info.port_tts = synth_lpc + 1; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci synth_release_region(synth_portlist[i], 3488c2ecf20Sopenharmony_ci SYNTH_IO_EXTENT); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci port_val &= 0xfbff; 3528c2ecf20Sopenharmony_ci if (port_val != 0x107f) { 3538c2ecf20Sopenharmony_ci pr_info("DoubleTalk PC: not found\n"); 3548c2ecf20Sopenharmony_ci if (synth_lpc) 3558c2ecf20Sopenharmony_ci synth_release_region(synth_lpc, SYNTH_IO_EXTENT); 3568c2ecf20Sopenharmony_ci return -ENODEV; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci while (inw_p(synth_lpc) != 0x147f) 3598c2ecf20Sopenharmony_ci cpu_relax(); /* wait until it's ready */ 3608c2ecf20Sopenharmony_ci sp = synth_interrogate(synth); 3618c2ecf20Sopenharmony_ci pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n", 3628c2ecf20Sopenharmony_ci synth->long_name, synth_lpc, synth_lpc + SYNTH_IO_EXTENT - 1, 3638c2ecf20Sopenharmony_ci sp->rom_version, sp->serial_number, synth->version); 3648c2ecf20Sopenharmony_ci synth->alive = 1; 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void dtlk_release(void) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci spk_stop_serial_interrupt(); 3718c2ecf20Sopenharmony_ci if (speakup_info.port_tts) 3728c2ecf20Sopenharmony_ci synth_release_region(speakup_info.port_tts - 1, 3738c2ecf20Sopenharmony_ci SYNTH_IO_EXTENT); 3748c2ecf20Sopenharmony_ci speakup_info.port_tts = 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cimodule_param_hw_named(port, port_forced, int, ioport, 0444); 3788c2ecf20Sopenharmony_cimodule_param_named(start, synth_dtlk.startup, short, 0444); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); 3818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cimodule_spk_synth(synth_dtlk); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 3868c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Borowski"); 3878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers"); 3888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3898c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 3908c2ecf20Sopenharmony_ci 391