brintos

brintos / linux-shallow public Read only

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