2018-01-11 11:08:40 +01:00
// SPDX-License-Identifier: GPL-2.0
2010-10-07 13:20:02 -05:00
# include <linux/types.h>
# include <linux/ctype.h> /* for isdigit() and friends */
# include <linux/fs.h>
# include <linux/mm.h> /* for verify_area */
# include <linux/errno.h> /* for -EBUSY */
# include <linux/ioport.h> /* for check_region, request_region */
# include <linux/interrupt.h>
# include <linux/delay.h> /* for loops_per_sec */
# include <linux/kmod.h>
# include <linux/jiffies.h>
2016-11-19 12:35:46 -05:00
# include <linux/uaccess.h> /* for copy_from_user */
2010-10-07 13:20:02 -05:00
# include <linux/sched.h>
# include <linux/timer.h>
# include <linux/kthread.h>
# include "spk_priv.h"
# include "speakup.h"
# include "serialio.h"
2018-06-04 10:52:12 +01:00
static LIST_HEAD ( synths ) ;
2010-10-15 22:13:37 -05:00
struct spk_synth * synth ;
2013-01-02 02:37:40 +01:00
char spk_pitch_buff [ 32 ] = " " ;
2010-10-07 13:20:02 -05:00
static int module_status ;
2013-01-02 02:37:40 +01:00
bool spk_quiet_boot ;
2010-10-07 13:20:02 -05:00
struct speakup_info_t speakup_info = {
2013-05-13 13:31:40 -05:00
/*
* This spinlock is used to protect the entire speakup machinery , and
* must be taken at each kernel - > speakup transition and released at
* each corresponding speakup - > kernel transition .
*
2014-12-24 04:07:31 +00:00
* The progression thread only interferes with the speakup machinery
* through the synth buffer , so only needs to take the lock
* while tinkering with the buffer .
2013-05-13 13:31:40 -05:00
*
* We use spin_lock / trylock_irqsave and spin_unlock_irqrestore with this
* spinlock because speakup needs to disable the keyboard IRQ .
*/
2010-10-15 22:13:37 -05:00
. spinlock = __SPIN_LOCK_UNLOCKED ( speakup_info . spinlock ) ,
2010-10-07 13:20:02 -05:00
. flushing = 0 ,
} ;
EXPORT_SYMBOL_GPL ( speakup_info ) ;
static int do_synth_init ( struct spk_synth * in_synth ) ;
2016-11-19 12:35:46 -05:00
/*
* Main loop of the progression thread : keep eating from the buffer
2010-10-07 13:20:02 -05:00
* and push to the serial port , waiting as needed
*
2012-07-09 07:30:27 -07:00
* For devices that have a " full " notification mechanism , the driver can
2010-10-07 13:20:02 -05:00
* adapt the loop the way they prefer .
*/
2018-03-10 11:56:27 +01:00
static void _spk_do_catch_up ( struct spk_synth * synth , int unicode )
2010-10-07 13:20:02 -05:00
{
2018-03-10 11:56:27 +01:00
u16 ch ;
2010-10-07 13:20:02 -05:00
unsigned long flags ;
unsigned long jiff_max ;
struct var_t * delay_time ;
struct var_t * full_time ;
struct var_t * jiffy_delta ;
int jiffy_delta_val ;
int delay_time_val ;
int full_time_val ;
2018-03-10 11:56:27 +01:00
int ret ;
2010-10-07 13:20:02 -05:00
2013-01-02 02:37:40 +01:00
jiffy_delta = spk_get_var ( JIFFY ) ;
full_time = spk_get_var ( FULL ) ;
delay_time = spk_get_var ( DELAY ) ;
2010-10-07 13:20:02 -05:00
2013-05-13 00:03:08 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
jiffy_delta_val = jiffy_delta - > u . n . value ;
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
jiff_max = jiffies + jiffy_delta_val ;
while ( ! kthread_should_stop ( ) ) {
2013-05-13 00:03:08 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
if ( speakup_info . flushing ) {
speakup_info . flushing = 0 ;
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
synth - > flush ( synth ) ;
continue ;
}
2018-03-10 11:56:27 +01:00
if ( ! unicode )
synth_buffer_skip_nonlatin1 ( ) ;
2010-10-07 13:20:02 -05:00
if ( synth_buffer_empty ( ) ) {
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
break ;
}
ch = synth_buffer_peek ( ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
full_time_val = full_time - > u . n . value ;
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
if ( ch = = ' \n ' )
ch = synth - > procspeech ;
2018-03-10 11:56:27 +01:00
if ( unicode )
ret = synth - > io_ops - > synth_out_unicode ( synth , ch ) ;
else
ret = synth - > io_ops - > synth_out ( synth , ch ) ;
if ( ! ret ) {
2010-10-07 13:20:02 -05:00
schedule_timeout ( msecs_to_jiffies ( full_time_val ) ) ;
continue ;
}
2014-09-21 19:22:51 +05:30
if ( time_after_eq ( jiffies , jiff_max ) & & ( ch = = SPACE ) ) {
2013-05-13 00:03:08 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
jiffy_delta_val = jiffy_delta - > u . n . value ;
delay_time_val = delay_time - > u . n . value ;
full_time_val = full_time - > u . n . value ;
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2017-03-14 13:41:53 +00:00
if ( synth - > io_ops - > synth_out ( synth , synth - > procspeech ) )
2010-10-15 22:13:37 -05:00
schedule_timeout (
msecs_to_jiffies ( delay_time_val ) ) ;
2010-10-07 13:20:02 -05:00
else
2010-10-15 22:13:37 -05:00
schedule_timeout (
msecs_to_jiffies ( full_time_val ) ) ;
2010-10-07 13:20:02 -05:00
jiff_max = jiffies + jiffy_delta_val ;
}
set_current_state ( TASK_RUNNING ) ;
2013-05-13 00:03:08 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
synth_buffer_getc ( ) ;
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
}
2017-03-14 13:41:53 +00:00
synth - > io_ops - > synth_out ( synth , synth - > procspeech ) ;
2010-10-07 13:20:02 -05:00
}
2018-03-10 11:56:27 +01:00
void spk_do_catch_up ( struct spk_synth * synth )
{
_spk_do_catch_up ( synth , 0 ) ;
}
2010-10-07 13:20:02 -05:00
EXPORT_SYMBOL_GPL ( spk_do_catch_up ) ;
2018-03-10 11:56:27 +01:00
void spk_do_catch_up_unicode ( struct spk_synth * synth )
{
_spk_do_catch_up ( synth , 1 ) ;
}
EXPORT_SYMBOL_GPL ( spk_do_catch_up_unicode ) ;
2010-10-07 13:20:02 -05:00
void spk_synth_flush ( struct spk_synth * synth )
{
2021-01-26 23:21:44 +01:00
synth - > io_ops - > flush_buffer ( synth ) ;
2017-03-14 13:41:53 +00:00
synth - > io_ops - > synth_out ( synth , synth - > clear ) ;
2010-10-07 13:20:02 -05:00
}
EXPORT_SYMBOL_GPL ( spk_synth_flush ) ;
2017-04-29 20:52:58 +01:00
unsigned char spk_synth_get_index ( struct spk_synth * synth )
{
2021-01-26 23:21:44 +01:00
return synth - > io_ops - > synth_in_nowait ( synth ) ;
2017-04-29 20:52:58 +01:00
}
EXPORT_SYMBOL_GPL ( spk_synth_get_index ) ;
2010-10-07 13:20:02 -05:00
int spk_synth_is_alive_nop ( struct spk_synth * synth )
{
synth - > alive = 1 ;
return 1 ;
}
EXPORT_SYMBOL_GPL ( spk_synth_is_alive_nop ) ;
int spk_synth_is_alive_restart ( struct spk_synth * synth )
{
if ( synth - > alive )
return 1 ;
2020-08-04 18:06:37 +02:00
if ( synth - > io_ops - > wait_for_xmitr ( synth ) > 0 ) {
2010-10-07 13:20:02 -05:00
/* restart */
synth - > alive = 1 ;
synth_printf ( " %s " , synth - > init ) ;
return 2 ; /* reenabled */
}
pr_warn ( " %s: can't restart synth \n " , synth - > long_name ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( spk_synth_is_alive_restart ) ;
2017-08-28 11:28:21 -07:00
static void thread_wake_up ( struct timer_list * unused )
2010-10-07 13:20:02 -05:00
{
wake_up_interruptible_all ( & speakup_event ) ;
}
2017-10-04 16:27:04 -07:00
static DEFINE_TIMER ( thread_timer , thread_wake_up ) ;
2010-10-07 13:20:02 -05:00
void synth_start ( void )
{
struct var_t * trigger_time ;
if ( ! synth - > alive ) {
synth_buffer_clear ( ) ;
return ;
}
2013-01-02 02:37:40 +01:00
trigger_time = spk_get_var ( TRIGGER ) ;
2010-10-07 13:20:02 -05:00
if ( ! timer_pending ( & thread_timer ) )
2010-10-15 22:13:37 -05:00
mod_timer ( & thread_timer , jiffies +
msecs_to_jiffies ( trigger_time - > u . n . value ) ) ;
2010-10-07 13:20:02 -05:00
}
2013-01-02 02:37:40 +01:00
void spk_do_flush ( void )
2010-10-07 13:20:02 -05:00
{
2014-05-27 19:08:36 -04:00
if ( ! synth )
return ;
2010-10-07 13:20:02 -05:00
speakup_info . flushing = 1 ;
synth_buffer_clear ( ) ;
if ( synth - > alive ) {
2013-01-02 02:37:40 +01:00
if ( spk_pitch_shift ) {
synth_printf ( " %s " , spk_pitch_buff ) ;
spk_pitch_shift = 0 ;
2010-10-07 13:20:02 -05:00
}
}
wake_up_interruptible_all ( & speakup_event ) ;
wake_up_process ( speakup_task ) ;
}
void synth_write ( const char * buf , size_t count )
{
while ( count - - )
synth_buffer_add ( * buf + + ) ;
synth_start ( ) ;
}
void synth_printf ( const char * fmt , . . . )
{
va_list args ;
unsigned char buf [ 160 ] , * p ;
int r ;
va_start ( args , fmt ) ;
r = vsnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
va_end ( args ) ;
if ( r > sizeof ( buf ) - 1 )
r = sizeof ( buf ) - 1 ;
p = buf ;
while ( r - - )
synth_buffer_add ( * p + + ) ;
synth_start ( ) ;
}
EXPORT_SYMBOL_GPL ( synth_printf ) ;
2017-03-04 15:01:55 +01:00
void synth_putwc ( u16 wc )
{
synth_buffer_add ( wc ) ;
}
EXPORT_SYMBOL_GPL ( synth_putwc ) ;
void synth_putwc_s ( u16 wc )
{
synth_buffer_add ( wc ) ;
synth_start ( ) ;
}
EXPORT_SYMBOL_GPL ( synth_putwc_s ) ;
void synth_putws ( const u16 * buf )
{
const u16 * p ;
for ( p = buf ; * p ; p + + )
synth_buffer_add ( * p ) ;
}
EXPORT_SYMBOL_GPL ( synth_putws ) ;
void synth_putws_s ( const u16 * buf )
{
synth_putws ( buf ) ;
synth_start ( ) ;
}
EXPORT_SYMBOL_GPL ( synth_putws_s ) ;
2010-10-15 22:13:37 -05:00
static int index_count ;
static int sentence_count ;
2010-10-07 13:20:02 -05:00
2013-01-02 02:37:40 +01:00
void spk_reset_index_count ( int sc )
2010-10-07 13:20:02 -05:00
{
static int first = 1 ;
2014-09-09 20:04:34 +02:00
2010-10-07 13:20:02 -05:00
if ( first )
first = 0 ;
else
2017-04-29 20:52:58 +01:00
synth - > get_index ( synth ) ;
2010-10-07 13:20:02 -05:00
index_count = 0 ;
sentence_count = sc ;
}
int synth_supports_indexing ( void )
{
2017-02-16 21:08:31 -08:00
if ( synth - > get_index )
2010-10-07 13:20:02 -05:00
return 1 ;
return 0 ;
}
void synth_insert_next_index ( int sent_num )
{
int out ;
2014-09-09 20:04:34 +02:00
2010-10-07 13:20:02 -05:00
if ( synth - > alive ) {
if ( sent_num = = 0 ) {
synth - > indexing . currindex + + ;
index_count + + ;
if ( synth - > indexing . currindex >
synth - > indexing . highindex )
synth - > indexing . currindex =
synth - > indexing . lowindex ;
}
out = synth - > indexing . currindex * 10 + sent_num ;
synth_printf ( synth - > indexing . command , out , out ) ;
}
}
2013-01-02 02:37:40 +01:00
void spk_get_index_count ( int * linecount , int * sentcount )
2010-10-07 13:20:02 -05:00
{
2017-04-29 20:52:58 +01:00
int ind = synth - > get_index ( synth ) ;
2014-09-09 20:04:34 +02:00
2010-10-07 13:20:02 -05:00
if ( ind ) {
sentence_count = ind % 10 ;
if ( ( ind / 10 ) < = synth - > indexing . currindex )
2016-11-19 14:34:35 -05:00
index_count = synth - > indexing . currindex - ( ind / 10 ) ;
2010-10-07 13:20:02 -05:00
else
2010-10-15 22:13:37 -05:00
index_count = synth - > indexing . currindex
2016-11-19 14:34:35 -05:00
- synth - > indexing . lowindex
+ synth - > indexing . highindex - ( ind / 10 ) + 1 ;
2010-10-07 13:20:02 -05:00
}
* sentcount = sentence_count ;
* linecount = index_count ;
}
static struct resource synth_res ;
int synth_request_region ( unsigned long start , unsigned long n )
{
struct resource * parent = & ioport_resource ;
2014-09-09 20:04:34 +02:00
2010-10-07 13:20:02 -05:00
memset ( & synth_res , 0 , sizeof ( synth_res ) ) ;
synth_res . name = synth - > name ;
synth_res . start = start ;
synth_res . end = start + n - 1 ;
synth_res . flags = IORESOURCE_BUSY ;
return request_resource ( parent , & synth_res ) ;
}
EXPORT_SYMBOL_GPL ( synth_request_region ) ;
int synth_release_region ( unsigned long start , unsigned long n )
{
return release_resource ( & synth_res ) ;
}
EXPORT_SYMBOL_GPL ( synth_release_region ) ;
struct var_t synth_time_vars [ ] = {
2010-10-15 22:13:37 -05:00
{ DELAY , . u . n = { NULL , 100 , 100 , 2000 , 0 , 0 , NULL } } ,
{ TRIGGER , . u . n = { NULL , 20 , 10 , 2000 , 0 , 0 , NULL } } ,
{ JIFFY , . u . n = { NULL , 50 , 20 , 200 , 0 , 0 , NULL } } ,
{ FULL , . u . n = { NULL , 400 , 200 , 60000 , 0 , 0 , NULL } } ,
2022-02-06 00:29:57 +01:00
{ FLUSH , . u . n = { NULL , 4000 , 10 , 4000 , 0 , 0 , NULL } } ,
2010-10-07 13:20:02 -05:00
V_LAST_VAR
} ;
/* called by: speakup_init() */
int synth_init ( char * synth_name )
{
int ret = 0 ;
2018-06-04 10:52:12 +01:00
struct spk_synth * tmp , * synth = NULL ;
2010-10-07 13:20:02 -05:00
2017-02-16 21:08:31 -08:00
if ( ! synth_name )
2010-10-07 13:20:02 -05:00
return 0 ;
if ( strcmp ( synth_name , " none " ) = = 0 ) {
mutex_lock ( & spk_mutex ) ;
synth_release ( ) ;
mutex_unlock ( & spk_mutex ) ;
return 0 ;
}
mutex_lock ( & spk_mutex ) ;
/* First, check if we already have it loaded. */
2018-06-04 10:52:12 +01:00
list_for_each_entry ( tmp , & synths , node ) {
if ( strcmp ( tmp - > name , synth_name ) = = 0 )
synth = tmp ;
}
2010-10-07 13:20:02 -05:00
/* If we got one, initialize it now. */
if ( synth )
ret = do_synth_init ( synth ) ;
else
ret = - ENODEV ;
mutex_unlock ( & spk_mutex ) ;
return ret ;
}
/* called by: synth_add() */
static int do_synth_init ( struct spk_synth * in_synth )
{
struct var_t * var ;
synth_release ( ) ;
if ( in_synth - > checkval ! = SYNTH_CHECK )
return - EINVAL ;
synth = in_synth ;
synth - > alive = 0 ;
pr_warn ( " synth probe \n " ) ;
if ( synth - > probe ( synth ) < 0 ) {
pr_warn ( " %s: device probe failed \n " , in_synth - > name ) ;
synth = NULL ;
return - ENODEV ;
}
synth_time_vars [ 0 ] . u . n . value =
synth_time_vars [ 0 ] . u . n . default_val = synth - > delay ;
synth_time_vars [ 1 ] . u . n . value =
synth_time_vars [ 1 ] . u . n . default_val = synth - > trigger ;
synth_time_vars [ 2 ] . u . n . value =
synth_time_vars [ 2 ] . u . n . default_val = synth - > jiffies ;
synth_time_vars [ 3 ] . u . n . value =
synth_time_vars [ 3 ] . u . n . default_val = synth - > full ;
2021-01-28 19:01:16 +01:00
synth_time_vars [ 4 ] . u . n . value =
synth_time_vars [ 4 ] . u . n . default_val = synth - > flush_time ;
2010-10-07 13:20:02 -05:00
synth_printf ( " %s " , synth - > init ) ;
2010-10-15 22:13:37 -05:00
for ( var = synth - > vars ;
( var - > var_id > = 0 ) & & ( var - > var_id < MAXVARS ) ; var + + )
2010-10-07 13:20:02 -05:00
speakup_register_var ( var ) ;
2013-01-02 02:37:40 +01:00
if ( ! spk_quiet_boot )
2010-10-07 13:20:02 -05:00
synth_printf ( " %s found \n " , synth - > long_name ) ;
2017-02-16 21:08:32 -08:00
if ( synth - > attributes . name & &
sysfs_create_group ( speakup_kobj , & synth - > attributes ) < 0 )
2010-10-07 13:20:02 -05:00
return - ENOMEM ;
synth_flags = synth - > flags ;
wake_up_interruptible_all ( & speakup_event ) ;
if ( speakup_task )
wake_up_process ( speakup_task ) ;
return 0 ;
}
void synth_release ( void )
{
struct var_t * var ;
unsigned long flags ;
2017-02-16 21:08:31 -08:00
if ( ! synth )
2010-10-07 13:20:02 -05:00
return ;
2013-05-13 00:03:08 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
pr_info ( " releasing synth %s \n " , synth - > name ) ;
synth - > alive = 0 ;
del_timer ( & thread_timer ) ;
2013-05-13 00:03:08 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
if ( synth - > attributes . name )
2016-09-21 15:13:20 -07:00
sysfs_remove_group ( speakup_kobj , & synth - > attributes ) ;
2010-10-07 13:20:02 -05:00
for ( var = synth - > vars ; var - > var_id ! = MAXVARS ; var + + )
speakup_unregister_var ( var - > var_id ) ;
2021-01-26 23:21:44 +01:00
synth - > release ( synth ) ;
2010-10-07 13:20:02 -05:00
synth = NULL ;
}
/* called by: all_driver_init() */
int synth_add ( struct spk_synth * in_synth )
{
int status = 0 ;
2018-06-04 10:52:12 +01:00
struct spk_synth * tmp ;
2014-09-09 20:04:34 +02:00
2010-10-07 13:20:02 -05:00
mutex_lock ( & spk_mutex ) ;
2018-06-04 10:52:12 +01:00
list_for_each_entry ( tmp , & synths , node ) {
if ( tmp = = in_synth ) {
2010-10-07 13:20:02 -05:00
mutex_unlock ( & spk_mutex ) ;
return 0 ;
}
}
2017-06-20 11:07:32 +01:00
2010-10-07 13:20:02 -05:00
if ( in_synth - > startup )
status = do_synth_init ( in_synth ) ;
2017-06-20 11:07:32 +01:00
2018-06-04 10:52:12 +01:00
if ( ! status )
list_add_tail ( & in_synth - > node , & synths ) ;
2017-06-20 11:07:32 +01:00
2010-10-07 13:20:02 -05:00
mutex_unlock ( & spk_mutex ) ;
return status ;
}
EXPORT_SYMBOL_GPL ( synth_add ) ;
void synth_remove ( struct spk_synth * in_synth )
{
mutex_lock ( & spk_mutex ) ;
if ( synth = = in_synth )
synth_release ( ) ;
2018-06-04 10:52:12 +01:00
list_del ( & in_synth - > node ) ;
2010-10-07 13:20:02 -05:00
module_status = 0 ;
mutex_unlock ( & spk_mutex ) ;
}
EXPORT_SYMBOL_GPL ( synth_remove ) ;
2019-03-07 23:06:57 +01:00
struct spk_synth * synth_current ( void )
{
return synth ;
}
EXPORT_SYMBOL_GPL ( synth_current ) ;
2016-11-19 14:34:35 -05:00
short spk_punc_masks [ ] = { 0 , SOME , MOST , PUNC , PUNC | B_SYM } ;