12067fd92SSamuel Thibault // SPDX-License-Identifier: GPL-2.0
22067fd92SSamuel Thibault /*
32067fd92SSamuel Thibault * Speakup kobject implementation
42067fd92SSamuel Thibault *
52067fd92SSamuel Thibault * Copyright (C) 2009 William Hubbs
62067fd92SSamuel Thibault *
72067fd92SSamuel Thibault * This code is based on kobject-example.c, which came with linux 2.6.x.
82067fd92SSamuel Thibault *
92067fd92SSamuel Thibault * Copyright (C) 2004-2007 Greg Kroah-Hartman <[email protected]>
102067fd92SSamuel Thibault * Copyright (C) 2007 Novell Inc.
112067fd92SSamuel Thibault *
122067fd92SSamuel Thibault * Released under the GPL version 2 only.
132067fd92SSamuel Thibault *
142067fd92SSamuel Thibault */
152067fd92SSamuel Thibault #include <linux/slab.h> /* For kmalloc. */
162067fd92SSamuel Thibault #include <linux/kernel.h>
172067fd92SSamuel Thibault #include <linux/kobject.h>
182067fd92SSamuel Thibault #include <linux/string.h>
192067fd92SSamuel Thibault #include <linux/string_helpers.h>
202067fd92SSamuel Thibault #include <linux/sysfs.h>
212067fd92SSamuel Thibault #include <linux/ctype.h>
222067fd92SSamuel Thibault
232067fd92SSamuel Thibault #include "speakup.h"
242067fd92SSamuel Thibault #include "spk_priv.h"
252067fd92SSamuel Thibault
262067fd92SSamuel Thibault /*
272067fd92SSamuel Thibault * This is called when a user reads the characters or chartab sys file.
282067fd92SSamuel Thibault */
chars_chartab_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)292067fd92SSamuel Thibault static ssize_t chars_chartab_show(struct kobject *kobj,
302067fd92SSamuel Thibault struct kobj_attribute *attr, char *buf)
312067fd92SSamuel Thibault {
322067fd92SSamuel Thibault int i;
332067fd92SSamuel Thibault int len = 0;
342067fd92SSamuel Thibault char *cp;
352067fd92SSamuel Thibault char *buf_pointer = buf;
362067fd92SSamuel Thibault size_t bufsize = PAGE_SIZE;
372067fd92SSamuel Thibault unsigned long flags;
382067fd92SSamuel Thibault
392067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
402067fd92SSamuel Thibault *buf_pointer = '\0';
412067fd92SSamuel Thibault for (i = 0; i < 256; i++) {
422067fd92SSamuel Thibault if (bufsize <= 1)
432067fd92SSamuel Thibault break;
442067fd92SSamuel Thibault if (strcmp("characters", attr->attr.name) == 0) {
452067fd92SSamuel Thibault len = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
462067fd92SSamuel Thibault i, spk_characters[i]);
472067fd92SSamuel Thibault } else { /* show chartab entry */
482067fd92SSamuel Thibault if (IS_TYPE(i, B_CTL))
492067fd92SSamuel Thibault cp = "B_CTL";
502067fd92SSamuel Thibault else if (IS_TYPE(i, WDLM))
512067fd92SSamuel Thibault cp = "WDLM";
522067fd92SSamuel Thibault else if (IS_TYPE(i, A_PUNC))
532067fd92SSamuel Thibault cp = "A_PUNC";
542067fd92SSamuel Thibault else if (IS_TYPE(i, PUNC))
552067fd92SSamuel Thibault cp = "PUNC";
562067fd92SSamuel Thibault else if (IS_TYPE(i, NUM))
572067fd92SSamuel Thibault cp = "NUM";
582067fd92SSamuel Thibault else if (IS_TYPE(i, A_CAP))
592067fd92SSamuel Thibault cp = "A_CAP";
602067fd92SSamuel Thibault else if (IS_TYPE(i, ALPHA))
612067fd92SSamuel Thibault cp = "ALPHA";
622067fd92SSamuel Thibault else if (IS_TYPE(i, B_CAPSYM))
632067fd92SSamuel Thibault cp = "B_CAPSYM";
642067fd92SSamuel Thibault else if (IS_TYPE(i, B_SYM))
652067fd92SSamuel Thibault cp = "B_SYM";
662067fd92SSamuel Thibault else
672067fd92SSamuel Thibault cp = "0";
682067fd92SSamuel Thibault len =
692067fd92SSamuel Thibault scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp);
702067fd92SSamuel Thibault }
712067fd92SSamuel Thibault bufsize -= len;
722067fd92SSamuel Thibault buf_pointer += len;
732067fd92SSamuel Thibault }
742067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
752067fd92SSamuel Thibault return buf_pointer - buf;
762067fd92SSamuel Thibault }
772067fd92SSamuel Thibault
782067fd92SSamuel Thibault /*
792067fd92SSamuel Thibault * Print informational messages or warnings after updating
802067fd92SSamuel Thibault * character descriptions or chartab entries.
812067fd92SSamuel Thibault */
report_char_chartab_status(int reset,int received,int used,int rejected,int do_characters)822067fd92SSamuel Thibault static void report_char_chartab_status(int reset, int received, int used,
832067fd92SSamuel Thibault int rejected, int do_characters)
842067fd92SSamuel Thibault {
852067fd92SSamuel Thibault static char const *object_type[] = {
862067fd92SSamuel Thibault "character class entries",
872067fd92SSamuel Thibault "character descriptions",
882067fd92SSamuel Thibault };
892067fd92SSamuel Thibault int len;
902067fd92SSamuel Thibault char buf[80];
912067fd92SSamuel Thibault
922067fd92SSamuel Thibault if (reset) {
932067fd92SSamuel Thibault pr_info("%s reset to defaults\n", object_type[do_characters]);
942067fd92SSamuel Thibault } else if (received) {
952067fd92SSamuel Thibault len = snprintf(buf, sizeof(buf),
962067fd92SSamuel Thibault " updated %d of %d %s\n",
972067fd92SSamuel Thibault used, received, object_type[do_characters]);
982067fd92SSamuel Thibault if (rejected)
992067fd92SSamuel Thibault snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
1002067fd92SSamuel Thibault " with %d reject%s\n",
1012067fd92SSamuel Thibault rejected, rejected > 1 ? "s" : "");
1022067fd92SSamuel Thibault pr_info("%s", buf);
1032067fd92SSamuel Thibault }
1042067fd92SSamuel Thibault }
1052067fd92SSamuel Thibault
1062067fd92SSamuel Thibault /*
1072067fd92SSamuel Thibault * This is called when a user changes the characters or chartab parameters.
1082067fd92SSamuel Thibault */
chars_chartab_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)1092067fd92SSamuel Thibault static ssize_t chars_chartab_store(struct kobject *kobj,
1102067fd92SSamuel Thibault struct kobj_attribute *attr,
1112067fd92SSamuel Thibault const char *buf, size_t count)
1122067fd92SSamuel Thibault {
1132067fd92SSamuel Thibault char *cp = (char *)buf;
1142067fd92SSamuel Thibault char *end = cp + count; /* the null at the end of the buffer */
1152067fd92SSamuel Thibault char *linefeed = NULL;
1162067fd92SSamuel Thibault char keyword[MAX_DESC_LEN + 1];
1172067fd92SSamuel Thibault char *outptr = NULL; /* Will hold keyword or desc. */
1182067fd92SSamuel Thibault char *temp = NULL;
1192067fd92SSamuel Thibault char *desc = NULL;
1202067fd92SSamuel Thibault ssize_t retval = count;
1212067fd92SSamuel Thibault unsigned long flags;
1222067fd92SSamuel Thibault unsigned long index = 0;
1232067fd92SSamuel Thibault int charclass = 0;
1242067fd92SSamuel Thibault int received = 0;
1252067fd92SSamuel Thibault int used = 0;
1262067fd92SSamuel Thibault int rejected = 0;
1272067fd92SSamuel Thibault int reset = 0;
1282067fd92SSamuel Thibault int do_characters = !strcmp(attr->attr.name, "characters");
1292067fd92SSamuel Thibault size_t desc_length = 0;
1302067fd92SSamuel Thibault int i;
1312067fd92SSamuel Thibault
1322067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
1332067fd92SSamuel Thibault while (cp < end) {
1342067fd92SSamuel Thibault while ((cp < end) && (*cp == ' ' || *cp == '\t'))
1352067fd92SSamuel Thibault cp++;
1362067fd92SSamuel Thibault
1372067fd92SSamuel Thibault if (cp == end)
1382067fd92SSamuel Thibault break;
1392067fd92SSamuel Thibault if ((*cp == '\n') || strchr("dDrR", *cp)) {
1402067fd92SSamuel Thibault reset = 1;
1412067fd92SSamuel Thibault break;
1422067fd92SSamuel Thibault }
1432067fd92SSamuel Thibault received++;
1442067fd92SSamuel Thibault
1452067fd92SSamuel Thibault linefeed = strchr(cp, '\n');
1462067fd92SSamuel Thibault if (!linefeed) {
1472067fd92SSamuel Thibault rejected++;
1482067fd92SSamuel Thibault break;
1492067fd92SSamuel Thibault }
1502067fd92SSamuel Thibault
1512067fd92SSamuel Thibault if (!isdigit(*cp)) {
1522067fd92SSamuel Thibault rejected++;
1532067fd92SSamuel Thibault cp = linefeed + 1;
1542067fd92SSamuel Thibault continue;
1552067fd92SSamuel Thibault }
1562067fd92SSamuel Thibault
1572067fd92SSamuel Thibault /*
1582067fd92SSamuel Thibault * Do not replace with kstrtoul:
1592067fd92SSamuel Thibault * here we need temp to be updated
1602067fd92SSamuel Thibault */
1612067fd92SSamuel Thibault index = simple_strtoul(cp, &temp, 10);
1622067fd92SSamuel Thibault if (index > 255) {
1632067fd92SSamuel Thibault rejected++;
1642067fd92SSamuel Thibault cp = linefeed + 1;
1652067fd92SSamuel Thibault continue;
1662067fd92SSamuel Thibault }
1672067fd92SSamuel Thibault
1682067fd92SSamuel Thibault while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
1692067fd92SSamuel Thibault temp++;
1702067fd92SSamuel Thibault
1712067fd92SSamuel Thibault desc_length = linefeed - temp;
1722067fd92SSamuel Thibault if (desc_length > MAX_DESC_LEN) {
1732067fd92SSamuel Thibault rejected++;
1742067fd92SSamuel Thibault cp = linefeed + 1;
1752067fd92SSamuel Thibault continue;
1762067fd92SSamuel Thibault }
1772067fd92SSamuel Thibault if (do_characters) {
1782067fd92SSamuel Thibault desc = kmalloc(desc_length + 1, GFP_ATOMIC);
1792067fd92SSamuel Thibault if (!desc) {
1802067fd92SSamuel Thibault retval = -ENOMEM;
1812067fd92SSamuel Thibault reset = 1; /* just reset on error. */
1822067fd92SSamuel Thibault break;
1832067fd92SSamuel Thibault }
1842067fd92SSamuel Thibault outptr = desc;
1852067fd92SSamuel Thibault } else {
1862067fd92SSamuel Thibault outptr = keyword;
1872067fd92SSamuel Thibault }
1882067fd92SSamuel Thibault
1892067fd92SSamuel Thibault for (i = 0; i < desc_length; i++)
1902067fd92SSamuel Thibault outptr[i] = temp[i];
1912067fd92SSamuel Thibault outptr[desc_length] = '\0';
1922067fd92SSamuel Thibault
1932067fd92SSamuel Thibault if (do_characters) {
1942067fd92SSamuel Thibault if (spk_characters[index] != spk_default_chars[index])
1952067fd92SSamuel Thibault kfree(spk_characters[index]);
1962067fd92SSamuel Thibault spk_characters[index] = desc;
1972067fd92SSamuel Thibault used++;
1982067fd92SSamuel Thibault } else {
1992067fd92SSamuel Thibault charclass = spk_chartab_get_value(keyword);
2002067fd92SSamuel Thibault if (charclass == 0) {
2012067fd92SSamuel Thibault rejected++;
2022067fd92SSamuel Thibault cp = linefeed + 1;
2032067fd92SSamuel Thibault continue;
2042067fd92SSamuel Thibault }
2052067fd92SSamuel Thibault if (charclass != spk_chartab[index]) {
2062067fd92SSamuel Thibault spk_chartab[index] = charclass;
2072067fd92SSamuel Thibault used++;
2082067fd92SSamuel Thibault }
2092067fd92SSamuel Thibault }
2102067fd92SSamuel Thibault cp = linefeed + 1;
2112067fd92SSamuel Thibault }
2122067fd92SSamuel Thibault
2132067fd92SSamuel Thibault if (reset) {
2142067fd92SSamuel Thibault if (do_characters)
2152067fd92SSamuel Thibault spk_reset_default_chars();
2162067fd92SSamuel Thibault else
2172067fd92SSamuel Thibault spk_reset_default_chartab();
2182067fd92SSamuel Thibault }
2192067fd92SSamuel Thibault
2202067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2212067fd92SSamuel Thibault report_char_chartab_status(reset, received, used, rejected,
2222067fd92SSamuel Thibault do_characters);
2232067fd92SSamuel Thibault return retval;
2242067fd92SSamuel Thibault }
2252067fd92SSamuel Thibault
2262067fd92SSamuel Thibault /*
2272067fd92SSamuel Thibault * This is called when a user reads the keymap parameter.
2282067fd92SSamuel Thibault */
keymap_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)2292067fd92SSamuel Thibault static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr,
2302067fd92SSamuel Thibault char *buf)
2312067fd92SSamuel Thibault {
2322067fd92SSamuel Thibault char *cp = buf;
2332067fd92SSamuel Thibault int i;
2342067fd92SSamuel Thibault int n;
2352067fd92SSamuel Thibault int num_keys;
2362067fd92SSamuel Thibault int nstates;
2372067fd92SSamuel Thibault u_char *cp1;
2382067fd92SSamuel Thibault u_char ch;
2392067fd92SSamuel Thibault unsigned long flags;
2402067fd92SSamuel Thibault
2412067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
2422067fd92SSamuel Thibault cp1 = spk_key_buf + SHIFT_TBL_SIZE;
2432067fd92SSamuel Thibault num_keys = (int)(*cp1);
2442067fd92SSamuel Thibault nstates = (int)cp1[1];
2452067fd92SSamuel Thibault cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates);
2462067fd92SSamuel Thibault cp1 += 2; /* now pointing at shift states */
2472067fd92SSamuel Thibault /* dump num_keys+1 as first row is shift states + flags,
2482067fd92SSamuel Thibault * each subsequent row is key + states
2492067fd92SSamuel Thibault */
2502067fd92SSamuel Thibault for (n = 0; n <= num_keys; n++) {
2512067fd92SSamuel Thibault for (i = 0; i <= nstates; i++) {
2522067fd92SSamuel Thibault ch = *cp1++;
2532067fd92SSamuel Thibault cp += sprintf(cp, "%d,", (int)ch);
2542067fd92SSamuel Thibault *cp++ = (i < nstates) ? SPACE : '\n';
2552067fd92SSamuel Thibault }
2562067fd92SSamuel Thibault }
2572067fd92SSamuel Thibault cp += sprintf(cp, "0, %d\n", KEY_MAP_VER);
2582067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2592067fd92SSamuel Thibault return (int)(cp - buf);
2602067fd92SSamuel Thibault }
2612067fd92SSamuel Thibault
2622067fd92SSamuel Thibault /*
2632067fd92SSamuel Thibault * This is called when a user changes the keymap parameter.
2642067fd92SSamuel Thibault */
keymap_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)2652067fd92SSamuel Thibault static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr,
2662067fd92SSamuel Thibault const char *buf, size_t count)
2672067fd92SSamuel Thibault {
2682067fd92SSamuel Thibault int i;
2692067fd92SSamuel Thibault ssize_t ret = count;
2702067fd92SSamuel Thibault char *in_buff = NULL;
2712067fd92SSamuel Thibault char *cp;
2722067fd92SSamuel Thibault u_char *cp1;
2732067fd92SSamuel Thibault unsigned long flags;
2742067fd92SSamuel Thibault
2752067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
2762067fd92SSamuel Thibault in_buff = kmemdup(buf, count + 1, GFP_ATOMIC);
2772067fd92SSamuel Thibault if (!in_buff) {
2782067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2792067fd92SSamuel Thibault return -ENOMEM;
2802067fd92SSamuel Thibault }
2812067fd92SSamuel Thibault if (strchr("dDrR", *in_buff)) {
2822067fd92SSamuel Thibault spk_set_key_info(spk_key_defaults, spk_key_buf);
2832067fd92SSamuel Thibault pr_info("keymap set to default values\n");
2842067fd92SSamuel Thibault kfree(in_buff);
2852067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2862067fd92SSamuel Thibault return count;
2872067fd92SSamuel Thibault }
2882067fd92SSamuel Thibault if (in_buff[count - 1] == '\n')
2892067fd92SSamuel Thibault in_buff[count - 1] = '\0';
2902067fd92SSamuel Thibault cp = in_buff;
2912067fd92SSamuel Thibault cp1 = (u_char *)in_buff;
2922067fd92SSamuel Thibault for (i = 0; i < 3; i++) {
2932067fd92SSamuel Thibault cp = spk_s2uchar(cp, cp1);
2942067fd92SSamuel Thibault cp1++;
2952067fd92SSamuel Thibault }
2962067fd92SSamuel Thibault i = (int)cp1[-2] + 1;
2972067fd92SSamuel Thibault i *= (int)cp1[-1] + 1;
2982067fd92SSamuel Thibault i += 2; /* 0 and last map ver */
2992067fd92SSamuel Thibault if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||
3002067fd92SSamuel Thibault i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
3012067fd92SSamuel Thibault pr_warn("i %d %d %d %d\n", i,
3022067fd92SSamuel Thibault (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
3032067fd92SSamuel Thibault kfree(in_buff);
3042067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3052067fd92SSamuel Thibault return -EINVAL;
3062067fd92SSamuel Thibault }
3072067fd92SSamuel Thibault while (--i >= 0) {
3082067fd92SSamuel Thibault cp = spk_s2uchar(cp, cp1);
3092067fd92SSamuel Thibault cp1++;
3102067fd92SSamuel Thibault if (!(*cp))
3112067fd92SSamuel Thibault break;
3122067fd92SSamuel Thibault }
3132067fd92SSamuel Thibault if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {
3142067fd92SSamuel Thibault ret = -EINVAL;
3152067fd92SSamuel Thibault pr_warn("end %d %d %d %d\n", i,
3162067fd92SSamuel Thibault (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
3172067fd92SSamuel Thibault } else {
3182067fd92SSamuel Thibault if (spk_set_key_info(in_buff, spk_key_buf)) {
3192067fd92SSamuel Thibault spk_set_key_info(spk_key_defaults, spk_key_buf);
3202067fd92SSamuel Thibault ret = -EINVAL;
3212067fd92SSamuel Thibault pr_warn("set key failed\n");
3222067fd92SSamuel Thibault }
3232067fd92SSamuel Thibault }
3242067fd92SSamuel Thibault kfree(in_buff);
3252067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3262067fd92SSamuel Thibault return ret;
3272067fd92SSamuel Thibault }
3282067fd92SSamuel Thibault
3292067fd92SSamuel Thibault /*
3302067fd92SSamuel Thibault * This is called when a user changes the value of the silent parameter.
3312067fd92SSamuel Thibault */
silent_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3322067fd92SSamuel Thibault static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,
3332067fd92SSamuel Thibault const char *buf, size_t count)
3342067fd92SSamuel Thibault {
3352067fd92SSamuel Thibault int len;
3362067fd92SSamuel Thibault struct vc_data *vc = vc_cons[fg_console].d;
3372067fd92SSamuel Thibault char ch = 0;
3382067fd92SSamuel Thibault char shut;
3392067fd92SSamuel Thibault unsigned long flags;
3402067fd92SSamuel Thibault
3412067fd92SSamuel Thibault len = strlen(buf);
3422067fd92SSamuel Thibault if (len > 0 && len < 3) {
3432067fd92SSamuel Thibault ch = buf[0];
3442067fd92SSamuel Thibault if (ch == '\n')
3452067fd92SSamuel Thibault ch = '0';
3462067fd92SSamuel Thibault }
3472067fd92SSamuel Thibault if (ch < '0' || ch > '7') {
3482067fd92SSamuel Thibault pr_warn("silent value '%c' not in range (0,7)\n", ch);
3492067fd92SSamuel Thibault return -EINVAL;
3502067fd92SSamuel Thibault }
3512067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
3522067fd92SSamuel Thibault if (ch & 2) {
3532067fd92SSamuel Thibault shut = 1;
3542067fd92SSamuel Thibault spk_do_flush();
3552067fd92SSamuel Thibault } else {
3562067fd92SSamuel Thibault shut = 0;
3572067fd92SSamuel Thibault }
3582067fd92SSamuel Thibault if (ch & 4)
3592067fd92SSamuel Thibault shut |= 0x40;
3602067fd92SSamuel Thibault if (ch & 1)
3612067fd92SSamuel Thibault spk_shut_up |= shut;
3622067fd92SSamuel Thibault else
3632067fd92SSamuel Thibault spk_shut_up &= ~shut;
3642067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
3652067fd92SSamuel Thibault return count;
3662067fd92SSamuel Thibault }
3672067fd92SSamuel Thibault
3682067fd92SSamuel Thibault /*
3692067fd92SSamuel Thibault * This is called when a user reads the synth setting.
3702067fd92SSamuel Thibault */
synth_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)3712067fd92SSamuel Thibault static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,
3722067fd92SSamuel Thibault char *buf)
3732067fd92SSamuel Thibault {
3742067fd92SSamuel Thibault int rv;
3752067fd92SSamuel Thibault
3762067fd92SSamuel Thibault if (!synth)
3772067fd92SSamuel Thibault rv = sprintf(buf, "%s\n", "none");
3782067fd92SSamuel Thibault else
3792067fd92SSamuel Thibault rv = sprintf(buf, "%s\n", synth->name);
3802067fd92SSamuel Thibault return rv;
3812067fd92SSamuel Thibault }
3822067fd92SSamuel Thibault
3832067fd92SSamuel Thibault /*
3842067fd92SSamuel Thibault * This is called when a user requests to change synthesizers.
3852067fd92SSamuel Thibault */
synth_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3862067fd92SSamuel Thibault static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,
3872067fd92SSamuel Thibault const char *buf, size_t count)
3882067fd92SSamuel Thibault {
3892067fd92SSamuel Thibault int len;
3902067fd92SSamuel Thibault char new_synth_name[10];
3912067fd92SSamuel Thibault
3922067fd92SSamuel Thibault len = strlen(buf);
3932067fd92SSamuel Thibault if (len < 2 || len > 9)
3942067fd92SSamuel Thibault return -EINVAL;
3952067fd92SSamuel Thibault memcpy(new_synth_name, buf, len);
3962067fd92SSamuel Thibault if (new_synth_name[len - 1] == '\n')
3972067fd92SSamuel Thibault len--;
3982067fd92SSamuel Thibault new_synth_name[len] = '\0';
3992067fd92SSamuel Thibault spk_strlwr(new_synth_name);
4002067fd92SSamuel Thibault if (synth && !strcmp(new_synth_name, synth->name)) {
4012067fd92SSamuel Thibault pr_warn("%s already in use\n", new_synth_name);
4022067fd92SSamuel Thibault } else if (synth_init(new_synth_name) != 0) {
4032067fd92SSamuel Thibault pr_warn("failed to init synth %s\n", new_synth_name);
4042067fd92SSamuel Thibault return -ENODEV;
4052067fd92SSamuel Thibault }
4062067fd92SSamuel Thibault return count;
4072067fd92SSamuel Thibault }
4082067fd92SSamuel Thibault
4092067fd92SSamuel Thibault /*
4102067fd92SSamuel Thibault * This is called when text is sent to the synth via the synth_direct file.
4112067fd92SSamuel Thibault */
synth_direct_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)4122067fd92SSamuel Thibault static ssize_t synth_direct_store(struct kobject *kobj,
4132067fd92SSamuel Thibault struct kobj_attribute *attr,
4142067fd92SSamuel Thibault const char *buf, size_t count)
4152067fd92SSamuel Thibault {
416*19e3e6cdSJustin Stitt char *unescaped;
4172067fd92SSamuel Thibault unsigned long flags;
4182067fd92SSamuel Thibault
4192067fd92SSamuel Thibault if (!synth)
4202067fd92SSamuel Thibault return -EPERM;
4212067fd92SSamuel Thibault
422*19e3e6cdSJustin Stitt unescaped = kstrdup(buf, GFP_KERNEL);
423*19e3e6cdSJustin Stitt if (!unescaped)
424*19e3e6cdSJustin Stitt return -ENOMEM;
425*19e3e6cdSJustin Stitt
426*19e3e6cdSJustin Stitt string_unescape_any_inplace(unescaped);
427*19e3e6cdSJustin Stitt
4282067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
429*19e3e6cdSJustin Stitt synth_write(unescaped, strlen(unescaped));
4302067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
431*19e3e6cdSJustin Stitt
432*19e3e6cdSJustin Stitt kfree(unescaped);
433*19e3e6cdSJustin Stitt
4342067fd92SSamuel Thibault return count;
4352067fd92SSamuel Thibault }
4362067fd92SSamuel Thibault
4372067fd92SSamuel Thibault /*
4382067fd92SSamuel Thibault * This function is called when a user reads the version.
4392067fd92SSamuel Thibault */
version_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4402067fd92SSamuel Thibault static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
4412067fd92SSamuel Thibault char *buf)
4422067fd92SSamuel Thibault {
4432067fd92SSamuel Thibault char *cp;
4442067fd92SSamuel Thibault
4452067fd92SSamuel Thibault cp = buf;
4462067fd92SSamuel Thibault cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
4472067fd92SSamuel Thibault if (synth)
4482067fd92SSamuel Thibault cp += sprintf(cp, "%s synthesizer driver version %s\n",
4492067fd92SSamuel Thibault synth->name, synth->version);
4502067fd92SSamuel Thibault return cp - buf;
4512067fd92SSamuel Thibault }
4522067fd92SSamuel Thibault
4532067fd92SSamuel Thibault /*
4542067fd92SSamuel Thibault * This is called when a user reads the punctuation settings.
4552067fd92SSamuel Thibault */
punc_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4562067fd92SSamuel Thibault static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
4572067fd92SSamuel Thibault char *buf)
4582067fd92SSamuel Thibault {
4592067fd92SSamuel Thibault int i;
4602067fd92SSamuel Thibault char *cp = buf;
4612067fd92SSamuel Thibault struct st_var_header *p_header;
4622067fd92SSamuel Thibault struct punc_var_t *var;
4632067fd92SSamuel Thibault struct st_bits_data *pb;
4642067fd92SSamuel Thibault short mask;
4652067fd92SSamuel Thibault unsigned long flags;
4662067fd92SSamuel Thibault
4672067fd92SSamuel Thibault p_header = spk_var_header_by_name(attr->attr.name);
4682067fd92SSamuel Thibault if (!p_header) {
4692067fd92SSamuel Thibault pr_warn("p_header is null, attr->attr.name is %s\n",
4702067fd92SSamuel Thibault attr->attr.name);
4712067fd92SSamuel Thibault return -EINVAL;
4722067fd92SSamuel Thibault }
4732067fd92SSamuel Thibault
4742067fd92SSamuel Thibault var = spk_get_punc_var(p_header->var_id);
4752067fd92SSamuel Thibault if (!var) {
4762067fd92SSamuel Thibault pr_warn("var is null, p_header->var_id is %i\n",
4772067fd92SSamuel Thibault p_header->var_id);
4782067fd92SSamuel Thibault return -EINVAL;
4792067fd92SSamuel Thibault }
4802067fd92SSamuel Thibault
4812067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
4822067fd92SSamuel Thibault pb = (struct st_bits_data *)&spk_punc_info[var->value];
4832067fd92SSamuel Thibault mask = pb->mask;
4842067fd92SSamuel Thibault for (i = 33; i < 128; i++) {
4852067fd92SSamuel Thibault if (!(spk_chartab[i] & mask))
4862067fd92SSamuel Thibault continue;
4872067fd92SSamuel Thibault *cp++ = (char)i;
4882067fd92SSamuel Thibault }
4892067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
4902067fd92SSamuel Thibault return cp - buf;
4912067fd92SSamuel Thibault }
4922067fd92SSamuel Thibault
4932067fd92SSamuel Thibault /*
4942067fd92SSamuel Thibault * This is called when a user changes the punctuation settings.
4952067fd92SSamuel Thibault */
punc_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)4962067fd92SSamuel Thibault static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
4972067fd92SSamuel Thibault const char *buf, size_t count)
4982067fd92SSamuel Thibault {
4992067fd92SSamuel Thibault int x;
5002067fd92SSamuel Thibault struct st_var_header *p_header;
5012067fd92SSamuel Thibault struct punc_var_t *var;
5022067fd92SSamuel Thibault char punc_buf[100];
5032067fd92SSamuel Thibault unsigned long flags;
5042067fd92SSamuel Thibault
5052067fd92SSamuel Thibault x = strlen(buf);
5062067fd92SSamuel Thibault if (x < 1 || x > 99)
5072067fd92SSamuel Thibault return -EINVAL;
5082067fd92SSamuel Thibault
5092067fd92SSamuel Thibault p_header = spk_var_header_by_name(attr->attr.name);
5102067fd92SSamuel Thibault if (!p_header) {
5112067fd92SSamuel Thibault pr_warn("p_header is null, attr->attr.name is %s\n",
5122067fd92SSamuel Thibault attr->attr.name);
5132067fd92SSamuel Thibault return -EINVAL;
5142067fd92SSamuel Thibault }
5152067fd92SSamuel Thibault
5162067fd92SSamuel Thibault var = spk_get_punc_var(p_header->var_id);
5172067fd92SSamuel Thibault if (!var) {
5182067fd92SSamuel Thibault pr_warn("var is null, p_header->var_id is %i\n",
5192067fd92SSamuel Thibault p_header->var_id);
5202067fd92SSamuel Thibault return -EINVAL;
5212067fd92SSamuel Thibault }
5222067fd92SSamuel Thibault
5232067fd92SSamuel Thibault memcpy(punc_buf, buf, x);
5242067fd92SSamuel Thibault
5252067fd92SSamuel Thibault while (x && punc_buf[x - 1] == '\n')
5262067fd92SSamuel Thibault x--;
5272067fd92SSamuel Thibault punc_buf[x] = '\0';
5282067fd92SSamuel Thibault
5292067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
5302067fd92SSamuel Thibault
5312067fd92SSamuel Thibault if (*punc_buf == 'd' || *punc_buf == 'r')
5322067fd92SSamuel Thibault x = spk_set_mask_bits(NULL, var->value, 3);
5332067fd92SSamuel Thibault else
5342067fd92SSamuel Thibault x = spk_set_mask_bits(punc_buf, var->value, 3);
5352067fd92SSamuel Thibault
5362067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
5372067fd92SSamuel Thibault return count;
5382067fd92SSamuel Thibault }
5392067fd92SSamuel Thibault
5402067fd92SSamuel Thibault /*
5412067fd92SSamuel Thibault * This function is called when a user reads one of the variable parameters.
5422067fd92SSamuel Thibault */
spk_var_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)5432067fd92SSamuel Thibault ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
5442067fd92SSamuel Thibault char *buf)
5452067fd92SSamuel Thibault {
5462067fd92SSamuel Thibault int rv = 0;
5472067fd92SSamuel Thibault struct st_var_header *param;
5482067fd92SSamuel Thibault struct var_t *var;
5492067fd92SSamuel Thibault char *cp1;
5502067fd92SSamuel Thibault char *cp;
5512067fd92SSamuel Thibault char ch;
5522067fd92SSamuel Thibault unsigned long flags;
5532067fd92SSamuel Thibault
5542067fd92SSamuel Thibault param = spk_var_header_by_name(attr->attr.name);
5552067fd92SSamuel Thibault if (!param)
5562067fd92SSamuel Thibault return -EINVAL;
5572067fd92SSamuel Thibault
5582067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
5592067fd92SSamuel Thibault var = (struct var_t *)param->data;
5602067fd92SSamuel Thibault switch (param->var_type) {
5612067fd92SSamuel Thibault case VAR_NUM:
5622067fd92SSamuel Thibault case VAR_TIME:
5632067fd92SSamuel Thibault if (var)
5642067fd92SSamuel Thibault rv = sprintf(buf, "%i\n", var->u.n.value);
5652067fd92SSamuel Thibault else
5662067fd92SSamuel Thibault rv = sprintf(buf, "0\n");
5672067fd92SSamuel Thibault break;
5682067fd92SSamuel Thibault case VAR_STRING:
5692067fd92SSamuel Thibault if (var) {
5702067fd92SSamuel Thibault cp1 = buf;
5712067fd92SSamuel Thibault *cp1++ = '"';
5722067fd92SSamuel Thibault for (cp = (char *)param->p_val; (ch = *cp); cp++) {
5732067fd92SSamuel Thibault if (ch >= ' ' && ch < '~')
5742067fd92SSamuel Thibault *cp1++ = ch;
5752067fd92SSamuel Thibault else
5762067fd92SSamuel Thibault cp1 += sprintf(cp1, "\\x%02x", ch);
5772067fd92SSamuel Thibault }
5782067fd92SSamuel Thibault *cp1++ = '"';
5792067fd92SSamuel Thibault *cp1++ = '\n';
5802067fd92SSamuel Thibault *cp1 = '\0';
5812067fd92SSamuel Thibault rv = cp1 - buf;
5822067fd92SSamuel Thibault } else {
5832067fd92SSamuel Thibault rv = sprintf(buf, "\"\"\n");
5842067fd92SSamuel Thibault }
5852067fd92SSamuel Thibault break;
5862067fd92SSamuel Thibault default:
5872067fd92SSamuel Thibault rv = sprintf(buf, "Bad parameter %s, type %i\n",
5882067fd92SSamuel Thibault param->name, param->var_type);
5892067fd92SSamuel Thibault break;
5902067fd92SSamuel Thibault }
5912067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
5922067fd92SSamuel Thibault return rv;
5932067fd92SSamuel Thibault }
5942067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_var_show);
5952067fd92SSamuel Thibault
5962067fd92SSamuel Thibault /*
5972067fd92SSamuel Thibault * Used to reset either default_pitch or default_vol.
5982067fd92SSamuel Thibault */
spk_reset_default_value(char * header_name,int * synth_default_value,int idx)5992067fd92SSamuel Thibault static inline void spk_reset_default_value(char *header_name,
6002067fd92SSamuel Thibault int *synth_default_value, int idx)
6012067fd92SSamuel Thibault {
6022067fd92SSamuel Thibault struct st_var_header *param;
6032067fd92SSamuel Thibault
6042067fd92SSamuel Thibault if (synth && synth_default_value) {
6052067fd92SSamuel Thibault param = spk_var_header_by_name(header_name);
6062067fd92SSamuel Thibault if (param) {
6072067fd92SSamuel Thibault spk_set_num_var(synth_default_value[idx],
6082067fd92SSamuel Thibault param, E_NEW_DEFAULT);
6092067fd92SSamuel Thibault spk_set_num_var(0, param, E_DEFAULT);
6102067fd92SSamuel Thibault pr_info("%s reset to default value\n", param->name);
6112067fd92SSamuel Thibault }
6122067fd92SSamuel Thibault }
6132067fd92SSamuel Thibault }
6142067fd92SSamuel Thibault
6152067fd92SSamuel Thibault /*
6162067fd92SSamuel Thibault * This function is called when a user echos a value to one of the
6172067fd92SSamuel Thibault * variable parameters.
6182067fd92SSamuel Thibault */
spk_var_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)6192067fd92SSamuel Thibault ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
6202067fd92SSamuel Thibault const char *buf, size_t count)
6212067fd92SSamuel Thibault {
6222067fd92SSamuel Thibault struct st_var_header *param;
6232067fd92SSamuel Thibault int ret;
6242067fd92SSamuel Thibault int len;
6252067fd92SSamuel Thibault char *cp;
6262067fd92SSamuel Thibault struct var_t *var_data;
6272067fd92SSamuel Thibault long value;
6282067fd92SSamuel Thibault unsigned long flags;
6292067fd92SSamuel Thibault
6302067fd92SSamuel Thibault param = spk_var_header_by_name(attr->attr.name);
6312067fd92SSamuel Thibault if (!param)
6322067fd92SSamuel Thibault return -EINVAL;
6332067fd92SSamuel Thibault if (!param->data)
6342067fd92SSamuel Thibault return 0;
6352067fd92SSamuel Thibault ret = 0;
6362067fd92SSamuel Thibault cp = (char *)buf;
6372067fd92SSamuel Thibault string_unescape_any_inplace(cp);
6382067fd92SSamuel Thibault
6392067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
6402067fd92SSamuel Thibault switch (param->var_type) {
6412067fd92SSamuel Thibault case VAR_NUM:
6422067fd92SSamuel Thibault case VAR_TIME:
6432067fd92SSamuel Thibault if (*cp == 'd' || *cp == 'r' || *cp == '\0')
6442067fd92SSamuel Thibault len = E_DEFAULT;
6452067fd92SSamuel Thibault else if (*cp == '+' || *cp == '-')
6462067fd92SSamuel Thibault len = E_INC;
6472067fd92SSamuel Thibault else
6482067fd92SSamuel Thibault len = E_SET;
6492067fd92SSamuel Thibault if (kstrtol(cp, 10, &value) == 0)
6502067fd92SSamuel Thibault ret = spk_set_num_var(value, param, len);
6512067fd92SSamuel Thibault else
6522067fd92SSamuel Thibault pr_warn("overflow or parsing error has occurred");
6532067fd92SSamuel Thibault if (ret == -ERANGE) {
6542067fd92SSamuel Thibault var_data = param->data;
6552067fd92SSamuel Thibault pr_warn("value for %s out of range, expect %d to %d\n",
6562067fd92SSamuel Thibault param->name,
6572067fd92SSamuel Thibault var_data->u.n.low, var_data->u.n.high);
6582067fd92SSamuel Thibault }
6592067fd92SSamuel Thibault
6602067fd92SSamuel Thibault /*
6612067fd92SSamuel Thibault * If voice was just changed, we might need to reset our default
6622067fd92SSamuel Thibault * pitch and volume.
6632067fd92SSamuel Thibault */
6642067fd92SSamuel Thibault if (param->var_id == VOICE && synth &&
6652067fd92SSamuel Thibault (ret == 0 || ret == -ERESTART)) {
6662067fd92SSamuel Thibault var_data = param->data;
6672067fd92SSamuel Thibault value = var_data->u.n.value;
6682067fd92SSamuel Thibault spk_reset_default_value("pitch", synth->default_pitch,
6692067fd92SSamuel Thibault value);
6702067fd92SSamuel Thibault spk_reset_default_value("vol", synth->default_vol,
6712067fd92SSamuel Thibault value);
6722067fd92SSamuel Thibault }
6732067fd92SSamuel Thibault break;
6742067fd92SSamuel Thibault case VAR_STRING:
6752067fd92SSamuel Thibault len = strlen(cp);
6762067fd92SSamuel Thibault if ((len >= 1) && (cp[len - 1] == '\n'))
6772067fd92SSamuel Thibault --len;
6782067fd92SSamuel Thibault if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
6792067fd92SSamuel Thibault ++cp;
6802067fd92SSamuel Thibault len -= 2;
6812067fd92SSamuel Thibault }
6822067fd92SSamuel Thibault cp[len] = '\0';
6832067fd92SSamuel Thibault ret = spk_set_string_var(cp, param, len);
6842067fd92SSamuel Thibault if (ret == -E2BIG)
6852067fd92SSamuel Thibault pr_warn("value too long for %s\n",
6862067fd92SSamuel Thibault param->name);
6872067fd92SSamuel Thibault break;
6882067fd92SSamuel Thibault default:
6892067fd92SSamuel Thibault pr_warn("%s unknown type %d\n",
6902067fd92SSamuel Thibault param->name, (int)param->var_type);
6912067fd92SSamuel Thibault break;
6922067fd92SSamuel Thibault }
6932067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
6942067fd92SSamuel Thibault
6952067fd92SSamuel Thibault if (ret == -ERESTART)
6962067fd92SSamuel Thibault pr_info("%s reset to default value\n", param->name);
6972067fd92SSamuel Thibault return count;
6982067fd92SSamuel Thibault }
6992067fd92SSamuel Thibault EXPORT_SYMBOL_GPL(spk_var_store);
7002067fd92SSamuel Thibault
7012067fd92SSamuel Thibault /*
7022067fd92SSamuel Thibault * Functions for reading and writing lists of i18n messages. Incomplete.
7032067fd92SSamuel Thibault */
7042067fd92SSamuel Thibault
message_show_helper(char * buf,enum msg_index_t first,enum msg_index_t last)7052067fd92SSamuel Thibault static ssize_t message_show_helper(char *buf, enum msg_index_t first,
7062067fd92SSamuel Thibault enum msg_index_t last)
7072067fd92SSamuel Thibault {
7082067fd92SSamuel Thibault size_t bufsize = PAGE_SIZE;
7092067fd92SSamuel Thibault char *buf_pointer = buf;
7102067fd92SSamuel Thibault int printed;
7112067fd92SSamuel Thibault enum msg_index_t cursor;
7122067fd92SSamuel Thibault int index = 0;
7132067fd92SSamuel Thibault *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
7142067fd92SSamuel Thibault
7152067fd92SSamuel Thibault for (cursor = first; cursor <= last; cursor++, index++) {
7162067fd92SSamuel Thibault if (bufsize <= 1)
7172067fd92SSamuel Thibault break;
7182067fd92SSamuel Thibault printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
7192067fd92SSamuel Thibault index, spk_msg_get(cursor));
7202067fd92SSamuel Thibault buf_pointer += printed;
7212067fd92SSamuel Thibault bufsize -= printed;
7222067fd92SSamuel Thibault }
7232067fd92SSamuel Thibault
7242067fd92SSamuel Thibault return buf_pointer - buf;
7252067fd92SSamuel Thibault }
7262067fd92SSamuel Thibault
report_msg_status(int reset,int received,int used,int rejected,char * groupname)7272067fd92SSamuel Thibault static void report_msg_status(int reset, int received, int used,
7282067fd92SSamuel Thibault int rejected, char *groupname)
7292067fd92SSamuel Thibault {
7302067fd92SSamuel Thibault int len;
7312067fd92SSamuel Thibault char buf[160];
7322067fd92SSamuel Thibault
7332067fd92SSamuel Thibault if (reset) {
7342067fd92SSamuel Thibault pr_info("i18n messages from group %s reset to defaults\n",
7352067fd92SSamuel Thibault groupname);
7362067fd92SSamuel Thibault } else if (received) {
7372067fd92SSamuel Thibault len = snprintf(buf, sizeof(buf),
7382067fd92SSamuel Thibault " updated %d of %d i18n messages from group %s\n",
7392067fd92SSamuel Thibault used, received, groupname);
7402067fd92SSamuel Thibault if (rejected)
7412067fd92SSamuel Thibault snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
7422067fd92SSamuel Thibault " with %d reject%s\n",
7432067fd92SSamuel Thibault rejected, rejected > 1 ? "s" : "");
7442067fd92SSamuel Thibault pr_info("%s", buf);
7452067fd92SSamuel Thibault }
7462067fd92SSamuel Thibault }
7472067fd92SSamuel Thibault
message_store_helper(const char * buf,size_t count,struct msg_group_t * group)7482067fd92SSamuel Thibault static ssize_t message_store_helper(const char *buf, size_t count,
7492067fd92SSamuel Thibault struct msg_group_t *group)
7502067fd92SSamuel Thibault {
7512067fd92SSamuel Thibault char *cp = (char *)buf;
7522067fd92SSamuel Thibault char *end = cp + count;
7532067fd92SSamuel Thibault char *linefeed = NULL;
7542067fd92SSamuel Thibault char *temp = NULL;
7552067fd92SSamuel Thibault ssize_t msg_stored = 0;
7562067fd92SSamuel Thibault ssize_t retval = count;
7572067fd92SSamuel Thibault size_t desc_length = 0;
7582067fd92SSamuel Thibault unsigned long index = 0;
7592067fd92SSamuel Thibault int received = 0;
7602067fd92SSamuel Thibault int used = 0;
7612067fd92SSamuel Thibault int rejected = 0;
7622067fd92SSamuel Thibault int reset = 0;
7632067fd92SSamuel Thibault enum msg_index_t firstmessage = group->start;
7642067fd92SSamuel Thibault enum msg_index_t lastmessage = group->end;
7652067fd92SSamuel Thibault enum msg_index_t curmessage;
7662067fd92SSamuel Thibault
7672067fd92SSamuel Thibault while (cp < end) {
7682067fd92SSamuel Thibault while ((cp < end) && (*cp == ' ' || *cp == '\t'))
7692067fd92SSamuel Thibault cp++;
7702067fd92SSamuel Thibault
7712067fd92SSamuel Thibault if (cp == end)
7722067fd92SSamuel Thibault break;
7732067fd92SSamuel Thibault if (strchr("dDrR", *cp)) {
7742067fd92SSamuel Thibault reset = 1;
7752067fd92SSamuel Thibault break;
7762067fd92SSamuel Thibault }
7772067fd92SSamuel Thibault received++;
7782067fd92SSamuel Thibault
7792067fd92SSamuel Thibault linefeed = strchr(cp, '\n');
7802067fd92SSamuel Thibault if (!linefeed) {
7812067fd92SSamuel Thibault rejected++;
7822067fd92SSamuel Thibault break;
7832067fd92SSamuel Thibault }
7842067fd92SSamuel Thibault
7852067fd92SSamuel Thibault if (!isdigit(*cp)) {
7862067fd92SSamuel Thibault rejected++;
7872067fd92SSamuel Thibault cp = linefeed + 1;
7882067fd92SSamuel Thibault continue;
7892067fd92SSamuel Thibault }
7902067fd92SSamuel Thibault
7912067fd92SSamuel Thibault /*
7922067fd92SSamuel Thibault * Do not replace with kstrtoul:
7932067fd92SSamuel Thibault * here we need temp to be updated
7942067fd92SSamuel Thibault */
7952067fd92SSamuel Thibault index = simple_strtoul(cp, &temp, 10);
7962067fd92SSamuel Thibault
7972067fd92SSamuel Thibault while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
7982067fd92SSamuel Thibault temp++;
7992067fd92SSamuel Thibault
8002067fd92SSamuel Thibault desc_length = linefeed - temp;
8012067fd92SSamuel Thibault curmessage = firstmessage + index;
8022067fd92SSamuel Thibault
8032067fd92SSamuel Thibault /*
8042067fd92SSamuel Thibault * Note the check (curmessage < firstmessage). It is not
8052067fd92SSamuel Thibault * redundant. Suppose that the user gave us an index
8062067fd92SSamuel Thibault * equal to ULONG_MAX - 1. If firstmessage > 1, then
8072067fd92SSamuel Thibault * firstmessage + index < firstmessage!
8082067fd92SSamuel Thibault */
8092067fd92SSamuel Thibault
8102067fd92SSamuel Thibault if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
8112067fd92SSamuel Thibault rejected++;
8122067fd92SSamuel Thibault cp = linefeed + 1;
8132067fd92SSamuel Thibault continue;
8142067fd92SSamuel Thibault }
8152067fd92SSamuel Thibault
8162067fd92SSamuel Thibault msg_stored = spk_msg_set(curmessage, temp, desc_length);
8172067fd92SSamuel Thibault if (msg_stored < 0) {
8182067fd92SSamuel Thibault retval = msg_stored;
8192067fd92SSamuel Thibault if (msg_stored == -ENOMEM)
8202067fd92SSamuel Thibault reset = 1;
8212067fd92SSamuel Thibault break;
8222067fd92SSamuel Thibault }
8232067fd92SSamuel Thibault
8242067fd92SSamuel Thibault used++;
8252067fd92SSamuel Thibault
8262067fd92SSamuel Thibault cp = linefeed + 1;
8272067fd92SSamuel Thibault }
8282067fd92SSamuel Thibault
8292067fd92SSamuel Thibault if (reset)
8302067fd92SSamuel Thibault spk_reset_msg_group(group);
8312067fd92SSamuel Thibault
8322067fd92SSamuel Thibault report_msg_status(reset, received, used, rejected, group->name);
8332067fd92SSamuel Thibault return retval;
8342067fd92SSamuel Thibault }
8352067fd92SSamuel Thibault
message_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)8362067fd92SSamuel Thibault static ssize_t message_show(struct kobject *kobj,
8372067fd92SSamuel Thibault struct kobj_attribute *attr, char *buf)
8382067fd92SSamuel Thibault {
8392067fd92SSamuel Thibault ssize_t retval = 0;
8402067fd92SSamuel Thibault struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
8412067fd92SSamuel Thibault unsigned long flags;
8422067fd92SSamuel Thibault
8432067fd92SSamuel Thibault if (WARN_ON(!group))
8442067fd92SSamuel Thibault return -EINVAL;
8452067fd92SSamuel Thibault
8462067fd92SSamuel Thibault spin_lock_irqsave(&speakup_info.spinlock, flags);
8472067fd92SSamuel Thibault retval = message_show_helper(buf, group->start, group->end);
8482067fd92SSamuel Thibault spin_unlock_irqrestore(&speakup_info.spinlock, flags);
8492067fd92SSamuel Thibault return retval;
8502067fd92SSamuel Thibault }
8512067fd92SSamuel Thibault
message_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)8522067fd92SSamuel Thibault static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
8532067fd92SSamuel Thibault const char *buf, size_t count)
8542067fd92SSamuel Thibault {
8552067fd92SSamuel Thibault struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
8562067fd92SSamuel Thibault
8572067fd92SSamuel Thibault if (WARN_ON(!group))
8582067fd92SSamuel Thibault return -EINVAL;
8592067fd92SSamuel Thibault
8602067fd92SSamuel Thibault return message_store_helper(buf, count, group);
8612067fd92SSamuel Thibault }
8622067fd92SSamuel Thibault
8632067fd92SSamuel Thibault /*
8642067fd92SSamuel Thibault * Declare the attributes.
8652067fd92SSamuel Thibault */
8662067fd92SSamuel Thibault static struct kobj_attribute keymap_attribute =
8672067fd92SSamuel Thibault __ATTR_RW(keymap);
8682067fd92SSamuel Thibault static struct kobj_attribute silent_attribute =
8692067fd92SSamuel Thibault __ATTR_WO(silent);
8702067fd92SSamuel Thibault static struct kobj_attribute synth_attribute =
8712067fd92SSamuel Thibault __ATTR_RW(synth);
8722067fd92SSamuel Thibault static struct kobj_attribute synth_direct_attribute =
8732067fd92SSamuel Thibault __ATTR_WO(synth_direct);
8742067fd92SSamuel Thibault static struct kobj_attribute version_attribute =
8752067fd92SSamuel Thibault __ATTR_RO(version);
8762067fd92SSamuel Thibault
8772067fd92SSamuel Thibault static struct kobj_attribute delimiters_attribute =
8782067fd92SSamuel Thibault __ATTR(delimiters, 0644, punc_show, punc_store);
8792067fd92SSamuel Thibault static struct kobj_attribute ex_num_attribute =
8802067fd92SSamuel Thibault __ATTR(ex_num, 0644, punc_show, punc_store);
8812067fd92SSamuel Thibault static struct kobj_attribute punc_all_attribute =
8822067fd92SSamuel Thibault __ATTR(punc_all, 0644, punc_show, punc_store);
8832067fd92SSamuel Thibault static struct kobj_attribute punc_most_attribute =
8842067fd92SSamuel Thibault __ATTR(punc_most, 0644, punc_show, punc_store);
8852067fd92SSamuel Thibault static struct kobj_attribute punc_some_attribute =
8862067fd92SSamuel Thibault __ATTR(punc_some, 0644, punc_show, punc_store);
8872067fd92SSamuel Thibault static struct kobj_attribute repeats_attribute =
8882067fd92SSamuel Thibault __ATTR(repeats, 0644, punc_show, punc_store);
8892067fd92SSamuel Thibault
8902067fd92SSamuel Thibault static struct kobj_attribute attrib_bleep_attribute =
8912067fd92SSamuel Thibault __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);
8922067fd92SSamuel Thibault static struct kobj_attribute bell_pos_attribute =
8932067fd92SSamuel Thibault __ATTR(bell_pos, 0644, spk_var_show, spk_var_store);
8942067fd92SSamuel Thibault static struct kobj_attribute bleep_time_attribute =
8952067fd92SSamuel Thibault __ATTR(bleep_time, 0644, spk_var_show, spk_var_store);
8962067fd92SSamuel Thibault static struct kobj_attribute bleeps_attribute =
8972067fd92SSamuel Thibault __ATTR(bleeps, 0644, spk_var_show, spk_var_store);
8982067fd92SSamuel Thibault static struct kobj_attribute cursor_time_attribute =
8992067fd92SSamuel Thibault __ATTR(cursor_time, 0644, spk_var_show, spk_var_store);
9002067fd92SSamuel Thibault static struct kobj_attribute key_echo_attribute =
9012067fd92SSamuel Thibault __ATTR(key_echo, 0644, spk_var_show, spk_var_store);
9022067fd92SSamuel Thibault static struct kobj_attribute no_interrupt_attribute =
9032067fd92SSamuel Thibault __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);
9042067fd92SSamuel Thibault static struct kobj_attribute punc_level_attribute =
9052067fd92SSamuel Thibault __ATTR(punc_level, 0644, spk_var_show, spk_var_store);
9062067fd92SSamuel Thibault static struct kobj_attribute reading_punc_attribute =
9072067fd92SSamuel Thibault __ATTR(reading_punc, 0644, spk_var_show, spk_var_store);
9082067fd92SSamuel Thibault static struct kobj_attribute say_control_attribute =
9092067fd92SSamuel Thibault __ATTR(say_control, 0644, spk_var_show, spk_var_store);
9102067fd92SSamuel Thibault static struct kobj_attribute say_word_ctl_attribute =
9112067fd92SSamuel Thibault __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
9122067fd92SSamuel Thibault static struct kobj_attribute spell_delay_attribute =
9132067fd92SSamuel Thibault __ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
91472b8ec15SMushahid Hussain static struct kobj_attribute cur_phonetic_attribute =
91572b8ec15SMushahid Hussain __ATTR(cur_phonetic, 0644, spk_var_show, spk_var_store);
9162067fd92SSamuel Thibault
9172067fd92SSamuel Thibault /*
9182067fd92SSamuel Thibault * These attributes are i18n related.
9192067fd92SSamuel Thibault */
9202067fd92SSamuel Thibault static struct kobj_attribute announcements_attribute =
9212067fd92SSamuel Thibault __ATTR(announcements, 0644, message_show, message_store);
9222067fd92SSamuel Thibault static struct kobj_attribute characters_attribute =
9232067fd92SSamuel Thibault __ATTR(characters, 0644, chars_chartab_show,
9242067fd92SSamuel Thibault chars_chartab_store);
9252067fd92SSamuel Thibault static struct kobj_attribute chartab_attribute =
9262067fd92SSamuel Thibault __ATTR(chartab, 0644, chars_chartab_show,
9272067fd92SSamuel Thibault chars_chartab_store);
9282067fd92SSamuel Thibault static struct kobj_attribute ctl_keys_attribute =
9292067fd92SSamuel Thibault __ATTR(ctl_keys, 0644, message_show, message_store);
9302067fd92SSamuel Thibault static struct kobj_attribute colors_attribute =
9312067fd92SSamuel Thibault __ATTR(colors, 0644, message_show, message_store);
9322067fd92SSamuel Thibault static struct kobj_attribute formatted_attribute =
9332067fd92SSamuel Thibault __ATTR(formatted, 0644, message_show, message_store);
9342067fd92SSamuel Thibault static struct kobj_attribute function_names_attribute =
9352067fd92SSamuel Thibault __ATTR(function_names, 0644, message_show, message_store);
9362067fd92SSamuel Thibault static struct kobj_attribute key_names_attribute =
9372067fd92SSamuel Thibault __ATTR(key_names, 0644, message_show, message_store);
9382067fd92SSamuel Thibault static struct kobj_attribute states_attribute =
9392067fd92SSamuel Thibault __ATTR(states, 0644, message_show, message_store);
9402067fd92SSamuel Thibault
9412067fd92SSamuel Thibault /*
9422067fd92SSamuel Thibault * Create groups of attributes so that we can create and destroy them all
9432067fd92SSamuel Thibault * at once.
9442067fd92SSamuel Thibault */
9452067fd92SSamuel Thibault static struct attribute *main_attrs[] = {
9462067fd92SSamuel Thibault &keymap_attribute.attr,
9472067fd92SSamuel Thibault &silent_attribute.attr,
9482067fd92SSamuel Thibault &synth_attribute.attr,
9492067fd92SSamuel Thibault &synth_direct_attribute.attr,
9502067fd92SSamuel Thibault &version_attribute.attr,
9512067fd92SSamuel Thibault &delimiters_attribute.attr,
9522067fd92SSamuel Thibault &ex_num_attribute.attr,
9532067fd92SSamuel Thibault &punc_all_attribute.attr,
9542067fd92SSamuel Thibault &punc_most_attribute.attr,
9552067fd92SSamuel Thibault &punc_some_attribute.attr,
9562067fd92SSamuel Thibault &repeats_attribute.attr,
9572067fd92SSamuel Thibault &attrib_bleep_attribute.attr,
9582067fd92SSamuel Thibault &bell_pos_attribute.attr,
9592067fd92SSamuel Thibault &bleep_time_attribute.attr,
9602067fd92SSamuel Thibault &bleeps_attribute.attr,
9612067fd92SSamuel Thibault &cursor_time_attribute.attr,
9622067fd92SSamuel Thibault &key_echo_attribute.attr,
9632067fd92SSamuel Thibault &no_interrupt_attribute.attr,
9642067fd92SSamuel Thibault &punc_level_attribute.attr,
9652067fd92SSamuel Thibault &reading_punc_attribute.attr,
9662067fd92SSamuel Thibault &say_control_attribute.attr,
9672067fd92SSamuel Thibault &say_word_ctl_attribute.attr,
9682067fd92SSamuel Thibault &spell_delay_attribute.attr,
96972b8ec15SMushahid Hussain &cur_phonetic_attribute.attr,
9702067fd92SSamuel Thibault NULL,
9712067fd92SSamuel Thibault };
9722067fd92SSamuel Thibault
9732067fd92SSamuel Thibault static struct attribute *i18n_attrs[] = {
9742067fd92SSamuel Thibault &announcements_attribute.attr,
9752067fd92SSamuel Thibault &characters_attribute.attr,
9762067fd92SSamuel Thibault &chartab_attribute.attr,
9772067fd92SSamuel Thibault &ctl_keys_attribute.attr,
9782067fd92SSamuel Thibault &colors_attribute.attr,
9792067fd92SSamuel Thibault &formatted_attribute.attr,
9802067fd92SSamuel Thibault &function_names_attribute.attr,
9812067fd92SSamuel Thibault &key_names_attribute.attr,
9822067fd92SSamuel Thibault &states_attribute.attr,
9832067fd92SSamuel Thibault NULL,
9842067fd92SSamuel Thibault };
9852067fd92SSamuel Thibault
9862067fd92SSamuel Thibault /*
9872067fd92SSamuel Thibault * An unnamed attribute group will put all of the attributes directly in
9882067fd92SSamuel Thibault * the kobject directory. If we specify a name, a subdirectory will be
9892067fd92SSamuel Thibault * created for the attributes with the directory being the name of the
9902067fd92SSamuel Thibault * attribute group.
9912067fd92SSamuel Thibault */
9922067fd92SSamuel Thibault static const struct attribute_group main_attr_group = {
9932067fd92SSamuel Thibault .attrs = main_attrs,
9942067fd92SSamuel Thibault };
9952067fd92SSamuel Thibault
9962067fd92SSamuel Thibault static const struct attribute_group i18n_attr_group = {
9972067fd92SSamuel Thibault .attrs = i18n_attrs,
9982067fd92SSamuel Thibault .name = "i18n",
9992067fd92SSamuel Thibault };
10002067fd92SSamuel Thibault
10012067fd92SSamuel Thibault static struct kobject *accessibility_kobj;
10022067fd92SSamuel Thibault struct kobject *speakup_kobj;
10032067fd92SSamuel Thibault
speakup_kobj_init(void)10042067fd92SSamuel Thibault int speakup_kobj_init(void)
10052067fd92SSamuel Thibault {
10062067fd92SSamuel Thibault int retval;
10072067fd92SSamuel Thibault
10082067fd92SSamuel Thibault /*
10092067fd92SSamuel Thibault * Create a simple kobject with the name of "accessibility",
10102067fd92SSamuel Thibault * located under /sys/
10112067fd92SSamuel Thibault *
10122067fd92SSamuel Thibault * As this is a simple directory, no uevent will be sent to
10132067fd92SSamuel Thibault * userspace. That is why this function should not be used for
10142067fd92SSamuel Thibault * any type of dynamic kobjects, where the name and number are
10152067fd92SSamuel Thibault * not known ahead of time.
10162067fd92SSamuel Thibault */
10172067fd92SSamuel Thibault accessibility_kobj = kobject_create_and_add("accessibility", NULL);
10182067fd92SSamuel Thibault if (!accessibility_kobj) {
10192067fd92SSamuel Thibault retval = -ENOMEM;
10202067fd92SSamuel Thibault goto out;
10212067fd92SSamuel Thibault }
10222067fd92SSamuel Thibault
10232067fd92SSamuel Thibault speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj);
10242067fd92SSamuel Thibault if (!speakup_kobj) {
10252067fd92SSamuel Thibault retval = -ENOMEM;
10262067fd92SSamuel Thibault goto err_acc;
10272067fd92SSamuel Thibault }
10282067fd92SSamuel Thibault
10292067fd92SSamuel Thibault /* Create the files associated with this kobject */
10302067fd92SSamuel Thibault retval = sysfs_create_group(speakup_kobj, &main_attr_group);
10312067fd92SSamuel Thibault if (retval)
10322067fd92SSamuel Thibault goto err_speakup;
10332067fd92SSamuel Thibault
10342067fd92SSamuel Thibault retval = sysfs_create_group(speakup_kobj, &i18n_attr_group);
10352067fd92SSamuel Thibault if (retval)
10362067fd92SSamuel Thibault goto err_group;
10372067fd92SSamuel Thibault
10382067fd92SSamuel Thibault goto out;
10392067fd92SSamuel Thibault
10402067fd92SSamuel Thibault err_group:
10412067fd92SSamuel Thibault sysfs_remove_group(speakup_kobj, &main_attr_group);
10422067fd92SSamuel Thibault err_speakup:
10432067fd92SSamuel Thibault kobject_put(speakup_kobj);
10442067fd92SSamuel Thibault err_acc:
10452067fd92SSamuel Thibault kobject_put(accessibility_kobj);
10462067fd92SSamuel Thibault out:
10472067fd92SSamuel Thibault return retval;
10482067fd92SSamuel Thibault }
10492067fd92SSamuel Thibault
speakup_kobj_exit(void)10502067fd92SSamuel Thibault void speakup_kobj_exit(void)
10512067fd92SSamuel Thibault {
10522067fd92SSamuel Thibault sysfs_remove_group(speakup_kobj, &i18n_attr_group);
10532067fd92SSamuel Thibault sysfs_remove_group(speakup_kobj, &main_attr_group);
10542067fd92SSamuel Thibault kobject_put(speakup_kobj);
10552067fd92SSamuel Thibault kobject_put(accessibility_kobj);
10562067fd92SSamuel Thibault }
1057