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>
6 * Copyright (C) 2003 Kirk Reiser.
8 * this code is specifically written as a driver for the speakup screenreview
9 * package and is not a general device driver.
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() */
16 /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */
17 #include <linux/sched/signal.h>
22 #define DRV_VERSION "2.6"
23 #define PROCSPEECH 0x0d
24 #define CLEAR_SYNTH 0x18
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
);
32 static struct miscdevice synth_device
, synthu_device
;
34 static int misc_registered
;
37 enum default_vars_id
{
38 DIRECT_ID
= 0, CAPS_START_ID
, CAPS_STOP_ID
,
39 PAUSE_ID
, RATE_ID
, PITCH_ID
, INFLECTION_ID
,
40 VOL_ID
, TONE_ID
, PUNCT_ID
, VOICE_ID
,
41 FREQUENCY_ID
, V_LAST_VAR_ID
,
46 static struct var_t vars
[NB_ID
] = {
48 [DIRECT_ID
] = { DIRECT
, .u
.n
= {NULL
, 0, 0, 1, 0, 0, NULL
} },
49 [CAPS_START_ID
] = { CAPS_START
, .u
.s
= {"\x01+3p" } },
50 [CAPS_STOP_ID
] = { CAPS_STOP
, .u
.s
= {"\x01-3p" } },
51 [PAUSE_ID
] = { PAUSE
, .u
.n
= {"\x01P" } },
52 [RATE_ID
] = { RATE
, .u
.n
= {"\x01%ds", 2, 0, 9, 0, 0, NULL
} },
53 [PITCH_ID
] = { PITCH
, .u
.n
= {"\x01%dp", 5, 0, 9, 0, 0, NULL
} },
54 [INFLECTION_ID
] = { INFLECTION
, .u
.n
= {"\x01%dr", 5, 0, 9, 0, 0, NULL
} },
55 [VOL_ID
] = { VOL
, .u
.n
= {"\x01%dv", 5, 0, 9, 0, 0, NULL
} },
56 [TONE_ID
] = { TONE
, .u
.n
= {"\x01%dx", 1, 0, 2, 0, 0, NULL
} },
57 [PUNCT_ID
] = { PUNCT
, .u
.n
= {"\x01%db", 0, 0, 3, 0, 0, NULL
} },
58 [VOICE_ID
] = { VOICE
, .u
.n
= {"\x01%do", 0, 0, 7, 0, 0, NULL
} },
59 [FREQUENCY_ID
] = { FREQUENCY
, .u
.n
= {"\x01%df", 5, 0, 9, 0, 0, NULL
} },
63 /* These attributes will appear in /sys/accessibility/speakup/soft. */
65 static struct kobj_attribute caps_start_attribute
=
66 __ATTR(caps_start
, 0644, spk_var_show
, spk_var_store
);
67 static struct kobj_attribute caps_stop_attribute
=
68 __ATTR(caps_stop
, 0644, spk_var_show
, spk_var_store
);
69 static struct kobj_attribute freq_attribute
=
70 __ATTR(freq
, 0644, spk_var_show
, spk_var_store
);
71 static struct kobj_attribute pitch_attribute
=
72 __ATTR(pitch
, 0644, spk_var_show
, spk_var_store
);
73 static struct kobj_attribute inflection_attribute
=
74 __ATTR(inflection
, 0644, spk_var_show
, spk_var_store
);
75 static struct kobj_attribute punct_attribute
=
76 __ATTR(punct
, 0644, spk_var_show
, spk_var_store
);
77 static struct kobj_attribute rate_attribute
=
78 __ATTR(rate
, 0644, spk_var_show
, spk_var_store
);
79 static struct kobj_attribute tone_attribute
=
80 __ATTR(tone
, 0644, spk_var_show
, spk_var_store
);
81 static struct kobj_attribute voice_attribute
=
82 __ATTR(voice
, 0644, spk_var_show
, spk_var_store
);
83 static struct kobj_attribute vol_attribute
=
84 __ATTR(vol
, 0644, spk_var_show
, spk_var_store
);
87 * We should uncomment the following definition, when we agree on a
88 * method of passing a language designation to the software synthesizer.
89 * static struct kobj_attribute lang_attribute =
90 * __ATTR(lang, 0644, spk_var_show, spk_var_store);
93 static struct kobj_attribute delay_time_attribute
=
94 __ATTR(delay_time
, 0644, spk_var_show
, spk_var_store
);
95 static struct kobj_attribute direct_attribute
=
96 __ATTR(direct
, 0644, spk_var_show
, spk_var_store
);
97 static struct kobj_attribute full_time_attribute
=
98 __ATTR(full_time
, 0644, spk_var_show
, spk_var_store
);
99 static struct kobj_attribute jiffy_delta_attribute
=
100 __ATTR(jiffy_delta
, 0644, spk_var_show
, spk_var_store
);
101 static struct kobj_attribute trigger_time_attribute
=
102 __ATTR(trigger_time
, 0644, spk_var_show
, spk_var_store
);
105 * Create a group of attributes so that we can create and destroy them all
108 static struct attribute
*synth_attrs
[] = {
109 &caps_start_attribute
.attr
,
110 &caps_stop_attribute
.attr
,
111 &freq_attribute
.attr
,
112 /* &lang_attribute.attr, */
113 &pitch_attribute
.attr
,
114 &inflection_attribute
.attr
,
115 &punct_attribute
.attr
,
116 &rate_attribute
.attr
,
117 &tone_attribute
.attr
,
118 &voice_attribute
.attr
,
120 &delay_time_attribute
.attr
,
121 &direct_attribute
.attr
,
122 &full_time_attribute
.attr
,
123 &jiffy_delta_attribute
.attr
,
124 &trigger_time_attribute
.attr
,
125 NULL
, /* need to NULL terminate the list of attributes */
128 static struct spk_synth synth_soft
= {
130 .version
= DRV_VERSION
,
131 .long_name
= "software synth",
132 .init
= "\01@\x01\x31y\n",
133 .procspeech
= PROCSPEECH
,
138 .startup
= SYNTH_START
,
139 .checkval
= SYNTH_CHECK
,
142 .probe
= softsynth_probe
,
143 .release
= softsynth_release
,
144 .synth_immediate
= NULL
,
147 .is_alive
= softsynth_is_alive
,
148 .synth_adjust
= softsynth_adjust
,
149 .read_buff_add
= NULL
,
150 .get_index
= get_index
,
152 .command
= "\x01%di",
158 .attrs
= synth_attrs
,
163 static char *get_initstring(void)
171 memset(buf
, 0, sizeof(buf
));
175 var
= synth_soft
.vars
;
176 while (var
->var_id
!= MAXVARS
) {
177 if (var
->var_id
!= CAPS_START
&& var
->var_id
!= CAPS_STOP
&&
178 var
->var_id
!= PAUSE
&& var
->var_id
!= DIRECT
) {
179 n
= scnprintf(cp
, len
, var
->u
.n
.synth_fmt
,
186 cp
= cp
+ scnprintf(cp
, len
, "\n");
190 static int softsynth_open(struct inode
*inode
, struct file
*fp
)
193 /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */
195 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
196 if (synth_soft
.alive
) {
197 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
200 synth_soft
.alive
= 1;
201 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
205 static int softsynth_close(struct inode
*inode
, struct file
*fp
)
209 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
210 synth_soft
.alive
= 0;
212 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
213 /* Make sure we let applications go before leaving */
214 speakup_start_ttys();
218 static ssize_t
softsynthx_read(struct file
*fp
, char __user
*buf
, size_t count
,
219 loff_t
*pos
, int unicode
)
224 size_t bytes_per_ch
= unicode
? 3 : 1;
230 if (count
< bytes_per_ch
)
233 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
234 synth_soft
.alive
= 1;
236 prepare_to_wait(&speakup_event
, &wait
, TASK_INTERRUPTIBLE
);
237 if (synth_current() == &synth_soft
) {
239 synth_buffer_skip_nonlatin1();
240 if (!synth_buffer_empty() || speakup_info
.flushing
)
243 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
244 if (fp
->f_flags
& O_NONBLOCK
) {
245 finish_wait(&speakup_event
, &wait
);
248 if (signal_pending(current
)) {
249 finish_wait(&speakup_event
, &wait
);
253 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
255 finish_wait(&speakup_event
, &wait
);
258 init
= get_initstring();
260 /* Keep 3 bytes available for a 16bit UTF-8-encoded character */
261 while (chars_sent
<= count
- bytes_per_ch
) {
262 if (synth_current() != &synth_soft
)
264 if (speakup_info
.flushing
) {
265 speakup_info
.flushing
= 0;
267 } else if (init
[init_pos
]) {
268 ch
= init
[init_pos
++];
271 synth_buffer_skip_nonlatin1();
272 if (synth_buffer_empty())
274 ch
= synth_buffer_getc();
276 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
278 if ((!unicode
&& ch
< 0x100) || (unicode
&& ch
< 0x80)) {
281 if (copy_to_user(cp
, &c
, 1))
286 } else if (unicode
&& ch
< 0x800) {
292 if (copy_to_user(cp
, s
, sizeof(s
)))
295 chars_sent
+= sizeof(s
);
297 } else if (unicode
) {
300 0x80 | ((ch
>> 6) & 0x3f),
304 if (copy_to_user(cp
, s
, sizeof(s
)))
307 chars_sent
+= sizeof(s
);
311 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
314 empty
= synth_buffer_empty();
315 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
317 speakup_start_ttys();
323 static ssize_t
softsynth_read(struct file
*fp
, char __user
*buf
, size_t count
,
326 return softsynthx_read(fp
, buf
, count
, pos
, 0);
329 static ssize_t
softsynthu_read(struct file
*fp
, char __user
*buf
, size_t count
,
332 return softsynthx_read(fp
, buf
, count
, pos
, 1);
335 static int last_index
;
337 static ssize_t
softsynth_write(struct file
*fp
, const char __user
*buf
,
338 size_t count
, loff_t
*pos
)
340 unsigned long supplied_index
= 0;
343 converted
= kstrtoul_from_user(buf
, count
, 0, &supplied_index
);
348 last_index
= supplied_index
;
352 static __poll_t
softsynth_poll(struct file
*fp
, struct poll_table_struct
*wait
)
357 poll_wait(fp
, &speakup_event
, wait
);
359 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
360 if (synth_current() == &synth_soft
&&
361 (!synth_buffer_empty() || speakup_info
.flushing
))
362 ret
= EPOLLIN
| EPOLLRDNORM
;
363 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
367 static unsigned char get_index(struct spk_synth
*synth
)
376 static const struct file_operations softsynth_fops
= {
377 .owner
= THIS_MODULE
,
378 .poll
= softsynth_poll
,
379 .read
= softsynth_read
,
380 .write
= softsynth_write
,
381 .open
= softsynth_open
,
382 .release
= softsynth_close
,
385 static const struct file_operations softsynthu_fops
= {
386 .owner
= THIS_MODULE
,
387 .poll
= softsynth_poll
,
388 .read
= softsynthu_read
,
389 .write
= softsynth_write
,
390 .open
= softsynth_open
,
391 .release
= softsynth_close
,
394 static int softsynth_probe(struct spk_synth
*synth
)
396 if (misc_registered
!= 0)
398 memset(&synth_device
, 0, sizeof(synth_device
));
399 synth_device
.minor
= MISC_DYNAMIC_MINOR
;
400 synth_device
.name
= "softsynth";
401 synth_device
.fops
= &softsynth_fops
;
402 if (misc_register(&synth_device
)) {
403 pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n");
407 memset(&synthu_device
, 0, sizeof(synthu_device
));
408 synthu_device
.minor
= MISC_DYNAMIC_MINOR
;
409 synthu_device
.name
= "softsynthu";
410 synthu_device
.fops
= &softsynthu_fops
;
411 if (misc_register(&synthu_device
)) {
412 misc_deregister(&synth_device
);
413 pr_warn("Couldn't initialize miscdevice /dev/softsynthu.\n");
418 pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR %d)\n",
420 pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR %d)\n",
421 synthu_device
.minor
);
425 static void softsynth_release(struct spk_synth
*synth
)
427 misc_deregister(&synth_device
);
428 misc_deregister(&synthu_device
);
430 pr_info("unregistered /dev/softsynth\n");
431 pr_info("unregistered /dev/softsynthu\n");
434 static int softsynth_is_alive(struct spk_synth
*synth
)
436 if (synth_soft
.alive
)
441 static int softsynth_adjust(struct spk_synth
*synth
, struct st_var_header
*var
)
443 struct st_var_header
*punc_level_var
;
444 struct var_t
*var_data
;
446 if (var
->var_id
!= PUNC_LEVEL
)
449 /* We want to set the the speech synthesis punctuation level
450 * accordingly, so it properly tunes speaking A_PUNC characters */
451 var_data
= var
->data
;
454 punc_level_var
= spk_get_var_header(PUNCT
);
457 spk_set_num_var(var_data
->u
.n
.value
, punc_level_var
, E_SET
);
462 module_param_named(start
, synth_soft
.startup
, short, 0444);
463 module_param_named(direct
, vars
[DIRECT_ID
].u
.n
.default_val
, int, 0444);
464 module_param_named(rate
, vars
[RATE_ID
].u
.n
.default_val
, int, 0444);
465 module_param_named(pitch
, vars
[PITCH_ID
].u
.n
.default_val
, int, 0444);
466 module_param_named(inflection
, vars
[INFLECTION_ID
].u
.n
.default_val
, int, 0444);
467 module_param_named(vol
, vars
[VOL_ID
].u
.n
.default_val
, int, 0444);
468 module_param_named(tone
, vars
[TONE_ID
].u
.n
.default_val
, int, 0444);
469 module_param_named(punct
, vars
[PUNCT_ID
].u
.n
.default_val
, int, 0444);
470 module_param_named(voice
, vars
[VOICE_ID
].u
.n
.default_val
, int, 0444);
471 module_param_named(frequency
, vars
[FREQUENCY_ID
].u
.n
.default_val
, int, 0444);
475 MODULE_PARM_DESC(start
, "Start the synthesizer once it is loaded.");
476 MODULE_PARM_DESC(direct
, "Set the direct variable on load.");
477 MODULE_PARM_DESC(rate
, "Sets the rate of the synthesizer.");
478 MODULE_PARM_DESC(pitch
, "Sets the pitch of the synthesizer.");
479 MODULE_PARM_DESC(inflection
, "Sets the inflection of the synthesizer.");
480 MODULE_PARM_DESC(vol
, "Sets the volume of the speech synthesizer.");
481 MODULE_PARM_DESC(tone
, "Sets the tone of the speech synthesizer.");
482 MODULE_PARM_DESC(punct
, "Sets the amount of punctuation spoken by the synthesizer.");
483 MODULE_PARM_DESC(voice
, "Sets the voice used by the synthesizer.");
484 MODULE_PARM_DESC(frequency
, "Sets the frequency of speech synthesizer.");
486 module_spk_synth(synth_soft
);
488 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
489 MODULE_DESCRIPTION("Speakup userspace software synthesizer support");
490 MODULE_LICENSE("GPL");
491 MODULE_VERSION(DRV_VERSION
);