Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Speakup kobject implementation
0004  *
0005  * Copyright (C) 2009 William Hubbs
0006  *
0007  * This code is based on kobject-example.c, which came with linux 2.6.x.
0008  *
0009  * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
0010  * Copyright (C) 2007 Novell Inc.
0011  *
0012  * Released under the GPL version 2 only.
0013  *
0014  */
0015 #include <linux/slab.h>     /* For kmalloc. */
0016 #include <linux/kernel.h>
0017 #include <linux/kobject.h>
0018 #include <linux/string.h>
0019 #include <linux/string_helpers.h>
0020 #include <linux/sysfs.h>
0021 #include <linux/ctype.h>
0022 
0023 #include "speakup.h"
0024 #include "spk_priv.h"
0025 
0026 /*
0027  * This is called when a user reads the characters or chartab sys file.
0028  */
0029 static ssize_t chars_chartab_show(struct kobject *kobj,
0030                   struct kobj_attribute *attr, char *buf)
0031 {
0032     int i;
0033     int len = 0;
0034     char *cp;
0035     char *buf_pointer = buf;
0036     size_t bufsize = PAGE_SIZE;
0037     unsigned long flags;
0038 
0039     spin_lock_irqsave(&speakup_info.spinlock, flags);
0040     *buf_pointer = '\0';
0041     for (i = 0; i < 256; i++) {
0042         if (bufsize <= 1)
0043             break;
0044         if (strcmp("characters", attr->attr.name) == 0) {
0045             len = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
0046                     i, spk_characters[i]);
0047         } else {    /* show chartab entry */
0048             if (IS_TYPE(i, B_CTL))
0049                 cp = "B_CTL";
0050             else if (IS_TYPE(i, WDLM))
0051                 cp = "WDLM";
0052             else if (IS_TYPE(i, A_PUNC))
0053                 cp = "A_PUNC";
0054             else if (IS_TYPE(i, PUNC))
0055                 cp = "PUNC";
0056             else if (IS_TYPE(i, NUM))
0057                 cp = "NUM";
0058             else if (IS_TYPE(i, A_CAP))
0059                 cp = "A_CAP";
0060             else if (IS_TYPE(i, ALPHA))
0061                 cp = "ALPHA";
0062             else if (IS_TYPE(i, B_CAPSYM))
0063                 cp = "B_CAPSYM";
0064             else if (IS_TYPE(i, B_SYM))
0065                 cp = "B_SYM";
0066             else
0067                 cp = "0";
0068             len =
0069                 scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp);
0070         }
0071         bufsize -= len;
0072         buf_pointer += len;
0073     }
0074     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0075     return buf_pointer - buf;
0076 }
0077 
0078 /*
0079  * Print informational messages or warnings after updating
0080  * character descriptions or chartab entries.
0081  */
0082 static void report_char_chartab_status(int reset, int received, int used,
0083                        int rejected, int do_characters)
0084 {
0085     static char const *object_type[] = {
0086         "character class entries",
0087         "character descriptions",
0088     };
0089     int len;
0090     char buf[80];
0091 
0092     if (reset) {
0093         pr_info("%s reset to defaults\n", object_type[do_characters]);
0094     } else if (received) {
0095         len = snprintf(buf, sizeof(buf),
0096                    " updated %d of %d %s\n",
0097                    used, received, object_type[do_characters]);
0098         if (rejected)
0099             snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
0100                  " with %d reject%s\n",
0101                  rejected, rejected > 1 ? "s" : "");
0102         pr_info("%s", buf);
0103     }
0104 }
0105 
0106 /*
0107  * This is called when a user changes the characters or chartab parameters.
0108  */
0109 static ssize_t chars_chartab_store(struct kobject *kobj,
0110                    struct kobj_attribute *attr,
0111                    const char *buf, size_t count)
0112 {
0113     char *cp = (char *)buf;
0114     char *end = cp + count; /* the null at the end of the buffer */
0115     char *linefeed = NULL;
0116     char keyword[MAX_DESC_LEN + 1];
0117     char *outptr = NULL;    /* Will hold keyword or desc. */
0118     char *temp = NULL;
0119     char *desc = NULL;
0120     ssize_t retval = count;
0121     unsigned long flags;
0122     unsigned long index = 0;
0123     int charclass = 0;
0124     int received = 0;
0125     int used = 0;
0126     int rejected = 0;
0127     int reset = 0;
0128     int do_characters = !strcmp(attr->attr.name, "characters");
0129     size_t desc_length = 0;
0130     int i;
0131 
0132     spin_lock_irqsave(&speakup_info.spinlock, flags);
0133     while (cp < end) {
0134         while ((cp < end) && (*cp == ' ' || *cp == '\t'))
0135             cp++;
0136 
0137         if (cp == end)
0138             break;
0139         if ((*cp == '\n') || strchr("dDrR", *cp)) {
0140             reset = 1;
0141             break;
0142         }
0143         received++;
0144 
0145         linefeed = strchr(cp, '\n');
0146         if (!linefeed) {
0147             rejected++;
0148             break;
0149         }
0150 
0151         if (!isdigit(*cp)) {
0152             rejected++;
0153             cp = linefeed + 1;
0154             continue;
0155         }
0156 
0157         /*
0158          * Do not replace with kstrtoul:
0159          * here we need temp to be updated
0160          */
0161         index = simple_strtoul(cp, &temp, 10);
0162         if (index > 255) {
0163             rejected++;
0164             cp = linefeed + 1;
0165             continue;
0166         }
0167 
0168         while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
0169             temp++;
0170 
0171         desc_length = linefeed - temp;
0172         if (desc_length > MAX_DESC_LEN) {
0173             rejected++;
0174             cp = linefeed + 1;
0175             continue;
0176         }
0177         if (do_characters) {
0178             desc = kmalloc(desc_length + 1, GFP_ATOMIC);
0179             if (!desc) {
0180                 retval = -ENOMEM;
0181                 reset = 1;  /* just reset on error. */
0182                 break;
0183             }
0184             outptr = desc;
0185         } else {
0186             outptr = keyword;
0187         }
0188 
0189         for (i = 0; i < desc_length; i++)
0190             outptr[i] = temp[i];
0191         outptr[desc_length] = '\0';
0192 
0193         if (do_characters) {
0194             if (spk_characters[index] != spk_default_chars[index])
0195                 kfree(spk_characters[index]);
0196             spk_characters[index] = desc;
0197             used++;
0198         } else {
0199             charclass = spk_chartab_get_value(keyword);
0200             if (charclass == 0) {
0201                 rejected++;
0202                 cp = linefeed + 1;
0203                 continue;
0204             }
0205             if (charclass != spk_chartab[index]) {
0206                 spk_chartab[index] = charclass;
0207                 used++;
0208             }
0209         }
0210         cp = linefeed + 1;
0211     }
0212 
0213     if (reset) {
0214         if (do_characters)
0215             spk_reset_default_chars();
0216         else
0217             spk_reset_default_chartab();
0218     }
0219 
0220     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0221     report_char_chartab_status(reset, received, used, rejected,
0222                    do_characters);
0223     return retval;
0224 }
0225 
0226 /*
0227  * This is called when a user reads the keymap parameter.
0228  */
0229 static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr,
0230                char *buf)
0231 {
0232     char *cp = buf;
0233     int i;
0234     int n;
0235     int num_keys;
0236     int nstates;
0237     u_char *cp1;
0238     u_char ch;
0239     unsigned long flags;
0240 
0241     spin_lock_irqsave(&speakup_info.spinlock, flags);
0242     cp1 = spk_key_buf + SHIFT_TBL_SIZE;
0243     num_keys = (int)(*cp1);
0244     nstates = (int)cp1[1];
0245     cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates);
0246     cp1 += 2; /* now pointing at shift states */
0247     /* dump num_keys+1 as first row is shift states + flags,
0248      * each subsequent row is key + states
0249      */
0250     for (n = 0; n <= num_keys; n++) {
0251         for (i = 0; i <= nstates; i++) {
0252             ch = *cp1++;
0253             cp += sprintf(cp, "%d,", (int)ch);
0254             *cp++ = (i < nstates) ? SPACE : '\n';
0255         }
0256     }
0257     cp += sprintf(cp, "0, %d\n", KEY_MAP_VER);
0258     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0259     return (int)(cp - buf);
0260 }
0261 
0262 /*
0263  * This is called when a user changes the keymap parameter.
0264  */
0265 static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr,
0266                 const char *buf, size_t count)
0267 {
0268     int i;
0269     ssize_t ret = count;
0270     char *in_buff = NULL;
0271     char *cp;
0272     u_char *cp1;
0273     unsigned long flags;
0274 
0275     spin_lock_irqsave(&speakup_info.spinlock, flags);
0276     in_buff = kmemdup(buf, count + 1, GFP_ATOMIC);
0277     if (!in_buff) {
0278         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0279         return -ENOMEM;
0280     }
0281     if (strchr("dDrR", *in_buff)) {
0282         spk_set_key_info(spk_key_defaults, spk_key_buf);
0283         pr_info("keymap set to default values\n");
0284         kfree(in_buff);
0285         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0286         return count;
0287     }
0288     if (in_buff[count - 1] == '\n')
0289         in_buff[count - 1] = '\0';
0290     cp = in_buff;
0291     cp1 = (u_char *)in_buff;
0292     for (i = 0; i < 3; i++) {
0293         cp = spk_s2uchar(cp, cp1);
0294         cp1++;
0295     }
0296     i = (int)cp1[-2] + 1;
0297     i *= (int)cp1[-1] + 1;
0298     i += 2; /* 0 and last map ver */
0299     if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||
0300         i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
0301         pr_warn("i %d %d %d %d\n", i,
0302             (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
0303         kfree(in_buff);
0304         spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0305         return -EINVAL;
0306     }
0307     while (--i >= 0) {
0308         cp = spk_s2uchar(cp, cp1);
0309         cp1++;
0310         if (!(*cp))
0311             break;
0312     }
0313     if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {
0314         ret = -EINVAL;
0315         pr_warn("end %d %d %d %d\n", i,
0316             (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
0317     } else {
0318         if (spk_set_key_info(in_buff, spk_key_buf)) {
0319             spk_set_key_info(spk_key_defaults, spk_key_buf);
0320             ret = -EINVAL;
0321             pr_warn("set key failed\n");
0322         }
0323     }
0324     kfree(in_buff);
0325     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0326     return ret;
0327 }
0328 
0329 /*
0330  * This is called when a user changes the value of the silent parameter.
0331  */
0332 static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,
0333                 const char *buf, size_t count)
0334 {
0335     int len;
0336     struct vc_data *vc = vc_cons[fg_console].d;
0337     char ch = 0;
0338     char shut;
0339     unsigned long flags;
0340 
0341     len = strlen(buf);
0342     if (len > 0 && len < 3) {
0343         ch = buf[0];
0344         if (ch == '\n')
0345             ch = '0';
0346     }
0347     if (ch < '0' || ch > '7') {
0348         pr_warn("silent value '%c' not in range (0,7)\n", ch);
0349         return -EINVAL;
0350     }
0351     spin_lock_irqsave(&speakup_info.spinlock, flags);
0352     if (ch & 2) {
0353         shut = 1;
0354         spk_do_flush();
0355     } else {
0356         shut = 0;
0357     }
0358     if (ch & 4)
0359         shut |= 0x40;
0360     if (ch & 1)
0361         spk_shut_up |= shut;
0362     else
0363         spk_shut_up &= ~shut;
0364     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0365     return count;
0366 }
0367 
0368 /*
0369  * This is called when a user reads the synth setting.
0370  */
0371 static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,
0372               char *buf)
0373 {
0374     int rv;
0375 
0376     if (!synth)
0377         rv = sprintf(buf, "%s\n", "none");
0378     else
0379         rv = sprintf(buf, "%s\n", synth->name);
0380     return rv;
0381 }
0382 
0383 /*
0384  * This is called when a user requests to change synthesizers.
0385  */
0386 static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,
0387                const char *buf, size_t count)
0388 {
0389     int len;
0390     char new_synth_name[10];
0391 
0392     len = strlen(buf);
0393     if (len < 2 || len > 9)
0394         return -EINVAL;
0395     memcpy(new_synth_name, buf, len);
0396     if (new_synth_name[len - 1] == '\n')
0397         len--;
0398     new_synth_name[len] = '\0';
0399     spk_strlwr(new_synth_name);
0400     if (synth && !strcmp(new_synth_name, synth->name)) {
0401         pr_warn("%s already in use\n", new_synth_name);
0402     } else if (synth_init(new_synth_name) != 0) {
0403         pr_warn("failed to init synth %s\n", new_synth_name);
0404         return -ENODEV;
0405     }
0406     return count;
0407 }
0408 
0409 /*
0410  * This is called when text is sent to the synth via the synth_direct file.
0411  */
0412 static ssize_t synth_direct_store(struct kobject *kobj,
0413                   struct kobj_attribute *attr,
0414                   const char *buf, size_t count)
0415 {
0416     u_char tmp[256];
0417     int len;
0418     int bytes;
0419     const char *ptr = buf;
0420     unsigned long flags;
0421 
0422     if (!synth)
0423         return -EPERM;
0424 
0425     len = strlen(buf);
0426     spin_lock_irqsave(&speakup_info.spinlock, flags);
0427     while (len > 0) {
0428         bytes = min_t(size_t, len, 250);
0429         strncpy(tmp, ptr, bytes);
0430         tmp[bytes] = '\0';
0431         string_unescape_any_inplace(tmp);
0432         synth_printf("%s", tmp);
0433         ptr += bytes;
0434         len -= bytes;
0435     }
0436     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0437     return count;
0438 }
0439 
0440 /*
0441  * This function is called when a user reads the version.
0442  */
0443 static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
0444                 char *buf)
0445 {
0446     char *cp;
0447 
0448     cp = buf;
0449     cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
0450     if (synth)
0451         cp += sprintf(cp, "%s synthesizer driver version %s\n",
0452         synth->name, synth->version);
0453     return cp - buf;
0454 }
0455 
0456 /*
0457  * This is called when a user reads the punctuation settings.
0458  */
0459 static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
0460              char *buf)
0461 {
0462     int i;
0463     char *cp = buf;
0464     struct st_var_header *p_header;
0465     struct punc_var_t *var;
0466     struct st_bits_data *pb;
0467     short mask;
0468     unsigned long flags;
0469 
0470     p_header = spk_var_header_by_name(attr->attr.name);
0471     if (!p_header) {
0472         pr_warn("p_header is null, attr->attr.name is %s\n",
0473             attr->attr.name);
0474         return -EINVAL;
0475     }
0476 
0477     var = spk_get_punc_var(p_header->var_id);
0478     if (!var) {
0479         pr_warn("var is null, p_header->var_id is %i\n",
0480             p_header->var_id);
0481         return -EINVAL;
0482     }
0483 
0484     spin_lock_irqsave(&speakup_info.spinlock, flags);
0485     pb = (struct st_bits_data *)&spk_punc_info[var->value];
0486     mask = pb->mask;
0487     for (i = 33; i < 128; i++) {
0488         if (!(spk_chartab[i] & mask))
0489             continue;
0490         *cp++ = (char)i;
0491     }
0492     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0493     return cp - buf;
0494 }
0495 
0496 /*
0497  * This is called when a user changes the punctuation settings.
0498  */
0499 static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
0500               const char *buf, size_t count)
0501 {
0502     int x;
0503     struct st_var_header *p_header;
0504     struct punc_var_t *var;
0505     char punc_buf[100];
0506     unsigned long flags;
0507 
0508     x = strlen(buf);
0509     if (x < 1 || x > 99)
0510         return -EINVAL;
0511 
0512     p_header = spk_var_header_by_name(attr->attr.name);
0513     if (!p_header) {
0514         pr_warn("p_header is null, attr->attr.name is %s\n",
0515             attr->attr.name);
0516         return -EINVAL;
0517     }
0518 
0519     var = spk_get_punc_var(p_header->var_id);
0520     if (!var) {
0521         pr_warn("var is null, p_header->var_id is %i\n",
0522             p_header->var_id);
0523         return -EINVAL;
0524     }
0525 
0526     memcpy(punc_buf, buf, x);
0527 
0528     while (x && punc_buf[x - 1] == '\n')
0529         x--;
0530     punc_buf[x] = '\0';
0531 
0532     spin_lock_irqsave(&speakup_info.spinlock, flags);
0533 
0534     if (*punc_buf == 'd' || *punc_buf == 'r')
0535         x = spk_set_mask_bits(NULL, var->value, 3);
0536     else
0537         x = spk_set_mask_bits(punc_buf, var->value, 3);
0538 
0539     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0540     return count;
0541 }
0542 
0543 /*
0544  * This function is called when a user reads one of the variable parameters.
0545  */
0546 ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
0547              char *buf)
0548 {
0549     int rv = 0;
0550     struct st_var_header *param;
0551     struct var_t *var;
0552     char *cp1;
0553     char *cp;
0554     char ch;
0555     unsigned long flags;
0556 
0557     param = spk_var_header_by_name(attr->attr.name);
0558     if (!param)
0559         return -EINVAL;
0560 
0561     spin_lock_irqsave(&speakup_info.spinlock, flags);
0562     var = (struct var_t *)param->data;
0563     switch (param->var_type) {
0564     case VAR_NUM:
0565     case VAR_TIME:
0566         if (var)
0567             rv = sprintf(buf, "%i\n", var->u.n.value);
0568         else
0569             rv = sprintf(buf, "0\n");
0570         break;
0571     case VAR_STRING:
0572         if (var) {
0573             cp1 = buf;
0574             *cp1++ = '"';
0575             for (cp = (char *)param->p_val; (ch = *cp); cp++) {
0576                 if (ch >= ' ' && ch < '~')
0577                     *cp1++ = ch;
0578                 else
0579                     cp1 += sprintf(cp1, "\\x%02x", ch);
0580             }
0581             *cp1++ = '"';
0582             *cp1++ = '\n';
0583             *cp1 = '\0';
0584             rv = cp1 - buf;
0585         } else {
0586             rv = sprintf(buf, "\"\"\n");
0587         }
0588         break;
0589     default:
0590         rv = sprintf(buf, "Bad parameter  %s, type %i\n",
0591                  param->name, param->var_type);
0592         break;
0593     }
0594     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0595     return rv;
0596 }
0597 EXPORT_SYMBOL_GPL(spk_var_show);
0598 
0599 /*
0600  * Used to reset either default_pitch or default_vol.
0601  */
0602 static inline void spk_reset_default_value(char *header_name,
0603                        int *synth_default_value, int idx)
0604 {
0605     struct st_var_header *param;
0606 
0607     if (synth && synth_default_value) {
0608         param = spk_var_header_by_name(header_name);
0609         if (param)  {
0610             spk_set_num_var(synth_default_value[idx],
0611                     param, E_NEW_DEFAULT);
0612             spk_set_num_var(0, param, E_DEFAULT);
0613             pr_info("%s reset to default value\n", param->name);
0614         }
0615     }
0616 }
0617 
0618 /*
0619  * This function is called when a user echos a value to one of the
0620  * variable parameters.
0621  */
0622 ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
0623               const char *buf, size_t count)
0624 {
0625     struct st_var_header *param;
0626     int ret;
0627     int len;
0628     char *cp;
0629     struct var_t *var_data;
0630     long value;
0631     unsigned long flags;
0632 
0633     param = spk_var_header_by_name(attr->attr.name);
0634     if (!param)
0635         return -EINVAL;
0636     if (!param->data)
0637         return 0;
0638     ret = 0;
0639     cp = (char *)buf;
0640     string_unescape_any_inplace(cp);
0641 
0642     spin_lock_irqsave(&speakup_info.spinlock, flags);
0643     switch (param->var_type) {
0644     case VAR_NUM:
0645     case VAR_TIME:
0646         if (*cp == 'd' || *cp == 'r' || *cp == '\0')
0647             len = E_DEFAULT;
0648         else if (*cp == '+' || *cp == '-')
0649             len = E_INC;
0650         else
0651             len = E_SET;
0652         if (kstrtol(cp, 10, &value) == 0)
0653             ret = spk_set_num_var(value, param, len);
0654         else
0655             pr_warn("overflow or parsing error has occurred");
0656         if (ret == -ERANGE) {
0657             var_data = param->data;
0658             pr_warn("value for %s out of range, expect %d to %d\n",
0659                 param->name,
0660                 var_data->u.n.low, var_data->u.n.high);
0661         }
0662 
0663            /*
0664         * If voice was just changed, we might need to reset our default
0665         * pitch and volume.
0666         */
0667         if (param->var_id == VOICE && synth &&
0668             (ret == 0 || ret == -ERESTART)) {
0669             var_data = param->data;
0670             value = var_data->u.n.value;
0671             spk_reset_default_value("pitch", synth->default_pitch,
0672                         value);
0673             spk_reset_default_value("vol", synth->default_vol,
0674                         value);
0675         }
0676         break;
0677     case VAR_STRING:
0678         len = strlen(cp);
0679         if ((len >= 1) && (cp[len - 1] == '\n'))
0680             --len;
0681         if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
0682             ++cp;
0683             len -= 2;
0684         }
0685         cp[len] = '\0';
0686         ret = spk_set_string_var(cp, param, len);
0687         if (ret == -E2BIG)
0688             pr_warn("value too long for %s\n",
0689                 param->name);
0690         break;
0691     default:
0692         pr_warn("%s unknown type %d\n",
0693             param->name, (int)param->var_type);
0694     break;
0695     }
0696     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0697 
0698     if (ret == -ERESTART)
0699         pr_info("%s reset to default value\n", param->name);
0700     return count;
0701 }
0702 EXPORT_SYMBOL_GPL(spk_var_store);
0703 
0704 /*
0705  * Functions for reading and writing lists of i18n messages.  Incomplete.
0706  */
0707 
0708 static ssize_t message_show_helper(char *buf, enum msg_index_t first,
0709                    enum msg_index_t last)
0710 {
0711     size_t bufsize = PAGE_SIZE;
0712     char *buf_pointer = buf;
0713     int printed;
0714     enum msg_index_t cursor;
0715     int index = 0;
0716     *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
0717 
0718     for (cursor = first; cursor <= last; cursor++, index++) {
0719         if (bufsize <= 1)
0720             break;
0721         printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
0722                     index, spk_msg_get(cursor));
0723         buf_pointer += printed;
0724         bufsize -= printed;
0725     }
0726 
0727     return buf_pointer - buf;
0728 }
0729 
0730 static void report_msg_status(int reset, int received, int used,
0731                   int rejected, char *groupname)
0732 {
0733     int len;
0734     char buf[160];
0735 
0736     if (reset) {
0737         pr_info("i18n messages from group %s reset to defaults\n",
0738             groupname);
0739     } else if (received) {
0740         len = snprintf(buf, sizeof(buf),
0741                    " updated %d of %d i18n messages from group %s\n",
0742                        used, received, groupname);
0743         if (rejected)
0744             snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
0745                  " with %d reject%s\n",
0746                  rejected, rejected > 1 ? "s" : "");
0747         pr_info("%s", buf);
0748     }
0749 }
0750 
0751 static ssize_t message_store_helper(const char *buf, size_t count,
0752                     struct msg_group_t *group)
0753 {
0754     char *cp = (char *)buf;
0755     char *end = cp + count;
0756     char *linefeed = NULL;
0757     char *temp = NULL;
0758     ssize_t msg_stored = 0;
0759     ssize_t retval = count;
0760     size_t desc_length = 0;
0761     unsigned long index = 0;
0762     int received = 0;
0763     int used = 0;
0764     int rejected = 0;
0765     int reset = 0;
0766     enum msg_index_t firstmessage = group->start;
0767     enum msg_index_t lastmessage = group->end;
0768     enum msg_index_t curmessage;
0769 
0770     while (cp < end) {
0771         while ((cp < end) && (*cp == ' ' || *cp == '\t'))
0772             cp++;
0773 
0774         if (cp == end)
0775             break;
0776         if (strchr("dDrR", *cp)) {
0777             reset = 1;
0778             break;
0779         }
0780         received++;
0781 
0782         linefeed = strchr(cp, '\n');
0783         if (!linefeed) {
0784             rejected++;
0785             break;
0786         }
0787 
0788         if (!isdigit(*cp)) {
0789             rejected++;
0790             cp = linefeed + 1;
0791             continue;
0792         }
0793 
0794         /*
0795          * Do not replace with kstrtoul:
0796          * here we need temp to be updated
0797          */
0798         index = simple_strtoul(cp, &temp, 10);
0799 
0800         while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
0801             temp++;
0802 
0803         desc_length = linefeed - temp;
0804         curmessage = firstmessage + index;
0805 
0806         /*
0807          * Note the check (curmessage < firstmessage).  It is not
0808          * redundant.  Suppose that the user gave us an index
0809          * equal to ULONG_MAX - 1.  If firstmessage > 1, then
0810          * firstmessage + index < firstmessage!
0811          */
0812 
0813         if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
0814             rejected++;
0815             cp = linefeed + 1;
0816             continue;
0817         }
0818 
0819         msg_stored = spk_msg_set(curmessage, temp, desc_length);
0820         if (msg_stored < 0) {
0821             retval = msg_stored;
0822             if (msg_stored == -ENOMEM)
0823                 reset = 1;
0824             break;
0825         }
0826 
0827         used++;
0828 
0829         cp = linefeed + 1;
0830     }
0831 
0832     if (reset)
0833         spk_reset_msg_group(group);
0834 
0835     report_msg_status(reset, received, used, rejected, group->name);
0836     return retval;
0837 }
0838 
0839 static ssize_t message_show(struct kobject *kobj,
0840                 struct kobj_attribute *attr, char *buf)
0841 {
0842     ssize_t retval = 0;
0843     struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
0844     unsigned long flags;
0845 
0846     if (WARN_ON(!group))
0847         return -EINVAL;
0848 
0849     spin_lock_irqsave(&speakup_info.spinlock, flags);
0850     retval = message_show_helper(buf, group->start, group->end);
0851     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0852     return retval;
0853 }
0854 
0855 static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
0856                  const char *buf, size_t count)
0857 {
0858     struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
0859 
0860     if (WARN_ON(!group))
0861         return -EINVAL;
0862 
0863     return message_store_helper(buf, count, group);
0864 }
0865 
0866 /*
0867  * Declare the attributes.
0868  */
0869 static struct kobj_attribute keymap_attribute =
0870     __ATTR_RW(keymap);
0871 static struct kobj_attribute silent_attribute =
0872     __ATTR_WO(silent);
0873 static struct kobj_attribute synth_attribute =
0874     __ATTR_RW(synth);
0875 static struct kobj_attribute synth_direct_attribute =
0876     __ATTR_WO(synth_direct);
0877 static struct kobj_attribute version_attribute =
0878     __ATTR_RO(version);
0879 
0880 static struct kobj_attribute delimiters_attribute =
0881     __ATTR(delimiters, 0644, punc_show, punc_store);
0882 static struct kobj_attribute ex_num_attribute =
0883     __ATTR(ex_num, 0644, punc_show, punc_store);
0884 static struct kobj_attribute punc_all_attribute =
0885     __ATTR(punc_all, 0644, punc_show, punc_store);
0886 static struct kobj_attribute punc_most_attribute =
0887     __ATTR(punc_most, 0644, punc_show, punc_store);
0888 static struct kobj_attribute punc_some_attribute =
0889     __ATTR(punc_some, 0644, punc_show, punc_store);
0890 static struct kobj_attribute repeats_attribute =
0891     __ATTR(repeats, 0644, punc_show, punc_store);
0892 
0893 static struct kobj_attribute attrib_bleep_attribute =
0894     __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);
0895 static struct kobj_attribute bell_pos_attribute =
0896     __ATTR(bell_pos, 0644, spk_var_show, spk_var_store);
0897 static struct kobj_attribute bleep_time_attribute =
0898     __ATTR(bleep_time, 0644, spk_var_show, spk_var_store);
0899 static struct kobj_attribute bleeps_attribute =
0900     __ATTR(bleeps, 0644, spk_var_show, spk_var_store);
0901 static struct kobj_attribute cursor_time_attribute =
0902     __ATTR(cursor_time, 0644, spk_var_show, spk_var_store);
0903 static struct kobj_attribute key_echo_attribute =
0904     __ATTR(key_echo, 0644, spk_var_show, spk_var_store);
0905 static struct kobj_attribute no_interrupt_attribute =
0906     __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);
0907 static struct kobj_attribute punc_level_attribute =
0908     __ATTR(punc_level, 0644, spk_var_show, spk_var_store);
0909 static struct kobj_attribute reading_punc_attribute =
0910     __ATTR(reading_punc, 0644, spk_var_show, spk_var_store);
0911 static struct kobj_attribute say_control_attribute =
0912     __ATTR(say_control, 0644, spk_var_show, spk_var_store);
0913 static struct kobj_attribute say_word_ctl_attribute =
0914     __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
0915 static struct kobj_attribute spell_delay_attribute =
0916     __ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
0917 
0918 /*
0919  * These attributes are i18n related.
0920  */
0921 static struct kobj_attribute announcements_attribute =
0922     __ATTR(announcements, 0644, message_show, message_store);
0923 static struct kobj_attribute characters_attribute =
0924     __ATTR(characters, 0644, chars_chartab_show,
0925            chars_chartab_store);
0926 static struct kobj_attribute chartab_attribute =
0927     __ATTR(chartab, 0644, chars_chartab_show,
0928            chars_chartab_store);
0929 static struct kobj_attribute ctl_keys_attribute =
0930     __ATTR(ctl_keys, 0644, message_show, message_store);
0931 static struct kobj_attribute colors_attribute =
0932     __ATTR(colors, 0644, message_show, message_store);
0933 static struct kobj_attribute formatted_attribute =
0934     __ATTR(formatted, 0644, message_show, message_store);
0935 static struct kobj_attribute function_names_attribute =
0936     __ATTR(function_names, 0644, message_show, message_store);
0937 static struct kobj_attribute key_names_attribute =
0938     __ATTR(key_names, 0644, message_show, message_store);
0939 static struct kobj_attribute states_attribute =
0940     __ATTR(states, 0644, message_show, message_store);
0941 
0942 /*
0943  * Create groups of attributes so that we can create and destroy them all
0944  * at once.
0945  */
0946 static struct attribute *main_attrs[] = {
0947     &keymap_attribute.attr,
0948     &silent_attribute.attr,
0949     &synth_attribute.attr,
0950     &synth_direct_attribute.attr,
0951     &version_attribute.attr,
0952     &delimiters_attribute.attr,
0953     &ex_num_attribute.attr,
0954     &punc_all_attribute.attr,
0955     &punc_most_attribute.attr,
0956     &punc_some_attribute.attr,
0957     &repeats_attribute.attr,
0958     &attrib_bleep_attribute.attr,
0959     &bell_pos_attribute.attr,
0960     &bleep_time_attribute.attr,
0961     &bleeps_attribute.attr,
0962     &cursor_time_attribute.attr,
0963     &key_echo_attribute.attr,
0964     &no_interrupt_attribute.attr,
0965     &punc_level_attribute.attr,
0966     &reading_punc_attribute.attr,
0967     &say_control_attribute.attr,
0968     &say_word_ctl_attribute.attr,
0969     &spell_delay_attribute.attr,
0970     NULL,
0971 };
0972 
0973 static struct attribute *i18n_attrs[] = {
0974     &announcements_attribute.attr,
0975     &characters_attribute.attr,
0976     &chartab_attribute.attr,
0977     &ctl_keys_attribute.attr,
0978     &colors_attribute.attr,
0979     &formatted_attribute.attr,
0980     &function_names_attribute.attr,
0981     &key_names_attribute.attr,
0982     &states_attribute.attr,
0983     NULL,
0984 };
0985 
0986 /*
0987  * An unnamed attribute group will put all of the attributes directly in
0988  * the kobject directory.  If we specify a name, a subdirectory will be
0989  * created for the attributes with the directory being the name of the
0990  * attribute group.
0991  */
0992 static const struct attribute_group main_attr_group = {
0993     .attrs = main_attrs,
0994 };
0995 
0996 static const struct attribute_group i18n_attr_group = {
0997     .attrs = i18n_attrs,
0998     .name = "i18n",
0999 };
1000 
1001 static struct kobject *accessibility_kobj;
1002 struct kobject *speakup_kobj;
1003 
1004 int 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 to
1013      * userspace.  That is why this function should not be used for
1014      * any type of dynamic kobjects, where the name and number are
1015      * 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 
1040 err_group:
1041     sysfs_remove_group(speakup_kobj, &main_attr_group);
1042 err_speakup:
1043     kobject_put(speakup_kobj);
1044 err_acc:
1045     kobject_put(accessibility_kobj);
1046 out:
1047     return retval;
1048 }
1049 
1050 void 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 }