162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Speakup kobject implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 William Hubbs 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This code is based on kobject-example.c, which came with linux 2.6.x. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com> 1062306a36Sopenharmony_ci * Copyright (C) 2007 Novell Inc. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Released under the GPL version 2 only. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci#include <linux/slab.h> /* For kmalloc. */ 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/kobject.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci#include <linux/string_helpers.h> 2062306a36Sopenharmony_ci#include <linux/sysfs.h> 2162306a36Sopenharmony_ci#include <linux/ctype.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "speakup.h" 2462306a36Sopenharmony_ci#include "spk_priv.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * This is called when a user reads the characters or chartab sys file. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic ssize_t chars_chartab_show(struct kobject *kobj, 3062306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int i; 3362306a36Sopenharmony_ci int len = 0; 3462306a36Sopenharmony_ci char *cp; 3562306a36Sopenharmony_ci char *buf_pointer = buf; 3662306a36Sopenharmony_ci size_t bufsize = PAGE_SIZE; 3762306a36Sopenharmony_ci unsigned long flags; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 4062306a36Sopenharmony_ci *buf_pointer = '\0'; 4162306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 4262306a36Sopenharmony_ci if (bufsize <= 1) 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci if (strcmp("characters", attr->attr.name) == 0) { 4562306a36Sopenharmony_ci len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", 4662306a36Sopenharmony_ci i, spk_characters[i]); 4762306a36Sopenharmony_ci } else { /* show chartab entry */ 4862306a36Sopenharmony_ci if (IS_TYPE(i, B_CTL)) 4962306a36Sopenharmony_ci cp = "B_CTL"; 5062306a36Sopenharmony_ci else if (IS_TYPE(i, WDLM)) 5162306a36Sopenharmony_ci cp = "WDLM"; 5262306a36Sopenharmony_ci else if (IS_TYPE(i, A_PUNC)) 5362306a36Sopenharmony_ci cp = "A_PUNC"; 5462306a36Sopenharmony_ci else if (IS_TYPE(i, PUNC)) 5562306a36Sopenharmony_ci cp = "PUNC"; 5662306a36Sopenharmony_ci else if (IS_TYPE(i, NUM)) 5762306a36Sopenharmony_ci cp = "NUM"; 5862306a36Sopenharmony_ci else if (IS_TYPE(i, A_CAP)) 5962306a36Sopenharmony_ci cp = "A_CAP"; 6062306a36Sopenharmony_ci else if (IS_TYPE(i, ALPHA)) 6162306a36Sopenharmony_ci cp = "ALPHA"; 6262306a36Sopenharmony_ci else if (IS_TYPE(i, B_CAPSYM)) 6362306a36Sopenharmony_ci cp = "B_CAPSYM"; 6462306a36Sopenharmony_ci else if (IS_TYPE(i, B_SYM)) 6562306a36Sopenharmony_ci cp = "B_SYM"; 6662306a36Sopenharmony_ci else 6762306a36Sopenharmony_ci cp = "0"; 6862306a36Sopenharmony_ci len = 6962306a36Sopenharmony_ci scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci bufsize -= len; 7262306a36Sopenharmony_ci buf_pointer += len; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 7562306a36Sopenharmony_ci return buf_pointer - buf; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Print informational messages or warnings after updating 8062306a36Sopenharmony_ci * character descriptions or chartab entries. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic void report_char_chartab_status(int reset, int received, int used, 8362306a36Sopenharmony_ci int rejected, int do_characters) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci static char const *object_type[] = { 8662306a36Sopenharmony_ci "character class entries", 8762306a36Sopenharmony_ci "character descriptions", 8862306a36Sopenharmony_ci }; 8962306a36Sopenharmony_ci int len; 9062306a36Sopenharmony_ci char buf[80]; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (reset) { 9362306a36Sopenharmony_ci pr_info("%s reset to defaults\n", object_type[do_characters]); 9462306a36Sopenharmony_ci } else if (received) { 9562306a36Sopenharmony_ci len = snprintf(buf, sizeof(buf), 9662306a36Sopenharmony_ci " updated %d of %d %s\n", 9762306a36Sopenharmony_ci used, received, object_type[do_characters]); 9862306a36Sopenharmony_ci if (rejected) 9962306a36Sopenharmony_ci snprintf(buf + (len - 1), sizeof(buf) - (len - 1), 10062306a36Sopenharmony_ci " with %d reject%s\n", 10162306a36Sopenharmony_ci rejected, rejected > 1 ? "s" : ""); 10262306a36Sopenharmony_ci pr_info("%s", buf); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * This is called when a user changes the characters or chartab parameters. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic ssize_t chars_chartab_store(struct kobject *kobj, 11062306a36Sopenharmony_ci struct kobj_attribute *attr, 11162306a36Sopenharmony_ci const char *buf, size_t count) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci char *cp = (char *)buf; 11462306a36Sopenharmony_ci char *end = cp + count; /* the null at the end of the buffer */ 11562306a36Sopenharmony_ci char *linefeed = NULL; 11662306a36Sopenharmony_ci char keyword[MAX_DESC_LEN + 1]; 11762306a36Sopenharmony_ci char *outptr = NULL; /* Will hold keyword or desc. */ 11862306a36Sopenharmony_ci char *temp = NULL; 11962306a36Sopenharmony_ci char *desc = NULL; 12062306a36Sopenharmony_ci ssize_t retval = count; 12162306a36Sopenharmony_ci unsigned long flags; 12262306a36Sopenharmony_ci unsigned long index = 0; 12362306a36Sopenharmony_ci int charclass = 0; 12462306a36Sopenharmony_ci int received = 0; 12562306a36Sopenharmony_ci int used = 0; 12662306a36Sopenharmony_ci int rejected = 0; 12762306a36Sopenharmony_ci int reset = 0; 12862306a36Sopenharmony_ci int do_characters = !strcmp(attr->attr.name, "characters"); 12962306a36Sopenharmony_ci size_t desc_length = 0; 13062306a36Sopenharmony_ci int i; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 13362306a36Sopenharmony_ci while (cp < end) { 13462306a36Sopenharmony_ci while ((cp < end) && (*cp == ' ' || *cp == '\t')) 13562306a36Sopenharmony_ci cp++; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (cp == end) 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci if ((*cp == '\n') || strchr("dDrR", *cp)) { 14062306a36Sopenharmony_ci reset = 1; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci received++; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci linefeed = strchr(cp, '\n'); 14662306a36Sopenharmony_ci if (!linefeed) { 14762306a36Sopenharmony_ci rejected++; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (!isdigit(*cp)) { 15262306a36Sopenharmony_ci rejected++; 15362306a36Sopenharmony_ci cp = linefeed + 1; 15462306a36Sopenharmony_ci continue; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * Do not replace with kstrtoul: 15962306a36Sopenharmony_ci * here we need temp to be updated 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci index = simple_strtoul(cp, &temp, 10); 16262306a36Sopenharmony_ci if (index > 255) { 16362306a36Sopenharmony_ci rejected++; 16462306a36Sopenharmony_ci cp = linefeed + 1; 16562306a36Sopenharmony_ci continue; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) 16962306a36Sopenharmony_ci temp++; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci desc_length = linefeed - temp; 17262306a36Sopenharmony_ci if (desc_length > MAX_DESC_LEN) { 17362306a36Sopenharmony_ci rejected++; 17462306a36Sopenharmony_ci cp = linefeed + 1; 17562306a36Sopenharmony_ci continue; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci if (do_characters) { 17862306a36Sopenharmony_ci desc = kmalloc(desc_length + 1, GFP_ATOMIC); 17962306a36Sopenharmony_ci if (!desc) { 18062306a36Sopenharmony_ci retval = -ENOMEM; 18162306a36Sopenharmony_ci reset = 1; /* just reset on error. */ 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci outptr = desc; 18562306a36Sopenharmony_ci } else { 18662306a36Sopenharmony_ci outptr = keyword; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci for (i = 0; i < desc_length; i++) 19062306a36Sopenharmony_ci outptr[i] = temp[i]; 19162306a36Sopenharmony_ci outptr[desc_length] = '\0'; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (do_characters) { 19462306a36Sopenharmony_ci if (spk_characters[index] != spk_default_chars[index]) 19562306a36Sopenharmony_ci kfree(spk_characters[index]); 19662306a36Sopenharmony_ci spk_characters[index] = desc; 19762306a36Sopenharmony_ci used++; 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci charclass = spk_chartab_get_value(keyword); 20062306a36Sopenharmony_ci if (charclass == 0) { 20162306a36Sopenharmony_ci rejected++; 20262306a36Sopenharmony_ci cp = linefeed + 1; 20362306a36Sopenharmony_ci continue; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci if (charclass != spk_chartab[index]) { 20662306a36Sopenharmony_ci spk_chartab[index] = charclass; 20762306a36Sopenharmony_ci used++; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci cp = linefeed + 1; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (reset) { 21462306a36Sopenharmony_ci if (do_characters) 21562306a36Sopenharmony_ci spk_reset_default_chars(); 21662306a36Sopenharmony_ci else 21762306a36Sopenharmony_ci spk_reset_default_chartab(); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 22162306a36Sopenharmony_ci report_char_chartab_status(reset, received, used, rejected, 22262306a36Sopenharmony_ci do_characters); 22362306a36Sopenharmony_ci return retval; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * This is called when a user reads the keymap parameter. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr, 23062306a36Sopenharmony_ci char *buf) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci char *cp = buf; 23362306a36Sopenharmony_ci int i; 23462306a36Sopenharmony_ci int n; 23562306a36Sopenharmony_ci int num_keys; 23662306a36Sopenharmony_ci int nstates; 23762306a36Sopenharmony_ci u_char *cp1; 23862306a36Sopenharmony_ci u_char ch; 23962306a36Sopenharmony_ci unsigned long flags; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 24262306a36Sopenharmony_ci cp1 = spk_key_buf + SHIFT_TBL_SIZE; 24362306a36Sopenharmony_ci num_keys = (int)(*cp1); 24462306a36Sopenharmony_ci nstates = (int)cp1[1]; 24562306a36Sopenharmony_ci cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates); 24662306a36Sopenharmony_ci cp1 += 2; /* now pointing at shift states */ 24762306a36Sopenharmony_ci /* dump num_keys+1 as first row is shift states + flags, 24862306a36Sopenharmony_ci * each subsequent row is key + states 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci for (n = 0; n <= num_keys; n++) { 25162306a36Sopenharmony_ci for (i = 0; i <= nstates; i++) { 25262306a36Sopenharmony_ci ch = *cp1++; 25362306a36Sopenharmony_ci cp += sprintf(cp, "%d,", (int)ch); 25462306a36Sopenharmony_ci *cp++ = (i < nstates) ? SPACE : '\n'; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci cp += sprintf(cp, "0, %d\n", KEY_MAP_VER); 25862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 25962306a36Sopenharmony_ci return (int)(cp - buf); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * This is called when a user changes the keymap parameter. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_cistatic ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr, 26662306a36Sopenharmony_ci const char *buf, size_t count) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int i; 26962306a36Sopenharmony_ci ssize_t ret = count; 27062306a36Sopenharmony_ci char *in_buff = NULL; 27162306a36Sopenharmony_ci char *cp; 27262306a36Sopenharmony_ci u_char *cp1; 27362306a36Sopenharmony_ci unsigned long flags; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 27662306a36Sopenharmony_ci in_buff = kmemdup(buf, count + 1, GFP_ATOMIC); 27762306a36Sopenharmony_ci if (!in_buff) { 27862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 27962306a36Sopenharmony_ci return -ENOMEM; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci if (strchr("dDrR", *in_buff)) { 28262306a36Sopenharmony_ci spk_set_key_info(spk_key_defaults, spk_key_buf); 28362306a36Sopenharmony_ci pr_info("keymap set to default values\n"); 28462306a36Sopenharmony_ci kfree(in_buff); 28562306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 28662306a36Sopenharmony_ci return count; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci if (in_buff[count - 1] == '\n') 28962306a36Sopenharmony_ci in_buff[count - 1] = '\0'; 29062306a36Sopenharmony_ci cp = in_buff; 29162306a36Sopenharmony_ci cp1 = (u_char *)in_buff; 29262306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 29362306a36Sopenharmony_ci cp = spk_s2uchar(cp, cp1); 29462306a36Sopenharmony_ci cp1++; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci i = (int)cp1[-2] + 1; 29762306a36Sopenharmony_ci i *= (int)cp1[-1] + 1; 29862306a36Sopenharmony_ci i += 2; /* 0 and last map ver */ 29962306a36Sopenharmony_ci if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || 30062306a36Sopenharmony_ci i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { 30162306a36Sopenharmony_ci pr_warn("i %d %d %d %d\n", i, 30262306a36Sopenharmony_ci (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); 30362306a36Sopenharmony_ci kfree(in_buff); 30462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci while (--i >= 0) { 30862306a36Sopenharmony_ci cp = spk_s2uchar(cp, cp1); 30962306a36Sopenharmony_ci cp1++; 31062306a36Sopenharmony_ci if (!(*cp)) 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) { 31462306a36Sopenharmony_ci ret = -EINVAL; 31562306a36Sopenharmony_ci pr_warn("end %d %d %d %d\n", i, 31662306a36Sopenharmony_ci (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci if (spk_set_key_info(in_buff, spk_key_buf)) { 31962306a36Sopenharmony_ci spk_set_key_info(spk_key_defaults, spk_key_buf); 32062306a36Sopenharmony_ci ret = -EINVAL; 32162306a36Sopenharmony_ci pr_warn("set key failed\n"); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci kfree(in_buff); 32562306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* 33062306a36Sopenharmony_ci * This is called when a user changes the value of the silent parameter. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_cistatic ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr, 33362306a36Sopenharmony_ci const char *buf, size_t count) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int len; 33662306a36Sopenharmony_ci struct vc_data *vc = vc_cons[fg_console].d; 33762306a36Sopenharmony_ci char ch = 0; 33862306a36Sopenharmony_ci char shut; 33962306a36Sopenharmony_ci unsigned long flags; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci len = strlen(buf); 34262306a36Sopenharmony_ci if (len > 0 && len < 3) { 34362306a36Sopenharmony_ci ch = buf[0]; 34462306a36Sopenharmony_ci if (ch == '\n') 34562306a36Sopenharmony_ci ch = '0'; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci if (ch < '0' || ch > '7') { 34862306a36Sopenharmony_ci pr_warn("silent value '%c' not in range (0,7)\n", ch); 34962306a36Sopenharmony_ci return -EINVAL; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 35262306a36Sopenharmony_ci if (ch & 2) { 35362306a36Sopenharmony_ci shut = 1; 35462306a36Sopenharmony_ci spk_do_flush(); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci shut = 0; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (ch & 4) 35962306a36Sopenharmony_ci shut |= 0x40; 36062306a36Sopenharmony_ci if (ch & 1) 36162306a36Sopenharmony_ci spk_shut_up |= shut; 36262306a36Sopenharmony_ci else 36362306a36Sopenharmony_ci spk_shut_up &= ~shut; 36462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 36562306a36Sopenharmony_ci return count; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * This is called when a user reads the synth setting. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_cistatic ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr, 37262306a36Sopenharmony_ci char *buf) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci int rv; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!synth) 37762306a36Sopenharmony_ci rv = sprintf(buf, "%s\n", "none"); 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci rv = sprintf(buf, "%s\n", synth->name); 38062306a36Sopenharmony_ci return rv; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * This is called when a user requests to change synthesizers. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_cistatic ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, 38762306a36Sopenharmony_ci const char *buf, size_t count) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci int len; 39062306a36Sopenharmony_ci char new_synth_name[10]; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci len = strlen(buf); 39362306a36Sopenharmony_ci if (len < 2 || len > 9) 39462306a36Sopenharmony_ci return -EINVAL; 39562306a36Sopenharmony_ci memcpy(new_synth_name, buf, len); 39662306a36Sopenharmony_ci if (new_synth_name[len - 1] == '\n') 39762306a36Sopenharmony_ci len--; 39862306a36Sopenharmony_ci new_synth_name[len] = '\0'; 39962306a36Sopenharmony_ci spk_strlwr(new_synth_name); 40062306a36Sopenharmony_ci if (synth && !strcmp(new_synth_name, synth->name)) { 40162306a36Sopenharmony_ci pr_warn("%s already in use\n", new_synth_name); 40262306a36Sopenharmony_ci } else if (synth_init(new_synth_name) != 0) { 40362306a36Sopenharmony_ci pr_warn("failed to init synth %s\n", new_synth_name); 40462306a36Sopenharmony_ci return -ENODEV; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci return count; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* 41062306a36Sopenharmony_ci * This is called when text is sent to the synth via the synth_direct file. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_cistatic ssize_t synth_direct_store(struct kobject *kobj, 41362306a36Sopenharmony_ci struct kobj_attribute *attr, 41462306a36Sopenharmony_ci const char *buf, size_t count) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci u_char tmp[256]; 41762306a36Sopenharmony_ci int len; 41862306a36Sopenharmony_ci int bytes; 41962306a36Sopenharmony_ci const char *ptr = buf; 42062306a36Sopenharmony_ci unsigned long flags; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!synth) 42362306a36Sopenharmony_ci return -EPERM; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci len = strlen(buf); 42662306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 42762306a36Sopenharmony_ci while (len > 0) { 42862306a36Sopenharmony_ci bytes = min_t(size_t, len, 250); 42962306a36Sopenharmony_ci strncpy(tmp, ptr, bytes); 43062306a36Sopenharmony_ci tmp[bytes] = '\0'; 43162306a36Sopenharmony_ci string_unescape_any_inplace(tmp); 43262306a36Sopenharmony_ci synth_printf("%s", tmp); 43362306a36Sopenharmony_ci ptr += bytes; 43462306a36Sopenharmony_ci len -= bytes; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 43762306a36Sopenharmony_ci return count; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* 44162306a36Sopenharmony_ci * This function is called when a user reads the version. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, 44462306a36Sopenharmony_ci char *buf) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci char *cp; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci cp = buf; 44962306a36Sopenharmony_ci cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION); 45062306a36Sopenharmony_ci if (synth) 45162306a36Sopenharmony_ci cp += sprintf(cp, "%s synthesizer driver version %s\n", 45262306a36Sopenharmony_ci synth->name, synth->version); 45362306a36Sopenharmony_ci return cp - buf; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* 45762306a36Sopenharmony_ci * This is called when a user reads the punctuation settings. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, 46062306a36Sopenharmony_ci char *buf) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int i; 46362306a36Sopenharmony_ci char *cp = buf; 46462306a36Sopenharmony_ci struct st_var_header *p_header; 46562306a36Sopenharmony_ci struct punc_var_t *var; 46662306a36Sopenharmony_ci struct st_bits_data *pb; 46762306a36Sopenharmony_ci short mask; 46862306a36Sopenharmony_ci unsigned long flags; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci p_header = spk_var_header_by_name(attr->attr.name); 47162306a36Sopenharmony_ci if (!p_header) { 47262306a36Sopenharmony_ci pr_warn("p_header is null, attr->attr.name is %s\n", 47362306a36Sopenharmony_ci attr->attr.name); 47462306a36Sopenharmony_ci return -EINVAL; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci var = spk_get_punc_var(p_header->var_id); 47862306a36Sopenharmony_ci if (!var) { 47962306a36Sopenharmony_ci pr_warn("var is null, p_header->var_id is %i\n", 48062306a36Sopenharmony_ci p_header->var_id); 48162306a36Sopenharmony_ci return -EINVAL; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 48562306a36Sopenharmony_ci pb = (struct st_bits_data *)&spk_punc_info[var->value]; 48662306a36Sopenharmony_ci mask = pb->mask; 48762306a36Sopenharmony_ci for (i = 33; i < 128; i++) { 48862306a36Sopenharmony_ci if (!(spk_chartab[i] & mask)) 48962306a36Sopenharmony_ci continue; 49062306a36Sopenharmony_ci *cp++ = (char)i; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 49362306a36Sopenharmony_ci return cp - buf; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/* 49762306a36Sopenharmony_ci * This is called when a user changes the punctuation settings. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr, 50062306a36Sopenharmony_ci const char *buf, size_t count) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci int x; 50362306a36Sopenharmony_ci struct st_var_header *p_header; 50462306a36Sopenharmony_ci struct punc_var_t *var; 50562306a36Sopenharmony_ci char punc_buf[100]; 50662306a36Sopenharmony_ci unsigned long flags; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci x = strlen(buf); 50962306a36Sopenharmony_ci if (x < 1 || x > 99) 51062306a36Sopenharmony_ci return -EINVAL; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci p_header = spk_var_header_by_name(attr->attr.name); 51362306a36Sopenharmony_ci if (!p_header) { 51462306a36Sopenharmony_ci pr_warn("p_header is null, attr->attr.name is %s\n", 51562306a36Sopenharmony_ci attr->attr.name); 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci var = spk_get_punc_var(p_header->var_id); 52062306a36Sopenharmony_ci if (!var) { 52162306a36Sopenharmony_ci pr_warn("var is null, p_header->var_id is %i\n", 52262306a36Sopenharmony_ci p_header->var_id); 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci memcpy(punc_buf, buf, x); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci while (x && punc_buf[x - 1] == '\n') 52962306a36Sopenharmony_ci x--; 53062306a36Sopenharmony_ci punc_buf[x] = '\0'; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (*punc_buf == 'd' || *punc_buf == 'r') 53562306a36Sopenharmony_ci x = spk_set_mask_bits(NULL, var->value, 3); 53662306a36Sopenharmony_ci else 53762306a36Sopenharmony_ci x = spk_set_mask_bits(punc_buf, var->value, 3); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 54062306a36Sopenharmony_ci return count; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/* 54462306a36Sopenharmony_ci * This function is called when a user reads one of the variable parameters. 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_cissize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr, 54762306a36Sopenharmony_ci char *buf) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int rv = 0; 55062306a36Sopenharmony_ci struct st_var_header *param; 55162306a36Sopenharmony_ci struct var_t *var; 55262306a36Sopenharmony_ci char *cp1; 55362306a36Sopenharmony_ci char *cp; 55462306a36Sopenharmony_ci char ch; 55562306a36Sopenharmony_ci unsigned long flags; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci param = spk_var_header_by_name(attr->attr.name); 55862306a36Sopenharmony_ci if (!param) 55962306a36Sopenharmony_ci return -EINVAL; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 56262306a36Sopenharmony_ci var = (struct var_t *)param->data; 56362306a36Sopenharmony_ci switch (param->var_type) { 56462306a36Sopenharmony_ci case VAR_NUM: 56562306a36Sopenharmony_ci case VAR_TIME: 56662306a36Sopenharmony_ci if (var) 56762306a36Sopenharmony_ci rv = sprintf(buf, "%i\n", var->u.n.value); 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci rv = sprintf(buf, "0\n"); 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci case VAR_STRING: 57262306a36Sopenharmony_ci if (var) { 57362306a36Sopenharmony_ci cp1 = buf; 57462306a36Sopenharmony_ci *cp1++ = '"'; 57562306a36Sopenharmony_ci for (cp = (char *)param->p_val; (ch = *cp); cp++) { 57662306a36Sopenharmony_ci if (ch >= ' ' && ch < '~') 57762306a36Sopenharmony_ci *cp1++ = ch; 57862306a36Sopenharmony_ci else 57962306a36Sopenharmony_ci cp1 += sprintf(cp1, "\\x%02x", ch); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci *cp1++ = '"'; 58262306a36Sopenharmony_ci *cp1++ = '\n'; 58362306a36Sopenharmony_ci *cp1 = '\0'; 58462306a36Sopenharmony_ci rv = cp1 - buf; 58562306a36Sopenharmony_ci } else { 58662306a36Sopenharmony_ci rv = sprintf(buf, "\"\"\n"); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci default: 59062306a36Sopenharmony_ci rv = sprintf(buf, "Bad parameter %s, type %i\n", 59162306a36Sopenharmony_ci param->name, param->var_type); 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 59562306a36Sopenharmony_ci return rv; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_var_show); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * Used to reset either default_pitch or default_vol. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_cistatic inline void spk_reset_default_value(char *header_name, 60362306a36Sopenharmony_ci int *synth_default_value, int idx) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct st_var_header *param; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (synth && synth_default_value) { 60862306a36Sopenharmony_ci param = spk_var_header_by_name(header_name); 60962306a36Sopenharmony_ci if (param) { 61062306a36Sopenharmony_ci spk_set_num_var(synth_default_value[idx], 61162306a36Sopenharmony_ci param, E_NEW_DEFAULT); 61262306a36Sopenharmony_ci spk_set_num_var(0, param, E_DEFAULT); 61362306a36Sopenharmony_ci pr_info("%s reset to default value\n", param->name); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci * This function is called when a user echos a value to one of the 62062306a36Sopenharmony_ci * variable parameters. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_cissize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr, 62362306a36Sopenharmony_ci const char *buf, size_t count) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct st_var_header *param; 62662306a36Sopenharmony_ci int ret; 62762306a36Sopenharmony_ci int len; 62862306a36Sopenharmony_ci char *cp; 62962306a36Sopenharmony_ci struct var_t *var_data; 63062306a36Sopenharmony_ci long value; 63162306a36Sopenharmony_ci unsigned long flags; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci param = spk_var_header_by_name(attr->attr.name); 63462306a36Sopenharmony_ci if (!param) 63562306a36Sopenharmony_ci return -EINVAL; 63662306a36Sopenharmony_ci if (!param->data) 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci ret = 0; 63962306a36Sopenharmony_ci cp = (char *)buf; 64062306a36Sopenharmony_ci string_unescape_any_inplace(cp); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 64362306a36Sopenharmony_ci switch (param->var_type) { 64462306a36Sopenharmony_ci case VAR_NUM: 64562306a36Sopenharmony_ci case VAR_TIME: 64662306a36Sopenharmony_ci if (*cp == 'd' || *cp == 'r' || *cp == '\0') 64762306a36Sopenharmony_ci len = E_DEFAULT; 64862306a36Sopenharmony_ci else if (*cp == '+' || *cp == '-') 64962306a36Sopenharmony_ci len = E_INC; 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci len = E_SET; 65262306a36Sopenharmony_ci if (kstrtol(cp, 10, &value) == 0) 65362306a36Sopenharmony_ci ret = spk_set_num_var(value, param, len); 65462306a36Sopenharmony_ci else 65562306a36Sopenharmony_ci pr_warn("overflow or parsing error has occurred"); 65662306a36Sopenharmony_ci if (ret == -ERANGE) { 65762306a36Sopenharmony_ci var_data = param->data; 65862306a36Sopenharmony_ci pr_warn("value for %s out of range, expect %d to %d\n", 65962306a36Sopenharmony_ci param->name, 66062306a36Sopenharmony_ci var_data->u.n.low, var_data->u.n.high); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* 66462306a36Sopenharmony_ci * If voice was just changed, we might need to reset our default 66562306a36Sopenharmony_ci * pitch and volume. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci if (param->var_id == VOICE && synth && 66862306a36Sopenharmony_ci (ret == 0 || ret == -ERESTART)) { 66962306a36Sopenharmony_ci var_data = param->data; 67062306a36Sopenharmony_ci value = var_data->u.n.value; 67162306a36Sopenharmony_ci spk_reset_default_value("pitch", synth->default_pitch, 67262306a36Sopenharmony_ci value); 67362306a36Sopenharmony_ci spk_reset_default_value("vol", synth->default_vol, 67462306a36Sopenharmony_ci value); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci case VAR_STRING: 67862306a36Sopenharmony_ci len = strlen(cp); 67962306a36Sopenharmony_ci if ((len >= 1) && (cp[len - 1] == '\n')) 68062306a36Sopenharmony_ci --len; 68162306a36Sopenharmony_ci if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) { 68262306a36Sopenharmony_ci ++cp; 68362306a36Sopenharmony_ci len -= 2; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci cp[len] = '\0'; 68662306a36Sopenharmony_ci ret = spk_set_string_var(cp, param, len); 68762306a36Sopenharmony_ci if (ret == -E2BIG) 68862306a36Sopenharmony_ci pr_warn("value too long for %s\n", 68962306a36Sopenharmony_ci param->name); 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci default: 69262306a36Sopenharmony_ci pr_warn("%s unknown type %d\n", 69362306a36Sopenharmony_ci param->name, (int)param->var_type); 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (ret == -ERESTART) 69962306a36Sopenharmony_ci pr_info("%s reset to default value\n", param->name); 70062306a36Sopenharmony_ci return count; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_var_store); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* 70562306a36Sopenharmony_ci * Functions for reading and writing lists of i18n messages. Incomplete. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic ssize_t message_show_helper(char *buf, enum msg_index_t first, 70962306a36Sopenharmony_ci enum msg_index_t last) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci size_t bufsize = PAGE_SIZE; 71262306a36Sopenharmony_ci char *buf_pointer = buf; 71362306a36Sopenharmony_ci int printed; 71462306a36Sopenharmony_ci enum msg_index_t cursor; 71562306a36Sopenharmony_ci int index = 0; 71662306a36Sopenharmony_ci *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */ 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci for (cursor = first; cursor <= last; cursor++, index++) { 71962306a36Sopenharmony_ci if (bufsize <= 1) 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n", 72262306a36Sopenharmony_ci index, spk_msg_get(cursor)); 72362306a36Sopenharmony_ci buf_pointer += printed; 72462306a36Sopenharmony_ci bufsize -= printed; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return buf_pointer - buf; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void report_msg_status(int reset, int received, int used, 73162306a36Sopenharmony_ci int rejected, char *groupname) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci int len; 73462306a36Sopenharmony_ci char buf[160]; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (reset) { 73762306a36Sopenharmony_ci pr_info("i18n messages from group %s reset to defaults\n", 73862306a36Sopenharmony_ci groupname); 73962306a36Sopenharmony_ci } else if (received) { 74062306a36Sopenharmony_ci len = snprintf(buf, sizeof(buf), 74162306a36Sopenharmony_ci " updated %d of %d i18n messages from group %s\n", 74262306a36Sopenharmony_ci used, received, groupname); 74362306a36Sopenharmony_ci if (rejected) 74462306a36Sopenharmony_ci snprintf(buf + (len - 1), sizeof(buf) - (len - 1), 74562306a36Sopenharmony_ci " with %d reject%s\n", 74662306a36Sopenharmony_ci rejected, rejected > 1 ? "s" : ""); 74762306a36Sopenharmony_ci pr_info("%s", buf); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic ssize_t message_store_helper(const char *buf, size_t count, 75262306a36Sopenharmony_ci struct msg_group_t *group) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci char *cp = (char *)buf; 75562306a36Sopenharmony_ci char *end = cp + count; 75662306a36Sopenharmony_ci char *linefeed = NULL; 75762306a36Sopenharmony_ci char *temp = NULL; 75862306a36Sopenharmony_ci ssize_t msg_stored = 0; 75962306a36Sopenharmony_ci ssize_t retval = count; 76062306a36Sopenharmony_ci size_t desc_length = 0; 76162306a36Sopenharmony_ci unsigned long index = 0; 76262306a36Sopenharmony_ci int received = 0; 76362306a36Sopenharmony_ci int used = 0; 76462306a36Sopenharmony_ci int rejected = 0; 76562306a36Sopenharmony_ci int reset = 0; 76662306a36Sopenharmony_ci enum msg_index_t firstmessage = group->start; 76762306a36Sopenharmony_ci enum msg_index_t lastmessage = group->end; 76862306a36Sopenharmony_ci enum msg_index_t curmessage; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci while (cp < end) { 77162306a36Sopenharmony_ci while ((cp < end) && (*cp == ' ' || *cp == '\t')) 77262306a36Sopenharmony_ci cp++; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (cp == end) 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci if (strchr("dDrR", *cp)) { 77762306a36Sopenharmony_ci reset = 1; 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci received++; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci linefeed = strchr(cp, '\n'); 78362306a36Sopenharmony_ci if (!linefeed) { 78462306a36Sopenharmony_ci rejected++; 78562306a36Sopenharmony_ci break; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!isdigit(*cp)) { 78962306a36Sopenharmony_ci rejected++; 79062306a36Sopenharmony_ci cp = linefeed + 1; 79162306a36Sopenharmony_ci continue; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * Do not replace with kstrtoul: 79662306a36Sopenharmony_ci * here we need temp to be updated 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci index = simple_strtoul(cp, &temp, 10); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) 80162306a36Sopenharmony_ci temp++; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci desc_length = linefeed - temp; 80462306a36Sopenharmony_ci curmessage = firstmessage + index; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* 80762306a36Sopenharmony_ci * Note the check (curmessage < firstmessage). It is not 80862306a36Sopenharmony_ci * redundant. Suppose that the user gave us an index 80962306a36Sopenharmony_ci * equal to ULONG_MAX - 1. If firstmessage > 1, then 81062306a36Sopenharmony_ci * firstmessage + index < firstmessage! 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if ((curmessage < firstmessage) || (curmessage > lastmessage)) { 81462306a36Sopenharmony_ci rejected++; 81562306a36Sopenharmony_ci cp = linefeed + 1; 81662306a36Sopenharmony_ci continue; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci msg_stored = spk_msg_set(curmessage, temp, desc_length); 82062306a36Sopenharmony_ci if (msg_stored < 0) { 82162306a36Sopenharmony_ci retval = msg_stored; 82262306a36Sopenharmony_ci if (msg_stored == -ENOMEM) 82362306a36Sopenharmony_ci reset = 1; 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci used++; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci cp = linefeed + 1; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (reset) 83362306a36Sopenharmony_ci spk_reset_msg_group(group); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci report_msg_status(reset, received, used, rejected, group->name); 83662306a36Sopenharmony_ci return retval; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic ssize_t message_show(struct kobject *kobj, 84062306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci ssize_t retval = 0; 84362306a36Sopenharmony_ci struct msg_group_t *group = spk_find_msg_group(attr->attr.name); 84462306a36Sopenharmony_ci unsigned long flags; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (WARN_ON(!group)) 84762306a36Sopenharmony_ci return -EINVAL; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 85062306a36Sopenharmony_ci retval = message_show_helper(buf, group->start, group->end); 85162306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 85262306a36Sopenharmony_ci return retval; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr, 85662306a36Sopenharmony_ci const char *buf, size_t count) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct msg_group_t *group = spk_find_msg_group(attr->attr.name); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (WARN_ON(!group)) 86162306a36Sopenharmony_ci return -EINVAL; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return message_store_helper(buf, count, group); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci/* 86762306a36Sopenharmony_ci * Declare the attributes. 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_cistatic struct kobj_attribute keymap_attribute = 87062306a36Sopenharmony_ci __ATTR_RW(keymap); 87162306a36Sopenharmony_cistatic struct kobj_attribute silent_attribute = 87262306a36Sopenharmony_ci __ATTR_WO(silent); 87362306a36Sopenharmony_cistatic struct kobj_attribute synth_attribute = 87462306a36Sopenharmony_ci __ATTR_RW(synth); 87562306a36Sopenharmony_cistatic struct kobj_attribute synth_direct_attribute = 87662306a36Sopenharmony_ci __ATTR_WO(synth_direct); 87762306a36Sopenharmony_cistatic struct kobj_attribute version_attribute = 87862306a36Sopenharmony_ci __ATTR_RO(version); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic struct kobj_attribute delimiters_attribute = 88162306a36Sopenharmony_ci __ATTR(delimiters, 0644, punc_show, punc_store); 88262306a36Sopenharmony_cistatic struct kobj_attribute ex_num_attribute = 88362306a36Sopenharmony_ci __ATTR(ex_num, 0644, punc_show, punc_store); 88462306a36Sopenharmony_cistatic struct kobj_attribute punc_all_attribute = 88562306a36Sopenharmony_ci __ATTR(punc_all, 0644, punc_show, punc_store); 88662306a36Sopenharmony_cistatic struct kobj_attribute punc_most_attribute = 88762306a36Sopenharmony_ci __ATTR(punc_most, 0644, punc_show, punc_store); 88862306a36Sopenharmony_cistatic struct kobj_attribute punc_some_attribute = 88962306a36Sopenharmony_ci __ATTR(punc_some, 0644, punc_show, punc_store); 89062306a36Sopenharmony_cistatic struct kobj_attribute repeats_attribute = 89162306a36Sopenharmony_ci __ATTR(repeats, 0644, punc_show, punc_store); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic struct kobj_attribute attrib_bleep_attribute = 89462306a36Sopenharmony_ci __ATTR(attrib_bleep, 0644, spk_var_show, spk_var_store); 89562306a36Sopenharmony_cistatic struct kobj_attribute bell_pos_attribute = 89662306a36Sopenharmony_ci __ATTR(bell_pos, 0644, spk_var_show, spk_var_store); 89762306a36Sopenharmony_cistatic struct kobj_attribute bleep_time_attribute = 89862306a36Sopenharmony_ci __ATTR(bleep_time, 0644, spk_var_show, spk_var_store); 89962306a36Sopenharmony_cistatic struct kobj_attribute bleeps_attribute = 90062306a36Sopenharmony_ci __ATTR(bleeps, 0644, spk_var_show, spk_var_store); 90162306a36Sopenharmony_cistatic struct kobj_attribute cursor_time_attribute = 90262306a36Sopenharmony_ci __ATTR(cursor_time, 0644, spk_var_show, spk_var_store); 90362306a36Sopenharmony_cistatic struct kobj_attribute key_echo_attribute = 90462306a36Sopenharmony_ci __ATTR(key_echo, 0644, spk_var_show, spk_var_store); 90562306a36Sopenharmony_cistatic struct kobj_attribute no_interrupt_attribute = 90662306a36Sopenharmony_ci __ATTR(no_interrupt, 0644, spk_var_show, spk_var_store); 90762306a36Sopenharmony_cistatic struct kobj_attribute punc_level_attribute = 90862306a36Sopenharmony_ci __ATTR(punc_level, 0644, spk_var_show, spk_var_store); 90962306a36Sopenharmony_cistatic struct kobj_attribute reading_punc_attribute = 91062306a36Sopenharmony_ci __ATTR(reading_punc, 0644, spk_var_show, spk_var_store); 91162306a36Sopenharmony_cistatic struct kobj_attribute say_control_attribute = 91262306a36Sopenharmony_ci __ATTR(say_control, 0644, spk_var_show, spk_var_store); 91362306a36Sopenharmony_cistatic struct kobj_attribute say_word_ctl_attribute = 91462306a36Sopenharmony_ci __ATTR(say_word_ctl, 0644, spk_var_show, spk_var_store); 91562306a36Sopenharmony_cistatic struct kobj_attribute spell_delay_attribute = 91662306a36Sopenharmony_ci __ATTR(spell_delay, 0644, spk_var_show, spk_var_store); 91762306a36Sopenharmony_cistatic struct kobj_attribute cur_phonetic_attribute = 91862306a36Sopenharmony_ci __ATTR(cur_phonetic, 0644, spk_var_show, spk_var_store); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/* 92162306a36Sopenharmony_ci * These attributes are i18n related. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_cistatic struct kobj_attribute announcements_attribute = 92462306a36Sopenharmony_ci __ATTR(announcements, 0644, message_show, message_store); 92562306a36Sopenharmony_cistatic struct kobj_attribute characters_attribute = 92662306a36Sopenharmony_ci __ATTR(characters, 0644, chars_chartab_show, 92762306a36Sopenharmony_ci chars_chartab_store); 92862306a36Sopenharmony_cistatic struct kobj_attribute chartab_attribute = 92962306a36Sopenharmony_ci __ATTR(chartab, 0644, chars_chartab_show, 93062306a36Sopenharmony_ci chars_chartab_store); 93162306a36Sopenharmony_cistatic struct kobj_attribute ctl_keys_attribute = 93262306a36Sopenharmony_ci __ATTR(ctl_keys, 0644, message_show, message_store); 93362306a36Sopenharmony_cistatic struct kobj_attribute colors_attribute = 93462306a36Sopenharmony_ci __ATTR(colors, 0644, message_show, message_store); 93562306a36Sopenharmony_cistatic struct kobj_attribute formatted_attribute = 93662306a36Sopenharmony_ci __ATTR(formatted, 0644, message_show, message_store); 93762306a36Sopenharmony_cistatic struct kobj_attribute function_names_attribute = 93862306a36Sopenharmony_ci __ATTR(function_names, 0644, message_show, message_store); 93962306a36Sopenharmony_cistatic struct kobj_attribute key_names_attribute = 94062306a36Sopenharmony_ci __ATTR(key_names, 0644, message_show, message_store); 94162306a36Sopenharmony_cistatic struct kobj_attribute states_attribute = 94262306a36Sopenharmony_ci __ATTR(states, 0644, message_show, message_store); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci/* 94562306a36Sopenharmony_ci * Create groups of attributes so that we can create and destroy them all 94662306a36Sopenharmony_ci * at once. 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_cistatic struct attribute *main_attrs[] = { 94962306a36Sopenharmony_ci &keymap_attribute.attr, 95062306a36Sopenharmony_ci &silent_attribute.attr, 95162306a36Sopenharmony_ci &synth_attribute.attr, 95262306a36Sopenharmony_ci &synth_direct_attribute.attr, 95362306a36Sopenharmony_ci &version_attribute.attr, 95462306a36Sopenharmony_ci &delimiters_attribute.attr, 95562306a36Sopenharmony_ci &ex_num_attribute.attr, 95662306a36Sopenharmony_ci &punc_all_attribute.attr, 95762306a36Sopenharmony_ci &punc_most_attribute.attr, 95862306a36Sopenharmony_ci &punc_some_attribute.attr, 95962306a36Sopenharmony_ci &repeats_attribute.attr, 96062306a36Sopenharmony_ci &attrib_bleep_attribute.attr, 96162306a36Sopenharmony_ci &bell_pos_attribute.attr, 96262306a36Sopenharmony_ci &bleep_time_attribute.attr, 96362306a36Sopenharmony_ci &bleeps_attribute.attr, 96462306a36Sopenharmony_ci &cursor_time_attribute.attr, 96562306a36Sopenharmony_ci &key_echo_attribute.attr, 96662306a36Sopenharmony_ci &no_interrupt_attribute.attr, 96762306a36Sopenharmony_ci &punc_level_attribute.attr, 96862306a36Sopenharmony_ci &reading_punc_attribute.attr, 96962306a36Sopenharmony_ci &say_control_attribute.attr, 97062306a36Sopenharmony_ci &say_word_ctl_attribute.attr, 97162306a36Sopenharmony_ci &spell_delay_attribute.attr, 97262306a36Sopenharmony_ci &cur_phonetic_attribute.attr, 97362306a36Sopenharmony_ci NULL, 97462306a36Sopenharmony_ci}; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic struct attribute *i18n_attrs[] = { 97762306a36Sopenharmony_ci &announcements_attribute.attr, 97862306a36Sopenharmony_ci &characters_attribute.attr, 97962306a36Sopenharmony_ci &chartab_attribute.attr, 98062306a36Sopenharmony_ci &ctl_keys_attribute.attr, 98162306a36Sopenharmony_ci &colors_attribute.attr, 98262306a36Sopenharmony_ci &formatted_attribute.attr, 98362306a36Sopenharmony_ci &function_names_attribute.attr, 98462306a36Sopenharmony_ci &key_names_attribute.attr, 98562306a36Sopenharmony_ci &states_attribute.attr, 98662306a36Sopenharmony_ci NULL, 98762306a36Sopenharmony_ci}; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci/* 99062306a36Sopenharmony_ci * An unnamed attribute group will put all of the attributes directly in 99162306a36Sopenharmony_ci * the kobject directory. If we specify a name, a subdirectory will be 99262306a36Sopenharmony_ci * created for the attributes with the directory being the name of the 99362306a36Sopenharmony_ci * attribute group. 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_cistatic const struct attribute_group main_attr_group = { 99662306a36Sopenharmony_ci .attrs = main_attrs, 99762306a36Sopenharmony_ci}; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic const struct attribute_group i18n_attr_group = { 100062306a36Sopenharmony_ci .attrs = i18n_attrs, 100162306a36Sopenharmony_ci .name = "i18n", 100262306a36Sopenharmony_ci}; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic struct kobject *accessibility_kobj; 100562306a36Sopenharmony_cistruct kobject *speakup_kobj; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ciint speakup_kobj_init(void) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci int retval; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* 101262306a36Sopenharmony_ci * Create a simple kobject with the name of "accessibility", 101362306a36Sopenharmony_ci * located under /sys/ 101462306a36Sopenharmony_ci * 101562306a36Sopenharmony_ci * As this is a simple directory, no uevent will be sent to 101662306a36Sopenharmony_ci * userspace. That is why this function should not be used for 101762306a36Sopenharmony_ci * any type of dynamic kobjects, where the name and number are 101862306a36Sopenharmony_ci * not known ahead of time. 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ci accessibility_kobj = kobject_create_and_add("accessibility", NULL); 102162306a36Sopenharmony_ci if (!accessibility_kobj) { 102262306a36Sopenharmony_ci retval = -ENOMEM; 102362306a36Sopenharmony_ci goto out; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj); 102762306a36Sopenharmony_ci if (!speakup_kobj) { 102862306a36Sopenharmony_ci retval = -ENOMEM; 102962306a36Sopenharmony_ci goto err_acc; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Create the files associated with this kobject */ 103362306a36Sopenharmony_ci retval = sysfs_create_group(speakup_kobj, &main_attr_group); 103462306a36Sopenharmony_ci if (retval) 103562306a36Sopenharmony_ci goto err_speakup; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci retval = sysfs_create_group(speakup_kobj, &i18n_attr_group); 103862306a36Sopenharmony_ci if (retval) 103962306a36Sopenharmony_ci goto err_group; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci goto out; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cierr_group: 104462306a36Sopenharmony_ci sysfs_remove_group(speakup_kobj, &main_attr_group); 104562306a36Sopenharmony_cierr_speakup: 104662306a36Sopenharmony_ci kobject_put(speakup_kobj); 104762306a36Sopenharmony_cierr_acc: 104862306a36Sopenharmony_ci kobject_put(accessibility_kobj); 104962306a36Sopenharmony_ciout: 105062306a36Sopenharmony_ci return retval; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_civoid speakup_kobj_exit(void) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci sysfs_remove_group(speakup_kobj, &i18n_attr_group); 105662306a36Sopenharmony_ci sysfs_remove_group(speakup_kobj, &main_attr_group); 105762306a36Sopenharmony_ci kobject_put(speakup_kobj); 105862306a36Sopenharmony_ci kobject_put(accessibility_kobj); 105962306a36Sopenharmony_ci} 1060