Linux Audio

Check our new training course

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