cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kobjects.c (26149B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Speakup kobject implementation
      4 *
      5 * Copyright (C) 2009 William Hubbs
      6 *
      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			else
     67				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 updating
     80 * 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 updated
    160		 */
    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		else
    217			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 + states
    249	 */
    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	else
    363		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	else
    379		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	u_char tmp[256];
    417	int len;
    418	int bytes;
    419	const char *ptr = buf;
    420	unsigned long flags;
    421
    422	if (!synth)
    423		return -EPERM;
    424
    425	len = strlen(buf);
    426	spin_lock_irqsave(&speakup_info.spinlock, flags);
    427	while (len > 0) {
    428		bytes = min_t(size_t, len, 250);
    429		strncpy(tmp, ptr, bytes);
    430		tmp[bytes] = '\0';
    431		string_unescape_any_inplace(tmp);
    432		synth_printf("%s", tmp);
    433		ptr += bytes;
    434		len -= bytes;
    435	}
    436	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
    437	return count;
    438}
    439
    440/*
    441 * This function is called when a user reads the version.
    442 */
    443static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
    444			    char *buf)
    445{
    446	char *cp;
    447
    448	cp = buf;
    449	cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
    450	if (synth)
    451		cp += sprintf(cp, "%s synthesizer driver version %s\n",
    452		synth->name, synth->version);
    453	return cp - buf;
    454}
    455
    456/*
    457 * This is called when a user reads the punctuation settings.
    458 */
    459static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
    460			 char *buf)
    461{
    462	int i;
    463	char *cp = buf;
    464	struct st_var_header *p_header;
    465	struct punc_var_t *var;
    466	struct st_bits_data *pb;
    467	short mask;
    468	unsigned long flags;
    469
    470	p_header = spk_var_header_by_name(attr->attr.name);
    471	if (!p_header) {
    472		pr_warn("p_header is null, attr->attr.name is %s\n",
    473			attr->attr.name);
    474		return -EINVAL;
    475	}
    476
    477	var = spk_get_punc_var(p_header->var_id);
    478	if (!var) {
    479		pr_warn("var is null, p_header->var_id is %i\n",
    480			p_header->var_id);
    481		return -EINVAL;
    482	}
    483
    484	spin_lock_irqsave(&speakup_info.spinlock, flags);
    485	pb = (struct st_bits_data *)&spk_punc_info[var->value];
    486	mask = pb->mask;
    487	for (i = 33; i < 128; i++) {
    488		if (!(spk_chartab[i] & mask))
    489			continue;
    490		*cp++ = (char)i;
    491	}
    492	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
    493	return cp - buf;
    494}
    495
    496/*
    497 * This is called when a user changes the punctuation settings.
    498 */
    499static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
    500			  const char *buf, size_t count)
    501{
    502	int x;
    503	struct st_var_header *p_header;
    504	struct punc_var_t *var;
    505	char punc_buf[100];
    506	unsigned long flags;
    507
    508	x = strlen(buf);
    509	if (x < 1 || x > 99)
    510		return -EINVAL;
    511
    512	p_header = spk_var_header_by_name(attr->attr.name);
    513	if (!p_header) {
    514		pr_warn("p_header is null, attr->attr.name is %s\n",
    515			attr->attr.name);
    516		return -EINVAL;
    517	}
    518
    519	var = spk_get_punc_var(p_header->var_id);
    520	if (!var) {
    521		pr_warn("var is null, p_header->var_id is %i\n",
    522			p_header->var_id);
    523		return -EINVAL;
    524	}
    525
    526	memcpy(punc_buf, buf, x);
    527
    528	while (x && punc_buf[x - 1] == '\n')
    529		x--;
    530	punc_buf[x] = '\0';
    531
    532	spin_lock_irqsave(&speakup_info.spinlock, flags);
    533
    534	if (*punc_buf == 'd' || *punc_buf == 'r')
    535		x = spk_set_mask_bits(NULL, var->value, 3);
    536	else
    537		x = spk_set_mask_bits(punc_buf, var->value, 3);
    538
    539	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
    540	return count;
    541}
    542
    543/*
    544 * This function is called when a user reads one of the variable parameters.
    545 */
    546ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
    547		     char *buf)
    548{
    549	int rv = 0;
    550	struct st_var_header *param;
    551	struct var_t *var;
    552	char *cp1;
    553	char *cp;
    554	char ch;
    555	unsigned long flags;
    556
    557	param = spk_var_header_by_name(attr->attr.name);
    558	if (!param)
    559		return -EINVAL;
    560
    561	spin_lock_irqsave(&speakup_info.spinlock, flags);
    562	var = (struct var_t *)param->data;
    563	switch (param->var_type) {
    564	case VAR_NUM:
    565	case VAR_TIME:
    566		if (var)
    567			rv = sprintf(buf, "%i\n", var->u.n.value);
    568		else
    569			rv = sprintf(buf, "0\n");
    570		break;
    571	case VAR_STRING:
    572		if (var) {
    573			cp1 = buf;
    574			*cp1++ = '"';
    575			for (cp = (char *)param->p_val; (ch = *cp); cp++) {
    576				if (ch >= ' ' && ch < '~')
    577					*cp1++ = ch;
    578				else
    579					cp1 += sprintf(cp1, "\\x%02x", ch);
    580			}
    581			*cp1++ = '"';
    582			*cp1++ = '\n';
    583			*cp1 = '\0';
    584			rv = cp1 - buf;
    585		} else {
    586			rv = sprintf(buf, "\"\"\n");
    587		}
    588		break;
    589	default:
    590		rv = sprintf(buf, "Bad parameter  %s, type %i\n",
    591			     param->name, param->var_type);
    592		break;
    593	}
    594	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
    595	return rv;
    596}
    597EXPORT_SYMBOL_GPL(spk_var_show);
    598
    599/*
    600 * Used to reset either default_pitch or default_vol.
    601 */
    602static inline void spk_reset_default_value(char *header_name,
    603					   int *synth_default_value, int idx)
    604{
    605	struct st_var_header *param;
    606
    607	if (synth && synth_default_value) {
    608		param = spk_var_header_by_name(header_name);
    609		if (param)  {
    610			spk_set_num_var(synth_default_value[idx],
    611					param, E_NEW_DEFAULT);
    612			spk_set_num_var(0, param, E_DEFAULT);
    613			pr_info("%s reset to default value\n", param->name);
    614		}
    615	}
    616}
    617
    618/*
    619 * This function is called when a user echos a value to one of the
    620 * variable parameters.
    621 */
    622ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
    623		      const char *buf, size_t count)
    624{
    625	struct st_var_header *param;
    626	int ret;
    627	int len;
    628	char *cp;
    629	struct var_t *var_data;
    630	long value;
    631	unsigned long flags;
    632
    633	param = spk_var_header_by_name(attr->attr.name);
    634	if (!param)
    635		return -EINVAL;
    636	if (!param->data)
    637		return 0;
    638	ret = 0;
    639	cp = (char *)buf;
    640	string_unescape_any_inplace(cp);
    641
    642	spin_lock_irqsave(&speakup_info.spinlock, flags);
    643	switch (param->var_type) {
    644	case VAR_NUM:
    645	case VAR_TIME:
    646		if (*cp == 'd' || *cp == 'r' || *cp == '\0')
    647			len = E_DEFAULT;
    648		else if (*cp == '+' || *cp == '-')
    649			len = E_INC;
    650		else
    651			len = E_SET;
    652		if (kstrtol(cp, 10, &value) == 0)
    653			ret = spk_set_num_var(value, param, len);
    654		else
    655			pr_warn("overflow or parsing error has occurred");
    656		if (ret == -ERANGE) {
    657			var_data = param->data;
    658			pr_warn("value for %s out of range, expect %d to %d\n",
    659				param->name,
    660				var_data->u.n.low, var_data->u.n.high);
    661		}
    662
    663	       /*
    664		* If voice was just changed, we might need to reset our default
    665		* pitch and volume.
    666		*/
    667		if (param->var_id == VOICE && synth &&
    668		    (ret == 0 || ret == -ERESTART)) {
    669			var_data = param->data;
    670			value = var_data->u.n.value;
    671			spk_reset_default_value("pitch", synth->default_pitch,
    672						value);
    673			spk_reset_default_value("vol", synth->default_vol,
    674						value);
    675		}
    676		break;
    677	case VAR_STRING:
    678		len = strlen(cp);
    679		if ((len >= 1) && (cp[len - 1] == '\n'))
    680			--len;
    681		if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
    682			++cp;
    683			len -= 2;
    684		}
    685		cp[len] = '\0';
    686		ret = spk_set_string_var(cp, param, len);
    687		if (ret == -E2BIG)
    688			pr_warn("value too long for %s\n",
    689				param->name);
    690		break;
    691	default:
    692		pr_warn("%s unknown type %d\n",
    693			param->name, (int)param->var_type);
    694	break;
    695	}
    696	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
    697
    698	if (ret == -ERESTART)
    699		pr_info("%s reset to default value\n", param->name);
    700	return count;
    701}
    702EXPORT_SYMBOL_GPL(spk_var_store);
    703
    704/*
    705 * Functions for reading and writing lists of i18n messages.  Incomplete.
    706 */
    707
    708static ssize_t message_show_helper(char *buf, enum msg_index_t first,
    709				   enum msg_index_t last)
    710{
    711	size_t bufsize = PAGE_SIZE;
    712	char *buf_pointer = buf;
    713	int printed;
    714	enum msg_index_t cursor;
    715	int index = 0;
    716	*buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
    717
    718	for (cursor = first; cursor <= last; cursor++, index++) {
    719		if (bufsize <= 1)
    720			break;
    721		printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
    722				    index, spk_msg_get(cursor));
    723		buf_pointer += printed;
    724		bufsize -= printed;
    725	}
    726
    727	return buf_pointer - buf;
    728}
    729
    730static void report_msg_status(int reset, int received, int used,
    731			      int rejected, char *groupname)
    732{
    733	int len;
    734	char buf[160];
    735
    736	if (reset) {
    737		pr_info("i18n messages from group %s reset to defaults\n",
    738			groupname);
    739	} else if (received) {
    740		len = snprintf(buf, sizeof(buf),
    741			       " updated %d of %d i18n messages from group %s\n",
    742				       used, received, groupname);
    743		if (rejected)
    744			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
    745				 " with %d reject%s\n",
    746				 rejected, rejected > 1 ? "s" : "");
    747		pr_info("%s", buf);
    748	}
    749}
    750
    751static ssize_t message_store_helper(const char *buf, size_t count,
    752				    struct msg_group_t *group)
    753{
    754	char *cp = (char *)buf;
    755	char *end = cp + count;
    756	char *linefeed = NULL;
    757	char *temp = NULL;
    758	ssize_t msg_stored = 0;
    759	ssize_t retval = count;
    760	size_t desc_length = 0;
    761	unsigned long index = 0;
    762	int received = 0;
    763	int used = 0;
    764	int rejected = 0;
    765	int reset = 0;
    766	enum msg_index_t firstmessage = group->start;
    767	enum msg_index_t lastmessage = group->end;
    768	enum msg_index_t curmessage;
    769
    770	while (cp < end) {
    771		while ((cp < end) && (*cp == ' ' || *cp == '\t'))
    772			cp++;
    773
    774		if (cp == end)
    775			break;
    776		if (strchr("dDrR", *cp)) {
    777			reset = 1;
    778			break;
    779		}
    780		received++;
    781
    782		linefeed = strchr(cp, '\n');
    783		if (!linefeed) {
    784			rejected++;
    785			break;
    786		}
    787
    788		if (!isdigit(*cp)) {
    789			rejected++;
    790			cp = linefeed + 1;
    791			continue;
    792		}
    793
    794		/*
    795		 * Do not replace with kstrtoul:
    796		 * here we need temp to be updated
    797		 */
    798		index = simple_strtoul(cp, &temp, 10);
    799
    800		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
    801			temp++;
    802
    803		desc_length = linefeed - temp;
    804		curmessage = firstmessage + index;
    805
    806		/*
    807		 * Note the check (curmessage < firstmessage).  It is not
    808		 * redundant.  Suppose that the user gave us an index
    809		 * equal to ULONG_MAX - 1.  If firstmessage > 1, then
    810		 * firstmessage + index < firstmessage!
    811		 */
    812
    813		if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
    814			rejected++;
    815			cp = linefeed + 1;
    816			continue;
    817		}
    818
    819		msg_stored = spk_msg_set(curmessage, temp, desc_length);
    820		if (msg_stored < 0) {
    821			retval = msg_stored;
    822			if (msg_stored == -ENOMEM)
    823				reset = 1;
    824			break;
    825		}
    826
    827		used++;
    828
    829		cp = linefeed + 1;
    830	}
    831
    832	if (reset)
    833		spk_reset_msg_group(group);
    834
    835	report_msg_status(reset, received, used, rejected, group->name);
    836	return retval;
    837}
    838
    839static ssize_t message_show(struct kobject *kobj,
    840			    struct kobj_attribute *attr, char *buf)
    841{
    842	ssize_t retval = 0;
    843	struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
    844	unsigned long flags;
    845
    846	if (WARN_ON(!group))
    847		return -EINVAL;
    848
    849	spin_lock_irqsave(&speakup_info.spinlock, flags);
    850	retval = message_show_helper(buf, group->start, group->end);
    851	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
    852	return retval;
    853}
    854
    855static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
    856			     const char *buf, size_t count)
    857{
    858	struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
    859
    860	if (WARN_ON(!group))
    861		return -EINVAL;
    862
    863	return message_store_helper(buf, count, group);
    864}
    865
    866/*
    867 * Declare the attributes.
    868 */
    869static struct kobj_attribute keymap_attribute =
    870	__ATTR_RW(keymap);
    871static struct kobj_attribute silent_attribute =
    872	__ATTR_WO(silent);
    873static struct kobj_attribute synth_attribute =
    874	__ATTR_RW(synth);
    875static struct kobj_attribute synth_direct_attribute =
    876	__ATTR_WO(synth_direct);
    877static struct kobj_attribute version_attribute =
    878	__ATTR_RO(version);
    879
    880static struct kobj_attribute delimiters_attribute =
    881	__ATTR(delimiters, 0644, punc_show, punc_store);
    882static struct kobj_attribute ex_num_attribute =
    883	__ATTR(ex_num, 0644, punc_show, punc_store);
    884static struct kobj_attribute punc_all_attribute =
    885	__ATTR(punc_all, 0644, punc_show, punc_store);
    886static struct kobj_attribute punc_most_attribute =
    887	__ATTR(punc_most, 0644, punc_show, punc_store);
    888static struct kobj_attribute punc_some_attribute =
    889	__ATTR(punc_some, 0644, punc_show, punc_store);
    890static struct kobj_attribute repeats_attribute =
    891	__ATTR(repeats, 0644, punc_show, punc_store);
    892
    893static struct kobj_attribute attrib_bleep_attribute =
    894	__ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);
    895static struct kobj_attribute bell_pos_attribute =
    896	__ATTR(bell_pos, 0644, spk_var_show, spk_var_store);
    897static struct kobj_attribute bleep_time_attribute =
    898	__ATTR(bleep_time, 0644, spk_var_show, spk_var_store);
    899static struct kobj_attribute bleeps_attribute =
    900	__ATTR(bleeps, 0644, spk_var_show, spk_var_store);
    901static struct kobj_attribute cursor_time_attribute =
    902	__ATTR(cursor_time, 0644, spk_var_show, spk_var_store);
    903static struct kobj_attribute key_echo_attribute =
    904	__ATTR(key_echo, 0644, spk_var_show, spk_var_store);
    905static struct kobj_attribute no_interrupt_attribute =
    906	__ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);
    907static struct kobj_attribute punc_level_attribute =
    908	__ATTR(punc_level, 0644, spk_var_show, spk_var_store);
    909static struct kobj_attribute reading_punc_attribute =
    910	__ATTR(reading_punc, 0644, spk_var_show, spk_var_store);
    911static struct kobj_attribute say_control_attribute =
    912	__ATTR(say_control, 0644, spk_var_show, spk_var_store);
    913static struct kobj_attribute say_word_ctl_attribute =
    914	__ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
    915static struct kobj_attribute spell_delay_attribute =
    916	__ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
    917
    918/*
    919 * These attributes are i18n related.
    920 */
    921static struct kobj_attribute announcements_attribute =
    922	__ATTR(announcements, 0644, message_show, message_store);
    923static struct kobj_attribute characters_attribute =
    924	__ATTR(characters, 0644, chars_chartab_show,
    925	       chars_chartab_store);
    926static struct kobj_attribute chartab_attribute =
    927	__ATTR(chartab, 0644, chars_chartab_show,
    928	       chars_chartab_store);
    929static struct kobj_attribute ctl_keys_attribute =
    930	__ATTR(ctl_keys, 0644, message_show, message_store);
    931static struct kobj_attribute colors_attribute =
    932	__ATTR(colors, 0644, message_show, message_store);
    933static struct kobj_attribute formatted_attribute =
    934	__ATTR(formatted, 0644, message_show, message_store);
    935static struct kobj_attribute function_names_attribute =
    936	__ATTR(function_names, 0644, message_show, message_store);
    937static struct kobj_attribute key_names_attribute =
    938	__ATTR(key_names, 0644, message_show, message_store);
    939static struct kobj_attribute states_attribute =
    940	__ATTR(states, 0644, message_show, message_store);
    941
    942/*
    943 * Create groups of attributes so that we can create and destroy them all
    944 * at once.
    945 */
    946static struct attribute *main_attrs[] = {
    947	&keymap_attribute.attr,
    948	&silent_attribute.attr,
    949	&synth_attribute.attr,
    950	&synth_direct_attribute.attr,
    951	&version_attribute.attr,
    952	&delimiters_attribute.attr,
    953	&ex_num_attribute.attr,
    954	&punc_all_attribute.attr,
    955	&punc_most_attribute.attr,
    956	&punc_some_attribute.attr,
    957	&repeats_attribute.attr,
    958	&attrib_bleep_attribute.attr,
    959	&bell_pos_attribute.attr,
    960	&bleep_time_attribute.attr,
    961	&bleeps_attribute.attr,
    962	&cursor_time_attribute.attr,
    963	&key_echo_attribute.attr,
    964	&no_interrupt_attribute.attr,
    965	&punc_level_attribute.attr,
    966	&reading_punc_attribute.attr,
    967	&say_control_attribute.attr,
    968	&say_word_ctl_attribute.attr,
    969	&spell_delay_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 in
    988 * the kobject directory.  If we specify a name, a subdirectory will be
    989 * created for the attributes with the directory being the name of the
    990 * 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 to
   1013	 * userspace.  That is why this function should not be used for
   1014	 * any type of dynamic kobjects, where the name and number are
   1015	 * 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}