1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * written by: Kirk Reiser <kirk@braille.uwo.ca> 4 * this version considerably modified by David Borowski, david575@rogers.com 5 * 6 * Copyright (C) 1998-99 Kirk Reiser. 7 * Copyright (C) 2003 David Borowski. 8 * 9 * this code is specifically written as a driver for the speakup screenreview 10 * package and is not a general device driver. 11 * This driver is for the Aicom Acent PC internal synthesizer. 12 */ 13 14 #include <linux/jiffies.h> 15 #include <linux/sched.h> 16 #include <linux/timer.h> 17 #include <linux/kthread.h> 18 19 #include "spk_priv.h" 20 #include "serialio.h" 21 #include "speakup.h" 22 #include "speakup_acnt.h" /* local header file for Accent values */ 23 24 #define DRV_VERSION "2.10" 25 #define PROCSPEECH '\r' 26 27 static int synth_probe(struct spk_synth *synth); 28 static void accent_release(struct spk_synth *synth); 29 static const char *synth_immediate(struct spk_synth *synth, const char *buf); 30 static void do_catch_up(struct spk_synth *synth); 31 static void synth_flush(struct spk_synth *synth); 32 33 static int synth_port_control; 34 static int port_forced; 35 static unsigned int synth_portlist[] = { 0x2a8, 0 }; 36 37 enum default_vars_id { 38 CAPS_START_ID = 0, CAPS_STOP_ID, 39 RATE_ID, PITCH_ID, 40 VOL_ID, TONE_ID, 41 DIRECT_ID, V_LAST_VAR_ID, 42 NB_ID 43 }; 44 45 46 static struct var_t vars[NB_ID] = { 47 [CAPS_START_ID] = { CAPS_START, .u.s = {"\033P8" } }, 48 [CAPS_STOP_ID] = { CAPS_STOP, .u.s = {"\033P5" } }, 49 [RATE_ID] = { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } }, 50 [PITCH_ID] = { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } }, 51 [VOL_ID] = { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } }, 52 [TONE_ID] = { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } }, 53 [DIRECT_ID] = { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 54 V_LAST_VAR 55 }; 56 57 /* 58 * These attributes will appear in /sys/accessibility/speakup/acntpc. 59 */ 60 static struct kobj_attribute caps_start_attribute = 61 __ATTR(caps_start, 0644, spk_var_show, spk_var_store); 62 static struct kobj_attribute caps_stop_attribute = 63 __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); 64 static struct kobj_attribute pitch_attribute = 65 __ATTR(pitch, 0644, spk_var_show, spk_var_store); 66 static struct kobj_attribute rate_attribute = 67 __ATTR(rate, 0644, spk_var_show, spk_var_store); 68 static struct kobj_attribute tone_attribute = 69 __ATTR(tone, 0644, spk_var_show, spk_var_store); 70 static struct kobj_attribute vol_attribute = 71 __ATTR(vol, 0644, spk_var_show, spk_var_store); 72 73 static struct kobj_attribute delay_time_attribute = 74 __ATTR(delay_time, 0644, spk_var_show, spk_var_store); 75 static struct kobj_attribute direct_attribute = 76 __ATTR(direct, 0644, spk_var_show, spk_var_store); 77 static struct kobj_attribute full_time_attribute = 78 __ATTR(full_time, 0644, spk_var_show, spk_var_store); 79 static struct kobj_attribute jiffy_delta_attribute = 80 __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); 81 static struct kobj_attribute trigger_time_attribute = 82 __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); 83 84 /* 85 * Create a group of attributes so that we can create and destroy them all 86 * at once. 87 */ 88 static struct attribute *synth_attrs[] = { 89 &caps_start_attribute.attr, 90 &caps_stop_attribute.attr, 91 &pitch_attribute.attr, 92 &rate_attribute.attr, 93 &tone_attribute.attr, 94 &vol_attribute.attr, 95 &delay_time_attribute.attr, 96 &direct_attribute.attr, 97 &full_time_attribute.attr, 98 &jiffy_delta_attribute.attr, 99 &trigger_time_attribute.attr, 100 NULL, /* need to NULL terminate the list of attributes */ 101 }; 102 103 static struct spk_synth synth_acntpc = { 104 .name = "acntpc", 105 .version = DRV_VERSION, 106 .long_name = "Accent PC", 107 .init = "\033=X \033Oi\033T2\033=M\033N1\n", 108 .procspeech = PROCSPEECH, 109 .clear = SYNTH_CLEAR, 110 .delay = 500, 111 .trigger = 50, 112 .jiffies = 50, 113 .full = 1000, 114 .startup = SYNTH_START, 115 .checkval = SYNTH_CHECK, 116 .vars = vars, 117 .io_ops = &spk_serial_io_ops, 118 .probe = synth_probe, 119 .release = accent_release, 120 .synth_immediate = synth_immediate, 121 .catch_up = do_catch_up, 122 .flush = synth_flush, 123 .is_alive = spk_synth_is_alive_nop, 124 .synth_adjust = NULL, 125 .read_buff_add = NULL, 126 .get_index = NULL, 127 .indexing = { 128 .command = NULL, 129 .lowindex = 0, 130 .highindex = 0, 131 .currindex = 0, 132 }, 133 .attributes = { 134 .attrs = synth_attrs, 135 .name = "acntpc", 136 }, 137 }; 138 139 static inline bool synth_writable(void) 140 { 141 return inb_p(synth_port_control) & SYNTH_WRITABLE; 142 } 143 144 static inline bool synth_full(void) 145 { 146 return inb_p(speakup_info.port_tts + UART_RX) == 'F'; 147 } 148 149 static const char *synth_immediate(struct spk_synth *synth, const char *buf) 150 { 151 u_char ch; 152 153 while ((ch = *buf)) { 154 int timeout = SPK_XMITR_TIMEOUT; 155 156 if (ch == '\n') 157 ch = PROCSPEECH; 158 if (synth_full()) 159 return buf; 160 while (synth_writable()) { 161 if (!--timeout) 162 return buf; 163 udelay(1); 164 } 165 outb_p(ch, speakup_info.port_tts); 166 buf++; 167 } 168 return NULL; 169 } 170 171 static void do_catch_up(struct spk_synth *synth) 172 { 173 u_char ch; 174 unsigned long flags; 175 unsigned long jiff_max; 176 int timeout; 177 int delay_time_val; 178 int jiffy_delta_val; 179 int full_time_val; 180 struct var_t *delay_time; 181 struct var_t *full_time; 182 struct var_t *jiffy_delta; 183 184 jiffy_delta = spk_get_var(JIFFY); 185 delay_time = spk_get_var(DELAY); 186 full_time = spk_get_var(FULL); 187 188 spin_lock_irqsave(&speakup_info.spinlock, flags); 189 jiffy_delta_val = jiffy_delta->u.n.value; 190 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 191 192 jiff_max = jiffies + jiffy_delta_val; 193 while (!kthread_should_stop()) { 194 spin_lock_irqsave(&speakup_info.spinlock, flags); 195 if (speakup_info.flushing) { 196 speakup_info.flushing = 0; 197 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 198 synth->flush(synth); 199 continue; 200 } 201 synth_buffer_skip_nonlatin1(); 202 if (synth_buffer_empty()) { 203 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 204 break; 205 } 206 set_current_state(TASK_INTERRUPTIBLE); 207 full_time_val = full_time->u.n.value; 208 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 209 if (synth_full()) { 210 schedule_timeout(msecs_to_jiffies(full_time_val)); 211 continue; 212 } 213 set_current_state(TASK_RUNNING); 214 timeout = SPK_XMITR_TIMEOUT; 215 while (synth_writable()) { 216 if (!--timeout) 217 break; 218 udelay(1); 219 } 220 spin_lock_irqsave(&speakup_info.spinlock, flags); 221 ch = synth_buffer_getc(); 222 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 223 if (ch == '\n') 224 ch = PROCSPEECH; 225 outb_p(ch, speakup_info.port_tts); 226 if (time_after_eq(jiffies, jiff_max) && ch == SPACE) { 227 timeout = SPK_XMITR_TIMEOUT; 228 while (synth_writable()) { 229 if (!--timeout) 230 break; 231 udelay(1); 232 } 233 outb_p(PROCSPEECH, speakup_info.port_tts); 234 spin_lock_irqsave(&speakup_info.spinlock, flags); 235 jiffy_delta_val = jiffy_delta->u.n.value; 236 delay_time_val = delay_time->u.n.value; 237 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 238 schedule_timeout(msecs_to_jiffies(delay_time_val)); 239 jiff_max = jiffies + jiffy_delta_val; 240 } 241 } 242 timeout = SPK_XMITR_TIMEOUT; 243 while (synth_writable()) { 244 if (!--timeout) 245 break; 246 udelay(1); 247 } 248 outb_p(PROCSPEECH, speakup_info.port_tts); 249 } 250 251 static void synth_flush(struct spk_synth *synth) 252 { 253 outb_p(SYNTH_CLEAR, speakup_info.port_tts); 254 } 255 256 static int synth_probe(struct spk_synth *synth) 257 { 258 unsigned int port_val = 0; 259 int i; 260 261 pr_info("Probing for %s.\n", synth->long_name); 262 if (port_forced) { 263 speakup_info.port_tts = port_forced; 264 pr_info("probe forced to %x by kernel command line\n", 265 speakup_info.port_tts); 266 if (synth_request_region(speakup_info.port_tts - 1, 267 SYNTH_IO_EXTENT)) { 268 pr_warn("sorry, port already reserved\n"); 269 return -EBUSY; 270 } 271 port_val = inw(speakup_info.port_tts - 1); 272 synth_port_control = speakup_info.port_tts - 1; 273 } else { 274 for (i = 0; synth_portlist[i]; i++) { 275 if (synth_request_region(synth_portlist[i], 276 SYNTH_IO_EXTENT)) { 277 pr_warn 278 ("request_region: failed with 0x%x, %d\n", 279 synth_portlist[i], SYNTH_IO_EXTENT); 280 continue; 281 } 282 port_val = inw(synth_portlist[i]) & 0xfffc; 283 if (port_val == 0x53fc) { 284 /* 'S' and out&input bits */ 285 synth_port_control = synth_portlist[i]; 286 speakup_info.port_tts = synth_port_control + 1; 287 break; 288 } 289 } 290 } 291 port_val &= 0xfffc; 292 if (port_val != 0x53fc) { 293 /* 'S' and out&input bits */ 294 pr_info("%s: not found\n", synth->long_name); 295 synth_release_region(synth_port_control, SYNTH_IO_EXTENT); 296 synth_port_control = 0; 297 return -ENODEV; 298 } 299 pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name, 300 synth_port_control, synth_port_control + SYNTH_IO_EXTENT - 1, 301 synth->version); 302 synth->alive = 1; 303 return 0; 304 } 305 306 static void accent_release(struct spk_synth *synth) 307 { 308 spk_stop_serial_interrupt(); 309 if (speakup_info.port_tts) 310 synth_release_region(speakup_info.port_tts - 1, 311 SYNTH_IO_EXTENT); 312 speakup_info.port_tts = 0; 313 } 314 315 module_param_hw_named(port, port_forced, int, ioport, 0444); 316 module_param_named(start, synth_acntpc.startup, short, 0444); 317 module_param_named(rate, vars[RATE_ID].u.n.default_val, int, 0444); 318 module_param_named(pitch, vars[PITCH_ID].u.n.default_val, int, 0444); 319 module_param_named(vol, vars[VOL_ID].u.n.default_val, int, 0444); 320 module_param_named(tone, vars[TONE_ID].u.n.default_val, int, 0444); 321 module_param_named(direct, vars[DIRECT_ID].u.n.default_val, int, 0444); 322 323 324 325 MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing)."); 326 MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 327 MODULE_PARM_DESC(rate, "Set the rate variable on load."); 328 MODULE_PARM_DESC(pitch, "Set the pitch variable on load."); 329 MODULE_PARM_DESC(vol, "Set the vol variable on load."); 330 MODULE_PARM_DESC(tone, "Set the tone variable on load."); 331 MODULE_PARM_DESC(direct, "Set the direct variable on load."); 332 333 334 module_spk_synth(synth_acntpc); 335 336 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 337 MODULE_AUTHOR("David Borowski"); 338 MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer"); 339 MODULE_LICENSE("GPL"); 340 MODULE_VERSION(DRV_VERSION); 341 342