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