// 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; unsignedlong flags;
/* * Print informational messages or warnings after updating * character descriptions or chartab entries.
*/ staticvoid report_char_chartab_status(int reset, int received, int used, int rejected, int do_characters)
{ staticcharconst *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]);
} elseif (received) {
len = snprintf(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, rejected > 1 ? "s" : "");
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, constchar *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; unsignedlong flags; unsignedlong index = 0; int 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;
/* * 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;
}
/* * 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; unsignedlong 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, constchar *buf, size_t count)
{ int i;
ssize_t ret = count; char *in_buff = NULL; char *cp;
u_char *cp1; unsignedlong 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, constchar *buf, size_t count)
{ int len; struct vc_data *vc = vc_cons[fg_console].d; char ch = 0; char shut; unsignedlong 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;
/* * This is called when a user requests to change synthesizers.
*/ static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, constchar *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);
} elseif (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, constchar *buf, size_t count)
{ char *unescaped; unsignedlong flags;
if (!synth) return -EPERM;
unescaped = kstrdup(buf, GFP_KERNEL); if (!unescaped) return -ENOMEM;
/* * 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; short mask; unsignedlong 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, constchar *buf, size_t count)
{ int x; struct st_var_header *p_header; struct punc_var_t *var; char punc_buf[100]; unsignedlong flags;
x = strlen(buf); if (x < 1 || x > 99) return -EINVAL;
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;
}
/* * This function is called when a user reads one of the variable parameters.
*/
ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{ int rv = 0; struct st_var_header *param; struct var_t *var; char *cp1; char *cp; char ch; unsignedlong flags;
param = spk_var_header_by_name(attr->attr.name); if (!param) return -EINVAL;
/* * Used to reset either default_pitch or default_vol.
*/ staticinlinevoid spk_reset_default_value(char *header_name, int *synth_default_value, int idx)
{ struct st_var_header *param;
if (synth && synth_default_value) {
param = spk_var_header_by_name(header_name); if (param) {
spk_set_num_var(synth_default_value[idx],
param, E_NEW_DEFAULT);
spk_set_num_var(0, param, E_DEFAULT);
pr_info("%s reset to default value\n", param->name);
}
}
}
/* * This function is called when a user echos a value to one of the * variable parameters.
*/
ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, constchar *buf, size_t count)
{ struct st_var_header *param; int ret; int len; char *cp; struct var_t *var_data; long value; unsignedlong flags;
param = spk_var_header_by_name(attr->attr.name); if (!param) return -EINVAL; if (!param->data) return 0;
ret = 0;
cp = (char *)buf;
string_unescape_any_inplace(cp);
spin_lock_irqsave(&speakup_info.spinlock, flags); switch (param->var_type) { case VAR_NUM: case VAR_TIME: if (*cp == 'd' || *cp == 'r' || *cp == '\0')
len = E_DEFAULT; elseif (*cp == '+' || *cp == '-')
len = E_INC; else
len = E_SET; if (kstrtol(cp, 10, &value) == 0)
ret = spk_set_num_var(value, param, len); else
pr_warn("overflow or parsing error has occurred"); if (ret == -ERANGE) {
var_data = param->data;
pr_warn("value for %s out of range, expect %d to %d\n",
param->name,
var_data->u.n.low, var_data->u.n.high);
}
/* * If voice was just changed, we might need to reset our default * pitch and volume.
*/ if (param->var_id == VOICE && synth &&
(ret == 0 || ret == -ERESTART)) {
var_data = param->data;
value = var_data->u.n.value;
spk_reset_default_value("pitch", synth->default_pitch,
value);
spk_reset_default_value("vol", synth->default_vol,
value);
} break; case VAR_STRING:
len = strlen(cp); if ((len >= 1) && (cp[len - 1] == '\n'))
--len; if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
++cp;
len -= 2;
}
cp[len] = '\0';
ret = spk_set_string_var(cp, param, len); if (ret == -E2BIG)
pr_warn("value too long for %s\n",
param->name); break; default:
pr_warn("%s unknown type %d\n",
param->name, (int)param->var_type); break;
}
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
if (ret == -ERESTART)
pr_info("%s reset to default value\n", param->name); return count;
}
EXPORT_SYMBOL_GPL(spk_var_store);
/* * Functions for reading and writing lists of i18n messages. Incomplete.
*/
static ssize_t message_show_helper(char *buf, enum msg_index_t first, enum msg_index_t last)
{
size_t bufsize = PAGE_SIZE; char *buf_pointer = buf; int printed; enum msg_index_t cursor; int index = 0;
*buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
/* * Note the check (curmessage < firstmessage). It is not * redundant. Suppose that the user gave us an index * equal to ULONG_MAX - 1. If firstmessage > 1, then * firstmessage + index < firstmessage!
*/
/* * Create groups of attributes so that we can create and destroy them all * at once.
*/ staticstruct attribute *main_attrs[] = {
&keymap_attribute.attr,
&silent_attribute.attr,
&synth_attribute.attr,
&synth_direct_attribute.attr,
&version_attribute.attr,
&delimiters_attribute.attr,
&ex_num_attribute.attr,
&punc_all_attribute.attr,
&punc_most_attribute.attr,
&punc_some_attribute.attr,
&repeats_attribute.attr,
&attrib_bleep_attribute.attr,
&bell_pos_attribute.attr,
&bleep_time_attribute.attr,
&bleeps_attribute.attr,
&cursor_time_attribute.attr,
&key_echo_attribute.attr,
&no_interrupt_attribute.attr,
&punc_level_attribute.attr,
&reading_punc_attribute.attr,
&say_control_attribute.attr,
&say_word_ctl_attribute.attr,
&spell_delay_attribute.attr,
&cur_phonetic_attribute.attr,
NULL,
};
/* * An unnamed attribute group will put all of the attributes directly in * the kobject directory. If we specify a name, a subdirectory will be * created for the attributes with the directory being the name of the * attribute group.
*/ staticconststruct attribute_group main_attr_group = {
.attrs = main_attrs,
};
/* * Create a simple kobject with the name of "accessibility", * located under /sys/ * * As this is a simple directory, no uevent will be sent to * userspace. That is why this function should not be used for * any type of dynamic kobjects, where the name and number are * not known ahead of time.
*/
accessibility_kobj = kobject_create_and_add("accessibility", NULL); if (!accessibility_kobj) {
retval = -ENOMEM; goto out;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.