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