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