2018-01-11 11:08:40 +01:00
// SPDX-License-Identifier: GPL-2.0
2010-10-07 13:20:02 -05:00
/*
* 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>
2013-04-30 15:27:31 -07:00
# include <linux/string_helpers.h>
2010-10-07 13:20:02 -05:00
# 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 ,
2017-03-07 18:38:20 +02:00
struct kobj_attribute * attr , char * buf )
2010-10-07 13:20:02 -05:00
{
int i ;
int len = 0 ;
char * cp ;
char * buf_pointer = buf ;
size_t bufsize = PAGE_SIZE ;
unsigned long flags ;
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
* 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 " ,
2013-01-02 02:37:40 +01:00
i , spk_characters [ i ] ) ;
2010-10-07 13:20:02 -05:00
} 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 ;
}
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
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 ,
2017-03-07 18:38:20 +02:00
int rejected , int do_characters )
2010-10-07 13:20:02 -05:00
{
2014-10-04 13:36:27 +03:00
static char const * object_type [ ] = {
2010-10-07 13:20:02 -05:00
" character class entries " ,
" character descriptions " ,
} ;
int len ;
char buf [ 80 ] ;
if ( reset ) {
pr_info ( " %s reset to defaults \n " , object_type [ do_characters ] ) ;
2010-10-12 11:39:30 -05:00
} else if ( received ) {
2010-10-07 13:20:02 -05:00
len = snprintf ( buf , sizeof ( buf ) ,
2017-03-07 18:38:20 +02:00
" updated %d of %d %s \n " ,
2017-03-14 02:10:18 +05:30
used , received , object_type [ do_characters ] ) ;
2010-10-07 13:20:02 -05:00
if ( rejected )
snprintf ( buf + ( len - 1 ) , sizeof ( buf ) - ( len - 1 ) ,
" with %d reject%s \n " ,
rejected , rejected > 1 ? " s " : " " ) ;
2019-03-18 13:21:10 +00:00
pr_info ( " %s " , buf ) ;
2010-10-07 13:20:02 -05:00
}
}
/*
* This is called when a user changes the characters or chartab parameters .
*/
static ssize_t chars_chartab_store ( struct kobject * kobj ,
2017-03-07 18:38:40 +02:00
struct kobj_attribute * attr ,
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
2017-03-07 18:37:41 +02:00
char * cp = ( char * ) buf ;
2010-10-07 13:20:02 -05:00
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 ;
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
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 ;
}
2010-10-12 11:39:30 -05:00
if ( ! isdigit ( * cp ) ) {
2010-10-07 13:20:02 -05:00
rejected + + ;
cp = linefeed + 1 ;
continue ;
}
2019-03-17 12:31:10 +00:00
/*
* Do not replace with kstrtoul :
* here we need temp to be updated
*/
2010-10-07 13:20:02 -05:00
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 ) ;
2010-10-12 11:39:30 -05:00
if ( ! desc ) {
2010-10-07 13:20:02 -05:00
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 ) {
2013-01-02 02:37:40 +01:00
if ( spk_characters [ index ] ! = spk_default_chars [ index ] )
kfree ( spk_characters [ index ] ) ;
spk_characters [ index ] = desc ;
2010-10-07 13:20:02 -05:00
used + + ;
} else {
2013-01-02 02:37:40 +01:00
charclass = spk_chartab_get_value ( keyword ) ;
2010-10-07 13:20:02 -05:00
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 )
2013-01-02 02:37:40 +01:00
spk_reset_default_chars ( ) ;
2010-10-07 13:20:02 -05:00
else
2013-01-02 02:37:40 +01:00
spk_reset_default_chartab ( ) ;
2010-10-07 13:20:02 -05:00
}
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-12 11:39:30 -05:00
report_char_chartab_status ( reset , received , used , rejected ,
2017-03-07 18:38:20 +02:00
do_characters ) ;
2010-10-07 13:20:02 -05:00
return retval ;
}
/*
* This is called when a user reads the keymap parameter .
*/
static ssize_t keymap_show ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
char * buf )
2010-10-07 13:20:02 -05:00
{
char * cp = buf ;
int i ;
int n ;
int num_keys ;
int nstates ;
u_char * cp1 ;
u_char ch ;
unsigned long flags ;
2014-09-09 20:04:34 +02:00
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2013-01-02 02:37:40 +01:00
cp1 = spk_key_buf + SHIFT_TBL_SIZE ;
2010-10-07 13:20:02 -05:00
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 */
2010-10-12 11:39:30 -05:00
/* dump num_keys+1 as first row is shift states + flags,
2015-08-14 22:34:37 +03:00
* each subsequent row is key + states
*/
2010-10-07 13:20:02 -05:00
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 ) ;
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2016-09-22 15:27:30 +05:30
return ( int ) ( cp - buf ) ;
2010-10-07 13:20:02 -05:00
}
/*
* This is called when a user changes the keymap parameter .
*/
static ssize_t keymap_store ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
int i ;
ssize_t ret = count ;
char * in_buff = NULL ;
char * cp ;
u_char * cp1 ;
unsigned long flags ;
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2011-11-12 13:11:18 +01:00
in_buff = kmemdup ( buf , count + 1 , GFP_ATOMIC ) ;
2010-10-12 11:39:30 -05:00
if ( ! in_buff ) {
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return - ENOMEM ;
}
if ( strchr ( " dDrR " , * in_buff ) ) {
2013-01-02 02:37:40 +01:00
spk_set_key_info ( spk_key_defaults , spk_key_buf ) ;
2010-10-07 13:20:02 -05:00
pr_info ( " keymap set to default values \n " ) ;
kfree ( in_buff ) ;
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return count ;
}
if ( in_buff [ count - 1 ] = = ' \n ' )
2010-10-12 11:39:30 -05:00
in_buff [ count - 1 ] = ' \0 ' ;
2010-10-07 13:20:02 -05:00
cp = in_buff ;
cp1 = ( u_char * ) in_buff ;
for ( i = 0 ; i < 3 ; i + + ) {
2013-01-02 02:37:40 +01:00
cp = spk_s2uchar ( cp , cp1 ) ;
2010-10-07 13:20:02 -05:00
cp1 + + ;
}
2016-09-22 15:27:30 +05:30
i = ( int ) cp1 [ - 2 ] + 1 ;
i * = ( int ) cp1 [ - 1 ] + 1 ;
2010-10-07 13:20:02 -05:00
i + = 2 ; /* 0 and last map ver */
if ( cp1 [ - 3 ] ! = KEY_MAP_VER | | cp1 [ - 1 ] > 10 | |
2017-03-07 18:38:20 +02:00
i + SHIFT_TBL_SIZE + 4 > = sizeof ( spk_key_buf ) ) {
2010-10-07 13:20:02 -05:00
pr_warn ( " i %d %d %d %d \n " , i ,
2017-03-07 18:38:20 +02:00
( int ) cp1 [ - 3 ] , ( int ) cp1 [ - 2 ] , ( int ) cp1 [ - 1 ] ) ;
2010-10-07 13:20:02 -05:00
kfree ( in_buff ) ;
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return - EINVAL ;
}
while ( - - i > = 0 ) {
2013-01-02 02:37:40 +01:00
cp = spk_s2uchar ( cp , cp1 ) ;
2010-10-07 13:20:02 -05:00
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 ,
2017-03-07 18:38:20 +02:00
( int ) cp1 [ - 3 ] , ( int ) cp1 [ - 2 ] , ( int ) cp1 [ - 1 ] ) ;
2010-10-07 13:20:02 -05:00
} else {
2013-01-02 02:37:40 +01:00
if ( spk_set_key_info ( in_buff , spk_key_buf ) ) {
spk_set_key_info ( spk_key_defaults , spk_key_buf ) ;
2010-10-07 13:20:02 -05:00
ret = - EINVAL ;
pr_warn ( " set key failed \n " ) ;
}
}
kfree ( in_buff ) ;
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
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 ,
2017-03-07 18:38:20 +02:00
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
int len ;
struct vc_data * vc = vc_cons [ fg_console ] . d ;
char ch = 0 ;
char shut ;
unsigned long flags ;
len = strlen ( buf ) ;
2011-01-03 11:59:48 -08:00
if ( len > 0 & & len < 3 ) {
2010-10-07 13:20:02 -05:00
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 ;
}
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2017-02-23 17:37:31 +05:30
if ( ch & 2 ) {
2010-10-07 13:20:02 -05:00
shut = 1 ;
2013-01-02 02:37:40 +01:00
spk_do_flush ( ) ;
2010-10-07 13:20:02 -05:00
} else {
shut = 0 ;
}
2016-09-22 15:27:30 +05:30
if ( ch & 4 )
2010-10-07 13:20:02 -05:00
shut | = 0x40 ;
2016-09-22 15:27:30 +05:30
if ( ch & 1 )
2010-10-07 13:20:02 -05:00
spk_shut_up | = shut ;
else
spk_shut_up & = ~ shut ;
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return count ;
}
/*
* This is called when a user reads the synth setting .
*/
static ssize_t synth_show ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
char * buf )
2010-10-07 13:20:02 -05:00
{
int rv ;
2015-09-11 11:32:28 +05:30
if ( ! synth )
2010-10-07 13:20:02 -05:00
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 ,
2017-03-07 18:38:20 +02:00
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
int len ;
char new_synth_name [ 10 ] ;
len = strlen ( buf ) ;
if ( len < 2 | | len > 9 )
return - EINVAL ;
2018-07-01 13:57:24 -07:00
memcpy ( new_synth_name , buf , len ) ;
2010-10-07 13:20:02 -05:00
if ( new_synth_name [ len - 1 ] = = ' \n ' )
len - - ;
new_synth_name [ len ] = ' \0 ' ;
2013-01-02 02:37:40 +01:00
spk_strlwr ( new_synth_name ) ;
2017-03-21 17:12:32 +05:30
if ( synth & & ! strcmp ( new_synth_name , synth - > name ) ) {
2010-10-07 13:20:02 -05:00
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 .
*/
2010-10-12 11:39:30 -05:00
static ssize_t synth_direct_store ( struct kobject * kobj ,
2017-03-07 18:38:40 +02:00
struct kobj_attribute * attr ,
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
u_char tmp [ 256 ] ;
int len ;
int bytes ;
const char * ptr = buf ;
2016-09-05 16:17:47 +03:00
unsigned long flags ;
2010-10-07 13:20:02 -05:00
2010-10-12 11:39:30 -05:00
if ( ! synth )
2010-10-07 13:20:02 -05:00
return - EPERM ;
len = strlen ( buf ) ;
2016-09-05 16:17:47 +03:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
while ( len > 0 ) {
bytes = min_t ( size_t , len , 250 ) ;
strncpy ( tmp , ptr , bytes ) ;
tmp [ bytes ] = ' \0 ' ;
2013-04-30 15:27:31 -07:00
string_unescape_any_inplace ( tmp ) ;
2010-10-07 13:20:02 -05:00
synth_printf ( " %s " , tmp ) ;
ptr + = bytes ;
len - = bytes ;
}
2016-09-05 16:17:47 +03:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return count ;
}
/*
* This function is called when a user reads the version .
*/
static ssize_t version_show ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
char * buf )
2010-10-07 13:20:02 -05:00
{
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 ,
2017-03-07 18:38:20 +02:00
char * buf )
2010-10-07 13:20:02 -05:00
{
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 ;
2013-01-02 02:37:40 +01:00
p_header = spk_var_header_by_name ( attr - > attr . name ) ;
2015-09-11 11:32:28 +05:30
if ( ! p_header ) {
2010-10-12 11:39:30 -05:00
pr_warn ( " p_header is null, attr->attr.name is %s \n " ,
attr - > attr . name ) ;
2010-10-07 13:20:02 -05:00
return - EINVAL ;
}
2013-01-02 02:37:40 +01:00
var = spk_get_punc_var ( p_header - > var_id ) ;
2015-09-11 11:32:28 +05:30
if ( ! var ) {
2010-10-07 13:20:02 -05:00
pr_warn ( " var is null, p_header->var_id is %i \n " ,
2017-03-07 18:38:20 +02:00
p_header - > var_id ) ;
2010-10-07 13:20:02 -05:00
return - EINVAL ;
}
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2017-03-07 18:37:41 +02:00
pb = ( struct st_bits_data * ) & spk_punc_info [ var - > value ] ;
2010-10-07 13:20:02 -05:00
mask = pb - > mask ;
for ( i = 33 ; i < 128 ; i + + ) {
2017-02-23 17:37:31 +05:30
if ( ! ( spk_chartab [ i ] & mask ) )
2010-10-07 13:20:02 -05:00
continue ;
* cp + + = ( char ) i ;
}
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2017-02-22 23:16:40 +05:30
return cp - buf ;
2010-10-07 13:20:02 -05:00
}
/*
* This is called when a user changes the punctuation settings .
*/
static ssize_t punc_store ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
int x ;
struct st_var_header * p_header ;
struct punc_var_t * var ;
char punc_buf [ 100 ] ;
unsigned long flags ;
x = strlen ( buf ) ;
if ( x < 1 | | x > 99 )
return - EINVAL ;
2013-01-02 02:37:40 +01:00
p_header = spk_var_header_by_name ( attr - > attr . name ) ;
2015-09-11 11:32:28 +05:30
if ( ! p_header ) {
2010-10-12 11:39:30 -05:00
pr_warn ( " p_header is null, attr->attr.name is %s \n " ,
attr - > attr . name ) ;
2010-10-07 13:20:02 -05:00
return - EINVAL ;
}
2013-01-02 02:37:40 +01:00
var = spk_get_punc_var ( p_header - > var_id ) ;
2015-09-11 11:32:28 +05:30
if ( ! var ) {
2010-10-07 13:20:02 -05:00
pr_warn ( " var is null, p_header->var_id is %i \n " ,
2017-03-07 18:38:20 +02:00
p_header - > var_id ) ;
2010-10-07 13:20:02 -05:00
return - EINVAL ;
}
2018-07-01 13:57:24 -07:00
memcpy ( punc_buf , buf , x ) ;
2010-10-07 13:20:02 -05:00
while ( x & & punc_buf [ x - 1 ] = = ' \n ' )
x - - ;
punc_buf [ x ] = ' \0 ' ;
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
if ( * punc_buf = = ' d ' | | * punc_buf = = ' r ' )
2013-05-22 14:37:24 +05:30
x = spk_set_mask_bits ( NULL , var - > value , 3 ) ;
2010-10-07 13:20:02 -05:00
else
2013-01-02 02:37:40 +01:00
x = spk_set_mask_bits ( punc_buf , var - > value , 3 ) ;
2010-10-07 13:20:02 -05:00
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return count ;
}
/*
* This function is called when a user reads one of the variable parameters .
*/
ssize_t spk_var_show ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
char * buf )
2010-10-07 13:20:02 -05:00
{
int rv = 0 ;
struct st_var_header * param ;
struct var_t * var ;
2018-10-30 11:09:59 +00:00
char * cp1 ;
2010-10-07 13:20:02 -05:00
char * cp ;
char ch ;
unsigned long flags ;
2013-01-02 02:37:40 +01:00
param = spk_var_header_by_name ( attr - > attr . name ) ;
2015-09-11 11:32:28 +05:30
if ( ! param )
2010-10-07 13:20:02 -05:00
return - EINVAL ;
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2017-03-07 18:37:41 +02:00
var = ( struct var_t * ) param - > data ;
2010-10-07 13:20:02 -05:00
switch ( param - > var_type ) {
case VAR_NUM :
case VAR_TIME :
if ( var )
rv = sprintf ( buf , " %i \n " , var - > u . n . value ) ;
else
rv = sprintf ( buf , " 0 \n " ) ;
break ;
case VAR_STRING :
if ( var ) {
cp1 = buf ;
* cp1 + + = ' " ' ;
for ( cp = ( char * ) param - > p_val ; ( ch = * cp ) ; cp + + ) {
if ( ch > = ' ' & & ch < ' ~ ' )
* cp1 + + = ch ;
else
2014-12-24 04:07:32 +00:00
cp1 + = sprintf ( cp1 , " \\ x%02x " , ch ) ;
2010-10-07 13:20:02 -05:00
}
* cp1 + + = ' " ' ;
* cp1 + + = ' \n ' ;
* cp1 = ' \0 ' ;
2017-02-22 23:16:40 +05:30
rv = cp1 - buf ;
2010-10-07 13:20:02 -05:00
} else {
rv = sprintf ( buf , " \" \" \n " ) ;
}
break ;
default :
rv = sprintf ( buf , " Bad parameter %s, type %i \n " ,
2017-03-07 18:38:20 +02:00
param - > name , param - > var_type ) ;
2010-10-07 13:20:02 -05:00
break ;
}
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return rv ;
}
EXPORT_SYMBOL_GPL ( spk_var_show ) ;
2013-09-02 19:20:18 -03:00
/*
* Used to reset either default_pitch or default_vol .
*/
static inline void spk_reset_default_value ( char * header_name ,
2017-03-07 18:38:20 +02:00
int * synth_default_value , int idx )
2013-09-02 19:20:18 -03:00
{
struct st_var_header * param ;
if ( synth & & synth_default_value ) {
param = spk_var_header_by_name ( header_name ) ;
if ( param ) {
spk_set_num_var ( synth_default_value [ idx ] ,
param , E_NEW_DEFAULT ) ;
spk_set_num_var ( 0 , param , E_DEFAULT ) ;
pr_info ( " %s reset to default value \n " , param - > name ) ;
}
}
}
2010-10-07 13:20:02 -05:00
/*
* This function is called when a user echos a value to one of the
* variable parameters .
*/
ssize_t spk_var_store ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
struct st_var_header * param ;
int ret ;
int len ;
char * cp ;
struct var_t * var_data ;
2013-10-19 20:58:26 +05:30
long value ;
2010-10-07 13:20:02 -05:00
unsigned long flags ;
2013-01-02 02:37:40 +01:00
param = spk_var_header_by_name ( attr - > attr . name ) ;
2015-09-11 11:32:28 +05:30
if ( ! param )
2010-10-07 13:20:02 -05:00
return - EINVAL ;
2015-09-11 11:32:28 +05:30
if ( ! param - > data )
2010-10-07 13:20:02 -05:00
return 0 ;
ret = 0 ;
2013-04-30 15:27:31 -07:00
cp = ( char * ) buf ;
string_unescape_any_inplace ( cp ) ;
2010-10-07 13:20:02 -05:00
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
switch ( param - > var_type ) {
case VAR_NUM :
case VAR_TIME :
if ( * cp = = ' d ' | | * cp = = ' r ' | | * cp = = ' \0 ' )
len = E_DEFAULT ;
else if ( * cp = = ' + ' | | * cp = = ' - ' )
len = E_INC ;
else
len = E_SET ;
2013-10-19 20:58:26 +05:30
if ( kstrtol ( cp , 10 , & value ) = = 0 )
ret = spk_set_num_var ( value , param , len ) ;
else
2014-04-16 20:57:14 +09:00
pr_warn ( " overflow or parsing error has occurred " ) ;
2013-03-28 11:02:44 +02:00
if ( ret = = - ERANGE ) {
2010-10-07 13:20:02 -05:00
var_data = param - > data ;
pr_warn ( " value for %s out of range, expect %d to %d \n " ,
2013-09-02 19:20:18 -03:00
param - > name ,
2010-10-07 13:20:02 -05:00
var_data - > u . n . low , var_data - > u . n . high ) ;
}
2013-09-02 19:20:18 -03:00
/*
* If voice was just changed , we might need to reset our default
* pitch and volume .
*/
2013-09-11 10:05:27 -07:00
if ( param - > var_id = = VOICE & & synth & &
( ret = = 0 | | ret = = - ERESTART ) ) {
var_data = param - > data ;
value = var_data - > u . n . value ;
2013-09-02 19:20:18 -03:00
spk_reset_default_value ( " pitch " , synth - > default_pitch ,
2017-03-07 18:38:20 +02:00
value ) ;
2013-09-02 19:20:18 -03:00
spk_reset_default_value ( " vol " , synth - > default_vol ,
2017-03-07 18:38:20 +02:00
value ) ;
2013-09-02 19:20:18 -03:00
}
2010-10-07 13:20:02 -05:00
break ;
case VAR_STRING :
2013-09-02 19:20:18 -03:00
len = strlen ( cp ) ;
if ( ( len > = 1 ) & & ( cp [ len - 1 ] = = ' \n ' ) )
2010-10-07 13:20:02 -05:00
- - len ;
2013-09-02 19:20:18 -03:00
if ( ( len > = 2 ) & & ( cp [ 0 ] = = ' " ' ) & & ( cp [ len - 1 ] = = ' " ' ) ) {
+ + cp ;
2010-10-07 13:20:02 -05:00
len - = 2 ;
}
cp [ len ] = ' \0 ' ;
2013-09-02 19:20:18 -03:00
ret = spk_set_string_var ( cp , param , len ) ;
2013-03-28 11:02:44 +02:00
if ( ret = = - E2BIG )
2010-10-07 13:20:02 -05:00
pr_warn ( " value too long for %s \n " ,
2017-03-07 18:38:20 +02:00
param - > name ) ;
2010-10-07 13:20:02 -05:00
break ;
default :
pr_warn ( " %s unknown type %d \n " ,
param - > name , ( int ) param - > var_type ) ;
break ;
}
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
2013-03-28 11:02:44 +02:00
if ( ret = = - ERESTART )
2013-09-02 19:20:18 -03:00
pr_info ( " %s reset to default value \n " , param - > name ) ;
2010-10-07 13:20:02 -05:00
return count ;
}
EXPORT_SYMBOL_GPL ( spk_var_store ) ;
/*
* Functions for reading and writing lists of i18n messages . Incomplete .
*/
static ssize_t message_show_helper ( char * buf , enum msg_index_t first ,
2017-03-07 18:38:20 +02:00
enum msg_index_t last )
2010-10-07 13:20:02 -05:00
{
size_t bufsize = PAGE_SIZE ;
char * buf_pointer = buf ;
int printed ;
enum msg_index_t cursor ;
int index = 0 ;
* buf_pointer = ' \0 ' ; /* buf_pointer always looking at a NUL byte. */
for ( cursor = first ; cursor < = last ; cursor + + , index + + ) {
if ( bufsize < = 1 )
break ;
printed = scnprintf ( buf_pointer , bufsize , " %d \t %s \n " ,
2017-03-07 18:38:20 +02:00
index , spk_msg_get ( cursor ) ) ;
2010-10-07 13:20:02 -05:00
buf_pointer + = printed ;
bufsize - = printed ;
}
return buf_pointer - buf ;
}
static void report_msg_status ( int reset , int received , int used ,
2017-03-07 18:38:20 +02:00
int rejected , char * groupname )
2010-10-07 13:20:02 -05:00
{
int len ;
char buf [ 160 ] ;
if ( reset ) {
pr_info ( " i18n messages from group %s reset to defaults \n " ,
groupname ) ;
2010-10-12 11:39:30 -05:00
} else if ( received ) {
2010-10-07 13:20:02 -05:00
len = snprintf ( buf , sizeof ( buf ) ,
" updated %d of %d i18n messages from group %s \n " ,
used , received , groupname ) ;
if ( rejected )
snprintf ( buf + ( len - 1 ) , sizeof ( buf ) - ( len - 1 ) ,
" with %d reject%s \n " ,
rejected , rejected > 1 ? " s " : " " ) ;
2019-03-18 13:21:10 +00:00
pr_info ( " %s " , buf ) ;
2010-10-07 13:20:02 -05:00
}
}
static ssize_t message_store_helper ( const char * buf , size_t count ,
2017-03-07 18:38:20 +02:00
struct msg_group_t * group )
2010-10-07 13:20:02 -05:00
{
2017-03-07 18:37:41 +02:00
char * cp = ( char * ) buf ;
2010-10-07 13:20:02 -05:00
char * end = cp + count ;
char * linefeed = NULL ;
char * temp = NULL ;
ssize_t msg_stored = 0 ;
ssize_t retval = count ;
size_t desc_length = 0 ;
unsigned long index = 0 ;
int received = 0 ;
int used = 0 ;
int rejected = 0 ;
int reset = 0 ;
enum msg_index_t firstmessage = group - > start ;
enum msg_index_t lastmessage = group - > end ;
enum msg_index_t curmessage ;
while ( cp < end ) {
while ( ( cp < end ) & & ( * cp = = ' ' | | * cp = = ' \t ' ) )
cp + + ;
if ( cp = = end )
break ;
if ( strchr ( " dDrR " , * cp ) ) {
reset = 1 ;
break ;
}
received + + ;
linefeed = strchr ( cp , ' \n ' ) ;
if ( ! linefeed ) {
rejected + + ;
break ;
}
2010-10-12 11:39:30 -05:00
if ( ! isdigit ( * cp ) ) {
2010-10-07 13:20:02 -05:00
rejected + + ;
cp = linefeed + 1 ;
continue ;
}
2019-03-17 12:31:10 +00:00
/*
* Do not replace with kstrtoul :
* here we need temp to be updated
*/
2010-10-07 13:20:02 -05:00
index = simple_strtoul ( cp , & temp , 10 ) ;
while ( ( temp < linefeed ) & & ( * temp = = ' ' | | * temp = = ' \t ' ) )
temp + + ;
desc_length = linefeed - temp ;
curmessage = firstmessage + index ;
/*
* Note the check ( curmessage < firstmessage ) . It is not
* redundant . Suppose that the user gave us an index
* equal to ULONG_MAX - 1. If firstmessage > 1 , then
* firstmessage + index < firstmessage !
*/
if ( ( curmessage < firstmessage ) | | ( curmessage > lastmessage ) ) {
rejected + + ;
cp = linefeed + 1 ;
continue ;
}
2013-01-02 02:37:40 +01:00
msg_stored = spk_msg_set ( curmessage , temp , desc_length ) ;
2010-10-07 13:20:02 -05:00
if ( msg_stored < 0 ) {
retval = msg_stored ;
if ( msg_stored = = - ENOMEM )
reset = 1 ;
break ;
}
2014-10-03 14:04:25 +03:00
used + + ;
2010-10-07 13:20:02 -05:00
cp = linefeed + 1 ;
}
if ( reset )
2013-01-02 02:37:40 +01:00
spk_reset_msg_group ( group ) ;
2010-10-07 13:20:02 -05:00
report_msg_status ( reset , received , used , rejected , group - > name ) ;
return retval ;
}
static ssize_t message_show ( struct kobject * kobj ,
2017-03-07 18:38:20 +02:00
struct kobj_attribute * attr , char * buf )
2010-10-07 13:20:02 -05:00
{
ssize_t retval = 0 ;
2013-01-02 02:37:40 +01:00
struct msg_group_t * group = spk_find_msg_group ( attr - > attr . name ) ;
2010-10-07 13:20:02 -05:00
unsigned long flags ;
2017-02-25 17:53:58 +05:30
if ( WARN_ON ( ! group ) )
return - EINVAL ;
2013-05-13 00:02:53 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
retval = message_show_helper ( buf , group - > start , group - > end ) ;
2013-05-13 00:02:53 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return retval ;
}
static ssize_t message_store ( struct kobject * kobj , struct kobj_attribute * attr ,
2017-03-07 18:38:20 +02:00
const char * buf , size_t count )
2010-10-07 13:20:02 -05:00
{
2013-01-02 02:37:40 +01:00
struct msg_group_t * group = spk_find_msg_group ( attr - > attr . name ) ;
2010-10-07 13:20:02 -05:00
2017-02-25 17:53:58 +05:30
if ( WARN_ON ( ! group ) )
return - EINVAL ;
2015-03-08 00:03:51 +03:00
return message_store_helper ( buf , count , group ) ;
2010-10-07 13:20:02 -05:00
}
/*
* Declare the attributes .
*/
static struct kobj_attribute keymap_attribute =
2014-04-24 13:57:49 +09:30
__ATTR_RW ( keymap ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute silent_attribute =
2014-04-24 13:57:49 +09:30
__ATTR_WO ( silent ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute synth_attribute =
2014-04-24 13:57:49 +09:30
__ATTR_RW ( synth ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute synth_direct_attribute =
2014-04-24 13:57:49 +09:30
__ATTR_WO ( synth_direct ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute version_attribute =
__ATTR_RO ( version ) ;
static struct kobj_attribute delimiters_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( delimiters , 0644 , punc_show , punc_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute ex_num_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( ex_num , 0644 , punc_show , punc_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute punc_all_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( punc_all , 0644 , punc_show , punc_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute punc_most_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( punc_most , 0644 , punc_show , punc_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute punc_some_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( punc_some , 0644 , punc_show , punc_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute repeats_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( repeats , 0644 , punc_show , punc_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute attrib_bleep_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( attrib_bleep , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute bell_pos_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( bell_pos , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute bleep_time_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( bleep_time , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute bleeps_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( bleeps , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute cursor_time_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( cursor_time , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute key_echo_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( key_echo , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute no_interrupt_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( no_interrupt , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute punc_level_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( punc_level , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute reading_punc_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( reading_punc , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute say_control_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( say_control , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute say_word_ctl_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( say_word_ctl , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute spell_delay_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( spell_delay , 0644 , spk_var_show , spk_var_store ) ;
2010-10-07 13:20:02 -05:00
/*
* These attributes are i18n related .
*/
static struct kobj_attribute announcements_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( announcements , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute characters_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( characters , 0644 , chars_chartab_show ,
2014-09-15 14:58:44 +03:00
chars_chartab_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute chartab_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( chartab , 0644 , chars_chartab_show ,
2014-09-15 14:58:44 +03:00
chars_chartab_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute ctl_keys_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( ctl_keys , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute colors_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( colors , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute formatted_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( formatted , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute function_names_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( function_names , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute key_names_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( key_names , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
static struct kobj_attribute states_attribute =
2017-01-09 17:35:57 +13:00
__ATTR ( states , 0644 , message_show , message_store ) ;
2010-10-07 13:20:02 -05:00
/*
* Create groups of attributes so that we can create and destroy them all
* at once .
*/
static struct attribute * main_attrs [ ] = {
& keymap_attribute . attr ,
& silent_attribute . attr ,
& synth_attribute . attr ,
& synth_direct_attribute . attr ,
& version_attribute . attr ,
& delimiters_attribute . attr ,
& ex_num_attribute . attr ,
& punc_all_attribute . attr ,
& punc_most_attribute . attr ,
& punc_some_attribute . attr ,
& repeats_attribute . attr ,
& attrib_bleep_attribute . attr ,
& bell_pos_attribute . attr ,
& bleep_time_attribute . attr ,
& bleeps_attribute . attr ,
& cursor_time_attribute . attr ,
& key_echo_attribute . attr ,
& no_interrupt_attribute . attr ,
& punc_level_attribute . attr ,
& reading_punc_attribute . attr ,
& say_control_attribute . attr ,
& say_word_ctl_attribute . attr ,
& spell_delay_attribute . attr ,
NULL ,
} ;
static struct attribute * i18n_attrs [ ] = {
& announcements_attribute . attr ,
& characters_attribute . attr ,
& chartab_attribute . attr ,
& ctl_keys_attribute . attr ,
& colors_attribute . attr ,
& formatted_attribute . attr ,
& function_names_attribute . attr ,
& key_names_attribute . attr ,
& states_attribute . attr ,
NULL ,
} ;
/*
* An unnamed attribute group will put all of the attributes directly in
* the kobject directory . If we specify a name , a subdirectory will be
* created for the attributes with the directory being the name of the
* attribute group .
*/
2016-09-29 20:52:00 +05:30
static const struct attribute_group main_attr_group = {
2010-10-07 13:20:02 -05:00
. attrs = main_attrs ,
} ;
2016-09-29 20:52:00 +05:30
static const struct attribute_group i18n_attr_group = {
2010-10-07 13:20:02 -05:00
. attrs = i18n_attrs ,
. name = " i18n " ,
} ;
static struct kobject * accessibility_kobj ;
struct kobject * speakup_kobj ;
int speakup_kobj_init ( void )
{
int retval ;
/*
* Create a simple kobject with the name of " accessibility " ,
* located under / sys /
*
* As this is a simple directory , no uevent will be sent to
* userspace . That is why this function should not be used for
* any type of dynamic kobjects , where the name and number are
* not known ahead of time .
*/
accessibility_kobj = kobject_create_and_add ( " accessibility " , NULL ) ;
2010-12-16 13:26:58 -06:00
if ( ! accessibility_kobj ) {
retval = - ENOMEM ;
goto out ;
}
2010-10-07 13:20:02 -05:00
speakup_kobj = kobject_create_and_add ( " speakup " , accessibility_kobj ) ;
if ( ! speakup_kobj ) {
2010-10-17 18:51:53 +04:00
retval = - ENOMEM ;
goto err_acc ;
2010-10-07 13:20:02 -05:00
}
/* Create the files associated with this kobject */
retval = sysfs_create_group ( speakup_kobj , & main_attr_group ) ;
if ( retval )
2010-10-17 18:51:53 +04:00
goto err_speakup ;
2010-10-07 13:20:02 -05:00
retval = sysfs_create_group ( speakup_kobj , & i18n_attr_group ) ;
if ( retval )
2010-10-17 18:51:53 +04:00
goto err_group ;
2010-12-16 13:26:58 -06:00
goto out ;
2010-10-07 13:20:02 -05:00
2010-10-17 18:51:53 +04:00
err_group :
sysfs_remove_group ( speakup_kobj , & main_attr_group ) ;
err_speakup :
kobject_put ( speakup_kobj ) ;
err_acc :
kobject_put ( accessibility_kobj ) ;
2010-12-16 13:26:58 -06:00
out :
2010-10-07 13:20:02 -05:00
return retval ;
}
void speakup_kobj_exit ( void )
{
2010-10-17 18:51:53 +04:00
sysfs_remove_group ( speakup_kobj , & i18n_attr_group ) ;
sysfs_remove_group ( speakup_kobj , & main_attr_group ) ;
2010-10-07 13:20:02 -05:00
kobject_put ( speakup_kobj ) ;
kobject_put ( accessibility_kobj ) ;
}