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