xref: /openbmc/linux/drivers/accessibility/speakup/kobjects.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
12067fd92SSamuel Thibault // SPDX-License-Identifier: GPL-2.0
22067fd92SSamuel Thibault /*
32067fd92SSamuel Thibault  * Speakup kobject implementation
42067fd92SSamuel Thibault  *
52067fd92SSamuel Thibault  * Copyright (C) 2009 William Hubbs
62067fd92SSamuel Thibault  *
72067fd92SSamuel Thibault  * This code is based on kobject-example.c, which came with linux 2.6.x.
82067fd92SSamuel Thibault  *
92067fd92SSamuel Thibault  * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
102067fd92SSamuel Thibault  * Copyright (C) 2007 Novell Inc.
112067fd92SSamuel Thibault  *
122067fd92SSamuel Thibault  * Released under the GPL version 2 only.
132067fd92SSamuel Thibault  *
142067fd92SSamuel Thibault  */
152067fd92SSamuel Thibault #include <linux/slab.h>		/* For kmalloc. */
162067fd92SSamuel Thibault #include <linux/kernel.h>
172067fd92SSamuel Thibault #include <linux/kobject.h>
182067fd92SSamuel Thibault #include <linux/string.h>
192067fd92SSamuel Thibault #include <linux/string_helpers.h>
202067fd92SSamuel Thibault #include <linux/sysfs.h>
212067fd92SSamuel Thibault #include <linux/ctype.h>
222067fd92SSamuel Thibault 
232067fd92SSamuel Thibault #include "speakup.h"
242067fd92SSamuel Thibault #include "spk_priv.h"
252067fd92SSamuel Thibault 
262067fd92SSamuel Thibault /*
272067fd92SSamuel Thibault  * This is called when a user reads the characters or chartab sys file.
282067fd92SSamuel Thibault  */
chars_chartab_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)292067fd92SSamuel Thibault static ssize_t chars_chartab_show(struct kobject *kobj,
302067fd92SSamuel Thibault 				  struct kobj_attribute *attr, char *buf)
312067fd92SSamuel Thibault {
322067fd92SSamuel Thibault 	int i;
332067fd92SSamuel Thibault 	int len = 0;
342067fd92SSamuel Thibault 	char *cp;
352067fd92SSamuel Thibault 	char *buf_pointer = buf;
362067fd92SSamuel Thibault 	size_t bufsize = PAGE_SIZE;
372067fd92SSamuel Thibault 	unsigned long flags;
382067fd92SSamuel Thibault 
392067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
402067fd92SSamuel Thibault 	*buf_pointer = '\0';
412067fd92SSamuel Thibault 	for (i = 0; i < 256; i++) {
422067fd92SSamuel Thibault 		if (bufsize <= 1)
432067fd92SSamuel Thibault 			break;
442067fd92SSamuel Thibault 		if (strcmp("characters", attr->attr.name) == 0) {
452067fd92SSamuel Thibault 			len = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
462067fd92SSamuel Thibault 					i, spk_characters[i]);
472067fd92SSamuel Thibault 		} else {	/* show chartab entry */
482067fd92SSamuel Thibault 			if (IS_TYPE(i, B_CTL))
492067fd92SSamuel Thibault 				cp = "B_CTL";
502067fd92SSamuel Thibault 			else if (IS_TYPE(i, WDLM))
512067fd92SSamuel Thibault 				cp = "WDLM";
522067fd92SSamuel Thibault 			else if (IS_TYPE(i, A_PUNC))
532067fd92SSamuel Thibault 				cp = "A_PUNC";
542067fd92SSamuel Thibault 			else if (IS_TYPE(i, PUNC))
552067fd92SSamuel Thibault 				cp = "PUNC";
562067fd92SSamuel Thibault 			else if (IS_TYPE(i, NUM))
572067fd92SSamuel Thibault 				cp = "NUM";
582067fd92SSamuel Thibault 			else if (IS_TYPE(i, A_CAP))
592067fd92SSamuel Thibault 				cp = "A_CAP";
602067fd92SSamuel Thibault 			else if (IS_TYPE(i, ALPHA))
612067fd92SSamuel Thibault 				cp = "ALPHA";
622067fd92SSamuel Thibault 			else if (IS_TYPE(i, B_CAPSYM))
632067fd92SSamuel Thibault 				cp = "B_CAPSYM";
642067fd92SSamuel Thibault 			else if (IS_TYPE(i, B_SYM))
652067fd92SSamuel Thibault 				cp = "B_SYM";
662067fd92SSamuel Thibault 			else
672067fd92SSamuel Thibault 				cp = "0";
682067fd92SSamuel Thibault 			len =
692067fd92SSamuel Thibault 			    scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp);
702067fd92SSamuel Thibault 		}
712067fd92SSamuel Thibault 		bufsize -= len;
722067fd92SSamuel Thibault 		buf_pointer += len;
732067fd92SSamuel Thibault 	}
742067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
752067fd92SSamuel Thibault 	return buf_pointer - buf;
762067fd92SSamuel Thibault }
772067fd92SSamuel Thibault 
782067fd92SSamuel Thibault /*
792067fd92SSamuel Thibault  * Print informational messages or warnings after updating
802067fd92SSamuel Thibault  * character descriptions or chartab entries.
812067fd92SSamuel Thibault  */
report_char_chartab_status(int reset,int received,int used,int rejected,int do_characters)822067fd92SSamuel Thibault static void report_char_chartab_status(int reset, int received, int used,
832067fd92SSamuel Thibault 				       int rejected, int do_characters)
842067fd92SSamuel Thibault {
852067fd92SSamuel Thibault 	static char const *object_type[] = {
862067fd92SSamuel Thibault 		"character class entries",
872067fd92SSamuel Thibault 		"character descriptions",
882067fd92SSamuel Thibault 	};
892067fd92SSamuel Thibault 	int len;
902067fd92SSamuel Thibault 	char buf[80];
912067fd92SSamuel Thibault 
922067fd92SSamuel Thibault 	if (reset) {
932067fd92SSamuel Thibault 		pr_info("%s reset to defaults\n", object_type[do_characters]);
942067fd92SSamuel Thibault 	} else if (received) {
952067fd92SSamuel Thibault 		len = snprintf(buf, sizeof(buf),
962067fd92SSamuel Thibault 			       " updated %d of %d %s\n",
972067fd92SSamuel Thibault 			       used, received, object_type[do_characters]);
982067fd92SSamuel Thibault 		if (rejected)
992067fd92SSamuel Thibault 			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
1002067fd92SSamuel Thibault 				 " with %d reject%s\n",
1012067fd92SSamuel Thibault 				 rejected, rejected > 1 ? "s" : "");
1022067fd92SSamuel Thibault 		pr_info("%s", buf);
1032067fd92SSamuel Thibault 	}
1042067fd92SSamuel Thibault }
1052067fd92SSamuel Thibault 
1062067fd92SSamuel Thibault /*
1072067fd92SSamuel Thibault  * This is called when a user changes the characters or chartab parameters.
1082067fd92SSamuel Thibault  */
chars_chartab_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)1092067fd92SSamuel Thibault static ssize_t chars_chartab_store(struct kobject *kobj,
1102067fd92SSamuel Thibault 				   struct kobj_attribute *attr,
1112067fd92SSamuel Thibault 				   const char *buf, size_t count)
1122067fd92SSamuel Thibault {
1132067fd92SSamuel Thibault 	char *cp = (char *)buf;
1142067fd92SSamuel Thibault 	char *end = cp + count; /* the null at the end of the buffer */
1152067fd92SSamuel Thibault 	char *linefeed = NULL;
1162067fd92SSamuel Thibault 	char keyword[MAX_DESC_LEN + 1];
1172067fd92SSamuel Thibault 	char *outptr = NULL;	/* Will hold keyword or desc. */
1182067fd92SSamuel Thibault 	char *temp = NULL;
1192067fd92SSamuel Thibault 	char *desc = NULL;
1202067fd92SSamuel Thibault 	ssize_t retval = count;
1212067fd92SSamuel Thibault 	unsigned long flags;
1222067fd92SSamuel Thibault 	unsigned long index = 0;
1232067fd92SSamuel Thibault 	int charclass = 0;
1242067fd92SSamuel Thibault 	int received = 0;
1252067fd92SSamuel Thibault 	int used = 0;
1262067fd92SSamuel Thibault 	int rejected = 0;
1272067fd92SSamuel Thibault 	int reset = 0;
1282067fd92SSamuel Thibault 	int do_characters = !strcmp(attr->attr.name, "characters");
1292067fd92SSamuel Thibault 	size_t desc_length = 0;
1302067fd92SSamuel Thibault 	int i;
1312067fd92SSamuel Thibault 
1322067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1332067fd92SSamuel Thibault 	while (cp < end) {
1342067fd92SSamuel Thibault 		while ((cp < end) && (*cp == ' ' || *cp == '\t'))
1352067fd92SSamuel Thibault 			cp++;
1362067fd92SSamuel Thibault 
1372067fd92SSamuel Thibault 		if (cp == end)
1382067fd92SSamuel Thibault 			break;
1392067fd92SSamuel Thibault 		if ((*cp == '\n') || strchr("dDrR", *cp)) {
1402067fd92SSamuel Thibault 			reset = 1;
1412067fd92SSamuel Thibault 			break;
1422067fd92SSamuel Thibault 		}
1432067fd92SSamuel Thibault 		received++;
1442067fd92SSamuel Thibault 
1452067fd92SSamuel Thibault 		linefeed = strchr(cp, '\n');
1462067fd92SSamuel Thibault 		if (!linefeed) {
1472067fd92SSamuel Thibault 			rejected++;
1482067fd92SSamuel Thibault 			break;
1492067fd92SSamuel Thibault 		}
1502067fd92SSamuel Thibault 
1512067fd92SSamuel Thibault 		if (!isdigit(*cp)) {
1522067fd92SSamuel Thibault 			rejected++;
1532067fd92SSamuel Thibault 			cp = linefeed + 1;
1542067fd92SSamuel Thibault 			continue;
1552067fd92SSamuel Thibault 		}
1562067fd92SSamuel Thibault 
1572067fd92SSamuel Thibault 		/*
1582067fd92SSamuel Thibault 		 * Do not replace with kstrtoul:
1592067fd92SSamuel Thibault 		 * here we need temp to be updated
1602067fd92SSamuel Thibault 		 */
1612067fd92SSamuel Thibault 		index = simple_strtoul(cp, &temp, 10);
1622067fd92SSamuel Thibault 		if (index > 255) {
1632067fd92SSamuel Thibault 			rejected++;
1642067fd92SSamuel Thibault 			cp = linefeed + 1;
1652067fd92SSamuel Thibault 			continue;
1662067fd92SSamuel Thibault 		}
1672067fd92SSamuel Thibault 
1682067fd92SSamuel Thibault 		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
1692067fd92SSamuel Thibault 			temp++;
1702067fd92SSamuel Thibault 
1712067fd92SSamuel Thibault 		desc_length = linefeed - temp;
1722067fd92SSamuel Thibault 		if (desc_length > MAX_DESC_LEN) {
1732067fd92SSamuel Thibault 			rejected++;
1742067fd92SSamuel Thibault 			cp = linefeed + 1;
1752067fd92SSamuel Thibault 			continue;
1762067fd92SSamuel Thibault 		}
1772067fd92SSamuel Thibault 		if (do_characters) {
1782067fd92SSamuel Thibault 			desc = kmalloc(desc_length + 1, GFP_ATOMIC);
1792067fd92SSamuel Thibault 			if (!desc) {
1802067fd92SSamuel Thibault 				retval = -ENOMEM;
1812067fd92SSamuel Thibault 				reset = 1;	/* just reset on error. */
1822067fd92SSamuel Thibault 				break;
1832067fd92SSamuel Thibault 			}
1842067fd92SSamuel Thibault 			outptr = desc;
1852067fd92SSamuel Thibault 		} else {
1862067fd92SSamuel Thibault 			outptr = keyword;
1872067fd92SSamuel Thibault 		}
1882067fd92SSamuel Thibault 
1892067fd92SSamuel Thibault 		for (i = 0; i < desc_length; i++)
1902067fd92SSamuel Thibault 			outptr[i] = temp[i];
1912067fd92SSamuel Thibault 		outptr[desc_length] = '\0';
1922067fd92SSamuel Thibault 
1932067fd92SSamuel Thibault 		if (do_characters) {
1942067fd92SSamuel Thibault 			if (spk_characters[index] != spk_default_chars[index])
1952067fd92SSamuel Thibault 				kfree(spk_characters[index]);
1962067fd92SSamuel Thibault 			spk_characters[index] = desc;
1972067fd92SSamuel Thibault 			used++;
1982067fd92SSamuel Thibault 		} else {
1992067fd92SSamuel Thibault 			charclass = spk_chartab_get_value(keyword);
2002067fd92SSamuel Thibault 			if (charclass == 0) {
2012067fd92SSamuel Thibault 				rejected++;
2022067fd92SSamuel Thibault 				cp = linefeed + 1;
2032067fd92SSamuel Thibault 				continue;
2042067fd92SSamuel Thibault 			}
2052067fd92SSamuel Thibault 			if (charclass != spk_chartab[index]) {
2062067fd92SSamuel Thibault 				spk_chartab[index] = charclass;
2072067fd92SSamuel Thibault 				used++;
2082067fd92SSamuel Thibault 			}
2092067fd92SSamuel Thibault 		}
2102067fd92SSamuel Thibault 		cp = linefeed + 1;
2112067fd92SSamuel Thibault 	}
2122067fd92SSamuel Thibault 
2132067fd92SSamuel Thibault 	if (reset) {
2142067fd92SSamuel Thibault 		if (do_characters)
2152067fd92SSamuel Thibault 			spk_reset_default_chars();
2162067fd92SSamuel Thibault 		else
2172067fd92SSamuel Thibault 			spk_reset_default_chartab();
2182067fd92SSamuel Thibault 	}
2192067fd92SSamuel Thibault 
2202067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2212067fd92SSamuel Thibault 	report_char_chartab_status(reset, received, used, rejected,
2222067fd92SSamuel Thibault 				   do_characters);
2232067fd92SSamuel Thibault 	return retval;
2242067fd92SSamuel Thibault }
2252067fd92SSamuel Thibault 
2262067fd92SSamuel Thibault /*
2272067fd92SSamuel Thibault  * This is called when a user reads the keymap parameter.
2282067fd92SSamuel Thibault  */
keymap_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)2292067fd92SSamuel Thibault static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr,
2302067fd92SSamuel Thibault 			   char *buf)
2312067fd92SSamuel Thibault {
2322067fd92SSamuel Thibault 	char *cp = buf;
2332067fd92SSamuel Thibault 	int i;
2342067fd92SSamuel Thibault 	int n;
2352067fd92SSamuel Thibault 	int num_keys;
2362067fd92SSamuel Thibault 	int nstates;
2372067fd92SSamuel Thibault 	u_char *cp1;
2382067fd92SSamuel Thibault 	u_char ch;
2392067fd92SSamuel Thibault 	unsigned long flags;
2402067fd92SSamuel Thibault 
2412067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
2422067fd92SSamuel Thibault 	cp1 = spk_key_buf + SHIFT_TBL_SIZE;
2432067fd92SSamuel Thibault 	num_keys = (int)(*cp1);
2442067fd92SSamuel Thibault 	nstates = (int)cp1[1];
2452067fd92SSamuel Thibault 	cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates);
2462067fd92SSamuel Thibault 	cp1 += 2; /* now pointing at shift states */
2472067fd92SSamuel Thibault 	/* dump num_keys+1 as first row is shift states + flags,
2482067fd92SSamuel Thibault 	 * each subsequent row is key + states
2492067fd92SSamuel Thibault 	 */
2502067fd92SSamuel Thibault 	for (n = 0; n <= num_keys; n++) {
2512067fd92SSamuel Thibault 		for (i = 0; i <= nstates; i++) {
2522067fd92SSamuel Thibault 			ch = *cp1++;
2532067fd92SSamuel Thibault 			cp += sprintf(cp, "%d,", (int)ch);
2542067fd92SSamuel Thibault 			*cp++ = (i < nstates) ? SPACE : '\n';
2552067fd92SSamuel Thibault 		}
2562067fd92SSamuel Thibault 	}
2572067fd92SSamuel Thibault 	cp += sprintf(cp, "0, %d\n", KEY_MAP_VER);
2582067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2592067fd92SSamuel Thibault 	return (int)(cp - buf);
2602067fd92SSamuel Thibault }
2612067fd92SSamuel Thibault 
2622067fd92SSamuel Thibault /*
2632067fd92SSamuel Thibault  * This is called when a user changes the keymap parameter.
2642067fd92SSamuel Thibault  */
keymap_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)2652067fd92SSamuel Thibault static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr,
2662067fd92SSamuel Thibault 			    const char *buf, size_t count)
2672067fd92SSamuel Thibault {
2682067fd92SSamuel Thibault 	int i;
2692067fd92SSamuel Thibault 	ssize_t ret = count;
2702067fd92SSamuel Thibault 	char *in_buff = NULL;
2712067fd92SSamuel Thibault 	char *cp;
2722067fd92SSamuel Thibault 	u_char *cp1;
2732067fd92SSamuel Thibault 	unsigned long flags;
2742067fd92SSamuel Thibault 
2752067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
2762067fd92SSamuel Thibault 	in_buff = kmemdup(buf, count + 1, GFP_ATOMIC);
2772067fd92SSamuel Thibault 	if (!in_buff) {
2782067fd92SSamuel Thibault 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2792067fd92SSamuel Thibault 		return -ENOMEM;
2802067fd92SSamuel Thibault 	}
2812067fd92SSamuel Thibault 	if (strchr("dDrR", *in_buff)) {
2822067fd92SSamuel Thibault 		spk_set_key_info(spk_key_defaults, spk_key_buf);
2832067fd92SSamuel Thibault 		pr_info("keymap set to default values\n");
2842067fd92SSamuel Thibault 		kfree(in_buff);
2852067fd92SSamuel Thibault 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2862067fd92SSamuel Thibault 		return count;
2872067fd92SSamuel Thibault 	}
2882067fd92SSamuel Thibault 	if (in_buff[count - 1] == '\n')
2892067fd92SSamuel Thibault 		in_buff[count - 1] = '\0';
2902067fd92SSamuel Thibault 	cp = in_buff;
2912067fd92SSamuel Thibault 	cp1 = (u_char *)in_buff;
2922067fd92SSamuel Thibault 	for (i = 0; i < 3; i++) {
2932067fd92SSamuel Thibault 		cp = spk_s2uchar(cp, cp1);
2942067fd92SSamuel Thibault 		cp1++;
2952067fd92SSamuel Thibault 	}
2962067fd92SSamuel Thibault 	i = (int)cp1[-2] + 1;
2972067fd92SSamuel Thibault 	i *= (int)cp1[-1] + 1;
2982067fd92SSamuel Thibault 	i += 2; /* 0 and last map ver */
2992067fd92SSamuel Thibault 	if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||
3002067fd92SSamuel Thibault 	    i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
3012067fd92SSamuel Thibault 		pr_warn("i %d %d %d %d\n", i,
3022067fd92SSamuel Thibault 			(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
3032067fd92SSamuel Thibault 		kfree(in_buff);
3042067fd92SSamuel Thibault 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3052067fd92SSamuel Thibault 		return -EINVAL;
3062067fd92SSamuel Thibault 	}
3072067fd92SSamuel Thibault 	while (--i >= 0) {
3082067fd92SSamuel Thibault 		cp = spk_s2uchar(cp, cp1);
3092067fd92SSamuel Thibault 		cp1++;
3102067fd92SSamuel Thibault 		if (!(*cp))
3112067fd92SSamuel Thibault 			break;
3122067fd92SSamuel Thibault 	}
3132067fd92SSamuel Thibault 	if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {
3142067fd92SSamuel Thibault 		ret = -EINVAL;
3152067fd92SSamuel Thibault 		pr_warn("end %d %d %d %d\n", i,
3162067fd92SSamuel Thibault 			(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
3172067fd92SSamuel Thibault 	} else {
3182067fd92SSamuel Thibault 		if (spk_set_key_info(in_buff, spk_key_buf)) {
3192067fd92SSamuel Thibault 			spk_set_key_info(spk_key_defaults, spk_key_buf);
3202067fd92SSamuel Thibault 			ret = -EINVAL;
3212067fd92SSamuel Thibault 			pr_warn("set key failed\n");
3222067fd92SSamuel Thibault 		}
3232067fd92SSamuel Thibault 	}
3242067fd92SSamuel Thibault 	kfree(in_buff);
3252067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3262067fd92SSamuel Thibault 	return ret;
3272067fd92SSamuel Thibault }
3282067fd92SSamuel Thibault 
3292067fd92SSamuel Thibault /*
3302067fd92SSamuel Thibault  * This is called when a user changes the value of the silent parameter.
3312067fd92SSamuel Thibault  */
silent_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3322067fd92SSamuel Thibault static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,
3332067fd92SSamuel Thibault 			    const char *buf, size_t count)
3342067fd92SSamuel Thibault {
3352067fd92SSamuel Thibault 	int len;
3362067fd92SSamuel Thibault 	struct vc_data *vc = vc_cons[fg_console].d;
3372067fd92SSamuel Thibault 	char ch = 0;
3382067fd92SSamuel Thibault 	char shut;
3392067fd92SSamuel Thibault 	unsigned long flags;
3402067fd92SSamuel Thibault 
3412067fd92SSamuel Thibault 	len = strlen(buf);
3422067fd92SSamuel Thibault 	if (len > 0 && len < 3) {
3432067fd92SSamuel Thibault 		ch = buf[0];
3442067fd92SSamuel Thibault 		if (ch == '\n')
3452067fd92SSamuel Thibault 			ch = '0';
3462067fd92SSamuel Thibault 	}
3472067fd92SSamuel Thibault 	if (ch < '0' || ch > '7') {
3482067fd92SSamuel Thibault 		pr_warn("silent value '%c' not in range (0,7)\n", ch);
3492067fd92SSamuel Thibault 		return -EINVAL;
3502067fd92SSamuel Thibault 	}
3512067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
3522067fd92SSamuel Thibault 	if (ch & 2) {
3532067fd92SSamuel Thibault 		shut = 1;
3542067fd92SSamuel Thibault 		spk_do_flush();
3552067fd92SSamuel Thibault 	} else {
3562067fd92SSamuel Thibault 		shut = 0;
3572067fd92SSamuel Thibault 	}
3582067fd92SSamuel Thibault 	if (ch & 4)
3592067fd92SSamuel Thibault 		shut |= 0x40;
3602067fd92SSamuel Thibault 	if (ch & 1)
3612067fd92SSamuel Thibault 		spk_shut_up |= shut;
3622067fd92SSamuel Thibault 	else
3632067fd92SSamuel Thibault 		spk_shut_up &= ~shut;
3642067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3652067fd92SSamuel Thibault 	return count;
3662067fd92SSamuel Thibault }
3672067fd92SSamuel Thibault 
3682067fd92SSamuel Thibault /*
3692067fd92SSamuel Thibault  * This is called when a user reads the synth setting.
3702067fd92SSamuel Thibault  */
synth_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)3712067fd92SSamuel Thibault static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,
3722067fd92SSamuel Thibault 			  char *buf)
3732067fd92SSamuel Thibault {
3742067fd92SSamuel Thibault 	int rv;
3752067fd92SSamuel Thibault 
3762067fd92SSamuel Thibault 	if (!synth)
3772067fd92SSamuel Thibault 		rv = sprintf(buf, "%s\n", "none");
3782067fd92SSamuel Thibault 	else
3792067fd92SSamuel Thibault 		rv = sprintf(buf, "%s\n", synth->name);
3802067fd92SSamuel Thibault 	return rv;
3812067fd92SSamuel Thibault }
3822067fd92SSamuel Thibault 
3832067fd92SSamuel Thibault /*
3842067fd92SSamuel Thibault  * This is called when a user requests to change synthesizers.
3852067fd92SSamuel Thibault  */
synth_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3862067fd92SSamuel Thibault static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,
3872067fd92SSamuel Thibault 			   const char *buf, size_t count)
3882067fd92SSamuel Thibault {
3892067fd92SSamuel Thibault 	int len;
3902067fd92SSamuel Thibault 	char new_synth_name[10];
3912067fd92SSamuel Thibault 
3922067fd92SSamuel Thibault 	len = strlen(buf);
3932067fd92SSamuel Thibault 	if (len < 2 || len > 9)
3942067fd92SSamuel Thibault 		return -EINVAL;
3952067fd92SSamuel Thibault 	memcpy(new_synth_name, buf, len);
3962067fd92SSamuel Thibault 	if (new_synth_name[len - 1] == '\n')
3972067fd92SSamuel Thibault 		len--;
3982067fd92SSamuel Thibault 	new_synth_name[len] = '\0';
3992067fd92SSamuel Thibault 	spk_strlwr(new_synth_name);
4002067fd92SSamuel Thibault 	if (synth && !strcmp(new_synth_name, synth->name)) {
4012067fd92SSamuel Thibault 		pr_warn("%s already in use\n", new_synth_name);
4022067fd92SSamuel Thibault 	} else if (synth_init(new_synth_name) != 0) {
4032067fd92SSamuel Thibault 		pr_warn("failed to init synth %s\n", new_synth_name);
4042067fd92SSamuel Thibault 		return -ENODEV;
4052067fd92SSamuel Thibault 	}
4062067fd92SSamuel Thibault 	return count;
4072067fd92SSamuel Thibault }
4082067fd92SSamuel Thibault 
4092067fd92SSamuel Thibault /*
4102067fd92SSamuel Thibault  * This is called when text is sent to the synth via the synth_direct file.
4112067fd92SSamuel Thibault  */
synth_direct_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)4122067fd92SSamuel Thibault static ssize_t synth_direct_store(struct kobject *kobj,
4132067fd92SSamuel Thibault 				  struct kobj_attribute *attr,
4142067fd92SSamuel Thibault 				  const char *buf, size_t count)
4152067fd92SSamuel Thibault {
4162067fd92SSamuel Thibault 	u_char tmp[256];
4172067fd92SSamuel Thibault 	int len;
4182067fd92SSamuel Thibault 	int bytes;
4192067fd92SSamuel Thibault 	const char *ptr = buf;
4202067fd92SSamuel Thibault 	unsigned long flags;
4212067fd92SSamuel Thibault 
4222067fd92SSamuel Thibault 	if (!synth)
4232067fd92SSamuel Thibault 		return -EPERM;
4242067fd92SSamuel Thibault 
4252067fd92SSamuel Thibault 	len = strlen(buf);
4262067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
4272067fd92SSamuel Thibault 	while (len > 0) {
4282067fd92SSamuel Thibault 		bytes = min_t(size_t, len, 250);
4292067fd92SSamuel Thibault 		strncpy(tmp, ptr, bytes);
4302067fd92SSamuel Thibault 		tmp[bytes] = '\0';
4312067fd92SSamuel Thibault 		string_unescape_any_inplace(tmp);
4322067fd92SSamuel Thibault 		synth_printf("%s", tmp);
4332067fd92SSamuel Thibault 		ptr += bytes;
4342067fd92SSamuel Thibault 		len -= bytes;
4352067fd92SSamuel Thibault 	}
4362067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
4372067fd92SSamuel Thibault 	return count;
4382067fd92SSamuel Thibault }
4392067fd92SSamuel Thibault 
4402067fd92SSamuel Thibault /*
4412067fd92SSamuel Thibault  * This function is called when a user reads the version.
4422067fd92SSamuel Thibault  */
version_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4432067fd92SSamuel Thibault static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
4442067fd92SSamuel Thibault 			    char *buf)
4452067fd92SSamuel Thibault {
4462067fd92SSamuel Thibault 	char *cp;
4472067fd92SSamuel Thibault 
4482067fd92SSamuel Thibault 	cp = buf;
4492067fd92SSamuel Thibault 	cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
4502067fd92SSamuel Thibault 	if (synth)
4512067fd92SSamuel Thibault 		cp += sprintf(cp, "%s synthesizer driver version %s\n",
4522067fd92SSamuel Thibault 		synth->name, synth->version);
4532067fd92SSamuel Thibault 	return cp - buf;
4542067fd92SSamuel Thibault }
4552067fd92SSamuel Thibault 
4562067fd92SSamuel Thibault /*
4572067fd92SSamuel Thibault  * This is called when a user reads the punctuation settings.
4582067fd92SSamuel Thibault  */
punc_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4592067fd92SSamuel Thibault static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
4602067fd92SSamuel Thibault 			 char *buf)
4612067fd92SSamuel Thibault {
4622067fd92SSamuel Thibault 	int i;
4632067fd92SSamuel Thibault 	char *cp = buf;
4642067fd92SSamuel Thibault 	struct st_var_header *p_header;
4652067fd92SSamuel Thibault 	struct punc_var_t *var;
4662067fd92SSamuel Thibault 	struct st_bits_data *pb;
4672067fd92SSamuel Thibault 	short mask;
4682067fd92SSamuel Thibault 	unsigned long flags;
4692067fd92SSamuel Thibault 
4702067fd92SSamuel Thibault 	p_header = spk_var_header_by_name(attr->attr.name);
4712067fd92SSamuel Thibault 	if (!p_header) {
4722067fd92SSamuel Thibault 		pr_warn("p_header is null, attr->attr.name is %s\n",
4732067fd92SSamuel Thibault 			attr->attr.name);
4742067fd92SSamuel Thibault 		return -EINVAL;
4752067fd92SSamuel Thibault 	}
4762067fd92SSamuel Thibault 
4772067fd92SSamuel Thibault 	var = spk_get_punc_var(p_header->var_id);
4782067fd92SSamuel Thibault 	if (!var) {
4792067fd92SSamuel Thibault 		pr_warn("var is null, p_header->var_id is %i\n",
4802067fd92SSamuel Thibault 			p_header->var_id);
4812067fd92SSamuel Thibault 		return -EINVAL;
4822067fd92SSamuel Thibault 	}
4832067fd92SSamuel Thibault 
4842067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
4852067fd92SSamuel Thibault 	pb = (struct st_bits_data *)&spk_punc_info[var->value];
4862067fd92SSamuel Thibault 	mask = pb->mask;
4872067fd92SSamuel Thibault 	for (i = 33; i < 128; i++) {
4882067fd92SSamuel Thibault 		if (!(spk_chartab[i] & mask))
4892067fd92SSamuel Thibault 			continue;
4902067fd92SSamuel Thibault 		*cp++ = (char)i;
4912067fd92SSamuel Thibault 	}
4922067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
4932067fd92SSamuel Thibault 	return cp - buf;
4942067fd92SSamuel Thibault }
4952067fd92SSamuel Thibault 
4962067fd92SSamuel Thibault /*
4972067fd92SSamuel Thibault  * This is called when a user changes the punctuation settings.
4982067fd92SSamuel Thibault  */
punc_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)4992067fd92SSamuel Thibault static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
5002067fd92SSamuel Thibault 			  const char *buf, size_t count)
5012067fd92SSamuel Thibault {
5022067fd92SSamuel Thibault 	int x;
5032067fd92SSamuel Thibault 	struct st_var_header *p_header;
5042067fd92SSamuel Thibault 	struct punc_var_t *var;
5052067fd92SSamuel Thibault 	char punc_buf[100];
5062067fd92SSamuel Thibault 	unsigned long flags;
5072067fd92SSamuel Thibault 
5082067fd92SSamuel Thibault 	x = strlen(buf);
5092067fd92SSamuel Thibault 	if (x < 1 || x > 99)
5102067fd92SSamuel Thibault 		return -EINVAL;
5112067fd92SSamuel Thibault 
5122067fd92SSamuel Thibault 	p_header = spk_var_header_by_name(attr->attr.name);
5132067fd92SSamuel Thibault 	if (!p_header) {
5142067fd92SSamuel Thibault 		pr_warn("p_header is null, attr->attr.name is %s\n",
5152067fd92SSamuel Thibault 			attr->attr.name);
5162067fd92SSamuel Thibault 		return -EINVAL;
5172067fd92SSamuel Thibault 	}
5182067fd92SSamuel Thibault 
5192067fd92SSamuel Thibault 	var = spk_get_punc_var(p_header->var_id);
5202067fd92SSamuel Thibault 	if (!var) {
5212067fd92SSamuel Thibault 		pr_warn("var is null, p_header->var_id is %i\n",
5222067fd92SSamuel Thibault 			p_header->var_id);
5232067fd92SSamuel Thibault 		return -EINVAL;
5242067fd92SSamuel Thibault 	}
5252067fd92SSamuel Thibault 
5262067fd92SSamuel Thibault 	memcpy(punc_buf, buf, x);
5272067fd92SSamuel Thibault 
5282067fd92SSamuel Thibault 	while (x && punc_buf[x - 1] == '\n')
5292067fd92SSamuel Thibault 		x--;
5302067fd92SSamuel Thibault 	punc_buf[x] = '\0';
5312067fd92SSamuel Thibault 
5322067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
5332067fd92SSamuel Thibault 
5342067fd92SSamuel Thibault 	if (*punc_buf == 'd' || *punc_buf == 'r')
5352067fd92SSamuel Thibault 		x = spk_set_mask_bits(NULL, var->value, 3);
5362067fd92SSamuel Thibault 	else
5372067fd92SSamuel Thibault 		x = spk_set_mask_bits(punc_buf, var->value, 3);
5382067fd92SSamuel Thibault 
5392067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
5402067fd92SSamuel Thibault 	return count;
5412067fd92SSamuel Thibault }
5422067fd92SSamuel Thibault 
5432067fd92SSamuel Thibault /*
5442067fd92SSamuel Thibault  * This function is called when a user reads one of the variable parameters.
5452067fd92SSamuel Thibault  */
spk_var_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)5462067fd92SSamuel Thibault ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
5472067fd92SSamuel Thibault 		     char *buf)
5482067fd92SSamuel Thibault {
5492067fd92SSamuel Thibault 	int rv = 0;
5502067fd92SSamuel Thibault 	struct st_var_header *param;
5512067fd92SSamuel Thibault 	struct var_t *var;
5522067fd92SSamuel Thibault 	char *cp1;
5532067fd92SSamuel Thibault 	char *cp;
5542067fd92SSamuel Thibault 	char ch;
5552067fd92SSamuel Thibault 	unsigned long flags;
5562067fd92SSamuel Thibault 
5572067fd92SSamuel Thibault 	param = spk_var_header_by_name(attr->attr.name);
5582067fd92SSamuel Thibault 	if (!param)
5592067fd92SSamuel Thibault 		return -EINVAL;
5602067fd92SSamuel Thibault 
5612067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
5622067fd92SSamuel Thibault 	var = (struct var_t *)param->data;
5632067fd92SSamuel Thibault 	switch (param->var_type) {
5642067fd92SSamuel Thibault 	case VAR_NUM:
5652067fd92SSamuel Thibault 	case VAR_TIME:
5662067fd92SSamuel Thibault 		if (var)
5672067fd92SSamuel Thibault 			rv = sprintf(buf, "%i\n", var->u.n.value);
5682067fd92SSamuel Thibault 		else
5692067fd92SSamuel Thibault 			rv = sprintf(buf, "0\n");
5702067fd92SSamuel Thibault 		break;
5712067fd92SSamuel Thibault 	case VAR_STRING:
5722067fd92SSamuel Thibault 		if (var) {
5732067fd92SSamuel Thibault 			cp1 = buf;
5742067fd92SSamuel Thibault 			*cp1++ = '"';
5752067fd92SSamuel Thibault 			for (cp = (char *)param->p_val; (ch = *cp); cp++) {
5762067fd92SSamuel Thibault 				if (ch >= ' ' && ch < '~')
5772067fd92SSamuel Thibault 					*cp1++ = ch;
5782067fd92SSamuel Thibault 				else
5792067fd92SSamuel Thibault 					cp1 += sprintf(cp1, "\\x%02x", ch);
5802067fd92SSamuel Thibault 			}
5812067fd92SSamuel Thibault 			*cp1++ = '"';
5822067fd92SSamuel Thibault 			*cp1++ = '\n';
5832067fd92SSamuel Thibault 			*cp1 = '\0';
5842067fd92SSamuel Thibault 			rv = cp1 - buf;
5852067fd92SSamuel Thibault 		} else {
5862067fd92SSamuel Thibault 			rv = sprintf(buf, "\"\"\n");
5872067fd92SSamuel Thibault 		}
5882067fd92SSamuel Thibault 		break;
5892067fd92SSamuel Thibault 	default:
5902067fd92SSamuel Thibault 		rv = sprintf(buf, "Bad parameter  %s, type %i\n",
5912067fd92SSamuel Thibault 			     param->name, param->var_type);
5922067fd92SSamuel Thibault 		break;
5932067fd92SSamuel Thibault 	}
5942067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
5952067fd92SSamuel Thibault 	return rv;
5962067fd92SSamuel Thibault }
5972067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_var_show);
5982067fd92SSamuel Thibault 
5992067fd92SSamuel Thibault /*
6002067fd92SSamuel Thibault  * Used to reset either default_pitch or default_vol.
6012067fd92SSamuel Thibault  */
spk_reset_default_value(char * header_name,int * synth_default_value,int idx)6022067fd92SSamuel Thibault static inline void spk_reset_default_value(char *header_name,
6032067fd92SSamuel Thibault 					   int *synth_default_value, int idx)
6042067fd92SSamuel Thibault {
6052067fd92SSamuel Thibault 	struct st_var_header *param;
6062067fd92SSamuel Thibault 
6072067fd92SSamuel Thibault 	if (synth && synth_default_value) {
6082067fd92SSamuel Thibault 		param = spk_var_header_by_name(header_name);
6092067fd92SSamuel Thibault 		if (param)  {
6102067fd92SSamuel Thibault 			spk_set_num_var(synth_default_value[idx],
6112067fd92SSamuel Thibault 					param, E_NEW_DEFAULT);
6122067fd92SSamuel Thibault 			spk_set_num_var(0, param, E_DEFAULT);
6132067fd92SSamuel Thibault 			pr_info("%s reset to default value\n", param->name);
6142067fd92SSamuel Thibault 		}
6152067fd92SSamuel Thibault 	}
6162067fd92SSamuel Thibault }
6172067fd92SSamuel Thibault 
6182067fd92SSamuel Thibault /*
6192067fd92SSamuel Thibault  * This function is called when a user echos a value to one of the
6202067fd92SSamuel Thibault  * variable parameters.
6212067fd92SSamuel Thibault  */
spk_var_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)6222067fd92SSamuel Thibault ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
6232067fd92SSamuel Thibault 		      const char *buf, size_t count)
6242067fd92SSamuel Thibault {
6252067fd92SSamuel Thibault 	struct st_var_header *param;
6262067fd92SSamuel Thibault 	int ret;
6272067fd92SSamuel Thibault 	int len;
6282067fd92SSamuel Thibault 	char *cp;
6292067fd92SSamuel Thibault 	struct var_t *var_data;
6302067fd92SSamuel Thibault 	long value;
6312067fd92SSamuel Thibault 	unsigned long flags;
6322067fd92SSamuel Thibault 
6332067fd92SSamuel Thibault 	param = spk_var_header_by_name(attr->attr.name);
6342067fd92SSamuel Thibault 	if (!param)
6352067fd92SSamuel Thibault 		return -EINVAL;
6362067fd92SSamuel Thibault 	if (!param->data)
6372067fd92SSamuel Thibault 		return 0;
6382067fd92SSamuel Thibault 	ret = 0;
6392067fd92SSamuel Thibault 	cp = (char *)buf;
6402067fd92SSamuel Thibault 	string_unescape_any_inplace(cp);
6412067fd92SSamuel Thibault 
6422067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
6432067fd92SSamuel Thibault 	switch (param->var_type) {
6442067fd92SSamuel Thibault 	case VAR_NUM:
6452067fd92SSamuel Thibault 	case VAR_TIME:
6462067fd92SSamuel Thibault 		if (*cp == 'd' || *cp == 'r' || *cp == '\0')
6472067fd92SSamuel Thibault 			len = E_DEFAULT;
6482067fd92SSamuel Thibault 		else if (*cp == '+' || *cp == '-')
6492067fd92SSamuel Thibault 			len = E_INC;
6502067fd92SSamuel Thibault 		else
6512067fd92SSamuel Thibault 			len = E_SET;
6522067fd92SSamuel Thibault 		if (kstrtol(cp, 10, &value) == 0)
6532067fd92SSamuel Thibault 			ret = spk_set_num_var(value, param, len);
6542067fd92SSamuel Thibault 		else
6552067fd92SSamuel Thibault 			pr_warn("overflow or parsing error has occurred");
6562067fd92SSamuel Thibault 		if (ret == -ERANGE) {
6572067fd92SSamuel Thibault 			var_data = param->data;
6582067fd92SSamuel Thibault 			pr_warn("value for %s out of range, expect %d to %d\n",
6592067fd92SSamuel Thibault 				param->name,
6602067fd92SSamuel Thibault 				var_data->u.n.low, var_data->u.n.high);
6612067fd92SSamuel Thibault 		}
6622067fd92SSamuel Thibault 
6632067fd92SSamuel Thibault 	       /*
6642067fd92SSamuel Thibault 		* If voice was just changed, we might need to reset our default
6652067fd92SSamuel Thibault 		* pitch and volume.
6662067fd92SSamuel Thibault 		*/
6672067fd92SSamuel Thibault 		if (param->var_id == VOICE && synth &&
6682067fd92SSamuel Thibault 		    (ret == 0 || ret == -ERESTART)) {
6692067fd92SSamuel Thibault 			var_data = param->data;
6702067fd92SSamuel Thibault 			value = var_data->u.n.value;
6712067fd92SSamuel Thibault 			spk_reset_default_value("pitch", synth->default_pitch,
6722067fd92SSamuel Thibault 						value);
6732067fd92SSamuel Thibault 			spk_reset_default_value("vol", synth->default_vol,
6742067fd92SSamuel Thibault 						value);
6752067fd92SSamuel Thibault 		}
6762067fd92SSamuel Thibault 		break;
6772067fd92SSamuel Thibault 	case VAR_STRING:
6782067fd92SSamuel Thibault 		len = strlen(cp);
6792067fd92SSamuel Thibault 		if ((len >= 1) && (cp[len - 1] == '\n'))
6802067fd92SSamuel Thibault 			--len;
6812067fd92SSamuel Thibault 		if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
6822067fd92SSamuel Thibault 			++cp;
6832067fd92SSamuel Thibault 			len -= 2;
6842067fd92SSamuel Thibault 		}
6852067fd92SSamuel Thibault 		cp[len] = '\0';
6862067fd92SSamuel Thibault 		ret = spk_set_string_var(cp, param, len);
6872067fd92SSamuel Thibault 		if (ret == -E2BIG)
6882067fd92SSamuel Thibault 			pr_warn("value too long for %s\n",
6892067fd92SSamuel Thibault 				param->name);
6902067fd92SSamuel Thibault 		break;
6912067fd92SSamuel Thibault 	default:
6922067fd92SSamuel Thibault 		pr_warn("%s unknown type %d\n",
6932067fd92SSamuel Thibault 			param->name, (int)param->var_type);
6942067fd92SSamuel Thibault 	break;
6952067fd92SSamuel Thibault 	}
6962067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
6972067fd92SSamuel Thibault 
6982067fd92SSamuel Thibault 	if (ret == -ERESTART)
6992067fd92SSamuel Thibault 		pr_info("%s reset to default value\n", param->name);
7002067fd92SSamuel Thibault 	return count;
7012067fd92SSamuel Thibault }
7022067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_var_store);
7032067fd92SSamuel Thibault 
7042067fd92SSamuel Thibault /*
7052067fd92SSamuel Thibault  * Functions for reading and writing lists of i18n messages.  Incomplete.
7062067fd92SSamuel Thibault  */
7072067fd92SSamuel Thibault 
message_show_helper(char * buf,enum msg_index_t first,enum msg_index_t last)7082067fd92SSamuel Thibault static ssize_t message_show_helper(char *buf, enum msg_index_t first,
7092067fd92SSamuel Thibault 				   enum msg_index_t last)
7102067fd92SSamuel Thibault {
7112067fd92SSamuel Thibault 	size_t bufsize = PAGE_SIZE;
7122067fd92SSamuel Thibault 	char *buf_pointer = buf;
7132067fd92SSamuel Thibault 	int printed;
7142067fd92SSamuel Thibault 	enum msg_index_t cursor;
7152067fd92SSamuel Thibault 	int index = 0;
7162067fd92SSamuel Thibault 	*buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
7172067fd92SSamuel Thibault 
7182067fd92SSamuel Thibault 	for (cursor = first; cursor <= last; cursor++, index++) {
7192067fd92SSamuel Thibault 		if (bufsize <= 1)
7202067fd92SSamuel Thibault 			break;
7212067fd92SSamuel Thibault 		printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
7222067fd92SSamuel Thibault 				    index, spk_msg_get(cursor));
7232067fd92SSamuel Thibault 		buf_pointer += printed;
7242067fd92SSamuel Thibault 		bufsize -= printed;
7252067fd92SSamuel Thibault 	}
7262067fd92SSamuel Thibault 
7272067fd92SSamuel Thibault 	return buf_pointer - buf;
7282067fd92SSamuel Thibault }
7292067fd92SSamuel Thibault 
report_msg_status(int reset,int received,int used,int rejected,char * groupname)7302067fd92SSamuel Thibault static void report_msg_status(int reset, int received, int used,
7312067fd92SSamuel Thibault 			      int rejected, char *groupname)
7322067fd92SSamuel Thibault {
7332067fd92SSamuel Thibault 	int len;
7342067fd92SSamuel Thibault 	char buf[160];
7352067fd92SSamuel Thibault 
7362067fd92SSamuel Thibault 	if (reset) {
7372067fd92SSamuel Thibault 		pr_info("i18n messages from group %s reset to defaults\n",
7382067fd92SSamuel Thibault 			groupname);
7392067fd92SSamuel Thibault 	} else if (received) {
7402067fd92SSamuel Thibault 		len = snprintf(buf, sizeof(buf),
7412067fd92SSamuel Thibault 			       " updated %d of %d i18n messages from group %s\n",
7422067fd92SSamuel Thibault 				       used, received, groupname);
7432067fd92SSamuel Thibault 		if (rejected)
7442067fd92SSamuel Thibault 			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
7452067fd92SSamuel Thibault 				 " with %d reject%s\n",
7462067fd92SSamuel Thibault 				 rejected, rejected > 1 ? "s" : "");
7472067fd92SSamuel Thibault 		pr_info("%s", buf);
7482067fd92SSamuel Thibault 	}
7492067fd92SSamuel Thibault }
7502067fd92SSamuel Thibault 
message_store_helper(const char * buf,size_t count,struct msg_group_t * group)7512067fd92SSamuel Thibault static ssize_t message_store_helper(const char *buf, size_t count,
7522067fd92SSamuel Thibault 				    struct msg_group_t *group)
7532067fd92SSamuel Thibault {
7542067fd92SSamuel Thibault 	char *cp = (char *)buf;
7552067fd92SSamuel Thibault 	char *end = cp + count;
7562067fd92SSamuel Thibault 	char *linefeed = NULL;
7572067fd92SSamuel Thibault 	char *temp = NULL;
7582067fd92SSamuel Thibault 	ssize_t msg_stored = 0;
7592067fd92SSamuel Thibault 	ssize_t retval = count;
7602067fd92SSamuel Thibault 	size_t desc_length = 0;
7612067fd92SSamuel Thibault 	unsigned long index = 0;
7622067fd92SSamuel Thibault 	int received = 0;
7632067fd92SSamuel Thibault 	int used = 0;
7642067fd92SSamuel Thibault 	int rejected = 0;
7652067fd92SSamuel Thibault 	int reset = 0;
7662067fd92SSamuel Thibault 	enum msg_index_t firstmessage = group->start;
7672067fd92SSamuel Thibault 	enum msg_index_t lastmessage = group->end;
7682067fd92SSamuel Thibault 	enum msg_index_t curmessage;
7692067fd92SSamuel Thibault 
7702067fd92SSamuel Thibault 	while (cp < end) {
7712067fd92SSamuel Thibault 		while ((cp < end) && (*cp == ' ' || *cp == '\t'))
7722067fd92SSamuel Thibault 			cp++;
7732067fd92SSamuel Thibault 
7742067fd92SSamuel Thibault 		if (cp == end)
7752067fd92SSamuel Thibault 			break;
7762067fd92SSamuel Thibault 		if (strchr("dDrR", *cp)) {
7772067fd92SSamuel Thibault 			reset = 1;
7782067fd92SSamuel Thibault 			break;
7792067fd92SSamuel Thibault 		}
7802067fd92SSamuel Thibault 		received++;
7812067fd92SSamuel Thibault 
7822067fd92SSamuel Thibault 		linefeed = strchr(cp, '\n');
7832067fd92SSamuel Thibault 		if (!linefeed) {
7842067fd92SSamuel Thibault 			rejected++;
7852067fd92SSamuel Thibault 			break;
7862067fd92SSamuel Thibault 		}
7872067fd92SSamuel Thibault 
7882067fd92SSamuel Thibault 		if (!isdigit(*cp)) {
7892067fd92SSamuel Thibault 			rejected++;
7902067fd92SSamuel Thibault 			cp = linefeed + 1;
7912067fd92SSamuel Thibault 			continue;
7922067fd92SSamuel Thibault 		}
7932067fd92SSamuel Thibault 
7942067fd92SSamuel Thibault 		/*
7952067fd92SSamuel Thibault 		 * Do not replace with kstrtoul:
7962067fd92SSamuel Thibault 		 * here we need temp to be updated
7972067fd92SSamuel Thibault 		 */
7982067fd92SSamuel Thibault 		index = simple_strtoul(cp, &temp, 10);
7992067fd92SSamuel Thibault 
8002067fd92SSamuel Thibault 		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
8012067fd92SSamuel Thibault 			temp++;
8022067fd92SSamuel Thibault 
8032067fd92SSamuel Thibault 		desc_length = linefeed - temp;
8042067fd92SSamuel Thibault 		curmessage = firstmessage + index;
8052067fd92SSamuel Thibault 
8062067fd92SSamuel Thibault 		/*
8072067fd92SSamuel Thibault 		 * Note the check (curmessage < firstmessage).  It is not
8082067fd92SSamuel Thibault 		 * redundant.  Suppose that the user gave us an index
8092067fd92SSamuel Thibault 		 * equal to ULONG_MAX - 1.  If firstmessage > 1, then
8102067fd92SSamuel Thibault 		 * firstmessage + index < firstmessage!
8112067fd92SSamuel Thibault 		 */
8122067fd92SSamuel Thibault 
8132067fd92SSamuel Thibault 		if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
8142067fd92SSamuel Thibault 			rejected++;
8152067fd92SSamuel Thibault 			cp = linefeed + 1;
8162067fd92SSamuel Thibault 			continue;
8172067fd92SSamuel Thibault 		}
8182067fd92SSamuel Thibault 
8192067fd92SSamuel Thibault 		msg_stored = spk_msg_set(curmessage, temp, desc_length);
8202067fd92SSamuel Thibault 		if (msg_stored < 0) {
8212067fd92SSamuel Thibault 			retval = msg_stored;
8222067fd92SSamuel Thibault 			if (msg_stored == -ENOMEM)
8232067fd92SSamuel Thibault 				reset = 1;
8242067fd92SSamuel Thibault 			break;
8252067fd92SSamuel Thibault 		}
8262067fd92SSamuel Thibault 
8272067fd92SSamuel Thibault 		used++;
8282067fd92SSamuel Thibault 
8292067fd92SSamuel Thibault 		cp = linefeed + 1;
8302067fd92SSamuel Thibault 	}
8312067fd92SSamuel Thibault 
8322067fd92SSamuel Thibault 	if (reset)
8332067fd92SSamuel Thibault 		spk_reset_msg_group(group);
8342067fd92SSamuel Thibault 
8352067fd92SSamuel Thibault 	report_msg_status(reset, received, used, rejected, group->name);
8362067fd92SSamuel Thibault 	return retval;
8372067fd92SSamuel Thibault }
8382067fd92SSamuel Thibault 
message_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)8392067fd92SSamuel Thibault static ssize_t message_show(struct kobject *kobj,
8402067fd92SSamuel Thibault 			    struct kobj_attribute *attr, char *buf)
8412067fd92SSamuel Thibault {
8422067fd92SSamuel Thibault 	ssize_t retval = 0;
8432067fd92SSamuel Thibault 	struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
8442067fd92SSamuel Thibault 	unsigned long flags;
8452067fd92SSamuel Thibault 
8462067fd92SSamuel Thibault 	if (WARN_ON(!group))
8472067fd92SSamuel Thibault 		return -EINVAL;
8482067fd92SSamuel Thibault 
8492067fd92SSamuel Thibault 	spin_lock_irqsave(&speakup_info.spinlock, flags);
8502067fd92SSamuel Thibault 	retval = message_show_helper(buf, group->start, group->end);
8512067fd92SSamuel Thibault 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
8522067fd92SSamuel Thibault 	return retval;
8532067fd92SSamuel Thibault }
8542067fd92SSamuel Thibault 
message_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)8552067fd92SSamuel Thibault static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
8562067fd92SSamuel Thibault 			     const char *buf, size_t count)
8572067fd92SSamuel Thibault {
8582067fd92SSamuel Thibault 	struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
8592067fd92SSamuel Thibault 
8602067fd92SSamuel Thibault 	if (WARN_ON(!group))
8612067fd92SSamuel Thibault 		return -EINVAL;
8622067fd92SSamuel Thibault 
8632067fd92SSamuel Thibault 	return message_store_helper(buf, count, group);
8642067fd92SSamuel Thibault }
8652067fd92SSamuel Thibault 
8662067fd92SSamuel Thibault /*
8672067fd92SSamuel Thibault  * Declare the attributes.
8682067fd92SSamuel Thibault  */
8692067fd92SSamuel Thibault static struct kobj_attribute keymap_attribute =
8702067fd92SSamuel Thibault 	__ATTR_RW(keymap);
8712067fd92SSamuel Thibault static struct kobj_attribute silent_attribute =
8722067fd92SSamuel Thibault 	__ATTR_WO(silent);
8732067fd92SSamuel Thibault static struct kobj_attribute synth_attribute =
8742067fd92SSamuel Thibault 	__ATTR_RW(synth);
8752067fd92SSamuel Thibault static struct kobj_attribute synth_direct_attribute =
8762067fd92SSamuel Thibault 	__ATTR_WO(synth_direct);
8772067fd92SSamuel Thibault static struct kobj_attribute version_attribute =
8782067fd92SSamuel Thibault 	__ATTR_RO(version);
8792067fd92SSamuel Thibault 
8802067fd92SSamuel Thibault static struct kobj_attribute delimiters_attribute =
8812067fd92SSamuel Thibault 	__ATTR(delimiters, 0644, punc_show, punc_store);
8822067fd92SSamuel Thibault static struct kobj_attribute ex_num_attribute =
8832067fd92SSamuel Thibault 	__ATTR(ex_num, 0644, punc_show, punc_store);
8842067fd92SSamuel Thibault static struct kobj_attribute punc_all_attribute =
8852067fd92SSamuel Thibault 	__ATTR(punc_all, 0644, punc_show, punc_store);
8862067fd92SSamuel Thibault static struct kobj_attribute punc_most_attribute =
8872067fd92SSamuel Thibault 	__ATTR(punc_most, 0644, punc_show, punc_store);
8882067fd92SSamuel Thibault static struct kobj_attribute punc_some_attribute =
8892067fd92SSamuel Thibault 	__ATTR(punc_some, 0644, punc_show, punc_store);
8902067fd92SSamuel Thibault static struct kobj_attribute repeats_attribute =
8912067fd92SSamuel Thibault 	__ATTR(repeats, 0644, punc_show, punc_store);
8922067fd92SSamuel Thibault 
8932067fd92SSamuel Thibault static struct kobj_attribute attrib_bleep_attribute =
8942067fd92SSamuel Thibault 	__ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);
8952067fd92SSamuel Thibault static struct kobj_attribute bell_pos_attribute =
8962067fd92SSamuel Thibault 	__ATTR(bell_pos, 0644, spk_var_show, spk_var_store);
8972067fd92SSamuel Thibault static struct kobj_attribute bleep_time_attribute =
8982067fd92SSamuel Thibault 	__ATTR(bleep_time, 0644, spk_var_show, spk_var_store);
8992067fd92SSamuel Thibault static struct kobj_attribute bleeps_attribute =
9002067fd92SSamuel Thibault 	__ATTR(bleeps, 0644, spk_var_show, spk_var_store);
9012067fd92SSamuel Thibault static struct kobj_attribute cursor_time_attribute =
9022067fd92SSamuel Thibault 	__ATTR(cursor_time, 0644, spk_var_show, spk_var_store);
9032067fd92SSamuel Thibault static struct kobj_attribute key_echo_attribute =
9042067fd92SSamuel Thibault 	__ATTR(key_echo, 0644, spk_var_show, spk_var_store);
9052067fd92SSamuel Thibault static struct kobj_attribute no_interrupt_attribute =
9062067fd92SSamuel Thibault 	__ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);
9072067fd92SSamuel Thibault static struct kobj_attribute punc_level_attribute =
9082067fd92SSamuel Thibault 	__ATTR(punc_level, 0644, spk_var_show, spk_var_store);
9092067fd92SSamuel Thibault static struct kobj_attribute reading_punc_attribute =
9102067fd92SSamuel Thibault 	__ATTR(reading_punc, 0644, spk_var_show, spk_var_store);
9112067fd92SSamuel Thibault static struct kobj_attribute say_control_attribute =
9122067fd92SSamuel Thibault 	__ATTR(say_control, 0644, spk_var_show, spk_var_store);
9132067fd92SSamuel Thibault static struct kobj_attribute say_word_ctl_attribute =
9142067fd92SSamuel Thibault 	__ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
9152067fd92SSamuel Thibault static struct kobj_attribute spell_delay_attribute =
9162067fd92SSamuel Thibault 	__ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
917*72b8ec15SMushahid Hussain static struct kobj_attribute cur_phonetic_attribute =
918*72b8ec15SMushahid Hussain 	__ATTR(cur_phonetic, 0644, spk_var_show, spk_var_store);
9192067fd92SSamuel Thibault 
9202067fd92SSamuel Thibault /*
9212067fd92SSamuel Thibault  * These attributes are i18n related.
9222067fd92SSamuel Thibault  */
9232067fd92SSamuel Thibault static struct kobj_attribute announcements_attribute =
9242067fd92SSamuel Thibault 	__ATTR(announcements, 0644, message_show, message_store);
9252067fd92SSamuel Thibault static struct kobj_attribute characters_attribute =
9262067fd92SSamuel Thibault 	__ATTR(characters, 0644, chars_chartab_show,
9272067fd92SSamuel Thibault 	       chars_chartab_store);
9282067fd92SSamuel Thibault static struct kobj_attribute chartab_attribute =
9292067fd92SSamuel Thibault 	__ATTR(chartab, 0644, chars_chartab_show,
9302067fd92SSamuel Thibault 	       chars_chartab_store);
9312067fd92SSamuel Thibault static struct kobj_attribute ctl_keys_attribute =
9322067fd92SSamuel Thibault 	__ATTR(ctl_keys, 0644, message_show, message_store);
9332067fd92SSamuel Thibault static struct kobj_attribute colors_attribute =
9342067fd92SSamuel Thibault 	__ATTR(colors, 0644, message_show, message_store);
9352067fd92SSamuel Thibault static struct kobj_attribute formatted_attribute =
9362067fd92SSamuel Thibault 	__ATTR(formatted, 0644, message_show, message_store);
9372067fd92SSamuel Thibault static struct kobj_attribute function_names_attribute =
9382067fd92SSamuel Thibault 	__ATTR(function_names, 0644, message_show, message_store);
9392067fd92SSamuel Thibault static struct kobj_attribute key_names_attribute =
9402067fd92SSamuel Thibault 	__ATTR(key_names, 0644, message_show, message_store);
9412067fd92SSamuel Thibault static struct kobj_attribute states_attribute =
9422067fd92SSamuel Thibault 	__ATTR(states, 0644, message_show, message_store);
9432067fd92SSamuel Thibault 
9442067fd92SSamuel Thibault /*
9452067fd92SSamuel Thibault  * Create groups of attributes so that we can create and destroy them all
9462067fd92SSamuel Thibault  * at once.
9472067fd92SSamuel Thibault  */
9482067fd92SSamuel Thibault static struct attribute *main_attrs[] = {
9492067fd92SSamuel Thibault 	&keymap_attribute.attr,
9502067fd92SSamuel Thibault 	&silent_attribute.attr,
9512067fd92SSamuel Thibault 	&synth_attribute.attr,
9522067fd92SSamuel Thibault 	&synth_direct_attribute.attr,
9532067fd92SSamuel Thibault 	&version_attribute.attr,
9542067fd92SSamuel Thibault 	&delimiters_attribute.attr,
9552067fd92SSamuel Thibault 	&ex_num_attribute.attr,
9562067fd92SSamuel Thibault 	&punc_all_attribute.attr,
9572067fd92SSamuel Thibault 	&punc_most_attribute.attr,
9582067fd92SSamuel Thibault 	&punc_some_attribute.attr,
9592067fd92SSamuel Thibault 	&repeats_attribute.attr,
9602067fd92SSamuel Thibault 	&attrib_bleep_attribute.attr,
9612067fd92SSamuel Thibault 	&bell_pos_attribute.attr,
9622067fd92SSamuel Thibault 	&bleep_time_attribute.attr,
9632067fd92SSamuel Thibault 	&bleeps_attribute.attr,
9642067fd92SSamuel Thibault 	&cursor_time_attribute.attr,
9652067fd92SSamuel Thibault 	&key_echo_attribute.attr,
9662067fd92SSamuel Thibault 	&no_interrupt_attribute.attr,
9672067fd92SSamuel Thibault 	&punc_level_attribute.attr,
9682067fd92SSamuel Thibault 	&reading_punc_attribute.attr,
9692067fd92SSamuel Thibault 	&say_control_attribute.attr,
9702067fd92SSamuel Thibault 	&say_word_ctl_attribute.attr,
9712067fd92SSamuel Thibault 	&spell_delay_attribute.attr,
972*72b8ec15SMushahid Hussain 	&cur_phonetic_attribute.attr,
9732067fd92SSamuel Thibault 	NULL,
9742067fd92SSamuel Thibault };
9752067fd92SSamuel Thibault 
9762067fd92SSamuel Thibault static struct attribute *i18n_attrs[] = {
9772067fd92SSamuel Thibault 	&announcements_attribute.attr,
9782067fd92SSamuel Thibault 	&characters_attribute.attr,
9792067fd92SSamuel Thibault 	&chartab_attribute.attr,
9802067fd92SSamuel Thibault 	&ctl_keys_attribute.attr,
9812067fd92SSamuel Thibault 	&colors_attribute.attr,
9822067fd92SSamuel Thibault 	&formatted_attribute.attr,
9832067fd92SSamuel Thibault 	&function_names_attribute.attr,
9842067fd92SSamuel Thibault 	&key_names_attribute.attr,
9852067fd92SSamuel Thibault 	&states_attribute.attr,
9862067fd92SSamuel Thibault 	NULL,
9872067fd92SSamuel Thibault };
9882067fd92SSamuel Thibault 
9892067fd92SSamuel Thibault /*
9902067fd92SSamuel Thibault  * An unnamed attribute group will put all of the attributes directly in
9912067fd92SSamuel Thibault  * the kobject directory.  If we specify a name, a subdirectory will be
9922067fd92SSamuel Thibault  * created for the attributes with the directory being the name of the
9932067fd92SSamuel Thibault  * attribute group.
9942067fd92SSamuel Thibault  */
9952067fd92SSamuel Thibault static const struct attribute_group main_attr_group = {
9962067fd92SSamuel Thibault 	.attrs = main_attrs,
9972067fd92SSamuel Thibault };
9982067fd92SSamuel Thibault 
9992067fd92SSamuel Thibault static const struct attribute_group i18n_attr_group = {
10002067fd92SSamuel Thibault 	.attrs = i18n_attrs,
10012067fd92SSamuel Thibault 	.name = "i18n",
10022067fd92SSamuel Thibault };
10032067fd92SSamuel Thibault 
10042067fd92SSamuel Thibault static struct kobject *accessibility_kobj;
10052067fd92SSamuel Thibault struct kobject *speakup_kobj;
10062067fd92SSamuel Thibault 
speakup_kobj_init(void)10072067fd92SSamuel Thibault int speakup_kobj_init(void)
10082067fd92SSamuel Thibault {
10092067fd92SSamuel Thibault 	int retval;
10102067fd92SSamuel Thibault 
10112067fd92SSamuel Thibault 	/*
10122067fd92SSamuel Thibault 	 * Create a simple kobject with the name of "accessibility",
10132067fd92SSamuel Thibault 	 * located under /sys/
10142067fd92SSamuel Thibault 	 *
10152067fd92SSamuel Thibault 	 * As this is a simple directory, no uevent will be sent to
10162067fd92SSamuel Thibault 	 * userspace.  That is why this function should not be used for
10172067fd92SSamuel Thibault 	 * any type of dynamic kobjects, where the name and number are
10182067fd92SSamuel Thibault 	 * not known ahead of time.
10192067fd92SSamuel Thibault 	 */
10202067fd92SSamuel Thibault 	accessibility_kobj = kobject_create_and_add("accessibility", NULL);
10212067fd92SSamuel Thibault 	if (!accessibility_kobj) {
10222067fd92SSamuel Thibault 		retval = -ENOMEM;
10232067fd92SSamuel Thibault 		goto out;
10242067fd92SSamuel Thibault 	}
10252067fd92SSamuel Thibault 
10262067fd92SSamuel Thibault 	speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj);
10272067fd92SSamuel Thibault 	if (!speakup_kobj) {
10282067fd92SSamuel Thibault 		retval = -ENOMEM;
10292067fd92SSamuel Thibault 		goto err_acc;
10302067fd92SSamuel Thibault 	}
10312067fd92SSamuel Thibault 
10322067fd92SSamuel Thibault 	/* Create the files associated with this kobject */
10332067fd92SSamuel Thibault 	retval = sysfs_create_group(speakup_kobj, &main_attr_group);
10342067fd92SSamuel Thibault 	if (retval)
10352067fd92SSamuel Thibault 		goto err_speakup;
10362067fd92SSamuel Thibault 
10372067fd92SSamuel Thibault 	retval = sysfs_create_group(speakup_kobj, &i18n_attr_group);
10382067fd92SSamuel Thibault 	if (retval)
10392067fd92SSamuel Thibault 		goto err_group;
10402067fd92SSamuel Thibault 
10412067fd92SSamuel Thibault 	goto out;
10422067fd92SSamuel Thibault 
10432067fd92SSamuel Thibault err_group:
10442067fd92SSamuel Thibault 	sysfs_remove_group(speakup_kobj, &main_attr_group);
10452067fd92SSamuel Thibault err_speakup:
10462067fd92SSamuel Thibault 	kobject_put(speakup_kobj);
10472067fd92SSamuel Thibault err_acc:
10482067fd92SSamuel Thibault 	kobject_put(accessibility_kobj);
10492067fd92SSamuel Thibault out:
10502067fd92SSamuel Thibault 	return retval;
10512067fd92SSamuel Thibault }
10522067fd92SSamuel Thibault 
speakup_kobj_exit(void)10532067fd92SSamuel Thibault void speakup_kobj_exit(void)
10542067fd92SSamuel Thibault {
10552067fd92SSamuel Thibault 	sysfs_remove_group(speakup_kobj, &i18n_attr_group);
10562067fd92SSamuel Thibault 	sysfs_remove_group(speakup_kobj, &main_attr_group);
10572067fd92SSamuel Thibault 	kobject_put(speakup_kobj);
10582067fd92SSamuel Thibault 	kobject_put(accessibility_kobj);
10592067fd92SSamuel Thibault }
1060