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