// SPDX-License-Identifier: GPL-2.0/* * Speakup kobject implementation * * Copyright (C) 2009 William Hubbs * * This code is based on kobject-example.c, which came with linux 2.6.x. * * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2007 Novell Inc. * * Released under the GPL version 2 only. * */#include <linux/slab.h> /* For kmalloc. */#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/string.h>#include <linux/string_helpers.h>#include <linux/sysfs.h>#include <linux/ctype.h> #include "speakup.h"#include "spk_priv.h" /* * This is called when a user reads the characters or chartab sys file. */static ssize_t chars_chartab_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ int i; int len = 0; char *cp; char *buf_pointer = buf; size_t bufsize = PAGE_SIZE; unsigned long flags; spin_lock_irqsave(&speakup_info.spinlock, flags); *buf_pointer = '\0'; for (i = 0; i < 256; i++) { if (bufsize <= 1) break; if (strcmp("characters", attr->attr.name) == 0) { len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, spk_characters[i]); } else { /* show chartab entry */ if (IS_TYPE(i, B_CTL)) cp = "B_CTL"; else if (IS_TYPE(i, WDLM)) cp = "WDLM"; else if (IS_TYPE(i, A_PUNC)) cp = "A_PUNC"; else if (IS_TYPE(i, PUNC)) cp = "PUNC"; else if (IS_TYPE(i, NUM)) cp = "NUM"; else if (IS_TYPE(i, A_CAP)) cp = "A_CAP"; else if (IS_TYPE(i, ALPHA)) cp = "ALPHA"; else if (IS_TYPE(i, B_CAPSYM)) cp = "B_CAPSYM"; else if (IS_TYPE(i, B_SYM)) cp = "B_SYM"; else cp = "0"; len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp); } bufsize -= len; buf_pointer += len; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return buf_pointer - buf;} /* * Print informational messages or warnings after updating * character descriptions or chartab entries. */static void report_char_chartab_status(int reset, int received, int used, int rejected, int do_characters){ static char const *object_type[] = { "character class entries", "character descriptions", }; int len; char buf[80]; if (reset) { pr_info("%s reset to defaults\n", object_type[do_characters]); } else if (received) { len = snprintf(buf, sizeof(buf), " updated %d of %d %s\n", used, received, object_type[do_characters]); if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", rejected, rejected > 1 ? "s" : ""); pr_info("%s", buf); }} /* * This is called when a user changes the characters or chartab parameters. */static ssize_t chars_chartab_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ char *cp = (char *)buf; char *end = cp + count; /* the null at the end of the buffer */ char *linefeed = NULL; char keyword[MAX_DESC_LEN + 1]; char *outptr = NULL; /* Will hold keyword or desc. */ char *temp = NULL; char *desc = NULL; ssize_t retval = count; unsigned long flags; unsigned long index = 0; int charclass = 0; int received = 0; int used = 0; int rejected = 0; int reset = 0; int do_characters = !strcmp(attr->attr.name, "characters"); size_t desc_length = 0; int i; spin_lock_irqsave(&speakup_info.spinlock, flags); while (cp < end) { while ((cp < end) && (*cp == ' ' || *cp == '\t')) cp++; if (cp == end) break; if ((*cp == '\n') || strchr("dDrR", *cp)) { reset = 1; break; } received++; linefeed = strchr(cp, '\n'); if (!linefeed) { rejected++; break; } if (!isdigit(*cp)) { rejected++; cp = linefeed + 1; continue; } /* * Do not replace with kstrtoul: * here we need temp to be updated */ index = simple_strtoul(cp, &temp, 10); if (index > 255) { rejected++; cp = linefeed + 1; continue; } while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) temp++; desc_length = linefeed - temp; if (desc_length > MAX_DESC_LEN) { rejected++; cp = linefeed + 1; continue; } if (do_characters) { desc = kmalloc(desc_length + 1, GFP_ATOMIC); if (!desc) { retval = -ENOMEM; reset = 1; /* just reset on error. */ break; } outptr = desc; } else { outptr = keyword; } for (i = 0; i < desc_length; i++) outptr[i] = temp[i]; outptr[desc_length] = '\0'; if (do_characters) { if (spk_characters[index] != spk_default_chars[index]) kfree(spk_characters[index]); spk_characters[index] = desc; used++; } else { charclass = spk_chartab_get_value(keyword); if (charclass == 0) { rejected++; cp = linefeed + 1; continue; } if (charclass != spk_chartab[index]) { spk_chartab[index] = charclass; used++; } } cp = linefeed + 1; } if (reset) { if (do_characters) spk_reset_default_chars(); else spk_reset_default_chartab(); } spin_unlock_irqrestore(&speakup_info.spinlock, flags); report_char_chartab_status(reset, received, used, rejected, do_characters); return retval;} /* * This is called when a user reads the keymap parameter. */static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ char *cp = buf; int i; int n; int num_keys; int nstates; u_char *cp1; u_char ch; unsigned long flags; spin_lock_irqsave(&speakup_info.spinlock, flags); cp1 = spk_key_buf + SHIFT_TBL_SIZE; num_keys = (int)(*cp1); nstates = (int)cp1[1]; cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates); cp1 += 2; /* now pointing at shift states */ /* dump num_keys+1 as first row is shift states + flags, * each subsequent row is key + states */ for (n = 0; n <= num_keys; n++) { for (i = 0; i <= nstates; i++) { ch = *cp1++; cp += sprintf(cp, "%d,", (int)ch); *cp++ = (i < nstates) ? SPACE : '\n'; } } cp += sprintf(cp, "0, %d\n", KEY_MAP_VER); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return (int)(cp - buf);} /* * This is called when a user changes the keymap parameter. */static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ int i; ssize_t ret = count; char *in_buff = NULL; char *cp; u_char *cp1; unsigned long flags; spin_lock_irqsave(&speakup_info.spinlock, flags); in_buff = kmemdup(buf, count + 1, GFP_ATOMIC); if (!in_buff) { spin_unlock_irqrestore(&speakup_info.spinlock, flags); return -ENOMEM; } if (strchr("dDrR", *in_buff)) { spk_set_key_info(spk_key_defaults, spk_key_buf); pr_info("keymap set to default values\n"); kfree(in_buff); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count; } if (in_buff[count - 1] == '\n') in_buff[count - 1] = '\0'; cp = in_buff; cp1 = (u_char *)in_buff; for (i = 0; i < 3; i++) { cp = spk_s2uchar(cp, cp1); cp1++; } i = (int)cp1[-2] + 1; i *= (int)cp1[-1] + 1; i += 2; /* 0 and last map ver */ if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { pr_warn("i %d %d %d %d\n", i, (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); kfree(in_buff); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return -EINVAL; } while (--i >= 0) { cp = spk_s2uchar(cp, cp1); cp1++; if (!(*cp)) break; } if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) { ret = -EINVAL; pr_warn("end %d %d %d %d\n", i, (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); } else { if (spk_set_key_info(in_buff, spk_key_buf)) { spk_set_key_info(spk_key_defaults, spk_key_buf); ret = -EINVAL; pr_warn("set key failed\n"); } } kfree(in_buff); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return ret;} /* * This is called when a user changes the value of the silent parameter. */static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ int len; struct vc_data *vc = vc_cons[fg_console].d; char ch = 0; char shut; unsigned long flags; len = strlen(buf); if (len > 0 && len < 3) { ch = buf[0]; if (ch == '\n') ch = '0'; } if (ch < '0' || ch > '7') { pr_warn("silent value '%c' not in range (0,7)\n", ch); return -EINVAL; } spin_lock_irqsave(&speakup_info.spinlock, flags); if (ch & 2) { shut = 1; spk_do_flush(); } else { shut = 0; } if (ch & 4) shut |= 0x40; if (ch & 1) spk_shut_up |= shut; else spk_shut_up &= ~shut; spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count;} /* * This is called when a user reads the synth setting. */static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ int rv; if (!synth) rv = sprintf(buf, "%s\n", "none"); else rv = sprintf(buf, "%s\n", synth->name); return rv;} /* * This is called when a user requests to change synthesizers. */static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ int len; char new_synth_name[10]; len = strlen(buf); if (len < 2 || len > 9) return -EINVAL; memcpy(new_synth_name, buf, len); if (new_synth_name[len - 1] == '\n') len--; new_synth_name[len] = '\0'; spk_strlwr(new_synth_name); if (synth && !strcmp(new_synth_name, synth->name)) { pr_warn("%s already in use\n", new_synth_name); } else if (synth_init(new_synth_name) != 0) { pr_warn("failed to init synth %s\n", new_synth_name); return -ENODEV; } return count;} /* * This is called when text is sent to the synth via the synth_direct file. */static ssize_t synth_direct_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ u_char tmp[256]; int len; int bytes; const char *ptr = buf; unsigned long flags; if (!synth) return -EPERM; len = strlen(buf); spin_lock_irqsave(&speakup_info.spinlock, flags); while (len > 0) { bytes = min_t(size_t, len, 250); strncpy(tmp, ptr, bytes); tmp[bytes] = '\0'; string_unescape_any_inplace(tmp); synth_printf("%s", tmp); ptr += bytes; len -= bytes; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count;} /* * This function is called when a user reads the version. */static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ char *cp; cp = buf; cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION); if (synth) cp += sprintf(cp, "%s synthesizer driver version %s\n", synth->name, synth->version); return cp - buf;} /* * This is called when a user reads the punctuation settings. */static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ int i; char *cp = buf; struct st_var_header *p_header; struct punc_var_t *var; struct st_bits_data *pb; short mask; unsigned long flags; p_header = spk_var_header_by_name(attr->attr.name); if (!p_header) { pr_warn("p_header is null, attr->attr.name is %s\n", attr->attr.name); return -EINVAL; } var = spk_get_punc_var(p_header->var_id); if (!var) { pr_warn("var is null, p_header->var_id is %i\n", p_header->var_id); return -EINVAL; } spin_lock_irqsave(&speakup_info.spinlock, flags); pb = (struct st_bits_data *)&spk_punc_info[var->value]; mask = pb->mask; for (i = 33; i < 128; i++) { if (!(spk_chartab[i] & mask)) continue; *cp++ = (char)i; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return cp - buf;} /* * This is called when a user changes the punctuation settings. */static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)Lines 1–500 of 1056. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Speakup kobject implementation * * Copyright (C) 2009 William Hubbs * * This code is based on kobject-example.c, which came with linux 2.6.x. * * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2007 Novell Inc. * * Released under the GPL version 2 only. * */ #include <linux/slab.h> /* For kmalloc. */ #include <linux/kernel.h> #include <linux/kobject.h> #include <linux/string.h> #include <linux/string_helpers.h> #include <linux/sysfs.h> #include <linux/ctype.h> #include "speakup.h" #include "spk_priv.h" /* * This is called when a user reads the characters or chartab sys file. */ static ssize_t chars_chartab_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int i; int len = 0; char *cp; char *buf_pointer = buf; size_t bufsize = PAGE_SIZE; unsigned long flags; spin_lock_irqsave(&speakup_info.spinlock, flags); *buf_pointer = '\0'; for (i = 0; i < 256; i++) { if (bufsize <= 1) break; if (strcmp("characters", attr->attr.name) == 0) { len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, spk_characters[i]); } else { /* show chartab entry */ if (IS_TYPE(i, B_CTL)) cp = "B_CTL"; else if (IS_TYPE(i, WDLM)) cp = "WDLM"; else if (IS_TYPE(i, A_PUNC)) cp = "A_PUNC"; else if (IS_TYPE(i, PUNC)) cp = "PUNC"; else if (IS_TYPE(i, NUM)) cp = "NUM"; else if (IS_TYPE(i, A_CAP)) cp = "A_CAP"; else if (IS_TYPE(i, ALPHA)) cp = "ALPHA"; else if (IS_TYPE(i, B_CAPSYM)) cp = "B_CAPSYM"; else if (IS_TYPE(i, B_SYM)) cp = "B_SYM"; else cp = "0"; len = scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp); } bufsize -= len; buf_pointer += len; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return buf_pointer - buf; } /* * Print informational messages or warnings after updating * character descriptions or chartab entries. */ static void report_char_chartab_status(int reset, int received, int used, int rejected, int do_characters) { static char const *object_type[] = { "character class entries", "character descriptions", }; int len; char buf[80]; if (reset) { pr_info("%s reset to defaults\n", object_type[do_characters]); } else if (received) { len = snprintf(buf, sizeof(buf), " updated %d of %d %s\n", used, received, object_type[do_characters]); if (rejected) snprintf(buf + (len - 1), sizeof(buf) - (len - 1), " with %d reject%s\n", rejected, rejected > 1 ? "s" : ""); pr_info("%s", buf); } } /* * This is called when a user changes the characters or chartab parameters. */ static ssize_t chars_chartab_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { char *cp = (char *)buf; char *end = cp + count; /* the null at the end of the buffer */ char *linefeed = NULL; char keyword[MAX_DESC_LEN + 1]; char *outptr = NULL; /* Will hold keyword or desc. */ char *temp = NULL; char *desc = NULL; ssize_t retval = count; unsigned long flags; unsigned long index = 0; int charclass = 0; int received = 0; int used = 0; int rejected = 0; int reset = 0; int do_characters = !strcmp(attr->attr.name, "characters"); size_t desc_length = 0; int i; spin_lock_irqsave(&speakup_info.spinlock, flags); while (cp < end) { while ((cp < end) && (*cp == ' ' || *cp == '\t')) cp++; if (cp == end) break; if ((*cp == '\n') || strchr("dDrR", *cp)) { reset = 1; break; } received++; linefeed = strchr(cp, '\n'); if (!linefeed) { rejected++; break; } if (!isdigit(*cp)) { rejected++; cp = linefeed + 1; continue; } /* * Do not replace with kstrtoul: * here we need temp to be updated */ index = simple_strtoul(cp, &temp, 10); if (index > 255) { rejected++; cp = linefeed + 1; continue; } while ((temp < linefeed) && (*temp == ' ' || *temp == '\t')) temp++; desc_length = linefeed - temp; if (desc_length > MAX_DESC_LEN) { rejected++; cp = linefeed + 1; continue; } if (do_characters) { desc = kmalloc(desc_length + 1, GFP_ATOMIC); if (!desc) { retval = -ENOMEM; reset = 1; /* just reset on error. */ break; } outptr = desc; } else { outptr = keyword; } for (i = 0; i < desc_length; i++) outptr[i] = temp[i]; outptr[desc_length] = '\0'; if (do_characters) { if (spk_characters[index] != spk_default_chars[index]) kfree(spk_characters[index]); spk_characters[index] = desc; used++; } else { charclass = spk_chartab_get_value(keyword); if (charclass == 0) { rejected++; cp = linefeed + 1; continue; } if (charclass != spk_chartab[index]) { spk_chartab[index] = charclass; used++; } } cp = linefeed + 1; } if (reset) { if (do_characters) spk_reset_default_chars(); else spk_reset_default_chartab(); } spin_unlock_irqrestore(&speakup_info.spinlock, flags); report_char_chartab_status(reset, received, used, rejected, do_characters); return retval; } /* * This is called when a user reads the keymap parameter. */ static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { char *cp = buf; int i; int n; int num_keys; int nstates; u_char *cp1; u_char ch; unsigned long flags; spin_lock_irqsave(&speakup_info.spinlock, flags); cp1 = spk_key_buf + SHIFT_TBL_SIZE; num_keys = (int)(*cp1); nstates = (int)cp1[1]; cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates); cp1 += 2; /* now pointing at shift states */ /* dump num_keys+1 as first row is shift states + flags, * each subsequent row is key + states */ for (n = 0; n <= num_keys; n++) { for (i = 0; i <= nstates; i++) { ch = *cp1++; cp += sprintf(cp, "%d,", (int)ch); *cp++ = (i < nstates) ? SPACE : '\n'; } } cp += sprintf(cp, "0, %d\n", KEY_MAP_VER); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return (int)(cp - buf); } /* * This is called when a user changes the keymap parameter. */ static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int i; ssize_t ret = count; char *in_buff = NULL; char *cp; u_char *cp1; unsigned long flags; spin_lock_irqsave(&speakup_info.spinlock, flags); in_buff = kmemdup(buf, count + 1, GFP_ATOMIC); if (!in_buff) { spin_unlock_irqrestore(&speakup_info.spinlock, flags); return -ENOMEM; } if (strchr("dDrR", *in_buff)) { spk_set_key_info(spk_key_defaults, spk_key_buf); pr_info("keymap set to default values\n"); kfree(in_buff); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count; } if (in_buff[count - 1] == '\n') in_buff[count - 1] = '\0'; cp = in_buff; cp1 = (u_char *)in_buff; for (i = 0; i < 3; i++) { cp = spk_s2uchar(cp, cp1); cp1++; } i = (int)cp1[-2] + 1; i *= (int)cp1[-1] + 1; i += 2; /* 0 and last map ver */ if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 || i + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { pr_warn("i %d %d %d %d\n", i, (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); kfree(in_buff); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return -EINVAL; } while (--i >= 0) { cp = spk_s2uchar(cp, cp1); cp1++; if (!(*cp)) break; } if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) { ret = -EINVAL; pr_warn("end %d %d %d %d\n", i, (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]); } else { if (spk_set_key_info(in_buff, spk_key_buf)) { spk_set_key_info(spk_key_defaults, spk_key_buf); ret = -EINVAL; pr_warn("set key failed\n"); } } kfree(in_buff); spin_unlock_irqrestore(&speakup_info.spinlock, flags); return ret; } /* * This is called when a user changes the value of the silent parameter. */ static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int len; struct vc_data *vc = vc_cons[fg_console].d; char ch = 0; char shut; unsigned long flags; len = strlen(buf); if (len > 0 && len < 3) { ch = buf[0]; if (ch == '\n') ch = '0'; } if (ch < '0' || ch > '7') { pr_warn("silent value '%c' not in range (0,7)\n", ch); return -EINVAL; } spin_lock_irqsave(&speakup_info.spinlock, flags); if (ch & 2) { shut = 1; spk_do_flush(); } else { shut = 0; } if (ch & 4) shut |= 0x40; if (ch & 1) spk_shut_up |= shut; else spk_shut_up &= ~shut; spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count; } /* * This is called when a user reads the synth setting. */ static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int rv; if (!synth) rv = sprintf(buf, "%s\n", "none"); else rv = sprintf(buf, "%s\n", synth->name); return rv; } /* * This is called when a user requests to change synthesizers. */ static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int len; char new_synth_name[10]; len = strlen(buf); if (len < 2 || len > 9) return -EINVAL; memcpy(new_synth_name, buf, len); if (new_synth_name[len - 1] == '\n') len--; new_synth_name[len] = '\0'; spk_strlwr(new_synth_name); if (synth && !strcmp(new_synth_name, synth->name)) { pr_warn("%s already in use\n", new_synth_name); } else if (synth_init(new_synth_name) != 0) { pr_warn("failed to init synth %s\n", new_synth_name); return -ENODEV; } return count; } /* * This is called when text is sent to the synth via the synth_direct file. */ static ssize_t synth_direct_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { u_char tmp[256]; int len; int bytes; const char *ptr = buf; unsigned long flags; if (!synth) return -EPERM; len = strlen(buf); spin_lock_irqsave(&speakup_info.spinlock, flags); while (len > 0) { bytes = min_t(size_t, len, 250); strncpy(tmp, ptr, bytes); tmp[bytes] = '\0'; string_unescape_any_inplace(tmp); synth_printf("%s", tmp); ptr += bytes; len -= bytes; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return count; } /* * This function is called when a user reads the version. */ static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { char *cp; cp = buf; cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION); if (synth) cp += sprintf(cp, "%s synthesizer driver version %s\n", synth->name, synth->version); return cp - buf; } /* * This is called when a user reads the punctuation settings. */ static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int i; char *cp = buf; struct st_var_header *p_header; struct punc_var_t *var; struct st_bits_data *pb; short mask; unsigned long flags; p_header = spk_var_header_by_name(attr->attr.name); if (!p_header) { pr_warn("p_header is null, attr->attr.name is %s\n", attr->attr.name); return -EINVAL; } var = spk_get_punc_var(p_header->var_id); if (!var) { pr_warn("var is null, p_header->var_id is %i\n", p_header->var_id); return -EINVAL; } spin_lock_irqsave(&speakup_info.spinlock, flags); pb = (struct st_bits_data *)&spk_punc_info[var->value]; mask = pb->mask; for (i = 33; i < 128; i++) { if (!(spk_chartab[i] & mask)) continue; *cp++ = (char)i; } spin_unlock_irqrestore(&speakup_info.spinlock, flags); return cp - buf; } /* * This is called when a user changes the punctuation settings. */ static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)