Linux Audio

Check our new training course

Loading...
   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 = kmemdup(buf, count + 1, GFP_ATOMIC);
 269	if (!in_buff) {
 270		spk_unlock(flags);
 271		return -ENOMEM;
 272	}
 273	if (strchr("dDrR", *in_buff)) {
 274		set_key_info(key_defaults, key_buf);
 275		pr_info("keymap set to default values\n");
 276		kfree(in_buff);
 277		spk_unlock(flags);
 278		return count;
 279	}
 280	if (in_buff[count - 1] == '\n')
 281		in_buff[count - 1] = '\0';
 282	cp = in_buff;
 283	cp1 = (u_char *)in_buff;
 284	for (i = 0; i < 3; i++) {
 285		cp = s2uchar(cp, cp1);
 286		cp1++;
 287	}
 288	i = (int)cp1[-2]+1;
 289	i *= (int)cp1[-1]+1;
 290	i += 2; /* 0 and last map ver */
 291	if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||
 292			i+SHIFT_TBL_SIZE+4 >= sizeof(key_buf)) {
 293		pr_warn("i %d %d %d %d\n", i,
 294				(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
 295		kfree(in_buff);
 296		spk_unlock(flags);
 297		return -EINVAL;
 298	}
 299	while (--i >= 0) {
 300		cp = s2uchar(cp, cp1);
 301		cp1++;
 302		if (!(*cp))
 303			break;
 304	}
 305	if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {
 306		ret = -EINVAL;
 307		pr_warn("end %d %d %d %d\n", i,
 308				(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
 309	} else {
 310		if (set_key_info(in_buff, key_buf)) {
 311			set_key_info(key_defaults, key_buf);
 312			ret = -EINVAL;
 313			pr_warn("set key failed\n");
 314		}
 315	}
 316	kfree(in_buff);
 317	spk_unlock(flags);
 318	return ret;
 319}
 320
 321/*
 322 * This is called when a user changes the value of the silent parameter.
 323 */
 324static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,
 325	const char *buf, size_t count)
 326{
 327	int len;
 328	struct vc_data *vc = vc_cons[fg_console].d;
 329	char ch = 0;
 330	char shut;
 331	unsigned long flags;
 332
 333	len = strlen(buf);
 334	if (len > 0 && len < 3) {
 335		ch = buf[0];
 336		if (ch == '\n')
 337			ch = '0';
 338	}
 339	if (ch < '0' || ch > '7') {
 340		pr_warn("silent value '%c' not in range (0,7)\n", ch);
 341		return -EINVAL;
 342	}
 343	spk_lock(flags);
 344	if (ch&2) {
 345		shut = 1;
 346		do_flush();
 347	} else {
 348		shut = 0;
 349	}
 350	if (ch&4)
 351		shut |= 0x40;
 352	if (ch&1)
 353		spk_shut_up |= shut;
 354	else
 355		spk_shut_up &= ~shut;
 356	spk_unlock(flags);
 357	return count;
 358}
 359
 360/*
 361 * This is called when a user reads the synth setting.
 362 */
 363static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,
 364	char *buf)
 365{
 366	int rv;
 367
 368	if (synth == NULL)
 369		rv = sprintf(buf, "%s\n", "none");
 370	else
 371		rv = sprintf(buf, "%s\n", synth->name);
 372	return rv;
 373}
 374
 375/*
 376 * This is called when a user requests to change synthesizers.
 377 */
 378static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,
 379	const char *buf, size_t count)
 380{
 381	int len;
 382	char new_synth_name[10];
 383
 384	len = strlen(buf);
 385	if (len < 2 || len > 9)
 386		return -EINVAL;
 387	strncpy(new_synth_name, buf, len);
 388	if (new_synth_name[len - 1] == '\n')
 389		len--;
 390	new_synth_name[len] = '\0';
 391	strlwr(new_synth_name);
 392	if ((synth != NULL) && (!strcmp(new_synth_name, synth->name))) {
 393		pr_warn("%s already in use\n", new_synth_name);
 394	} else if (synth_init(new_synth_name) != 0) {
 395		pr_warn("failed to init synth %s\n", new_synth_name);
 396		return -ENODEV;
 397	}
 398	return count;
 399}
 400
 401/*
 402 * This is called when text is sent to the synth via the synth_direct file.
 403 */
 404static ssize_t synth_direct_store(struct kobject *kobj,
 405	struct kobj_attribute *attr, const char *buf, size_t count)
 406{
 407	u_char tmp[256];
 408	int len;
 409	int bytes;
 410	const char *ptr = buf;
 411
 412	if (!synth)
 413		return -EPERM;
 414
 415	len = strlen(buf);
 416	while (len > 0) {
 417		bytes = min_t(size_t, len, 250);
 418		strncpy(tmp, ptr, bytes);
 419		tmp[bytes] = '\0';
 420		xlate(tmp);
 421		synth_printf("%s", tmp);
 422		ptr += bytes;
 423		len -= bytes;
 424	}
 425	return count;
 426}
 427
 428/*
 429 * This function is called when a user reads the version.
 430 */
 431static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
 432	char *buf)
 433{
 434	char *cp;
 435
 436	cp = buf;
 437	cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
 438	if (synth)
 439		cp += sprintf(cp, "%s synthesizer driver version %s\n",
 440		synth->name, synth->version);
 441	return cp - buf;
 442}
 443
 444/*
 445 * This is called when a user reads the punctuation settings.
 446 */
 447static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
 448	char *buf)
 449{
 450	int i;
 451	char *cp = buf;
 452	struct st_var_header *p_header;
 453	struct punc_var_t *var;
 454	struct st_bits_data *pb;
 455	short mask;
 456	unsigned long flags;
 457
 458	p_header = var_header_by_name(attr->attr.name);
 459	if (p_header == NULL) {
 460		pr_warn("p_header is null, attr->attr.name is %s\n",
 461			attr->attr.name);
 462		return -EINVAL;
 463	}
 464
 465	var = get_punc_var(p_header->var_id);
 466	if (var == NULL) {
 467		pr_warn("var is null, p_header->var_id is %i\n",
 468				p_header->var_id);
 469		return -EINVAL;
 470	}
 471
 472	spk_lock(flags);
 473	pb = (struct st_bits_data *) &punc_info[var->value];
 474	mask = pb->mask;
 475	for (i = 33; i < 128; i++) {
 476		if (!(spk_chartab[i]&mask))
 477			continue;
 478		*cp++ = (char)i;
 479	}
 480	spk_unlock(flags);
 481	return cp-buf;
 482}
 483
 484/*
 485 * This is called when a user changes the punctuation settings.
 486 */
 487static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
 488			 const char *buf, size_t count)
 489{
 490	int x;
 491	struct st_var_header *p_header;
 492	struct punc_var_t *var;
 493	char punc_buf[100];
 494	unsigned long flags;
 495
 496	x = strlen(buf);
 497	if (x < 1 || x > 99)
 498		return -EINVAL;
 499
 500	p_header = var_header_by_name(attr->attr.name);
 501	if (p_header == NULL) {
 502		pr_warn("p_header is null, attr->attr.name is %s\n",
 503			attr->attr.name);
 504		return -EINVAL;
 505	}
 506
 507	var = get_punc_var(p_header->var_id);
 508	if (var == NULL) {
 509		pr_warn("var is null, p_header->var_id is %i\n",
 510				p_header->var_id);
 511		return -EINVAL;
 512	}
 513
 514	strncpy(punc_buf, buf, x);
 515
 516	while (x && punc_buf[x - 1] == '\n')
 517		x--;
 518	punc_buf[x] = '\0';
 519
 520	spk_lock(flags);
 521
 522	if (*punc_buf == 'd' || *punc_buf == 'r')
 523		x = set_mask_bits(0, var->value, 3);
 524	else
 525		x = set_mask_bits(punc_buf, var->value, 3);
 526
 527	spk_unlock(flags);
 528	return count;
 529}
 530
 531/*
 532 * This function is called when a user reads one of the variable parameters.
 533 */
 534ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
 535	char *buf)
 536{
 537	int rv = 0;
 538	struct st_var_header *param;
 539	struct var_t *var;
 540		char *cp1;
 541	char *cp;
 542	char ch;
 543	unsigned long flags;
 544
 545	param = var_header_by_name(attr->attr.name);
 546	if (param == NULL)
 547		return -EINVAL;
 548
 549	spk_lock(flags);
 550	var = (struct var_t *) param->data;
 551	switch (param->var_type) {
 552	case VAR_NUM:
 553	case VAR_TIME:
 554		if (var)
 555			rv = sprintf(buf, "%i\n", var->u.n.value);
 556		else
 557			rv = sprintf(buf, "0\n");
 558		break;
 559	case VAR_STRING:
 560		if (var) {
 561			cp1 = buf;
 562			*cp1++ = '"';
 563			for (cp = (char *)param->p_val; (ch = *cp); cp++) {
 564				if (ch >= ' ' && ch < '~')
 565					*cp1++ = ch;
 566				else
 567					cp1 += sprintf(cp1, "\\""x%02x", ch);
 568			}
 569			*cp1++ = '"';
 570			*cp1++ = '\n';
 571			*cp1 = '\0';
 572			rv = cp1-buf;
 573		} else {
 574			rv = sprintf(buf, "\"\"\n");
 575		}
 576		break;
 577	default:
 578		rv = sprintf(buf, "Bad parameter  %s, type %i\n",
 579			param->name, param->var_type);
 580		break;
 581	}
 582	spk_unlock(flags);
 583	return rv;
 584}
 585EXPORT_SYMBOL_GPL(spk_var_show);
 586
 587/*
 588 * This function is called when a user echos a value to one of the
 589 * variable parameters.
 590 */
 591ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
 592			 const char *buf, size_t count)
 593{
 594	struct st_var_header *param;
 595	int ret;
 596	int len;
 597	char *cp;
 598	struct var_t *var_data;
 599	int value;
 600	unsigned long flags;
 601
 602	param = var_header_by_name(attr->attr.name);
 603	if (param == NULL)
 604		return -EINVAL;
 605	if (param->data == NULL)
 606		return 0;
 607	ret = 0;
 608	cp = xlate((char *) buf);
 609
 610	spk_lock(flags);
 611	switch (param->var_type) {
 612	case VAR_NUM:
 613	case VAR_TIME:
 614		if (*cp == 'd' || *cp == 'r' || *cp == '\0')
 615			len = E_DEFAULT;
 616		else if (*cp == '+' || *cp == '-')
 617			len = E_INC;
 618		else
 619			len = E_SET;
 620		speakup_s2i(cp, &value);
 621		ret = set_num_var(value, param, len);
 622		if (ret == E_RANGE) {
 623			var_data = param->data;
 624			pr_warn("value for %s out of range, expect %d to %d\n",
 625				attr->attr.name,
 626				var_data->u.n.low, var_data->u.n.high);
 627		}
 628		break;
 629	case VAR_STRING:
 630		len = strlen(buf);
 631		if ((len >= 1) && (buf[len - 1] == '\n'))
 632			--len;
 633		if ((len >= 2) && (buf[0] == '"') && (buf[len - 1] == '"')) {
 634			++buf;
 635			len -= 2;
 636		}
 637		cp = (char *) buf;
 638		cp[len] = '\0';
 639		ret = set_string_var(buf, param, len);
 640		if (ret == E_TOOLONG)
 641			pr_warn("value too long for %s\n",
 642					attr->attr.name);
 643		break;
 644	default:
 645		pr_warn("%s unknown type %d\n",
 646			param->name, (int)param->var_type);
 647	break;
 648	}
 649	/*
 650	 * If voice was just changed, we might need to reset our default
 651	 * pitch and volume.
 652	 */
 653	if (strcmp(attr->attr.name, "voice") == 0) {
 654		if (synth && synth->default_pitch) {
 655			param = var_header_by_name("pitch");
 656			if (param)  {
 657				set_num_var(synth->default_pitch[value], param,
 658					E_NEW_DEFAULT);
 659				set_num_var(0, param, E_DEFAULT);
 660			}
 661		}
 662		if (synth && synth->default_vol) {
 663			param = var_header_by_name("vol");
 664			if (param)  {
 665				set_num_var(synth->default_vol[value], param,
 666					E_NEW_DEFAULT);
 667				set_num_var(0, param, E_DEFAULT);
 668			}
 669		}
 670	}
 671	spk_unlock(flags);
 672
 673	if (ret == SET_DEFAULT)
 674		pr_info("%s reset to default value\n", attr->attr.name);
 675	return count;
 676}
 677EXPORT_SYMBOL_GPL(spk_var_store);
 678
 679/*
 680 * Functions for reading and writing lists of i18n messages.  Incomplete.
 681 */
 682
 683static ssize_t message_show_helper(char *buf, enum msg_index_t first,
 684	enum msg_index_t last)
 685{
 686	size_t bufsize = PAGE_SIZE;
 687	char *buf_pointer = buf;
 688	int printed;
 689	enum msg_index_t cursor;
 690	int index = 0;
 691	*buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
 692
 693	for (cursor = first; cursor <= last; cursor++, index++) {
 694		if (bufsize <= 1)
 695			break;
 696		printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
 697			index, msg_get(cursor));
 698		buf_pointer += printed;
 699		bufsize -= printed;
 700	}
 701
 702	return buf_pointer - buf;
 703}
 704
 705static void report_msg_status(int reset, int received, int used,
 706	int rejected, char *groupname)
 707{
 708	int len;
 709	char buf[160];
 710
 711	if (reset) {
 712		pr_info("i18n messages from group %s reset to defaults\n",
 713			groupname);
 714	} else if (received) {
 715		len = snprintf(buf, sizeof(buf),
 716			       " updated %d of %d i18n messages from group %s\n",
 717				       used, received, groupname);
 718		if (rejected)
 719			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
 720				 " with %d reject%s\n",
 721				 rejected, rejected > 1 ? "s" : "");
 722		printk(buf);
 723	}
 724}
 725
 726static ssize_t message_store_helper(const char *buf, size_t count,
 727	struct msg_group_t *group)
 728{
 729	char *cp = (char *) buf;
 730	char *end = cp + count;
 731	char *linefeed = NULL;
 732	char *temp = NULL;
 733	ssize_t msg_stored = 0;
 734	ssize_t retval = count;
 735	size_t desc_length = 0;
 736	unsigned long index = 0;
 737	int received = 0;
 738	int used = 0;
 739	int rejected = 0;
 740	int reset = 0;
 741	enum msg_index_t firstmessage = group->start;
 742	enum msg_index_t lastmessage = group->end;
 743	enum msg_index_t curmessage;
 744
 745	while (cp < end) {
 746
 747		while ((cp < end) && (*cp == ' ' || *cp == '\t'))
 748			cp++;
 749
 750		if (cp == end)
 751			break;
 752		if (strchr("dDrR", *cp)) {
 753			reset = 1;
 754			break;
 755		}
 756		received++;
 757
 758		linefeed = strchr(cp, '\n');
 759		if (!linefeed) {
 760			rejected++;
 761			break;
 762		}
 763
 764		if (!isdigit(*cp)) {
 765			rejected++;
 766			cp = linefeed + 1;
 767			continue;
 768		}
 769
 770		index = simple_strtoul(cp, &temp, 10);
 771
 772		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
 773			temp++;
 774
 775		desc_length = linefeed - temp;
 776		curmessage = firstmessage + index;
 777
 778		/*
 779		 * Note the check (curmessage < firstmessage).  It is not
 780		 * redundant.  Suppose that the user gave us an index
 781		 * equal to ULONG_MAX - 1.  If firstmessage > 1, then
 782		 * firstmessage + index < firstmessage!
 783		 */
 784
 785		if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
 786			rejected++;
 787			cp = linefeed + 1;
 788			continue;
 789		}
 790
 791		msg_stored = msg_set(curmessage, temp, desc_length);
 792		if (msg_stored < 0) {
 793			retval = msg_stored;
 794			if (msg_stored == -ENOMEM)
 795				reset = 1;
 796			break;
 797		} else {
 798			used++;
 799		}
 800
 801		cp = linefeed + 1;
 802	}
 803
 804	if (reset)
 805		reset_msg_group(group);
 806
 807	report_msg_status(reset, received, used, rejected, group->name);
 808	return retval;
 809}
 810
 811static ssize_t message_show(struct kobject *kobj,
 812	struct kobj_attribute *attr, char *buf)
 813{
 814	ssize_t retval = 0;
 815	struct msg_group_t *group = find_msg_group(attr->attr.name);
 816	unsigned long flags;
 817
 818	BUG_ON(!group);
 819	spk_lock(flags);
 820	retval = message_show_helper(buf, group->start, group->end);
 821	spk_unlock(flags);
 822	return retval;
 823}
 824
 825static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
 826	const char *buf, size_t count)
 827{
 828	ssize_t retval = 0;
 829	struct msg_group_t *group = find_msg_group(attr->attr.name);
 830
 831	BUG_ON(!group);
 832	retval = message_store_helper(buf, count, group);
 833	return retval;
 834}
 835
 836/*
 837 * Declare the attributes.
 838 */
 839static struct kobj_attribute keymap_attribute =
 840	__ATTR(keymap, ROOT_W, keymap_show, keymap_store);
 841static struct kobj_attribute silent_attribute =
 842	__ATTR(silent, USER_W, NULL, silent_store);
 843static struct kobj_attribute synth_attribute =
 844	__ATTR(synth, USER_RW, synth_show, synth_store);
 845static struct kobj_attribute synth_direct_attribute =
 846	__ATTR(synth_direct, USER_W, NULL, synth_direct_store);
 847static struct kobj_attribute version_attribute =
 848	__ATTR_RO(version);
 849
 850static struct kobj_attribute delimiters_attribute =
 851	__ATTR(delimiters, USER_RW, punc_show, punc_store);
 852static struct kobj_attribute ex_num_attribute =
 853	__ATTR(ex_num, USER_RW, punc_show, punc_store);
 854static struct kobj_attribute punc_all_attribute =
 855	__ATTR(punc_all, USER_RW, punc_show, punc_store);
 856static struct kobj_attribute punc_most_attribute =
 857	__ATTR(punc_most, USER_RW, punc_show, punc_store);
 858static struct kobj_attribute punc_some_attribute =
 859	__ATTR(punc_some, USER_RW, punc_show, punc_store);
 860static struct kobj_attribute repeats_attribute =
 861	__ATTR(repeats, USER_RW, punc_show, punc_store);
 862
 863static struct kobj_attribute attrib_bleep_attribute =
 864	__ATTR(attrib_bleep, USER_RW, spk_var_show, spk_var_store);
 865static struct kobj_attribute bell_pos_attribute =
 866	__ATTR(bell_pos, USER_RW, spk_var_show, spk_var_store);
 867static struct kobj_attribute bleep_time_attribute =
 868	__ATTR(bleep_time, USER_RW, spk_var_show, spk_var_store);
 869static struct kobj_attribute bleeps_attribute =
 870	__ATTR(bleeps, USER_RW, spk_var_show, spk_var_store);
 871static struct kobj_attribute cursor_time_attribute =
 872	__ATTR(cursor_time, USER_RW, spk_var_show, spk_var_store);
 873static struct kobj_attribute key_echo_attribute =
 874	__ATTR(key_echo, USER_RW, spk_var_show, spk_var_store);
 875static struct kobj_attribute no_interrupt_attribute =
 876	__ATTR(no_interrupt, USER_RW, spk_var_show, spk_var_store);
 877static struct kobj_attribute punc_level_attribute =
 878	__ATTR(punc_level, USER_RW, spk_var_show, spk_var_store);
 879static struct kobj_attribute reading_punc_attribute =
 880	__ATTR(reading_punc, USER_RW, spk_var_show, spk_var_store);
 881static struct kobj_attribute say_control_attribute =
 882	__ATTR(say_control, USER_RW, spk_var_show, spk_var_store);
 883static struct kobj_attribute say_word_ctl_attribute =
 884	__ATTR(say_word_ctl, USER_RW, spk_var_show, spk_var_store);
 885static struct kobj_attribute spell_delay_attribute =
 886	__ATTR(spell_delay, USER_RW, spk_var_show, spk_var_store);
 887
 888/*
 889 * These attributes are i18n related.
 890 */
 891static struct kobj_attribute announcements_attribute =
 892	__ATTR(announcements, USER_RW, message_show, message_store);
 893static struct kobj_attribute characters_attribute =
 894	__ATTR(characters, USER_RW, chars_chartab_show, chars_chartab_store);
 895static struct kobj_attribute chartab_attribute =
 896	__ATTR(chartab, USER_RW, chars_chartab_show, chars_chartab_store);
 897static struct kobj_attribute ctl_keys_attribute =
 898	__ATTR(ctl_keys, USER_RW, message_show, message_store);
 899static struct kobj_attribute colors_attribute =
 900	__ATTR(colors, USER_RW, message_show, message_store);
 901static struct kobj_attribute formatted_attribute =
 902	__ATTR(formatted, USER_RW, message_show, message_store);
 903static struct kobj_attribute function_names_attribute =
 904	__ATTR(function_names, USER_RW, message_show, message_store);
 905static struct kobj_attribute key_names_attribute =
 906	__ATTR(key_names, USER_RW, message_show, message_store);
 907static struct kobj_attribute states_attribute =
 908	__ATTR(states, USER_RW, message_show, message_store);
 909
 910/*
 911 * Create groups of attributes so that we can create and destroy them all
 912 * at once.
 913 */
 914static struct attribute *main_attrs[] = {
 915	&keymap_attribute.attr,
 916	&silent_attribute.attr,
 917	&synth_attribute.attr,
 918	&synth_direct_attribute.attr,
 919	&version_attribute.attr,
 920	&delimiters_attribute.attr,
 921	&ex_num_attribute.attr,
 922	&punc_all_attribute.attr,
 923	&punc_most_attribute.attr,
 924	&punc_some_attribute.attr,
 925	&repeats_attribute.attr,
 926	&attrib_bleep_attribute.attr,
 927	&bell_pos_attribute.attr,
 928	&bleep_time_attribute.attr,
 929	&bleeps_attribute.attr,
 930	&cursor_time_attribute.attr,
 931	&key_echo_attribute.attr,
 932	&no_interrupt_attribute.attr,
 933	&punc_level_attribute.attr,
 934	&reading_punc_attribute.attr,
 935	&say_control_attribute.attr,
 936	&say_word_ctl_attribute.attr,
 937	&spell_delay_attribute.attr,
 938	NULL,
 939};
 940
 941static struct attribute *i18n_attrs[] = {
 942	&announcements_attribute.attr,
 943	&characters_attribute.attr,
 944	&chartab_attribute.attr,
 945	&ctl_keys_attribute.attr,
 946	&colors_attribute.attr,
 947	&formatted_attribute.attr,
 948	&function_names_attribute.attr,
 949	&key_names_attribute.attr,
 950	&states_attribute.attr,
 951	NULL,
 952};
 953
 954/*
 955 * An unnamed attribute group will put all of the attributes directly in
 956 * the kobject directory.  If we specify a name, a subdirectory will be
 957 * created for the attributes with the directory being the name of the
 958 * attribute group.
 959 */
 960static struct attribute_group main_attr_group = {
 961	.attrs = main_attrs,
 962};
 963
 964static struct attribute_group i18n_attr_group = {
 965	.attrs = i18n_attrs,
 966	.name = "i18n",
 967};
 968
 969static struct kobject *accessibility_kobj;
 970struct kobject *speakup_kobj;
 971
 972int speakup_kobj_init(void)
 973{
 974	int retval;
 975
 976	/*
 977	 * Create a simple kobject with the name of "accessibility",
 978	 * located under /sys/
 979	 *
 980	 * As this is a simple directory, no uevent will be sent to
 981	 * userspace.  That is why this function should not be used for
 982	 * any type of dynamic kobjects, where the name and number are
 983	 * not known ahead of time.
 984	 */
 985	accessibility_kobj = kobject_create_and_add("accessibility", NULL);
 986	if (!accessibility_kobj) {
 987		retval = -ENOMEM;
 988		goto out;
 989	}
 990
 991	speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj);
 992	if (!speakup_kobj) {
 993		retval = -ENOMEM;
 994		goto err_acc;
 995	}
 996
 997	/* Create the files associated with this kobject */
 998	retval = sysfs_create_group(speakup_kobj, &main_attr_group);
 999	if (retval)
1000		goto err_speakup;
1001
1002	retval = sysfs_create_group(speakup_kobj, &i18n_attr_group);
1003	if (retval)
1004		goto err_group;
1005
1006	goto out;
1007
1008err_group:
1009	sysfs_remove_group(speakup_kobj, &main_attr_group);
1010err_speakup:
1011	kobject_put(speakup_kobj);
1012err_acc:
1013	kobject_put(accessibility_kobj);
1014out:
1015	return retval;
1016}
1017
1018void speakup_kobj_exit(void)
1019{
1020	sysfs_remove_group(speakup_kobj, &i18n_attr_group);
1021	sysfs_remove_group(speakup_kobj, &main_attr_group);
1022	kobject_put(speakup_kobj);
1023	kobject_put(accessibility_kobj);
1024}