Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
26 kB · 1056 lines
C
at master
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057// 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 = 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, 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; 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;
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; short 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; struct punc_var_t *var; char punc_buf[100]; unsigned long 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; }
memcpy(punc_buf, buf, x);
while (x && punc_buf[x - 1] == '\n') x--; punc_buf[x] = '\0';
spin_lock_irqsave(&speakup_info.spinlock, flags);
if (*punc_buf == 'd' || *punc_buf == 'r') x = spk_set_mask_bits(NULL, var->value, 3); else x = spk_set_mask_bits(punc_buf, var->value, 3);
spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count;}
/* * 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; unsigned long flags;
param = spk_var_header_by_name(attr->attr.name); if (!param) return -EINVAL;
spin_lock_irqsave(&speakup_info.spinlock, flags); var = (struct var_t *)param->data; switch (param->var_type) { case VAR_NUM: case VAR_TIME: if (var) rv = sprintf(buf, "%i\n", var->u.n.value); else rv = sprintf(buf, "0\n"); break; case VAR_STRING: if (var) { cp1 = buf; *cp1++ = '"'; for (cp = (char *)param->p_val; (ch = *cp); cp++) { if (ch >= ' ' && ch < '~') *cp1++ = ch; else cp1 += sprintf(cp1, "\\x%02x", ch); } *cp1++ = '"'; *cp1++ = '\n'; *cp1 = '\0'; rv = cp1 - buf; } else { rv = sprintf(buf, "\"\"\n"); } break; default: rv = sprintf(buf, "Bad parameter %s, type %i\n", param->name, param->var_type); break; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return rv;}EXPORT_SYMBOL_GPL(spk_var_show);
/* * Used to reset either default_pitch or default_vol. */static inline void 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, const char *buf, size_t count){ struct st_var_header *param; int ret; int len; char *cp; struct var_t *var_data; long value; unsigned long 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; else if (*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. */
for (cursor = first; cursor <= last; cursor++, index++) { if (bufsize <= 1) break; printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n", index, spk_msg_get(cursor)); buf_pointer += printed; bufsize -= printed; }
return buf_pointer - buf;}
static void report_msg_status(int reset, int received, int used, int rejected, char *groupname){ int len; char buf[160];
if (reset) { pr_info("i18n messages from group %s reset to defaults\n", groupname); } else if (received) { len = snprintf(buf, sizeof(buf), " updated %d of %d i18n messages from group %s\n", used, received, groupname); if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", rejected, rejected > 1 ? "s" : ""); pr_info("%s", buf); }}
static ssize_t message_store_helper(const char *buf, size_t count, struct msg_group_t *group){ char *cp = (char *)buf; char *end = cp + count; char *linefeed = NULL; char *temp = NULL; ssize_t msg_stored = 0; ssize_t retval = count; size_t desc_length = 0; unsigned long index = 0; int received = 0; int used = 0; int rejected = 0; int reset = 0; enum msg_index_t firstmessage = group->start; enum msg_index_t lastmessage = group->end; enum msg_index_t curmessage;
while (cp < end) { while ((cp < end) && (*cp == ' ' || *cp == '\t')) cp++;
if (cp == end) break; if (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);
while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) temp++;
desc_length = linefeed - temp; curmessage = firstmessage + index;
/* * 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! */
if ((curmessage < firstmessage) || (curmessage > lastmessage)) { rejected++; cp = linefeed + 1; continue; }
msg_stored = spk_msg_set(curmessage, temp, desc_length); if (msg_stored < 0) { retval = msg_stored; if (msg_stored == -ENOMEM) reset = 1; break; }
used++;
cp = linefeed + 1; }
if (reset) spk_reset_msg_group(group);
report_msg_status(reset, received, used, rejected, group->name); return retval;}
static ssize_t message_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ ssize_t retval = 0; struct msg_group_t *group = spk_find_msg_group(attr->attr.name); unsigned long flags;
if (WARN_ON(!group)) return -EINVAL;
spin_lock_irqsave(&speakup_info.spinlock, flags); retval = message_show_helper(buf, group->start, group->end); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return retval;}
static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
if (WARN_ON(!group)) return -EINVAL;
return message_store_helper(buf, count, group);}
/* * Declare the attributes. */static struct kobj_attribute keymap_attribute = __ATTR_RW(keymap);static struct kobj_attribute silent_attribute = __ATTR_WO(silent);static struct kobj_attribute synth_attribute = __ATTR_RW(synth);static struct kobj_attribute synth_direct_attribute = __ATTR_WO(synth_direct);static struct kobj_attribute version_attribute = __ATTR_RO(version);
static struct kobj_attribute delimiters_attribute = __ATTR(delimiters, 0644, punc_show, punc_store);static struct kobj_attribute ex_num_attribute = __ATTR(ex_num, 0644, punc_show, punc_store);static struct kobj_attribute punc_all_attribute = __ATTR(punc_all, 0644, punc_show, punc_store);static struct kobj_attribute punc_most_attribute = __ATTR(punc_most, 0644, punc_show, punc_store);static struct kobj_attribute punc_some_attribute = __ATTR(punc_some, 0644, punc_show, punc_store);static struct kobj_attribute repeats_attribute = __ATTR(repeats, 0644, punc_show, punc_store);
static struct kobj_attribute attrib_bleep_attribute = __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);static struct kobj_attribute bell_pos_attribute = __ATTR(bell_pos, 0644, spk_var_show, spk_var_store);static struct kobj_attribute bleep_time_attribute = __ATTR(bleep_time, 0644, spk_var_show, spk_var_store);static struct kobj_attribute bleeps_attribute = __ATTR(bleeps, 0644, spk_var_show, spk_var_store);static struct kobj_attribute cursor_time_attribute = __ATTR(cursor_time, 0644, spk_var_show, spk_var_store);static struct kobj_attribute key_echo_attribute = __ATTR(key_echo, 0644, spk_var_show, spk_var_store);static struct kobj_attribute no_interrupt_attribute = __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);static struct kobj_attribute punc_level_attribute = __ATTR(punc_level, 0644, spk_var_show, spk_var_store);static struct kobj_attribute reading_punc_attribute = __ATTR(reading_punc, 0644, spk_var_show, spk_var_store);static struct kobj_attribute say_control_attribute = __ATTR(say_control, 0644, spk_var_show, spk_var_store);static struct kobj_attribute say_word_ctl_attribute = __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);static struct kobj_attribute spell_delay_attribute = __ATTR(spell_delay, 0644, spk_var_show, spk_var_store);static struct kobj_attribute cur_phonetic_attribute = __ATTR(cur_phonetic, 0644, spk_var_show, spk_var_store);
/* * These attributes are i18n related. */static struct kobj_attribute announcements_attribute = __ATTR(announcements, 0644, message_show, message_store);static struct kobj_attribute characters_attribute = __ATTR(characters, 0644, chars_chartab_show, chars_chartab_store);static struct kobj_attribute chartab_attribute = __ATTR(chartab, 0644, chars_chartab_show, chars_chartab_store);static struct kobj_attribute ctl_keys_attribute = __ATTR(ctl_keys, 0644, message_show, message_store);static struct kobj_attribute colors_attribute = __ATTR(colors, 0644, message_show, message_store);static struct kobj_attribute formatted_attribute = __ATTR(formatted, 0644, message_show, message_store);static struct kobj_attribute function_names_attribute = __ATTR(function_names, 0644, message_show, message_store);static struct kobj_attribute key_names_attribute = __ATTR(key_names, 0644, message_show, message_store);static struct kobj_attribute states_attribute = __ATTR(states, 0644, message_show, message_store);
/* * Create groups of attributes so that we can create and destroy them all * at once. */static struct 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,};
static struct attribute *i18n_attrs[] = { &announcements_attribute.attr, &characters_attribute.attr, &chartab_attribute.attr, &ctl_keys_attribute.attr, &colors_attribute.attr, &formatted_attribute.attr, &function_names_attribute.attr, &key_names_attribute.attr, &states_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. */static const struct attribute_group main_attr_group = { .attrs = main_attrs,};
static const struct attribute_group i18n_attr_group = { .attrs = i18n_attrs, .name = "i18n",};
static struct kobject *accessibility_kobj;struct kobject *speakup_kobj;
int speakup_kobj_init(void){ int retval;
/* * 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; }
speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj); if (!speakup_kobj) { retval = -ENOMEM; goto err_acc; }
/* Create the files associated with this kobject */ retval = sysfs_create_group(speakup_kobj, &main_attr_group); if (retval) goto err_speakup;
retval = sysfs_create_group(speakup_kobj, &i18n_attr_group); if (retval) goto err_group;
goto out;
err_group: sysfs_remove_group(speakup_kobj, &main_attr_group);err_speakup: kobject_put(speakup_kobj);err_acc: kobject_put(accessibility_kobj);out: return retval;}
void speakup_kobj_exit(void){ sysfs_remove_group(speakup_kobj, &i18n_attr_group); sysfs_remove_group(speakup_kobj, &main_attr_group); kobject_put(speakup_kobj); kobject_put(accessibility_kobj);}