xref: /OK3568_Linux_fs/kernel/drivers/accessibility/speakup/kobjects.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Speakup kobject implementation
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2009 William Hubbs
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * This code is based on kobject-example.c, which came with linux 2.6.x.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
10*4882a593Smuzhiyun  * Copyright (C) 2007 Novell Inc.
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * Released under the GPL version 2 only.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun #include <linux/slab.h>		/* For kmalloc. */
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/kobject.h>
18*4882a593Smuzhiyun #include <linux/string.h>
19*4882a593Smuzhiyun #include <linux/string_helpers.h>
20*4882a593Smuzhiyun #include <linux/sysfs.h>
21*4882a593Smuzhiyun #include <linux/ctype.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include "speakup.h"
24*4882a593Smuzhiyun #include "spk_priv.h"
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /*
27*4882a593Smuzhiyun  * This is called when a user reads the characters or chartab sys file.
28*4882a593Smuzhiyun  */
chars_chartab_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)29*4882a593Smuzhiyun static ssize_t chars_chartab_show(struct kobject *kobj,
30*4882a593Smuzhiyun 				  struct kobj_attribute *attr, char *buf)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	int i;
33*4882a593Smuzhiyun 	int len = 0;
34*4882a593Smuzhiyun 	char *cp;
35*4882a593Smuzhiyun 	char *buf_pointer = buf;
36*4882a593Smuzhiyun 	size_t bufsize = PAGE_SIZE;
37*4882a593Smuzhiyun 	unsigned long flags;
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
40*4882a593Smuzhiyun 	*buf_pointer = '\0';
41*4882a593Smuzhiyun 	for (i = 0; i < 256; i++) {
42*4882a593Smuzhiyun 		if (bufsize <= 1)
43*4882a593Smuzhiyun 			break;
44*4882a593Smuzhiyun 		if (strcmp("characters", attr->attr.name) == 0) {
45*4882a593Smuzhiyun 			len = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
46*4882a593Smuzhiyun 					i, spk_characters[i]);
47*4882a593Smuzhiyun 		} else {	/* show chartab entry */
48*4882a593Smuzhiyun 			if (IS_TYPE(i, B_CTL))
49*4882a593Smuzhiyun 				cp = "B_CTL";
50*4882a593Smuzhiyun 			else if (IS_TYPE(i, WDLM))
51*4882a593Smuzhiyun 				cp = "WDLM";
52*4882a593Smuzhiyun 			else if (IS_TYPE(i, A_PUNC))
53*4882a593Smuzhiyun 				cp = "A_PUNC";
54*4882a593Smuzhiyun 			else if (IS_TYPE(i, PUNC))
55*4882a593Smuzhiyun 				cp = "PUNC";
56*4882a593Smuzhiyun 			else if (IS_TYPE(i, NUM))
57*4882a593Smuzhiyun 				cp = "NUM";
58*4882a593Smuzhiyun 			else if (IS_TYPE(i, A_CAP))
59*4882a593Smuzhiyun 				cp = "A_CAP";
60*4882a593Smuzhiyun 			else if (IS_TYPE(i, ALPHA))
61*4882a593Smuzhiyun 				cp = "ALPHA";
62*4882a593Smuzhiyun 			else if (IS_TYPE(i, B_CAPSYM))
63*4882a593Smuzhiyun 				cp = "B_CAPSYM";
64*4882a593Smuzhiyun 			else if (IS_TYPE(i, B_SYM))
65*4882a593Smuzhiyun 				cp = "B_SYM";
66*4882a593Smuzhiyun 			else
67*4882a593Smuzhiyun 				cp = "0";
68*4882a593Smuzhiyun 			len =
69*4882a593Smuzhiyun 			    scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp);
70*4882a593Smuzhiyun 		}
71*4882a593Smuzhiyun 		bufsize -= len;
72*4882a593Smuzhiyun 		buf_pointer += len;
73*4882a593Smuzhiyun 	}
74*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
75*4882a593Smuzhiyun 	return buf_pointer - buf;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun /*
79*4882a593Smuzhiyun  * Print informational messages or warnings after updating
80*4882a593Smuzhiyun  * character descriptions or chartab entries.
81*4882a593Smuzhiyun  */
report_char_chartab_status(int reset,int received,int used,int rejected,int do_characters)82*4882a593Smuzhiyun static void report_char_chartab_status(int reset, int received, int used,
83*4882a593Smuzhiyun 				       int rejected, int do_characters)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	static char const *object_type[] = {
86*4882a593Smuzhiyun 		"character class entries",
87*4882a593Smuzhiyun 		"character descriptions",
88*4882a593Smuzhiyun 	};
89*4882a593Smuzhiyun 	int len;
90*4882a593Smuzhiyun 	char buf[80];
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (reset) {
93*4882a593Smuzhiyun 		pr_info("%s reset to defaults\n", object_type[do_characters]);
94*4882a593Smuzhiyun 	} else if (received) {
95*4882a593Smuzhiyun 		len = snprintf(buf, sizeof(buf),
96*4882a593Smuzhiyun 			       " updated %d of %d %s\n",
97*4882a593Smuzhiyun 			       used, received, object_type[do_characters]);
98*4882a593Smuzhiyun 		if (rejected)
99*4882a593Smuzhiyun 			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
100*4882a593Smuzhiyun 				 " with %d reject%s\n",
101*4882a593Smuzhiyun 				 rejected, rejected > 1 ? "s" : "");
102*4882a593Smuzhiyun 		pr_info("%s", buf);
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun /*
107*4882a593Smuzhiyun  * This is called when a user changes the characters or chartab parameters.
108*4882a593Smuzhiyun  */
chars_chartab_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)109*4882a593Smuzhiyun static ssize_t chars_chartab_store(struct kobject *kobj,
110*4882a593Smuzhiyun 				   struct kobj_attribute *attr,
111*4882a593Smuzhiyun 				   const char *buf, size_t count)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	char *cp = (char *)buf;
114*4882a593Smuzhiyun 	char *end = cp + count; /* the null at the end of the buffer */
115*4882a593Smuzhiyun 	char *linefeed = NULL;
116*4882a593Smuzhiyun 	char keyword[MAX_DESC_LEN + 1];
117*4882a593Smuzhiyun 	char *outptr = NULL;	/* Will hold keyword or desc. */
118*4882a593Smuzhiyun 	char *temp = NULL;
119*4882a593Smuzhiyun 	char *desc = NULL;
120*4882a593Smuzhiyun 	ssize_t retval = count;
121*4882a593Smuzhiyun 	unsigned long flags;
122*4882a593Smuzhiyun 	unsigned long index = 0;
123*4882a593Smuzhiyun 	int charclass = 0;
124*4882a593Smuzhiyun 	int received = 0;
125*4882a593Smuzhiyun 	int used = 0;
126*4882a593Smuzhiyun 	int rejected = 0;
127*4882a593Smuzhiyun 	int reset = 0;
128*4882a593Smuzhiyun 	int do_characters = !strcmp(attr->attr.name, "characters");
129*4882a593Smuzhiyun 	size_t desc_length = 0;
130*4882a593Smuzhiyun 	int i;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
133*4882a593Smuzhiyun 	while (cp < end) {
134*4882a593Smuzhiyun 		while ((cp < end) && (*cp == ' ' || *cp == '\t'))
135*4882a593Smuzhiyun 			cp++;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		if (cp == end)
138*4882a593Smuzhiyun 			break;
139*4882a593Smuzhiyun 		if ((*cp == '\n') || strchr("dDrR", *cp)) {
140*4882a593Smuzhiyun 			reset = 1;
141*4882a593Smuzhiyun 			break;
142*4882a593Smuzhiyun 		}
143*4882a593Smuzhiyun 		received++;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 		linefeed = strchr(cp, '\n');
146*4882a593Smuzhiyun 		if (!linefeed) {
147*4882a593Smuzhiyun 			rejected++;
148*4882a593Smuzhiyun 			break;
149*4882a593Smuzhiyun 		}
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 		if (!isdigit(*cp)) {
152*4882a593Smuzhiyun 			rejected++;
153*4882a593Smuzhiyun 			cp = linefeed + 1;
154*4882a593Smuzhiyun 			continue;
155*4882a593Smuzhiyun 		}
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 		/*
158*4882a593Smuzhiyun 		 * Do not replace with kstrtoul:
159*4882a593Smuzhiyun 		 * here we need temp to be updated
160*4882a593Smuzhiyun 		 */
161*4882a593Smuzhiyun 		index = simple_strtoul(cp, &temp, 10);
162*4882a593Smuzhiyun 		if (index > 255) {
163*4882a593Smuzhiyun 			rejected++;
164*4882a593Smuzhiyun 			cp = linefeed + 1;
165*4882a593Smuzhiyun 			continue;
166*4882a593Smuzhiyun 		}
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
169*4882a593Smuzhiyun 			temp++;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 		desc_length = linefeed - temp;
172*4882a593Smuzhiyun 		if (desc_length > MAX_DESC_LEN) {
173*4882a593Smuzhiyun 			rejected++;
174*4882a593Smuzhiyun 			cp = linefeed + 1;
175*4882a593Smuzhiyun 			continue;
176*4882a593Smuzhiyun 		}
177*4882a593Smuzhiyun 		if (do_characters) {
178*4882a593Smuzhiyun 			desc = kmalloc(desc_length + 1, GFP_ATOMIC);
179*4882a593Smuzhiyun 			if (!desc) {
180*4882a593Smuzhiyun 				retval = -ENOMEM;
181*4882a593Smuzhiyun 				reset = 1;	/* just reset on error. */
182*4882a593Smuzhiyun 				break;
183*4882a593Smuzhiyun 			}
184*4882a593Smuzhiyun 			outptr = desc;
185*4882a593Smuzhiyun 		} else {
186*4882a593Smuzhiyun 			outptr = keyword;
187*4882a593Smuzhiyun 		}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 		for (i = 0; i < desc_length; i++)
190*4882a593Smuzhiyun 			outptr[i] = temp[i];
191*4882a593Smuzhiyun 		outptr[desc_length] = '\0';
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		if (do_characters) {
194*4882a593Smuzhiyun 			if (spk_characters[index] != spk_default_chars[index])
195*4882a593Smuzhiyun 				kfree(spk_characters[index]);
196*4882a593Smuzhiyun 			spk_characters[index] = desc;
197*4882a593Smuzhiyun 			used++;
198*4882a593Smuzhiyun 		} else {
199*4882a593Smuzhiyun 			charclass = spk_chartab_get_value(keyword);
200*4882a593Smuzhiyun 			if (charclass == 0) {
201*4882a593Smuzhiyun 				rejected++;
202*4882a593Smuzhiyun 				cp = linefeed + 1;
203*4882a593Smuzhiyun 				continue;
204*4882a593Smuzhiyun 			}
205*4882a593Smuzhiyun 			if (charclass != spk_chartab[index]) {
206*4882a593Smuzhiyun 				spk_chartab[index] = charclass;
207*4882a593Smuzhiyun 				used++;
208*4882a593Smuzhiyun 			}
209*4882a593Smuzhiyun 		}
210*4882a593Smuzhiyun 		cp = linefeed + 1;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	if (reset) {
214*4882a593Smuzhiyun 		if (do_characters)
215*4882a593Smuzhiyun 			spk_reset_default_chars();
216*4882a593Smuzhiyun 		else
217*4882a593Smuzhiyun 			spk_reset_default_chartab();
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
221*4882a593Smuzhiyun 	report_char_chartab_status(reset, received, used, rejected,
222*4882a593Smuzhiyun 				   do_characters);
223*4882a593Smuzhiyun 	return retval;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun /*
227*4882a593Smuzhiyun  * This is called when a user reads the keymap parameter.
228*4882a593Smuzhiyun  */
keymap_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)229*4882a593Smuzhiyun static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr,
230*4882a593Smuzhiyun 			   char *buf)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	char *cp = buf;
233*4882a593Smuzhiyun 	int i;
234*4882a593Smuzhiyun 	int n;
235*4882a593Smuzhiyun 	int num_keys;
236*4882a593Smuzhiyun 	int nstates;
237*4882a593Smuzhiyun 	u_char *cp1;
238*4882a593Smuzhiyun 	u_char ch;
239*4882a593Smuzhiyun 	unsigned long flags;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
242*4882a593Smuzhiyun 	cp1 = spk_key_buf + SHIFT_TBL_SIZE;
243*4882a593Smuzhiyun 	num_keys = (int)(*cp1);
244*4882a593Smuzhiyun 	nstates = (int)cp1[1];
245*4882a593Smuzhiyun 	cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates);
246*4882a593Smuzhiyun 	cp1 += 2; /* now pointing at shift states */
247*4882a593Smuzhiyun 	/* dump num_keys+1 as first row is shift states + flags,
248*4882a593Smuzhiyun 	 * each subsequent row is key + states
249*4882a593Smuzhiyun 	 */
250*4882a593Smuzhiyun 	for (n = 0; n <= num_keys; n++) {
251*4882a593Smuzhiyun 		for (i = 0; i <= nstates; i++) {
252*4882a593Smuzhiyun 			ch = *cp1++;
253*4882a593Smuzhiyun 			cp += sprintf(cp, "%d,", (int)ch);
254*4882a593Smuzhiyun 			*cp++ = (i < nstates) ? SPACE : '\n';
255*4882a593Smuzhiyun 		}
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun 	cp += sprintf(cp, "0, %d\n", KEY_MAP_VER);
258*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
259*4882a593Smuzhiyun 	return (int)(cp - buf);
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun /*
263*4882a593Smuzhiyun  * This is called when a user changes the keymap parameter.
264*4882a593Smuzhiyun  */
keymap_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)265*4882a593Smuzhiyun static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr,
266*4882a593Smuzhiyun 			    const char *buf, size_t count)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun 	int i;
269*4882a593Smuzhiyun 	ssize_t ret = count;
270*4882a593Smuzhiyun 	char *in_buff = NULL;
271*4882a593Smuzhiyun 	char *cp;
272*4882a593Smuzhiyun 	u_char *cp1;
273*4882a593Smuzhiyun 	unsigned long flags;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
276*4882a593Smuzhiyun 	in_buff = kmemdup(buf, count + 1, GFP_ATOMIC);
277*4882a593Smuzhiyun 	if (!in_buff) {
278*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
279*4882a593Smuzhiyun 		return -ENOMEM;
280*4882a593Smuzhiyun 	}
281*4882a593Smuzhiyun 	if (strchr("dDrR", *in_buff)) {
282*4882a593Smuzhiyun 		spk_set_key_info(spk_key_defaults, spk_key_buf);
283*4882a593Smuzhiyun 		pr_info("keymap set to default values\n");
284*4882a593Smuzhiyun 		kfree(in_buff);
285*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
286*4882a593Smuzhiyun 		return count;
287*4882a593Smuzhiyun 	}
288*4882a593Smuzhiyun 	if (in_buff[count - 1] == '\n')
289*4882a593Smuzhiyun 		in_buff[count - 1] = '\0';
290*4882a593Smuzhiyun 	cp = in_buff;
291*4882a593Smuzhiyun 	cp1 = (u_char *)in_buff;
292*4882a593Smuzhiyun 	for (i = 0; i < 3; i++) {
293*4882a593Smuzhiyun 		cp = spk_s2uchar(cp, cp1);
294*4882a593Smuzhiyun 		cp1++;
295*4882a593Smuzhiyun 	}
296*4882a593Smuzhiyun 	i = (int)cp1[-2] + 1;
297*4882a593Smuzhiyun 	i *= (int)cp1[-1] + 1;
298*4882a593Smuzhiyun 	i += 2; /* 0 and last map ver */
299*4882a593Smuzhiyun 	if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||
300*4882a593Smuzhiyun 	    i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
301*4882a593Smuzhiyun 		pr_warn("i %d %d %d %d\n", i,
302*4882a593Smuzhiyun 			(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
303*4882a593Smuzhiyun 		kfree(in_buff);
304*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
305*4882a593Smuzhiyun 		return -EINVAL;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 	while (--i >= 0) {
308*4882a593Smuzhiyun 		cp = spk_s2uchar(cp, cp1);
309*4882a593Smuzhiyun 		cp1++;
310*4882a593Smuzhiyun 		if (!(*cp))
311*4882a593Smuzhiyun 			break;
312*4882a593Smuzhiyun 	}
313*4882a593Smuzhiyun 	if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {
314*4882a593Smuzhiyun 		ret = -EINVAL;
315*4882a593Smuzhiyun 		pr_warn("end %d %d %d %d\n", i,
316*4882a593Smuzhiyun 			(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
317*4882a593Smuzhiyun 	} else {
318*4882a593Smuzhiyun 		if (spk_set_key_info(in_buff, spk_key_buf)) {
319*4882a593Smuzhiyun 			spk_set_key_info(spk_key_defaults, spk_key_buf);
320*4882a593Smuzhiyun 			ret = -EINVAL;
321*4882a593Smuzhiyun 			pr_warn("set key failed\n");
322*4882a593Smuzhiyun 		}
323*4882a593Smuzhiyun 	}
324*4882a593Smuzhiyun 	kfree(in_buff);
325*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
326*4882a593Smuzhiyun 	return ret;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun /*
330*4882a593Smuzhiyun  * This is called when a user changes the value of the silent parameter.
331*4882a593Smuzhiyun  */
silent_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)332*4882a593Smuzhiyun static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,
333*4882a593Smuzhiyun 			    const char *buf, size_t count)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	int len;
336*4882a593Smuzhiyun 	struct vc_data *vc = vc_cons[fg_console].d;
337*4882a593Smuzhiyun 	char ch = 0;
338*4882a593Smuzhiyun 	char shut;
339*4882a593Smuzhiyun 	unsigned long flags;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	len = strlen(buf);
342*4882a593Smuzhiyun 	if (len > 0 && len < 3) {
343*4882a593Smuzhiyun 		ch = buf[0];
344*4882a593Smuzhiyun 		if (ch == '\n')
345*4882a593Smuzhiyun 			ch = '0';
346*4882a593Smuzhiyun 	}
347*4882a593Smuzhiyun 	if (ch < '0' || ch > '7') {
348*4882a593Smuzhiyun 		pr_warn("silent value '%c' not in range (0,7)\n", ch);
349*4882a593Smuzhiyun 		return -EINVAL;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
352*4882a593Smuzhiyun 	if (ch & 2) {
353*4882a593Smuzhiyun 		shut = 1;
354*4882a593Smuzhiyun 		spk_do_flush();
355*4882a593Smuzhiyun 	} else {
356*4882a593Smuzhiyun 		shut = 0;
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun 	if (ch & 4)
359*4882a593Smuzhiyun 		shut |= 0x40;
360*4882a593Smuzhiyun 	if (ch & 1)
361*4882a593Smuzhiyun 		spk_shut_up |= shut;
362*4882a593Smuzhiyun 	else
363*4882a593Smuzhiyun 		spk_shut_up &= ~shut;
364*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
365*4882a593Smuzhiyun 	return count;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun /*
369*4882a593Smuzhiyun  * This is called when a user reads the synth setting.
370*4882a593Smuzhiyun  */
synth_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)371*4882a593Smuzhiyun static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,
372*4882a593Smuzhiyun 			  char *buf)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	int rv;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	if (!synth)
377*4882a593Smuzhiyun 		rv = sprintf(buf, "%s\n", "none");
378*4882a593Smuzhiyun 	else
379*4882a593Smuzhiyun 		rv = sprintf(buf, "%s\n", synth->name);
380*4882a593Smuzhiyun 	return rv;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun /*
384*4882a593Smuzhiyun  * This is called when a user requests to change synthesizers.
385*4882a593Smuzhiyun  */
synth_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)386*4882a593Smuzhiyun static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,
387*4882a593Smuzhiyun 			   const char *buf, size_t count)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun 	int len;
390*4882a593Smuzhiyun 	char new_synth_name[10];
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	len = strlen(buf);
393*4882a593Smuzhiyun 	if (len < 2 || len > 9)
394*4882a593Smuzhiyun 		return -EINVAL;
395*4882a593Smuzhiyun 	memcpy(new_synth_name, buf, len);
396*4882a593Smuzhiyun 	if (new_synth_name[len - 1] == '\n')
397*4882a593Smuzhiyun 		len--;
398*4882a593Smuzhiyun 	new_synth_name[len] = '\0';
399*4882a593Smuzhiyun 	spk_strlwr(new_synth_name);
400*4882a593Smuzhiyun 	if (synth && !strcmp(new_synth_name, synth->name)) {
401*4882a593Smuzhiyun 		pr_warn("%s already in use\n", new_synth_name);
402*4882a593Smuzhiyun 	} else if (synth_init(new_synth_name) != 0) {
403*4882a593Smuzhiyun 		pr_warn("failed to init synth %s\n", new_synth_name);
404*4882a593Smuzhiyun 		return -ENODEV;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 	return count;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun /*
410*4882a593Smuzhiyun  * This is called when text is sent to the synth via the synth_direct file.
411*4882a593Smuzhiyun  */
synth_direct_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)412*4882a593Smuzhiyun static ssize_t synth_direct_store(struct kobject *kobj,
413*4882a593Smuzhiyun 				  struct kobj_attribute *attr,
414*4882a593Smuzhiyun 				  const char *buf, size_t count)
415*4882a593Smuzhiyun {
416*4882a593Smuzhiyun 	u_char tmp[256];
417*4882a593Smuzhiyun 	int len;
418*4882a593Smuzhiyun 	int bytes;
419*4882a593Smuzhiyun 	const char *ptr = buf;
420*4882a593Smuzhiyun 	unsigned long flags;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	if (!synth)
423*4882a593Smuzhiyun 		return -EPERM;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	len = strlen(buf);
426*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
427*4882a593Smuzhiyun 	while (len > 0) {
428*4882a593Smuzhiyun 		bytes = min_t(size_t, len, 250);
429*4882a593Smuzhiyun 		strncpy(tmp, ptr, bytes);
430*4882a593Smuzhiyun 		tmp[bytes] = '\0';
431*4882a593Smuzhiyun 		string_unescape_any_inplace(tmp);
432*4882a593Smuzhiyun 		synth_printf("%s", tmp);
433*4882a593Smuzhiyun 		ptr += bytes;
434*4882a593Smuzhiyun 		len -= bytes;
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
437*4882a593Smuzhiyun 	return count;
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun /*
441*4882a593Smuzhiyun  * This function is called when a user reads the version.
442*4882a593Smuzhiyun  */
version_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)443*4882a593Smuzhiyun static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
444*4882a593Smuzhiyun 			    char *buf)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun 	char *cp;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	cp = buf;
449*4882a593Smuzhiyun 	cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
450*4882a593Smuzhiyun 	if (synth)
451*4882a593Smuzhiyun 		cp += sprintf(cp, "%s synthesizer driver version %s\n",
452*4882a593Smuzhiyun 		synth->name, synth->version);
453*4882a593Smuzhiyun 	return cp - buf;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun /*
457*4882a593Smuzhiyun  * This is called when a user reads the punctuation settings.
458*4882a593Smuzhiyun  */
punc_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)459*4882a593Smuzhiyun static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
460*4882a593Smuzhiyun 			 char *buf)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun 	int i;
463*4882a593Smuzhiyun 	char *cp = buf;
464*4882a593Smuzhiyun 	struct st_var_header *p_header;
465*4882a593Smuzhiyun 	struct punc_var_t *var;
466*4882a593Smuzhiyun 	struct st_bits_data *pb;
467*4882a593Smuzhiyun 	short mask;
468*4882a593Smuzhiyun 	unsigned long flags;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	p_header = spk_var_header_by_name(attr->attr.name);
471*4882a593Smuzhiyun 	if (!p_header) {
472*4882a593Smuzhiyun 		pr_warn("p_header is null, attr->attr.name is %s\n",
473*4882a593Smuzhiyun 			attr->attr.name);
474*4882a593Smuzhiyun 		return -EINVAL;
475*4882a593Smuzhiyun 	}
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	var = spk_get_punc_var(p_header->var_id);
478*4882a593Smuzhiyun 	if (!var) {
479*4882a593Smuzhiyun 		pr_warn("var is null, p_header->var_id is %i\n",
480*4882a593Smuzhiyun 			p_header->var_id);
481*4882a593Smuzhiyun 		return -EINVAL;
482*4882a593Smuzhiyun 	}
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
485*4882a593Smuzhiyun 	pb = (struct st_bits_data *)&spk_punc_info[var->value];
486*4882a593Smuzhiyun 	mask = pb->mask;
487*4882a593Smuzhiyun 	for (i = 33; i < 128; i++) {
488*4882a593Smuzhiyun 		if (!(spk_chartab[i] & mask))
489*4882a593Smuzhiyun 			continue;
490*4882a593Smuzhiyun 		*cp++ = (char)i;
491*4882a593Smuzhiyun 	}
492*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
493*4882a593Smuzhiyun 	return cp - buf;
494*4882a593Smuzhiyun }
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun /*
497*4882a593Smuzhiyun  * This is called when a user changes the punctuation settings.
498*4882a593Smuzhiyun  */
punc_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)499*4882a593Smuzhiyun static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
500*4882a593Smuzhiyun 			  const char *buf, size_t count)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun 	int x;
503*4882a593Smuzhiyun 	struct st_var_header *p_header;
504*4882a593Smuzhiyun 	struct punc_var_t *var;
505*4882a593Smuzhiyun 	char punc_buf[100];
506*4882a593Smuzhiyun 	unsigned long flags;
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	x = strlen(buf);
509*4882a593Smuzhiyun 	if (x < 1 || x > 99)
510*4882a593Smuzhiyun 		return -EINVAL;
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	p_header = spk_var_header_by_name(attr->attr.name);
513*4882a593Smuzhiyun 	if (!p_header) {
514*4882a593Smuzhiyun 		pr_warn("p_header is null, attr->attr.name is %s\n",
515*4882a593Smuzhiyun 			attr->attr.name);
516*4882a593Smuzhiyun 		return -EINVAL;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	var = spk_get_punc_var(p_header->var_id);
520*4882a593Smuzhiyun 	if (!var) {
521*4882a593Smuzhiyun 		pr_warn("var is null, p_header->var_id is %i\n",
522*4882a593Smuzhiyun 			p_header->var_id);
523*4882a593Smuzhiyun 		return -EINVAL;
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	memcpy(punc_buf, buf, x);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	while (x && punc_buf[x - 1] == '\n')
529*4882a593Smuzhiyun 		x--;
530*4882a593Smuzhiyun 	punc_buf[x] = '\0';
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	if (*punc_buf == 'd' || *punc_buf == 'r')
535*4882a593Smuzhiyun 		x = spk_set_mask_bits(NULL, var->value, 3);
536*4882a593Smuzhiyun 	else
537*4882a593Smuzhiyun 		x = spk_set_mask_bits(punc_buf, var->value, 3);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
540*4882a593Smuzhiyun 	return count;
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun /*
544*4882a593Smuzhiyun  * This function is called when a user reads one of the variable parameters.
545*4882a593Smuzhiyun  */
spk_var_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)546*4882a593Smuzhiyun ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
547*4882a593Smuzhiyun 		     char *buf)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun 	int rv = 0;
550*4882a593Smuzhiyun 	struct st_var_header *param;
551*4882a593Smuzhiyun 	struct var_t *var;
552*4882a593Smuzhiyun 	char *cp1;
553*4882a593Smuzhiyun 	char *cp;
554*4882a593Smuzhiyun 	char ch;
555*4882a593Smuzhiyun 	unsigned long flags;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	param = spk_var_header_by_name(attr->attr.name);
558*4882a593Smuzhiyun 	if (!param)
559*4882a593Smuzhiyun 		return -EINVAL;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
562*4882a593Smuzhiyun 	var = (struct var_t *)param->data;
563*4882a593Smuzhiyun 	switch (param->var_type) {
564*4882a593Smuzhiyun 	case VAR_NUM:
565*4882a593Smuzhiyun 	case VAR_TIME:
566*4882a593Smuzhiyun 		if (var)
567*4882a593Smuzhiyun 			rv = sprintf(buf, "%i\n", var->u.n.value);
568*4882a593Smuzhiyun 		else
569*4882a593Smuzhiyun 			rv = sprintf(buf, "0\n");
570*4882a593Smuzhiyun 		break;
571*4882a593Smuzhiyun 	case VAR_STRING:
572*4882a593Smuzhiyun 		if (var) {
573*4882a593Smuzhiyun 			cp1 = buf;
574*4882a593Smuzhiyun 			*cp1++ = '"';
575*4882a593Smuzhiyun 			for (cp = (char *)param->p_val; (ch = *cp); cp++) {
576*4882a593Smuzhiyun 				if (ch >= ' ' && ch < '~')
577*4882a593Smuzhiyun 					*cp1++ = ch;
578*4882a593Smuzhiyun 				else
579*4882a593Smuzhiyun 					cp1 += sprintf(cp1, "\\x%02x", ch);
580*4882a593Smuzhiyun 			}
581*4882a593Smuzhiyun 			*cp1++ = '"';
582*4882a593Smuzhiyun 			*cp1++ = '\n';
583*4882a593Smuzhiyun 			*cp1 = '\0';
584*4882a593Smuzhiyun 			rv = cp1 - buf;
585*4882a593Smuzhiyun 		} else {
586*4882a593Smuzhiyun 			rv = sprintf(buf, "\"\"\n");
587*4882a593Smuzhiyun 		}
588*4882a593Smuzhiyun 		break;
589*4882a593Smuzhiyun 	default:
590*4882a593Smuzhiyun 		rv = sprintf(buf, "Bad parameter  %s, type %i\n",
591*4882a593Smuzhiyun 			     param->name, param->var_type);
592*4882a593Smuzhiyun 		break;
593*4882a593Smuzhiyun 	}
594*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
595*4882a593Smuzhiyun 	return rv;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(spk_var_show);
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun /*
600*4882a593Smuzhiyun  * Used to reset either default_pitch or default_vol.
601*4882a593Smuzhiyun  */
spk_reset_default_value(char * header_name,int * synth_default_value,int idx)602*4882a593Smuzhiyun static inline void spk_reset_default_value(char *header_name,
603*4882a593Smuzhiyun 					   int *synth_default_value, int idx)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun 	struct st_var_header *param;
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	if (synth && synth_default_value) {
608*4882a593Smuzhiyun 		param = spk_var_header_by_name(header_name);
609*4882a593Smuzhiyun 		if (param)  {
610*4882a593Smuzhiyun 			spk_set_num_var(synth_default_value[idx],
611*4882a593Smuzhiyun 					param, E_NEW_DEFAULT);
612*4882a593Smuzhiyun 			spk_set_num_var(0, param, E_DEFAULT);
613*4882a593Smuzhiyun 			pr_info("%s reset to default value\n", param->name);
614*4882a593Smuzhiyun 		}
615*4882a593Smuzhiyun 	}
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun /*
619*4882a593Smuzhiyun  * This function is called when a user echos a value to one of the
620*4882a593Smuzhiyun  * variable parameters.
621*4882a593Smuzhiyun  */
spk_var_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)622*4882a593Smuzhiyun ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
623*4882a593Smuzhiyun 		      const char *buf, size_t count)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	struct st_var_header *param;
626*4882a593Smuzhiyun 	int ret;
627*4882a593Smuzhiyun 	int len;
628*4882a593Smuzhiyun 	char *cp;
629*4882a593Smuzhiyun 	struct var_t *var_data;
630*4882a593Smuzhiyun 	long value;
631*4882a593Smuzhiyun 	unsigned long flags;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	param = spk_var_header_by_name(attr->attr.name);
634*4882a593Smuzhiyun 	if (!param)
635*4882a593Smuzhiyun 		return -EINVAL;
636*4882a593Smuzhiyun 	if (!param->data)
637*4882a593Smuzhiyun 		return 0;
638*4882a593Smuzhiyun 	ret = 0;
639*4882a593Smuzhiyun 	cp = (char *)buf;
640*4882a593Smuzhiyun 	string_unescape_any_inplace(cp);
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
643*4882a593Smuzhiyun 	switch (param->var_type) {
644*4882a593Smuzhiyun 	case VAR_NUM:
645*4882a593Smuzhiyun 	case VAR_TIME:
646*4882a593Smuzhiyun 		if (*cp == 'd' || *cp == 'r' || *cp == '\0')
647*4882a593Smuzhiyun 			len = E_DEFAULT;
648*4882a593Smuzhiyun 		else if (*cp == '+' || *cp == '-')
649*4882a593Smuzhiyun 			len = E_INC;
650*4882a593Smuzhiyun 		else
651*4882a593Smuzhiyun 			len = E_SET;
652*4882a593Smuzhiyun 		if (kstrtol(cp, 10, &value) == 0)
653*4882a593Smuzhiyun 			ret = spk_set_num_var(value, param, len);
654*4882a593Smuzhiyun 		else
655*4882a593Smuzhiyun 			pr_warn("overflow or parsing error has occurred");
656*4882a593Smuzhiyun 		if (ret == -ERANGE) {
657*4882a593Smuzhiyun 			var_data = param->data;
658*4882a593Smuzhiyun 			pr_warn("value for %s out of range, expect %d to %d\n",
659*4882a593Smuzhiyun 				param->name,
660*4882a593Smuzhiyun 				var_data->u.n.low, var_data->u.n.high);
661*4882a593Smuzhiyun 		}
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	       /*
664*4882a593Smuzhiyun 		* If voice was just changed, we might need to reset our default
665*4882a593Smuzhiyun 		* pitch and volume.
666*4882a593Smuzhiyun 		*/
667*4882a593Smuzhiyun 		if (param->var_id == VOICE && synth &&
668*4882a593Smuzhiyun 		    (ret == 0 || ret == -ERESTART)) {
669*4882a593Smuzhiyun 			var_data = param->data;
670*4882a593Smuzhiyun 			value = var_data->u.n.value;
671*4882a593Smuzhiyun 			spk_reset_default_value("pitch", synth->default_pitch,
672*4882a593Smuzhiyun 						value);
673*4882a593Smuzhiyun 			spk_reset_default_value("vol", synth->default_vol,
674*4882a593Smuzhiyun 						value);
675*4882a593Smuzhiyun 		}
676*4882a593Smuzhiyun 		break;
677*4882a593Smuzhiyun 	case VAR_STRING:
678*4882a593Smuzhiyun 		len = strlen(cp);
679*4882a593Smuzhiyun 		if ((len >= 1) && (cp[len - 1] == '\n'))
680*4882a593Smuzhiyun 			--len;
681*4882a593Smuzhiyun 		if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
682*4882a593Smuzhiyun 			++cp;
683*4882a593Smuzhiyun 			len -= 2;
684*4882a593Smuzhiyun 		}
685*4882a593Smuzhiyun 		cp[len] = '\0';
686*4882a593Smuzhiyun 		ret = spk_set_string_var(cp, param, len);
687*4882a593Smuzhiyun 		if (ret == -E2BIG)
688*4882a593Smuzhiyun 			pr_warn("value too long for %s\n",
689*4882a593Smuzhiyun 				param->name);
690*4882a593Smuzhiyun 		break;
691*4882a593Smuzhiyun 	default:
692*4882a593Smuzhiyun 		pr_warn("%s unknown type %d\n",
693*4882a593Smuzhiyun 			param->name, (int)param->var_type);
694*4882a593Smuzhiyun 	break;
695*4882a593Smuzhiyun 	}
696*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	if (ret == -ERESTART)
699*4882a593Smuzhiyun 		pr_info("%s reset to default value\n", param->name);
700*4882a593Smuzhiyun 	return count;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(spk_var_store);
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun /*
705*4882a593Smuzhiyun  * Functions for reading and writing lists of i18n messages.  Incomplete.
706*4882a593Smuzhiyun  */
707*4882a593Smuzhiyun 
message_show_helper(char * buf,enum msg_index_t first,enum msg_index_t last)708*4882a593Smuzhiyun static ssize_t message_show_helper(char *buf, enum msg_index_t first,
709*4882a593Smuzhiyun 				   enum msg_index_t last)
710*4882a593Smuzhiyun {
711*4882a593Smuzhiyun 	size_t bufsize = PAGE_SIZE;
712*4882a593Smuzhiyun 	char *buf_pointer = buf;
713*4882a593Smuzhiyun 	int printed;
714*4882a593Smuzhiyun 	enum msg_index_t cursor;
715*4882a593Smuzhiyun 	int index = 0;
716*4882a593Smuzhiyun 	*buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun 	for (cursor = first; cursor <= last; cursor++, index++) {
719*4882a593Smuzhiyun 		if (bufsize <= 1)
720*4882a593Smuzhiyun 			break;
721*4882a593Smuzhiyun 		printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
722*4882a593Smuzhiyun 				    index, spk_msg_get(cursor));
723*4882a593Smuzhiyun 		buf_pointer += printed;
724*4882a593Smuzhiyun 		bufsize -= printed;
725*4882a593Smuzhiyun 	}
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 	return buf_pointer - buf;
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun 
report_msg_status(int reset,int received,int used,int rejected,char * groupname)730*4882a593Smuzhiyun static void report_msg_status(int reset, int received, int used,
731*4882a593Smuzhiyun 			      int rejected, char *groupname)
732*4882a593Smuzhiyun {
733*4882a593Smuzhiyun 	int len;
734*4882a593Smuzhiyun 	char buf[160];
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	if (reset) {
737*4882a593Smuzhiyun 		pr_info("i18n messages from group %s reset to defaults\n",
738*4882a593Smuzhiyun 			groupname);
739*4882a593Smuzhiyun 	} else if (received) {
740*4882a593Smuzhiyun 		len = snprintf(buf, sizeof(buf),
741*4882a593Smuzhiyun 			       " updated %d of %d i18n messages from group %s\n",
742*4882a593Smuzhiyun 				       used, received, groupname);
743*4882a593Smuzhiyun 		if (rejected)
744*4882a593Smuzhiyun 			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
745*4882a593Smuzhiyun 				 " with %d reject%s\n",
746*4882a593Smuzhiyun 				 rejected, rejected > 1 ? "s" : "");
747*4882a593Smuzhiyun 		pr_info("%s", buf);
748*4882a593Smuzhiyun 	}
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun 
message_store_helper(const char * buf,size_t count,struct msg_group_t * group)751*4882a593Smuzhiyun static ssize_t message_store_helper(const char *buf, size_t count,
752*4882a593Smuzhiyun 				    struct msg_group_t *group)
753*4882a593Smuzhiyun {
754*4882a593Smuzhiyun 	char *cp = (char *)buf;
755*4882a593Smuzhiyun 	char *end = cp + count;
756*4882a593Smuzhiyun 	char *linefeed = NULL;
757*4882a593Smuzhiyun 	char *temp = NULL;
758*4882a593Smuzhiyun 	ssize_t msg_stored = 0;
759*4882a593Smuzhiyun 	ssize_t retval = count;
760*4882a593Smuzhiyun 	size_t desc_length = 0;
761*4882a593Smuzhiyun 	unsigned long index = 0;
762*4882a593Smuzhiyun 	int received = 0;
763*4882a593Smuzhiyun 	int used = 0;
764*4882a593Smuzhiyun 	int rejected = 0;
765*4882a593Smuzhiyun 	int reset = 0;
766*4882a593Smuzhiyun 	enum msg_index_t firstmessage = group->start;
767*4882a593Smuzhiyun 	enum msg_index_t lastmessage = group->end;
768*4882a593Smuzhiyun 	enum msg_index_t curmessage;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	while (cp < end) {
771*4882a593Smuzhiyun 		while ((cp < end) && (*cp == ' ' || *cp == '\t'))
772*4882a593Smuzhiyun 			cp++;
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun 		if (cp == end)
775*4882a593Smuzhiyun 			break;
776*4882a593Smuzhiyun 		if (strchr("dDrR", *cp)) {
777*4882a593Smuzhiyun 			reset = 1;
778*4882a593Smuzhiyun 			break;
779*4882a593Smuzhiyun 		}
780*4882a593Smuzhiyun 		received++;
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 		linefeed = strchr(cp, '\n');
783*4882a593Smuzhiyun 		if (!linefeed) {
784*4882a593Smuzhiyun 			rejected++;
785*4882a593Smuzhiyun 			break;
786*4882a593Smuzhiyun 		}
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 		if (!isdigit(*cp)) {
789*4882a593Smuzhiyun 			rejected++;
790*4882a593Smuzhiyun 			cp = linefeed + 1;
791*4882a593Smuzhiyun 			continue;
792*4882a593Smuzhiyun 		}
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 		/*
795*4882a593Smuzhiyun 		 * Do not replace with kstrtoul:
796*4882a593Smuzhiyun 		 * here we need temp to be updated
797*4882a593Smuzhiyun 		 */
798*4882a593Smuzhiyun 		index = simple_strtoul(cp, &temp, 10);
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
801*4882a593Smuzhiyun 			temp++;
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 		desc_length = linefeed - temp;
804*4882a593Smuzhiyun 		curmessage = firstmessage + index;
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 		/*
807*4882a593Smuzhiyun 		 * Note the check (curmessage < firstmessage).  It is not
808*4882a593Smuzhiyun 		 * redundant.  Suppose that the user gave us an index
809*4882a593Smuzhiyun 		 * equal to ULONG_MAX - 1.  If firstmessage > 1, then
810*4882a593Smuzhiyun 		 * firstmessage + index < firstmessage!
811*4882a593Smuzhiyun 		 */
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 		if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
814*4882a593Smuzhiyun 			rejected++;
815*4882a593Smuzhiyun 			cp = linefeed + 1;
816*4882a593Smuzhiyun 			continue;
817*4882a593Smuzhiyun 		}
818*4882a593Smuzhiyun 
819*4882a593Smuzhiyun 		msg_stored = spk_msg_set(curmessage, temp, desc_length);
820*4882a593Smuzhiyun 		if (msg_stored < 0) {
821*4882a593Smuzhiyun 			retval = msg_stored;
822*4882a593Smuzhiyun 			if (msg_stored == -ENOMEM)
823*4882a593Smuzhiyun 				reset = 1;
824*4882a593Smuzhiyun 			break;
825*4882a593Smuzhiyun 		}
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 		used++;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 		cp = linefeed + 1;
830*4882a593Smuzhiyun 	}
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	if (reset)
833*4882a593Smuzhiyun 		spk_reset_msg_group(group);
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	report_msg_status(reset, received, used, rejected, group->name);
836*4882a593Smuzhiyun 	return retval;
837*4882a593Smuzhiyun }
838*4882a593Smuzhiyun 
message_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)839*4882a593Smuzhiyun static ssize_t message_show(struct kobject *kobj,
840*4882a593Smuzhiyun 			    struct kobj_attribute *attr, char *buf)
841*4882a593Smuzhiyun {
842*4882a593Smuzhiyun 	ssize_t retval = 0;
843*4882a593Smuzhiyun 	struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
844*4882a593Smuzhiyun 	unsigned long flags;
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 	if (WARN_ON(!group))
847*4882a593Smuzhiyun 		return -EINVAL;
848*4882a593Smuzhiyun 
849*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
850*4882a593Smuzhiyun 	retval = message_show_helper(buf, group->start, group->end);
851*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
852*4882a593Smuzhiyun 	return retval;
853*4882a593Smuzhiyun }
854*4882a593Smuzhiyun 
message_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)855*4882a593Smuzhiyun static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
856*4882a593Smuzhiyun 			     const char *buf, size_t count)
857*4882a593Smuzhiyun {
858*4882a593Smuzhiyun 	struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 	if (WARN_ON(!group))
861*4882a593Smuzhiyun 		return -EINVAL;
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 	return message_store_helper(buf, count, group);
864*4882a593Smuzhiyun }
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun /*
867*4882a593Smuzhiyun  * Declare the attributes.
868*4882a593Smuzhiyun  */
869*4882a593Smuzhiyun static struct kobj_attribute keymap_attribute =
870*4882a593Smuzhiyun 	__ATTR_RW(keymap);
871*4882a593Smuzhiyun static struct kobj_attribute silent_attribute =
872*4882a593Smuzhiyun 	__ATTR_WO(silent);
873*4882a593Smuzhiyun static struct kobj_attribute synth_attribute =
874*4882a593Smuzhiyun 	__ATTR_RW(synth);
875*4882a593Smuzhiyun static struct kobj_attribute synth_direct_attribute =
876*4882a593Smuzhiyun 	__ATTR_WO(synth_direct);
877*4882a593Smuzhiyun static struct kobj_attribute version_attribute =
878*4882a593Smuzhiyun 	__ATTR_RO(version);
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun static struct kobj_attribute delimiters_attribute =
881*4882a593Smuzhiyun 	__ATTR(delimiters, 0644, punc_show, punc_store);
882*4882a593Smuzhiyun static struct kobj_attribute ex_num_attribute =
883*4882a593Smuzhiyun 	__ATTR(ex_num, 0644, punc_show, punc_store);
884*4882a593Smuzhiyun static struct kobj_attribute punc_all_attribute =
885*4882a593Smuzhiyun 	__ATTR(punc_all, 0644, punc_show, punc_store);
886*4882a593Smuzhiyun static struct kobj_attribute punc_most_attribute =
887*4882a593Smuzhiyun 	__ATTR(punc_most, 0644, punc_show, punc_store);
888*4882a593Smuzhiyun static struct kobj_attribute punc_some_attribute =
889*4882a593Smuzhiyun 	__ATTR(punc_some, 0644, punc_show, punc_store);
890*4882a593Smuzhiyun static struct kobj_attribute repeats_attribute =
891*4882a593Smuzhiyun 	__ATTR(repeats, 0644, punc_show, punc_store);
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun static struct kobj_attribute attrib_bleep_attribute =
894*4882a593Smuzhiyun 	__ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);
895*4882a593Smuzhiyun static struct kobj_attribute bell_pos_attribute =
896*4882a593Smuzhiyun 	__ATTR(bell_pos, 0644, spk_var_show, spk_var_store);
897*4882a593Smuzhiyun static struct kobj_attribute bleep_time_attribute =
898*4882a593Smuzhiyun 	__ATTR(bleep_time, 0644, spk_var_show, spk_var_store);
899*4882a593Smuzhiyun static struct kobj_attribute bleeps_attribute =
900*4882a593Smuzhiyun 	__ATTR(bleeps, 0644, spk_var_show, spk_var_store);
901*4882a593Smuzhiyun static struct kobj_attribute cursor_time_attribute =
902*4882a593Smuzhiyun 	__ATTR(cursor_time, 0644, spk_var_show, spk_var_store);
903*4882a593Smuzhiyun static struct kobj_attribute key_echo_attribute =
904*4882a593Smuzhiyun 	__ATTR(key_echo, 0644, spk_var_show, spk_var_store);
905*4882a593Smuzhiyun static struct kobj_attribute no_interrupt_attribute =
906*4882a593Smuzhiyun 	__ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);
907*4882a593Smuzhiyun static struct kobj_attribute punc_level_attribute =
908*4882a593Smuzhiyun 	__ATTR(punc_level, 0644, spk_var_show, spk_var_store);
909*4882a593Smuzhiyun static struct kobj_attribute reading_punc_attribute =
910*4882a593Smuzhiyun 	__ATTR(reading_punc, 0644, spk_var_show, spk_var_store);
911*4882a593Smuzhiyun static struct kobj_attribute say_control_attribute =
912*4882a593Smuzhiyun 	__ATTR(say_control, 0644, spk_var_show, spk_var_store);
913*4882a593Smuzhiyun static struct kobj_attribute say_word_ctl_attribute =
914*4882a593Smuzhiyun 	__ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
915*4882a593Smuzhiyun static struct kobj_attribute spell_delay_attribute =
916*4882a593Smuzhiyun 	__ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun /*
919*4882a593Smuzhiyun  * These attributes are i18n related.
920*4882a593Smuzhiyun  */
921*4882a593Smuzhiyun static struct kobj_attribute announcements_attribute =
922*4882a593Smuzhiyun 	__ATTR(announcements, 0644, message_show, message_store);
923*4882a593Smuzhiyun static struct kobj_attribute characters_attribute =
924*4882a593Smuzhiyun 	__ATTR(characters, 0644, chars_chartab_show,
925*4882a593Smuzhiyun 	       chars_chartab_store);
926*4882a593Smuzhiyun static struct kobj_attribute chartab_attribute =
927*4882a593Smuzhiyun 	__ATTR(chartab, 0644, chars_chartab_show,
928*4882a593Smuzhiyun 	       chars_chartab_store);
929*4882a593Smuzhiyun static struct kobj_attribute ctl_keys_attribute =
930*4882a593Smuzhiyun 	__ATTR(ctl_keys, 0644, message_show, message_store);
931*4882a593Smuzhiyun static struct kobj_attribute colors_attribute =
932*4882a593Smuzhiyun 	__ATTR(colors, 0644, message_show, message_store);
933*4882a593Smuzhiyun static struct kobj_attribute formatted_attribute =
934*4882a593Smuzhiyun 	__ATTR(formatted, 0644, message_show, message_store);
935*4882a593Smuzhiyun static struct kobj_attribute function_names_attribute =
936*4882a593Smuzhiyun 	__ATTR(function_names, 0644, message_show, message_store);
937*4882a593Smuzhiyun static struct kobj_attribute key_names_attribute =
938*4882a593Smuzhiyun 	__ATTR(key_names, 0644, message_show, message_store);
939*4882a593Smuzhiyun static struct kobj_attribute states_attribute =
940*4882a593Smuzhiyun 	__ATTR(states, 0644, message_show, message_store);
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun /*
943*4882a593Smuzhiyun  * Create groups of attributes so that we can create and destroy them all
944*4882a593Smuzhiyun  * at once.
945*4882a593Smuzhiyun  */
946*4882a593Smuzhiyun static struct attribute *main_attrs[] = {
947*4882a593Smuzhiyun 	&keymap_attribute.attr,
948*4882a593Smuzhiyun 	&silent_attribute.attr,
949*4882a593Smuzhiyun 	&synth_attribute.attr,
950*4882a593Smuzhiyun 	&synth_direct_attribute.attr,
951*4882a593Smuzhiyun 	&version_attribute.attr,
952*4882a593Smuzhiyun 	&delimiters_attribute.attr,
953*4882a593Smuzhiyun 	&ex_num_attribute.attr,
954*4882a593Smuzhiyun 	&punc_all_attribute.attr,
955*4882a593Smuzhiyun 	&punc_most_attribute.attr,
956*4882a593Smuzhiyun 	&punc_some_attribute.attr,
957*4882a593Smuzhiyun 	&repeats_attribute.attr,
958*4882a593Smuzhiyun 	&attrib_bleep_attribute.attr,
959*4882a593Smuzhiyun 	&bell_pos_attribute.attr,
960*4882a593Smuzhiyun 	&bleep_time_attribute.attr,
961*4882a593Smuzhiyun 	&bleeps_attribute.attr,
962*4882a593Smuzhiyun 	&cursor_time_attribute.attr,
963*4882a593Smuzhiyun 	&key_echo_attribute.attr,
964*4882a593Smuzhiyun 	&no_interrupt_attribute.attr,
965*4882a593Smuzhiyun 	&punc_level_attribute.attr,
966*4882a593Smuzhiyun 	&reading_punc_attribute.attr,
967*4882a593Smuzhiyun 	&say_control_attribute.attr,
968*4882a593Smuzhiyun 	&say_word_ctl_attribute.attr,
969*4882a593Smuzhiyun 	&spell_delay_attribute.attr,
970*4882a593Smuzhiyun 	NULL,
971*4882a593Smuzhiyun };
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun static struct attribute *i18n_attrs[] = {
974*4882a593Smuzhiyun 	&announcements_attribute.attr,
975*4882a593Smuzhiyun 	&characters_attribute.attr,
976*4882a593Smuzhiyun 	&chartab_attribute.attr,
977*4882a593Smuzhiyun 	&ctl_keys_attribute.attr,
978*4882a593Smuzhiyun 	&colors_attribute.attr,
979*4882a593Smuzhiyun 	&formatted_attribute.attr,
980*4882a593Smuzhiyun 	&function_names_attribute.attr,
981*4882a593Smuzhiyun 	&key_names_attribute.attr,
982*4882a593Smuzhiyun 	&states_attribute.attr,
983*4882a593Smuzhiyun 	NULL,
984*4882a593Smuzhiyun };
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun /*
987*4882a593Smuzhiyun  * An unnamed attribute group will put all of the attributes directly in
988*4882a593Smuzhiyun  * the kobject directory.  If we specify a name, a subdirectory will be
989*4882a593Smuzhiyun  * created for the attributes with the directory being the name of the
990*4882a593Smuzhiyun  * attribute group.
991*4882a593Smuzhiyun  */
992*4882a593Smuzhiyun static const struct attribute_group main_attr_group = {
993*4882a593Smuzhiyun 	.attrs = main_attrs,
994*4882a593Smuzhiyun };
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun static const struct attribute_group i18n_attr_group = {
997*4882a593Smuzhiyun 	.attrs = i18n_attrs,
998*4882a593Smuzhiyun 	.name = "i18n",
999*4882a593Smuzhiyun };
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun static struct kobject *accessibility_kobj;
1002*4882a593Smuzhiyun struct kobject *speakup_kobj;
1003*4882a593Smuzhiyun 
speakup_kobj_init(void)1004*4882a593Smuzhiyun int speakup_kobj_init(void)
1005*4882a593Smuzhiyun {
1006*4882a593Smuzhiyun 	int retval;
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	/*
1009*4882a593Smuzhiyun 	 * Create a simple kobject with the name of "accessibility",
1010*4882a593Smuzhiyun 	 * located under /sys/
1011*4882a593Smuzhiyun 	 *
1012*4882a593Smuzhiyun 	 * As this is a simple directory, no uevent will be sent to
1013*4882a593Smuzhiyun 	 * userspace.  That is why this function should not be used for
1014*4882a593Smuzhiyun 	 * any type of dynamic kobjects, where the name and number are
1015*4882a593Smuzhiyun 	 * not known ahead of time.
1016*4882a593Smuzhiyun 	 */
1017*4882a593Smuzhiyun 	accessibility_kobj = kobject_create_and_add("accessibility", NULL);
1018*4882a593Smuzhiyun 	if (!accessibility_kobj) {
1019*4882a593Smuzhiyun 		retval = -ENOMEM;
1020*4882a593Smuzhiyun 		goto out;
1021*4882a593Smuzhiyun 	}
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun 	speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj);
1024*4882a593Smuzhiyun 	if (!speakup_kobj) {
1025*4882a593Smuzhiyun 		retval = -ENOMEM;
1026*4882a593Smuzhiyun 		goto err_acc;
1027*4882a593Smuzhiyun 	}
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	/* Create the files associated with this kobject */
1030*4882a593Smuzhiyun 	retval = sysfs_create_group(speakup_kobj, &main_attr_group);
1031*4882a593Smuzhiyun 	if (retval)
1032*4882a593Smuzhiyun 		goto err_speakup;
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	retval = sysfs_create_group(speakup_kobj, &i18n_attr_group);
1035*4882a593Smuzhiyun 	if (retval)
1036*4882a593Smuzhiyun 		goto err_group;
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	goto out;
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun err_group:
1041*4882a593Smuzhiyun 	sysfs_remove_group(speakup_kobj, &main_attr_group);
1042*4882a593Smuzhiyun err_speakup:
1043*4882a593Smuzhiyun 	kobject_put(speakup_kobj);
1044*4882a593Smuzhiyun err_acc:
1045*4882a593Smuzhiyun 	kobject_put(accessibility_kobj);
1046*4882a593Smuzhiyun out:
1047*4882a593Smuzhiyun 	return retval;
1048*4882a593Smuzhiyun }
1049*4882a593Smuzhiyun 
speakup_kobj_exit(void)1050*4882a593Smuzhiyun void speakup_kobj_exit(void)
1051*4882a593Smuzhiyun {
1052*4882a593Smuzhiyun 	sysfs_remove_group(speakup_kobj, &i18n_attr_group);
1053*4882a593Smuzhiyun 	sysfs_remove_group(speakup_kobj, &main_attr_group);
1054*4882a593Smuzhiyun 	kobject_put(speakup_kobj);
1055*4882a593Smuzhiyun 	kobject_put(accessibility_kobj);
1056*4882a593Smuzhiyun }
1057