xref: /OK3568_Linux_fs/kernel/drivers/accessibility/speakup/speakup_soft.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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