1056 lines · 25.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Speakup kobject implementation * * Copyright (C) 2009 William Hubbs * * This code is based on kobject-example.c, which came with linux 2.6.x. * * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2007 Novell Inc. * * Released under the GPL version 2 only. * */#include <linux/slab.h>		/* For kmalloc. */#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/string.h>#include <linux/string_helpers.h>#include <linux/sysfs.h>#include <linux/ctype.h> #include "speakup.h"#include "spk_priv.h" /* * This is called when a user reads the characters or chartab sys file. */static ssize_t chars_chartab_show(struct kobject *kobj,				  struct kobj_attribute *attr, char *buf){	int i;	int len = 0;	char *cp;	char *buf_pointer = buf;	size_t bufsize = PAGE_SIZE;	unsigned long flags; 	spin_lock_irqsave(&speakup_info.spinlock, flags);	*buf_pointer = '\0';	for (i = 0; i < 256; i++) {		if (bufsize <= 1)			break;		if (strcmp("characters", attr->attr.name) == 0) {			len = scnprintf(buf_pointer, bufsize, "%d\t%s\n",					i, spk_characters[i]);		} else {	/* show chartab entry */			if (IS_TYPE(i, B_CTL))				cp = "B_CTL";			else if (IS_TYPE(i, WDLM))				cp = "WDLM";			else if (IS_TYPE(i, A_PUNC))				cp = "A_PUNC";			else if (IS_TYPE(i, PUNC))				cp = "PUNC";			else if (IS_TYPE(i, NUM))				cp = "NUM";			else if (IS_TYPE(i, A_CAP))				cp = "A_CAP";			else if (IS_TYPE(i, ALPHA))				cp = "ALPHA";			else if (IS_TYPE(i, B_CAPSYM))				cp = "B_CAPSYM";			else if (IS_TYPE(i, B_SYM))				cp = "B_SYM";			else				cp = "0";			len =			    scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp);		}		bufsize -= len;		buf_pointer += len;	}	spin_unlock_irqrestore(&speakup_info.spinlock, flags);	return buf_pointer - buf;} /* * Print informational messages or warnings after updating * character descriptions or chartab entries. */static void report_char_chartab_status(int reset, int received, int used,				       int rejected, int do_characters){	static char const *object_type[] = {		"character class entries",		"character descriptions",	};	int len;	char buf[80]; 	if (reset) {		pr_info("%s reset to defaults\n", object_type[do_characters]);	} else if (received) {		len = scnprintf(buf, sizeof(buf),				" updated %d of %d %s\n",				used, received, object_type[do_characters]);		if (rejected)			snprintf(buf + (len - 1), sizeof(buf) - (len - 1),				 " with %d reject%s\n",				 rejected, str_plural(rejected));		pr_info("%s", buf);	}} /* * This is called when a user changes the characters or chartab parameters. */static ssize_t chars_chartab_store(struct kobject *kobj,				   struct kobj_attribute *attr,				   const char *buf, size_t count){	char *cp = (char *)buf;	char *end = cp + count; /* the null at the end of the buffer */	char *linefeed = NULL;	char keyword[MAX_DESC_LEN + 1];	char *outptr = NULL;	/* Will hold keyword or desc. */	char *temp = NULL;	char *desc = NULL;	ssize_t retval = count;	unsigned long flags;	unsigned long index = 0;	u16 charclass = 0;	int received = 0;	int used = 0;	int rejected = 0;	int reset = 0;	int do_characters = !strcmp(attr->attr.name, "characters");	size_t desc_length = 0;	int i; 	spin_lock_irqsave(&speakup_info.spinlock, flags);	while (cp < end) {		while ((cp < end) && (*cp == ' ' || *cp == '\t'))			cp++; 		if (cp == end)			break;		if ((*cp == '\n') || strchr("dDrR", *cp)) {			reset = 1;			break;		}		received++; 		linefeed = strchr(cp, '\n');		if (!linefeed) {			rejected++;			break;		} 		if (!isdigit(*cp)) {			rejected++;			cp = linefeed + 1;			continue;		} 		/*		 * Do not replace with kstrtoul:		 * here we need temp to be updated		 */		index = simple_strtoul(cp, &temp, 10);		if (index > 255) {			rejected++;			cp = linefeed + 1;			continue;		} 		while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))			temp++; 		desc_length = linefeed - temp;		if (desc_length > MAX_DESC_LEN) {			rejected++;			cp = linefeed + 1;			continue;		}		if (do_characters) {			desc = kmalloc(desc_length + 1, GFP_ATOMIC);			if (!desc) {				retval = -ENOMEM;				reset = 1;	/* just reset on error. */				break;			}			outptr = desc;		} else {			outptr = keyword;		} 		for (i = 0; i < desc_length; i++)			outptr[i] = temp[i];		outptr[desc_length] = '\0'; 		if (do_characters) {			if (spk_characters[index] != spk_default_chars[index])				kfree(spk_characters[index]);			spk_characters[index] = desc;			used++;		} else {			charclass = spk_chartab_get_value(keyword);			if (charclass == 0) {				rejected++;				cp = linefeed + 1;				continue;			}			if (charclass != spk_chartab[index]) {				spk_chartab[index] = charclass;				used++;			}		}		cp = linefeed + 1;	} 	if (reset) {		if (do_characters)			spk_reset_default_chars();		else			spk_reset_default_chartab();	} 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);	report_char_chartab_status(reset, received, used, rejected,				   do_characters);	return retval;} /* * This is called when a user reads the keymap parameter. */static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr,			   char *buf){	char *cp = buf;	int i;	int n;	int num_keys;	int nstates;	u_char *cp1;	u_char ch;	unsigned long flags; 	spin_lock_irqsave(&speakup_info.spinlock, flags);	cp1 = spk_key_buf + SHIFT_TBL_SIZE;	num_keys = (int)(*cp1);	nstates = (int)cp1[1];	cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates);	cp1 += 2; /* now pointing at shift states */	/* dump num_keys+1 as first row is shift states + flags,	 * each subsequent row is key + states	 */	for (n = 0; n <= num_keys; n++) {		for (i = 0; i <= nstates; i++) {			ch = *cp1++;			cp += sprintf(cp, "%d,", (int)ch);			*cp++ = (i < nstates) ? SPACE : '\n';		}	}	cp += sprintf(cp, "0, %d\n", KEY_MAP_VER);	spin_unlock_irqrestore(&speakup_info.spinlock, flags);	return (int)(cp - buf);} /* * This is called when a user changes the keymap parameter. */static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr,			    const char *buf, size_t count){	int i;	ssize_t ret = count;	char *in_buff = NULL;	char *cp;	u_char *cp1;	unsigned long flags; 	spin_lock_irqsave(&speakup_info.spinlock, flags);	in_buff = kmemdup(buf, count + 1, GFP_ATOMIC);	if (!in_buff) {		spin_unlock_irqrestore(&speakup_info.spinlock, flags);		return -ENOMEM;	}	if (strchr("dDrR", *in_buff)) {		spk_set_key_info(spk_key_defaults, spk_key_buf);		pr_info("keymap set to default values\n");		kfree(in_buff);		spin_unlock_irqrestore(&speakup_info.spinlock, flags);		return count;	}	if (in_buff[count - 1] == '\n')		in_buff[count - 1] = '\0';	cp = in_buff;	cp1 = (u_char *)in_buff;	for (i = 0; i < 3; i++) {		cp = spk_s2uchar(cp, cp1);		cp1++;	}	i = (int)cp1[-2] + 1;	i *= (int)cp1[-1] + 1;	i += 2; /* 0 and last map ver */	if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||	    i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {		pr_warn("i %d %d %d %d\n", i,			(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);		kfree(in_buff);		spin_unlock_irqrestore(&speakup_info.spinlock, flags);		return -EINVAL;	}	while (--i >= 0) {		cp = spk_s2uchar(cp, cp1);		cp1++;		if (!(*cp))			break;	}	if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {		ret = -EINVAL;		pr_warn("end %d %d %d %d\n", i,			(int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);	} else {		if (spk_set_key_info(in_buff, spk_key_buf)) {			spk_set_key_info(spk_key_defaults, spk_key_buf);			ret = -EINVAL;			pr_warn("set key failed\n");		}	}	kfree(in_buff);	spin_unlock_irqrestore(&speakup_info.spinlock, flags);	return ret;} /* * This is called when a user changes the value of the silent parameter. */static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,			    const char *buf, size_t count){	int len;	struct vc_data *vc = vc_cons[fg_console].d;	char ch = 0;	char shut;	unsigned long flags; 	len = strlen(buf);	if (len > 0 && len < 3) {		ch = buf[0];		if (ch == '\n')			ch = '0';	}	if (ch < '0' || ch > '7') {		pr_warn("silent value '%c' not in range (0,7)\n", ch);		return -EINVAL;	}	spin_lock_irqsave(&speakup_info.spinlock, flags);	if (ch & 2) {		shut = 1;		spk_do_flush();	} else {		shut = 0;	}	if (ch & 4)		shut |= 0x40;	if (ch & 1)		spk_shut_up |= shut;	else		spk_shut_up &= ~shut;	spin_unlock_irqrestore(&speakup_info.spinlock, flags);	return count;} /* * This is called when a user reads the synth setting. */static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,			  char *buf){	int rv; 	if (!synth)		rv = sprintf(buf, "%s\n", "none");	else		rv = sprintf(buf, "%s\n", synth->name);	return rv;} /* * This is called when a user requests to change synthesizers. */static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,			   const char *buf, size_t count){	int len;	char new_synth_name[10]; 	len = strlen(buf);	if (len < 2 || len > 9)		return -EINVAL;	memcpy(new_synth_name, buf, len);	if (new_synth_name[len - 1] == '\n')		len--;	new_synth_name[len] = '\0';	spk_strlwr(new_synth_name);	if (synth && !strcmp(new_synth_name, synth->name)) {		pr_warn("%s already in use\n", new_synth_name);	} else if (synth_init(new_synth_name) != 0) {		pr_warn("failed to init synth %s\n", new_synth_name);		return -ENODEV;	}	return count;} /* * This is called when text is sent to the synth via the synth_direct file. */static ssize_t synth_direct_store(struct kobject *kobj,				  struct kobj_attribute *attr,				  const char *buf, size_t count){	char *unescaped;	unsigned long flags; 	if (!synth)		return -EPERM; 	unescaped = kstrdup(buf, GFP_KERNEL);	if (!unescaped)		return -ENOMEM; 	string_unescape_any_inplace(unescaped); 	spin_lock_irqsave(&speakup_info.spinlock, flags);	synth_write(unescaped, strlen(unescaped));	spin_unlock_irqrestore(&speakup_info.spinlock, flags); 	kfree(unescaped); 	return count;} /* * This function is called when a user reads the version. */static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,			    char *buf){	char *cp; 	cp = buf;	cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);	if (synth)		cp += sprintf(cp, "%s synthesizer driver version %s\n",		synth->name, synth->version);	return cp - buf;} /* * This is called when a user reads the punctuation settings. */static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,			 char *buf){	int i;	char *cp = buf;	struct st_var_header *p_header;	struct punc_var_t *var;	struct st_bits_data *pb;	u16 mask;	unsigned long flags; 	p_header = spk_var_header_by_name(attr->attr.name);	if (!p_header) {		pr_warn("p_header is null, attr->attr.name is %s\n",			attr->attr.name);		return -EINVAL;	} 	var = spk_get_punc_var(p_header->var_id);	if (!var) {		pr_warn("var is null, p_header->var_id is %i\n",			p_header->var_id);		return -EINVAL;	} 	spin_lock_irqsave(&speakup_info.spinlock, flags);	pb = (struct st_bits_data *)&spk_punc_info[var->value];	mask = pb->mask;	for (i = 33; i < 128; i++) {		if (!(spk_chartab[i] & mask))			continue;		*cp++ = (char)i;	}	spin_unlock_irqrestore(&speakup_info.spinlock, flags);	return cp - buf;} /* * This is called when a user changes the punctuation settings. */static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,			  const char *buf, size_t count){	int x;	struct st_var_header *p_header;

Lines 1–500 of 1056.