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