1/* speakup_soft.c - speakup driver to register and make available 2 * a user space device for software synthesizers. written by: Kirk 3 * Reiser <kirk@braille.uwo.ca> 4 * 5 * Copyright (C) 2003 Kirk Reiser. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 * this code is specificly written as a driver for the speakup screenreview 22 * package and is not a general device driver. */ 23 24#include <linux/unistd.h> 25#include <linux/miscdevice.h> /* for misc_register, and SYNTH_MINOR */ 26#include <linux/poll.h> /* for poll_wait() */ 27#include <linux/sched.h> /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ 28 29#include "spk_priv.h" 30#include "speakup.h" 31 32#define DRV_VERSION "2.6" 33#define SOFTSYNTH_MINOR 26 /* might as well give it one more than /dev/synth */ 34#define PROCSPEECH 0x0d 35#define CLEAR_SYNTH 0x18 36 37static int softsynth_probe(struct spk_synth *synth); 38static void softsynth_release(void); 39static int softsynth_is_alive(struct spk_synth *synth); 40static unsigned char get_index(void); 41 42static struct miscdevice synth_device; 43static int init_pos; 44static int misc_registered; 45 46static struct var_t vars[] = { 47 { CAPS_START, .u.s = {"\x01+3p" } }, 48 { CAPS_STOP, .u.s = {"\x01-3p" } }, 49 { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } }, 50 { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } }, 51 { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } }, 52 { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } }, 53 { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL } }, 54 { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } }, 55 { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } }, 56 { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } }, 57 V_LAST_VAR 58}; 59 60/* 61 * These attributes will appear in /sys/accessibility/speakup/soft. 62 */ 63static struct kobj_attribute caps_start_attribute = 64 __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 65static struct kobj_attribute caps_stop_attribute = 66 __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 67static struct kobj_attribute freq_attribute = 68 __ATTR(freq, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 69static struct kobj_attribute pitch_attribute = 70 __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 71static struct kobj_attribute punct_attribute = 72 __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 73static struct kobj_attribute rate_attribute = 74 __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 75static struct kobj_attribute tone_attribute = 76 __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 77static struct kobj_attribute voice_attribute = 78 __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 79static struct kobj_attribute vol_attribute = 80 __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 81 82/* 83 * We should uncomment the following definition, when we agree on a 84 * method of passing a language designation to the software synthesizer. 85 * static struct kobj_attribute lang_attribute = 86 * __ATTR(lang, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 87 */ 88 89static struct kobj_attribute delay_time_attribute = 90 __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 91static struct kobj_attribute direct_attribute = 92 __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 93static struct kobj_attribute full_time_attribute = 94 __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 95static struct kobj_attribute jiffy_delta_attribute = 96 __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 97static struct kobj_attribute trigger_time_attribute = 98 __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store); 99 100/* 101 * Create a group of attributes so that we can create and destroy them all 102 * at once. 103 */ 104static struct attribute *synth_attrs[] = { 105 &caps_start_attribute.attr, 106 &caps_stop_attribute.attr, 107 &freq_attribute.attr, 108/* &lang_attribute.attr, */ 109 &pitch_attribute.attr, 110 &punct_attribute.attr, 111 &rate_attribute.attr, 112 &tone_attribute.attr, 113 &voice_attribute.attr, 114 &vol_attribute.attr, 115 &delay_time_attribute.attr, 116 &direct_attribute.attr, 117 &full_time_attribute.attr, 118 &jiffy_delta_attribute.attr, 119 &trigger_time_attribute.attr, 120 NULL, /* need to NULL terminate the list of attributes */ 121}; 122 123static struct spk_synth synth_soft = { 124 .name = "soft", 125 .version = DRV_VERSION, 126 .long_name = "software synth", 127 .init = "\01@\x01\x31y\n", 128 .procspeech = PROCSPEECH, 129 .delay = 0, 130 .trigger = 0, 131 .jiffies = 0, 132 .full = 0, 133 .startup = SYNTH_START, 134 .checkval = SYNTH_CHECK, 135 .vars = vars, 136 .probe = softsynth_probe, 137 .release = softsynth_release, 138 .synth_immediate = NULL, 139 .catch_up = NULL, 140 .flush = NULL, 141 .is_alive = softsynth_is_alive, 142 .synth_adjust = NULL, 143 .read_buff_add = NULL, 144 .get_index = get_index, 145 .indexing = { 146 .command = "\x01%di", 147 .lowindex = 1, 148 .highindex = 5, 149 .currindex = 1, 150 }, 151 .attributes = { 152 .attrs = synth_attrs, 153 .name = "soft", 154 }, 155}; 156 157static char *get_initstring(void) 158{ 159 static char buf[40]; 160 char *cp; 161 struct var_t *var; 162 163 memset(buf, 0, sizeof(buf)); 164 cp = buf; 165 var = synth_soft.vars; 166 while (var->var_id != MAXVARS) { 167 if (var->var_id != CAPS_START && var->var_id != CAPS_STOP 168 && var->var_id != DIRECT) 169 cp = cp + sprintf(cp, var->u.n.synth_fmt, 170 var->u.n.value); 171 var++; 172 } 173 cp = cp + sprintf(cp, "\n"); 174 return buf; 175} 176 177static int softsynth_open(struct inode *inode, struct file *fp) 178{ 179 unsigned long flags; 180 /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */ 181 /* return -EPERM; */ 182 spin_lock_irqsave(&speakup_info.spinlock, flags); 183 if (synth_soft.alive) { 184 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 185 return -EBUSY; 186 } 187 synth_soft.alive = 1; 188 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 189 return 0; 190} 191 192static int softsynth_close(struct inode *inode, struct file *fp) 193{ 194 unsigned long flags; 195 196 spin_lock_irqsave(&speakup_info.spinlock, flags); 197 synth_soft.alive = 0; 198 init_pos = 0; 199 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 200 /* Make sure we let applications go before leaving */ 201 speakup_start_ttys(); 202 return 0; 203} 204 205static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count, 206 loff_t *pos) 207{ 208 int chars_sent = 0; 209 char __user *cp; 210 char *init; 211 char ch; 212 int empty; 213 unsigned long flags; 214 DEFINE_WAIT(wait); 215 216 spin_lock_irqsave(&speakup_info.spinlock, flags); 217 while (1) { 218 prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE); 219 if (!synth_buffer_empty() || speakup_info.flushing) 220 break; 221 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 222 if (fp->f_flags & O_NONBLOCK) { 223 finish_wait(&speakup_event, &wait); 224 return -EAGAIN; 225 } 226 if (signal_pending(current)) { 227 finish_wait(&speakup_event, &wait); 228 return -ERESTARTSYS; 229 } 230 schedule(); 231 spin_lock_irqsave(&speakup_info.spinlock, flags); 232 } 233 finish_wait(&speakup_event, &wait); 234 235 cp = buf; 236 init = get_initstring(); 237 while (chars_sent < count) { 238 if (speakup_info.flushing) { 239 speakup_info.flushing = 0; 240 ch = '\x18'; 241 } else if (synth_buffer_empty()) { 242 break; 243 } else if (init[init_pos]) { 244 ch = init[init_pos++]; 245 } else { 246 ch = synth_buffer_getc(); 247 } 248 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 249 if (copy_to_user(cp, &ch, 1)) 250 return -EFAULT; 251 spin_lock_irqsave(&speakup_info.spinlock, flags); 252 chars_sent++; 253 cp++; 254 } 255 *pos += chars_sent; 256 empty = synth_buffer_empty(); 257 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 258 if (empty) { 259 speakup_start_ttys(); 260 *pos = 0; 261 } 262 return chars_sent; 263} 264 265static int last_index; 266 267static ssize_t softsynth_write(struct file *fp, const char __user *buf, 268 size_t count, loff_t *pos) 269{ 270 unsigned long supplied_index = 0; 271 int converted; 272 273 converted = kstrtoul_from_user(buf, count, 0, &supplied_index); 274 275 if (converted < 0) 276 return converted; 277 278 last_index = supplied_index; 279 return count; 280} 281 282static unsigned int softsynth_poll(struct file *fp, 283 struct poll_table_struct *wait) 284{ 285 unsigned long flags; 286 int ret = 0; 287 288 poll_wait(fp, &speakup_event, wait); 289 290 spin_lock_irqsave(&speakup_info.spinlock, flags); 291 if (!synth_buffer_empty() || speakup_info.flushing) 292 ret = POLLIN | POLLRDNORM; 293 spin_unlock_irqrestore(&speakup_info.spinlock, flags); 294 return ret; 295} 296 297static unsigned char get_index(void) 298{ 299 int rv; 300 301 rv = last_index; 302 last_index = 0; 303 return rv; 304} 305 306static const struct file_operations softsynth_fops = { 307 .owner = THIS_MODULE, 308 .poll = softsynth_poll, 309 .read = softsynth_read, 310 .write = softsynth_write, 311 .open = softsynth_open, 312 .release = softsynth_close, 313}; 314 315 316static int softsynth_probe(struct spk_synth *synth) 317{ 318 319 if (misc_registered != 0) 320 return 0; 321 memset(&synth_device, 0, sizeof(synth_device)); 322 synth_device.minor = SOFTSYNTH_MINOR; 323 synth_device.name = "softsynth"; 324 synth_device.fops = &softsynth_fops; 325 if (misc_register(&synth_device)) { 326 pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n"); 327 return -ENODEV; 328 } 329 330 misc_registered = 1; 331 pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR 26)\n"); 332 return 0; 333} 334 335static void softsynth_release(void) 336{ 337 misc_deregister(&synth_device); 338 misc_registered = 0; 339 pr_info("unregistered /dev/softsynth\n"); 340} 341 342static int softsynth_is_alive(struct spk_synth *synth) 343{ 344 if (synth_soft.alive) 345 return 1; 346 return 0; 347} 348 349module_param_named(start, synth_soft.startup, short, S_IRUGO); 350 351MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded."); 352 353 354static int __init soft_init(void) 355{ 356 return synth_add(&synth_soft); 357} 358 359static void __exit soft_exit(void) 360{ 361 synth_remove(&synth_soft); 362} 363 364module_init(soft_init); 365module_exit(soft_exit); 366MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 367MODULE_DESCRIPTION("Speakup userspace software synthesizer support"); 368MODULE_LICENSE("GPL"); 369MODULE_VERSION(DRV_VERSION); 370 371