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