1 // SPDX-License-Identifier: GPL-2.0+ 2 /* speakup_soft.c - speakup driver to register and make available 3 * a user space device for software synthesizers. written by: Kirk 4 * Reiser <kirk@braille.uwo.ca> 5 * 6 * Copyright (C) 2003 Kirk Reiser. 7 * 8 * this code is specifically written as a driver for the speakup screenreview 9 * package and is not a general device driver. 10 */ 11 12 #include <linux/unistd.h> 13 #include <linux/miscdevice.h> /* for misc_register, and MISC_DYNAMIC_MINOR */ 14 #include <linux/poll.h> /* for poll_wait() */ 15 16 /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ 17 #include <linux/sched/signal.h> 18 19 #include "spk_priv.h" 20 #include "speakup.h" 21 22 #define DRV_VERSION "2.6" 23 #define PROCSPEECH 0x0d 24 #define CLEAR_SYNTH 0x18 25 26 static int softsynth_probe(struct spk_synth *synth); 27 static void softsynth_release(struct spk_synth *synth); 28 static int softsynth_is_alive(struct spk_synth *synth); 29 static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var); 30 static unsigned char get_index(struct spk_synth *synth); 31 32 static struct miscdevice synth_device, synthu_device; 33 static int init_pos; 34 static int misc_registered; 35 36 static struct var_t vars[] = { 37 /* DIRECT is put first so that module_param_named can access it easily */ 38 { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 39 40 { CAPS_START, .u.s = {"\x01+3p" } }, 41 { CAPS_STOP, .u.s = {"\x01-3p" } }, 42 { PAUSE, .u.n = {"\x01P" } }, 43 { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } }, 44 { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } }, 45 { INFLECTION, .u.n = {"\x01%dr", 5, 0, 9, 0, 0, NULL } }, 46 { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, 47 { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, 48 { PUNCT, .u.n = {"\x01%db", 0, 0, 3, 0, 0, NULL } }, 49 { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, 50 { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, 51 V_LAST_VAR 52 }; 53 54 /* These attributes will appear in /sys/accessibility/speakup/soft. */ 55 56 static struct kobj_attribute caps_start_attribute = 57 __ATTR(caps_start, 0644, spk_var_show, spk_var_store); 58 static struct kobj_attribute caps_stop_attribute = 59 __ATTR(caps_stop, 0644, spk_var_show, spk_var_store); 60 static struct kobj_attribute freq_attribute = 61 __ATTR(freq, 0644, spk_var_show, spk_var_store); 62 static struct kobj_attribute pitch_attribute = 63 __ATTR(pitch, 0644, spk_var_show, spk_var_store); 64 static struct kobj_attribute inflection_attribute = 65 __ATTR(inflection, 0644, spk_var_show, spk_var_store); 66 static struct kobj_attribute punct_attribute = 67 __ATTR(punct, 0644, spk_var_show, spk_var_store); 68 static struct kobj_attribute rate_attribute = 69 __ATTR(rate, 0644, spk_var_show, spk_var_store); 70 static struct kobj_attribute tone_attribute = 71 __ATTR(tone, 0644, spk_var_show, spk_var_store); 72 static struct kobj_attribute voice_attribute = 73 __ATTR(voice, 0644, spk_var_show, spk_var_store); 74 static struct kobj_attribute vol_attribute = 75 __ATTR(vol, 0644, spk_var_show, spk_var_store); 76 77 /* 78 * We should uncomment the following definition, when we agree on a 79 * method of passing a language designation to the software synthesizer. 80 * static struct kobj_attribute lang_attribute = 81 * __ATTR(lang, 0644, spk_var_show, spk_var_store); 82 */ 83 84 static struct kobj_attribute delay_time_attribute = 85 __ATTR(delay_time, 0644, spk_var_show, spk_var_store); 86 static struct kobj_attribute direct_attribute = 87 __ATTR(direct, 0644, spk_var_show, spk_var_store); 88 static struct kobj_attribute full_time_attribute = 89 __ATTR(full_time, 0644, spk_var_show, spk_var_store); 90 static struct kobj_attribute jiffy_delta_attribute = 91 __ATTR(jiffy_delta, 0644, spk_var_show, spk_var_store); 92 static struct kobj_attribute trigger_time_attribute = 93 __ATTR(trigger_time, 0644, spk_var_show, spk_var_store); 94 95 /* 96 * Create a group of attributes so that we can create and destroy them all 97 * at once. 98 */ 99 static struct attribute *synth_attrs[] = { 100 &caps_start_attribute.attr, 101 &caps_stop_attribute.attr, 102 &freq_attribute.attr, 103 /* &lang_attribute.attr, */ 104 &pitch_attribute.attr, 105 &inflection_attribute.attr, 106 &punct_attribute.attr, 107 &rate_attribute.attr, 108 &tone_attribute.attr, 109 &voice_attribute.attr, 110 &vol_attribute.attr, 111 &delay_time_attribute.attr, 112 &direct_attribute.attr, 113 &full_time_attribute.attr, 114 &jiffy_delta_attribute.attr, 115 &trigger_time_attribute.attr, 116 NULL, /* need to NULL terminate the list of attributes */ 117 }; 118 119 static struct spk_synth synth_soft = { 120 .name = "soft", 121 .version = DRV_VERSION, 122 .long_name = "software synth", 123 .init = "\01@\x01\x31y\n", 124 .procspeech = PROCSPEECH, 125 .delay = 0, 126 .trigger = 0, 127 .jiffies = 0, 128 .full = 0, 129 .startup = SYNTH_START, 130 .checkval = SYNTH_CHECK, 131 .vars = vars, 132 .io_ops = NULL, 133 .probe = softsynth_probe, 134 .release = softsynth_release, 135 .synth_immediate = NULL, 136 .catch_up = NULL, 137 .flush = NULL, 138 .is_alive = softsynth_is_alive, 139 .synth_adjust = softsynth_adjust, 140 .read_buff_add = NULL, 141 .get_index = get_index, 142 .indexing = { 143 .command = "\x01%di", 144 .lowindex = 1, 145 .highindex = 5, 146 .currindex = 1, 147 }, 148 .attributes = { 149 .attrs = synth_attrs, 150 .name = "soft", 151 }, 152 }; 153 154 static char *get_initstring(void) 155 { 156 static char buf[40]; 157 char *cp; 158 struct var_t *var; 159 size_t len; 160 size_t n; 161 162 memset(buf, 0, sizeof(buf)); 163 cp = buf; 164 len = sizeof(buf); 165 166 var = synth_soft.vars; 167 while (var->var_id != MAXVARS) { 168 if (var->var_id != CAPS_START && var->var_id != CAPS_STOP && 169 var->var_id != PAUSE && var->var_id != DIRECT) { 170 n = scnprintf(cp, len, var->u.n.synth_fmt, 171 var->u.n.value); 172 cp = cp + n; 173 len = len - n; 174 } 175 var++; 176 } 177 cp = cp + scnprintf(cp, len, "\n"); 178 return buf; 179 } 180 181 static int softsynth_open(struct inode *inode, struct file *fp) 182 { 183 unsigned long flags; 184 /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */ 185 /* return -EPERM; */ 186 spin_lock_irqsave(&speakup_info.spinlock, flags); 187 if (synth_soft.alive) { 188 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 189 return -EBUSY; 190 } 191 synth_soft.alive = 1; 192 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 193 return 0; 194 } 195 196 static int softsynth_close(struct inode *inode, struct file *fp) 197 { 198 unsigned long flags; 199 200 spin_lock_irqsave(&speakup_info.spinlock, flags); 201 synth_soft.alive = 0; 202 init_pos = 0; 203 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 204 /* Make sure we let applications go before leaving */ 205 speakup_start_ttys(); 206 return 0; 207 } 208 209 static ssize_t softsynthx_read(struct file *fp, char __user *buf, size_t count, 210 loff_t *pos, int unicode) 211 { 212 int chars_sent = 0; 213 char __user *cp; 214 char *init; 215 size_t bytes_per_ch = unicode ? 3 : 1; 216 u16 ch; 217 int empty; 218 unsigned long flags; 219 DEFINE_WAIT(wait); 220 221 if (count < bytes_per_ch) 222 return -EINVAL; 223 224 spin_lock_irqsave(&speakup_info.spinlock, flags); 225 synth_soft.alive = 1; 226 while (1) { 227 prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); 228 if (synth_current() == &synth_soft) { 229 if (!unicode) 230 synth_buffer_skip_nonlatin1(); 231 if (!synth_buffer_empty() || speakup_info.flushing) 232 break; 233 } 234 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 235 if (fp->f_flags & O_NONBLOCK) { 236 finish_wait(&speakup_event, &wait); 237 return -EAGAIN; 238 } 239 if (signal_pending(current)) { 240 finish_wait(&speakup_event, &wait); 241 return -ERESTARTSYS; 242 } 243 schedule(); 244 spin_lock_irqsave(&speakup_info.spinlock, flags); 245 } 246 finish_wait(&speakup_event, &wait); 247 248 cp = buf; 249 init = get_initstring(); 250 251 /* Keep 3 bytes available for a 16bit UTF-8-encoded character */ 252 while (chars_sent <= count - bytes_per_ch) { 253 if (synth_current() != &synth_soft) 254 break; 255 if (speakup_info.flushing) { 256 speakup_info.flushing = 0; 257 ch = '\x18'; 258 } else if (init[init_pos]) { 259 ch = init[init_pos++]; 260 } else { 261 if (!unicode) 262 synth_buffer_skip_nonlatin1(); 263 if (synth_buffer_empty()) 264 break; 265 ch = synth_buffer_getc(); 266 } 267 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 268 269 if ((!unicode && ch < 0x100) || (unicode && ch < 0x80)) { 270 u_char c = ch; 271 272 if (copy_to_user(cp, &c, 1)) 273 return -EFAULT; 274 275 chars_sent++; 276 cp++; 277 } else if (unicode && ch < 0x800) { 278 u_char s[2] = { 279 0xc0 | (ch >> 6), 280 0x80 | (ch & 0x3f) 281 }; 282 283 if (copy_to_user(cp, s, sizeof(s))) 284 return -EFAULT; 285 286 chars_sent += sizeof(s); 287 cp += sizeof(s); 288 } else if (unicode) { 289 u_char s[3] = { 290 0xe0 | (ch >> 12), 291 0x80 | ((ch >> 6) & 0x3f), 292 0x80 | (ch & 0x3f) 293 }; 294 295 if (copy_to_user(cp, s, sizeof(s))) 296 return -EFAULT; 297 298 chars_sent += sizeof(s); 299 cp += sizeof(s); 300 } 301 302 spin_lock_irqsave(&speakup_info.spinlock, flags); 303 } 304 *pos += chars_sent; 305 empty = synth_buffer_empty(); 306 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 307 if (empty) { 308 speakup_start_ttys(); 309 *pos = 0; 310 } 311 return chars_sent; 312 } 313 314 static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, 315 loff_t *pos) 316 { 317 return softsynthx_read(fp, buf, count, pos, 0); 318 } 319 320 static ssize_t softsynthu_read(struct file *fp, char __user *buf, size_t count, 321 loff_t *pos) 322 { 323 return softsynthx_read(fp, buf, count, pos, 1); 324 } 325 326 static int last_index; 327 328 static ssize_t softsynth_write(struct file *fp, const char __user *buf, 329 size_t count, loff_t *pos) 330 { 331 unsigned long supplied_index = 0; 332 int converted; 333 334 converted = kstrtoul_from_user(buf, count, 0, &supplied_index); 335 336 if (converted < 0) 337 return converted; 338 339 last_index = supplied_index; 340 return count; 341 } 342 343 static __poll_t softsynth_poll(struct file *fp, struct poll_table_struct *wait) 344 { 345 unsigned long flags; 346 __poll_t ret = 0; 347 348 poll_wait(fp, &speakup_event, wait); 349 350 spin_lock_irqsave(&speakup_info.spinlock, flags); 351 if (synth_current() == &synth_soft && 352 (!synth_buffer_empty() || speakup_info.flushing)) 353 ret = EPOLLIN | EPOLLRDNORM; 354 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 355 return ret; 356 } 357 358 static unsigned char get_index(struct spk_synth *synth) 359 { 360 int rv; 361 362 rv = last_index; 363 last_index = 0; 364 return rv; 365 } 366 367 static const struct file_operations softsynth_fops = { 368 .owner = THIS_MODULE, 369 .poll = softsynth_poll, 370 .read = softsynth_read, 371 .write = softsynth_write, 372 .open = softsynth_open, 373 .release = softsynth_close, 374 }; 375 376 static const struct file_operations softsynthu_fops = { 377 .owner = THIS_MODULE, 378 .poll = softsynth_poll, 379 .read = softsynthu_read, 380 .write = softsynth_write, 381 .open = softsynth_open, 382 .release = softsynth_close, 383 }; 384 385 static int softsynth_probe(struct spk_synth *synth) 386 { 387 if (misc_registered != 0) 388 return 0; 389 memset(&synth_device, 0, sizeof(synth_device)); 390 synth_device.minor = MISC_DYNAMIC_MINOR; 391 synth_device.name = "softsynth"; 392 synth_device.fops = &softsynth_fops; 393 if (misc_register(&synth_device)) { 394 pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n"); 395 return -ENODEV; 396 } 397 398 memset(&synthu_device, 0, sizeof(synthu_device)); 399 synthu_device.minor = MISC_DYNAMIC_MINOR; 400 synthu_device.name = "softsynthu"; 401 synthu_device.fops = &softsynthu_fops; 402 if (misc_register(&synthu_device)) { 403 misc_deregister(&synth_device); 404 pr_warn("Couldn't initialize miscdevice /dev/softsynthu.\n"); 405 return -ENODEV; 406 } 407 408 misc_registered = 1; 409 pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR %d)\n", 410 synth_device.minor); 411 pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR %d)\n", 412 synthu_device.minor); 413 return 0; 414 } 415 416 static void softsynth_release(struct spk_synth *synth) 417 { 418 misc_deregister(&synth_device); 419 misc_deregister(&synthu_device); 420 misc_registered = 0; 421 pr_info("unregistered /dev/softsynth\n"); 422 pr_info("unregistered /dev/softsynthu\n"); 423 } 424 425 static int softsynth_is_alive(struct spk_synth *synth) 426 { 427 if (synth_soft.alive) 428 return 1; 429 return 0; 430 } 431 432 static int softsynth_adjust(struct spk_synth *synth, struct st_var_header *var) 433 { 434 struct st_var_header *punc_level_var; 435 struct var_t *var_data; 436 437 if (var->var_id != PUNC_LEVEL) 438 return 0; 439 440 /* We want to set the the speech synthesis punctuation level 441 * accordingly, so it properly tunes speaking A_PUNC characters */ 442 var_data = var->data; 443 if (!var_data) 444 return 0; 445 punc_level_var = spk_get_var_header(PUNCT); 446 if (!punc_level_var) 447 return 0; 448 spk_set_num_var(var_data->u.n.value, punc_level_var, E_SET); 449 450 return 1; 451 } 452 453 module_param_named(start, synth_soft.startup, short, 0444); 454 module_param_named(direct, vars[0].u.n.default_val, int, 0444); 455 456 MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 457 MODULE_PARM_DESC(direct, "Set the direct variable on load."); 458 459 module_spk_synth(synth_soft); 460 461 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 462 MODULE_DESCRIPTION("Speakup userspace software synthesizer support"); 463 MODULE_LICENSE("GPL"); 464 MODULE_VERSION(DRV_VERSION); 465