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 specificly 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(void);
28 static int softsynth_is_alive(struct spk_synth
*synth
);
29 static unsigned char get_index(struct spk_synth
*synth
);
31 static struct miscdevice synth_device
, synthu_device
;
33 static int misc_registered
;
35 static struct var_t vars
[] = {
36 { CAPS_START
, .u
.s
= {"\x01+3p" } },
37 { CAPS_STOP
, .u
.s
= {"\x01-3p" } },
38 { PAUSE
, .u
.n
= {"\x01P" } },
39 { RATE
, .u
.n
= {"\x01%ds", 2, 0, 9, 0, 0, NULL
} },
40 { PITCH
, .u
.n
= {"\x01%dp", 5, 0, 9, 0, 0, NULL
} },
41 { INFLECTION
, .u
.n
= {"\x01%dr", 5, 0, 9, 0, 0, NULL
} },
42 { VOL
, .u
.n
= {"\x01%dv", 5, 0, 9, 0, 0, NULL
} },
43 { TONE
, .u
.n
= {"\x01%dx", 1, 0, 2, 0, 0, NULL
} },
44 { PUNCT
, .u
.n
= {"\x01%db", 0, 0, 2, 0, 0, NULL
} },
45 { VOICE
, .u
.n
= {"\x01%do", 0, 0, 7, 0, 0, NULL
} },
46 { FREQUENCY
, .u
.n
= {"\x01%df", 5, 0, 9, 0, 0, NULL
} },
47 { DIRECT
, .u
.n
= {NULL
, 0, 0, 1, 0, 0, NULL
} },
51 /* These attributes will appear in /sys/accessibility/speakup/soft. */
53 static struct kobj_attribute caps_start_attribute
=
54 __ATTR(caps_start
, 0644, spk_var_show
, spk_var_store
);
55 static struct kobj_attribute caps_stop_attribute
=
56 __ATTR(caps_stop
, 0644, spk_var_show
, spk_var_store
);
57 static struct kobj_attribute freq_attribute
=
58 __ATTR(freq
, 0644, spk_var_show
, spk_var_store
);
59 static struct kobj_attribute pitch_attribute
=
60 __ATTR(pitch
, 0644, spk_var_show
, spk_var_store
);
61 static struct kobj_attribute inflection_attribute
=
62 __ATTR(inflection
, 0644, spk_var_show
, spk_var_store
);
63 static struct kobj_attribute punct_attribute
=
64 __ATTR(punct
, 0644, spk_var_show
, spk_var_store
);
65 static struct kobj_attribute rate_attribute
=
66 __ATTR(rate
, 0644, spk_var_show
, spk_var_store
);
67 static struct kobj_attribute tone_attribute
=
68 __ATTR(tone
, 0644, spk_var_show
, spk_var_store
);
69 static struct kobj_attribute voice_attribute
=
70 __ATTR(voice
, 0644, spk_var_show
, spk_var_store
);
71 static struct kobj_attribute vol_attribute
=
72 __ATTR(vol
, 0644, spk_var_show
, spk_var_store
);
75 * We should uncomment the following definition, when we agree on a
76 * method of passing a language designation to the software synthesizer.
77 * static struct kobj_attribute lang_attribute =
78 * __ATTR(lang, 0644, spk_var_show, spk_var_store);
81 static struct kobj_attribute delay_time_attribute
=
82 __ATTR(delay_time
, 0644, spk_var_show
, spk_var_store
);
83 static struct kobj_attribute direct_attribute
=
84 __ATTR(direct
, 0644, spk_var_show
, spk_var_store
);
85 static struct kobj_attribute full_time_attribute
=
86 __ATTR(full_time
, 0644, spk_var_show
, spk_var_store
);
87 static struct kobj_attribute jiffy_delta_attribute
=
88 __ATTR(jiffy_delta
, 0644, spk_var_show
, spk_var_store
);
89 static struct kobj_attribute trigger_time_attribute
=
90 __ATTR(trigger_time
, 0644, spk_var_show
, spk_var_store
);
93 * Create a group of attributes so that we can create and destroy them all
96 static struct attribute
*synth_attrs
[] = {
97 &caps_start_attribute
.attr
,
98 &caps_stop_attribute
.attr
,
100 /* &lang_attribute.attr, */
101 &pitch_attribute
.attr
,
102 &inflection_attribute
.attr
,
103 &punct_attribute
.attr
,
104 &rate_attribute
.attr
,
105 &tone_attribute
.attr
,
106 &voice_attribute
.attr
,
108 &delay_time_attribute
.attr
,
109 &direct_attribute
.attr
,
110 &full_time_attribute
.attr
,
111 &jiffy_delta_attribute
.attr
,
112 &trigger_time_attribute
.attr
,
113 NULL
, /* need to NULL terminate the list of attributes */
116 static struct spk_synth synth_soft
= {
118 .version
= DRV_VERSION
,
119 .long_name
= "software synth",
120 .init
= "\01@\x01\x31y\n",
121 .procspeech
= PROCSPEECH
,
126 .startup
= SYNTH_START
,
127 .checkval
= SYNTH_CHECK
,
130 .probe
= softsynth_probe
,
131 .release
= softsynth_release
,
132 .synth_immediate
= NULL
,
135 .is_alive
= softsynth_is_alive
,
136 .synth_adjust
= NULL
,
137 .read_buff_add
= NULL
,
138 .get_index
= get_index
,
140 .command
= "\x01%di",
146 .attrs
= synth_attrs
,
151 static char *get_initstring(void)
157 memset(buf
, 0, sizeof(buf
));
159 var
= synth_soft
.vars
;
160 while (var
->var_id
!= MAXVARS
) {
161 if (var
->var_id
!= CAPS_START
&& var
->var_id
!= CAPS_STOP
&&
162 var
->var_id
!= PAUSE
&& var
->var_id
!= DIRECT
)
163 cp
= cp
+ sprintf(cp
, var
->u
.n
.synth_fmt
,
167 cp
= cp
+ sprintf(cp
, "\n");
171 static int softsynth_open(struct inode
*inode
, struct file
*fp
)
174 /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */
176 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
177 if (synth_soft
.alive
) {
178 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
181 synth_soft
.alive
= 1;
182 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
186 static int softsynth_close(struct inode
*inode
, struct file
*fp
)
190 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
191 synth_soft
.alive
= 0;
193 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
194 /* Make sure we let applications go before leaving */
195 speakup_start_ttys();
199 static ssize_t
softsynthx_read(struct file
*fp
, char __user
*buf
, size_t count
,
200 loff_t
*pos
, int unicode
)
205 size_t bytes_per_ch
= unicode
? 3 : 1;
211 if (count
< bytes_per_ch
)
214 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
215 synth_soft
.alive
= 1;
217 prepare_to_wait(&speakup_event
, &wait
, TASK_INTERRUPTIBLE
);
218 if (synth_current() == &synth_soft
) {
220 synth_buffer_skip_nonlatin1();
221 if (!synth_buffer_empty() || speakup_info
.flushing
)
224 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
225 if (fp
->f_flags
& O_NONBLOCK
) {
226 finish_wait(&speakup_event
, &wait
);
229 if (signal_pending(current
)) {
230 finish_wait(&speakup_event
, &wait
);
234 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
236 finish_wait(&speakup_event
, &wait
);
239 init
= get_initstring();
241 /* Keep 3 bytes available for a 16bit UTF-8-encoded character */
242 while (chars_sent
<= count
- bytes_per_ch
) {
243 if (synth_current() != &synth_soft
)
245 if (speakup_info
.flushing
) {
246 speakup_info
.flushing
= 0;
248 } else if (init
[init_pos
]) {
249 ch
= init
[init_pos
++];
252 synth_buffer_skip_nonlatin1();
253 if (synth_buffer_empty())
255 ch
= synth_buffer_getc();
257 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
259 if ((!unicode
&& ch
< 0x100) || (unicode
&& ch
< 0x80)) {
262 if (copy_to_user(cp
, &c
, 1))
267 } else if (unicode
&& ch
< 0x800) {
273 if (copy_to_user(cp
, s
, sizeof(s
)))
276 chars_sent
+= sizeof(s
);
278 } else if (unicode
) {
281 0x80 | ((ch
>> 6) & 0x3f),
285 if (copy_to_user(cp
, s
, sizeof(s
)))
288 chars_sent
+= sizeof(s
);
292 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
295 empty
= synth_buffer_empty();
296 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
298 speakup_start_ttys();
304 static ssize_t
softsynth_read(struct file
*fp
, char __user
*buf
, size_t count
,
307 return softsynthx_read(fp
, buf
, count
, pos
, 0);
310 static ssize_t
softsynthu_read(struct file
*fp
, char __user
*buf
, size_t count
,
313 return softsynthx_read(fp
, buf
, count
, pos
, 1);
316 static int last_index
;
318 static ssize_t
softsynth_write(struct file
*fp
, const char __user
*buf
,
319 size_t count
, loff_t
*pos
)
321 unsigned long supplied_index
= 0;
324 converted
= kstrtoul_from_user(buf
, count
, 0, &supplied_index
);
329 last_index
= supplied_index
;
333 static __poll_t
softsynth_poll(struct file
*fp
, struct poll_table_struct
*wait
)
338 poll_wait(fp
, &speakup_event
, wait
);
340 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
341 if (synth_current() == &synth_soft
&&
342 (!synth_buffer_empty() || speakup_info
.flushing
))
343 ret
= EPOLLIN
| EPOLLRDNORM
;
344 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
348 static unsigned char get_index(struct spk_synth
*synth
)
357 static const struct file_operations softsynth_fops
= {
358 .owner
= THIS_MODULE
,
359 .poll
= softsynth_poll
,
360 .read
= softsynth_read
,
361 .write
= softsynth_write
,
362 .open
= softsynth_open
,
363 .release
= softsynth_close
,
366 static const struct file_operations softsynthu_fops
= {
367 .owner
= THIS_MODULE
,
368 .poll
= softsynth_poll
,
369 .read
= softsynthu_read
,
370 .write
= softsynth_write
,
371 .open
= softsynth_open
,
372 .release
= softsynth_close
,
375 static int softsynth_probe(struct spk_synth
*synth
)
377 if (misc_registered
!= 0)
379 memset(&synth_device
, 0, sizeof(synth_device
));
380 synth_device
.minor
= MISC_DYNAMIC_MINOR
;
381 synth_device
.name
= "softsynth";
382 synth_device
.fops
= &softsynth_fops
;
383 if (misc_register(&synth_device
)) {
384 pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n");
388 memset(&synthu_device
, 0, sizeof(synthu_device
));
389 synthu_device
.minor
= MISC_DYNAMIC_MINOR
;
390 synthu_device
.name
= "softsynthu";
391 synthu_device
.fops
= &softsynthu_fops
;
392 if (misc_register(&synthu_device
)) {
393 pr_warn("Couldn't initialize miscdevice /dev/softsynthu.\n");
398 pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR %d)\n",
400 pr_info("initialized device: /dev/softsynthu, node (MAJOR 10, MINOR %d)\n",
401 synthu_device
.minor
);
405 static void softsynth_release(void)
407 misc_deregister(&synth_device
);
408 misc_deregister(&synthu_device
);
410 pr_info("unregistered /dev/softsynth\n");
411 pr_info("unregistered /dev/softsynthu\n");
414 static int softsynth_is_alive(struct spk_synth
*synth
)
416 if (synth_soft
.alive
)
421 module_param_named(start
, synth_soft
.startup
, short, 0444);
423 MODULE_PARM_DESC(start
, "Start the synthesizer once it is loaded.");
425 module_spk_synth(synth_soft
);
427 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
428 MODULE_DESCRIPTION("Speakup userspace software synthesizer support");
429 MODULE_LICENSE("GPL");
430 MODULE_VERSION(DRV_VERSION
);