^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * this version considerably modified by David Borowski, david575@rogers.com
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 1998-99 Kirk Reiser.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (C) 2003 David Borowski.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * specificly written as a driver for the speakup screenreview
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * package it's not a general device driver.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * This driver is for the RC Systems DoubleTalk PC internal synthesizer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/jiffies.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/timer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/kthread.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include "spk_priv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include "serialio.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include "speakup_dtlk.h" /* local header file for DoubleTalk values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include "speakup.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define DRV_VERSION "2.10"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define PROCSPEECH 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static int synth_probe(struct spk_synth *synth);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static void dtlk_release(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static const char *synth_immediate(struct spk_synth *synth, const char *buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static void do_catch_up(struct spk_synth *synth);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static void synth_flush(struct spk_synth *synth);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) static int synth_lpc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static int port_forced;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static unsigned int synth_portlist[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static u_char synth_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static struct var_t vars[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) { CAPS_START, .u.s = {"\x01+35p" } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) { CAPS_STOP, .u.s = {"\x01-35p" } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) V_LAST_VAR
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) * These attributes will appear in /sys/accessibility/speakup/dtlk.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) static struct kobj_attribute caps_start_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) __ATTR(caps_start, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) static struct kobj_attribute caps_stop_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) __ATTR(caps_stop, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static struct kobj_attribute freq_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) __ATTR(freq, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static struct kobj_attribute pitch_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) __ATTR(pitch, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static struct kobj_attribute punct_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) __ATTR(punct, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static struct kobj_attribute rate_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) __ATTR(rate, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static struct kobj_attribute tone_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) __ATTR(tone, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static struct kobj_attribute voice_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) __ATTR(voice, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) static struct kobj_attribute vol_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) __ATTR(vol, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static struct kobj_attribute delay_time_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) __ATTR(delay_time, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static struct kobj_attribute direct_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) __ATTR(direct, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static struct kobj_attribute full_time_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) __ATTR(full_time, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static struct kobj_attribute jiffy_delta_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) static struct kobj_attribute trigger_time_attribute =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) __ATTR(trigger_time, 0644, spk_var_show, spk_var_store);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * Create a group of attributes so that we can create and destroy them all
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * at once.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) static struct attribute *synth_attrs[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) &caps_start_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) &caps_stop_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) &freq_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) &pitch_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) &punct_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) &rate_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) &tone_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) &voice_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) &vol_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) &delay_time_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) &direct_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) &full_time_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) &jiffy_delta_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) &trigger_time_attribute.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) NULL, /* need to NULL terminate the list of attributes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static struct spk_synth synth_dtlk = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) .name = "dtlk",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) .version = DRV_VERSION,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) .long_name = "DoubleTalk PC",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) .init = "\x01@\x01\x31y",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) .procspeech = PROCSPEECH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) .clear = SYNTH_CLEAR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) .delay = 500,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) .trigger = 30,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) .jiffies = 50,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) .full = 1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) .startup = SYNTH_START,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) .checkval = SYNTH_CHECK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) .vars = vars,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) .io_ops = &spk_serial_io_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .probe = synth_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .release = dtlk_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .synth_immediate = synth_immediate,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) .catch_up = do_catch_up,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) .flush = synth_flush,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) .is_alive = spk_synth_is_alive_nop,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) .synth_adjust = NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) .read_buff_add = NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) .get_index = spk_synth_get_index,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) .indexing = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .command = "\x01%di",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .lowindex = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .highindex = 5,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .currindex = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) .attributes = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) .attrs = synth_attrs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) .name = "dtlk",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static inline bool synth_readable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) synth_status = inb_p(speakup_info.port_tts + UART_RX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) return (synth_status & TTS_READABLE) != 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) static inline bool synth_writable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) synth_status = inb_p(speakup_info.port_tts + UART_RX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return (synth_status & TTS_WRITABLE) != 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) static inline bool synth_full(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) synth_status = inb_p(speakup_info.port_tts + UART_RX);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) return (synth_status & TTS_ALMOST_FULL) != 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) static void spk_out(const char ch)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) int timeout = SPK_XMITR_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) while (!synth_writable()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) if (!--timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) udelay(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) outb_p(ch, speakup_info.port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) timeout = SPK_XMITR_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) while (synth_writable()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) if (!--timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) udelay(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) static void do_catch_up(struct spk_synth *synth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) u_char ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) unsigned long jiff_max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) struct var_t *jiffy_delta;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) struct var_t *delay_time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) int jiffy_delta_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) int delay_time_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) jiffy_delta = spk_get_var(JIFFY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) delay_time = spk_get_var(DELAY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) spin_lock_irqsave(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) jiffy_delta_val = jiffy_delta->u.n.value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) spin_unlock_irqrestore(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) jiff_max = jiffies + jiffy_delta_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) while (!kthread_should_stop()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) spin_lock_irqsave(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) if (speakup_info.flushing) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) speakup_info.flushing = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) spin_unlock_irqrestore(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) synth->flush(synth);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) synth_buffer_skip_nonlatin1();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) if (synth_buffer_empty()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) spin_unlock_irqrestore(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) set_current_state(TASK_INTERRUPTIBLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) delay_time_val = delay_time->u.n.value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) spin_unlock_irqrestore(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) if (synth_full()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) schedule_timeout(msecs_to_jiffies(delay_time_val));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) set_current_state(TASK_RUNNING);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) spin_lock_irqsave(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) ch = synth_buffer_getc();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) spin_unlock_irqrestore(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) if (ch == '\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) ch = PROCSPEECH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) spk_out(ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) spk_out(PROCSPEECH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) spin_lock_irqsave(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) delay_time_val = delay_time->u.n.value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) jiffy_delta_val = jiffy_delta->u.n.value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) spin_unlock_irqrestore(&speakup_info.spinlock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) schedule_timeout(msecs_to_jiffies(delay_time_val));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) jiff_max = jiffies + jiffy_delta_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) spk_out(PROCSPEECH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) static const char *synth_immediate(struct spk_synth *synth, const char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) u_char ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) while ((ch = (u_char)*buf)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) if (synth_full())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) return buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) if (ch == '\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) ch = PROCSPEECH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) spk_out(ch);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) buf++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) static void synth_flush(struct spk_synth *synth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) outb_p(SYNTH_CLEAR, speakup_info.port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) while (synth_writable())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) cpu_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) static char synth_read_tts(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) u_char ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) while (!synth_readable())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) cpu_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) ch = synth_status & 0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) outb_p(ch, speakup_info.port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) while (synth_readable())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) cpu_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) return (char)ch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) /* interrogate the DoubleTalk PC and return its settings */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) static struct synth_settings *synth_interrogate(struct spk_synth *synth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) u_char *t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) static char buf[sizeof(struct synth_settings) + 1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) int total, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) static struct synth_settings status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) synth_immediate(synth, "\x18\x01?");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) for (total = 0, i = 0; i < 50; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) buf[total] = synth_read_tts();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) if (total > 2 && buf[total] == 0x7f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) if (total < sizeof(struct synth_settings))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) total++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) t = buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) /* serial number is little endian */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) status.serial_number = t[0] + t[1] * 256;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) t += 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) for (i = 0; *t != '\r'; t++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) status.rom_version[i] = *t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) if (i < sizeof(status.rom_version) - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) status.rom_version[i] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) status.mode = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) status.punc_level = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) status.formant_freq = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) status.pitch = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) status.speed = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) status.volume = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) status.tone = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) status.expression = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) status.ext_dict_loaded = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) status.ext_dict_status = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) status.free_ram = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) status.articulation = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) status.reverb = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) status.eob = *t++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) return &status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) static int synth_probe(struct spk_synth *synth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) unsigned int port_val = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) struct synth_settings *sp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) pr_info("Probing for DoubleTalk.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) if (port_forced) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) speakup_info.port_tts = port_forced;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) pr_info("probe forced to %x by kernel command line\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) speakup_info.port_tts);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) if ((port_forced & 0xf) != 0xf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) pr_info("warning: port base should probably end with f\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) if (synth_request_region(speakup_info.port_tts - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) SYNTH_IO_EXTENT)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) pr_warn("sorry, port already reserved\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) port_val = inw(speakup_info.port_tts - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) synth_lpc = speakup_info.port_tts - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) for (i = 0; synth_portlist[i]; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) if (synth_request_region(synth_portlist[i],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) SYNTH_IO_EXTENT))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) port_val = inw(synth_portlist[i]) & 0xfbff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) if (port_val == 0x107f) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) synth_lpc = synth_portlist[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) speakup_info.port_tts = synth_lpc + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) synth_release_region(synth_portlist[i],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) SYNTH_IO_EXTENT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) port_val &= 0xfbff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) if (port_val != 0x107f) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) pr_info("DoubleTalk PC: not found\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) if (synth_lpc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) synth_release_region(synth_lpc, SYNTH_IO_EXTENT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) while (inw_p(synth_lpc) != 0x147f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) cpu_relax(); /* wait until it's ready */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) sp = synth_interrogate(synth);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) synth->long_name, synth_lpc, synth_lpc + SYNTH_IO_EXTENT - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) sp->rom_version, sp->serial_number, synth->version);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) synth->alive = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) static void dtlk_release(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) spk_stop_serial_interrupt();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) if (speakup_info.port_tts)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) synth_release_region(speakup_info.port_tts - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) SYNTH_IO_EXTENT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) speakup_info.port_tts = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) module_param_hw_named(port, port_forced, int, ioport, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) module_param_named(start, synth_dtlk.startup, short, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) module_spk_synth(synth_dtlk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) MODULE_AUTHOR("David Borowski");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) MODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) MODULE_VERSION(DRV_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390)