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 * 90b4efcb1STom Rix * specifically written as a driver for the speakup screenreview 102067fd92SSamuel Thibault * s not a general device driver. 112067fd92SSamuel Thibault */ 122067fd92SSamuel Thibault #include <linux/unistd.h> 132067fd92SSamuel Thibault #include <linux/proc_fs.h> 142067fd92SSamuel Thibault #include <linux/jiffies.h> 152067fd92SSamuel Thibault #include <linux/spinlock.h> 162067fd92SSamuel Thibault #include <linux/sched.h> 172067fd92SSamuel Thibault #include <linux/timer.h> 182067fd92SSamuel Thibault #include <linux/kthread.h> 192067fd92SSamuel Thibault #include "speakup.h" 202067fd92SSamuel Thibault #include "spk_priv.h" 212067fd92SSamuel Thibault 222067fd92SSamuel Thibault #define DRV_VERSION "2.20" 232067fd92SSamuel Thibault #define SYNTH_CLEAR 0x03 242067fd92SSamuel Thibault #define PROCSPEECH 0x0b 252067fd92SSamuel Thibault static int xoff; 262067fd92SSamuel Thibault 272067fd92SSamuel Thibault static inline int synth_full(void) 282067fd92SSamuel Thibault { 292067fd92SSamuel Thibault return xoff; 302067fd92SSamuel Thibault } 312067fd92SSamuel Thibault 322067fd92SSamuel Thibault static void do_catch_up(struct spk_synth *synth); 332067fd92SSamuel Thibault static void synth_flush(struct spk_synth *synth); 342067fd92SSamuel Thibault static void read_buff_add(u_char c); 352067fd92SSamuel Thibault static unsigned char get_index(struct spk_synth *synth); 362067fd92SSamuel Thibault 372067fd92SSamuel Thibault static int in_escape; 382067fd92SSamuel Thibault static int is_flushing; 392067fd92SSamuel Thibault 40d1b928eeSYang Yingliang static DEFINE_SPINLOCK(flush_lock); 412067fd92SSamuel Thibault static DECLARE_WAIT_QUEUE_HEAD(flush); 422067fd92SSamuel Thibault 43*44d3e977SOsama Muhammad enum default_vars_id { 44*44d3e977SOsama Muhammad CAPS_START_ID = 0, CAPS_STOP_ID, 45*44d3e977SOsama Muhammad RATE_ID, PITCH_ID, INFLECTION_ID, 46*44d3e977SOsama Muhammad VOL_ID, PUNCT_ID, VOICE_ID, 47*44d3e977SOsama Muhammad DIRECT_ID, V_LAST_VAR_ID, 48*44d3e977SOsama Muhammad NB_ID, 49*44d3e977SOsama Muhammad }; 50*44d3e977SOsama Muhammad 51*44d3e977SOsama Muhammad static struct var_t vars[NB_ID] = { 52*44d3e977SOsama Muhammad [CAPS_START_ID] = { CAPS_START, .u.s = {"[:dv ap 160] " } }, 53*44d3e977SOsama Muhammad [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } }, 54*44d3e977SOsama Muhammad [RATE_ID] = { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } }, 55*44d3e977SOsama Muhammad [PITCH_ID] = { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL } }, 56*44d3e977SOsama Muhammad [INFLECTION_ID] = { INFLECTION, .u.n = {"[:dv pr %d] ", 100, 0, 10000, 0, 0, NULL } }, 57*44d3e977SOsama Muhammad [VOL_ID] = { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } }, 58*44d3e977SOsama Muhammad [PUNCT_ID] = { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } }, 59*44d3e977SOsama Muhammad [VOICE_ID] = { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } }, 60*44d3e977SOsama Muhammad [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 612067fd92SSamuel Thibault V_LAST_VAR 622067fd92SSamuel Thibault }; 632067fd92SSamuel Thibault 642067fd92SSamuel Thibault /* 652067fd92SSamuel Thibault * These attributes will appear in /sys/accessibility/speakup/dectlk. 662067fd92SSamuel Thibault */ 672067fd92SSamuel Thibault static struct kobj_attribute caps_start_attribute = 682067fd92SSamuel Thibault __ATTR(caps_start, 0644, spk_var_show, spk_var_store); 692067fd92SSamuel Thibault static struct kobj_attribute caps_stop_attribute = 702067fd92SSamuel Thibault __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); 712067fd92SSamuel Thibault static struct kobj_attribute pitch_attribute = 722067fd92SSamuel Thibault __ATTR(pitch, 0644, spk_var_show, spk_var_store); 732067fd92SSamuel Thibault static struct kobj_attribute inflection_attribute = 742067fd92SSamuel Thibault __ATTR(inflection, 0644, spk_var_show, spk_var_store); 752067fd92SSamuel Thibault static struct kobj_attribute punct_attribute = 762067fd92SSamuel Thibault __ATTR(punct, 0644, spk_var_show, spk_var_store); 772067fd92SSamuel Thibault static struct kobj_attribute rate_attribute = 782067fd92SSamuel Thibault __ATTR(rate, 0644, spk_var_show, spk_var_store); 792067fd92SSamuel Thibault static struct kobj_attribute voice_attribute = 802067fd92SSamuel Thibault __ATTR(voice, 0644, spk_var_show, spk_var_store); 812067fd92SSamuel Thibault static struct kobj_attribute vol_attribute = 822067fd92SSamuel Thibault __ATTR(vol, 0644, spk_var_show, spk_var_store); 832067fd92SSamuel Thibault 842067fd92SSamuel Thibault static struct kobj_attribute delay_time_attribute = 852067fd92SSamuel Thibault __ATTR(delay_time, 0644, spk_var_show, spk_var_store); 862067fd92SSamuel Thibault static struct kobj_attribute direct_attribute = 872067fd92SSamuel Thibault __ATTR(direct, 0644, spk_var_show, spk_var_store); 882067fd92SSamuel Thibault static struct kobj_attribute full_time_attribute = 892067fd92SSamuel Thibault __ATTR(full_time, 0644, spk_var_show, spk_var_store); 901f7c14afSSamuel Thibault static struct kobj_attribute flush_time_attribute = 911f7c14afSSamuel Thibault __ATTR(flush_time, 0644, spk_var_show, spk_var_store); 922067fd92SSamuel Thibault static struct kobj_attribute jiffy_delta_attribute = 932067fd92SSamuel Thibault __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); 942067fd92SSamuel Thibault static struct kobj_attribute trigger_time_attribute = 952067fd92SSamuel Thibault __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); 962067fd92SSamuel Thibault 972067fd92SSamuel Thibault /* 982067fd92SSamuel Thibault * Create a group of attributes so that we can create and destroy them all 992067fd92SSamuel Thibault * at once. 1002067fd92SSamuel Thibault */ 1012067fd92SSamuel Thibault static struct attribute *synth_attrs[] = { 1022067fd92SSamuel Thibault &caps_start_attribute.attr, 1032067fd92SSamuel Thibault &caps_stop_attribute.attr, 1042067fd92SSamuel Thibault &pitch_attribute.attr, 1052067fd92SSamuel Thibault &inflection_attribute.attr, 1062067fd92SSamuel Thibault &punct_attribute.attr, 1072067fd92SSamuel Thibault &rate_attribute.attr, 1082067fd92SSamuel Thibault &voice_attribute.attr, 1092067fd92SSamuel Thibault &vol_attribute.attr, 1102067fd92SSamuel Thibault &delay_time_attribute.attr, 1112067fd92SSamuel Thibault &direct_attribute.attr, 1122067fd92SSamuel Thibault &full_time_attribute.attr, 1131f7c14afSSamuel Thibault &flush_time_attribute.attr, 1142067fd92SSamuel Thibault &jiffy_delta_attribute.attr, 1152067fd92SSamuel Thibault &trigger_time_attribute.attr, 1162067fd92SSamuel Thibault NULL, /* need to NULL terminate the list of attributes */ 1172067fd92SSamuel Thibault }; 1182067fd92SSamuel Thibault 1192067fd92SSamuel Thibault static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306}; 1202067fd92SSamuel Thibault static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73}; 1212067fd92SSamuel Thibault 1222067fd92SSamuel Thibault static struct spk_synth synth_dectlk = { 1232067fd92SSamuel Thibault .name = "dectlk", 1242067fd92SSamuel Thibault .version = DRV_VERSION, 1252067fd92SSamuel Thibault .long_name = "Dectalk Express", 1262067fd92SSamuel Thibault .init = "[:error sp :name paul :rate 180 :tsr off] ", 1272067fd92SSamuel Thibault .procspeech = PROCSPEECH, 1282067fd92SSamuel Thibault .clear = SYNTH_CLEAR, 1292067fd92SSamuel Thibault .delay = 500, 1302067fd92SSamuel Thibault .trigger = 50, 1312067fd92SSamuel Thibault .jiffies = 50, 1322067fd92SSamuel Thibault .full = 40000, 1331f7c14afSSamuel Thibault .flush_time = 4000, 1342067fd92SSamuel Thibault .dev_name = SYNTH_DEFAULT_DEV, 1352067fd92SSamuel Thibault .startup = SYNTH_START, 1362067fd92SSamuel Thibault .checkval = SYNTH_CHECK, 1372067fd92SSamuel Thibault .vars = vars, 1382067fd92SSamuel Thibault .default_pitch = ap_defaults, 1392067fd92SSamuel Thibault .default_vol = g5_defaults, 1402067fd92SSamuel Thibault .io_ops = &spk_ttyio_ops, 1412067fd92SSamuel Thibault .probe = spk_ttyio_synth_probe, 1422067fd92SSamuel Thibault .release = spk_ttyio_release, 1432067fd92SSamuel Thibault .synth_immediate = spk_ttyio_synth_immediate, 1442067fd92SSamuel Thibault .catch_up = do_catch_up, 1452067fd92SSamuel Thibault .flush = synth_flush, 1462067fd92SSamuel Thibault .is_alive = spk_synth_is_alive_restart, 1472067fd92SSamuel Thibault .synth_adjust = NULL, 1482067fd92SSamuel Thibault .read_buff_add = read_buff_add, 1492067fd92SSamuel Thibault .get_index = get_index, 1502067fd92SSamuel Thibault .indexing = { 1512067fd92SSamuel Thibault .command = "[:in re %d ] ", 1522067fd92SSamuel Thibault .lowindex = 1, 1532067fd92SSamuel Thibault .highindex = 8, 1542067fd92SSamuel Thibault .currindex = 1, 1552067fd92SSamuel Thibault }, 1562067fd92SSamuel Thibault .attributes = { 1572067fd92SSamuel Thibault .attrs = synth_attrs, 1582067fd92SSamuel Thibault .name = "dectlk", 1592067fd92SSamuel Thibault }, 1602067fd92SSamuel Thibault }; 1612067fd92SSamuel Thibault 1622067fd92SSamuel Thibault static int is_indnum(u_char *ch) 1632067fd92SSamuel Thibault { 1642067fd92SSamuel Thibault if ((*ch >= '0') && (*ch <= '9')) { 1652067fd92SSamuel Thibault *ch = *ch - '0'; 1662067fd92SSamuel Thibault return 1; 1672067fd92SSamuel Thibault } 1682067fd92SSamuel Thibault return 0; 1692067fd92SSamuel Thibault } 1702067fd92SSamuel Thibault 1712067fd92SSamuel Thibault static u_char lastind; 1722067fd92SSamuel Thibault 1732067fd92SSamuel Thibault static unsigned char get_index(struct spk_synth *synth) 1742067fd92SSamuel Thibault { 1752067fd92SSamuel Thibault u_char rv; 1762067fd92SSamuel Thibault 1772067fd92SSamuel Thibault rv = lastind; 1782067fd92SSamuel Thibault lastind = 0; 1792067fd92SSamuel Thibault return rv; 1802067fd92SSamuel Thibault } 1812067fd92SSamuel Thibault 1822067fd92SSamuel Thibault static void read_buff_add(u_char c) 1832067fd92SSamuel Thibault { 1842067fd92SSamuel Thibault static int ind = -1; 1852067fd92SSamuel Thibault 1862067fd92SSamuel Thibault if (c == 0x01) { 1872067fd92SSamuel Thibault unsigned long flags; 1882067fd92SSamuel Thibault 1892067fd92SSamuel Thibault spin_lock_irqsave(&flush_lock, flags); 1902067fd92SSamuel Thibault is_flushing = 0; 1912067fd92SSamuel Thibault wake_up_interruptible(&flush); 1922067fd92SSamuel Thibault spin_unlock_irqrestore(&flush_lock, flags); 1932067fd92SSamuel Thibault } else if (c == 0x13) { 1942067fd92SSamuel Thibault xoff = 1; 1952067fd92SSamuel Thibault } else if (c == 0x11) { 1962067fd92SSamuel Thibault xoff = 0; 1972067fd92SSamuel Thibault } else if (is_indnum(&c)) { 1982067fd92SSamuel Thibault if (ind == -1) 1992067fd92SSamuel Thibault ind = c; 2002067fd92SSamuel Thibault else 2012067fd92SSamuel Thibault ind = ind * 10 + c; 2022067fd92SSamuel Thibault } else if ((c > 31) && (c < 127)) { 2032067fd92SSamuel Thibault if (ind != -1) 2042067fd92SSamuel Thibault lastind = (u_char)ind; 2052067fd92SSamuel Thibault ind = -1; 2062067fd92SSamuel Thibault } 2072067fd92SSamuel Thibault } 2082067fd92SSamuel Thibault 2092067fd92SSamuel Thibault static void do_catch_up(struct spk_synth *synth) 2102067fd92SSamuel Thibault { 2112067fd92SSamuel Thibault int synth_full_val = 0; 2122067fd92SSamuel Thibault static u_char ch; 2132067fd92SSamuel Thibault static u_char last = '\0'; 2142067fd92SSamuel Thibault unsigned long flags; 2152067fd92SSamuel Thibault unsigned long jiff_max; 2161f7c14afSSamuel Thibault unsigned long timeout; 2172067fd92SSamuel Thibault DEFINE_WAIT(wait); 2182067fd92SSamuel Thibault struct var_t *jiffy_delta; 2192067fd92SSamuel Thibault struct var_t *delay_time; 2201f7c14afSSamuel Thibault struct var_t *flush_time; 2212067fd92SSamuel Thibault int jiffy_delta_val; 2222067fd92SSamuel Thibault int delay_time_val; 2231f7c14afSSamuel Thibault int timeout_val; 2242067fd92SSamuel Thibault 2252067fd92SSamuel Thibault jiffy_delta = spk_get_var(JIFFY); 2262067fd92SSamuel Thibault delay_time = spk_get_var(DELAY); 2271f7c14afSSamuel Thibault flush_time = spk_get_var(FLUSH); 2282067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 2292067fd92SSamuel Thibault jiffy_delta_val = jiffy_delta->u.n.value; 2301f7c14afSSamuel Thibault timeout_val = flush_time->u.n.value; 2312067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2321f7c14afSSamuel Thibault timeout = msecs_to_jiffies(timeout_val); 2332067fd92SSamuel Thibault jiff_max = jiffies + jiffy_delta_val; 2342067fd92SSamuel Thibault 2352067fd92SSamuel Thibault while (!kthread_should_stop()) { 2362067fd92SSamuel Thibault /* if no ctl-a in 4, send data anyway */ 2372067fd92SSamuel Thibault spin_lock_irqsave(&flush_lock, flags); 2382067fd92SSamuel Thibault while (is_flushing && timeout) { 2392067fd92SSamuel Thibault prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE); 2402067fd92SSamuel Thibault spin_unlock_irqrestore(&flush_lock, flags); 2412067fd92SSamuel Thibault timeout = schedule_timeout(timeout); 2422067fd92SSamuel Thibault spin_lock_irqsave(&flush_lock, flags); 2432067fd92SSamuel Thibault } 2442067fd92SSamuel Thibault finish_wait(&flush, &wait); 2452067fd92SSamuel Thibault is_flushing = 0; 2462067fd92SSamuel Thibault spin_unlock_irqrestore(&flush_lock, flags); 2472067fd92SSamuel Thibault 2482067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 2492067fd92SSamuel Thibault if (speakup_info.flushing) { 2502067fd92SSamuel Thibault speakup_info.flushing = 0; 2512067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2522067fd92SSamuel Thibault synth->flush(synth); 2532067fd92SSamuel Thibault continue; 2542067fd92SSamuel Thibault } 2552067fd92SSamuel Thibault synth_buffer_skip_nonlatin1(); 2562067fd92SSamuel Thibault if (synth_buffer_empty()) { 2572067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2582067fd92SSamuel Thibault break; 2592067fd92SSamuel Thibault } 2602067fd92SSamuel Thibault ch = synth_buffer_peek(); 2612067fd92SSamuel Thibault set_current_state(TASK_INTERRUPTIBLE); 2622067fd92SSamuel Thibault delay_time_val = delay_time->u.n.value; 2632067fd92SSamuel Thibault synth_full_val = synth_full(); 2642067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2652067fd92SSamuel Thibault if (ch == '\n') 2662067fd92SSamuel Thibault ch = 0x0D; 2672067fd92SSamuel Thibault if (synth_full_val || !synth->io_ops->synth_out(synth, ch)) { 2682067fd92SSamuel Thibault schedule_timeout(msecs_to_jiffies(delay_time_val)); 2692067fd92SSamuel Thibault continue; 2702067fd92SSamuel Thibault } 2712067fd92SSamuel Thibault set_current_state(TASK_RUNNING); 2722067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags); 2732067fd92SSamuel Thibault synth_buffer_getc(); 2742067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags); 2752067fd92SSamuel Thibault if (ch == '[') { 2762067fd92SSamuel Thibault in_escape = 1; 2772067fd92SSamuel Thibault } else if (ch == ']') { 2782067fd92SSamuel Thibault in_escape = 0; 2792067fd92SSamuel Thibault } else if (ch <= SPACE) { 2802067fd92SSamuel Thibault if (!in_escape && strchr(",.!?;:", last)) 2812067fd92SSamuel Thibault synth->io_ops->synth_out(synth, PROCSPEECH); 2822067fd92SSamuel Thibault if (time_after_eq(jiffies, jiff_max)) { 2832067fd92SSamuel Thibault if (!in_escape) 2842067fd92SSamuel Thibault synth->io_ops->synth_out(synth, 2852067fd92SSamuel Thibault PROCSPEECH); 2862067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, 2872067fd92SSamuel Thibault flags); 2882067fd92SSamuel Thibault jiffy_delta_val = jiffy_delta->u.n.value; 2892067fd92SSamuel Thibault delay_time_val = delay_time->u.n.value; 2902067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, 2912067fd92SSamuel Thibault flags); 2922067fd92SSamuel Thibault schedule_timeout(msecs_to_jiffies 2932067fd92SSamuel Thibault (delay_time_val)); 2942067fd92SSamuel Thibault jiff_max = jiffies + jiffy_delta_val; 2952067fd92SSamuel Thibault } 2962067fd92SSamuel Thibault } 2972067fd92SSamuel Thibault last = ch; 2982067fd92SSamuel Thibault } 2992067fd92SSamuel Thibault if (!in_escape) 3002067fd92SSamuel Thibault synth->io_ops->synth_out(synth, PROCSPEECH); 3012067fd92SSamuel Thibault } 3022067fd92SSamuel Thibault 3032067fd92SSamuel Thibault static void synth_flush(struct spk_synth *synth) 3042067fd92SSamuel Thibault { 3052067fd92SSamuel Thibault if (in_escape) 3062067fd92SSamuel Thibault /* if in command output ']' so we don't get an error */ 3072067fd92SSamuel Thibault synth->io_ops->synth_out(synth, ']'); 3082067fd92SSamuel Thibault in_escape = 0; 3092067fd92SSamuel Thibault is_flushing = 1; 3101941ab1dSSamuel Thibault synth->io_ops->flush_buffer(synth); 3112067fd92SSamuel Thibault synth->io_ops->synth_out(synth, SYNTH_CLEAR); 3122067fd92SSamuel Thibault } 3132067fd92SSamuel Thibault 3142067fd92SSamuel Thibault module_param_named(ser, synth_dectlk.ser, int, 0444); 3152067fd92SSamuel Thibault module_param_named(dev, synth_dectlk.dev_name, charp, 0444); 3162067fd92SSamuel Thibault module_param_named(start, synth_dectlk.startup, short, 0444); 317*44d3e977SOsama Muhammad module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444); 318*44d3e977SOsama Muhammad module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444); 319*44d3e977SOsama Muhammad module_param_named(inflection, vars[INFLECTION_ID].u.n.default_val, int, 0444); 320*44d3e977SOsama Muhammad module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444); 321*44d3e977SOsama Muhammad module_param_named(punct, vars[PUNCT_ID].u.n.default_val, int, 0444); 322*44d3e977SOsama Muhammad module_param_named(voice, vars[VOICE_ID].u.n.default_val, int, 0444); 323*44d3e977SOsama Muhammad module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444); 324*44d3e977SOsama Muhammad 325*44d3e977SOsama Muhammad 3262067fd92SSamuel Thibault 3272067fd92SSamuel Thibault MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based)."); 3282067fd92SSamuel Thibault MODULE_PARM_DESC(dev, "Set the device e.g. ttyUSB0, for the synthesizer."); 3292067fd92SSamuel Thibault MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 330*44d3e977SOsama Muhammad MODULE_PARM_DESC(rate, "Set the rate variable on load."); 331*44d3e977SOsama Muhammad MODULE_PARM_DESC(pitch, "Set the pitch variable on load."); 332*44d3e977SOsama Muhammad MODULE_PARM_DESC(inflection, "Set the inflection variable on load."); 333*44d3e977SOsama Muhammad MODULE_PARM_DESC(vol, "Set the vol variable on load."); 334*44d3e977SOsama Muhammad MODULE_PARM_DESC(punct, "Set the punct variable on load."); 335*44d3e977SOsama Muhammad MODULE_PARM_DESC(voice, "Set the voice variable on load."); 336*44d3e977SOsama Muhammad MODULE_PARM_DESC(direct, "Set the direct variable on load."); 337*44d3e977SOsama Muhammad 3382067fd92SSamuel Thibault 3392067fd92SSamuel Thibault module_spk_synth(synth_dectlk); 3402067fd92SSamuel Thibault 3412067fd92SSamuel Thibault MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 3422067fd92SSamuel Thibault MODULE_AUTHOR("David Borowski"); 3432067fd92SSamuel Thibault MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers"); 3442067fd92SSamuel Thibault MODULE_LICENSE("GPL"); 3452067fd92SSamuel Thibault MODULE_VERSION(DRV_VERSION); 3462067fd92SSamuel Thibault 347