12067fd92SSamuel Thibault // SPDX-License-Identifier: GPL-2.0+ 22067fd92SSamuel Thibault /* 32067fd92SSamuel Thibault * originally written by: Kirk Reiser <kirk@braille.uwo.ca> 42067fd92SSamuel Thibault * this version considerably modified by David Borowski, david575@rogers.com 52067fd92SSamuel Thibault * 62067fd92SSamuel Thibault * Copyright (C) 1998-99 Kirk Reiser. 72067fd92SSamuel Thibault * Copyright (C) 2003 David Borowski. 82067fd92SSamuel Thibault * 9*0b4efcb1STom Rix * specifically written as a driver for the speakup screenreview 102067fd92SSamuel Thibault * package it's not a general device driver. 112067fd92SSamuel Thibault * This driver is for the RC Systems DoubleTalk PC internal synthesizer. 122067fd92SSamuel Thibault */ 132067fd92SSamuel Thibault #include <linux/jiffies.h> 142067fd92SSamuel Thibault #include <linux/sched.h> 152067fd92SSamuel Thibault #include <linux/timer.h> 162067fd92SSamuel Thibault #include <linux/kthread.h> 172067fd92SSamuel Thibault 182067fd92SSamuel Thibault #include "spk_priv.h" 192067fd92SSamuel Thibault #include "serialio.h" 202067fd92SSamuel Thibault #include "speakup_dtlk.h" /* local header file for DoubleTalk values */ 212067fd92SSamuel Thibault #include "speakup.h" 222067fd92SSamuel Thibault 232067fd92SSamuel Thibault #define DRV_VERSION "2.10" 242067fd92SSamuel Thibault #define PROCSPEECH 0x00 252067fd92SSamuel Thibault 262067fd92SSamuel Thibault static int synth_probe(struct spk_synth *synth); 271941ab1dSSamuel Thibault static void dtlk_release(struct spk_synth *synth); 282067fd92SSamuel Thibault static const char *synth_immediate(struct spk_synth *synth, const char *buf); 292067fd92SSamuel Thibault static void do_catch_up(struct spk_synth *synth); 302067fd92SSamuel Thibault static void synth_flush(struct spk_synth *synth); 312067fd92SSamuel Thibault 322067fd92SSamuel Thibault static int synth_lpc; 332067fd92SSamuel Thibault static int port_forced; 342067fd92SSamuel Thibault static unsigned int synth_portlist[] = { 352067fd92SSamuel Thibault 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0 362067fd92SSamuel Thibault }; 372067fd92SSamuel Thibault 382067fd92SSamuel Thibault static u_char synth_status; 392067fd92SSamuel Thibault 402067fd92SSamuel Thibault static struct var_t vars[] = { 412067fd92SSamuel Thibault { CAPS_START, .u.s = {"\x01+35p" } }, 422067fd92SSamuel Thibault { CAPS_STOP, .u.s = {"\x01-35p" } }, 432067fd92SSamuel Thibault { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } }, 442067fd92SSamuel Thibault { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } }, 452067fd92SSamuel Thibault { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, 462067fd92SSamuel Thibault { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, 472067fd92SSamuel Thibault { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } }, 482067fd92SSamuel Thibault { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, 492067fd92SSamuel Thibault { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, 502067fd92SSamuel Thibault { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 512067fd92SSamuel Thibault V_LAST_VAR 522067fd92SSamuel Thibault }; 532067fd92SSamuel Thibault 542067fd92SSamuel Thibault /* 552067fd92SSamuel Thibault * These attributes will appear in /sys/accessibility/speakup/dtlk. 562067fd92SSamuel Thibault */ 572067fd92SSamuel Thibault static struct kobj_attribute caps_start_attribute = 582067fd92SSamuel Thibault __ATTR(caps_start, 0644, spk_var_show, spk_var_store); 592067fd92SSamuel Thibault static struct kobj_attribute caps_stop_attribute = 602067fd92SSamuel Thibault __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); 612067fd92SSamuel Thibault static struct kobj_attribute freq_attribute = 622067fd92SSamuel Thibault __ATTR(freq, 0644, spk_var_show, spk_var_store); 632067fd92SSamuel Thibault static struct kobj_attribute pitch_attribute = 642067fd92SSamuel Thibault __ATTR(pitch, 0644, spk_var_show, spk_var_store); 652067fd92SSamuel Thibault static struct kobj_attribute punct_attribute = 662067fd92SSamuel Thibault __ATTR(punct, 0644, spk_var_show, spk_var_store); 672067fd92SSamuel Thibault static struct kobj_attribute rate_attribute = 682067fd92SSamuel Thibault __ATTR(rate, 0644, spk_var_show, spk_var_store); 692067fd92SSamuel Thibault static struct kobj_attribute tone_attribute = 702067fd92SSamuel Thibault __ATTR(tone, 0644, spk_var_show, spk_var_store); 712067fd92SSamuel Thibault static struct kobj_attribute voice_attribute = 722067fd92SSamuel Thibault __ATTR(voice, 0644, spk_var_show, spk_var_store); 732067fd92SSamuel Thibault static struct kobj_attribute vol_attribute = 742067fd92SSamuel Thibault __ATTR(vol, 0644, spk_var_show, spk_var_store); 752067fd92SSamuel Thibault 762067fd92SSamuel Thibault static struct kobj_attribute delay_time_attribute = 772067fd92SSamuel Thibault __ATTR(delay_time, 0644, spk_var_show, spk_var_store); 782067fd92SSamuel Thibault static struct kobj_attribute direct_attribute = 792067fd92SSamuel Thibault __ATTR(direct, 0644, spk_var_show, spk_var_store); 802067fd92SSamuel Thibault static struct kobj_attribute full_time_attribute = 812067fd92SSamuel Thibault __ATTR(full_time, 0644, spk_var_show, spk_var_store); 822067fd92SSamuel Thibault static struct kobj_attribute jiffy_delta_attribute = 832067fd92SSamuel Thibault __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); 842067fd92SSamuel Thibault static struct kobj_attribute trigger_time_attribute = 852067fd92SSamuel Thibault __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); 862067fd92SSamuel Thibault 872067fd92SSamuel Thibault /* 882067fd92SSamuel Thibault * Create a group of attributes so that we can create and destroy them all 892067fd92SSamuel Thibault * at once. 902067fd92SSamuel Thibault */ 912067fd92SSamuel Thibault static struct attribute *synth_attrs[] = { 922067fd92SSamuel Thibault &caps_start_attribute.attr, 932067fd92SSamuel Thibault &caps_stop_attribute.attr, 942067fd92SSamuel Thibault &freq_attribute.attr, 952067fd92SSamuel Thibault &pitch_attribute.attr, 962067fd92SSamuel Thibault &punct_attribute.attr, 972067fd92SSamuel Thibault &rate_attribute.attr, 982067fd92SSamuel Thibault &tone_attribute.attr, 992067fd92SSamuel Thibault &voice_attribute.attr, 1002067fd92SSamuel Thibault &vol_attribute.attr, 1012067fd92SSamuel Thibault &delay_time_attribute.attr, 1022067fd92SSamuel Thibault &direct_attribute.attr, 1032067fd92SSamuel Thibault &full_time_attribute.attr, 1042067fd92SSamuel Thibault &jiffy_delta_attribute.attr, 1052067fd92SSamuel Thibault &trigger_time_attribute.attr, 1062067fd92SSamuel Thibault NULL, /* need to NULL terminate the list of attributes */ 1072067fd92SSamuel Thibault }; 1082067fd92SSamuel Thibault 1092067fd92SSamuel Thibault static struct spk_synth synth_dtlk = { 1102067fd92SSamuel Thibault .name = "dtlk", 1112067fd92SSamuel Thibault .version = DRV_VERSION, 1122067fd92SSamuel Thibault .long_name = "DoubleTalk PC", 1132067fd92SSamuel Thibault .init = "\x01@\x01\x31y", 1142067fd92SSamuel Thibault .procspeech = PROCSPEECH, 1152067fd92SSamuel Thibault .clear = SYNTH_CLEAR, 1162067fd92SSamuel Thibault .delay = 500, 1172067fd92SSamuel Thibault .trigger = 30, 1182067fd92SSamuel Thibault .jiffies = 50, 1192067fd92SSamuel Thibault .full = 1000, 1202067fd92SSamuel Thibault .startup = SYNTH_START, 1212067fd92SSamuel Thibault .checkval = SYNTH_CHECK, 1222067fd92SSamuel Thibault .vars = vars, 1232067fd92SSamuel Thibault .io_ops = &spk_serial_io_ops, 1242067fd92SSamuel Thibault .probe = synth_probe, 1252067fd92SSamuel Thibault .release = dtlk_release, 1262067fd92SSamuel Thibault .synth_immediate = synth_immediate, 1272067fd92SSamuel Thibault .catch_up = do_catch_up, 1282067fd92SSamuel Thibault .flush = synth_flush, 1292067fd92SSamuel Thibault .is_alive = spk_synth_is_alive_nop, 1302067fd92SSamuel Thibault .synth_adjust = NULL, 1312067fd92SSamuel Thibault .read_buff_add = NULL, 1322067fd92SSamuel Thibault .get_index = spk_synth_get_index, 1332067fd92SSamuel Thibault .indexing = { 1342067fd92SSamuel Thibault .command = "\x01%di", 1352067fd92SSamuel Thibault .lowindex = 1, 1362067fd92SSamuel Thibault .highindex = 5, 1372067fd92SSamuel Thibault .currindex = 1, 1382067fd92SSamuel Thibault }, 1392067fd92SSamuel Thibault .attributes = { 1402067fd92SSamuel Thibault .attrs = synth_attrs, 1412067fd92SSamuel Thibault .name = "dtlk", 1422067fd92SSamuel Thibault }, 1432067fd92SSamuel Thibault }; 1442067fd92SSamuel Thibault 1452067fd92SSamuel Thibault static inline bool synth_readable(void) 1462067fd92SSamuel Thibault { 1472067fd92SSamuel Thibault synth_status = inb_p(speakup_info.port_tts + UART_RX); 1482067fd92SSamuel Thibault return (synth_status & TTS_READABLE) != 0; 1492067fd92SSamuel Thibault } 1502067fd92SSamuel Thibault 1512067fd92SSamuel Thibault static inline bool synth_writable(void) 1522067fd92SSamuel Thibault { 1532067fd92SSamuel Thibault synth_status = inb_p(speakup_info.port_tts + UART_RX); 1542067fd92SSamuel Thibault return (synth_status & TTS_WRITABLE) != 0; 1552067fd92SSamuel Thibault } 1562067fd92SSamuel Thibault 1572067fd92SSamuel Thibault static inline bool synth_full(void) 1582067fd92SSamuel Thibault { 1592067fd92SSamuel Thibault synth_status = inb_p(speakup_info.port_tts + UART_RX); 1602067fd92SSamuel Thibault return (synth_status & TTS_ALMOST_FULL) != 0; 1612067fd92SSamuel Thibault } 1622067fd92SSamuel Thibault 1632067fd92SSamuel Thibault static void spk_out(const char ch) 1642067fd92SSamuel Thibault { 1652067fd92SSamuel Thibault int timeout = SPK_XMITR_TIMEOUT; 1662067fd92SSamuel Thibault 1672067fd92SSamuel Thibault while (!synth_writable()) { 1682067fd92SSamuel Thibault if (!--timeout) 1692067fd92SSamuel Thibault break; 1702067fd92SSamuel Thibault udelay(1); 1712067fd92SSamuel Thibault } 1722067fd92SSamuel Thibault outb_p(ch, speakup_info.port_tts); 1732067fd92SSamuel Thibault timeout = SPK_XMITR_TIMEOUT; 1742067fd92SSamuel Thibault while (synth_writable()) { 1752067fd92SSamuel Thibault if (!--timeout) 1762067fd92SSamuel Thibault break; 1772067fd92SSamuel Thibault udelay(1); 1782067fd92SSamuel Thibault } 1792067fd92SSamuel Thibault } 1802067fd92SSamuel Thibault 1812067fd92SSamuel Thibault static void do_catch_up(struct spk_synth *synth) 1822067fd92SSamuel Thibault { 1832067fd92SSamuel Thibault u_char ch; 1842067fd92SSamuel Thibault unsigned long flags; 1852067fd92SSamuel Thibault unsigned long jiff_max; 1862067fd92SSamuel Thibault struct var_t *jiffy_delta; 1872067fd92SSamuel Thibault struct var_t *delay_time; 1882067fd92SSamuel Thibault int jiffy_delta_val; 1892067fd92SSamuel Thibault int delay_time_val; 1902067fd92SSamuel Thibault 1912067fd92SSamuel Thibault jiffy_delta = spk_get_var(JIFFY); 1922067fd92SSamuel Thibault delay_time = spk_get_var(DELAY); 1932067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 1942067fd92SSamuel Thibault jiffy_delta_val = jiffy_delta->u.n.value; 1952067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 1962067fd92SSamuel Thibault jiff_max = jiffies + jiffy_delta_val; 1972067fd92SSamuel Thibault while (!kthread_should_stop()) { 1982067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 1992067fd92SSamuel Thibault if (speakup_info.flushing) { 2002067fd92SSamuel Thibault speakup_info.flushing = 0; 2012067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2022067fd92SSamuel Thibault synth->flush(synth); 2032067fd92SSamuel Thibault continue; 2042067fd92SSamuel Thibault } 2052067fd92SSamuel Thibault synth_buffer_skip_nonlatin1(); 2062067fd92SSamuel Thibault if (synth_buffer_empty()) { 2072067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2082067fd92SSamuel Thibault break; 2092067fd92SSamuel Thibault } 2102067fd92SSamuel Thibault set_current_state(TASK_INTERRUPTIBLE); 2112067fd92SSamuel Thibault delay_time_val = delay_time->u.n.value; 2122067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2132067fd92SSamuel Thibault if (synth_full()) { 2142067fd92SSamuel Thibault schedule_timeout(msecs_to_jiffies(delay_time_val)); 2152067fd92SSamuel Thibault continue; 2162067fd92SSamuel Thibault } 2172067fd92SSamuel Thibault set_current_state(TASK_RUNNING); 2182067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 2192067fd92SSamuel Thibault ch = synth_buffer_getc(); 2202067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2212067fd92SSamuel Thibault if (ch == '\n') 2222067fd92SSamuel Thibault ch = PROCSPEECH; 2232067fd92SSamuel Thibault spk_out(ch); 2242067fd92SSamuel Thibault if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) { 2252067fd92SSamuel Thibault spk_out(PROCSPEECH); 2262067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 2272067fd92SSamuel Thibault delay_time_val = delay_time->u.n.value; 2282067fd92SSamuel Thibault jiffy_delta_val = jiffy_delta->u.n.value; 2292067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2302067fd92SSamuel Thibault schedule_timeout(msecs_to_jiffies(delay_time_val)); 2312067fd92SSamuel Thibault jiff_max = jiffies + jiffy_delta_val; 2322067fd92SSamuel Thibault } 2332067fd92SSamuel Thibault } 2342067fd92SSamuel Thibault spk_out(PROCSPEECH); 2352067fd92SSamuel Thibault } 2362067fd92SSamuel Thibault 2372067fd92SSamuel Thibault static const char *synth_immediate(struct spk_synth *synth, const char *buf) 2382067fd92SSamuel Thibault { 2392067fd92SSamuel Thibault u_char ch; 2402067fd92SSamuel Thibault 2412067fd92SSamuel Thibault while ((ch = (u_char)*buf)) { 2422067fd92SSamuel Thibault if (synth_full()) 2432067fd92SSamuel Thibault return buf; 2442067fd92SSamuel Thibault if (ch == '\n') 2452067fd92SSamuel Thibault ch = PROCSPEECH; 2462067fd92SSamuel Thibault spk_out(ch); 2472067fd92SSamuel Thibault buf++; 2482067fd92SSamuel Thibault } 2492067fd92SSamuel Thibault return NULL; 2502067fd92SSamuel Thibault } 2512067fd92SSamuel Thibault 2522067fd92SSamuel Thibault static void synth_flush(struct spk_synth *synth) 2532067fd92SSamuel Thibault { 2542067fd92SSamuel Thibault outb_p(SYNTH_CLEAR, speakup_info.port_tts); 2552067fd92SSamuel Thibault while (synth_writable()) 2562067fd92SSamuel Thibault cpu_relax(); 2572067fd92SSamuel Thibault } 2582067fd92SSamuel Thibault 2592067fd92SSamuel Thibault static char synth_read_tts(void) 2602067fd92SSamuel Thibault { 2612067fd92SSamuel Thibault u_char ch; 2622067fd92SSamuel Thibault 2632067fd92SSamuel Thibault while (!synth_readable()) 2642067fd92SSamuel Thibault cpu_relax(); 2652067fd92SSamuel Thibault ch = synth_status & 0x7f; 2662067fd92SSamuel Thibault outb_p(ch, speakup_info.port_tts); 2672067fd92SSamuel Thibault while (synth_readable()) 2682067fd92SSamuel Thibault cpu_relax(); 2692067fd92SSamuel Thibault return (char)ch; 2702067fd92SSamuel Thibault } 2712067fd92SSamuel Thibault 2722067fd92SSamuel Thibault /* interrogate the DoubleTalk PC and return its settings */ 2732067fd92SSamuel Thibault static struct synth_settings *synth_interrogate(struct spk_synth *synth) 2742067fd92SSamuel Thibault { 2752067fd92SSamuel Thibault u_char *t; 2762067fd92SSamuel Thibault static char buf[sizeof(struct synth_settings) + 1]; 2772067fd92SSamuel Thibault int total, i; 2782067fd92SSamuel Thibault static struct synth_settings status; 2792067fd92SSamuel Thibault 2802067fd92SSamuel Thibault synth_immediate(synth, "\x18\x01?"); 2812067fd92SSamuel Thibault for (total = 0, i = 0; i < 50; i++) { 2822067fd92SSamuel Thibault buf[total] = synth_read_tts(); 2832067fd92SSamuel Thibault if (total > 2 && buf[total] == 0x7f) 2842067fd92SSamuel Thibault break; 2852067fd92SSamuel Thibault if (total < sizeof(struct synth_settings)) 2862067fd92SSamuel Thibault total++; 2872067fd92SSamuel Thibault } 2882067fd92SSamuel Thibault t = buf; 2892067fd92SSamuel Thibault /* serial number is little endian */ 2902067fd92SSamuel Thibault status.serial_number = t[0] + t[1] * 256; 2912067fd92SSamuel Thibault t += 2; 2922067fd92SSamuel Thibault for (i = 0; *t != '\r'; t++) { 2932067fd92SSamuel Thibault status.rom_version[i] = *t; 2942067fd92SSamuel Thibault if (i < sizeof(status.rom_version) - 1) 2952067fd92SSamuel Thibault i++; 2962067fd92SSamuel Thibault } 2972067fd92SSamuel Thibault status.rom_version[i] = 0; 2982067fd92SSamuel Thibault t++; 2992067fd92SSamuel Thibault status.mode = *t++; 3002067fd92SSamuel Thibault status.punc_level = *t++; 3012067fd92SSamuel Thibault status.formant_freq = *t++; 3022067fd92SSamuel Thibault status.pitch = *t++; 3032067fd92SSamuel Thibault status.speed = *t++; 3042067fd92SSamuel Thibault status.volume = *t++; 3052067fd92SSamuel Thibault status.tone = *t++; 3062067fd92SSamuel Thibault status.expression = *t++; 3072067fd92SSamuel Thibault status.ext_dict_loaded = *t++; 3082067fd92SSamuel Thibault status.ext_dict_status = *t++; 3092067fd92SSamuel Thibault status.free_ram = *t++; 3102067fd92SSamuel Thibault status.articulation = *t++; 3112067fd92SSamuel Thibault status.reverb = *t++; 3122067fd92SSamuel Thibault status.eob = *t++; 3132067fd92SSamuel Thibault return &status; 3142067fd92SSamuel Thibault } 3152067fd92SSamuel Thibault 3162067fd92SSamuel Thibault static int synth_probe(struct spk_synth *synth) 3172067fd92SSamuel Thibault { 3182067fd92SSamuel Thibault unsigned int port_val = 0; 3191f8ff525SColin Ian King int i; 3202067fd92SSamuel Thibault struct synth_settings *sp; 3212067fd92SSamuel Thibault 3222067fd92SSamuel Thibault pr_info("Probing for DoubleTalk.\n"); 3232067fd92SSamuel Thibault if (port_forced) { 3242067fd92SSamuel Thibault speakup_info.port_tts = port_forced; 3252067fd92SSamuel Thibault pr_info("probe forced to %x by kernel command line\n", 3262067fd92SSamuel Thibault speakup_info.port_tts); 3272067fd92SSamuel Thibault if ((port_forced & 0xf) != 0xf) 3282067fd92SSamuel Thibault pr_info("warning: port base should probably end with f\n"); 3292067fd92SSamuel Thibault if (synth_request_region(speakup_info.port_tts - 1, 3302067fd92SSamuel Thibault SYNTH_IO_EXTENT)) { 3312067fd92SSamuel Thibault pr_warn("sorry, port already reserved\n"); 3322067fd92SSamuel Thibault return -EBUSY; 3332067fd92SSamuel Thibault } 3342067fd92SSamuel Thibault port_val = inw(speakup_info.port_tts - 1); 3352067fd92SSamuel Thibault synth_lpc = speakup_info.port_tts - 1; 3362067fd92SSamuel Thibault } else { 3372067fd92SSamuel Thibault for (i = 0; synth_portlist[i]; i++) { 3382067fd92SSamuel Thibault if (synth_request_region(synth_portlist[i], 3392067fd92SSamuel Thibault SYNTH_IO_EXTENT)) 3402067fd92SSamuel Thibault continue; 3412067fd92SSamuel Thibault port_val = inw(synth_portlist[i]) & 0xfbff; 3422067fd92SSamuel Thibault if (port_val == 0x107f) { 3432067fd92SSamuel Thibault synth_lpc = synth_portlist[i]; 3442067fd92SSamuel Thibault speakup_info.port_tts = synth_lpc + 1; 3452067fd92SSamuel Thibault break; 3462067fd92SSamuel Thibault } 3472067fd92SSamuel Thibault synth_release_region(synth_portlist[i], 3482067fd92SSamuel Thibault SYNTH_IO_EXTENT); 3492067fd92SSamuel Thibault } 3502067fd92SSamuel Thibault } 3512067fd92SSamuel Thibault port_val &= 0xfbff; 3522067fd92SSamuel Thibault if (port_val != 0x107f) { 3532067fd92SSamuel Thibault pr_info("DoubleTalk PC: not found\n"); 3542067fd92SSamuel Thibault if (synth_lpc) 3552067fd92SSamuel Thibault synth_release_region(synth_lpc, SYNTH_IO_EXTENT); 3562067fd92SSamuel Thibault return -ENODEV; 3572067fd92SSamuel Thibault } 3582067fd92SSamuel Thibault while (inw_p(synth_lpc) != 0x147f) 3592067fd92SSamuel Thibault cpu_relax(); /* wait until it's ready */ 3602067fd92SSamuel Thibault sp = synth_interrogate(synth); 3612067fd92SSamuel Thibault pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n", 3622067fd92SSamuel Thibault synth->long_name, synth_lpc, synth_lpc + SYNTH_IO_EXTENT - 1, 3632067fd92SSamuel Thibault sp->rom_version, sp->serial_number, synth->version); 3642067fd92SSamuel Thibault synth->alive = 1; 3652067fd92SSamuel Thibault return 0; 3662067fd92SSamuel Thibault } 3672067fd92SSamuel Thibault 3681941ab1dSSamuel Thibault static void dtlk_release(struct spk_synth *synth) 3692067fd92SSamuel Thibault { 3702067fd92SSamuel Thibault spk_stop_serial_interrupt(); 3712067fd92SSamuel Thibault if (speakup_info.port_tts) 3722067fd92SSamuel Thibault synth_release_region(speakup_info.port_tts - 1, 3732067fd92SSamuel Thibault SYNTH_IO_EXTENT); 3742067fd92SSamuel Thibault speakup_info.port_tts = 0; 3752067fd92SSamuel Thibault } 3762067fd92SSamuel Thibault 3772067fd92SSamuel Thibault module_param_hw_named(port, port_forced, int, ioport, 0444); 3782067fd92SSamuel Thibault module_param_named(start, synth_dtlk.startup, short, 0444); 3792067fd92SSamuel Thibault 3802067fd92SSamuel Thibault MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); 3812067fd92SSamuel Thibault MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 3822067fd92SSamuel Thibault 3832067fd92SSamuel Thibault module_spk_synth(synth_dtlk); 3842067fd92SSamuel Thibault 3852067fd92SSamuel Thibault MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 3862067fd92SSamuel Thibault MODULE_AUTHOR("David Borowski"); 3872067fd92SSamuel Thibault MODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers"); 3882067fd92SSamuel Thibault MODULE_LICENSE("GPL"); 3892067fd92SSamuel Thibault MODULE_VERSION(DRV_VERSION); 3902067fd92SSamuel Thibault 391