linux/drivers/accessibility/speakup/kobjects.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Speakup kobject implementation
   4 *
   5 * Copyright (C) 2009 William Hubbs
   6 *
   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                        else
  67                                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 updating
  80 * 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 updated
 160                 */
 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                else
 217                        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 + states
 249         */
 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        else
 363                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        else
 379                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        u_char tmp[256];
 417        int len;
 418        int bytes;
 419        const char *ptr = buf;
 420        unsigned long flags;
 421
 422        if (!synth)
 423                return -EPERM;
 424
 425        len = strlen(buf);
 426        spin_lock_irqsave(&speakup_info.spinlock, flags);
 427        while (len > 0) {
 428                bytes = min_t(size_t, len, 250);
 429                strncpy(tmp, ptr, bytes);
 430                tmp[bytes] = '\0';
 431                string_unescape_any_inplace(tmp);
 432                synth_printf("%s", tmp);
 433                ptr += bytes;
 434                len -= bytes;
 435        }
 436        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 437        return count;
 438}
 439
 440/*
 441 * This function is called when a user reads the version.
 442 */
 443static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
 444                            char *buf)
 445{
 446        char *cp;
 447
 448        cp = buf;
 449        cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
 450        if (synth)
 451                cp += sprintf(cp, "%s synthesizer driver version %s\n",
 452                synth->name, synth->version);
 453        return cp - buf;
 454}
 455
 456/*
 457 * This is called when a user reads the punctuation settings.
 458 */
 459static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
 460                         char *buf)
 461{
 462        int i;
 463        char *cp = buf;
 464        struct st_var_header *p_header;
 465        struct punc_var_t *var;
 466        struct st_bits_data *pb;
 467        short mask;
 468        unsigned long flags;
 469
 470        p_header = spk_var_header_by_name(attr->attr.name);
 471        if (!p_header) {
 472                pr_warn("p_header is null, attr->attr.name is %s\n",
 473                        attr->attr.name);
 474                return -EINVAL;
 475        }
 476
 477        var = spk_get_punc_var(p_header->var_id);
 478        if (!var) {
 479                pr_warn("var is null, p_header->var_id is %i\n",
 480                        p_header->var_id);
 481                return -EINVAL;
 482        }
 483
 484        spin_lock_irqsave(&speakup_info.spinlock, flags);
 485        pb = (struct st_bits_data *)&spk_punc_info[var->value];
 486        mask = pb->mask;
 487        for (i = 33; i < 128; i++) {
 488                if (!(spk_chartab[i] & mask))
 489                        continue;
 490                *cp++ = (char)i;
 491        }
 492        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 493        return cp - buf;
 494}
 495
 496/*
 497 * This is called when a user changes the punctuation settings.
 498 */
 499static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
 500                          const char *buf, size_t count)
 501{
 502        int x;
 503        struct st_var_header *p_header;
 504        struct punc_var_t *var;
 505        char punc_buf[100];
 506        unsigned long flags;
 507
 508        x = strlen(buf);
 509        if (x < 1 || x > 99)
 510                return -EINVAL;
 511
 512        p_header = spk_var_header_by_name(attr->attr.name);
 513        if (!p_header) {
 514                pr_warn("p_header is null, attr->attr.name is %s\n",
 515                        attr->attr.name);
 516                return -EINVAL;
 517        }
 518
 519        var = spk_get_punc_var(p_header->var_id);
 520        if (!var) {
 521                pr_warn("var is null, p_header->var_id is %i\n",
 522                        p_header->var_id);
 523                return -EINVAL;
 524        }
 525
 526        memcpy(punc_buf, buf, x);
 527
 528        while (x && punc_buf[x - 1] == '\n')
 529                x--;
 530        punc_buf[x] = '\0';
 531
 532        spin_lock_irqsave(&speakup_info.spinlock, flags);
 533
 534        if (*punc_buf == 'd' || *punc_buf == 'r')
 535                x = spk_set_mask_bits(NULL, var->value, 3);
 536        else
 537                x = spk_set_mask_bits(punc_buf, var->value, 3);
 538
 539        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 540        return count;
 541}
 542
 543/*
 544 * This function is called when a user reads one of the variable parameters.
 545 */
 546ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
 547                     char *buf)
 548{
 549        int rv = 0;
 550        struct st_var_header *param;
 551        struct var_t *var;
 552        char *cp1;
 553        char *cp;
 554        char ch;
 555        unsigned long flags;
 556
 557        param = spk_var_header_by_name(attr->attr.name);
 558        if (!param)
 559                return -EINVAL;
 560
 561        spin_lock_irqsave(&speakup_info.spinlock, flags);
 562        var = (struct var_t *)param->data;
 563        switch (param->var_type) {
 564        case VAR_NUM:
 565        case VAR_TIME:
 566                if (var)
 567                        rv = sprintf(buf, "%i\n", var->u.n.value);
 568                else
 569                        rv = sprintf(buf, "0\n");
 570                break;
 571        case VAR_STRING:
 572                if (var) {
 573                        cp1 = buf;
 574                        *cp1++ = '"';
 575                        for (cp = (char *)param->p_val; (ch = *cp); cp++) {
 576                                if (ch >= ' ' && ch < '~')
 577                                        *cp1++ = ch;
 578                                else
 579                                        cp1 += sprintf(cp1, "\\x%02x", ch);
 580                        }
 581                        *cp1++ = '"';
 582                        *cp1++ = '\n';
 583                        *cp1 = '\0';
 584                        rv = cp1 - buf;
 585                } else {
 586                        rv = sprintf(buf, "\"\"\n");
 587                }
 588                break;
 589        default:
 590                rv = sprintf(buf, "Bad parameter  %s, type %i\n",
 591                             param->name, param->var_type);
 592                break;
 593        }
 594        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 595        return rv;
 596}
 597EXPORT_SYMBOL_GPL(spk_var_show);
 598
 599/*
 600 * Used to reset either default_pitch or default_vol.
 601 */
 602static inline void spk_reset_default_value(char *header_name,
 603                                           int *synth_default_value, int idx)
 604{
 605        struct st_var_header *param;
 606
 607        if (synth && synth_default_value) {
 608                param = spk_var_header_by_name(header_name);
 609                if (param)  {
 610                        spk_set_num_var(synth_default_value[idx],
 611                                        param, E_NEW_DEFAULT);
 612                        spk_set_num_var(0, param, E_DEFAULT);
 613                        pr_info("%s reset to default value\n", param->name);
 614                }
 615        }
 616}
 617
 618/*
 619 * This function is called when a user echos a value to one of the
 620 * variable parameters.
 621 */
 622ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
 623                      const char *buf, size_t count)
 624{
 625        struct st_var_header *param;
 626        int ret;
 627        int len;
 628        char *cp;
 629        struct var_t *var_data;
 630        long value;
 631        unsigned long flags;
 632
 633        param = spk_var_header_by_name(attr->attr.name);
 634        if (!param)
 635                return -EINVAL;
 636        if (!param->data)
 637                return 0;
 638        ret = 0;
 639        cp = (char *)buf;
 640        string_unescape_any_inplace(cp);
 641
 642        spin_lock_irqsave(&speakup_info.spinlock, flags);
 643        switch (param->var_type) {
 644        case VAR_NUM:
 645        case VAR_TIME:
 646                if (*cp == 'd' || *cp == 'r' || *cp == '\0')
 647                        len = E_DEFAULT;
 648                else if (*cp == '+' || *cp == '-')
 649                        len = E_INC;
 650                else
 651                        len = E_SET;
 652                if (kstrtol(cp, 10, &value) == 0)
 653                        ret = spk_set_num_var(value, param, len);
 654                else
 655                        pr_warn("overflow or parsing error has occurred");
 656                if (ret == -ERANGE) {
 657                        var_data = param->data;
 658                        pr_warn("value for %s out of range, expect %d to %d\n",
 659                                param->name,
 660                                var_data->u.n.low, var_data->u.n.high);
 661                }
 662
 663               /*
 664                * If voice was just changed, we might need to reset our default
 665                * pitch and volume.
 666                */
 667                if (param->var_id == VOICE && synth &&
 668                    (ret == 0 || ret == -ERESTART)) {
 669                        var_data = param->data;
 670                        value = var_data->u.n.value;
 671                        spk_reset_default_value("pitch", synth->default_pitch,
 672                                                value);
 673                        spk_reset_default_value("vol", synth->default_vol,
 674                                                value);
 675                }
 676                break;
 677        case VAR_STRING:
 678                len = strlen(cp);
 679                if ((len >= 1) && (cp[len - 1] == '\n'))
 680                        --len;
 681                if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
 682                        ++cp;
 683                        len -= 2;
 684                }
 685                cp[len] = '\0';
 686                ret = spk_set_string_var(cp, param, len);
 687                if (ret == -E2BIG)
 688                        pr_warn("value too long for %s\n",
 689                                param->name);
 690                break;
 691        default:
 692                pr_warn("%s unknown type %d\n",
 693                        param->name, (int)param->var_type);
 694        break;
 695        }
 696        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 697
 698        if (ret == -ERESTART)
 699                pr_info("%s reset to default value\n", param->name);
 700        return count;
 701}
 702EXPORT_SYMBOL_GPL(spk_var_store);
 703
 704/*
 705 * Functions for reading and writing lists of i18n messages.  Incomplete.
 706 */
 707
 708static ssize_t message_show_helper(char *buf, enum msg_index_t first,
 709                                   enum msg_index_t last)
 710{
 711        size_t bufsize = PAGE_SIZE;
 712        char *buf_pointer = buf;
 713        int printed;
 714        enum msg_index_t cursor;
 715        int index = 0;
 716        *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
 717
 718        for (cursor = first; cursor <= last; cursor++, index++) {
 719                if (bufsize <= 1)
 720                        break;
 721                printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
 722                                    index, spk_msg_get(cursor));
 723                buf_pointer += printed;
 724                bufsize -= printed;
 725        }
 726
 727        return buf_pointer - buf;
 728}
 729
 730static void report_msg_status(int reset, int received, int used,
 731                              int rejected, char *groupname)
 732{
 733        int len;
 734        char buf[160];
 735
 736        if (reset) {
 737                pr_info("i18n messages from group %s reset to defaults\n",
 738                        groupname);
 739        } else if (received) {
 740                len = snprintf(buf, sizeof(buf),
 741                               " updated %d of %d i18n messages from group %s\n",
 742                                       used, received, groupname);
 743                if (rejected)
 744                        snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
 745                                 " with %d reject%s\n",
 746                                 rejected, rejected > 1 ? "s" : "");
 747                pr_info("%s", buf);
 748        }
 749}
 750
 751static ssize_t message_store_helper(const char *buf, size_t count,
 752                                    struct msg_group_t *group)
 753{
 754        char *cp = (char *)buf;
 755        char *end = cp + count;
 756        char *linefeed = NULL;
 757        char *temp = NULL;
 758        ssize_t msg_stored = 0;
 759        ssize_t retval = count;
 760        size_t desc_length = 0;
 761        unsigned long index = 0;
 762        int received = 0;
 763        int used = 0;
 764        int rejected = 0;
 765        int reset = 0;
 766        enum msg_index_t firstmessage = group->start;
 767        enum msg_index_t lastmessage = group->end;
 768        enum msg_index_t curmessage;
 769
 770        while (cp < end) {
 771                while ((cp < end) && (*cp == ' ' || *cp == '\t'))
 772                        cp++;
 773
 774                if (cp == end)
 775                        break;
 776                if (strchr("dDrR", *cp)) {
 777                        reset = 1;
 778                        break;
 779                }
 780                received++;
 781
 782                linefeed = strchr(cp, '\n');
 783                if (!linefeed) {
 784                        rejected++;
 785                        break;
 786                }
 787
 788                if (!isdigit(*cp)) {
 789                        rejected++;
 790                        cp = linefeed + 1;
 791                        continue;
 792                }
 793
 794                /*
 795                 * Do not replace with kstrtoul:
 796                 * here we need temp to be updated
 797                 */
 798                index = simple_strtoul(cp, &temp, 10);
 799
 800                while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
 801                        temp++;
 802
 803                desc_length = linefeed - temp;
 804                curmessage = firstmessage + index;
 805
 806                /*
 807                 * Note the check (curmessage < firstmessage).  It is not
 808                 * redundant.  Suppose that the user gave us an index
 809                 * equal to ULONG_MAX - 1.  If firstmessage > 1, then
 810                 * firstmessage + index < firstmessage!
 811                 */
 812
 813                if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
 814                        rejected++;
 815                        cp = linefeed + 1;
 816                        continue;
 817                }
 818
 819                msg_stored = spk_msg_set(curmessage, temp, desc_length);
 820                if (msg_stored < 0) {
 821                        retval = msg_stored;
 822                        if (msg_stored == -ENOMEM)
 823                                reset = 1;
 824                        break;
 825                }
 826
 827                used++;
 828
 829                cp = linefeed + 1;
 830        }
 831
 832        if (reset)
 833                spk_reset_msg_group(group);
 834
 835        report_msg_status(reset, received, used, rejected, group->name);
 836        return retval;
 837}
 838
 839static ssize_t message_show(struct kobject *kobj,
 840                            struct kobj_attribute *attr, char *buf)
 841{
 842        ssize_t retval = 0;
 843        struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
 844        unsigned long flags;
 845
 846        if (WARN_ON(!group))
 847                return -EINVAL;
 848
 849        spin_lock_irqsave(&speakup_info.spinlock, flags);
 850        retval = message_show_helper(buf, group->start, group->end);
 851        spin_unlock_irqrestore(&speakup_info.spinlock, flags);
 852        return retval;
 853}
 854
 855static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
 856                             const char *buf, size_t count)
 857{
 858        struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
 859
 860        if (WARN_ON(!group))
 861                return -EINVAL;
 862
 863        return message_store_helper(buf, count, group);
 864}
 865
 866/*
 867 * Declare the attributes.
 868 */
 869static struct kobj_attribute keymap_attribute =
 870        __ATTR_RW(keymap);
 871static struct kobj_attribute silent_attribute =
 872        __ATTR_WO(silent);
 873static struct kobj_attribute synth_attribute =
 874        __ATTR_RW(synth);
 875static struct kobj_attribute synth_direct_attribute =
 876        __ATTR_WO(synth_direct);
 877static struct kobj_attribute version_attribute =
 878        __ATTR_RO(version);
 879
 880static struct kobj_attribute delimiters_attribute =
 881        __ATTR(delimiters, 0644, punc_show, punc_store);
 882static struct kobj_attribute ex_num_attribute =
 883        __ATTR(ex_num, 0644, punc_show, punc_store);
 884static struct kobj_attribute punc_all_attribute =
 885        __ATTR(punc_all, 0644, punc_show, punc_store);
 886static struct kobj_attribute punc_most_attribute =
 887        __ATTR(punc_most, 0644, punc_show, punc_store);
 888static struct kobj_attribute punc_some_attribute =
 889        __ATTR(punc_some, 0644, punc_show, punc_store);
 890static struct kobj_attribute repeats_attribute =
 891        __ATTR(repeats, 0644, punc_show, punc_store);
 892
 893static struct kobj_attribute attrib_bleep_attribute =
 894        __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store);
 895static struct kobj_attribute bell_pos_attribute =
 896        __ATTR(bell_pos, 0644, spk_var_show, spk_var_store);
 897static struct kobj_attribute bleep_time_attribute =
 898        __ATTR(bleep_time, 0644, spk_var_show, spk_var_store);
 899static struct kobj_attribute bleeps_attribute =
 900        __ATTR(bleeps, 0644, spk_var_show, spk_var_store);
 901static struct kobj_attribute cursor_time_attribute =
 902        __ATTR(cursor_time, 0644, spk_var_show, spk_var_store);
 903static struct kobj_attribute key_echo_attribute =
 904        __ATTR(key_echo, 0644, spk_var_show, spk_var_store);
 905static struct kobj_attribute no_interrupt_attribute =
 906        __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store);
 907static struct kobj_attribute punc_level_attribute =
 908        __ATTR(punc_level, 0644, spk_var_show, spk_var_store);
 909static struct kobj_attribute reading_punc_attribute =
 910        __ATTR(reading_punc, 0644, spk_var_show, spk_var_store);
 911static struct kobj_attribute say_control_attribute =
 912        __ATTR(say_control, 0644, spk_var_show, spk_var_store);
 913static struct kobj_attribute say_word_ctl_attribute =
 914        __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store);
 915static struct kobj_attribute spell_delay_attribute =
 916        __ATTR(spell_delay, 0644, spk_var_show, spk_var_store);
 917
 918/*
 919 * These attributes are i18n related.
 920 */
 921static struct kobj_attribute announcements_attribute =
 922        __ATTR(announcements, 0644, message_show, message_store);
 923static struct kobj_attribute characters_attribute =
 924        __ATTR(characters, 0644, chars_chartab_show,
 925               chars_chartab_store);
 926static struct kobj_attribute chartab_attribute =
 927        __ATTR(chartab, 0644, chars_chartab_show,
 928               chars_chartab_store);
 929static struct kobj_attribute ctl_keys_attribute =
 930        __ATTR(ctl_keys, 0644, message_show, message_store);
 931static struct kobj_attribute colors_attribute =
 932        __ATTR(colors, 0644, message_show, message_store);
 933static struct kobj_attribute formatted_attribute =
 934        __ATTR(formatted, 0644, message_show, message_store);
 935static struct kobj_attribute function_names_attribute =
 936        __ATTR(function_names, 0644, message_show, message_store);
 937static struct kobj_attribute key_names_attribute =
 938        __ATTR(key_names, 0644, message_show, message_store);
 939static struct kobj_attribute states_attribute =
 940        __ATTR(states, 0644, message_show, message_store);
 941
 942/*
 943 * Create groups of attributes so that we can create and destroy them all
 944 * at once.
 945 */
 946static struct attribute *main_attrs[] = {
 947        &keymap_attribute.attr,
 948        &silent_attribute.attr,
 949        &synth_attribute.attr,
 950        &synth_direct_attribute.attr,
 951        &version_attribute.attr,
 952        &delimiters_attribute.attr,
 953        &ex_num_attribute.attr,
 954        &punc_all_attribute.attr,
 955        &punc_most_attribute.attr,
 956        &punc_some_attribute.attr,
 957        &repeats_attribute.attr,
 958        &attrib_bleep_attribute.attr,
 959        &bell_pos_attribute.attr,
 960        &bleep_time_attribute.attr,
 961        &bleeps_attribute.attr,
 962        &cursor_time_attribute.attr,
 963        &key_echo_attribute.attr,
 964        &no_interrupt_attribute.attr,
 965        &punc_level_attribute.attr,
 966        &reading_punc_attribute.attr,
 967        &say_control_attribute.attr,
 968        &say_word_ctl_attribute.attr,
 969        &spell_delay_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 in
 988 * the kobject directory.  If we specify a name, a subdirectory will be
 989 * created for the attributes with the directory being the name of the
 990 * 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 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
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