2018-01-11 11:08:40 +01:00
// SPDX-License-Identifier: GPL-2.0
2010-10-07 13:20:02 -05:00
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include "spk_types.h"
# include "speakup.h"
# include "spk_priv.h"
# include "serialio.h"
2016-01-15 00:47:41 +01:00
# include <linux/serial_core.h>
/* WARNING: Do not change this to <linux/serial.h> without testing that
2016-02-13 22:05:01 -05:00
* SERIAL_PORT_DFNS does get defined to the appropriate value .
*/
2016-01-15 00:47:41 +01:00
# include <asm/serial.h>
2013-10-31 15:27:37 +08:00
# ifndef SERIAL_PORT_DFNS
# define SERIAL_PORT_DFNS
# endif
2010-10-07 13:20:02 -05:00
static void start_serial_interrupt ( int irq ) ;
2012-03-05 14:52:11 +01:00
static const struct old_serial_port rs_table [ ] = {
2010-10-07 13:20:02 -05:00
SERIAL_PORT_DFNS
} ;
2017-03-14 10:46:42 +05:30
2012-03-05 14:52:11 +01:00
static const struct old_serial_port * serstate ;
2010-10-07 13:20:02 -05:00
static int timeouts ;
2017-03-14 13:41:53 +00:00
static int spk_serial_out ( struct spk_synth * in_synth , const char ch ) ;
2021-01-26 23:21:44 +01:00
static void spk_serial_send_xchar ( struct spk_synth * in_synth , char ch ) ;
static void spk_serial_tiocmset ( struct spk_synth * in_synth , unsigned int set , unsigned int clear ) ;
static unsigned char spk_serial_in ( struct spk_synth * in_synth ) ;
static unsigned char spk_serial_in_nowait ( struct spk_synth * in_synth ) ;
static void spk_serial_flush_buffer ( struct spk_synth * in_synth ) ;
2020-08-04 18:06:37 +02:00
static int spk_serial_wait_for_xmitr ( struct spk_synth * in_synth ) ;
2017-04-22 09:00:28 +01:00
2017-03-14 13:41:53 +00:00
struct spk_io_ops spk_serial_io_ops = {
. synth_out = spk_serial_out ,
2017-04-22 09:00:28 +01:00
. send_xchar = spk_serial_send_xchar ,
. tiocmset = spk_serial_tiocmset ,
2017-04-29 20:52:58 +01:00
. synth_in = spk_serial_in ,
. synth_in_nowait = spk_serial_in_nowait ,
2017-05-15 18:45:37 +01:00
. flush_buffer = spk_serial_flush_buffer ,
2020-08-04 18:06:37 +02:00
. wait_for_xmitr = spk_serial_wait_for_xmitr ,
2017-03-14 13:41:53 +00:00
} ;
EXPORT_SYMBOL_GPL ( spk_serial_io_ops ) ;
2012-03-05 14:52:11 +01:00
const struct old_serial_port * spk_serial_init ( int index )
2010-10-07 13:20:02 -05:00
{
int baud = 9600 , quot = 0 ;
unsigned int cval = 0 ;
int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8 ;
2016-01-15 00:47:41 +01:00
const struct old_serial_port * ser ;
2010-10-07 13:20:02 -05:00
int err ;
2016-01-15 00:47:41 +01:00
if ( index > = ARRAY_SIZE ( rs_table ) ) {
pr_info ( " no port info for ttyS%d \n " , index ) ;
return NULL ;
}
ser = rs_table + index ;
2010-10-07 13:20:02 -05:00
/* Divisor, bytesize and parity */
quot = ser - > baud_base / baud ;
cval = cflag & ( CSIZE | CSTOPB ) ;
# if defined(__powerpc__) || defined(__alpha__)
cval > > = 8 ;
# else /* !__powerpc__ && !__alpha__ */
cval > > = 4 ;
# endif /* !__powerpc__ && !__alpha__ */
if ( cflag & PARENB )
cval | = UART_LCR_PARITY ;
if ( ! ( cflag & PARODD ) )
cval | = UART_LCR_EPAR ;
if ( synth_request_region ( ser - > port , 8 ) ) {
/* try to take it back. */
2014-03-18 14:10:13 +05:30
pr_info ( " Ports not available, trying to steal them \n " ) ;
2010-10-07 13:20:02 -05:00
__release_region ( & ioport_resource , ser - > port , 8 ) ;
err = synth_request_region ( ser - > port , 8 ) ;
if ( err ) {
2012-03-05 14:52:11 +01:00
pr_warn ( " Unable to allocate port at %x, errno %i " ,
2010-10-15 22:13:36 -05:00
ser - > port , err ) ;
2010-10-07 13:20:02 -05:00
return NULL ;
}
}
/* Disable UART interrupts, set DTR and RTS high
2015-08-14 22:34:37 +03:00
* and set speed .
*/
2010-10-07 13:20:02 -05:00
outb ( cval | UART_LCR_DLAB , ser - > port + UART_LCR ) ; /* set DLAB */
outb ( quot & 0xff , ser - > port + UART_DLL ) ; /* LS of divisor */
outb ( quot > > 8 , ser - > port + UART_DLM ) ; /* MS of divisor */
outb ( cval , ser - > port + UART_LCR ) ; /* reset DLAB */
/* Turn off Interrupts */
outb ( 0 , ser - > port + UART_IER ) ;
outb ( UART_MCR_DTR | UART_MCR_RTS , ser - > port + UART_MCR ) ;
/* If we read 0xff from the LSR, there is no UART here. */
if ( inb ( ser - > port + UART_LSR ) = = 0xff ) {
synth_release_region ( ser - > port , 8 ) ;
serstate = NULL ;
return NULL ;
}
mdelay ( 1 ) ;
speakup_info . port_tts = ser - > port ;
serstate = ser ;
start_serial_interrupt ( ser - > irq ) ;
return ser ;
}
static irqreturn_t synth_readbuf_handler ( int irq , void * dev_id )
{
unsigned long flags ;
int c ;
2014-09-09 20:04:34 +02:00
2013-05-13 00:02:58 -05:00
spin_lock_irqsave ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
while ( inb_p ( speakup_info . port_tts + UART_LSR ) & UART_LSR_DR ) {
2017-02-22 23:16:40 +05:30
c = inb_p ( speakup_info . port_tts + UART_RX ) ;
2016-11-06 15:09:18 +01:00
synth - > read_buff_add ( ( u_char ) c ) ;
2010-10-07 13:20:02 -05:00
}
2013-05-13 00:02:58 -05:00
spin_unlock_irqrestore ( & speakup_info . spinlock , flags ) ;
2010-10-07 13:20:02 -05:00
return IRQ_HANDLED ;
}
static void start_serial_interrupt ( int irq )
{
int rv ;
2015-09-11 11:32:27 +05:30
if ( ! synth - > read_buff_add )
2010-10-07 13:20:02 -05:00
return ;
rv = request_irq ( irq , synth_readbuf_handler , IRQF_SHARED ,
2016-11-06 15:09:18 +01:00
" serial " , ( void * ) synth_readbuf_handler ) ;
2010-10-07 13:20:02 -05:00
if ( rv )
2014-03-18 14:10:13 +05:30
pr_err ( " Unable to request Speakup serial I R Q \n " ) ;
2010-10-07 13:20:02 -05:00
/* Set MCR */
outb ( UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2 ,
2017-03-21 17:12:34 +05:30
speakup_info . port_tts + UART_MCR ) ;
2010-10-07 13:20:02 -05:00
/* Turn on Interrupts */
2017-06-05 23:44:11 -04:00
outb ( UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI ,
2017-06-05 23:44:40 -04:00
speakup_info . port_tts + UART_IER ) ;
2017-02-22 23:16:40 +05:30
inb ( speakup_info . port_tts + UART_LSR ) ;
inb ( speakup_info . port_tts + UART_RX ) ;
inb ( speakup_info . port_tts + UART_IIR ) ;
inb ( speakup_info . port_tts + UART_MSR ) ;
2010-10-07 13:20:02 -05:00
outb ( 1 , speakup_info . port_tts + UART_FCR ) ; /* Turn FIFO On */
}
2021-01-26 23:21:44 +01:00
static void spk_serial_send_xchar ( struct spk_synth * synth , char ch )
2017-04-22 09:00:28 +01:00
{
int timeout = SPK_XMITR_TIMEOUT ;
while ( spk_serial_tx_busy ( ) ) {
if ( ! - - timeout )
break ;
udelay ( 1 ) ;
}
outb ( ch , speakup_info . port_tts ) ;
}
2021-01-26 23:21:44 +01:00
static void spk_serial_tiocmset ( struct spk_synth * in_synth , unsigned int set , unsigned int clear )
2017-04-22 09:00:28 +01:00
{
int old = inb ( speakup_info . port_tts + UART_MCR ) ;
2017-06-05 23:43:55 -04:00
2017-04-22 09:00:28 +01:00
outb ( ( old & ~ clear ) | set , speakup_info . port_tts + UART_MCR ) ;
}
2017-03-16 08:10:17 +00:00
int spk_serial_synth_probe ( struct spk_synth * synth )
{
const struct old_serial_port * ser ;
int failed = 0 ;
if ( ( synth - > ser > = SPK_LO_TTY ) & & ( synth - > ser < = SPK_HI_TTY ) ) {
ser = spk_serial_init ( synth - > ser ) ;
if ( ! ser ) {
failed = - 1 ;
} else {
outb_p ( 0 , ser - > port ) ;
mdelay ( 1 ) ;
outb_p ( ' \r ' , ser - > port ) ;
}
} else {
failed = - 1 ;
pr_warn ( " ttyS%i is an invalid port \n " , synth - > ser ) ;
}
if ( failed ) {
pr_info ( " %s: not found \n " , synth - > long_name ) ;
return - ENODEV ;
}
pr_info ( " %s: ttyS%i, Driver Version %s \n " ,
synth - > long_name , synth - > ser , synth - > version ) ;
synth - > alive = 1 ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( spk_serial_synth_probe ) ;
2013-01-02 02:37:40 +01:00
void spk_stop_serial_interrupt ( void )
2010-10-07 13:20:02 -05:00
{
if ( speakup_info . port_tts = = 0 )
return ;
2015-09-11 11:32:27 +05:30
if ( ! synth - > read_buff_add )
2010-10-07 13:20:02 -05:00
return ;
/* Turn off interrupts */
2017-02-22 23:16:40 +05:30
outb ( 0 , speakup_info . port_tts + UART_IER ) ;
2010-10-07 13:20:02 -05:00
/* Free IRQ */
2016-11-06 15:09:18 +01:00
free_irq ( serstate - > irq , ( void * ) synth_readbuf_handler ) ;
2010-10-07 13:20:02 -05:00
}
2017-03-14 13:41:54 +00:00
EXPORT_SYMBOL_GPL ( spk_stop_serial_interrupt ) ;
2010-10-07 13:20:02 -05:00
2020-08-04 18:06:37 +02:00
static int spk_serial_wait_for_xmitr ( struct spk_synth * in_synth )
2010-10-07 13:20:02 -05:00
{
int tmout = SPK_XMITR_TIMEOUT ;
2014-09-09 20:04:34 +02:00
2017-03-14 13:41:52 +00:00
if ( ( in_synth - > alive ) & & ( timeouts > = NUM_DISABLE_TIMEOUTS ) ) {
2010-10-15 22:13:36 -05:00
pr_warn ( " %s: too many timeouts, deactivating speakup \n " ,
2017-03-14 13:41:52 +00:00
in_synth - > long_name ) ;
in_synth - > alive = 0 ;
2010-10-07 13:20:02 -05:00
/* No synth any more, so nobody will restart TTYs, and we thus
* need to do it ourselves . Now that there is no synth we can
2015-08-14 22:34:37 +03:00
* let application flood anyway
*/
2010-10-07 13:20:02 -05:00
speakup_start_ttys ( ) ;
timeouts = 0 ;
return 0 ;
}
while ( spk_serial_tx_busy ( ) ) {
if ( - - tmout = = 0 ) {
2017-06-05 23:44:26 -04:00
pr_warn ( " %s: timed out (tx busy) \n " ,
in_synth - > long_name ) ;
2010-10-07 13:20:02 -05:00
timeouts + + ;
return 0 ;
}
udelay ( 1 ) ;
}
tmout = SPK_CTS_TIMEOUT ;
while ( ! ( ( inb_p ( speakup_info . port_tts + UART_MSR ) ) & UART_MSR_CTS ) ) {
/* CTS */
if ( - - tmout = = 0 ) {
timeouts + + ;
return 0 ;
}
udelay ( 1 ) ;
}
timeouts = 0 ;
return 1 ;
}
2021-01-26 23:21:44 +01:00
static unsigned char spk_serial_in ( struct spk_synth * in_synth )
2010-10-07 13:20:02 -05:00
{
int tmout = SPK_SERIAL_TIMEOUT ;
while ( ! ( inb_p ( speakup_info . port_tts + UART_LSR ) & UART_LSR_DR ) ) {
if ( - - tmout = = 0 ) {
pr_warn ( " time out while waiting for input. \n " ) ;
return 0xff ;
}
udelay ( 1 ) ;
}
return inb_p ( speakup_info . port_tts + UART_RX ) ;
}
2021-01-26 23:21:44 +01:00
static unsigned char spk_serial_in_nowait ( struct spk_synth * in_synth )
2010-10-07 13:20:02 -05:00
{
unsigned char lsr ;
lsr = inb_p ( speakup_info . port_tts + UART_LSR ) ;
if ( ! ( lsr & UART_LSR_DR ) )
return 0 ;
return inb_p ( speakup_info . port_tts + UART_RX ) ;
}
2021-01-26 23:21:44 +01:00
static void spk_serial_flush_buffer ( struct spk_synth * in_synth )
2017-05-15 18:45:37 +01:00
{
/* TODO: flush the UART 16550 buffer */
}
2017-03-27 01:37:29 -05:00
static int spk_serial_out ( struct spk_synth * in_synth , const char ch )
2010-10-07 13:20:02 -05:00
{
2020-08-04 18:06:37 +02:00
if ( in_synth - > alive & & spk_serial_wait_for_xmitr ( in_synth ) ) {
2010-10-07 13:20:02 -05:00
outb_p ( ch , speakup_info . port_tts ) ;
return 1 ;
}
return 0 ;
}
2017-06-05 23:44:26 -04:00
const char * spk_serial_synth_immediate ( struct spk_synth * synth ,
const char * buff )
2017-03-16 08:10:17 +00:00
{
u_char ch ;
while ( ( ch = * buff ) ) {
if ( ch = = ' \n ' )
ch = synth - > procspeech ;
2020-08-04 18:06:37 +02:00
if ( spk_serial_wait_for_xmitr ( synth ) )
2017-03-16 08:10:17 +00:00
outb ( ch , speakup_info . port_tts ) ;
else
return buff ;
buff + + ;
}
return NULL ;
}
EXPORT_SYMBOL_GPL ( spk_serial_synth_immediate ) ;
2021-01-26 23:21:44 +01:00
void spk_serial_release ( struct spk_synth * synth )
2010-10-07 13:20:02 -05:00
{
2017-03-14 13:41:54 +00:00
spk_stop_serial_interrupt ( ) ;
2010-10-07 13:20:02 -05:00
if ( speakup_info . port_tts = = 0 )
return ;
synth_release_region ( speakup_info . port_tts , 8 ) ;
speakup_info . port_tts = 0 ;
}
EXPORT_SYMBOL_GPL ( spk_serial_release ) ;